@react-hive/honey-utils 3.13.1 → 3.15.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.
Files changed (36) hide show
  1. package/README.md +8 -5
  2. package/dist/README.md +8 -5
  3. package/dist/a11y/focus/is-html-element-focusable.d.ts +1 -1
  4. package/dist/a11y/focus/move-focus-within-container.d.ts +1 -1
  5. package/dist/{async.d.ts → async/async.d.ts} +1 -1
  6. package/dist/async/delay.d.ts +26 -0
  7. package/dist/async/index.d.ts +4 -0
  8. package/dist/async/retry.d.ts +72 -0
  9. package/dist/async/timeout.d.ts +23 -0
  10. package/dist/file.d.ts +1 -17
  11. package/dist/function/function.d.ts +47 -0
  12. package/dist/function/index.d.ts +2 -0
  13. package/dist/function/invoke-if-function.d.ts +13 -0
  14. package/dist/geometry/apply-inertia-step.d.ts +134 -0
  15. package/dist/geometry/index.d.ts +2 -0
  16. package/dist/geometry/resolve-bounded-delta.d.ts +79 -0
  17. package/dist/index.cjs +1 -1
  18. package/dist/index.cjs.map +1 -1
  19. package/dist/index.dev.cjs +719 -273
  20. package/dist/index.dev.cjs.map +1 -1
  21. package/dist/index.mjs +1 -1
  22. package/dist/index.mjs.map +1 -1
  23. package/dist/math/hash-string.d.ts +25 -0
  24. package/dist/math/index.d.ts +2 -0
  25. package/dist/string/camel-to-dash-case.d.ts +20 -0
  26. package/dist/string/camel-to-words.d.ts +19 -0
  27. package/dist/string/index.d.ts +7 -0
  28. package/dist/string/is-nil-or-empty-string.d.ts +13 -0
  29. package/dist/string/is-string.d.ts +8 -0
  30. package/dist/string/parse-file-name.d.ts +16 -0
  31. package/dist/string/split-string-into-words.d.ts +8 -0
  32. package/dist/string/to-kebab-case.d.ts +18 -0
  33. package/package.json +1 -1
  34. package/dist/function.d.ts +0 -181
  35. package/dist/string.d.ts +0 -111
  36. /package/dist/{math.d.ts → math/math.d.ts} +0 -0
