diff --git a/packages/misc/default-number-value/README.md b/packages/misc/default-number-value/README.md new file mode 100644 index 00000000..4124caf2 --- /dev/null +++ b/packages/misc/default-number-value/README.md @@ -0,0 +1,19 @@ +# @x-oasis/default-number-value + +## Installation + +```bash +$ npm i @x-oasis/default-number-value +``` + +## How to use + +```typescript +import booleanWithDefault from '@x-oasis/default-number-value' +``` + +## How to run test + +```bash +$ pnpm test +``` \ No newline at end of file diff --git a/packages/misc/default-number-value/package.json b/packages/misc/default-number-value/package.json new file mode 100644 index 00000000..69b88521 --- /dev/null +++ b/packages/misc/default-number-value/package.json @@ -0,0 +1,27 @@ +{ + "name": "@x-oasis/default-number-value", + "version": "0.1.8", + "description": "default-number-value function", + "main": "dist/index.js", + "typings": "dist/index.d.ts", + "module": "dist/default-number-value.esm.js", + "files": [ + "dist", + "index.ts", + "src" + ], + "scripts": { + "build": "tsdx build --tsconfig tsconfig.build.json", + "clean": "rimraf ./dist", + "test": "vitest", + "compile": "tsc -p tsconfig.build.json" + }, + "author": "", + "license": "ISC", + "devDependencies": { + "tsdx": "^0.14.1" + }, + "dependencies": { + "@x-oasis/default-value": "workspace:*" + } +} diff --git a/packages/misc/default-number-value/src/index.ts b/packages/misc/default-number-value/src/index.ts new file mode 100644 index 00000000..c119075a --- /dev/null +++ b/packages/misc/default-number-value/src/index.ts @@ -0,0 +1,10 @@ +import defaultValue from '@x-oasis/default-value'; + +function defaultNumberValue(value, _defaultValue) { + const _value = defaultValue(value, _defaultValue); + const _n = Number(_value); + if (typeof _n !== 'number') return 0; + return _n; +} + +export default defaultNumberValue; diff --git a/packages/misc/default-number-value/test/test.spec.ts b/packages/misc/default-number-value/test/test.spec.ts new file mode 100644 index 00000000..e2e270a0 --- /dev/null +++ b/packages/misc/default-number-value/test/test.spec.ts @@ -0,0 +1,10 @@ +import { expect, test } from 'vitest'; +import defaultNumberValue from '../src'; + +test('boolean with default', async () => { + expect(defaultNumberValue(undefined, 1)).toBe(1); + expect(defaultNumberValue(NaN, 1)).toBe(1); + expect(defaultNumberValue(null, 1)).toBe(1); + expect(defaultNumberValue(3, 4)).toBe(3); + expect(defaultNumberValue(0, 4)).toBe(0); +}); diff --git a/packages/misc/default-number-value/tsconfig.build.json b/packages/misc/default-number-value/tsconfig.build.json new file mode 100644 index 00000000..c8613f05 --- /dev/null +++ b/packages/misc/default-number-value/tsconfig.build.json @@ -0,0 +1,12 @@ +{ + "extends": "../../../tsconfig.build.json", + "compilerOptions": { + "outDir": "./dist", + "esModuleInterop": true + }, + + "include": [ + "index.ts", + "src/**/*" + ] +} \ No newline at end of file diff --git a/packages/misc/default-number-value/tsconfig.json b/packages/misc/default-number-value/tsconfig.json new file mode 100644 index 00000000..a5aa7cc0 --- /dev/null +++ b/packages/misc/default-number-value/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../../../tsconfig.json", + "compilerOptions": { + "jsx": "react", + "esModuleInterop": true + } +} \ No newline at end of file diff --git a/packages/misc/default-number-value/vitest.config.ts b/packages/misc/default-number-value/vitest.config.ts new file mode 100644 index 00000000..31d7ded7 --- /dev/null +++ b/packages/misc/default-number-value/vitest.config.ts @@ -0,0 +1,26 @@ +// import path from 'path'; +// import tsPath from 'vite-tsconfig-paths'; +import { defineConfig } from 'vitest/config'; + +// const __dirname = import.meta.url.slice(7, import.meta.url.lastIndexOf('/')); + +export default defineConfig({ + test: { + globals: true, + include: ['test/**/*.(spec|test).ts'], + exclude: ['node_modules/**'], + threads: false, + + coverage: { + provider: 'istanbul', // or 'c8' + }, + }, + + // plugins: [tsPath()], + resolve: { + alias: {}, + }, + define: { + __DEV__: false, + }, +}); diff --git a/packages/schedule/debounce/README.md b/packages/schedule/debounce/README.md new file mode 100644 index 00000000..57d2962a --- /dev/null +++ b/packages/schedule/debounce/README.md @@ -0,0 +1,33 @@ +# @x-oasis/debounce + +## Installation + +```bash +$ npm i @x-oasis/debounce +``` + +## How to use + +```typescript +import debounce from '@x-oasis/debounce' +``` + +## How to run test + +```bash +$ pnpm test +``` + +- resize: only care the final value +- keypress on autocomplete form with Ajax request + +The main difference between this and debouncing is that debounce guarantees the execution of the function regularly, at least every X milliseconds. + + + +## Further Reading + +- [lodash - debounce](https://lodash.com/docs/4.17.15#debounce) +- [Debouncing and Throttling Explained Through Examples](https://css-tricks.com/debouncing-throttling-explained-examples/) +- [Debouncing Javascript Methods](http://unscriptable.com/2009/03/20/debouncing-javascript-methods/) +- [Difference Between throttling and debouncing a function](https://stackoverflow.com/questions/25991367/difference-between-throttling-and-debouncing-a-function) \ No newline at end of file diff --git a/packages/schedule/debounce/package.json b/packages/schedule/debounce/package.json new file mode 100644 index 00000000..d21d00e5 --- /dev/null +++ b/packages/schedule/debounce/package.json @@ -0,0 +1,23 @@ +{ + "name": "@x-oasis/debounce", + "version": "0.1.8", + "description": "debounce function", + "main": "dist/index.js", + "typings": "dist/index.d.ts", + "module": "dist/debounce.esm.js", + "scripts": { + "build": "tsdx build --tsconfig tsconfig.build.json", + "clean": "rimraf ./dist", + "test": "vitest", + "compile": "tsc -p tsconfig.build.json" + }, + "author": "", + "license": "ISC", + "dependencies": { + "@x-oasis/default-boolean-value": "workspace:*", + "@x-oasis/default-number-value": "workspace:*" + }, + "devDependencies": { + "tsdx": "^0.14.1" + } +} diff --git a/packages/schedule/debounce/src/index.ts b/packages/schedule/debounce/src/index.ts new file mode 100644 index 00000000..9cc2457d --- /dev/null +++ b/packages/schedule/debounce/src/index.ts @@ -0,0 +1,67 @@ +import { Options } from './types'; +import defaultBooleanValue from '@x-oasis/default-boolean-value'; +import defaultNumberValue from '@x-oasis/default-number-value'; + +const DEFAULT_TIMEOUT = 200; + +/** + * + * @param func + * @param timeout + * @returns + * + * trigger first, then trigger on the last + */ + +export default function debounce( + func: Function, + timeout = DEFAULT_TIMEOUT, + options: Options +) { + let lastPerformTime = 0; + let lastCallTime = 0; + const leading = defaultBooleanValue(options?.leading, false); + const trailing = defaultBooleanValue(options?.trailing, true); + const maxTime = defaultNumberValue(options?.maxTime, 0); + const resetTime = defaultNumberValue(options?.resetTime, 0); + let timeoutId = undefined; + let lastArgs = undefined; + let result = undefined; + let lastThis = undefined; + + function clock() {} + + function shouldPerform(time) { + if (!lastPerformTime) return true; + if (Date.now() - time > timeout) return true; + return false; + } + + function perform() { + result = func.apply(lastThis, lastArgs); + lastPerformTime = Date.now(); + timeoutId = undefined; + lastArgs = undefined; + } + + function maxTimeoutHandler() {} + + function performLeading() { + if (leading) perform(); + } + + function performTrailing() {} + + return function (...args) { + const now = Date.now(); + lastCallTime = now; + lastArgs = args; + lastThis = this; // eslint-disable-line + + if (shouldPerform(now)) { + if (!timeoutId) { + performLeading(); + } + } + }; +} diff --git a/packages/schedule/debounce/src/types.ts b/packages/schedule/debounce/src/types.ts new file mode 100644 index 00000000..3a8c5170 --- /dev/null +++ b/packages/schedule/debounce/src/types.ts @@ -0,0 +1,9 @@ +type ResolveArgs = (...args: [any]) => any; + +export type Options = { + leading: boolean; + trailing: boolean; + maxTime: number; + resetTime: number; + resolveArgs: ResolveArgs; +}; diff --git a/packages/schedule/debounce/test/test.spec.ts b/packages/schedule/debounce/test/test.spec.ts new file mode 100644 index 00000000..88f35611 --- /dev/null +++ b/packages/schedule/debounce/test/test.spec.ts @@ -0,0 +1,5 @@ +import { expect, test } from 'vitest'; + +test('vitest', async () => { + expect('vitest').toBe('vitest'); +}); diff --git a/packages/schedule/debounce/tsconfig.build.json b/packages/schedule/debounce/tsconfig.build.json new file mode 100644 index 00000000..17632b25 --- /dev/null +++ b/packages/schedule/debounce/tsconfig.build.json @@ -0,0 +1,11 @@ +{ + "extends": "../../../tsconfig.build.json", + "compilerOptions": { + "outDir": "./dist", + "esModuleInterop": true + }, + + "include": [ + "src/**/*" + ] +} \ No newline at end of file diff --git a/packages/schedule/debounce/tsconfig.json b/packages/schedule/debounce/tsconfig.json new file mode 100644 index 00000000..a5aa7cc0 --- /dev/null +++ b/packages/schedule/debounce/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../../../tsconfig.json", + "compilerOptions": { + "jsx": "react", + "esModuleInterop": true + } +} \ No newline at end of file diff --git a/packages/schedule/debounce/vitest.config.ts b/packages/schedule/debounce/vitest.config.ts new file mode 100644 index 00000000..31d7ded7 --- /dev/null +++ b/packages/schedule/debounce/vitest.config.ts @@ -0,0 +1,26 @@ +// import path from 'path'; +// import tsPath from 'vite-tsconfig-paths'; +import { defineConfig } from 'vitest/config'; + +// const __dirname = import.meta.url.slice(7, import.meta.url.lastIndexOf('/')); + +export default defineConfig({ + test: { + globals: true, + include: ['test/**/*.(spec|test).ts'], + exclude: ['node_modules/**'], + threads: false, + + coverage: { + provider: 'istanbul', // or 'c8' + }, + }, + + // plugins: [tsPath()], + resolve: { + alias: {}, + }, + define: { + __DEV__: false, + }, +}); diff --git a/packages/schedule/throttle/src/index.ts b/packages/schedule/throttle/src/index.ts index c7c62780..37ae5f45 100644 --- a/packages/schedule/throttle/src/index.ts +++ b/packages/schedule/throttle/src/index.ts @@ -17,45 +17,24 @@ export default function throttle( | boolean ) { let last = 0; - let timeoutHandler = null; - const fallback = !!fallbackOptions; - - let queue = []; + let _args = []; return function throttled(...args: any[]) { const now = Date.now(); - if (now - last > threshold) { - last = now; - if (timeoutHandler) { - clearTimeout(timeoutHandler); - timeoutHandler = null; - queue = []; - } - - fn.apply(null, args); - return; - } - - if (!fallback) return; - - timeoutHandler = setTimeout(() => { - const len = queue.length; - if (len) { - const currentArgs = queue[len - 1]; - queue = []; - fn.apply(null, currentArgs); - } - }, threshold); - - let nextArgs = args; const persistArgs = typeof fallbackOptions === 'object' ? fallbackOptions.persistArgs : null; + _args = args; + if (typeof persistArgs === 'function') { - nextArgs = persistArgs(args); + _args = persistArgs(args); } - queue.push(nextArgs); + if (now - last > threshold) { + last = now; + + return fn.apply(this, args); + } }; } diff --git a/packages/struct/heap/src/index.ts b/packages/struct/heap/src/index.ts index fc543ed2..7cc82c08 100644 --- a/packages/struct/heap/src/index.ts +++ b/packages/struct/heap/src/index.ts @@ -10,7 +10,7 @@ class Heap { private _size: number; private _comparator: Comparator; - constructor(items: Array, comparator: Comparator) { + constructor(items: Array, comparator?: Comparator) { this._items = items || []; this._size = this._items.length; this._comparator = comparator || defaultComparator; diff --git a/packages/struct/heap/test/test.spec.ts b/packages/struct/heap/test/test.spec.ts index f1a160bf..272e686f 100644 --- a/packages/struct/heap/test/test.spec.ts +++ b/packages/struct/heap/test/test.spec.ts @@ -1,85 +1,9 @@ import { expect, describe, it } from 'vitest'; - -import PrefixIntervalTree from '../src'; +import Heap from '../src'; describe('basic', () => { - it('init with number ', () => { - const intervalTree = new PrefixIntervalTree(4); - expect(intervalTree.getHeap()).toEqual([0, 0, 0, 0, 0, 0, 0, 0]); - }); - - it('init with arr ', () => { - const intervalTree = new PrefixIntervalTree([0, 2, 3, 1]); - expect(intervalTree.getHeap()).toEqual([0, 6, 2, 4, 0, 2, 3, 1]); - }); - - it('this.heap[1] should be the total value', () => { - const intervalTree = new PrefixIntervalTree([0, 2, 3, 1]); - expect(intervalTree.getHeap()[1]).toEqual(6); - }); - - it('heap size should be the power of 2', () => { - const intervalTree = new PrefixIntervalTree(10); - expect(intervalTree.getSize()).toBe(16); - }); -}); - -describe('leastStrictUpperBound', () => { - it('set index value', () => { - const intervalTree = new PrefixIntervalTree(4); - intervalTree.set(0, 100); - intervalTree.set(1, 100); - intervalTree.set(2, 100); - intervalTree.set(3, 100); - intervalTree.set(4, 100); - intervalTree.set(5, 100); - intervalTree.set(6, 100); - intervalTree.set(7, 100); - intervalTree.set(8, 100); - intervalTree.set(9, 100); - expect(intervalTree.getHeap()).toEqual([ - 0, 1000, 800, 200, 400, 400, 200, 0, 200, 200, 200, 200, 200, 0, 0, 0, - 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 0, 0, 0, 0, 0, 0, - ]); - expect(intervalTree.leastStrictUpperBound(330)).toBe(3); - expect(intervalTree.leastStrictUpperBound(400)).toBe(3); - expect(intervalTree.leastStrictUpperBound(401)).toBe(4); - expect(intervalTree.leastStrictUpperBound(901)).toBe(9); - expect(intervalTree.leastStrictUpperBound(1001)).toBe(9); - expect(intervalTree.leastStrictUpperBound(1100)).toBe(9); - }); - - it('computeRange', () => { - const intervalTree = new PrefixIntervalTree(4); - intervalTree.set(0, 100); - intervalTree.set(1, 100); - intervalTree.set(2, 100); - intervalTree.set(3, 100); - intervalTree.set(4, 100); - intervalTree.set(5, 100); - intervalTree.set(6, 100); - intervalTree.set(7, 100); - intervalTree.set(8, 100); - intervalTree.set(9, 100); - expect(intervalTree.getHeap()).toEqual([ - 0, 1000, 800, 200, 400, 400, 200, 0, 200, 200, 200, 200, 200, 0, 0, 0, - 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 0, 0, 0, 0, 0, 0, - ]); - expect(intervalTree.computeRange(0, 330)).toEqual({ - startIndex: 0, - endIndex: 3, - }); - expect(intervalTree.computeRange(0, 400)).toEqual({ - startIndex: 0, - endIndex: 3, - }); - expect(intervalTree.computeRange(400, 500)).toEqual({ - startIndex: 3, - endIndex: 4, - }); - expect(intervalTree.computeRange(400, 901)).toEqual({ - startIndex: 3, - endIndex: 9, - }); + it('basic', () => { + const heap = new Heap([3, 4, 8, 2]); + expect(heap.pop()).toEqual(2); }); }); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 67e9979c..ba69f604 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -95,6 +95,15 @@ importers: devDependencies: tsdx: 0.14.1 + packages/misc/default-number-value: + specifiers: + '@x-oasis/default-value': workspace:* + tsdx: ^0.14.1 + dependencies: + '@x-oasis/default-value': link:../default-value + devDependencies: + tsdx: 0.14.1 + packages/misc/default-value: specifiers: tsdx: ^0.14.1 @@ -167,6 +176,17 @@ importers: devDependencies: tsdx: 0.14.1 + packages/schedule/debounce: + specifiers: + '@x-oasis/default-boolean-value': workspace:* + '@x-oasis/default-number-value': workspace:* + tsdx: ^0.14.1 + dependencies: + '@x-oasis/default-boolean-value': link:../../misc/default-boolean-value + '@x-oasis/default-number-value': link:../../misc/default-number-value + devDependencies: + tsdx: 0.14.1 + packages/schedule/throttle: specifiers: tsdx: ^0.14.1