diff --git a/package.json b/package.json index 034e538..8aff106 100644 --- a/package.json +++ b/package.json @@ -39,14 +39,12 @@ }, "dependencies": { "blurhash": "2.0.5", - "lodash.debounce": "4.0.8", "react-merge-refs": "2.1.1", "thumbhash": "0.1.1" }, "devDependencies": { "@splinetool/runtime": "^1.10.29", "@types/animejs": "^3.1.12", - "@types/lodash.debounce": "^4.0.9", "@types/node": "^20.14.1", "@types/react": "^18.3.3", "@types/react-dom": "^18.3.0", diff --git a/src/ParentSize.tsx b/src/ParentSize.tsx index 9d4395e..4bf980d 100644 --- a/src/ParentSize.tsx +++ b/src/ParentSize.tsx @@ -1,7 +1,7 @@ // Modified version of // https://github.com/airbnb/visx/blob/master/packages/visx-responsive/src/components/ParentSize.tsx 'use client'; -import debounce from 'lodash.debounce'; +import debounce from './debounce'; import { forwardRef, useEffect, useMemo, useRef, useState } from 'react'; import { mergeRefs } from 'react-merge-refs'; diff --git a/src/debounce.ts b/src/debounce.ts new file mode 100644 index 0000000..5fa36a2 --- /dev/null +++ b/src/debounce.ts @@ -0,0 +1,71 @@ +type DebouncedFunction void> = T & { + cancel: () => void; +}; + +/** + * Lightweight debounce utility replacing lodash.debounce. + * + * Supports: + * - Trailing-only (default): fires after `delay` ms of inactivity + * - Leading+trailing: fires immediately on first call, then once more + * on trailing edge if additional calls occurred during the delay + * - cancel(): clears pending invocation and resets all state + * + * NOT supported (not needed by ParentSize): + * - { trailing: false } option + * - maxWait + * - flush() / pending() + */ +export default function debounce void>( + fn: T, + delay: number, + { leading = false }: { leading?: boolean } = {} +): DebouncedFunction { + let timeoutId: ReturnType | undefined; + let isLeadingInvoked = false; + let lastThis: unknown; + let lastArgs: Parameters | undefined; + + const debounced = function (this: unknown, ...args: Parameters) { + lastThis = this; + lastArgs = args; + + if (leading && !isLeadingInvoked) { + isLeadingInvoked = true; + fn.apply(this, args); + lastArgs = undefined; // Clear so we can detect subsequent calls + } + + if (timeoutId !== undefined) { + clearTimeout(timeoutId); + } + + timeoutId = setTimeout(() => { + const thisCtx = lastThis; + const thisArgs = lastArgs; + // Reset state BEFORE calling fn so re-entrant calls work correctly + isLeadingInvoked = false; + timeoutId = undefined; + lastArgs = undefined; + + if (!leading && thisArgs !== undefined) { + fn.apply(thisCtx, thisArgs); + } else if (thisArgs !== undefined) { + // Leading+trailing: only fire trailing if there were calls after the leading + fn.apply(thisCtx, thisArgs); + } + }, delay); + } as DebouncedFunction; + + debounced.cancel = () => { + if (timeoutId !== undefined) { + clearTimeout(timeoutId); + } + timeoutId = undefined; + isLeadingInvoked = false; + lastArgs = undefined; + lastThis = undefined; + }; + + return debounced; +} diff --git a/vite.config.ts b/vite.config.ts index 637504e..0eaf437 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -33,7 +33,6 @@ export default defineConfig({ 'next/image', // these are the dependencies, they are listed in the package.json already 'blurhash', - 'lodash.debounce', 'react-merge-refs', 'thumbhash', ], diff --git a/yarn.lock b/yarn.lock index 012cde5..cedb4d8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1095,14 +1095,12 @@ __metadata: dependencies: "@splinetool/runtime": ^1.10.29 "@types/animejs": ^3.1.12 - "@types/lodash.debounce": ^4.0.9 "@types/node": ^20.14.1 "@types/react": ^18.3.3 "@types/react-dom": ^18.3.0 "@vitejs/plugin-react": ^4.3.0 animejs: ^3.2.2 blurhash: 2.0.5 - lodash.debounce: 4.0.8 modern-normalize: ^1.1.0 next: 15.0.0-rc.0 np: ^10.0.5 @@ -1223,22 +1221,6 @@ __metadata: languageName: node linkType: hard -"@types/lodash.debounce@npm:^4.0.9": - version: 4.0.9 - resolution: "@types/lodash.debounce@npm:4.0.9" - dependencies: - "@types/lodash": "*" - checksum: 8183a152e01928e3b97ca773f6ae6038b8695e76493ba8bf6b743ec143948a62294fbc9d49fa4a78b52265b3ba4892ef57534e0c13d04aa0f111671b5a944feb - languageName: node - linkType: hard - -"@types/lodash@npm:*": - version: 4.14.191 - resolution: "@types/lodash@npm:4.14.191" - checksum: ba0d5434e10690869f32d5ea49095250157cae502f10d57de0a723fd72229ce6c6a4979576f0f13e0aa9fbe3ce2457bfb9fa7d4ec3d6daba56730a51906d1491 - languageName: node - linkType: hard - "@types/node@npm:^20.14.1": version: 20.14.1 resolution: "@types/node@npm:20.14.1" @@ -3545,13 +3527,6 @@ __metadata: languageName: node linkType: hard -"lodash.debounce@npm:4.0.8": - version: 4.0.8 - resolution: "lodash.debounce@npm:4.0.8" - checksum: a3f527d22c548f43ae31c861ada88b2637eb48ac6aa3eb56e82d44917971b8aa96fbb37aa60efea674dc4ee8c42074f90f7b1f772e9db375435f6c83a19b3bc6 - languageName: node - linkType: hard - "lodash.zip@npm:^4.2.0": version: 4.2.0 resolution: "lodash.zip@npm:4.2.0"