package/README.md CHANGED
@@ -553,7 +553,7 @@ function divide(a: number, b: number): number {
553
553
  - `toKebabCase(input: string): string` - Converts a string to kebab-case.
554
554
  - `camelToDashCase(input: string): string` - Converts camelCase to dash-case.
555
555
  - `splitStringIntoWords(input: string): string[]` - Splits a string into an array of words.
556
- - `hashString(input: string): string` - Generates a short hash from a string.
556
+ - `parseFileName(fileName: string): [baseName: string, extension: string]` - Splits a file name into its base name and extension using the last `.` as the separator. Handles edge cases such as hidden files (`.gitignore`), multi-dot names (`archive.tar.gz`), and names ending with a dot (`"file."`). The extension is returned in lowercase.
557
557
 
558
558
  ### Object Utilities
559
559
 
@@ -583,9 +583,6 @@ function divide(a: number, b: number): number {
583
583
  - `noop(): void` - A no-operation function.
584
584
  - `not<Args extends any[]>(fn: (...args: Args) => any): (...args: Args) => boolean` - Creates a new function that negates the result of the given predicate function. Useful for logical inversions, e.g., turning `isEven` into `isOdd`.
585
585
  - `invokeIfFunction<Args extends any[], Result>(input: ((...args: Args) => Result) | Result, ...args: Args): Result` - Invokes the input if it's a function, otherwise returns it as-is.
586
- - `delay(delayMs: number): Promise<void>` - Creates a promise that resolves after the specified delay in milliseconds.
587
- - `timeout<T>(promise: Promise<T>, timeoutMs: number, message?: string): Promise<T>` - Wraps a promise with a timeout. If the promise does not settle within the given duration, it rejects with a timeout error.
588
- - `retry<Task, TaskResult>(task: Task, options?: RetryOptions): Function` - Wraps an asynchronous function with retry logic, with configurable max attempts, delay between retries, exponential backoff, and retry callbacks.
589
586
  - `once<T extends (...args: any[]) => any>(fn: T): T` - Wraps a function so it can only be executed once. The result of the first invocation is cached and returned for all subsequent calls. Preserves both the original function’s parameter types and `this` binding.
590
587
 
591
588
  ### Type Guards
@@ -620,6 +617,7 @@ function divide(a: number, b: number): number {
620
617
  - `calculateEuclideanDistance(startX: number, startY: number, endX: number, endY: number): number` - Calculates the Euclidean distance between two points.
621
618
  - `calculateMovingSpeed(distance: number, elapsedTime: number): number` - Calculates moving speed.
622
619
  - `calculatePercentage(value: number, percentage: number): number` - Calculates the specified percentage of a value.
620
+ - `hashString(input: string): string` - Generates a short hash from a string.
623
621
 
624
622
  ### ENV
625
623
 
@@ -634,6 +632,9 @@ function divide(a: number, b: number): number {
634
632
 
635
633
  ---
636
634
 
635
+ - `resolveBoundedDelta(options: ResolveBoundedDeltaOptions): Nullable<number>` – Resolves the next numeric value by consuming a delta within fixed bounds. Prevents overshoot, partially consumes deltas at boundaries, and returns `null` when movement in the given direction is no longer possible. Useful for drag constraints, sliders, synthetic scrolling, and inertia systems.
636
+ - `applyInertiaStep(options: ApplyInertiaStepOptions): Nullable<{ value: number; velocity: number }>` – Applies a single inertia step on top of bounded delta resolution using velocity integration and exponential friction. Intended to be called from an animation loop (e.g. `requestAnimationFrame`) to implement momentum-based scrolling, sliders, or carousels. Returns updated value and velocity, or `null` when inertia has completed.
637
+
637
638
  #### Layout
638
639
 
639
640
  - `calculateCenterOffset(options: CalculateCenterOffsetOptions): number` - Calculates a clamped offset value that centers an element within a container along a single axis. Returns a negative value suitable for use in a CSS `translate` transform, or `0` when no overflow exists.
@@ -668,7 +669,6 @@ function divide(a: number, b: number): number {
668
669
  ---
669
670
 
670
671
  - `isFile(value: unknown): value is File` - Checks if a value is a `File`.
671
- - `parseFileName(fileName: string): [baseName: string, extension: string]` - Splits a file name into its base name and extension using the last `.` as the separator. Handles edge cases such as hidden files (`.gitignore`), multi-dot names (`archive.tar.gz`), and names ending with a dot (`"file."`). The extension is returned in lowercase.
672
672
  - `fileListToFiles(fileList: FileList | null): File[]` - Converts a `FileList` object (such as the one returned from an `<input type="file">`) into a standard array of `File` objects. Returns an empty array when the input is `null`.
673
673
  - `blobToFile(blob: Blob, fileName: string): File` - Converts a Blob object into a File object with the specified name.
674
674
  - `traverseFileSystemDirectory(directoryEntry: FileSystemDirectoryEntry, options?: TraverseDirectoryOptions): Promise<File[]>` — Recursively scans a directory using the File System API and returns all nested files as `File` objects. Supports skipping system files.
@@ -687,6 +687,9 @@ function divide(a: number, b: number): number {
687
687
  - `someAsync<Item>(array: Item[], predicate): Promise<boolean>` - Returns `true` if **any** item in the array passes the async predicate.
688
688
  - `everyAsync<Item>(array: Item[], predicate): Promise<boolean>` - Returns `true` if **all** items in the array pass the async predicate.
689
689
  - `findAsync<Item>(array: Item[], predicate): Promise<Nullable<Item>>` - Returns the first array item that passes the async predicate, or `null` if no match is found.
690
+ - `delay(delayMs: number): Promise<void>` - Creates a promise that resolves after the specified delay in milliseconds.
691
+ - `timeout<T>(promise: Promise<T>, timeoutMs: number, message?: string): Promise<T>` - Wraps a promise with a timeout. If the promise does not settle within the given duration, it rejects with a timeout error.
692
+ - `retry<Task, TaskResult>(task: Task, options?: RetryOptions): Function` - Wraps an asynchronous function with retry logic, with configurable max attempts, delay between retries, exponential backoff, and retry callbacks.
690
693
 
691
694
  ### Intersection Utilities
692
695
 
package/dist/README.md CHANGED
@@ -553,7 +553,7 @@ function divide(a: number, b: number): number {
553
553
  - `toKebabCase(input: string): string` - Converts a string to kebab-case.
554
554
  - `camelToDashCase(input: string): string` - Converts camelCase to dash-case.
555
555
  - `splitStringIntoWords(input: string): string[]` - Splits a string into an array of words.
556
- - `hashString(input: string): string` - Generates a short hash from a string.
556
+ - `parseFileName(fileName: string): [baseName: string, extension: string]` - Splits a file name into its base name and extension using the last `.` as the separator. Handles edge cases such as hidden files (`.gitignore`), multi-dot names (`archive.tar.gz`), and names ending with a dot (`"file."`). The extension is returned in lowercase.
557
557
 
558
558
  ### Object Utilities
559
559
 
@@ -583,9 +583,6 @@ function divide(a: number, b: number): number {
583
583
  - `noop(): void` - A no-operation function.
584
584
  - `not<Args extends any[]>(fn: (...args: Args) => any): (...args: Args) => boolean` - Creates a new function that negates the result of the given predicate function. Useful for logical inversions, e.g., turning `isEven` into `isOdd`.
585
585
  - `invokeIfFunction<Args extends any[], Result>(input: ((...args: Args) => Result) | Result, ...args: Args): Result` - Invokes the input if it's a function, otherwise returns it as-is.
586
- - `delay(delayMs: number): Promise<void>` - Creates a promise that resolves after the specified delay in milliseconds.
587
- - `timeout<T>(promise: Promise<T>, timeoutMs: number, message?: string): Promise<T>` - Wraps a promise with a timeout. If the promise does not settle within the given duration, it rejects with a timeout error.
588
- - `retry<Task, TaskResult>(task: Task, options?: RetryOptions): Function` - Wraps an asynchronous function with retry logic, with configurable max attempts, delay between retries, exponential backoff, and retry callbacks.
589
586
  - `once<T extends (...args: any[]) => any>(fn: T): T` - Wraps a function so it can only be executed once. The result of the first invocation is cached and returned for all subsequent calls. Preserves both the original function’s parameter types and `this` binding.
590
587
 
591
588
  ### Type Guards
@@ -620,6 +617,7 @@ function divide(a: number, b: number): number {
620
617
  - `calculateEuclideanDistance(startX: number, startY: number, endX: number, endY: number): number` - Calculates the Euclidean distance between two points.
621
618
  - `calculateMovingSpeed(distance: number, elapsedTime: number): number` - Calculates moving speed.
622
619
  - `calculatePercentage(value: number, percentage: number): number` - Calculates the specified percentage of a value.
620
+ - `hashString(input: string): string` - Generates a short hash from a string.
623
621
 
624
622
  ### ENV
625
623
 
@@ -634,6 +632,9 @@ function divide(a: number, b: number): number {
634
632
 
635
633
  ---
636
634
 
635
+ - `resolveBoundedDelta(options: ResolveBoundedDeltaOptions): Nullable<number>` – Resolves the next numeric value by consuming a delta within fixed bounds. Prevents overshoot, partially consumes deltas at boundaries, and returns `null` when movement in the given direction is no longer possible. Useful for drag constraints, sliders, synthetic scrolling, and inertia systems.
636
+ - `applyInertiaStep(options: ApplyInertiaStepOptions): Nullable<{ value: number; velocity: number }>` – Applies a single inertia step on top of bounded delta resolution using velocity integration and exponential friction. Intended to be called from an animation loop (e.g. `requestAnimationFrame`) to implement momentum-based scrolling, sliders, or carousels. Returns updated value and velocity, or `null` when inertia has completed.
637
+
637
638
  #### Layout
638
639
 
639
640
  - `calculateCenterOffset(options: CalculateCenterOffsetOptions): number` - Calculates a clamped offset value that centers an element within a container along a single axis. Returns a negative value suitable for use in a CSS `translate` transform, or `0` when no overflow exists.
@@ -668,7 +669,6 @@ function divide(a: number, b: number): number {
668
669
  ---
669
670
 
670
671
  - `isFile(value: unknown): value is File` - Checks if a value is a `File`.
671
- - `parseFileName(fileName: string): [baseName: string, extension: string]` - Splits a file name into its base name and extension using the last `.` as the separator. Handles edge cases such as hidden files (`.gitignore`), multi-dot names (`archive.tar.gz`), and names ending with a dot (`"file."`). The extension is returned in lowercase.
672
672
  - `fileListToFiles(fileList: FileList | null): File[]` - Converts a `FileList` object (such as the one returned from an `<input type="file">`) into a standard array of `File` objects. Returns an empty array when the input is `null`.
673
673
  - `blobToFile(blob: Blob, fileName: string): File` - Converts a Blob object into a File object with the specified name.
674
674
  - `traverseFileSystemDirectory(directoryEntry: FileSystemDirectoryEntry, options?: TraverseDirectoryOptions): Promise<File[]>` — Recursively scans a directory using the File System API and returns all nested files as `File` objects. Supports skipping system files.
@@ -687,6 +687,9 @@ function divide(a: number, b: number): number {
687
687
  - `someAsync<Item>(array: Item[], predicate): Promise<boolean>` - Returns `true` if **any** item in the array passes the async predicate.
688
688
  - `everyAsync<Item>(array: Item[], predicate): Promise<boolean>` - Returns `true` if **all** items in the array pass the async predicate.
689
689
  - `findAsync<Item>(array: Item[], predicate): Promise<Nullable<Item>>` - Returns the first array item that passes the async predicate, or `null` if no match is found.
690
+ - `delay(delayMs: number): Promise<void>` - Creates a promise that resolves after the specified delay in milliseconds.
691
+ - `timeout<T>(promise: Promise<T>, timeoutMs: number, message?: string): Promise<T>` - Wraps a promise with a timeout. If the promise does not settle within the given duration, it rejects with a timeout error.
692
+ - `retry<Task, TaskResult>(task: Task, options?: RetryOptions): Function` - Wraps an asynchronous function with retry logic, with configurable max attempts, delay between retries, exponential backoff, and retry callbacks.
690
693
 
691
694
  ### Intersection Utilities
692
695
 
@@ -1,4 +1,4 @@
1
- import type { Nullable } from '../../types';
1
+ import type { Nullable } from '~/types';
2
2
  export declare const FOCUSABLE_HTML_TAGS: string[];
3
3
  /**
4
4
  * Determines whether an HTMLElement is focusable under standard browser rules.
@@ -1,4 +1,4 @@
1
- import type { Nullable } from '../../types';
1
+ import type { Nullable } from '~/types';
2
2
  export type FocusMoveDirection = 'next' | 'previous';
3
3
  export interface MoveFocusWithinContainerOptions {
4
4
  /**
@@ -1,4 +1,4 @@
1
- import type { Nullable } from './types';
1
+ import type { Nullable } from '~/types';
2
2
  /**
3
3
  * Checks if a value is a Promise.
4
4
  *
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Creates a promise that resolves after the specified delay.
3
+ *
4
+ * Useful for creating artificial delays, implementing timeouts, or spacing operations.
5
+ *
6
+ * @param delayMs - The delay in milliseconds.
7
+ *
8
+ * @returns A promise that resolves after the specified delay.
9
+ *
10
+ * @example
11
+ * ```ts
12
+ * // Wait for 1 second
13
+ * await delay(1000);
14
+ * console.log('This logs after 1 second');
15
+ *
16
+ * // Use with other async operations
17
+ * const fetchWithTimeout = async () => {
18
+ * const timeoutPromise = delay(5000).then(() => {
19
+ * throw new Error('Request timed out');
20
+ * });
21
+ *
22
+ * return Promise.race([fetchData(), timeoutPromise]);
23
+ * }
24
+ * ```
25
+ */
26
+ export declare const delay: (delayMs: number) => Promise<void>;
@@ -0,0 +1,4 @@
1
+ export * from './async';
2
+ export * from './delay';
3
+ export * from './timeout';
4
+ export * from './retry';
@@ -0,0 +1,72 @@
1
+ interface RetryOptions {
2
+ /**
3
+ * Maximum number of retry attempts before failing.
4
+ *
5
+ * @default 3
6
+ */
7
+ maxAttempts?: number;
8
+ /**
9
+ * Delay in milliseconds between retry attempts.
10
+ * If `backoff` is true, this is the base delay for exponential backoff.
11
+ *
12
+ * @default 300
13
+ */
14
+ delayMs?: number;
15
+ /**
16
+ * Whether to use exponential backoff for delays between attempts.
17
+ * When enabled, the delay is multiplied by 2 ^ (`attempt` - 1).
18
+ *
19
+ * @default true
20
+ */
21
+ backoff?: boolean;
22
+ /**
23
+ * Optional callback triggered before each retry attempt.
24
+ *
25
+ * @param attempt - The current attempt number (starting from 1).
26
+ * @param error - The error that caused the retry.
27
+ */
28
+ onRetry?: (attempt: number, error: unknown) => void;
29
+ }
30
+ /**
31
+ * Wraps an asynchronous function with retry logic.
32
+ *
33
+ * The returned function will attempt to call the original function up to `maxAttempts` times,
34
+ * with a delay between retries. If all attempts fail, the last encountered error is thrown.
35
+ *
36
+ * Useful for operations that may fail intermittently, such as network requests.
37
+ *
38
+ * @template Task - The type of the async function to wrap.
39
+ * @template TaskResult - The result type of the async function.
40
+ *
41
+ * @param task - The async function to wrap with retry logic.
42
+ * @param options - Configuration options for retry behavior.
43
+ *
44
+ * @returns A function that wraps the original function with retry support.
45
+ *
46
+ * @example
47
+ * ```ts
48
+ * async function fetchData() {
49
+ * const response = await fetch('/api/data');
50
+ *
51
+ * if (!response.ok) {
52
+ * throw new Error('Network error');
53
+ * }
54
+ *
55
+ * return await response.json();
56
+ * }
57
+ *
58
+ * const fetchWithRetry = retry(fetchData, {
59
+ * maxAttempts: 5,
60
+ * delayMs: 500,
61
+ * onRetry: (attempt, error) => {
62
+ * console.warn(`Attempt ${attempt} failed:`, error);
63
+ * }
64
+ * });
65
+ *
66
+ * fetchWithRetry()
67
+ * .then(data => console.log('Success:', data))
68
+ * .catch(error => console.error('Failed after retries:', error));
69
+ * ```
70
+ */
71
+ export declare const retry: <Task extends (...args: unknown[]) => Promise<TaskResult>, TaskResult>(task: Task, { maxAttempts, delayMs, backoff, onRetry }?: RetryOptions) => ((...args: Parameters<Task>) => Promise<TaskResult>);
72
+ export {};
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Wraps a promise with a timeout. If the promise does not settle within the specified time,
3
+ * it will reject with a timeout error.
4
+ *
5
+ * @template T - The type of the promise result.
6
+ *
7
+ * @param promise - The promise to wrap.
8
+ * @param timeoutMs - Timeout duration in milliseconds.
9
+ * @param errorMessage - Optional custom error message.
10
+ *
11
+ * @returns A promise that resolves or rejects with the original promise,
12
+ * or rejects with a timeout error if the duration is exceeded.
13
+ *
14
+ * @example
15
+ * ```ts
16
+ * // Rejects if fetch takes longer than 3 seconds
17
+ * const response = await timeout(fetch('/api/data'), 3000);
18
+ *
19
+ * // With custom message
20
+ * await timeout(fetchData(), 2000, 'Too long');
21
+ * ```
22
+ */
23
+ export declare const timeout: <T>(promise: Promise<T>, timeoutMs: number, errorMessage?: string) => Promise<T>;
package/dist/file.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { Nullable } from './types';
1
+ import type { Nullable } from '~/types';
2
2
  /**
3
3
  * Checks if a value is a `File` object.
4
4
  *
@@ -7,22 +7,6 @@ import type { Nullable } from './types';
7
7
  * @returns `true` if the value is a `File` object; otherwise, `false`.
8
8
  */
9
9
  export declare const isFile: (value: unknown) => value is File;
10
- /**
11
- * Splits a file name into its base name and extension.
12
- *
13
- * Special cases:
14
- * - Files without a dot return `[fileName, ""]`
15
- * - Hidden files like `.gitignore` return `[".gitignore", ""]`
16
- * - Names ending with a trailing dot (e.g., `"file."`) return `["file.", ""]`
17
- * - Multi-dot names (e.g., `"archive.tar.gz"`) split on the last dot
18
- *
19
- * @param fileName - The full file name to parse.
20
- *
21
- * @returns A tuple where:
22
- * - index 0 is the base name
23
- * - index 1 is the file extension (lowercased), or an empty string if none exists
24
- */
25
- export declare const parseFileName: (fileName: string) => string[];
26
10
  /**
27
11
  * Converts a `FileList` object to an array of `File` objects.
28
12
  *
@@ -0,0 +1,47 @@
1
+ export declare const noop: () => void;
2
+ /**
3
+ * Checks if a value is a function.
4
+ *
5
+ * @param value - The value to check.
6
+ *
7
+ * @returns `true` if the value is a function; otherwise, `false`.
8
+ */
9
+ export declare const isFunction: (value: unknown) => value is Function;
10
+ /**
11
+ * Creates a function that negates the result of the given predicate function.
12
+ *
13
+ * @template Args - Argument types of the predicate function.
14
+ *
15
+ * @param fn - A function that returns any value.
16
+ *
17
+ * @returns A new function that returns the negated result of the original function.
18
+ *
19
+ * @example
20
+ * ```ts
21
+ * const isEven = (n: number) => n % 2 === 0;
22
+ * const isOdd = not(isEven);
23
+ *
24
+ * console.log(isOdd(2)); // false
25
+ * console.log(isOdd(3)); // true
26
+ * ```
27
+ */
28
+ export declare const not: <Args extends unknown[]>(fn: (...args: Args) => any) => ((...args: Args) => boolean);
29
+ /**
30
+ * Wraps a function so that it can only be executed once.
31
+ * The wrapped function remembers (caches) the result of the first invocation
32
+ * and returns that same result for all subsequent calls, regardless of the arguments provided.
33
+ *
34
+ * Common use cases include:
35
+ * - initializing singletons
36
+ * - running setup logic only once
37
+ * - avoiding repeated expensive computations
38
+ *
39
+ * @template T - A function type whose return value should be cached.
40
+ *
41
+ * @param fn - The function to execute at most once.
42
+ *
43
+ * @returns A new function with the same signature as `fn`, but guaranteed to
44
+ * execute `fn` only on the first call and return the cached result
45
+ * thereafter.
46
+ */
47
+ export declare const once: <T extends (...args: any[]) => any>(fn: T) => T;
@@ -0,0 +1,2 @@
1
+ export * from './function';
2
+ export * from './invoke-if-function';
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Invokes the given input if it is a function, passing the provided arguments.
3
+ * Otherwise, returns the input as-is.
4
+ *
5
+ * @template Args - Tuple of argument types to pass to the function.
6
+ * @template Result - Return type of the function or the value.
7
+ *
8
+ * @param input - A function to invoke with `args`, or a direct value of type `Result`.
9
+ * @param args - Arguments to pass if `input` is a function.
10
+ *
11
+ * @returns The result of invoking the function, or the original value if it's not a function.
12
+ */
13
+ export declare const invokeIfFunction: <Args extends unknown[], Result>(input: ((...args: Args) => Result) | Result, ...args: Args) => Result;
@@ -0,0 +1,134 @@
1
+ import type { Nullable } from '~/types';
2
+ export interface InertiaOptions {
3
+ /**
4
+ * Current velocity of the motion, expressed in units per millisecond.
5
+ *
6
+ * The unit depends on the context in which inertia is applied:
7
+ * - `px/ms` for scrolling or dragging
8
+ * - arbitrary units for sliders or timelines
9
+ *
10
+ * The sign indicates direction (positive or negative).
11
+ */
12
+ velocity: number;
13
+ /**
14
+ * Time elapsed since the previous inertia step, in milliseconds.
15
+ *
16
+ * This value is typically derived from a high-resolution clock such as `performance.now()`
17
+ * and allows inertia behavior to remain frame-rate independent.
18
+ */
19
+ deltaTime: number;
20
+ /**
21
+ * Exponential friction coefficient controlling how quickly velocity decays over time.
22
+ *
23
+ * This value is applied per millisecond and produces smooth, natural-feeling
24
+ * deceleration when used with an exponential decay model.
25
+ *
26
+ * Smaller values result in a longer glide; larger values cause inertia
27
+ * to stop more quickly.
28
+ *
29
+ * Typical values:
30
+ * - `0.001` – very long, floaty motion
31
+ * - `0.002` – balanced, natural decay (default)
32
+ * - `0.005` – quick stop
33
+ *
34
+ * @default 0.002
35
+ */
36
+ friction?: number;
37
+ /**
38
+ * Minimum absolute velocity below which inertia is considered complete.
39
+ *
40
+ * When the absolute value of the current velocity drops below this threshold,
41
+ * no further movement is applied and inertia terminates.
42
+ *
43
+ * This prevents unnecessary micro-updates and jitter near rest.
44
+ *
45
+ * @default 0.01
46
+ */
47
+ minVelocity?: number;
48
+ }
49
+ interface ApplyInertiaStepOptions extends InertiaOptions {
50
+ /**
51
+ * Current value before applying the inertia step.
52
+ *
53
+ * This typically represents a translated position (e.g. scroll offset),
54
+ * but may be any bounded numeric value.
55
+ */
56
+ value: number;
57
+ /**
58
+ * Lower bound for the value (inclusive).
59
+ */
60
+ min: number;
61
+ /**
62
+ * Upper bound for the value (inclusive).
63
+ */
64
+ max: number;
65
+ }
66
+ /**
67
+ * Advances a value by one inertia step using velocity, friction, and hard bounds.
68
+ *
69
+ * This utility models **inertial motion** (momentum + decay) on top of
70
+ * {@link resolveBoundedDelta}, which acts as the authoritative constraint layer.
71
+ *
72
+ * The function:
73
+ * - Integrates velocity over the elapsed time to produce a movement delta
74
+ * - Resolves that delta against fixed bounds
75
+ * - Applies exponential friction to gradually reduce velocity
76
+ * - Stops immediately when a bound is hit or velocity falls below a threshold
77
+ *
78
+ * ⚠️ This function performs **one step only** and is intended to be called
79
+ * repeatedly from an animation loop (e.g. `requestAnimationFrame`).
80
+ *
81
+ * ### Common use cases
82
+ * - Synthetic scrolling with momentum
83
+ * - Carousels and sliders
84
+ * - Timelines and scrubbers
85
+ * - Drag-to-scroll interactions with inertia
86
+ *
87
+ * @param value - Current value before applying inertia (e.g. translate position).
88
+ * @param min - Minimum allowed value (inclusive).
89
+ * @param max - Maximum allowed value (inclusive).
90
+ * @param velocity - Current velocity in units per millisecond (e.g. px/ms).
91
+ * @param deltaTime - Time elapsed since the previous step, in milliseconds.
92
+ * @param friction - Exponential friction coefficient controlling decay rate.
93
+ * @param minVelocity - Minimum velocity below which inertia stops.
94
+ *
95
+ * @returns An object containing the updated value and velocity,
96
+ * or `null` when inertia has completed or movement is no longer possible.
97
+ *
98
+ * @example
99
+ * ```ts
100
+ * let value = translateX;
101
+ * let velocity = releaseVelocity; // px/ms from drag end
102
+ * let lastTime = performance.now();
103
+ *
104
+ * const step = (time: number) => {
105
+ * const deltaTime = time - lastTime;
106
+ * lastTime = time;
107
+ *
108
+ * const result = applyInertiaStep({
109
+ * value,
110
+ * velocity,
111
+ * min: -maxOverflow,
112
+ * max: 0,
113
+ * deltaTime,
114
+ * });
115
+ *
116
+ * if (!result) {
117
+ * return; // inertia finished
118
+ * }
119
+ *
120
+ * value = result.value;
121
+ * velocity = result.velocity;
122
+ *
123
+ * container.style.transform = `translateX(${value}px)`;
124
+ * requestAnimationFrame(step);
125
+ * };
126
+ *
127
+ * requestAnimationFrame(step);
128
+ * ```
129
+ */
130
+ export declare const applyInertiaStep: ({ value, min, max, velocity, deltaTime, friction, minVelocity, }: ApplyInertiaStepOptions) => Nullable<{
131
+ value: number;
132
+ velocity: number;
133
+ }>;
134
+ export {};
@@ -1 +1,3 @@
1
1
  export * from './layout';
2
+ export * from './resolve-bounded-delta';
3
+ export * from './apply-inertia-step';
@@ -0,0 +1,79 @@
1
+ import type { Nullable } from '~/types';
2
+ interface ResolveBoundedDeltaOptions {
3
+ /**
4
+ * Incremental change to apply to the current value.
5
+ *
6
+ * The sign of this value determines the direction of movement:
7
+ * - Negative values move toward the minimum bound
8
+ * - Positive values move toward the maximum bound
9
+ */
10
+ delta: number;
11
+ /**
12
+ * Current numeric value before applying the delta.
13
+ */
14
+ value: number;
15
+ /**
16
+ * Minimum allowed value (inclusive).
17
+ *
18
+ * Movement beyond this boundary is prevented.
19
+ */
20
+ min: number;
21
+ /**
22
+ * Maximum allowed value (inclusive).
23
+ *
24
+ * Movement beyond this boundary is prevented.
25
+ */
26
+ max: number;
27
+ }
28
+ /**
29
+ * Resolves the next value by consuming a delta within fixed numeric bounds.
30
+ *
31
+ * This function applies **bounded delta consumption** rather than naive clamping:
32
+ *
33
+ * - The delta is applied in the given direction as long as movement is possible.
34
+ * - If the delta would overshoot a bound, it is partially consumed so the
35
+ * resulting value lands exactly on the boundary.
36
+ * - If movement in the given direction is no longer possible, `null` is returned.
37
+ *
38
+ * This behavior mirrors how native scroll engines and drag constraints
39
+ * handle fast input without jitter or overshoot.
40
+ *
41
+ * ### Key characteristics
42
+ * - Direction-aware (positive and negative deltas behave independently)
43
+ * - Prevents overshoot while preserving remaining movement
44
+ * - Does **not** clamp unconditionally
45
+ * - Side-effect free and fully deterministic
46
+ *
47
+ * ### Common use cases
48
+ * - Synthetic scrolling
49
+ * - Drag constraints
50
+ * - Sliders and carousels
51
+ * - Timelines and scrubbers
52
+ * - Inertia and momentum systems
53
+ *
54
+ * @param delta - Incremental change to apply to the current value.
55
+ * The sign determines the movement direction.
56
+ * @param value - Current numeric value before applying the delta.
57
+ * @param min - Minimum allowed value (inclusive).
58
+ * @param max - Maximum allowed value (inclusive).
59
+ *
60
+ * @returns The next resolved value after applying the delta,
61
+ * or `null` if movement in the given direction is not possible.
62
+ *
63
+ * @example
64
+ * ```ts
65
+ * // Simple bounded movement
66
+ * resolveBoundedDelta({ value: 10, delta: -5, min: 0, max: 100 });
67
+ * // → 5
68
+ *
69
+ * // Overshoot is clamped to the boundary
70
+ * resolveBoundedDelta({ value: 2, delta: -10, min: 0, max: 100 });
71
+ * // → 0
72
+ *
73
+ * // Movement blocked at the boundary
74
+ * resolveBoundedDelta({ value: 0, delta: -5, min: 0, max: 100 });
75
+ * // → null
76
+ * ```
77
+ */
78
+ export declare const resolveBoundedDelta: ({ delta, value, min, max, }: ResolveBoundedDeltaOptions) => Nullable<number>;
79
+ export {};
package/dist/index.cjs CHANGED
@@ -1,2 +1,2 @@
1
- (()=>{"use strict";var e={d:(t,n)=>{for(var r in n)e.o(n,r)&&!e.o(t,r)&&Object.defineProperty(t,r,{enumerable:!0,get:n[r]})},o:(e,t)=>Object.prototype.hasOwnProperty.call(e,t),r:e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})}},t={};function n(e,t){if(!e)throw new Error(t)}e.r(t),e.d(t,{FOCUSABLE_HTML_TAGS:()=>xe,assert:()=>n,blobToFile:()=>be,calculateCenterOffset:()=>he,calculateEuclideanDistance:()=>te,calculateMovingSpeed:()=>ne,calculatePercentage:()=>re,camelToDashCase:()=>K,camelToWords:()=>J,centerElementInContainer:()=>me,chunk:()=>F,cloneBlob:()=>ie,compact:()=>E,compose:()=>C,definedProps:()=>Oe,delay:()=>N,difference:()=>T,downloadFile:()=>se,everyAsync:()=>U,fileListToFiles:()=>we,filterParallel:()=>W,filterSequential:()=>z,findAsync:()=>q,getDOMRectIntersectionRatio:()=>ve,getElementOffsetRect:()=>ae,getFocusableHtmlElements:()=>Me,getLocalStorageCapabilities:()=>Ce,getXOverflowWidth:()=>ue,getYOverflowHeight:()=>de,hasXOverflow:()=>ce,hasYOverflow:()=>fe,hashString:()=>ee,intersection:()=>M,invokeIfFunction:()=>D,isAnchorHtmlElement:()=>le,isArray:()=>O,isBlob:()=>d,isBool:()=>s,isContentEditableHtmlElement:()=>oe,isDate:()=>f,isDecimal:()=>A,isDefined:()=>a,isEmptyArray:()=>v,isEmptyObject:()=>u,isError:()=>h,isFile:()=>ge,isFiniteNumber:()=>b,isFunction:()=>_,isHtmlElementFocusable:()=>Fe,isInteger:()=>S,isLocalStorageReadable:()=>Pe,isMap:()=>g,isNil:()=>i,isNilOrEmptyString:()=>V,isNull:()=>r,isNumber:()=>o,isObject:()=>c,isPromise:()=>j,isRegExp:()=>y,isSet:()=>p,isString:()=>G,isSymbol:()=>w,isUndefined:()=>l,isValidDate:()=>m,moveFocusWithinContainer:()=>Te,noop:()=>k,not:()=>L,once:()=>Y,parse2DMatrix:()=>ye,parseFileName:()=>pe,pipe:()=>P,readFilesFromDataTransfer:()=>Ae,reduceAsync:()=>$,resolveAxisDelta:()=>Ee,retry:()=>X,runParallel:()=>I,runSequential:()=>H,someAsync:()=>B,splitStringIntoWords:()=>Q,timeout:()=>R,toKebabCase:()=>Z,traverseFileSystemDirectory:()=>Se,unique:()=>x});const r=e=>null===e,i=e=>null==e,a=e=>null!=e,l=e=>void 0===e,o=e=>"number"==typeof e,s=e=>"boolean"==typeof e,c=e=>"object"==typeof e,u=e=>c(e)&&!r(e)&&0===Object.keys(e).length,f=e=>e instanceof Date,d=e=>e instanceof Blob,h=e=>e instanceof Error,m=e=>f(e)&&!isNaN(e.getTime()),y=e=>e instanceof RegExp,g=e=>e instanceof Map,p=e=>e instanceof Set,w=e=>"symbol"==typeof e,b=e=>o(e)&&isFinite(e),S=e=>o(e)&&Number.isInteger(e),A=e=>b(e)&&!Number.isInteger(e),O=e=>Array.isArray(e),v=e=>O(e)&&0===e.length,E=e=>e.filter(Boolean),x=e=>[...new Set(e)],F=(e,t)=>(n(t>0,"Chunk size must be greater than 0"),Array.from({length:Math.ceil(e.length/t)},(n,r)=>e.slice(r*t,(r+1)*t))),M=(...e)=>{if(0===e.length)return[];if(1===e.length)return[...e[0]];const[t,...n]=e;return x(t).filter(e=>n.every(t=>t.includes(e)))},T=(e,t)=>e.filter(e=>!t.includes(e)),P=(...e)=>t=>e.reduce((e,t)=>t(e),t),C=(...e)=>t=>e.reduceRight((e,t)=>t(e),t),k=()=>{},_=e=>"function"==typeof e,L=e=>(...t)=>!e(...t),D=(e,...t)=>"function"==typeof e?e(...t):e,N=e=>new Promise(t=>setTimeout(t,e)),R=async(e,t,n="Operation timed out")=>{try{return await Promise.race([e,N(t).then(()=>Promise.reject(new Error(n)))])}finally{}},X=(e,{maxAttempts:t=3,delayMs:n=300,backoff:r=!0,onRetry:i}={})=>async(...a)=>{let l;for(let o=1;o<=t;o++)try{return await e(...a)}catch(e){if(l=e,o<t){i?.(o,e);const t=r?n*2**(o-1):n;await N(t)}}throw l},Y=e=>{let t,n=!1;return function(...r){return n||(n=!0,t=e.apply(this,r)),t}},j=e=>_(e?.then),H=async(e,t)=>{const n=[];for(let r=0;r<e.length;r++)n.push(await t(e[r],r,e));return n},I=async(e,t)=>Promise.all(e.map(t)),z=async(e,t)=>{const n=[];for(let r=0;r<e.length;r++){const i=e[r];await t(i,r,e)&&n.push(i)}return n},W=async(e,t)=>{const n=await I(e,async(e,n,r)=>!!await t(e,n,r)&&e);return E(n)},B=async(e,t)=>{for(let n=0;n<e.length;n++)if(await t(e[n],n,e))return!0;return!1},U=async(e,t)=>{for(let n=0;n<e.length;n++)if(!await t(e[n],n,e))return!1;return!0},$=async(e,t,n)=>{let r=n;for(let n=0;n<e.length;n++)r=await t(r,e[n],n,e);return r},q=async(e,t)=>{for(let n=0;n<e.length;n++)if(await t(e[n],n,e))return e[n];return null},G=e=>"string"==typeof e,V=e=>""===e||i(e),Z=e=>e.replace(/([a-z0-9])([A-Z])/g,"$1-$2").toLowerCase(),K=e=>{const t=e.charAt(0),n=e.slice(1);return t.toLowerCase()+n.replace(/[A-Z]/g,e=>`-${e.toLowerCase()}`)},J=e=>e.replace(/([a-z0-9])([A-Z])/g,"$1 $2"),Q=e=>e.split(" ").filter(Boolean),ee=e=>{let t=5381;for(let n=0;n<e.length;n++)t=33*t^e.charCodeAt(n);return(t>>>0).toString(36)},te=(e,t,n,r)=>{const i=n-e,a=r-t;return Math.hypot(i,a)},ne=(e,t)=>Math.abs(e/t),re=(e,t)=>e*t/100,ie=e=>new Blob([e],{type:e.type}),ae=e=>new DOMRect(e.offsetLeft,e.offsetTop,e.clientWidth,e.clientHeight),le=e=>"A"===e.tagName,oe=e=>"true"===e.getAttribute("contenteditable"),se=(e,{fileName:t,target:r}={})=>{if(l(document))return;const i=document.createElement("a");let a=null;try{const n=G(e)?e:a=URL.createObjectURL(e);i.href=n,t&&(i.download=t),r&&(i.target=r),document.body.appendChild(i),i.click()}finally{i.remove(),a&&setTimeout(()=>{n(a,"Object URL should not be null"),URL.revokeObjectURL(a)},0)}},ce=e=>e.scrollWidth>e.clientWidth,ue=e=>Math.max(0,e.scrollWidth-e.clientWidth),fe=e=>e.scrollHeight>e.clientHeight,de=e=>Math.max(0,e.scrollHeight-e.clientHeight),he=({overflowSize:e,containerSize:t,elementOffset:n,elementSize:r})=>{if(e<=0)return 0;const i=n+r/2-t/2;return-Math.max(0,Math.min(i,e))},me=(e,t,{axis:n="both"}={})=>{let r=0,i=0;"x"!==n&&"both"!==n||(r=he({overflowSize:ue(e),containerSize:e.clientWidth,elementOffset:t.offsetLeft,elementSize:t.clientWidth})),"y"!==n&&"both"!==n||(i=he({overflowSize:de(e),containerSize:e.clientHeight,elementOffset:t.offsetTop,elementSize:t.clientHeight})),e.style.transform=`translate(${r}px, ${i}px)`},ye=e=>{const t=window.getComputedStyle(e).getPropertyValue("transform").match(/^matrix\((.+)\)$/);if(!t)return{translateX:0,translateY:0,scaleX:1,scaleY:1,skewX:0,skewY:0};const[n,r,i,a,l,o]=t[1].split(", ").map(parseFloat);return{translateX:l,translateY:o,scaleX:n,scaleY:a,skewX:i,skewY:r}},ge=e=>e instanceof File,pe=e=>{const t=e.lastIndexOf(".");return t<=0||t===e.length-1?[e,""]:[e.slice(0,t),e.slice(t+1).toLowerCase()]},we=e=>{if(!e)return[];const t=[];for(let n=0;n<e.length;n++)t.push(e[n]);return t},be=(e,t)=>new File([e],t,{type:e.type}),Se=async(e,{skipFiles:t=[".DS_Store","Thumbs.db","desktop.ini","ehthumbs.db",".Spotlight-V100",".Trashes",".fseventsd","__MACOSX"]}={})=>{const n=new Set(t),r=await(async e=>{const t=e.createReader(),n=async()=>new Promise((e,r)=>{t.readEntries(async t=>{if(t.length)try{const r=await n();e([...t,...r])}catch(e){r(e)}else e([])},r)});return n()})(e);return(await I(r,async e=>e.isDirectory?Se(e,{skipFiles:t}):n.has(e.name)?[]:[await new Promise((t,n)=>{e.file(t,n)})])).flat()},Ae=async(e,t={})=>{const n=e?.items;if(!n)return[];const r=[];for(let e=0;e<n.length;e++){const i=n[e];if("webkitGetAsEntry"in i){const e=i.webkitGetAsEntry?.();if(e?.isDirectory){r.push(Se(e,t));continue}if(e?.isFile){r.push(new Promise((t,n)=>e.file(e=>t([e]),n)));continue}}const a=i.getAsFile();a&&r.push(Promise.resolve([a]))}return(await Promise.all(r)).flat()},Oe=e=>Object.entries(e).reduce((e,[t,n])=>(void 0!==n&&(e[t]=n),e),{}),ve=(e,t)=>Math.max(0,Math.min(e.right,t.right)-Math.max(e.left,t.left))*Math.max(0,Math.min(e.bottom,t.bottom)-Math.max(e.top,t.top))/(t.width*t.height),Ee=(e,t,{allowFallback:n=!0,invert:r=!0}={})=>{const i=r?-1:1;switch(t){case"x":return{deltaX:i*(0!==e.deltaX?e.deltaX:n?e.deltaY:0),deltaY:0};case"y":return{deltaX:0,deltaY:i*e.deltaY};default:return{deltaX:i*e.deltaX,deltaY:i*e.deltaY}}},xe=["INPUT","SELECT","TEXTAREA","BUTTON","A"],Fe=e=>{if(!e)return!1;const t=window.getComputedStyle(e);if("hidden"===t.visibility||"none"===t.display)return!1;if("disabled"in e&&e.disabled)return!1;const n=e.getAttribute("tabindex");return"-1"!==n&&(xe.includes(e.tagName)?!le(e)||""!==e.href:!!oe(e)||null!==n)},Me=e=>Array.from(e.querySelectorAll("*")).filter(Fe),Te=(e,t=null,{wrap:n=!0,getNextIndex:r}={})=>{const i=document.activeElement,a=t??i?.parentElement;if(!i||!a)return;const l=Me(a);if(0===l.length)return;const o=l.indexOf(i);if(-1===o)return;let s;r?s=r(o,e,l):"next"===e?(s=o+1,s>=l.length&&(s=n?0:null)):(s=o-1,s<0&&(s=n?l.length-1:null)),null!==s&&l[s]?.focus()},Pe=()=>{if("undefined"==typeof window||!window.localStorage)return!1;try{return window.localStorage.getItem("__non_existing_key__"),!0}catch{return!1}},Ce=()=>{if(!Pe())return{readable:!1,writable:!1};try{const e="__test_write__";return window.localStorage.setItem(e,"1"),window.localStorage.removeItem(e),{readable:!0,writable:!0}}catch{}return{readable:!0,writable:!1}};module.exports=t})();
1
+ (()=>{"use strict";var e={d:(t,n)=>{for(var r in n)e.o(n,r)&&!e.o(t,r)&&Object.defineProperty(t,r,{enumerable:!0,get:n[r]})},o:(e,t)=>Object.prototype.hasOwnProperty.call(e,t),r:e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})}},t={};function n(e,t){if(!e)throw new Error(t)}e.r(t),e.d(t,{FOCUSABLE_HTML_TAGS:()=>Fe,applyInertiaStep:()=>Oe,assert:()=>n,blobToFile:()=>be,calculateCenterOffset:()=>he,calculateEuclideanDistance:()=>te,calculateMovingSpeed:()=>ne,calculatePercentage:()=>re,camelToDashCase:()=>G,camelToWords:()=>Z,centerElementInContainer:()=>ye,chunk:()=>M,cloneBlob:()=>le,compact:()=>O,compose:()=>C,definedProps:()=>xe,delay:()=>B,difference:()=>T,downloadFile:()=>ce,everyAsync:()=>H,fileListToFiles:()=>we,filterParallel:()=>Y,filterSequential:()=>I,findAsync:()=>W,getDOMRectIntersectionRatio:()=>Ee,getElementOffsetRect:()=>ae,getFocusableHtmlElements:()=>Pe,getLocalStorageCapabilities:()=>_e,getXOverflowWidth:()=>fe,getYOverflowHeight:()=>de,hasXOverflow:()=>ue,hasYOverflow:()=>me,hashString:()=>ie,intersection:()=>F,invokeIfFunction:()=>ee,isAnchorHtmlElement:()=>oe,isArray:()=>x,isBlob:()=>m,isBool:()=>s,isContentEditableHtmlElement:()=>se,isDate:()=>f,isDecimal:()=>v,isDefined:()=>l,isEmptyArray:()=>A,isEmptyObject:()=>u,isError:()=>d,isFile:()=>pe,isFiniteNumber:()=>b,isFunction:()=>_,isHtmlElementFocusable:()=>Te,isInteger:()=>S,isLocalStorageReadable:()=>ke,isMap:()=>g,isNil:()=>i,isNilOrEmptyString:()=>J,isNull:()=>r,isNumber:()=>o,isObject:()=>c,isPromise:()=>N,isRegExp:()=>y,isSet:()=>p,isString:()=>q,isSymbol:()=>w,isUndefined:()=>a,isValidDate:()=>h,moveFocusWithinContainer:()=>Ce,noop:()=>k,not:()=>D,once:()=>L,parse2DMatrix:()=>ge,parseFileName:()=>Q,pipe:()=>P,readFilesFromDataTransfer:()=>ve,reduceAsync:()=>z,resolveAxisDelta:()=>Me,resolveBoundedDelta:()=>Ae,retry:()=>$,runParallel:()=>X,runSequential:()=>R,someAsync:()=>j,splitStringIntoWords:()=>K,timeout:()=>U,toKebabCase:()=>V,traverseFileSystemDirectory:()=>Se,unique:()=>E});const r=e=>null===e,i=e=>null==e,l=e=>null!=e,a=e=>void 0===e,o=e=>"number"==typeof e,s=e=>"boolean"==typeof e,c=e=>"object"==typeof e,u=e=>c(e)&&!r(e)&&0===Object.keys(e).length,f=e=>e instanceof Date,m=e=>e instanceof Blob,d=e=>e instanceof Error,h=e=>f(e)&&!isNaN(e.getTime()),y=e=>e instanceof RegExp,g=e=>e instanceof Map,p=e=>e instanceof Set,w=e=>"symbol"==typeof e,b=e=>o(e)&&isFinite(e),S=e=>o(e)&&Number.isInteger(e),v=e=>b(e)&&!Number.isInteger(e),x=e=>Array.isArray(e),A=e=>x(e)&&0===e.length,O=e=>e.filter(Boolean),E=e=>[...new Set(e)],M=(e,t)=>(n(t>0,"Chunk size must be greater than 0"),Array.from({length:Math.ceil(e.length/t)},(n,r)=>e.slice(r*t,(r+1)*t))),F=(...e)=>{if(0===e.length)return[];if(1===e.length)return[...e[0]];const[t,...n]=e;return E(t).filter(e=>n.every(t=>t.includes(e)))},T=(e,t)=>e.filter(e=>!t.includes(e)),P=(...e)=>t=>e.reduce((e,t)=>t(e),t),C=(...e)=>t=>e.reduceRight((e,t)=>t(e),t),k=()=>{},_=e=>"function"==typeof e,D=e=>(...t)=>!e(...t),L=e=>{let t,n=!1;return function(...r){return n||(n=!0,t=e.apply(this,r)),t}},N=e=>_(e?.then),R=async(e,t)=>{const n=[];for(let r=0;r<e.length;r++)n.push(await t(e[r],r,e));return n},X=async(e,t)=>Promise.all(e.map(t)),I=async(e,t)=>{const n=[];for(let r=0;r<e.length;r++){const i=e[r];await t(i,r,e)&&n.push(i)}return n},Y=async(e,t)=>{const n=await X(e,async(e,n,r)=>!!await t(e,n,r)&&e);return O(n)},j=async(e,t)=>{for(let n=0;n<e.length;n++)if(await t(e[n],n,e))return!0;return!1},H=async(e,t)=>{for(let n=0;n<e.length;n++)if(!await t(e[n],n,e))return!1;return!0},z=async(e,t,n)=>{let r=n;for(let n=0;n<e.length;n++)r=await t(r,e[n],n,e);return r},W=async(e,t)=>{for(let n=0;n<e.length;n++)if(await t(e[n],n,e))return e[n];return null},B=e=>new Promise(t=>setTimeout(t,e)),U=async(e,t,n="Operation timed out")=>{try{return await Promise.race([e,B(t).then(()=>Promise.reject(new Error(n)))])}finally{}},$=(e,{maxAttempts:t=3,delayMs:n=300,backoff:r=!0,onRetry:i}={})=>async(...l)=>{let a;for(let o=1;o<=t;o++)try{return await e(...l)}catch(e){if(a=e,o<t){i?.(o,e);const t=r?n*2**(o-1):n;await B(t)}}throw a},q=e=>"string"==typeof e,V=e=>e.replace(/([a-z0-9])([A-Z])/g,"$1-$2").toLowerCase(),G=e=>{const t=e.charAt(0),n=e.slice(1);return t.toLowerCase()+n.replace(/[A-Z]/g,e=>`-${e.toLowerCase()}`)},Z=e=>e.replace(/([a-z0-9])([A-Z])/g,"$1 $2"),K=e=>e.split(" ").filter(Boolean),J=e=>""===e||i(e),Q=e=>{const t=e.lastIndexOf(".");return t<=0||t===e.length-1?[e,""]:[e.slice(0,t),e.slice(t+1).toLowerCase()]},ee=(e,...t)=>"function"==typeof e?e(...t):e,te=(e,t,n,r)=>{const i=n-e,l=r-t;return Math.hypot(i,l)},ne=(e,t)=>Math.abs(e/t),re=(e,t)=>e*t/100,ie=e=>{let t=5381;for(let n=0;n<e.length;n++)t=33*t^e.charCodeAt(n);return(t>>>0).toString(36)},le=e=>new Blob([e],{type:e.type}),ae=e=>new DOMRect(e.offsetLeft,e.offsetTop,e.clientWidth,e.clientHeight),oe=e=>"A"===e.tagName,se=e=>"true"===e.getAttribute("contenteditable"),ce=(e,{fileName:t,target:r}={})=>{if(a(document))return;const i=document.createElement("a");let l=null;try{const n=q(e)?e:l=URL.createObjectURL(e);i.href=n,t&&(i.download=t),r&&(i.target=r),document.body.appendChild(i),i.click()}finally{i.remove(),l&&setTimeout(()=>{n(l,"Object URL should not be null"),URL.revokeObjectURL(l)},0)}},ue=e=>e.scrollWidth>e.clientWidth,fe=e=>Math.max(0,e.scrollWidth-e.clientWidth),me=e=>e.scrollHeight>e.clientHeight,de=e=>Math.max(0,e.scrollHeight-e.clientHeight),he=({overflowSize:e,containerSize:t,elementOffset:n,elementSize:r})=>{if(e<=0)return 0;const i=n+r/2-t/2;return-Math.max(0,Math.min(i,e))},ye=(e,t,{axis:n="both"}={})=>{let r=0,i=0;"x"!==n&&"both"!==n||(r=he({overflowSize:fe(e),containerSize:e.clientWidth,elementOffset:t.offsetLeft,elementSize:t.clientWidth})),"y"!==n&&"both"!==n||(i=he({overflowSize:de(e),containerSize:e.clientHeight,elementOffset:t.offsetTop,elementSize:t.clientHeight})),e.style.transform=`translate(${r}px, ${i}px)`},ge=e=>{const t=window.getComputedStyle(e).getPropertyValue("transform").match(/^matrix\((.+)\)$/);if(!t)return{translateX:0,translateY:0,scaleX:1,scaleY:1,skewX:0,skewY:0};const[n,r,i,l,a,o]=t[1].split(", ").map(parseFloat);return{translateX:a,translateY:o,scaleX:n,scaleY:l,skewX:i,skewY:r}},pe=e=>e instanceof File,we=e=>{if(!e)return[];const t=[];for(let n=0;n<e.length;n++)t.push(e[n]);return t},be=(e,t)=>new File([e],t,{type:e.type}),Se=async(e,{skipFiles:t=[".DS_Store","Thumbs.db","desktop.ini","ehthumbs.db",".Spotlight-V100",".Trashes",".fseventsd","__MACOSX"]}={})=>{const n=new Set(t),r=await(async e=>{const t=e.createReader(),n=async()=>new Promise((e,r)=>{t.readEntries(async t=>{if(t.length)try{const r=await n();e([...t,...r])}catch(e){r(e)}else e([])},r)});return n()})(e);return(await X(r,async e=>e.isDirectory?Se(e,{skipFiles:t}):n.has(e.name)?[]:[await new Promise((t,n)=>{e.file(t,n)})])).flat()},ve=async(e,t={})=>{const n=e?.items;if(!n)return[];const r=[];for(let e=0;e<n.length;e++){const i=n[e];if("webkitGetAsEntry"in i){const e=i.webkitGetAsEntry?.();if(e?.isDirectory){r.push(Se(e,t));continue}if(e?.isFile){r.push(new Promise((t,n)=>e.file(e=>t([e]),n)));continue}}const l=i.getAsFile();l&&r.push(Promise.resolve([l]))}return(await Promise.all(r)).flat()},xe=e=>Object.entries(e).reduce((e,[t,n])=>(void 0!==n&&(e[t]=n),e),{}),Ae=({delta:e,value:t,min:n,max:r})=>{if(0===e)return null;const i=t+e;return e<0?t<=n?null:Math.max(i,n):e>0?t>=r?null:Math.min(i,r):null},Oe=({value:e,min:t,max:n,velocity:r,deltaTime:i,friction:l=.002,minVelocity:a=.01})=>{if(Math.abs(r)<a)return null;const o=Ae({delta:r*i,value:e,min:t,max:n});return null===o?null:{value:o,velocity:r*Math.exp(-l*i)}},Ee=(e,t)=>Math.max(0,Math.min(e.right,t.right)-Math.max(e.left,t.left))*Math.max(0,Math.min(e.bottom,t.bottom)-Math.max(e.top,t.top))/(t.width*t.height),Me=(e,t,{allowFallback:n=!0,invert:r=!0}={})=>{const i=r?-1:1;switch(t){case"x":return{deltaX:i*(0!==e.deltaX?e.deltaX:n?e.deltaY:0),deltaY:0};case"y":return{deltaX:0,deltaY:i*e.deltaY};default:return{deltaX:i*e.deltaX,deltaY:i*e.deltaY}}},Fe=["INPUT","SELECT","TEXTAREA","BUTTON","A"],Te=e=>{if(!e)return!1;const t=window.getComputedStyle(e);if("hidden"===t.visibility||"none"===t.display)return!1;if("disabled"in e&&e.disabled)return!1;const n=e.getAttribute("tabindex");return"-1"!==n&&(Fe.includes(e.tagName)?!oe(e)||""!==e.href:!!se(e)||null!==n)},Pe=e=>Array.from(e.querySelectorAll("*")).filter(Te),Ce=(e,t=null,{wrap:n=!0,getNextIndex:r}={})=>{const i=document.activeElement,l=t??i?.parentElement;if(!i||!l)return;const a=Pe(l);if(0===a.length)return;const o=a.indexOf(i);if(-1===o)return;let s;r?s=r(o,e,a):"next"===e?(s=o+1,s>=a.length&&(s=n?0:null)):(s=o-1,s<0&&(s=n?a.length-1:null)),null!==s&&a[s]?.focus()},ke=()=>{if("undefined"==typeof window||!window.localStorage)return!1;try{return window.localStorage.getItem("__non_existing_key__"),!0}catch{return!1}},_e=()=>{if(!ke())return{readable:!1,writable:!1};try{const e="__test_write__";return window.localStorage.setItem(e,"1"),window.localStorage.removeItem(e),{readable:!0,writable:!0}}catch{}return{readable:!0,writable:!1}};module.exports=t})();
2
2
  //# sourceMappingURL=index.cjs.map