@react-hive/honey-utils 4.2.0 → 5.0.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 +56 -12
- package/dist/README.md +56 -12
- package/dist/a11y/focus/is-html-element-focusable.d.ts +8 -6
- package/dist/a11y/focus/move-focus-within-container.d.ts +1 -1
- package/dist/array/chunk.d.ts +4 -1
- package/dist/array/compose.d.ts +7 -3
- package/dist/array/get-adjacent-items.d.ts +1 -1
- package/dist/array/index.d.ts +1 -0
- package/dist/array/intersection.d.ts +2 -1
- package/dist/array/is-array.d.ts +6 -0
- package/dist/array/is-empty-array.d.ts +7 -0
- package/dist/array/pipe.d.ts +5 -2
- package/dist/array/to-map-by.d.ts +48 -0
- package/dist/async/async.d.ts +10 -1
- package/dist/async/retry.d.ts +4 -3
- package/dist/dom/data-transfer.d.ts +22 -4
- package/dist/dom/dom.d.ts +7 -5
- package/dist/dom/transform/parse-2d-matrix.d.ts +5 -0
- package/dist/encoding/decode-base64-json.d.ts +22 -1
- package/dist/encoding/decode-base64.d.ts +23 -1
- package/dist/encoding/encode-base64-json.d.ts +16 -0
- package/dist/encoding/encode-base64.d.ts +20 -0
- package/dist/env/storage/index.d.ts +1 -0
- package/dist/env/storage/local-storage-json.d.ts +29 -0
- package/dist/env/storage/local-storage.d.ts +1 -6
- package/dist/file.d.ts +3 -2
- package/dist/function/function.d.ts +12 -0
- package/dist/geometry/apply-inertia-step.d.ts +1 -1
- package/dist/geometry/layout/calculate-center-offset.d.ts +11 -1
- package/dist/geometry/layout/clamp-offset-to-overflow.d.ts +6 -0
- package/dist/geometry/resolve-bounded-delta.d.ts +1 -1
- package/dist/guards.d.ts +24 -3
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.dev.cjs +474 -100
- package/dist/index.dev.cjs.map +1 -1
- package/dist/index.mjs +1 -1
- package/dist/index.mjs.map +1 -1
- package/dist/intersection/get-dom-rect-intersection-ratio.d.ts +8 -1
- package/dist/intersection/resolve-axis-delta.d.ts +7 -0
- package/dist/json/parse-json.d.ts +22 -1
- package/dist/math/hash-string.d.ts +1 -1
- package/dist/math/math.d.ts +17 -1
- package/dist/object.d.ts +7 -1
- package/dist/string/for-each-char.d.ts +1 -1
- package/dist/string/is-nil-or-empty-string.d.ts +1 -1
- package/dist/string/parse-file-name.d.ts +1 -1
- package/dist/tree/flatten-tree.d.ts +2 -2
- package/dist/tree/get-tree-children.d.ts +2 -2
- package/dist/tree/search-tree.d.ts +2 -2
- package/dist/tree/tree.types.d.ts +1 -1
- package/package.json +8 -7
- package/dist/geometry/apply-inertia-step.spec.d.ts +0 -1
- package/dist/geometry/resolve-bounded-delta.spec.d.ts +0 -1
- package/dist/id/generate-ephemeral-id.spec.d.ts +0 -1
package/README.md
CHANGED
|
@@ -99,7 +99,7 @@ splitStringIntoWords('hello world');
|
|
|
99
99
|
* Generate a hash from a string
|
|
100
100
|
*/
|
|
101
101
|
const hash = hashString('background-color: red;');
|
|
102
|
-
// ➜ '
|
|
102
|
+
// ➜ '1s0s409'
|
|
103
103
|
```
|
|
104
104
|
|
|
105
105
|
### Encoding Utilities
|
|
@@ -188,6 +188,36 @@ parseJson('{name:"Mike"}');
|
|
|
188
188
|
// ➜ null
|
|
189
189
|
```
|
|
190
190
|
|
|
191
|
+
### Storage Utilities
|
|
192
|
+
|
|
193
|
+
```ts
|
|
194
|
+
import { readLocalStorageJson, writeLocalStorageJson } from '@react-hive/honey-utils';
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Store a value in localStorage as JSON
|
|
198
|
+
*/
|
|
199
|
+
writeLocalStorageJson('basket', [{ productId: 'p1', quantity: 2 }]);
|
|
200
|
+
// ➜ true
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Read it back
|
|
204
|
+
*/
|
|
205
|
+
readLocalStorageJson<{ productId: string; quantity: number }[]>('basket');
|
|
206
|
+
// ➜ [{ productId: 'p1', quantity: 2 }]
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Nothing stored, invalid JSON, or storage that cannot be read returns null
|
|
210
|
+
*/
|
|
211
|
+
readLocalStorageJson('missing');
|
|
212
|
+
// ➜ null
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* A write that fails (storage full, private mode, a circular value) returns false rather than throwing
|
|
216
|
+
*/
|
|
217
|
+
writeLocalStorageJson('basket', circularValue);
|
|
218
|
+
// ➜ false
|
|
219
|
+
```
|
|
220
|
+
|
|
191
221
|
### Array Utilities
|
|
192
222
|
|
|
193
223
|
```ts
|
|
@@ -200,6 +230,7 @@ import {
|
|
|
200
230
|
intersection,
|
|
201
231
|
difference,
|
|
202
232
|
toggleArrayItem,
|
|
233
|
+
toMapBy,
|
|
203
234
|
pipe,
|
|
204
235
|
compose,
|
|
205
236
|
} from '@react-hive/honey-utils';
|
|
@@ -260,6 +291,16 @@ toggleArrayItem(['a', 'b'], 'b', false);
|
|
|
260
291
|
toggleArrayItem(['a', 'b'], 'a');
|
|
261
292
|
// ➜ ['b']
|
|
262
293
|
|
|
294
|
+
/**
|
|
295
|
+
* Index an array by a key
|
|
296
|
+
*/
|
|
297
|
+
const users = [{ id: 'u1', name: 'Ann' }, { id: 'u2', name: 'Bob' }];
|
|
298
|
+
|
|
299
|
+
toMapBy(users, user => user.id);
|
|
300
|
+
// ➜ Map { 'u1' => { id: 'u1', name: 'Ann' }, 'u2' => { id: 'u2', name: 'Bob' } }
|
|
301
|
+
toMapBy(users, user => user.id, user => user.name);
|
|
302
|
+
// ➜ Map { 'u1' => 'Ann', 'u2' => 'Bob' }
|
|
303
|
+
|
|
263
304
|
/**
|
|
264
305
|
* Compose functions from left to right
|
|
265
306
|
*/
|
|
@@ -679,7 +720,7 @@ function divide(a: number, b: number): number {
|
|
|
679
720
|
---
|
|
680
721
|
|
|
681
722
|
- `isString(value: unknown): value is string` - Checks if a value is a `string`.
|
|
682
|
-
- `isNilOrEmptyString(value: unknown): value is null | undefined` - Checks if a value is `null`, `undefined`, or an empty string.
|
|
723
|
+
- `isNilOrEmptyString(value: unknown): value is null | undefined | ''` - Checks if a value is `null`, `undefined`, or an empty string.
|
|
683
724
|
- `toKebabCase(input: string): string` - Converts a string to kebab-case.
|
|
684
725
|
- `camelToDashCase(input: string): string` - Converts camelCase to dash-case.
|
|
685
726
|
- `splitStringIntoWords(input: string): string[]` - Splits a string into an array of words.
|
|
@@ -714,7 +755,7 @@ function divide(a: number, b: number): number {
|
|
|
714
755
|
|
|
715
756
|
---
|
|
716
757
|
|
|
717
|
-
- `definedProps<T extends object>(obj: T): DefinedProps<T>` - Creates a new object by removing all properties whose values are `undefined
|
|
758
|
+
- `definedProps<T extends object>(obj: T): DefinedProps<T>` - Creates a new object by removing all properties whose values are `undefined`, symbol-keyed ones included. A key that may be `undefined` comes out optional.
|
|
718
759
|
|
|
719
760
|
### Array Utilities
|
|
720
761
|
|
|
@@ -724,8 +765,9 @@ function divide(a: number, b: number): number {
|
|
|
724
765
|
- `isEmptyArray(value: unknown): value is []` - Checks if a value is an empty array.
|
|
725
766
|
- `compact<T>(array: (T | Falsy)[]): T[]` – Returns a new array with all falsy values (false, null, undefined, 0, '', NaN) removed, preserving only truthy items of type `T`.
|
|
726
767
|
- `unique<T>(array: T[]): T[]` - Returns a new array with all duplicate elements removed. Keeps only the first occurrence of each value.
|
|
727
|
-
- `
|
|
728
|
-
- `
|
|
768
|
+
- `toMapBy<Item, Key, Value>(items: readonly Item[], getKey: (item: Item, index: number) => Key, getValue?: (item: Item, index: number) => Value): Map<Key, Item | Value>` - Indexes an array by a key: a `Map` from each item's key to the item, or to what `getValue` returns for it. Where two items share a key, the later one wins.
|
|
769
|
+
- `chunk<T>(array: T[], size: number): T[][]` - Splits an array into smaller arrays ("chunks") of the specified size. Throws unless `size` is a positive whole number.
|
|
770
|
+
- `intersection<T>(...arrays: T[][]): T[]` - Returns an array of elements that exist in all provided arrays, each once, in the order of the first array.
|
|
729
771
|
- `difference<T>(array: T[], exclude: T[]): T[]` - Returns a new array that contains items from `array` that are not present in `exclude`.
|
|
730
772
|
- `pipe(...fns: Function[]): Function` - Composes unary functions left-to-right. Returns a new function that applies all given functions in a sequence.
|
|
731
773
|
- `compose(...fns: Function[]): Function` - Composes unary functions **right-to-left**. Same as `pipe`, but applies functions in reverse order.
|
|
@@ -740,7 +782,7 @@ function divide(a: number, b: number): number {
|
|
|
740
782
|
- `noop(): void` - A no-operation function.
|
|
741
783
|
- `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`.
|
|
742
784
|
- `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.
|
|
743
|
-
- `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.
|
|
785
|
+
- `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; if it throws, every call throws that same error. Preserves both the original function’s parameter types and `this` binding.
|
|
744
786
|
|
|
745
787
|
### Type Guards
|
|
746
788
|
|
|
@@ -749,9 +791,9 @@ function divide(a: number, b: number): number {
|
|
|
749
791
|
- `assert(condition: any, message: string): asserts condition` - Asserts that a condition is truthy, throwing an error with the provided message if it's not.
|
|
750
792
|
- `isNumber(value: unknown): value is number` - Checks if a value is a `number`.
|
|
751
793
|
- `isBool(value: unknown): value is boolean` - Checks if a value is a `boolean`.
|
|
752
|
-
- `isObject(value: unknown): value is object` - Checks if a value is an `object
|
|
794
|
+
- `isObject(value: unknown): value is object` - Checks if a value is an `object`, `null` excluded.
|
|
753
795
|
- `isNil(value: unknown): value is null | undefined` - Checks if a value is `null` or `undefined`.
|
|
754
|
-
- `isEmptyObject(value: unknown): value is Record<string, never>` - Checks if a value is an empty object.
|
|
796
|
+
- `isEmptyObject(value: unknown): value is Record<string, never>` - Checks if a value is an empty object - no own enumerable properties, symbol-keyed ones included.
|
|
755
797
|
- `isNull(value: unknown): value is null` - Checks if a value is `null`.
|
|
756
798
|
- `isUndefined(value: unknown): value is undefined` - Checks if a value is `undefined`.
|
|
757
799
|
- `isDefined<T>(value: T): value is NonNullable<T>` - Checks if a value is neither `null` nor `undefined`.
|
|
@@ -786,6 +828,8 @@ function divide(a: number, b: number): number {
|
|
|
786
828
|
|
|
787
829
|
- `isLocalStorageReadable(): boolean` - Determines whether `localStorage` can be safely read from. This check works even when writes fail (e.g., due to `QuotaExceededError`) and ensures that calling `getItem()` does not throw in restricted environments.
|
|
788
830
|
- `getLocalStorageCapabilities(): LocalStorageCapabilities` - Detects the browser's read and write capabilities for `localStorage`. Readability is determined by safe execution of `getItem()`, while writability requires successful `setItem()` and `removeItem()`.
|
|
831
|
+
- `readLocalStorageJson<Value>(key: string): Nullable<Value>` - Reads the value stored as JSON under a key in `localStorage`. Returns `null` when nothing is stored, the stored value is not valid JSON, or storage cannot be read (SSR, disabled storage, sandboxed iframes). Never throws. The value is cast to `Value`, not validated.
|
|
832
|
+
- `writeLocalStorageJson(key: string, value: unknown): boolean` - Stores a value under a key in `localStorage` as JSON. Returns `true` once stored, or `false` when storage cannot be written (`QuotaExceededError`, private mode, restricted contexts, SSR) or the value cannot be serialised. Never throws.
|
|
789
833
|
|
|
790
834
|
### Geometry
|
|
791
835
|
|
|
@@ -806,7 +850,7 @@ function divide(a: number, b: number): number {
|
|
|
806
850
|
- `cloneBlob(blob: Blob): Blob` - Creates a clone of a Blob object with the same content and type as the original.
|
|
807
851
|
- `getElementOffsetRect(element: HTMLElement): DOMRect` - Returns a `DOMRect` representing the element's layout position using `offsetLeft`, `offsetTop`, and `clientWidth`/`clientHeight`.
|
|
808
852
|
- `isAnchorHtmlElement(element: HTMLElement): element is HTMLAnchorElement` - Determines whether the provided element is an `<a>` tag. Acts as a type guard that narrows the element to `HTMLAnchorElement`.
|
|
809
|
-
- `isContentEditableHtmlElement(element: HTMLElement): boolean` - Returns `true` if the element
|
|
853
|
+
- `isContentEditableHtmlElement(element: HTMLElement): boolean` - Returns `true` if the element's `contenteditable` is `""`, `"true"` or `"plaintext-only"` (in any case), making it user-editable and implicitly focusable.
|
|
810
854
|
|
|
811
855
|
#### File
|
|
812
856
|
|
|
@@ -858,8 +902,8 @@ function divide(a: number, b: number): number {
|
|
|
858
902
|
- `everyAsync<Item>(array: Item[], predicate): Promise<boolean>` - Returns `true` if **all** items in the array pass the async predicate.
|
|
859
903
|
- `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.
|
|
860
904
|
- `delay(delayMs: number): Promise<void>` - Creates a promise that resolves after the specified delay in milliseconds.
|
|
861
|
-
- `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.
|
|
862
|
-
- `retry<
|
|
905
|
+
- `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. Its timer is cleared however the race ends.
|
|
906
|
+
- `retry<Args, TaskResult>(task: (...args: Args) => Promise<TaskResult>, options?: RetryOptions): (...args: Args) => Promise<TaskResult>` - Wraps an asynchronous function with retry logic, with configurable max attempts (at least one), delay between retries, exponential backoff, and retry callbacks.
|
|
863
907
|
|
|
864
908
|
### Intersection Utilities
|
|
865
909
|
|
|
@@ -874,7 +918,7 @@ function divide(a: number, b: number): number {
|
|
|
874
918
|
|
|
875
919
|
#### Focus
|
|
876
920
|
|
|
877
|
-
- `isHtmlElementFocusable(element: Nullable<HTMLElement>): boolean` - Checks whether an element is considered focusable according to browser rules. Factors include: visibility, `display
|
|
921
|
+
- `isHtmlElementFocusable(element: Nullable<HTMLElement>): boolean` - Checks whether an element is considered focusable according to browser rules. Factors include: visibility, `display` (an ancestor's too), `:disabled` (a disabled `<fieldset>`'s controls too), a valid `tabindex` (negative removes it, non-negative makes any element focusable), native focusable tags, and `contenteditable`.
|
|
878
922
|
- `getFocusableHtmlElements(container: HTMLElement): HTMLElement[]` - Returns all focusable descendant elements within a container, using `isHtmlElementFocusable` to filter them.
|
|
879
923
|
- `moveFocusWithinContainer(direction: FocusMoveDirection, container?: Nullable<HTMLElement>, options?: MoveFocusWithinContainerOptions): void` - Moves focus to the next or previous focusable element within a container. Supports cyclic navigation, optional wrapping control, and custom focus index resolution for advanced keyboard navigation patterns (e.g. roving tabindex).
|
|
880
924
|
|
package/dist/README.md
CHANGED
|
@@ -99,7 +99,7 @@ splitStringIntoWords('hello world');
|
|
|
99
99
|
* Generate a hash from a string
|
|
100
100
|
*/
|
|
101
101
|
const hash = hashString('background-color: red;');
|
|
102
|
-
// ➜ '
|
|
102
|
+
// ➜ '1s0s409'
|
|
103
103
|
```
|
|
104
104
|
|
|
105
105
|
### Encoding Utilities
|
|
@@ -188,6 +188,36 @@ parseJson('{name:"Mike"}');
|
|
|
188
188
|
// ➜ null
|
|
189
189
|
```
|
|
190
190
|
|
|
191
|
+
### Storage Utilities
|
|
192
|
+
|
|
193
|
+
```ts
|
|
194
|
+
import { readLocalStorageJson, writeLocalStorageJson } from '@react-hive/honey-utils';
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Store a value in localStorage as JSON
|
|
198
|
+
*/
|
|
199
|
+
writeLocalStorageJson('basket', [{ productId: 'p1', quantity: 2 }]);
|
|
200
|
+
// ➜ true
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Read it back
|
|
204
|
+
*/
|
|
205
|
+
readLocalStorageJson<{ productId: string; quantity: number }[]>('basket');
|
|
206
|
+
// ➜ [{ productId: 'p1', quantity: 2 }]
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Nothing stored, invalid JSON, or storage that cannot be read returns null
|
|
210
|
+
*/
|
|
211
|
+
readLocalStorageJson('missing');
|
|
212
|
+
// ➜ null
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* A write that fails (storage full, private mode, a circular value) returns false rather than throwing
|
|
216
|
+
*/
|
|
217
|
+
writeLocalStorageJson('basket', circularValue);
|
|
218
|
+
// ➜ false
|
|
219
|
+
```
|
|
220
|
+
|
|
191
221
|
### Array Utilities
|
|
192
222
|
|
|
193
223
|
```ts
|
|
@@ -200,6 +230,7 @@ import {
|
|
|
200
230
|
intersection,
|
|
201
231
|
difference,
|
|
202
232
|
toggleArrayItem,
|
|
233
|
+
toMapBy,
|
|
203
234
|
pipe,
|
|
204
235
|
compose,
|
|
205
236
|
} from '@react-hive/honey-utils';
|
|
@@ -260,6 +291,16 @@ toggleArrayItem(['a', 'b'], 'b', false);
|
|
|
260
291
|
toggleArrayItem(['a', 'b'], 'a');
|
|
261
292
|
// ➜ ['b']
|
|
262
293
|
|
|
294
|
+
/**
|
|
295
|
+
* Index an array by a key
|
|
296
|
+
*/
|
|
297
|
+
const users = [{ id: 'u1', name: 'Ann' }, { id: 'u2', name: 'Bob' }];
|
|
298
|
+
|
|
299
|
+
toMapBy(users, user => user.id);
|
|
300
|
+
// ➜ Map { 'u1' => { id: 'u1', name: 'Ann' }, 'u2' => { id: 'u2', name: 'Bob' } }
|
|
301
|
+
toMapBy(users, user => user.id, user => user.name);
|
|
302
|
+
// ➜ Map { 'u1' => 'Ann', 'u2' => 'Bob' }
|
|
303
|
+
|
|
263
304
|
/**
|
|
264
305
|
* Compose functions from left to right
|
|
265
306
|
*/
|
|
@@ -679,7 +720,7 @@ function divide(a: number, b: number): number {
|
|
|
679
720
|
---
|
|
680
721
|
|
|
681
722
|
- `isString(value: unknown): value is string` - Checks if a value is a `string`.
|
|
682
|
-
- `isNilOrEmptyString(value: unknown): value is null | undefined` - Checks if a value is `null`, `undefined`, or an empty string.
|
|
723
|
+
- `isNilOrEmptyString(value: unknown): value is null | undefined | ''` - Checks if a value is `null`, `undefined`, or an empty string.
|
|
683
724
|
- `toKebabCase(input: string): string` - Converts a string to kebab-case.
|
|
684
725
|
- `camelToDashCase(input: string): string` - Converts camelCase to dash-case.
|
|
685
726
|
- `splitStringIntoWords(input: string): string[]` - Splits a string into an array of words.
|
|
@@ -714,7 +755,7 @@ function divide(a: number, b: number): number {
|
|
|
714
755
|
|
|
715
756
|
---
|
|
716
757
|
|
|
717
|
-
- `definedProps<T extends object>(obj: T): DefinedProps<T>` - Creates a new object by removing all properties whose values are `undefined
|
|
758
|
+
- `definedProps<T extends object>(obj: T): DefinedProps<T>` - Creates a new object by removing all properties whose values are `undefined`, symbol-keyed ones included. A key that may be `undefined` comes out optional.
|
|
718
759
|
|
|
719
760
|
### Array Utilities
|
|
720
761
|
|
|
@@ -724,8 +765,9 @@ function divide(a: number, b: number): number {
|
|
|
724
765
|
- `isEmptyArray(value: unknown): value is []` - Checks if a value is an empty array.
|
|
725
766
|
- `compact<T>(array: (T | Falsy)[]): T[]` – Returns a new array with all falsy values (false, null, undefined, 0, '', NaN) removed, preserving only truthy items of type `T`.
|
|
726
767
|
- `unique<T>(array: T[]): T[]` - Returns a new array with all duplicate elements removed. Keeps only the first occurrence of each value.
|
|
727
|
-
- `
|
|
728
|
-
- `
|
|
768
|
+
- `toMapBy<Item, Key, Value>(items: readonly Item[], getKey: (item: Item, index: number) => Key, getValue?: (item: Item, index: number) => Value): Map<Key, Item | Value>` - Indexes an array by a key: a `Map` from each item's key to the item, or to what `getValue` returns for it. Where two items share a key, the later one wins.
|
|
769
|
+
- `chunk<T>(array: T[], size: number): T[][]` - Splits an array into smaller arrays ("chunks") of the specified size. Throws unless `size` is a positive whole number.
|
|
770
|
+
- `intersection<T>(...arrays: T[][]): T[]` - Returns an array of elements that exist in all provided arrays, each once, in the order of the first array.
|
|
729
771
|
- `difference<T>(array: T[], exclude: T[]): T[]` - Returns a new array that contains items from `array` that are not present in `exclude`.
|
|
730
772
|
- `pipe(...fns: Function[]): Function` - Composes unary functions left-to-right. Returns a new function that applies all given functions in a sequence.
|
|
731
773
|
- `compose(...fns: Function[]): Function` - Composes unary functions **right-to-left**. Same as `pipe`, but applies functions in reverse order.
|
|
@@ -740,7 +782,7 @@ function divide(a: number, b: number): number {
|
|
|
740
782
|
- `noop(): void` - A no-operation function.
|
|
741
783
|
- `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`.
|
|
742
784
|
- `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.
|
|
743
|
-
- `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.
|
|
785
|
+
- `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; if it throws, every call throws that same error. Preserves both the original function’s parameter types and `this` binding.
|
|
744
786
|
|
|
745
787
|
### Type Guards
|
|
746
788
|
|
|
@@ -749,9 +791,9 @@ function divide(a: number, b: number): number {
|
|
|
749
791
|
- `assert(condition: any, message: string): asserts condition` - Asserts that a condition is truthy, throwing an error with the provided message if it's not.
|
|
750
792
|
- `isNumber(value: unknown): value is number` - Checks if a value is a `number`.
|
|
751
793
|
- `isBool(value: unknown): value is boolean` - Checks if a value is a `boolean`.
|
|
752
|
-
- `isObject(value: unknown): value is object` - Checks if a value is an `object
|
|
794
|
+
- `isObject(value: unknown): value is object` - Checks if a value is an `object`, `null` excluded.
|
|
753
795
|
- `isNil(value: unknown): value is null | undefined` - Checks if a value is `null` or `undefined`.
|
|
754
|
-
- `isEmptyObject(value: unknown): value is Record<string, never>` - Checks if a value is an empty object.
|
|
796
|
+
- `isEmptyObject(value: unknown): value is Record<string, never>` - Checks if a value is an empty object - no own enumerable properties, symbol-keyed ones included.
|
|
755
797
|
- `isNull(value: unknown): value is null` - Checks if a value is `null`.
|
|
756
798
|
- `isUndefined(value: unknown): value is undefined` - Checks if a value is `undefined`.
|
|
757
799
|
- `isDefined<T>(value: T): value is NonNullable<T>` - Checks if a value is neither `null` nor `undefined`.
|
|
@@ -786,6 +828,8 @@ function divide(a: number, b: number): number {
|
|
|
786
828
|
|
|
787
829
|
- `isLocalStorageReadable(): boolean` - Determines whether `localStorage` can be safely read from. This check works even when writes fail (e.g., due to `QuotaExceededError`) and ensures that calling `getItem()` does not throw in restricted environments.
|
|
788
830
|
- `getLocalStorageCapabilities(): LocalStorageCapabilities` - Detects the browser's read and write capabilities for `localStorage`. Readability is determined by safe execution of `getItem()`, while writability requires successful `setItem()` and `removeItem()`.
|
|
831
|
+
- `readLocalStorageJson<Value>(key: string): Nullable<Value>` - Reads the value stored as JSON under a key in `localStorage`. Returns `null` when nothing is stored, the stored value is not valid JSON, or storage cannot be read (SSR, disabled storage, sandboxed iframes). Never throws. The value is cast to `Value`, not validated.
|
|
832
|
+
- `writeLocalStorageJson(key: string, value: unknown): boolean` - Stores a value under a key in `localStorage` as JSON. Returns `true` once stored, or `false` when storage cannot be written (`QuotaExceededError`, private mode, restricted contexts, SSR) or the value cannot be serialised. Never throws.
|
|
789
833
|
|
|
790
834
|
### Geometry
|
|
791
835
|
|
|
@@ -806,7 +850,7 @@ function divide(a: number, b: number): number {
|
|
|
806
850
|
- `cloneBlob(blob: Blob): Blob` - Creates a clone of a Blob object with the same content and type as the original.
|
|
807
851
|
- `getElementOffsetRect(element: HTMLElement): DOMRect` - Returns a `DOMRect` representing the element's layout position using `offsetLeft`, `offsetTop`, and `clientWidth`/`clientHeight`.
|
|
808
852
|
- `isAnchorHtmlElement(element: HTMLElement): element is HTMLAnchorElement` - Determines whether the provided element is an `<a>` tag. Acts as a type guard that narrows the element to `HTMLAnchorElement`.
|
|
809
|
-
- `isContentEditableHtmlElement(element: HTMLElement): boolean` - Returns `true` if the element
|
|
853
|
+
- `isContentEditableHtmlElement(element: HTMLElement): boolean` - Returns `true` if the element's `contenteditable` is `""`, `"true"` or `"plaintext-only"` (in any case), making it user-editable and implicitly focusable.
|
|
810
854
|
|
|
811
855
|
#### File
|
|
812
856
|
|
|
@@ -858,8 +902,8 @@ function divide(a: number, b: number): number {
|
|
|
858
902
|
- `everyAsync<Item>(array: Item[], predicate): Promise<boolean>` - Returns `true` if **all** items in the array pass the async predicate.
|
|
859
903
|
- `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.
|
|
860
904
|
- `delay(delayMs: number): Promise<void>` - Creates a promise that resolves after the specified delay in milliseconds.
|
|
861
|
-
- `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.
|
|
862
|
-
- `retry<
|
|
905
|
+
- `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. Its timer is cleared however the race ends.
|
|
906
|
+
- `retry<Args, TaskResult>(task: (...args: Args) => Promise<TaskResult>, options?: RetryOptions): (...args: Args) => Promise<TaskResult>` - Wraps an asynchronous function with retry logic, with configurable max attempts (at least one), delay between retries, exponential backoff, and retry callbacks.
|
|
863
907
|
|
|
864
908
|
### Intersection Utilities
|
|
865
909
|
|
|
@@ -874,7 +918,7 @@ function divide(a: number, b: number): number {
|
|
|
874
918
|
|
|
875
919
|
#### Focus
|
|
876
920
|
|
|
877
|
-
- `isHtmlElementFocusable(element: Nullable<HTMLElement>): boolean` - Checks whether an element is considered focusable according to browser rules. Factors include: visibility, `display
|
|
921
|
+
- `isHtmlElementFocusable(element: Nullable<HTMLElement>): boolean` - Checks whether an element is considered focusable according to browser rules. Factors include: visibility, `display` (an ancestor's too), `:disabled` (a disabled `<fieldset>`'s controls too), a valid `tabindex` (negative removes it, non-negative makes any element focusable), native focusable tags, and `contenteditable`.
|
|
878
922
|
- `getFocusableHtmlElements(container: HTMLElement): HTMLElement[]` - Returns all focusable descendant elements within a container, using `isHtmlElementFocusable` to filter them.
|
|
879
923
|
- `moveFocusWithinContainer(direction: FocusMoveDirection, container?: Nullable<HTMLElement>, options?: MoveFocusWithinContainerOptions): void` - Moves focus to the next or previous focusable element within a container. Supports cyclic navigation, optional wrapping control, and custom focus index resolution for advanced keyboard navigation patterns (e.g. roving tabindex).
|
|
880
924
|
|
|
@@ -1,15 +1,17 @@
|
|
|
1
|
-
import type { Nullable } from '
|
|
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.
|
|
5
5
|
*
|
|
6
6
|
* The function checks a combination of factors:
|
|
7
|
-
* - The element must be rendered
|
|
8
|
-
*
|
|
9
|
-
* -
|
|
7
|
+
* - The element must be rendered: neither it nor an ancestor has `display: none`, and it is not
|
|
8
|
+
* `visibility: hidden`.
|
|
9
|
+
* - Disabled form controls are never focusable - including those inside a disabled `<fieldset>`.
|
|
10
|
+
* - Elements with a negative `tabindex` are intentionally removed from the focus order.
|
|
11
|
+
* - Any element with a valid, non-negative `tabindex` is focusable. A `tabindex` that is not an
|
|
12
|
+
* integer is ignored, as browsers ignore it.
|
|
10
13
|
* - Certain native HTML elements are inherently focusable (e.g. inputs, buttons, anchors with `href`).
|
|
11
|
-
* -
|
|
12
|
-
* - Any element with a valid `tabindex` (not null) is considered focusable.
|
|
14
|
+
* - Editable elements (`contenteditable`) are treated as focusable.
|
|
13
15
|
*
|
|
14
16
|
* This logic approximates how browsers and the accessibility tree
|
|
15
17
|
* determine real-world focusability—not just tabindex presence.
|
package/dist/array/chunk.d.ts
CHANGED
|
@@ -6,11 +6,14 @@
|
|
|
6
6
|
* @template T - The type of the items in the array.
|
|
7
7
|
*
|
|
8
8
|
* @param array - The input array to be chunked.
|
|
9
|
-
* @param size - The size of each chunk. Must be
|
|
9
|
+
* @param size - The size of each chunk. Must be a positive whole number.
|
|
10
10
|
*
|
|
11
11
|
* @returns An array of chunks, where each chunk is an array of the specified size
|
|
12
12
|
* (except possibly the last chunk, which may be smaller).
|
|
13
13
|
*
|
|
14
|
+
* @throws {Error} When `size` is not a positive whole number - a fractional size would give
|
|
15
|
+
* chunks of other sizes, and `Infinity` none at all.
|
|
16
|
+
*
|
|
14
17
|
* @example
|
|
15
18
|
* ```ts
|
|
16
19
|
* chunk([1, 2, 3, 4, 5], 2); // [[1, 2], [3, 4], [5]]
|
package/dist/array/compose.d.ts
CHANGED
|
@@ -1,17 +1,21 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Any unary function: one taking a typed argument is assignable here, where it is not to
|
|
3
|
+
* `ComposeFn`.
|
|
4
|
+
*/
|
|
5
|
+
type AnyUnaryFn = (arg: never) => unknown;
|
|
2
6
|
type Compose = {
|
|
3
7
|
<A, R>(fn1: (a: A) => R): (a: A) => R;
|
|
4
8
|
<A, B, R>(fn1: (b: B) => R, fn2: (a: A) => B): (a: A) => R;
|
|
5
9
|
<A, B, C, R>(fn1: (c: C) => R, fn2: (b: B) => C, fn3: (a: A) => B): (a: A) => R;
|
|
6
10
|
<A, B, C, D, R>(fn1: (d: D) => R, fn2: (c: C) => D, fn3: (b: B) => C, fn4: (a: A) => B): (a: A) => R;
|
|
7
11
|
<A, B, C, D, E, R>(fn1: (e: E) => R, fn2: (d: D) => E, fn3: (c: C) => D, fn4: (b: B) => C, fn5: (a: A) => B): (a: A) => R;
|
|
8
|
-
(...fns:
|
|
12
|
+
(...fns: AnyUnaryFn[]): (arg: unknown) => unknown;
|
|
9
13
|
};
|
|
10
14
|
/**
|
|
11
15
|
* Composes multiple unary functions into a single function, applying them from **right to left**.
|
|
12
16
|
*
|
|
13
17
|
* Often used for building functional pipelines where the innermost function runs first.
|
|
14
|
-
* Types are inferred up to 5 chained functions for full type safety.
|
|
18
|
+
* Types are inferred up to 5 chained functions for full type safety. Beyond that, it falls back to the unknown.
|
|
15
19
|
*
|
|
16
20
|
* @param fns - A list of unary functions to compose.
|
|
17
21
|
*
|
package/dist/array/index.d.ts
CHANGED
|
@@ -5,7 +5,8 @@
|
|
|
5
5
|
*
|
|
6
6
|
* @param arrays - Two or more arrays to find common elements from.
|
|
7
7
|
*
|
|
8
|
-
* @returns A new array containing only the elements that exist in all input arrays
|
|
8
|
+
* @returns A new array containing only the elements that exist in all input arrays, each once, in
|
|
9
|
+
* the order of the first array.
|
|
9
10
|
*
|
|
10
11
|
* @example
|
|
11
12
|
* ```ts
|
package/dist/array/is-array.d.ts
CHANGED
|
@@ -4,5 +4,11 @@
|
|
|
4
4
|
* @param value - The value to check.
|
|
5
5
|
*
|
|
6
6
|
* @returns `true` if the value is an array; otherwise, `false`.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```ts
|
|
10
|
+
* isArray([1, 2]); // true
|
|
11
|
+
* isArray('12'); // false
|
|
12
|
+
* ```
|
|
7
13
|
*/
|
|
8
14
|
export declare const isArray: (value: unknown) => value is unknown[];
|
|
@@ -4,5 +4,12 @@
|
|
|
4
4
|
* @param value - The value to check.
|
|
5
5
|
*
|
|
6
6
|
* @returns `true` if the value is an empty array; otherwise, `false`.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```ts
|
|
10
|
+
* isEmptyArray([]); // true
|
|
11
|
+
* isEmptyArray([1]); // false
|
|
12
|
+
* isEmptyArray({}); // false
|
|
13
|
+
* ```
|
|
7
14
|
*/
|
|
8
15
|
export declare const isEmptyArray: (value: unknown) => value is [];
|
package/dist/array/pipe.d.ts
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Any unary function: one taking a typed argument is assignable here, where it is not to `PipeFn`.
|
|
3
|
+
*/
|
|
4
|
+
type AnyUnaryFn = (arg: never) => unknown;
|
|
2
5
|
type Pipe = {
|
|
3
6
|
<A, B>(fn1: (a: A) => B): (a: A) => B;
|
|
4
7
|
<A, B, C>(fn1: (a: A) => B, fn2: (b: B) => C): (a: A) => C;
|
|
5
8
|
<A, B, C, D>(fn1: (a: A) => B, fn2: (b: B) => C, fn3: (c: C) => D): (a: A) => D;
|
|
6
9
|
<A, B, C, D, E>(fn1: (a: A) => B, fn2: (b: B) => C, fn3: (c: C) => D, fn4: (d: D) => E): (a: A) => E;
|
|
7
10
|
<A, B, C, D, E, F>(fn1: (a: A) => B, fn2: (b: B) => C, fn3: (c: C) => D, fn4: (d: D) => E, fn5: (e: E) => F): (a: A) => F;
|
|
8
|
-
(...fns:
|
|
11
|
+
(...fns: AnyUnaryFn[]): (arg: unknown) => unknown;
|
|
9
12
|
};
|
|
10
13
|
/**
|
|
11
14
|
* Composes multiple unary functions into a single function, applying them from left to right.
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Indexes an array by a key: a `Map` from each item's key to the item itself.
|
|
3
|
+
*
|
|
4
|
+
* Items are added in order, so where two share a key the later one wins, as with
|
|
5
|
+
* `new Map(entries)`.
|
|
6
|
+
*
|
|
7
|
+
* @template Item - The type of the items in the array.
|
|
8
|
+
* @template Key - The type of the keys.
|
|
9
|
+
*
|
|
10
|
+
* @param items - The items to index.
|
|
11
|
+
* @param getKey - Returns the key an item is stored under.
|
|
12
|
+
*
|
|
13
|
+
* @returns A new `Map` from each key to its item.
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```ts
|
|
17
|
+
* const users = [{ id: 'u1', name: 'Ann' }, { id: 'u2', name: 'Bob' }];
|
|
18
|
+
*
|
|
19
|
+
* toMapBy(users, user => user.id);
|
|
20
|
+
* // Map { 'u1' => { id: 'u1', name: 'Ann' }, 'u2' => { id: 'u2', name: 'Bob' } }
|
|
21
|
+
* ```
|
|
22
|
+
*/
|
|
23
|
+
export declare function toMapBy<Item, Key>(items: readonly Item[], getKey: (item: Item, index: number) => Key): Map<Key, Item>;
|
|
24
|
+
/**
|
|
25
|
+
* Indexes an array by a key: a `Map` from each item's key to what `getValue` returns for the item.
|
|
26
|
+
*
|
|
27
|
+
* Items are added in order, so where two share a key the later one wins, as with
|
|
28
|
+
* `new Map(entries)`.
|
|
29
|
+
*
|
|
30
|
+
* @template Item - The type of the items in the array.
|
|
31
|
+
* @template Key - The type of the keys.
|
|
32
|
+
* @template Value - The type of the values.
|
|
33
|
+
*
|
|
34
|
+
* @param items - The items to index.
|
|
35
|
+
* @param getKey - Returns the key an item is stored under.
|
|
36
|
+
* @param getValue - Returns the value stored for an item.
|
|
37
|
+
*
|
|
38
|
+
* @returns A new `Map` from each key to its value.
|
|
39
|
+
*
|
|
40
|
+
* @example
|
|
41
|
+
* ```ts
|
|
42
|
+
* const users = [{ id: 'u1', name: 'Ann' }, { id: 'u2', name: 'Bob' }];
|
|
43
|
+
*
|
|
44
|
+
* toMapBy(users, user => user.id, user => user.name);
|
|
45
|
+
* // Map { 'u1' => 'Ann', 'u2' => 'Bob' }
|
|
46
|
+
* ```
|
|
47
|
+
*/
|
|
48
|
+
export declare function toMapBy<Item, Key, Value>(items: readonly Item[], getKey: (item: Item, index: number) => Key, getValue: (item: Item, index: number) => Value): Map<Key, Value>;
|
package/dist/async/async.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Nullable } from '
|
|
1
|
+
import type { Nullable } from '../types';
|
|
2
2
|
/**
|
|
3
3
|
* Checks if a value is a Promise.
|
|
4
4
|
*
|
|
@@ -109,6 +109,9 @@ export declare const filterParallel: <Item>(array: Item[], predicate: (item: Ite
|
|
|
109
109
|
/**
|
|
110
110
|
* Asynchronously checks if at least one element in the array satisfies the async condition.
|
|
111
111
|
*
|
|
112
|
+
* Runs one item at a time, in order, and stops at the first item that satisfies it - as
|
|
113
|
+
* `Array.prototype.some` does.
|
|
114
|
+
*
|
|
112
115
|
* @param array - The array of items to check.
|
|
113
116
|
* @param predicate - An async function that returns a boolean.
|
|
114
117
|
*
|
|
@@ -118,6 +121,9 @@ export declare const someAsync: <Item>(array: Item[], predicate: (item: Item, in
|
|
|
118
121
|
/**
|
|
119
122
|
* Asynchronously checks if all elements in the array satisfy the async condition.
|
|
120
123
|
*
|
|
124
|
+
* Runs one item at a time, in order, and stops at the first item that fails it - as
|
|
125
|
+
* `Array.prototype.every` does.
|
|
126
|
+
*
|
|
121
127
|
* @param array - The array of items to check.
|
|
122
128
|
* @param predicate - An async function that returns a boolean.
|
|
123
129
|
*
|
|
@@ -140,6 +146,9 @@ export declare const reduceAsync: <Item, Accumulator>(array: Item[], fn: (accumu
|
|
|
140
146
|
/**
|
|
141
147
|
* Asynchronously finds the first element that satisfies the async condition.
|
|
142
148
|
*
|
|
149
|
+
* Runs one item at a time, in order, and stops at the first match - as `Array.prototype.find`
|
|
150
|
+
* does.
|
|
151
|
+
*
|
|
143
152
|
* @param array - The array of items to search.
|
|
144
153
|
* @param predicate - An async function that returns a boolean.
|
|
145
154
|
*
|
package/dist/async/retry.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
interface RetryOptions {
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
3
|
+
* The total number of attempts, the first one included, before the last error is thrown. Taken
|
|
4
|
+
* as at least 1.
|
|
4
5
|
*
|
|
5
6
|
* @default 3
|
|
6
7
|
*/
|
|
@@ -35,7 +36,7 @@ interface RetryOptions {
|
|
|
35
36
|
*
|
|
36
37
|
* Useful for operations that may fail intermittently, such as network requests.
|
|
37
38
|
*
|
|
38
|
-
* @template
|
|
39
|
+
* @template Args - The arguments the async function takes.
|
|
39
40
|
* @template TaskResult - The result type of the async function.
|
|
40
41
|
*
|
|
41
42
|
* @param task - The async function to wrap with retry logic.
|
|
@@ -68,5 +69,5 @@ interface RetryOptions {
|
|
|
68
69
|
* .catch(error => console.error('Failed after retries:', error));
|
|
69
70
|
* ```
|
|
70
71
|
*/
|
|
71
|
-
export declare const retry: <
|
|
72
|
+
export declare const retry: <Args extends unknown[], TaskResult>(task: (...args: Args) => Promise<TaskResult>, { maxAttempts, delayMs, backoff, onRetry }?: RetryOptions) => ((...args: Args) => Promise<TaskResult>);
|
|
72
73
|
export {};
|
|
@@ -1,19 +1,37 @@
|
|
|
1
|
-
import type { Nullable } from '
|
|
1
|
+
import type { Nullable } from '../types';
|
|
2
2
|
export declare const DATA_TRANSFER_JSON_MIME = "application/json";
|
|
3
3
|
/**
|
|
4
4
|
* Serializes and stores JSON data in a DataTransfer object.
|
|
5
5
|
*
|
|
6
6
|
* Useful for drag-and-drop communication between elements.
|
|
7
7
|
*
|
|
8
|
-
* @
|
|
9
|
-
*
|
|
8
|
+
* @template T - The type of the data.
|
|
9
|
+
*
|
|
10
|
+
* @param dataTransfer - DataTransfer instance from a drag event.
|
|
11
|
+
* @param data - Data to serialize.
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```ts
|
|
15
|
+
* element.addEventListener('dragstart', event => {
|
|
16
|
+
* setDataTransferJson(event.dataTransfer, { id: 'card-1' });
|
|
17
|
+
* });
|
|
18
|
+
* ```
|
|
10
19
|
*/
|
|
11
20
|
export declare const setDataTransferJson: <T>(dataTransfer: DataTransfer, data: T) => void;
|
|
12
21
|
/**
|
|
13
22
|
* Retrieves and parses JSON data from a DataTransfer object.
|
|
14
23
|
*
|
|
15
|
-
* @
|
|
24
|
+
* @template Value - The type the JSON is expected to have.
|
|
25
|
+
*
|
|
26
|
+
* @param dataTransfer - DataTransfer instance from a drop event.
|
|
16
27
|
*
|
|
17
28
|
* @returns Parsed JSON value or null if not present or invalid.
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* ```ts
|
|
32
|
+
* element.addEventListener('drop', event => {
|
|
33
|
+
* const card = getDataTransferJson<{ id: string }>(event.dataTransfer);
|
|
34
|
+
* });
|
|
35
|
+
* ```
|
|
18
36
|
*/
|
|
19
37
|
export declare const getDataTransferJson: <Value>(dataTransfer: DataTransfer) => Nullable<Value>;
|