@bquery/bquery 1.3.0 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +546 -501
- package/dist/component/component.d.ts.map +1 -1
- package/dist/component/index.d.ts +2 -0
- package/dist/component/index.d.ts.map +1 -1
- package/dist/component/library.d.ts +34 -0
- package/dist/component/library.d.ts.map +1 -0
- package/dist/component/types.d.ts +10 -6
- package/dist/component/types.d.ts.map +1 -1
- package/dist/component-CY5MVoYN.js +531 -0
- package/dist/component-CY5MVoYN.js.map +1 -0
- package/dist/component.es.mjs +6 -184
- package/dist/config-DRmZZno3.js +40 -0
- package/dist/config-DRmZZno3.js.map +1 -0
- package/dist/core/collection.d.ts +19 -3
- package/dist/core/collection.d.ts.map +1 -1
- package/dist/core/element.d.ts +23 -4
- package/dist/core/element.d.ts.map +1 -1
- package/dist/core/index.d.ts +1 -0
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/utils/function.d.ts +21 -4
- package/dist/core/utils/function.d.ts.map +1 -1
- package/dist/core-CK2Mfpf4.js +648 -0
- package/dist/core-CK2Mfpf4.js.map +1 -0
- package/dist/core-DPdbItcq.js +112 -0
- package/dist/core-DPdbItcq.js.map +1 -0
- package/dist/core.es.mjs +45 -1218
- package/dist/full.d.ts +6 -6
- package/dist/full.d.ts.map +1 -1
- package/dist/full.es.mjs +98 -92
- package/dist/full.iife.js +173 -3
- package/dist/full.iife.js.map +1 -1
- package/dist/full.umd.js +173 -3
- package/dist/full.umd.js.map +1 -1
- package/dist/index.es.mjs +143 -139
- package/dist/motion/transition.d.ts +1 -1
- package/dist/motion/transition.d.ts.map +1 -1
- package/dist/motion/types.d.ts +11 -1
- package/dist/motion/types.d.ts.map +1 -1
- package/dist/motion-C5DRdPnO.js +415 -0
- package/dist/motion-C5DRdPnO.js.map +1 -0
- package/dist/motion.es.mjs +25 -361
- package/dist/object-qGpWr6-J.js +38 -0
- package/dist/object-qGpWr6-J.js.map +1 -0
- package/dist/platform/announcer.d.ts +59 -0
- package/dist/platform/announcer.d.ts.map +1 -0
- package/dist/platform/config.d.ts +92 -0
- package/dist/platform/config.d.ts.map +1 -0
- package/dist/platform/cookies.d.ts +45 -0
- package/dist/platform/cookies.d.ts.map +1 -0
- package/dist/platform/index.d.ts +8 -0
- package/dist/platform/index.d.ts.map +1 -1
- package/dist/platform/meta.d.ts +62 -0
- package/dist/platform/meta.d.ts.map +1 -0
- package/dist/platform/storage.d.ts.map +1 -1
- package/dist/platform-B7JhGBc7.js +361 -0
- package/dist/platform-B7JhGBc7.js.map +1 -0
- package/dist/platform.es.mjs +11 -243
- package/dist/reactive/async-data.d.ts +114 -0
- package/dist/reactive/async-data.d.ts.map +1 -0
- package/dist/reactive/core.d.ts +12 -0
- package/dist/reactive/core.d.ts.map +1 -1
- package/dist/reactive/effect.d.ts.map +1 -1
- package/dist/reactive/index.d.ts +2 -2
- package/dist/reactive/index.d.ts.map +1 -1
- package/dist/reactive/internals.d.ts +6 -0
- package/dist/reactive/internals.d.ts.map +1 -1
- package/dist/reactive/signal.d.ts +2 -0
- package/dist/reactive/signal.d.ts.map +1 -1
- package/dist/reactive-BDya-ia8.js +253 -0
- package/dist/reactive-BDya-ia8.js.map +1 -0
- package/dist/reactive.es.mjs +18 -34
- package/dist/router-CijiICxt.js +188 -0
- package/dist/router-CijiICxt.js.map +1 -0
- package/dist/router.es.mjs +11 -200
- package/dist/sanitize-jyJ2ryE2.js +302 -0
- package/dist/sanitize-jyJ2ryE2.js.map +1 -0
- package/dist/security/constants.d.ts.map +1 -1
- package/dist/security/sanitize-core.d.ts.map +1 -1
- package/dist/security.es.mjs +10 -56
- package/dist/store-CPK9E62U.js +262 -0
- package/dist/store-CPK9E62U.js.map +1 -0
- package/dist/store.es.mjs +12 -25
- package/dist/view/evaluate.d.ts.map +1 -1
- package/dist/view-Cdi0g-qo.js +396 -0
- package/dist/view-Cdi0g-qo.js.map +1 -0
- package/dist/view.es.mjs +10 -424
- package/package.json +136 -132
- package/src/component/component.ts +319 -289
- package/src/component/index.ts +42 -40
- package/src/component/library.ts +504 -0
- package/src/component/types.ts +91 -85
- package/src/core/collection.ts +44 -4
- package/src/core/element.ts +33 -5
- package/src/core/index.ts +1 -0
- package/src/core/utils/function.ts +56 -15
- package/src/full.ts +223 -187
- package/src/motion/transition.ts +97 -51
- package/src/motion/types.ts +208 -198
- package/src/platform/announcer.ts +208 -0
- package/src/platform/config.ts +163 -0
- package/src/platform/cookies.ts +165 -0
- package/src/platform/index.ts +39 -18
- package/src/platform/meta.ts +168 -0
- package/src/platform/storage.ts +8 -1
- package/src/reactive/async-data.ts +486 -0
- package/src/reactive/core.ts +21 -0
- package/src/reactive/effect.ts +18 -7
- package/src/reactive/index.ts +37 -23
- package/src/reactive/internals.ts +18 -1
- package/src/reactive/signal.ts +29 -20
- package/src/security/constants.ts +211 -209
- package/src/security/sanitize-core.ts +22 -1
- package/src/view/evaluate.ts +29 -13
- package/dist/batch-4LAvfLE7.js +0 -13
- package/dist/batch-4LAvfLE7.js.map +0 -1
- package/dist/component.es.mjs.map +0 -1
- package/dist/core-COenAZjD.js +0 -145
- package/dist/core-COenAZjD.js.map +0 -1
- package/dist/core.es.mjs.map +0 -1
- package/dist/full.es.mjs.map +0 -1
- package/dist/index.es.mjs.map +0 -1
- package/dist/motion.es.mjs.map +0 -1
- package/dist/persisted-Dz_ryNuC.js +0 -278
- package/dist/persisted-Dz_ryNuC.js.map +0 -1
- package/dist/platform.es.mjs.map +0 -1
- package/dist/reactive.es.mjs.map +0 -1
- package/dist/router.es.mjs.map +0 -1
- package/dist/sanitize-1FBEPAFH.js +0 -272
- package/dist/sanitize-1FBEPAFH.js.map +0 -1
- package/dist/security.es.mjs.map +0 -1
- package/dist/store.es.mjs.map +0 -1
- package/dist/type-guards-DRma3-Kc.js +0 -16
- package/dist/type-guards-DRma3-Kc.js.map +0 -1
- package/dist/untrack-BuEQKH7_.js +0 -6
- package/dist/untrack-BuEQKH7_.js.map +0 -1
- package/dist/view.es.mjs.map +0 -1
- package/dist/watch-CXyaBC_9.js +0 -58
- package/dist/watch-CXyaBC_9.js.map +0 -1
package/src/component/types.ts
CHANGED
|
@@ -1,85 +1,91 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Component types and render context definitions.
|
|
3
|
-
*
|
|
4
|
-
* @module bquery/component
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Defines a single prop's type and configuration.
|
|
9
|
-
*
|
|
10
|
-
* @template T - The TypeScript type of the prop value
|
|
11
|
-
*
|
|
12
|
-
* @example
|
|
13
|
-
* ```ts
|
|
14
|
-
* const myProp: PropDefinition<number> = {
|
|
15
|
-
* type: Number,
|
|
16
|
-
* required: false,
|
|
17
|
-
* default: 0,
|
|
18
|
-
* };
|
|
19
|
-
* ```
|
|
20
|
-
*/
|
|
21
|
-
export type PropDefinition<T = unknown> = {
|
|
22
|
-
/** Constructor or converter function for the prop type */
|
|
23
|
-
type:
|
|
24
|
-
| StringConstructor
|
|
25
|
-
| NumberConstructor
|
|
26
|
-
| BooleanConstructor
|
|
27
|
-
| ObjectConstructor
|
|
28
|
-
| ArrayConstructor
|
|
29
|
-
| { new (value: unknown): T }
|
|
30
|
-
| ((value: unknown) => T);
|
|
31
|
-
/** Whether the prop must be provided */
|
|
32
|
-
required?: boolean;
|
|
33
|
-
/** Default value when prop is not provided */
|
|
34
|
-
default?: T;
|
|
35
|
-
/** Optional validator function to validate prop values */
|
|
36
|
-
validator?: (value: T) => boolean;
|
|
37
|
-
/**
|
|
38
|
-
* Explicitly control whether to invoke `type` with `new` (constructor) or as a plain function.
|
|
39
|
-
* - `true`: Always use `new type(value)` (for class constructors, Date, etc.)
|
|
40
|
-
* - `false`: Always call `type(value)` (for converter functions)
|
|
41
|
-
* - `undefined` (default): Auto-detect based on heuristics with fallback
|
|
42
|
-
*/
|
|
43
|
-
construct?: boolean;
|
|
44
|
-
};
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* Render context passed into a component render function.
|
|
48
|
-
*/
|
|
49
|
-
export type ComponentRenderContext<TProps extends Record<string, unknown>> = {
|
|
50
|
-
/** Typed props object populated from attributes */
|
|
51
|
-
props: TProps;
|
|
52
|
-
/** Internal mutable state object */
|
|
53
|
-
state: Record<string, unknown>;
|
|
54
|
-
/** Emit a custom event from the component */
|
|
55
|
-
emit: (event: string, detail?: unknown) => void;
|
|
56
|
-
};
|
|
57
|
-
|
|
58
|
-
/**
|
|
59
|
-
* Complete component definition including props, state, styles, and lifecycle.
|
|
60
|
-
*
|
|
61
|
-
* @template TProps - Type of the component's props
|
|
62
|
-
*/
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
/**
|
|
72
|
-
|
|
73
|
-
/**
|
|
74
|
-
|
|
75
|
-
/**
|
|
76
|
-
|
|
77
|
-
/** Lifecycle hook called before
|
|
78
|
-
|
|
79
|
-
/** Lifecycle hook called
|
|
80
|
-
|
|
81
|
-
/**
|
|
82
|
-
|
|
83
|
-
/**
|
|
84
|
-
|
|
85
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Component types and render context definitions.
|
|
3
|
+
*
|
|
4
|
+
* @module bquery/component
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Defines a single prop's type and configuration.
|
|
9
|
+
*
|
|
10
|
+
* @template T - The TypeScript type of the prop value
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```ts
|
|
14
|
+
* const myProp: PropDefinition<number> = {
|
|
15
|
+
* type: Number,
|
|
16
|
+
* required: false,
|
|
17
|
+
* default: 0,
|
|
18
|
+
* };
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
export type PropDefinition<T = unknown> = {
|
|
22
|
+
/** Constructor or converter function for the prop type */
|
|
23
|
+
type:
|
|
24
|
+
| StringConstructor
|
|
25
|
+
| NumberConstructor
|
|
26
|
+
| BooleanConstructor
|
|
27
|
+
| ObjectConstructor
|
|
28
|
+
| ArrayConstructor
|
|
29
|
+
| { new (value: unknown): T }
|
|
30
|
+
| ((value: unknown) => T);
|
|
31
|
+
/** Whether the prop must be provided */
|
|
32
|
+
required?: boolean;
|
|
33
|
+
/** Default value when prop is not provided */
|
|
34
|
+
default?: T;
|
|
35
|
+
/** Optional validator function to validate prop values */
|
|
36
|
+
validator?: (value: T) => boolean;
|
|
37
|
+
/**
|
|
38
|
+
* Explicitly control whether to invoke `type` with `new` (constructor) or as a plain function.
|
|
39
|
+
* - `true`: Always use `new type(value)` (for class constructors, Date, etc.)
|
|
40
|
+
* - `false`: Always call `type(value)` (for converter functions)
|
|
41
|
+
* - `undefined` (default): Auto-detect based on heuristics with fallback
|
|
42
|
+
*/
|
|
43
|
+
construct?: boolean;
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Render context passed into a component render function.
|
|
48
|
+
*/
|
|
49
|
+
export type ComponentRenderContext<TProps extends Record<string, unknown>> = {
|
|
50
|
+
/** Typed props object populated from attributes */
|
|
51
|
+
props: TProps;
|
|
52
|
+
/** Internal mutable state object */
|
|
53
|
+
state: Record<string, unknown>;
|
|
54
|
+
/** Emit a custom event from the component */
|
|
55
|
+
emit: (event: string, detail?: unknown) => void;
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Complete component definition including props, state, styles, and lifecycle.
|
|
60
|
+
*
|
|
61
|
+
* @template TProps - Type of the component's props
|
|
62
|
+
*/
|
|
63
|
+
type ComponentHook<TResult = void> = ((this: HTMLElement) => TResult) | (() => TResult);
|
|
64
|
+
type ComponentHookWithProps<TProps extends Record<string, unknown>, TResult = void> =
|
|
65
|
+
| ((this: HTMLElement, props: TProps) => TResult)
|
|
66
|
+
| ((props: TProps) => TResult);
|
|
67
|
+
type ComponentErrorHook = ((this: HTMLElement, error: Error) => void) | ((error: Error) => void);
|
|
68
|
+
|
|
69
|
+
export type ComponentDefinition<TProps extends Record<string, unknown> = Record<string, unknown>> =
|
|
70
|
+
{
|
|
71
|
+
/** Prop definitions with types and defaults */
|
|
72
|
+
props?: Record<keyof TProps, PropDefinition>;
|
|
73
|
+
/** Initial internal state */
|
|
74
|
+
state?: Record<string, unknown>;
|
|
75
|
+
/** CSS styles scoped to the component's shadow DOM */
|
|
76
|
+
styles?: string;
|
|
77
|
+
/** Lifecycle hook called before the component mounts (before first render) */
|
|
78
|
+
beforeMount?: ComponentHook;
|
|
79
|
+
/** Lifecycle hook called when component is added to DOM */
|
|
80
|
+
connected?: ComponentHook;
|
|
81
|
+
/** Lifecycle hook called when component is removed from DOM */
|
|
82
|
+
disconnected?: ComponentHook;
|
|
83
|
+
/** Lifecycle hook called before an update render; return false to prevent */
|
|
84
|
+
beforeUpdate?: ComponentHookWithProps<TProps, boolean | void>;
|
|
85
|
+
/** Lifecycle hook called after reactive updates trigger a render */
|
|
86
|
+
updated?: ComponentHook;
|
|
87
|
+
/** Error handler for errors during rendering or lifecycle */
|
|
88
|
+
onError?: ComponentErrorHook;
|
|
89
|
+
/** Render function returning HTML string */
|
|
90
|
+
render: (context: ComponentRenderContext<TProps>) => string;
|
|
91
|
+
};
|
package/src/core/collection.ts
CHANGED
|
@@ -272,20 +272,33 @@ export class BQueryCollection {
|
|
|
272
272
|
}
|
|
273
273
|
|
|
274
274
|
/**
|
|
275
|
-
*
|
|
275
|
+
* Gets or sets CSS styles on all elements.
|
|
276
|
+
* When getting, returns the computed style value from the first element.
|
|
276
277
|
*
|
|
277
278
|
* @param property - Property name or object of properties
|
|
278
279
|
* @param value - Value when setting single property
|
|
279
|
-
* @returns The instance
|
|
280
|
+
* @returns The computed style value when getting, instance when setting
|
|
280
281
|
*/
|
|
281
|
-
css(property: string
|
|
282
|
+
css(property: string): string;
|
|
283
|
+
css(property: string, value: string): this;
|
|
284
|
+
css(property: Record<string, string>): this;
|
|
285
|
+
css(property: string | Record<string, string>, value?: string): string | this {
|
|
282
286
|
if (typeof property === 'string') {
|
|
283
287
|
if (value !== undefined) {
|
|
284
288
|
applyAll(this.elements, (el) => {
|
|
285
289
|
(el as HTMLElement).style.setProperty(property, value);
|
|
286
290
|
});
|
|
291
|
+
return this;
|
|
292
|
+
}
|
|
293
|
+
const first = this.first();
|
|
294
|
+
if (!first) {
|
|
295
|
+
return '';
|
|
287
296
|
}
|
|
288
|
-
|
|
297
|
+
const view = first.ownerDocument?.defaultView;
|
|
298
|
+
if (!view || typeof view.getComputedStyle !== 'function') {
|
|
299
|
+
return '';
|
|
300
|
+
}
|
|
301
|
+
return view.getComputedStyle(first).getPropertyValue(property);
|
|
289
302
|
}
|
|
290
303
|
|
|
291
304
|
applyAll(this.elements, (el) => {
|
|
@@ -545,6 +558,33 @@ export class BQueryCollection {
|
|
|
545
558
|
return this;
|
|
546
559
|
}
|
|
547
560
|
|
|
561
|
+
/**
|
|
562
|
+
* Finds all descendant elements matching the selector across all elements
|
|
563
|
+
* in the collection. Returns a new BQueryCollection with the results.
|
|
564
|
+
*
|
|
565
|
+
* @param selector - CSS selector to match
|
|
566
|
+
* @returns A new BQueryCollection with all matching descendants
|
|
567
|
+
*
|
|
568
|
+
* @example
|
|
569
|
+
* ```ts
|
|
570
|
+
* $$('.container').find('.item').addClass('highlight');
|
|
571
|
+
* ```
|
|
572
|
+
*/
|
|
573
|
+
find(selector: string): BQueryCollection {
|
|
574
|
+
const seen = new Set<Element>();
|
|
575
|
+
const results: Element[] = [];
|
|
576
|
+
for (const el of this.elements) {
|
|
577
|
+
const found = el.querySelectorAll(selector);
|
|
578
|
+
for (let i = 0; i < found.length; i++) {
|
|
579
|
+
if (!seen.has(found[i])) {
|
|
580
|
+
seen.add(found[i]);
|
|
581
|
+
results.push(found[i]);
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
return new BQueryCollection(results);
|
|
586
|
+
}
|
|
587
|
+
|
|
548
588
|
/**
|
|
549
589
|
* Removes all elements from the DOM.
|
|
550
590
|
*
|
package/src/core/element.ts
CHANGED
|
@@ -165,23 +165,34 @@ export class BQueryElement {
|
|
|
165
165
|
*
|
|
166
166
|
* @param property - A CSS property name or an object of property-value pairs
|
|
167
167
|
* @param value - The value when setting a single property
|
|
168
|
-
* @returns The instance for method chaining
|
|
168
|
+
* @returns The computed style value when getting a single property, or the instance for method chaining when setting
|
|
169
169
|
*
|
|
170
170
|
* @example
|
|
171
171
|
* ```ts
|
|
172
|
-
* //
|
|
172
|
+
* // Get a computed style value
|
|
173
|
+
* const color = $('#box').css('color');
|
|
174
|
+
*
|
|
175
|
+
* // Set a single property
|
|
173
176
|
* $('#box').css('color', 'red');
|
|
174
177
|
*
|
|
175
|
-
* //
|
|
178
|
+
* // Set multiple properties
|
|
176
179
|
* $('#box').css({ color: 'red', 'font-size': '16px' });
|
|
177
180
|
* ```
|
|
178
181
|
*/
|
|
179
|
-
css(property: string
|
|
182
|
+
css(property: string): string;
|
|
183
|
+
css(property: string, value: string): this;
|
|
184
|
+
css(property: Record<string, string>): this;
|
|
185
|
+
css(property: string | Record<string, string>, value?: string): string | this {
|
|
180
186
|
if (typeof property === 'string') {
|
|
181
187
|
if (value !== undefined) {
|
|
182
188
|
(this.element as HTMLElement).style.setProperty(property, value);
|
|
189
|
+
return this;
|
|
183
190
|
}
|
|
184
|
-
|
|
191
|
+
const view = this.element.ownerDocument?.defaultView;
|
|
192
|
+
if (!view || typeof view.getComputedStyle !== 'function') {
|
|
193
|
+
return '';
|
|
194
|
+
}
|
|
195
|
+
return view.getComputedStyle(this.element).getPropertyValue(property);
|
|
185
196
|
}
|
|
186
197
|
|
|
187
198
|
for (const [key, val] of Object.entries(property)) {
|
|
@@ -563,6 +574,23 @@ export class BQueryElement {
|
|
|
563
574
|
return this.element.matches(selector);
|
|
564
575
|
}
|
|
565
576
|
|
|
577
|
+
/**
|
|
578
|
+
* Alias for `matches()`. Checks if the element matches a CSS selector.
|
|
579
|
+
*
|
|
580
|
+
* @param selector - CSS selector to match against
|
|
581
|
+
* @returns True if the element matches the selector
|
|
582
|
+
*
|
|
583
|
+
* @example
|
|
584
|
+
* ```ts
|
|
585
|
+
* if ($('#el').is('.active')) {
|
|
586
|
+
* console.log('Element is active');
|
|
587
|
+
* }
|
|
588
|
+
* ```
|
|
589
|
+
*/
|
|
590
|
+
is(selector: string): boolean {
|
|
591
|
+
return this.matches(selector);
|
|
592
|
+
}
|
|
593
|
+
|
|
566
594
|
/**
|
|
567
595
|
* Checks if the element has a specific class.
|
|
568
596
|
*
|
package/src/core/index.ts
CHANGED
|
@@ -4,6 +4,20 @@
|
|
|
4
4
|
* @module bquery/core/utils/function
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
+
/** A debounced function with a cancel method to clear the pending timeout. */
|
|
8
|
+
export interface DebouncedFn<TArgs extends unknown[]> {
|
|
9
|
+
(...args: TArgs): void;
|
|
10
|
+
/** Cancels the pending debounced invocation. */
|
|
11
|
+
cancel(): void;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/** A throttled function with a cancel method to reset the throttle timer. */
|
|
15
|
+
export interface ThrottledFn<TArgs extends unknown[]> {
|
|
16
|
+
(...args: TArgs): void;
|
|
17
|
+
/** Resets the throttle timer, allowing the next call to execute immediately. */
|
|
18
|
+
cancel(): void;
|
|
19
|
+
}
|
|
20
|
+
|
|
7
21
|
/**
|
|
8
22
|
* Creates a debounced function that delays execution until after
|
|
9
23
|
* the specified delay has elapsed since the last call.
|
|
@@ -11,7 +25,7 @@
|
|
|
11
25
|
* @template TArgs - The argument types of the function
|
|
12
26
|
* @param fn - The function to debounce
|
|
13
27
|
* @param delayMs - Delay in milliseconds
|
|
14
|
-
* @returns A debounced version of the function
|
|
28
|
+
* @returns A debounced version of the function with a `cancel()` method
|
|
15
29
|
*
|
|
16
30
|
* @example
|
|
17
31
|
* ```ts
|
|
@@ -22,19 +36,36 @@
|
|
|
22
36
|
* search('h');
|
|
23
37
|
* search('he');
|
|
24
38
|
* search('hello'); // Only this call executes after 300ms
|
|
39
|
+
*
|
|
40
|
+
* search('cancel me');
|
|
41
|
+
* search.cancel(); // Cancels the pending invocation
|
|
25
42
|
* ```
|
|
26
43
|
*/
|
|
27
44
|
export function debounce<TArgs extends unknown[]>(
|
|
28
45
|
fn: (...args: TArgs) => void,
|
|
29
46
|
delayMs: number
|
|
30
|
-
):
|
|
47
|
+
): DebouncedFn<TArgs> {
|
|
31
48
|
let timeoutId: ReturnType<typeof setTimeout> | undefined;
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
49
|
+
const debounced: DebouncedFn<TArgs> = Object.assign(
|
|
50
|
+
(...args: TArgs) => {
|
|
51
|
+
if (timeoutId !== undefined) {
|
|
52
|
+
clearTimeout(timeoutId);
|
|
53
|
+
}
|
|
54
|
+
timeoutId = setTimeout(() => {
|
|
55
|
+
timeoutId = undefined;
|
|
56
|
+
fn(...args);
|
|
57
|
+
}, delayMs);
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
cancel: () => {
|
|
61
|
+
if (timeoutId !== undefined) {
|
|
62
|
+
clearTimeout(timeoutId);
|
|
63
|
+
timeoutId = undefined;
|
|
64
|
+
}
|
|
65
|
+
},
|
|
35
66
|
}
|
|
36
|
-
|
|
37
|
-
|
|
67
|
+
);
|
|
68
|
+
return debounced;
|
|
38
69
|
}
|
|
39
70
|
|
|
40
71
|
/**
|
|
@@ -43,7 +74,7 @@ export function debounce<TArgs extends unknown[]>(
|
|
|
43
74
|
* @template TArgs - The argument types of the function
|
|
44
75
|
* @param fn - The function to throttle
|
|
45
76
|
* @param intervalMs - Minimum interval between calls in milliseconds
|
|
46
|
-
* @returns A throttled version of the function
|
|
77
|
+
* @returns A throttled version of the function with a `cancel()` method
|
|
47
78
|
*
|
|
48
79
|
* @example
|
|
49
80
|
* ```ts
|
|
@@ -52,20 +83,30 @@ export function debounce<TArgs extends unknown[]>(
|
|
|
52
83
|
* }, 100);
|
|
53
84
|
*
|
|
54
85
|
* window.addEventListener('scroll', handleScroll);
|
|
86
|
+
*
|
|
87
|
+
* handleScroll.cancel(); // Resets throttle, next call executes immediately
|
|
55
88
|
* ```
|
|
56
89
|
*/
|
|
57
90
|
export function throttle<TArgs extends unknown[]>(
|
|
58
91
|
fn: (...args: TArgs) => void,
|
|
59
92
|
intervalMs: number
|
|
60
|
-
):
|
|
93
|
+
): ThrottledFn<TArgs> {
|
|
61
94
|
let lastRun = 0;
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
lastRun
|
|
66
|
-
|
|
95
|
+
const throttled: ThrottledFn<TArgs> = Object.assign(
|
|
96
|
+
(...args: TArgs) => {
|
|
97
|
+
const now = Date.now();
|
|
98
|
+
if (now - lastRun >= intervalMs) {
|
|
99
|
+
lastRun = now;
|
|
100
|
+
fn(...args);
|
|
101
|
+
}
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
cancel: () => {
|
|
105
|
+
lastRun = 0;
|
|
106
|
+
},
|
|
67
107
|
}
|
|
68
|
-
|
|
108
|
+
);
|
|
109
|
+
return throttled;
|
|
69
110
|
}
|
|
70
111
|
|
|
71
112
|
/**
|