@wordpress/compose 7.6.0 → 7.8.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/CHANGELOG.md +11 -2
- package/README.md +41 -11
- package/build/hooks/use-copy-on-click/index.js +5 -5
- package/build/hooks/use-copy-on-click/index.js.map +1 -1
- package/build/hooks/use-drop-zone/index.js +1 -0
- package/build/hooks/use-drop-zone/index.js.map +1 -1
- package/build/hooks/use-event/index.js +51 -0
- package/build/hooks/use-event/index.js.map +1 -0
- package/build/hooks/use-focus-on-mount/index.js +11 -11
- package/build/hooks/use-focus-on-mount/index.js.map +1 -1
- package/build/hooks/use-focus-outside/index.js +11 -11
- package/build/hooks/use-focus-outside/index.js.map +1 -1
- package/build/hooks/use-keyboard-shortcut/index.js +3 -3
- package/build/hooks/use-keyboard-shortcut/index.js.map +1 -1
- package/build/hooks/use-merge-refs/index.js +12 -12
- package/build/hooks/use-merge-refs/index.js.map +1 -1
- package/build/hooks/use-ref-effect/index.js +4 -4
- package/build/hooks/use-ref-effect/index.js.map +1 -1
- package/build/hooks/use-resize-observer/_legacy/index.js +139 -0
- package/build/hooks/use-resize-observer/_legacy/index.js.map +1 -0
- package/build/hooks/use-resize-observer/_legacy/index.native.js.map +1 -0
- package/build/hooks/use-resize-observer/index.js +70 -212
- package/build/hooks/use-resize-observer/index.js.map +1 -1
- package/build/index.js +8 -0
- package/build/index.js.map +1 -1
- package/build-module/hooks/use-copy-on-click/index.js +5 -5
- package/build-module/hooks/use-copy-on-click/index.js.map +1 -1
- package/build-module/hooks/use-drop-zone/index.js +1 -0
- package/build-module/hooks/use-drop-zone/index.js.map +1 -1
- package/build-module/hooks/use-event/index.js +45 -0
- package/build-module/hooks/use-event/index.js.map +1 -0
- package/build-module/hooks/use-focus-on-mount/index.js +11 -11
- package/build-module/hooks/use-focus-on-mount/index.js.map +1 -1
- package/build-module/hooks/use-focus-outside/index.js +11 -11
- package/build-module/hooks/use-focus-outside/index.js.map +1 -1
- package/build-module/hooks/use-keyboard-shortcut/index.js +3 -3
- package/build-module/hooks/use-keyboard-shortcut/index.js.map +1 -1
- package/build-module/hooks/use-merge-refs/index.js +12 -12
- package/build-module/hooks/use-merge-refs/index.js.map +1 -1
- package/build-module/hooks/use-ref-effect/index.js +4 -4
- package/build-module/hooks/use-ref-effect/index.js.map +1 -1
- package/build-module/hooks/use-resize-observer/_legacy/index.js +130 -0
- package/build-module/hooks/use-resize-observer/_legacy/index.js.map +1 -0
- package/build-module/hooks/use-resize-observer/_legacy/index.native.js.map +1 -0
- package/build-module/hooks/use-resize-observer/index.js +69 -213
- package/build-module/hooks/use-resize-observer/index.js.map +1 -1
- package/build-module/index.js +1 -0
- package/build-module/index.js.map +1 -1
- package/build-types/hooks/use-drop-zone/index.d.ts.map +1 -1
- package/build-types/hooks/use-event/index.d.ts +32 -0
- package/build-types/hooks/use-event/index.d.ts.map +1 -0
- package/build-types/hooks/use-focus-on-mount/index.d.ts.map +1 -1
- package/build-types/hooks/use-merge-refs/index.d.ts.map +1 -1
- package/build-types/hooks/use-resize-observer/_legacy/index.d.ts +32 -0
- package/build-types/hooks/use-resize-observer/_legacy/index.d.ts.map +1 -0
- package/build-types/hooks/use-resize-observer/index.d.ts +42 -14
- package/build-types/hooks/use-resize-observer/index.d.ts.map +1 -1
- package/build-types/index.d.ts +1 -0
- package/package.json +9 -9
- package/src/hooks/use-copy-on-click/index.js +5 -5
- package/src/hooks/use-drop-zone/index.js +2 -0
- package/src/hooks/use-event/index.ts +51 -0
- package/src/hooks/use-focus-on-mount/index.js +11 -13
- package/src/hooks/use-focus-outside/index.ts +11 -11
- package/src/hooks/use-keyboard-shortcut/index.js +3 -3
- package/src/hooks/use-merge-refs/index.js +15 -13
- package/src/hooks/use-ref-effect/index.ts +4 -4
- package/src/hooks/use-resize-observer/_legacy/index.tsx +143 -0
- package/src/hooks/use-resize-observer/{test → _legacy/test}/index.native.js +1 -1
- package/src/hooks/use-resize-observer/index.ts +119 -0
- package/src/index.js +1 -0
- package/tsconfig.tsbuildinfo +1 -1
- package/build/hooks/use-resize-observer/index.native.js.map +0 -1
- package/build-module/hooks/use-resize-observer/index.native.js.map +0 -1
- package/src/hooks/use-resize-observer/index.tsx +0 -362
- /package/build/hooks/use-resize-observer/{index.native.js → _legacy/index.native.js} +0 -0
- /package/build-module/hooks/use-resize-observer/{index.native.js → _legacy/index.native.js} +0 -0
- /package/src/hooks/use-resize-observer/{index.native.js → _legacy/index.native.js} +0 -0
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Any function.
|
|
3
|
+
*/
|
|
4
|
+
export type AnyFunction = (...args: any) => any;
|
|
5
|
+
/**
|
|
6
|
+
* Creates a stable callback function that has access to the latest state and
|
|
7
|
+
* can be used within event handlers and effect callbacks. Throws when used in
|
|
8
|
+
* the render phase.
|
|
9
|
+
*
|
|
10
|
+
* @param callback The callback function to wrap.
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
*
|
|
14
|
+
* ```tsx
|
|
15
|
+
* function Component( props ) {
|
|
16
|
+
* const onClick = useEvent( props.onClick );
|
|
17
|
+
* useEffect( () => {
|
|
18
|
+
* onClick();
|
|
19
|
+
* // Won't trigger the effect again when props.onClick is updated.
|
|
20
|
+
* }, [ onClick ] );
|
|
21
|
+
* // Won't re-render Button when props.onClick is updated (if `Button` is
|
|
22
|
+
* // wrapped in `React.memo`).
|
|
23
|
+
* return <Button onClick={ onClick } />;
|
|
24
|
+
* }
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
export default function useEvent<T extends AnyFunction>(
|
|
28
|
+
/**
|
|
29
|
+
* The callback function to wrap.
|
|
30
|
+
*/
|
|
31
|
+
callback?: T): T;
|
|
32
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/hooks/use-event/index.ts"],"names":[],"mappings":"AAKA;;GAEG;AACH,MAAM,MAAM,WAAW,GAAG,CAAE,GAAG,IAAI,EAAE,GAAG,KAAM,GAAG,CAAC;AAElD;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,CAAC,OAAO,UAAU,QAAQ,CAAE,CAAC,SAAS,WAAW;AACtD;;GAEG;AACH,QAAQ,CAAC,EAAE,CAAC,GAaP,CAAC,CACN"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/hooks/use-focus-on-mount/index.js"],"names":[],"mappings":"AAWA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,uDAlBW,OAAO,GAAG,cAAc,GACvB,OAAO,OAAO,EAAE,WAAW,CAAC,WAAW,CAAC,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/hooks/use-focus-on-mount/index.js"],"names":[],"mappings":"AAWA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,uDAlBW,OAAO,GAAG,cAAc,GACvB,OAAO,OAAO,EAAE,WAAW,CAAC,WAAW,CAAC,CAqEnD"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/hooks/use-merge-refs/index.js"],"names":[],"mappings":"AA4BA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CG;AACH,qCALwC,IAAI,SAA/B,OAAQ,OAAO,EAAE,GAAG,CAAC,GAAG,CAAE,QAC5B,KAAK,CAAC,IAAI,CAAC,GAEV,OAAO,OAAO,EAAE,WAAW,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/hooks/use-merge-refs/index.js"],"names":[],"mappings":"AA4BA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CG;AACH,qCALwC,IAAI,SAA/B,OAAQ,OAAO,EAAE,GAAG,CAAC,GAAG,CAAE,QAC5B,KAAK,CAAC,IAAI,CAAC,GAEV,OAAO,OAAO,EAAE,WAAW,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,CA+DzD;wBA7HY,CAAC,IACD,CAAC,SAAS,OAAO,OAAO,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,GAAG,KAAK"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* External dependencies
|
|
3
|
+
*/
|
|
4
|
+
import type { ReactElement } from 'react';
|
|
5
|
+
export type ObservedSize = {
|
|
6
|
+
width: number | null;
|
|
7
|
+
height: number | null;
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* Hook which allows to listen to the resize event of any target element when it changes size.
|
|
11
|
+
* _Note: `useResizeObserver` will report `null` sizes until after first render.
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
*
|
|
15
|
+
* ```js
|
|
16
|
+
* const App = () => {
|
|
17
|
+
* const [ resizeListener, sizes ] = useResizeObserver();
|
|
18
|
+
*
|
|
19
|
+
* return (
|
|
20
|
+
* <div>
|
|
21
|
+
* { resizeListener }
|
|
22
|
+
* Your content here
|
|
23
|
+
* </div>
|
|
24
|
+
* );
|
|
25
|
+
* };
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
export default function useLegacyResizeObserver(): [
|
|
29
|
+
ReactElement,
|
|
30
|
+
ObservedSize
|
|
31
|
+
];
|
|
32
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/hooks/use-resize-observer/_legacy/index.tsx"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,OAAO,CAAC;AAW1C,MAAM,MAAM,YAAY,GAAG;IAC1B,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;CACtB,CAAC;AAwFF;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,CAAC,OAAO,UAAU,uBAAuB,IAAI;IAClD,YAAY;IACZ,YAAY;CACZ,CAeA"}
|
|
@@ -1,15 +1,49 @@
|
|
|
1
|
+
import type { ObservedSize } from './_legacy';
|
|
1
2
|
/**
|
|
2
3
|
* External dependencies
|
|
3
4
|
*/
|
|
4
5
|
import type { ReactElement } from 'react';
|
|
5
|
-
declare global {
|
|
6
|
-
interface ResizeObserverEntry {
|
|
7
|
-
readonly devicePixelContentBoxSize: ReadonlyArray<ResizeObserverSize>;
|
|
8
|
-
}
|
|
9
|
-
}
|
|
10
6
|
/**
|
|
11
|
-
*
|
|
12
|
-
*
|
|
7
|
+
* Sets up a [`ResizeObserver`](https://developer.mozilla.org/en-US/docs/Web/API/Resize_Observer_API)
|
|
8
|
+
* for an HTML or SVG element.
|
|
9
|
+
*
|
|
10
|
+
* Pass the returned setter as a callback ref to the React element you want
|
|
11
|
+
* to observe, or use it in layout effects for advanced use cases.
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
*
|
|
15
|
+
* ```tsx
|
|
16
|
+
* const setElement = useResizeObserver(
|
|
17
|
+
* ( resizeObserverEntries ) => console.log( resizeObserverEntries ),
|
|
18
|
+
* { box: 'border-box' }
|
|
19
|
+
* );
|
|
20
|
+
* <div ref={ setElement } />;
|
|
21
|
+
*
|
|
22
|
+
* // The setter can be used in other ways, for example:
|
|
23
|
+
* useLayoutEffect( () => {
|
|
24
|
+
* setElement( document.querySelector( `data-element-id="${ elementId }"` ) );
|
|
25
|
+
* }, [ elementId ] );
|
|
26
|
+
* ```
|
|
27
|
+
*
|
|
28
|
+
* @param callback The `ResizeObserver` callback - [MDN docs](https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver/ResizeObserver#callback).
|
|
29
|
+
* @param options Options passed to `ResizeObserver.observe` when called - [MDN docs](https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver/observe#options). Changes will be ignored.
|
|
30
|
+
*/
|
|
31
|
+
export default function useResizeObserver<T extends Element>(
|
|
32
|
+
/**
|
|
33
|
+
* The `ResizeObserver` callback - [MDN docs](https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver/ResizeObserver#callback).
|
|
34
|
+
*/
|
|
35
|
+
callback: ResizeObserverCallback,
|
|
36
|
+
/**
|
|
37
|
+
* Options passed to `ResizeObserver.observe` when called - [MDN docs](https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver/observe#options). Changes will be ignored.
|
|
38
|
+
*/
|
|
39
|
+
options?: ResizeObserverOptions): (element?: T | null) => void;
|
|
40
|
+
/**
|
|
41
|
+
* **This is a legacy API and should not be used.**
|
|
42
|
+
*
|
|
43
|
+
* @deprecated Use the other `useResizeObserver` API instead: `const ref = useResizeObserver( ( entries ) => { ... } )`.
|
|
44
|
+
*
|
|
45
|
+
* Hook which allows to listen to the resize event of any target element when it changes size.
|
|
46
|
+
* _Note: `useResizeObserver` will report `null` sizes until after first render.
|
|
13
47
|
*
|
|
14
48
|
* @example
|
|
15
49
|
*
|
|
@@ -26,11 +60,5 @@ declare global {
|
|
|
26
60
|
* };
|
|
27
61
|
* ```
|
|
28
62
|
*/
|
|
29
|
-
export default function
|
|
30
|
-
ReactElement,
|
|
31
|
-
{
|
|
32
|
-
width: number | null;
|
|
33
|
-
height: number | null;
|
|
34
|
-
}
|
|
35
|
-
];
|
|
63
|
+
export default function useResizeObserver(): [ReactElement, ObservedSize];
|
|
36
64
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/hooks/use-resize-observer/index.
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/hooks/use-resize-observer/index.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAE9C;;GAEG;AACH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,OAAO,CAAC;AAqC1C;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,MAAM,CAAC,OAAO,UAAU,iBAAiB,CAAE,CAAC,SAAS,OAAO;AAC3D;;GAEG;AACH,QAAQ,EAAE,sBAAsB;AAChC;;GAEG;AACH,OAAO,CAAC,EAAE,qBAAqB,GAC7B,CAAE,OAAO,CAAC,EAAE,CAAC,GAAG,IAAI,KAAM,IAAI,CAAC;AAElC;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,MAAM,CAAC,OAAO,UAAU,iBAAiB,IAAI,CAAE,YAAY,EAAE,YAAY,CAAE,CAAC"}
|
package/build-types/index.d.ts
CHANGED
|
@@ -15,6 +15,7 @@ export { default as useCopyOnClick } from "./hooks/use-copy-on-click";
|
|
|
15
15
|
export { default as useCopyToClipboard } from "./hooks/use-copy-to-clipboard";
|
|
16
16
|
export { default as __experimentalUseDialog } from "./hooks/use-dialog";
|
|
17
17
|
export { default as useDisabled } from "./hooks/use-disabled";
|
|
18
|
+
export { default as useEvent } from "./hooks/use-event";
|
|
18
19
|
export { default as __experimentalUseDragging } from "./hooks/use-dragging";
|
|
19
20
|
export { default as useFocusOnMount } from "./hooks/use-focus-on-mount";
|
|
20
21
|
export { default as __experimentalUseFocusOutside } from "./hooks/use-focus-outside";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wordpress/compose",
|
|
3
|
-
"version": "7.
|
|
3
|
+
"version": "7.8.0",
|
|
4
4
|
"description": "WordPress higher-order components (HOCs).",
|
|
5
5
|
"author": "The WordPress Contributors",
|
|
6
6
|
"license": "GPL-2.0-or-later",
|
|
@@ -32,13 +32,13 @@
|
|
|
32
32
|
"dependencies": {
|
|
33
33
|
"@babel/runtime": "^7.16.0",
|
|
34
34
|
"@types/mousetrap": "^1.6.8",
|
|
35
|
-
"@wordpress/deprecated": "^4.
|
|
36
|
-
"@wordpress/dom": "^4.
|
|
37
|
-
"@wordpress/element": "^6.
|
|
38
|
-
"@wordpress/is-shallow-equal": "^5.
|
|
39
|
-
"@wordpress/keycodes": "^4.
|
|
40
|
-
"@wordpress/priority-queue": "^3.
|
|
41
|
-
"@wordpress/undo-manager": "^1.
|
|
35
|
+
"@wordpress/deprecated": "^4.8.0",
|
|
36
|
+
"@wordpress/dom": "^4.8.0",
|
|
37
|
+
"@wordpress/element": "^6.8.0",
|
|
38
|
+
"@wordpress/is-shallow-equal": "^5.8.0",
|
|
39
|
+
"@wordpress/keycodes": "^4.8.0",
|
|
40
|
+
"@wordpress/priority-queue": "^3.8.0",
|
|
41
|
+
"@wordpress/undo-manager": "^1.8.0",
|
|
42
42
|
"change-case": "^4.1.2",
|
|
43
43
|
"clipboard": "^2.0.11",
|
|
44
44
|
"mousetrap": "^1.6.5",
|
|
@@ -50,5 +50,5 @@
|
|
|
50
50
|
"publishConfig": {
|
|
51
51
|
"access": "public"
|
|
52
52
|
},
|
|
53
|
-
"gitHead": "
|
|
53
|
+
"gitHead": "cecf5e14d317aa67407f77a7e5c8b6a43016bd42"
|
|
54
54
|
}
|
|
@@ -31,7 +31,7 @@ export default function useCopyOnClick( ref, text, timeout = 4000 ) {
|
|
|
31
31
|
} );
|
|
32
32
|
|
|
33
33
|
/** @type {import('react').MutableRefObject<Clipboard | undefined>} */
|
|
34
|
-
const
|
|
34
|
+
const clipboardRef = useRef();
|
|
35
35
|
const [ hasCopied, setHasCopied ] = useState( false );
|
|
36
36
|
|
|
37
37
|
useEffect( () => {
|
|
@@ -43,11 +43,11 @@ export default function useCopyOnClick( ref, text, timeout = 4000 ) {
|
|
|
43
43
|
}
|
|
44
44
|
|
|
45
45
|
// Clipboard listens to click events.
|
|
46
|
-
|
|
46
|
+
clipboardRef.current = new Clipboard( ref.current, {
|
|
47
47
|
text: () => ( typeof text === 'function' ? text() : text ),
|
|
48
48
|
} );
|
|
49
49
|
|
|
50
|
-
|
|
50
|
+
clipboardRef.current.on( 'success', ( { clearSelection, trigger } ) => {
|
|
51
51
|
// Clearing selection will move focus back to the triggering button,
|
|
52
52
|
// ensuring that it is not reset to the body, and further that it is
|
|
53
53
|
// kept within the rendered node.
|
|
@@ -66,8 +66,8 @@ export default function useCopyOnClick( ref, text, timeout = 4000 ) {
|
|
|
66
66
|
} );
|
|
67
67
|
|
|
68
68
|
return () => {
|
|
69
|
-
if (
|
|
70
|
-
|
|
69
|
+
if ( clipboardRef.current ) {
|
|
70
|
+
clipboardRef.current.destroy();
|
|
71
71
|
}
|
|
72
72
|
clearTimeout( timeoutId );
|
|
73
73
|
};
|
|
@@ -86,6 +86,7 @@ export default function useDropZone( {
|
|
|
86
86
|
*/
|
|
87
87
|
function isElementInZone( targetToCheck ) {
|
|
88
88
|
const { defaultView } = ownerDocument;
|
|
89
|
+
|
|
89
90
|
if (
|
|
90
91
|
! targetToCheck ||
|
|
91
92
|
! defaultView ||
|
|
@@ -165,6 +166,7 @@ export default function useDropZone( {
|
|
|
165
166
|
// zone.
|
|
166
167
|
// Note: This is not entirely reliable in Safari due to this bug
|
|
167
168
|
// https://bugs.webkit.org/show_bug.cgi?id=66547
|
|
169
|
+
|
|
168
170
|
if ( isElementInZone( event.relatedTarget ) ) {
|
|
169
171
|
return;
|
|
170
172
|
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WordPress dependencies
|
|
3
|
+
*/
|
|
4
|
+
import { useRef, useInsertionEffect, useCallback } from '@wordpress/element';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Any function.
|
|
8
|
+
*/
|
|
9
|
+
export type AnyFunction = ( ...args: any ) => any;
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Creates a stable callback function that has access to the latest state and
|
|
13
|
+
* can be used within event handlers and effect callbacks. Throws when used in
|
|
14
|
+
* the render phase.
|
|
15
|
+
*
|
|
16
|
+
* @param callback The callback function to wrap.
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
*
|
|
20
|
+
* ```tsx
|
|
21
|
+
* function Component( props ) {
|
|
22
|
+
* const onClick = useEvent( props.onClick );
|
|
23
|
+
* useEffect( () => {
|
|
24
|
+
* onClick();
|
|
25
|
+
* // Won't trigger the effect again when props.onClick is updated.
|
|
26
|
+
* }, [ onClick ] );
|
|
27
|
+
* // Won't re-render Button when props.onClick is updated (if `Button` is
|
|
28
|
+
* // wrapped in `React.memo`).
|
|
29
|
+
* return <Button onClick={ onClick } />;
|
|
30
|
+
* }
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
export default function useEvent< T extends AnyFunction >(
|
|
34
|
+
/**
|
|
35
|
+
* The callback function to wrap.
|
|
36
|
+
*/
|
|
37
|
+
callback?: T
|
|
38
|
+
) {
|
|
39
|
+
const ref = useRef< AnyFunction | undefined >( () => {
|
|
40
|
+
throw new Error(
|
|
41
|
+
'Callbacks created with `useEvent` cannot be called during rendering.'
|
|
42
|
+
);
|
|
43
|
+
} );
|
|
44
|
+
useInsertionEffect( () => {
|
|
45
|
+
ref.current = callback;
|
|
46
|
+
} );
|
|
47
|
+
return useCallback< AnyFunction >(
|
|
48
|
+
( ...args ) => ref.current?.( ...args ),
|
|
49
|
+
[]
|
|
50
|
+
) as T;
|
|
51
|
+
}
|
|
@@ -49,7 +49,7 @@ export default function useFocusOnMount( focusOnMount = 'firstElement' ) {
|
|
|
49
49
|
};
|
|
50
50
|
|
|
51
51
|
/** @type {import('react').MutableRefObject<ReturnType<setTimeout> | undefined>} */
|
|
52
|
-
const
|
|
52
|
+
const timerIdRef = useRef();
|
|
53
53
|
|
|
54
54
|
useEffect( () => {
|
|
55
55
|
focusOnMountRef.current = focusOnMount;
|
|
@@ -64,23 +64,21 @@ export default function useFocusOnMount( focusOnMount = 'firstElement' ) {
|
|
|
64
64
|
return;
|
|
65
65
|
}
|
|
66
66
|
|
|
67
|
-
if ( focusOnMountRef.current
|
|
68
|
-
|
|
69
|
-
const firstTabbable = focus.tabbable.find( node )[ 0 ];
|
|
70
|
-
|
|
71
|
-
if ( firstTabbable ) {
|
|
72
|
-
setFocus( firstTabbable );
|
|
73
|
-
}
|
|
74
|
-
}, 0 );
|
|
75
|
-
|
|
67
|
+
if ( focusOnMountRef.current !== 'firstElement' ) {
|
|
68
|
+
setFocus( node );
|
|
76
69
|
return;
|
|
77
70
|
}
|
|
78
71
|
|
|
79
|
-
|
|
72
|
+
timerIdRef.current = setTimeout( () => {
|
|
73
|
+
const firstTabbable = focus.tabbable.find( node )[ 0 ];
|
|
74
|
+
if ( firstTabbable ) {
|
|
75
|
+
setFocus( firstTabbable );
|
|
76
|
+
}
|
|
77
|
+
}, 0 );
|
|
80
78
|
|
|
81
79
|
return () => {
|
|
82
|
-
if (
|
|
83
|
-
clearTimeout(
|
|
80
|
+
if ( timerIdRef.current ) {
|
|
81
|
+
clearTimeout( timerIdRef.current );
|
|
84
82
|
}
|
|
85
83
|
};
|
|
86
84
|
}, [] );
|
|
@@ -71,20 +71,20 @@ type UseFocusOutsideReturn = {
|
|
|
71
71
|
export default function useFocusOutside(
|
|
72
72
|
onFocusOutside: ( ( event: React.FocusEvent ) => void ) | undefined
|
|
73
73
|
): UseFocusOutsideReturn {
|
|
74
|
-
const
|
|
74
|
+
const currentOnFocusOutsideRef = useRef( onFocusOutside );
|
|
75
75
|
useEffect( () => {
|
|
76
|
-
|
|
76
|
+
currentOnFocusOutsideRef.current = onFocusOutside;
|
|
77
77
|
}, [ onFocusOutside ] );
|
|
78
78
|
|
|
79
|
-
const
|
|
79
|
+
const preventBlurCheckRef = useRef( false );
|
|
80
80
|
|
|
81
|
-
const
|
|
81
|
+
const blurCheckTimeoutIdRef = useRef< number | undefined >();
|
|
82
82
|
|
|
83
83
|
/**
|
|
84
84
|
* Cancel a blur check timeout.
|
|
85
85
|
*/
|
|
86
86
|
const cancelBlurCheck = useCallback( () => {
|
|
87
|
-
clearTimeout(
|
|
87
|
+
clearTimeout( blurCheckTimeoutIdRef.current );
|
|
88
88
|
}, [] );
|
|
89
89
|
|
|
90
90
|
// Cancel blur checks on unmount.
|
|
@@ -116,9 +116,9 @@ export default function useFocusOutside(
|
|
|
116
116
|
const isInteractionEnd = [ 'mouseup', 'touchend' ].includes( type );
|
|
117
117
|
|
|
118
118
|
if ( isInteractionEnd ) {
|
|
119
|
-
|
|
119
|
+
preventBlurCheckRef.current = false;
|
|
120
120
|
} else if ( isFocusNormalizedButton( target ) ) {
|
|
121
|
-
|
|
121
|
+
preventBlurCheckRef.current = true;
|
|
122
122
|
}
|
|
123
123
|
}, [] );
|
|
124
124
|
|
|
@@ -135,7 +135,7 @@ export default function useFocusOutside(
|
|
|
135
135
|
event.persist();
|
|
136
136
|
|
|
137
137
|
// Skip blur check if clicking button. See `normalizeButtonFocus`.
|
|
138
|
-
if (
|
|
138
|
+
if ( preventBlurCheckRef.current ) {
|
|
139
139
|
return;
|
|
140
140
|
}
|
|
141
141
|
|
|
@@ -156,7 +156,7 @@ export default function useFocusOutside(
|
|
|
156
156
|
return;
|
|
157
157
|
}
|
|
158
158
|
|
|
159
|
-
|
|
159
|
+
blurCheckTimeoutIdRef.current = setTimeout( () => {
|
|
160
160
|
// If document is not focused then focus should remain
|
|
161
161
|
// inside the wrapped component and therefore we cancel
|
|
162
162
|
// this blur event thereby leaving focus in place.
|
|
@@ -166,8 +166,8 @@ export default function useFocusOutside(
|
|
|
166
166
|
return;
|
|
167
167
|
}
|
|
168
168
|
|
|
169
|
-
if ( 'function' === typeof
|
|
170
|
-
|
|
169
|
+
if ( 'function' === typeof currentOnFocusOutsideRef.current ) {
|
|
170
|
+
currentOnFocusOutsideRef.current( event );
|
|
171
171
|
}
|
|
172
172
|
}, 0 );
|
|
173
173
|
}, [] );
|
|
@@ -42,9 +42,9 @@ function useKeyboardShortcut(
|
|
|
42
42
|
target,
|
|
43
43
|
} = {}
|
|
44
44
|
) {
|
|
45
|
-
const
|
|
45
|
+
const currentCallbackRef = useRef( callback );
|
|
46
46
|
useEffect( () => {
|
|
47
|
-
|
|
47
|
+
currentCallbackRef.current = callback;
|
|
48
48
|
}, [ callback ] );
|
|
49
49
|
|
|
50
50
|
useEffect( () => {
|
|
@@ -93,7 +93,7 @@ function useKeyboardShortcut(
|
|
|
93
93
|
/** @type {[e: import('mousetrap').ExtendedKeyboardEvent, combo: string]} */ ...args
|
|
94
94
|
) =>
|
|
95
95
|
/* eslint-enable jsdoc/valid-types */
|
|
96
|
-
|
|
96
|
+
currentCallbackRef.current( ...args ),
|
|
97
97
|
eventName
|
|
98
98
|
);
|
|
99
99
|
} );
|
|
@@ -71,28 +71,28 @@ function assignRef( ref, value ) {
|
|
|
71
71
|
*/
|
|
72
72
|
export default function useMergeRefs( refs ) {
|
|
73
73
|
const element = useRef();
|
|
74
|
-
const
|
|
75
|
-
const
|
|
74
|
+
const isAttachedRef = useRef( false );
|
|
75
|
+
const didElementChangeRef = useRef( false );
|
|
76
76
|
/* eslint-disable jsdoc/no-undefined-types */
|
|
77
77
|
/** @type {import('react').MutableRefObject<TRef[]>} */
|
|
78
78
|
/* eslint-enable jsdoc/no-undefined-types */
|
|
79
|
-
const
|
|
80
|
-
const
|
|
79
|
+
const previousRefsRef = useRef( [] );
|
|
80
|
+
const currentRefsRef = useRef( refs );
|
|
81
81
|
|
|
82
82
|
// Update on render before the ref callback is called, so the ref callback
|
|
83
83
|
// always has access to the current refs.
|
|
84
|
-
|
|
84
|
+
currentRefsRef.current = refs;
|
|
85
85
|
|
|
86
86
|
// If any of the refs change, call the previous ref with `null` and the new
|
|
87
87
|
// ref with the node, except when the element changes in the same cycle, in
|
|
88
88
|
// which case the ref callbacks will already have been called.
|
|
89
89
|
useLayoutEffect( () => {
|
|
90
90
|
if (
|
|
91
|
-
|
|
92
|
-
|
|
91
|
+
didElementChangeRef.current === false &&
|
|
92
|
+
isAttachedRef.current === true
|
|
93
93
|
) {
|
|
94
94
|
refs.forEach( ( ref, index ) => {
|
|
95
|
-
const previousRef =
|
|
95
|
+
const previousRef = previousRefsRef.current[ index ];
|
|
96
96
|
if ( ref !== previousRef ) {
|
|
97
97
|
assignRef( previousRef, null );
|
|
98
98
|
assignRef( ref, element.current );
|
|
@@ -100,13 +100,13 @@ export default function useMergeRefs( refs ) {
|
|
|
100
100
|
} );
|
|
101
101
|
}
|
|
102
102
|
|
|
103
|
-
|
|
103
|
+
previousRefsRef.current = refs;
|
|
104
104
|
}, refs );
|
|
105
105
|
|
|
106
106
|
// No dependencies, must be reset after every render so ref callbacks are
|
|
107
107
|
// correctly called after a ref change.
|
|
108
108
|
useLayoutEffect( () => {
|
|
109
|
-
|
|
109
|
+
didElementChangeRef.current = false;
|
|
110
110
|
} );
|
|
111
111
|
|
|
112
112
|
// There should be no dependencies so that `callback` is only called when
|
|
@@ -116,12 +116,14 @@ export default function useMergeRefs( refs ) {
|
|
|
116
116
|
// dependency change.
|
|
117
117
|
assignRef( element, value );
|
|
118
118
|
|
|
119
|
-
|
|
120
|
-
|
|
119
|
+
didElementChangeRef.current = true;
|
|
120
|
+
isAttachedRef.current = value !== null;
|
|
121
121
|
|
|
122
122
|
// When an element changes, the current ref callback should be called
|
|
123
123
|
// with the new element and the previous one with `null`.
|
|
124
|
-
const refsToAssign = value
|
|
124
|
+
const refsToAssign = value
|
|
125
|
+
? currentRefsRef.current
|
|
126
|
+
: previousRefsRef.current;
|
|
125
127
|
|
|
126
128
|
// Update the latest refs.
|
|
127
129
|
for ( const ref of refsToAssign ) {
|
|
@@ -31,12 +31,12 @@ export default function useRefEffect< TElement = Node >(
|
|
|
31
31
|
callback: ( node: TElement ) => ( () => void ) | void,
|
|
32
32
|
dependencies: DependencyList
|
|
33
33
|
): RefCallback< TElement | null > {
|
|
34
|
-
const
|
|
34
|
+
const cleanupRef = useRef< ( () => void ) | void >();
|
|
35
35
|
return useCallback( ( node: TElement | null ) => {
|
|
36
36
|
if ( node ) {
|
|
37
|
-
|
|
38
|
-
} else if (
|
|
39
|
-
|
|
37
|
+
cleanupRef.current = callback( node );
|
|
38
|
+
} else if ( cleanupRef.current ) {
|
|
39
|
+
cleanupRef.current();
|
|
40
40
|
}
|
|
41
41
|
}, dependencies );
|
|
42
42
|
}
|