diff --git a/package-lock.json b/package-lock.json index f1e2809..26ff68d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,8 +10,6 @@ "license": "MIT", "dependencies": { "core-js": "^3.6.4", - "lodash.clamp": "^4.0.3", - "lodash.debounce": "^4.0.8", "tslib": "^1.10.0" }, "devDependencies": { @@ -6274,22 +6272,12 @@ "integrity": "sha1-soqmKIorn8ZRA1x3EfZathkDMaY=", "dev": true }, - "node_modules/lodash.clamp": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/lodash.clamp/-/lodash.clamp-4.0.3.tgz", - "integrity": "sha512-HvzRFWjtcguTW7yd8NJBshuNaCa8aqNFtnswdT7f/cMd/1YKy5Zzoq4W/Oxvnx9l7aeY258uSdDfM793+eLsVg==" - }, "node_modules/lodash.clonedeep": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=", "dev": true }, - "node_modules/lodash.debounce": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", - "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==" - }, "node_modules/lodash.memoize": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", @@ -20897,22 +20885,12 @@ "integrity": "sha1-soqmKIorn8ZRA1x3EfZathkDMaY=", "dev": true }, - "lodash.clamp": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/lodash.clamp/-/lodash.clamp-4.0.3.tgz", - "integrity": "sha512-HvzRFWjtcguTW7yd8NJBshuNaCa8aqNFtnswdT7f/cMd/1YKy5Zzoq4W/Oxvnx9l7aeY258uSdDfM793+eLsVg==" - }, "lodash.clonedeep": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=", "dev": true }, - "lodash.debounce": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", - "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==" - }, "lodash.memoize": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", diff --git a/package.json b/package.json index b99c2e2..2648c34 100644 --- a/package.json +++ b/package.json @@ -80,8 +80,6 @@ }, "dependencies": { "core-js": "^3.6.4", - "lodash.clamp": "^4.0.3", - "lodash.debounce": "^4.0.8", "tslib": "^1.10.0" } } diff --git a/src/decorators/debounce.ts b/src/decorators/debounce.ts index 05b36c5..beed53e 100644 --- a/src/decorators/debounce.ts +++ b/src/decorators/debounce.ts @@ -1,4 +1,4 @@ -import $debounce from 'lodash.debounce'; +import { debounce as $debounce } from '../utils'; export function debounce(...options) { return (_proto: any, key: string, descriptor: PropertyDescriptor) => { diff --git a/src/decorators/range.ts b/src/decorators/range.ts index bac1c50..5de5f1b 100644 --- a/src/decorators/range.ts +++ b/src/decorators/range.ts @@ -1,4 +1,4 @@ -import clamp from 'lodash.clamp'; +import { clamp } from '../utils'; export function range(min = -Infinity, max = Infinity) { return (proto: any, key: string) => { diff --git a/src/events/mouse.ts b/src/events/mouse.ts index 75a46a6..854b73e 100644 --- a/src/events/mouse.ts +++ b/src/events/mouse.ts @@ -1,4 +1,4 @@ -import clamp from 'lodash.clamp'; +import { clamp } from '../utils'; import * as I from '../interfaces/'; import { diff --git a/src/events/resize.ts b/src/events/resize.ts index 987e428..0bbf055 100644 --- a/src/events/resize.ts +++ b/src/events/resize.ts @@ -1,5 +1,5 @@ import * as I from '../interfaces/'; -import debounce from 'lodash.debounce'; +import { debounce } from '../utils'; import { eventScope, diff --git a/src/events/select.ts b/src/events/select.ts index a56729f..116e614 100644 --- a/src/events/select.ts +++ b/src/events/select.ts @@ -1,4 +1,4 @@ -import clamp from 'lodash.clamp'; +import { clamp } from '../utils'; import * as I from '../interfaces/'; import { diff --git a/src/plugins/overscroll/glow.ts b/src/plugins/overscroll/glow.ts index 0452c1e..2ed69b1 100644 --- a/src/plugins/overscroll/glow.ts +++ b/src/plugins/overscroll/glow.ts @@ -1,4 +1,4 @@ -import clamp from 'lodash.clamp'; +import { clamp } from '../../utils'; import Scrollbar from 'smooth-scrollbar'; import { setStyle } from '../../utils/set-style'; diff --git a/src/plugins/overscroll/index.ts b/src/plugins/overscroll/index.ts index f5592b2..147200b 100644 --- a/src/plugins/overscroll/index.ts +++ b/src/plugins/overscroll/index.ts @@ -1,5 +1,4 @@ -import clamp from 'lodash.clamp'; -import debounce from 'lodash.debounce'; +import { clamp, debounce } from '../../utils'; import { ScrollbarPlugin } from 'smooth-scrollbar'; import { Bounce } from './bounce'; import { Glow } from './glow'; diff --git a/src/scrollbar.ts b/src/scrollbar.ts index 3bd1e0e..b3cc48f 100644 --- a/src/scrollbar.ts +++ b/src/scrollbar.ts @@ -1,4 +1,4 @@ -import clamp from 'lodash.clamp'; +import { clamp } from './utils'; import { Options } from './options'; diff --git a/src/scrolling/scroll-into-view.ts b/src/scrolling/scroll-into-view.ts index 8e4756e..4d65d57 100644 --- a/src/scrolling/scroll-into-view.ts +++ b/src/scrolling/scroll-into-view.ts @@ -1,4 +1,4 @@ -import clamp from 'lodash.clamp'; +import { clamp } from '../utils'; import * as I from '../interfaces/'; diff --git a/src/scrolling/scroll-to.ts b/src/scrolling/scroll-to.ts index e3a5c3e..eb9ae7e 100644 --- a/src/scrolling/scroll-to.ts +++ b/src/scrolling/scroll-to.ts @@ -1,4 +1,4 @@ -import clamp from 'lodash.clamp'; +import { clamp } from '../utils'; import * as I from '../interfaces/'; diff --git a/src/scrolling/set-position.ts b/src/scrolling/set-position.ts index a337aad..d17fc90 100644 --- a/src/scrolling/set-position.ts +++ b/src/scrolling/set-position.ts @@ -1,4 +1,4 @@ -import clamp from 'lodash.clamp'; +import { clamp } from '../utils'; import * as I from '../interfaces/'; import { diff --git a/src/utils/clamp.ts b/src/utils/clamp.ts new file mode 100644 index 0000000..62617df --- /dev/null +++ b/src/utils/clamp.ts @@ -0,0 +1,3 @@ +export function clamp(value: number, lower: number, upper: number): number { + return Math.max(lower, Math.min(upper, value)); +} diff --git a/src/utils/debounce.ts b/src/utils/debounce.ts new file mode 100644 index 0000000..3365b5b --- /dev/null +++ b/src/utils/debounce.ts @@ -0,0 +1,142 @@ +interface DebounceOptions { + maxWait?: number; + leading?: boolean; + trailing?: boolean; +} + +export interface IDebounceReturn { + cancel: () => void; + flush: () => void; + isPending: () => boolean; +} + +export interface DebouncedReturn ReturnType> extends IDebounceReturn { + (...args: Parameters): ReturnType | undefined; +} + +export function debounce ReturnType>(fn: T, wait?: number, options?: DebounceOptions): DebouncedReturn { + + let lastArgs; + let lastThis: unknown; + let timerId; + let lastCallTime; + let lastInvokeTime = 0; + let leading = false; + let maxing = false; + let trailing = true; + let result: ReturnType; + + const useRAF = !wait && wait !== 0 && typeof window !== 'undefined' && typeof window.requestAnimationFrame === 'function'; + + wait = +wait! || 0; + options = options || {}; + + leading = !!options.leading; + trailing = 'trailing' in options ? !!options.trailing : true; + maxing = 'maxWait' in options; + const maxWait = maxing ? Math.max(+options.maxWait! || 0, wait) : null; + + function invokeFunc(time: number) { + const args = lastArgs; + const thisArg = lastThis; + + lastArgs = lastThis = null; + lastInvokeTime = time; + result = fn.apply(thisArg, args); + return result; + } + + function startTimer(pendingFunc: () => void, wait: number) { + if (useRAF) { + window.cancelAnimationFrame(timerId); + return window.requestAnimationFrame(pendingFunc); + } + return setTimeout(pendingFunc, wait); + } + + function cancelTimer(id) { + useRAF ? window.cancelAnimationFrame(id) : clearTimeout(id); + } + + function shouldInvoke(time: number) { + const timeSinceLastCall = time - lastCallTime; + const timeSinceLastInvoke = time - lastInvokeTime; + + return ( + lastCallTime === null || + timeSinceLastCall >= wait! || + timeSinceLastCall < 0 || + (maxing && timeSinceLastInvoke >= maxWait!) + ); + } + + function timerExpired(): void | ReturnType { + const time = Date.now(); + if (shouldInvoke(time)) { + return trailingEdge(time); + } + + const timeSinceLastCall = time - lastCallTime; + const timeSinceLastInvoke = time - lastInvokeTime; + const timeWaiting = wait! - timeSinceLastCall; + const remainingWait = maxing ? Math.min(timeWaiting, maxWait! - timeSinceLastInvoke) : timeWaiting; + + timerId = startTimer(timerExpired, remainingWait); + } + + function trailingEdge(time: number) { + timerId = null; + + if (trailing && lastArgs) { + return invokeFunc(time); + } + lastArgs = lastThis = null; + return result; + } + + function cancel() { + if (timerId) { + cancelTimer(timerId); + } + lastInvokeTime = 0; + lastArgs = lastCallTime = lastThis = timerId = null; + } + + function flush() { + return !timerId ? result : trailingEdge(Date.now()); + } + + function isPending() { + return !!timerId; + } + + function debounced(this: unknown, ...args) { + const time = Date.now(); + const isInvoking = shouldInvoke(time); + + lastArgs = args; + lastThis = this; + lastCallTime = time; + + if (isInvoking) { + if (!timerId) { + lastInvokeTime = time; + timerId = startTimer(timerExpired, wait!); + return leading ? invokeFunc(time) : result; + } + if (maxing) { + timerId = startTimer(timerExpired, wait!); + return invokeFunc(lastCallTime); + } + } + if (!timerId) { + timerId = startTimer(timerExpired, wait!); + } + return result; + } + debounced.cancel = cancel; + debounced.flush = flush; + debounced.isPending = isPending; + + return debounced; +} diff --git a/src/utils/index.ts b/src/utils/index.ts index 6133b92..fbd8f36 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -4,3 +4,5 @@ export * from './get-position'; export * from './is-one-of'; export * from './set-style'; export * from './touch-record'; +export { clamp } from './clamp'; +export { debounce } from './debounce';