@lipemat/js-helpers 1.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 +61 -0
- package/dist/classes/classes.d.ts +28 -0
- package/dist/classes/classes.d.ts.map +1 -0
- package/dist/classes/classes.js +50 -0
- package/dist/classes/classes.js.map +1 -0
- package/dist/colors/colors.d.ts +14 -0
- package/dist/colors/colors.d.ts.map +1 -0
- package/dist/colors/colors.js +63 -0
- package/dist/colors/colors.js.map +1 -0
- package/dist/csvExport/csvExport.d.ts +19 -0
- package/dist/csvExport/csvExport.d.ts.map +1 -0
- package/dist/csvExport/csvExport.js +74 -0
- package/dist/csvExport/csvExport.js.map +1 -0
- package/dist/date/date.d.ts +10 -0
- package/dist/date/date.d.ts.map +1 -0
- package/dist/date/date.js +20 -0
- package/dist/date/date.js.map +1 -0
- package/dist/debounce/debounce.d.ts +17 -0
- package/dist/debounce/debounce.d.ts.map +1 -0
- package/dist/debounce/debounce.js +34 -0
- package/dist/debounce/debounce.js.map +1 -0
- package/dist/delay/delay.d.ts +18 -0
- package/dist/delay/delay.d.ts.map +1 -0
- package/dist/delay/delay.js +20 -0
- package/dist/delay/delay.js.map +1 -0
- package/dist/device/device.d.ts +26 -0
- package/dist/device/device.d.ts.map +1 -0
- package/dist/device/device.js +42 -0
- package/dist/device/device.js.map +1 -0
- package/dist/dom-ready/dom-ready.d.ts +12 -0
- package/dist/dom-ready/dom-ready.d.ts.map +1 -0
- package/dist/dom-ready/dom-ready.js +19 -0
- package/dist/dom-ready/dom-ready.js.map +1 -0
- package/dist/error/error.d.ts +28 -0
- package/dist/error/error.d.ts.map +1 -0
- package/dist/error/error.js +33 -0
- package/dist/error/error.js.map +1 -0
- package/dist/escaping/escaping.d.ts +17 -0
- package/dist/escaping/escaping.d.ts.map +1 -0
- package/dist/escaping/escaping.js +39 -0
- package/dist/escaping/escaping.js.map +1 -0
- package/dist/index.d.ts +25 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +25 -0
- package/dist/index.js.map +1 -0
- package/dist/injectScript/injectScript.d.ts +14 -0
- package/dist/injectScript/injectScript.d.ts.map +1 -0
- package/dist/injectScript/injectScript.js +30 -0
- package/dist/injectScript/injectScript.js.map +1 -0
- package/dist/memoize/memoize.d.ts +13 -0
- package/dist/memoize/memoize.d.ts.map +1 -0
- package/dist/memoize/memoize.js +19 -0
- package/dist/memoize/memoize.js.map +1 -0
- package/dist/noop/noop.d.ts +9 -0
- package/dist/noop/noop.d.ts.map +1 -0
- package/dist/noop/noop.js +10 -0
- package/dist/noop/noop.js.map +1 -0
- package/dist/objects/objects.d.ts +20 -0
- package/dist/objects/objects.d.ts.map +1 -0
- package/dist/objects/objects.js +24 -0
- package/dist/objects/objects.js.map +1 -0
- package/dist/once/once.d.ts +9 -0
- package/dist/once/once.d.ts.map +1 -0
- package/dist/once/once.js +17 -0
- package/dist/once/once.js.map +1 -0
- package/dist/string/string.d.ts +34 -0
- package/dist/string/string.d.ts.map +1 -0
- package/dist/string/string.js +65 -0
- package/dist/string/string.js.map +1 -0
- package/dist/throttle/throttle.d.ts +17 -0
- package/dist/throttle/throttle.d.ts.map +1 -0
- package/dist/throttle/throttle.js +43 -0
- package/dist/throttle/throttle.js.map +1 -0
- package/dist/url/url.d.ts +17 -0
- package/dist/url/url.d.ts.map +1 -0
- package/dist/url/url.js +28 -0
- package/dist/url/url.js.map +1 -0
- package/package.json +71 -0
- package/src/classes/Readme.md +28 -0
- package/src/classes/classes.ts +51 -0
- package/src/colors/Readme.md +28 -0
- package/src/colors/colors.ts +80 -0
- package/src/csvExport/Readme.md +35 -0
- package/src/csvExport/csvExport.ts +89 -0
- package/src/date/Readme.md +21 -0
- package/src/date/date.ts +23 -0
- package/src/debounce/Readme.md +27 -0
- package/src/debounce/debounce.ts +47 -0
- package/src/delay/Readme.md +20 -0
- package/src/delay/delay.ts +19 -0
- package/src/device/Readme.md +31 -0
- package/src/device/device.ts +46 -0
- package/src/dom-ready/Readme.md +27 -0
- package/src/dom-ready/dom-ready.ts +22 -0
- package/src/error/Readme.md +40 -0
- package/src/error/error.ts +49 -0
- package/src/escaping/Readme.md +23 -0
- package/src/escaping/escaping.ts +41 -0
- package/src/index.ts +24 -0
- package/src/injectScript/Readme.md +26 -0
- package/src/injectScript/injectScript.ts +31 -0
- package/src/memoize/Readme.md +28 -0
- package/src/memoize/memoize.ts +27 -0
- package/src/noop/Readme.md +19 -0
- package/src/noop/noop.ts +9 -0
- package/src/objects/Readme.md +31 -0
- package/src/objects/objects.ts +26 -0
- package/src/once/Readme.md +26 -0
- package/src/once/once.ts +22 -0
- package/src/string/Readme.md +37 -0
- package/src/string/string.ts +72 -0
- package/src/throttle/Readme.md +27 -0
- package/src/throttle/throttle.ts +57 -0
- package/src/url/Readme.md +32 -0
- package/src/url/url.ts +35 -0
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# error
|
|
2
|
+
|
|
3
|
+
An `Error` subclass that carries per-field messages. Useful for translating `WP_Error` REST API responses into form field errors.
|
|
4
|
+
|
|
5
|
+
## Exports
|
|
6
|
+
|
|
7
|
+
### `class ErrorWithFields<T>`
|
|
8
|
+
|
|
9
|
+
Extends `Error`. Accepts an `ErrorField<T>` (which may include `additional_errors`) and exposes a `fields` map of field code to message. Field codes are normalized (e.g. `user_email` → `email`).
|
|
10
|
+
|
|
11
|
+
### `ErrorField<T>` (type)
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
interface ErrorField<T> {
|
|
15
|
+
code: keyof T;
|
|
16
|
+
data?: object | string | boolean | number;
|
|
17
|
+
message: string;
|
|
18
|
+
additional_errors?: Array<ErrorField<T>>;
|
|
19
|
+
}
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
### `Fields<T>` (type)
|
|
23
|
+
|
|
24
|
+
```ts
|
|
25
|
+
type Fields<T> = { [field in keyof T]?: string };
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Usage
|
|
29
|
+
|
|
30
|
+
```ts
|
|
31
|
+
import {ErrorWithFields} from '@lipemat/js-helpers';
|
|
32
|
+
|
|
33
|
+
type Form = {email: string; username: string};
|
|
34
|
+
|
|
35
|
+
throw new ErrorWithFields<Form>( {
|
|
36
|
+
code: 'rest_user_invalid_email',
|
|
37
|
+
message: 'Invalid email address.',
|
|
38
|
+
} );
|
|
39
|
+
// error.fields.email === 'Invalid email address.'
|
|
40
|
+
```
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
export interface ErrorField<T extends { [ field: string ]: string }> {
|
|
2
|
+
code: keyof T;
|
|
3
|
+
data?: object | string | boolean | number;
|
|
4
|
+
message: string;
|
|
5
|
+
|
|
6
|
+
additional_errors?: Array<ErrorField<T>>;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export type Fields<T> = {
|
|
10
|
+
[field in keyof T]?: string;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Use in place from the default Error handler to include
|
|
15
|
+
* information about form fields which caused the errors.
|
|
16
|
+
*
|
|
17
|
+
* Translates `WP_Error` responses from REST API into
|
|
18
|
+
* field name and their corresponding messages.
|
|
19
|
+
*
|
|
20
|
+
* @version 1.2.1
|
|
21
|
+
*/
|
|
22
|
+
export class ErrorWithFields<T extends { [ field: string ]: string }> extends Error {
|
|
23
|
+
public fields: Fields<T>;
|
|
24
|
+
|
|
25
|
+
constructor( error: ErrorField<T> ) {
|
|
26
|
+
const errors = [ error ];
|
|
27
|
+
if ( undefined !== error.additional_errors ) {
|
|
28
|
+
errors.push( ...error.additional_errors );
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
super( errors.map( er => er.message ).join( ' ' ) );
|
|
32
|
+
|
|
33
|
+
this.name = 'ErrorWithData';
|
|
34
|
+
this.fields = {};
|
|
35
|
+
errors.forEach( field => {
|
|
36
|
+
this.fields[ this.translate( field ).code ] = field.message;
|
|
37
|
+
} );
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
translate( error: ErrorField<T> ): ErrorField<T> {
|
|
41
|
+
error.code = error.code
|
|
42
|
+
.toString()
|
|
43
|
+
.replace( 'user_name', 'username' )
|
|
44
|
+
.replace( 'user_email', 'email' )
|
|
45
|
+
.replace( 'rest_user_invalid_email', 'email' );
|
|
46
|
+
|
|
47
|
+
return error;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# escaping
|
|
2
|
+
|
|
3
|
+
Decode HTML entities in a string. Taken nearly verbatim from Gutenberg's `@wordpress/html-entities`, with the input sanitized through [DOMPurify](https://github.com/cure53/DOMPurify).
|
|
4
|
+
|
|
5
|
+
## Exports
|
|
6
|
+
|
|
7
|
+
### `decodeEntities( html ): string`
|
|
8
|
+
|
|
9
|
+
Return `html` with HTML entities decoded. Strings without a `&` are returned unchanged. A reusable detached `<textarea>` is used for decoding.
|
|
10
|
+
|
|
11
|
+
- `html: string` — string that may contain HTML entities.
|
|
12
|
+
|
|
13
|
+
## Usage
|
|
14
|
+
|
|
15
|
+
```ts
|
|
16
|
+
import {decodeEntities} from '@lipemat/js-helpers';
|
|
17
|
+
|
|
18
|
+
decodeEntities( 'Ben & Jerry's' ); // "Ben & Jerry's"
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Notes
|
|
22
|
+
|
|
23
|
+
Requires a DOM (`document`); intended for browser use.
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import DOMPurify from 'dompurify';
|
|
2
|
+
|
|
3
|
+
let _decodeTextArea: HTMLTextAreaElement;
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Taken nearly verbatim from the Gutenberg package.
|
|
7
|
+
*
|
|
8
|
+
* Added to the helpers in case we are
|
|
9
|
+
* not loading wp-html-entities on the front-end.
|
|
10
|
+
*
|
|
11
|
+
* @notice We likely are loading wp-html-entities unless a special project.
|
|
12
|
+
*
|
|
13
|
+
* @see @wordpress/html-entities
|
|
14
|
+
*
|
|
15
|
+
* @version 1.0.5
|
|
16
|
+
*
|
|
17
|
+
* @link https://github.com/WordPress/gutenberg/blob/d5915916abc45e6682f4bdb70888aa41e98aa395/packages/html-entities/src/index.js#L14
|
|
18
|
+
* @param html
|
|
19
|
+
*/
|
|
20
|
+
export function decodeEntities( html: string ) {
|
|
21
|
+
// Not a string, or no entities to decode.
|
|
22
|
+
if ( -1 === html.indexOf( '&' ) ) {
|
|
23
|
+
return html;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Create a textarea for decoding entities, that we can reuse.
|
|
27
|
+
if ( undefined === _decodeTextArea ) {
|
|
28
|
+
if ( undefined !== document.implementation ) {
|
|
29
|
+
_decodeTextArea = document.implementation
|
|
30
|
+
.createHTMLDocument( '' )
|
|
31
|
+
.createElement( 'textarea' );
|
|
32
|
+
} else {
|
|
33
|
+
_decodeTextArea = document.createElement( 'textarea' );
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
_decodeTextArea.innerHTML = DOMPurify.sanitize( html );
|
|
38
|
+
const decoded = _decodeTextArea.textContent;
|
|
39
|
+
_decodeTextArea.innerHTML = '';
|
|
40
|
+
return ( decoded );
|
|
41
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Public entry point for `@lipemat/js-helpers`.
|
|
3
|
+
*
|
|
4
|
+
* Re-exports the public API of every helper so consumers can use
|
|
5
|
+
* `import {<helper>} from '@lipemat/js-helpers'`.
|
|
6
|
+
*/
|
|
7
|
+
export * from './classes/classes.js';
|
|
8
|
+
export * from './colors/colors.js';
|
|
9
|
+
export * from './csvExport/csvExport.js';
|
|
10
|
+
export * from './date/date.js';
|
|
11
|
+
export * from './debounce/debounce.js';
|
|
12
|
+
export * from './delay/delay.js';
|
|
13
|
+
export * from './device/device.js';
|
|
14
|
+
export * from './dom-ready/dom-ready.js';
|
|
15
|
+
export * from './error/error.js';
|
|
16
|
+
export * from './escaping/escaping.js';
|
|
17
|
+
export * from './injectScript/injectScript.js';
|
|
18
|
+
export * from './memoize/memoize.js';
|
|
19
|
+
export * from './noop/noop.js';
|
|
20
|
+
export * from './objects/objects.js';
|
|
21
|
+
export * from './once/once.js';
|
|
22
|
+
export * from './string/string.js';
|
|
23
|
+
export * from './throttle/throttle.js';
|
|
24
|
+
export * from './url/url.js';
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# injectScript
|
|
2
|
+
|
|
3
|
+
Inject an external script into the DOM once. Memoized by `src`, so repeated calls with the same URL reuse the same promise instead of adding the script again.
|
|
4
|
+
|
|
5
|
+
## Exports
|
|
6
|
+
|
|
7
|
+
### `injectScript( src, inBody? )`
|
|
8
|
+
|
|
9
|
+
- `src: string` — URL of the script to inject.
|
|
10
|
+
- `inBody?: boolean` — append to `<body>` instead of `<head>`. Defaults to `false`.
|
|
11
|
+
|
|
12
|
+
Returns a `Promise` that resolves when the script loads and rejects on error or abort.
|
|
13
|
+
|
|
14
|
+
## Usage
|
|
15
|
+
|
|
16
|
+
```ts
|
|
17
|
+
import {injectScript} from '@lipemat/js-helpers';
|
|
18
|
+
|
|
19
|
+
await injectScript( 'https://example.com/widget.js' );
|
|
20
|
+
// Calling again with the same URL reuses the first promise.
|
|
21
|
+
await injectScript( 'https://example.com/widget.js' );
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Notes
|
|
25
|
+
|
|
26
|
+
Requires a DOM (`document`); intended for browser use.
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import {memoize} from '../memoize/memoize.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Inject a script into the DOM.
|
|
5
|
+
*
|
|
6
|
+
* May be called multiple times and will ony inject the script once.
|
|
7
|
+
*
|
|
8
|
+
* @param {string} $src - URL of the script.
|
|
9
|
+
* @param {boolean} $inBody - Put in body tag instead of head
|
|
10
|
+
*
|
|
11
|
+
* @version 1.2.1
|
|
12
|
+
*
|
|
13
|
+
* @return {Promise}
|
|
14
|
+
*/
|
|
15
|
+
export const injectScript = memoize( ( src, inBody: boolean = false ) => {
|
|
16
|
+
return new Promise( ( resolve, reject ) => {
|
|
17
|
+
const script = document.createElement( 'script' );
|
|
18
|
+
script.async = false;
|
|
19
|
+
script.src = src;
|
|
20
|
+
script.addEventListener( 'load', () => resolve( 'Loaded: ' + src ) );
|
|
21
|
+
script.addEventListener( 'error', () => reject( 'Error loading script.' ) );
|
|
22
|
+
script.addEventListener( 'abort', () =>
|
|
23
|
+
reject( 'Script loading aborted.' ),
|
|
24
|
+
);
|
|
25
|
+
if ( inBody ) {
|
|
26
|
+
document.body.appendChild( script );
|
|
27
|
+
} else {
|
|
28
|
+
document.head.appendChild( script );
|
|
29
|
+
}
|
|
30
|
+
} );
|
|
31
|
+
} );
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# memoize
|
|
2
|
+
|
|
3
|
+
Lodash-free memoization. Caches results of `fn` keyed by the first argument, or by a custom `resolver`.
|
|
4
|
+
|
|
5
|
+
## Exports
|
|
6
|
+
|
|
7
|
+
### `memoize( fn, resolver? )`
|
|
8
|
+
|
|
9
|
+
- `fn: T` — function whose results are cached.
|
|
10
|
+
- `resolver?: ( ...args ) => string` — derive a custom cache key. Defaults to the first argument.
|
|
11
|
+
|
|
12
|
+
Returns the memoized function with a `.cache` `Map` you can inspect or clear.
|
|
13
|
+
|
|
14
|
+
### `MemoizedFunction<T>` (type)
|
|
15
|
+
|
|
16
|
+
The returned function type, including the `cache: Map<string, ReturnType<T>>` property.
|
|
17
|
+
|
|
18
|
+
## Usage
|
|
19
|
+
|
|
20
|
+
```ts
|
|
21
|
+
import {memoize} from '@lipemat/js-helpers';
|
|
22
|
+
|
|
23
|
+
const slugify = memoize( ( value: string ) => value.toLowerCase().replace( /\s+/g, '-' ) );
|
|
24
|
+
|
|
25
|
+
slugify( 'Hello World' ); // computed
|
|
26
|
+
slugify( 'Hello World' ); // from cache
|
|
27
|
+
slugify.cache.clear();
|
|
28
|
+
```
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type {Callback, Return} from '../once/once.js';
|
|
2
|
+
|
|
3
|
+
type Resolver<T extends Callback> = ( ...args: Parameters<T> ) => string;
|
|
4
|
+
|
|
5
|
+
export type MemoizedFunction<T extends Callback> = Return<T> & {
|
|
6
|
+
cache: Map<string, ReturnType<T>>;
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Similar to lodash memoize but without requiring lodash.
|
|
11
|
+
*
|
|
12
|
+
* @version 1.1.3
|
|
13
|
+
*/
|
|
14
|
+
export function memoize<T extends Callback>( fn: T, resolver?: Resolver<T> ): MemoizedFunction<T> {
|
|
15
|
+
const memoized = function( ...args: Parameters<T> ): ReturnType<Callback> {
|
|
16
|
+
const key = resolver ? resolver( ...args ) : args[ 0 ];
|
|
17
|
+
if ( memoized.cache.has( key ) ) {
|
|
18
|
+
return memoized.cache.get( key );
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const result = fn( ...args );
|
|
22
|
+
memoized.cache = memoized.cache.set( key, result );
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
25
|
+
memoized.cache = new Map();
|
|
26
|
+
return memoized;
|
|
27
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# noop
|
|
2
|
+
|
|
3
|
+
A no-operation function. Lodash-free replacement for `lodash.noop`. Useful as a default callback.
|
|
4
|
+
|
|
5
|
+
## Exports
|
|
6
|
+
|
|
7
|
+
### `noop(): void`
|
|
8
|
+
|
|
9
|
+
Does nothing.
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
import {noop} from '@lipemat/js-helpers';
|
|
15
|
+
|
|
16
|
+
function setup( {onChange = noop} = {} ) {
|
|
17
|
+
onChange();
|
|
18
|
+
}
|
|
19
|
+
```
|
package/src/noop/noop.ts
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# objects
|
|
2
|
+
|
|
3
|
+
Type-safe wrappers around `Object.keys` and `Object.entries` that preserve key types.
|
|
4
|
+
|
|
5
|
+
> More robust versions are available from the `@lipemat/js-boilerplate-gutenberg` package.
|
|
6
|
+
|
|
7
|
+
## Exports
|
|
8
|
+
|
|
9
|
+
### `keysOf( obj ): Array<keyof T>`
|
|
10
|
+
|
|
11
|
+
Like `Object.keys` but typed as `Array<keyof T>` instead of `string[]`.
|
|
12
|
+
|
|
13
|
+
### `entriesOf( obj ): Array<[keyof T, T[keyof T]]>`
|
|
14
|
+
|
|
15
|
+
Like `Object.entries` but typed as `Array<[keyof T, T[keyof T]]>`.
|
|
16
|
+
|
|
17
|
+
## Usage
|
|
18
|
+
|
|
19
|
+
```ts
|
|
20
|
+
import {keysOf, entriesOf} from '@lipemat/js-helpers';
|
|
21
|
+
|
|
22
|
+
const config = {width: 100, label: 'box'};
|
|
23
|
+
|
|
24
|
+
keysOf( config ).forEach( key => {
|
|
25
|
+
// key is 'width' | 'label'
|
|
26
|
+
} );
|
|
27
|
+
|
|
28
|
+
entriesOf( config ).forEach( ( [ key, value ] ) => {
|
|
29
|
+
// fully typed key/value pair
|
|
30
|
+
} );
|
|
31
|
+
```
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @notice More robust versions are available from the `@lipemat/js-boilerplate-gutenberg` package.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Object.keys() with type inference.
|
|
7
|
+
*
|
|
8
|
+
* Instead of getting `string[]` we get `keyof T[]`.
|
|
9
|
+
*
|
|
10
|
+
* @since 4.3.0
|
|
11
|
+
*/
|
|
12
|
+
export function keysOf<T extends object>( obj: T ): Array<keyof T> {
|
|
13
|
+
return Object.keys( obj ) as Array<keyof T>;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Object.entries() with type inference.
|
|
19
|
+
*
|
|
20
|
+
* Instead of getting `Array<[string, any]>` we get `Array<[keyof T, T[keyof T]]>`.
|
|
21
|
+
*
|
|
22
|
+
* @since 4.3.0
|
|
23
|
+
*/
|
|
24
|
+
export function entriesOf<T extends object>( obj: T ): Array<[ keyof T, T[keyof T] ]> {
|
|
25
|
+
return Object.entries( obj ) as Array<[ keyof T, T[keyof T] ]>;
|
|
26
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# once
|
|
2
|
+
|
|
3
|
+
Lodash-free `once`. Wrap a function so it only runs the first time; subsequent calls return the cached result.
|
|
4
|
+
|
|
5
|
+
## Exports
|
|
6
|
+
|
|
7
|
+
### `once( fn ): Return<T>`
|
|
8
|
+
|
|
9
|
+
- `fn: T` — function to run a single time.
|
|
10
|
+
|
|
11
|
+
Returns a function with the same signature that caches and returns the first result.
|
|
12
|
+
|
|
13
|
+
### `Callback` / `Return<T>` (types)
|
|
14
|
+
|
|
15
|
+
Shared function-shape types used by `once`, `debounce`, `throttle`, and `memoize`.
|
|
16
|
+
|
|
17
|
+
## Usage
|
|
18
|
+
|
|
19
|
+
```ts
|
|
20
|
+
import {once} from '@lipemat/js-helpers';
|
|
21
|
+
|
|
22
|
+
const init = once( () => expensiveSetup() );
|
|
23
|
+
|
|
24
|
+
init(); // runs expensiveSetup()
|
|
25
|
+
init(); // returns the first result, no re-run
|
|
26
|
+
```
|
package/src/once/once.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// eslint-disable-next-line -- Only being used for a shape to guarantee the other types.
|
|
2
|
+
export type Callback = ( ...args: Array<any> ) => any;
|
|
3
|
+
export type Return<T extends Callback> = ( ...args: Parameters<T> ) => ReturnType<T>
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Similar to lodash once but without requiring lodash.
|
|
7
|
+
*
|
|
8
|
+
* @version 1.0.2
|
|
9
|
+
*/
|
|
10
|
+
export function once<T extends Callback>( fn: T ): Return<T> {
|
|
11
|
+
let result: ReturnType<T>;
|
|
12
|
+
let called = false;
|
|
13
|
+
|
|
14
|
+
return ( ...args: Parameters<T> ) => {
|
|
15
|
+
if ( ! called ) {
|
|
16
|
+
called = true;
|
|
17
|
+
result = fn( ...args );
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return result;
|
|
21
|
+
};
|
|
22
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# string
|
|
2
|
+
|
|
3
|
+
Small string helpers for random keys and slash management.
|
|
4
|
+
|
|
5
|
+
> More robust versions are available from the `@lipemat/js-boilerplate-gutenberg` package.
|
|
6
|
+
|
|
7
|
+
## Exports
|
|
8
|
+
|
|
9
|
+
### `generateRandomKey( length? ): string`
|
|
10
|
+
|
|
11
|
+
Return a random alphanumeric string. `length` defaults to `10`.
|
|
12
|
+
|
|
13
|
+
### `addLeadingSlash( url ): string`
|
|
14
|
+
|
|
15
|
+
Ensure `url` starts with a slash. Empty/whitespace input is returned unchanged.
|
|
16
|
+
|
|
17
|
+
### `addTrailingSlash( url ): string`
|
|
18
|
+
|
|
19
|
+
Ensure `url` ends with a slash. Empty/whitespace input is returned unchanged.
|
|
20
|
+
|
|
21
|
+
### `removeLeadingSlash( url ): string`
|
|
22
|
+
|
|
23
|
+
Remove a leading slash if present. Empty/whitespace input is returned unchanged.
|
|
24
|
+
|
|
25
|
+
### `removeTrailingSlash( url ): string`
|
|
26
|
+
|
|
27
|
+
Remove a trailing slash if present. Empty/whitespace input is returned unchanged.
|
|
28
|
+
|
|
29
|
+
## Usage
|
|
30
|
+
|
|
31
|
+
```ts
|
|
32
|
+
import {generateRandomKey, addTrailingSlash, removeLeadingSlash} from '@lipemat/js-helpers';
|
|
33
|
+
|
|
34
|
+
generateRandomKey(); // e.g. 'a8Kd0Lm2Qz'
|
|
35
|
+
addTrailingSlash( 'path/to' ); // 'path/to/'
|
|
36
|
+
removeLeadingSlash( '/path' ); // 'path'
|
|
37
|
+
```
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @notice More robust versions are available from the `@lipemat/js-boilerplate-gutenberg` package.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Generate a random key of a given length.
|
|
7
|
+
*
|
|
8
|
+
* @version 1.0.0
|
|
9
|
+
*/
|
|
10
|
+
export function generateRandomKey( length: number = 10 ) {
|
|
11
|
+
const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
|
12
|
+
let result = '';
|
|
13
|
+
for ( let i = 0; i < length; i++ ) {
|
|
14
|
+
result += characters.charAt( Math.floor( Math.random() * characters.length ) );
|
|
15
|
+
}
|
|
16
|
+
return result;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Adds a leading slash to a URL if it doesn't already have one.
|
|
22
|
+
*
|
|
23
|
+
* @version 1.0.0
|
|
24
|
+
*/
|
|
25
|
+
export function addLeadingSlash( url: string ): string {
|
|
26
|
+
const trimmedURL = url.trim();
|
|
27
|
+
if ( '' === trimmedURL ) {
|
|
28
|
+
return url;
|
|
29
|
+
}
|
|
30
|
+
return url?.replace( /^\/?/, '/' );
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Adds a trailing slash to a URL if it doesn't already have one.
|
|
36
|
+
*
|
|
37
|
+
* @version 1.0.0
|
|
38
|
+
*/
|
|
39
|
+
export function addTrailingSlash( url: string ): string {
|
|
40
|
+
const trimmedURL = url.trim();
|
|
41
|
+
if ( '' === trimmedURL ) {
|
|
42
|
+
return url;
|
|
43
|
+
}
|
|
44
|
+
return url.replace( /\/?$/, '/' );
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Removes a leading slash from a URL if it has one.
|
|
50
|
+
*
|
|
51
|
+
* @version 1.0.0
|
|
52
|
+
*/
|
|
53
|
+
export function removeLeadingSlash( url: string ): string {
|
|
54
|
+
const trimmedURL = url.trim();
|
|
55
|
+
if ( '' === trimmedURL ) {
|
|
56
|
+
return url;
|
|
57
|
+
}
|
|
58
|
+
return url.replace( /^\//, '' );
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Removes a trailing slash from a URL if it has one.
|
|
63
|
+
*
|
|
64
|
+
* @version 1.0.0
|
|
65
|
+
*/
|
|
66
|
+
export function removeTrailingSlash( url: string ): string {
|
|
67
|
+
const trimmedURL = url.trim();
|
|
68
|
+
if ( '' === trimmedURL ) {
|
|
69
|
+
return url;
|
|
70
|
+
}
|
|
71
|
+
return url.replace( /\/$/, '' );
|
|
72
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# throttle
|
|
2
|
+
|
|
3
|
+
Lodash-free throttle. Ensures `fn` runs at most once per `wait` ms.
|
|
4
|
+
|
|
5
|
+
## Exports
|
|
6
|
+
|
|
7
|
+
### `throttle( fn, wait? )`
|
|
8
|
+
|
|
9
|
+
- `fn: T` — function to throttle.
|
|
10
|
+
- `wait?: number` — minimum delay between calls in milliseconds, defaults to `300`.
|
|
11
|
+
|
|
12
|
+
Returns a throttled function plus:
|
|
13
|
+
|
|
14
|
+
- `.immediate( ...args )` — run `fn` right away, bypassing the wait.
|
|
15
|
+
- `.cancel()` — cancel a pending trailing call.
|
|
16
|
+
|
|
17
|
+
## Usage
|
|
18
|
+
|
|
19
|
+
```ts
|
|
20
|
+
import {throttle} from '@lipemat/js-helpers';
|
|
21
|
+
|
|
22
|
+
const onScroll = throttle( () => update(), 100 );
|
|
23
|
+
window.addEventListener( 'scroll', onScroll );
|
|
24
|
+
|
|
25
|
+
onScroll.immediate(); // run now
|
|
26
|
+
onScroll.cancel(); // cancel a pending call
|
|
27
|
+
```
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import type {Callback, Return} from '../once/once.js';
|
|
2
|
+
|
|
3
|
+
type Result<T extends Callback> = {
|
|
4
|
+
( ...args: Parameters<T> ): ReturnType<T>;
|
|
5
|
+
immediate: ( ...args: Parameters<T> ) => ReturnType<T>;
|
|
6
|
+
cancel: () => void;
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Alternative to lodash throttle.
|
|
11
|
+
*
|
|
12
|
+
* - Additional support for `immediate` execution to bypass the wait time.
|
|
13
|
+
* - Returns a promise to allow waiting for a completion of the throttled function.
|
|
14
|
+
*
|
|
15
|
+
* @version 1.0.1
|
|
16
|
+
*/
|
|
17
|
+
export function throttle<T extends Callback>( fn: T, wait = 300 ): Result<T> {
|
|
18
|
+
let lastTime = 0;
|
|
19
|
+
let timer: number | NodeJS.Timeout | undefined;
|
|
20
|
+
let result: ReturnType<T>;
|
|
21
|
+
|
|
22
|
+
const throttled = function( ...args: Parameters<T> ): Promise<ReturnType<T>> {
|
|
23
|
+
const now = Date.now();
|
|
24
|
+
const remaining = now - lastTime;
|
|
25
|
+
|
|
26
|
+
if ( remaining >= wait ) {
|
|
27
|
+
clearTimeout( timer );
|
|
28
|
+
timer = undefined;
|
|
29
|
+
lastTime = now;
|
|
30
|
+
return fn( ...args );
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if ( 'undefined' === typeof timer ) {
|
|
34
|
+
timer = setTimeout( () => {
|
|
35
|
+
lastTime = Date.now();
|
|
36
|
+
result = fn( ...args );
|
|
37
|
+
timer = undefined;
|
|
38
|
+
}, wait - remaining );
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return result;
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
// Immediate execution method.
|
|
45
|
+
throttled.immediate = function( ...args: Parameters<T> ): Return<T> {
|
|
46
|
+
clearTimeout( timer );
|
|
47
|
+
lastTime = Date.now();
|
|
48
|
+
return fn( ...args );
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
// Cancel method.
|
|
52
|
+
throttled.cancel = function() {
|
|
53
|
+
clearTimeout( timer );
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
return throttled as Result<T>;
|
|
57
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# url
|
|
2
|
+
|
|
3
|
+
Read and append URL query arguments. Inputs are sanitized through [DOMPurify](https://github.com/cure53/DOMPurify).
|
|
4
|
+
|
|
5
|
+
> More robust versions are available from the `@lipemat/js-boilerplate-gutenberg` and `@wordpress/url` packages.
|
|
6
|
+
|
|
7
|
+
## Exports
|
|
8
|
+
|
|
9
|
+
### `getUrlParam( parameter, defaultValue? ): string | null`
|
|
10
|
+
|
|
11
|
+
Return the value of `parameter` from the current `window.location.search`, or `defaultValue` (default `''`) when it is absent.
|
|
12
|
+
|
|
13
|
+
### `addQueryArgs( url?, args ): string`
|
|
14
|
+
|
|
15
|
+
Append `args` to `url` as query parameters and return the resulting URL string.
|
|
16
|
+
|
|
17
|
+
- `url?: string` — base URL, defaults to `''`.
|
|
18
|
+
- `args: { [name: string]: string | number }` — parameters to append.
|
|
19
|
+
|
|
20
|
+
## Usage
|
|
21
|
+
|
|
22
|
+
```ts
|
|
23
|
+
import {getUrlParam, addQueryArgs} from '@lipemat/js-helpers';
|
|
24
|
+
|
|
25
|
+
getUrlParam( 'page', '1' ); // current ?page value or '1'
|
|
26
|
+
addQueryArgs( 'https://example.com', {page: 2, sort: 'name'} );
|
|
27
|
+
// 'https://example.com/?page=2&sort=name'
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Notes
|
|
31
|
+
|
|
32
|
+
`getUrlParam` requires `window`; intended for browser use.
|