@wordpress/compose 8.1.1 → 8.1.2-next.v.202606191442.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 +42 -38
- package/build/hooks/use-constrained-tabbing/index.cjs +3 -9
- package/build/hooks/use-constrained-tabbing/index.cjs.map +3 -3
- package/build/hooks/use-debounce/index.cjs +1 -1
- package/build/hooks/use-debounce/index.cjs.map +3 -3
- package/build/hooks/use-disabled/index.cjs.map +2 -2
- package/build/hooks/use-dragging/index.cjs +27 -22
- package/build/hooks/use-dragging/index.cjs.map +4 -4
- package/build/hooks/use-dragging/types.cjs +19 -0
- package/build/hooks/use-dragging/types.cjs.map +7 -0
- package/build/hooks/use-drop-zone/index.cjs +2 -5
- package/build/hooks/use-drop-zone/index.cjs.map +3 -3
- package/build/hooks/use-drop-zone/types.cjs +19 -0
- package/build/hooks/use-drop-zone/types.cjs.map +7 -0
- package/build/hooks/use-fixed-window-list/index.cjs +10 -8
- package/build/hooks/use-fixed-window-list/index.cjs.map +3 -3
- package/build/hooks/use-focus-return/index.cjs +12 -9
- package/build/hooks/use-focus-return/index.cjs.map +3 -3
- package/build/hooks/use-isomorphic-layout-effect/index.cjs +1 -1
- package/build/hooks/use-isomorphic-layout-effect/index.cjs.map +1 -1
- package/build/hooks/use-keyboard-shortcut/index.cjs +1 -4
- package/build/hooks/use-keyboard-shortcut/index.cjs.map +3 -3
- package/build/hooks/use-reduced-motion/index.cjs +1 -1
- package/build/hooks/use-reduced-motion/index.cjs.map +2 -2
- package/build/hooks/use-throttle/index.cjs +1 -1
- package/build/hooks/use-throttle/index.cjs.map +3 -3
- package/build/hooks/use-viewport-match/index.cjs +2 -5
- package/build/hooks/use-viewport-match/index.cjs.map +3 -3
- package/build/hooks/use-warn-on-change/index.cjs +3 -10
- package/build/hooks/use-warn-on-change/index.cjs.map +3 -3
- package/build/utils/subscribe-delegated-listener/index.cjs.map +1 -1
- package/build-module/hooks/use-constrained-tabbing/index.mjs +3 -9
- package/build-module/hooks/use-constrained-tabbing/index.mjs.map +3 -3
- package/build-module/hooks/use-debounce/index.mjs +1 -1
- package/build-module/hooks/use-debounce/index.mjs.map +3 -3
- package/build-module/hooks/use-disabled/index.mjs.map +2 -2
- package/build-module/hooks/use-dragging/index.mjs +27 -22
- package/build-module/hooks/use-dragging/index.mjs.map +4 -4
- package/build-module/hooks/use-dragging/types.mjs +1 -0
- package/build-module/hooks/use-dragging/types.mjs.map +7 -0
- package/build-module/hooks/use-drop-zone/index.mjs +2 -5
- package/build-module/hooks/use-drop-zone/index.mjs.map +3 -3
- package/build-module/hooks/use-drop-zone/types.mjs +1 -0
- package/build-module/hooks/use-drop-zone/types.mjs.map +7 -0
- package/build-module/hooks/use-fixed-window-list/index.mjs +10 -8
- package/build-module/hooks/use-fixed-window-list/index.mjs.map +3 -3
- package/build-module/hooks/use-focus-return/index.mjs +12 -9
- package/build-module/hooks/use-focus-return/index.mjs.map +3 -3
- package/build-module/hooks/use-isomorphic-layout-effect/index.mjs +1 -1
- package/build-module/hooks/use-isomorphic-layout-effect/index.mjs.map +1 -1
- package/build-module/hooks/use-keyboard-shortcut/index.mjs +1 -4
- package/build-module/hooks/use-keyboard-shortcut/index.mjs.map +3 -3
- package/build-module/hooks/use-reduced-motion/index.mjs +1 -1
- package/build-module/hooks/use-reduced-motion/index.mjs.map +2 -2
- package/build-module/hooks/use-throttle/index.mjs +1 -1
- package/build-module/hooks/use-throttle/index.mjs.map +3 -3
- package/build-module/hooks/use-viewport-match/index.mjs +2 -5
- package/build-module/hooks/use-viewport-match/index.mjs.map +3 -3
- package/build-module/hooks/use-warn-on-change/index.mjs +3 -10
- package/build-module/hooks/use-warn-on-change/index.mjs.map +3 -3
- package/build-module/utils/subscribe-delegated-listener/index.mjs.map +1 -1
- package/build-types/hooks/use-constrained-tabbing/index.d.ts +2 -2
- package/build-types/hooks/use-constrained-tabbing/index.d.ts.map +1 -1
- package/build-types/hooks/use-debounce/index.d.ts +7 -6
- package/build-types/hooks/use-debounce/index.d.ts.map +1 -1
- package/build-types/hooks/use-disabled/index.d.ts +1 -1
- package/build-types/hooks/use-disabled/index.d.ts.map +1 -1
- package/build-types/hooks/use-dragging/index.d.ts +9 -13
- package/build-types/hooks/use-dragging/index.d.ts.map +1 -1
- package/build-types/hooks/use-dragging/types.d.ts +11 -0
- package/build-types/hooks/use-dragging/types.d.ts.map +1 -0
- package/build-types/hooks/use-drop-zone/index.d.ts +4 -21
- package/build-types/hooks/use-drop-zone/index.d.ts.map +1 -1
- package/build-types/hooks/use-drop-zone/types.d.ts +35 -0
- package/build-types/hooks/use-drop-zone/types.d.ts.map +1 -0
- package/build-types/hooks/use-fixed-window-list/index.d.ts +22 -26
- package/build-types/hooks/use-fixed-window-list/index.d.ts.map +1 -1
- package/build-types/hooks/use-focus-return/index.d.ts +3 -3
- package/build-types/hooks/use-focus-return/index.d.ts.map +1 -1
- package/build-types/hooks/use-isomorphic-layout-effect/index.d.ts.map +1 -1
- package/build-types/hooks/use-keyboard-shortcut/index.d.ts +29 -23
- package/build-types/hooks/use-keyboard-shortcut/index.d.ts.map +1 -1
- package/build-types/hooks/use-reduced-motion/index.d.ts +1 -1
- package/build-types/hooks/use-reduced-motion/index.d.ts.map +1 -1
- package/build-types/hooks/use-throttle/index.d.ts +8 -6
- package/build-types/hooks/use-throttle/index.d.ts.map +1 -1
- package/build-types/hooks/use-viewport-match/index.d.ts +3 -3
- package/build-types/hooks/use-viewport-match/index.d.ts.map +1 -1
- package/build-types/hooks/use-warn-on-change/index.d.ts +7 -7
- package/build-types/hooks/use-warn-on-change/index.d.ts.map +1 -1
- package/package.json +16 -11
- package/src/hooks/use-constrained-tabbing/{index.js → index.ts} +7 -11
- package/src/hooks/use-debounce/{index.js → index.ts} +11 -6
- package/src/hooks/use-disabled/index.ts +3 -1
- package/src/hooks/use-dragging/index.ts +78 -0
- package/src/hooks/use-dragging/types.ts +11 -0
- package/src/hooks/use-drop-zone/{index.js → index.ts} +17 -31
- package/src/hooks/use-drop-zone/types.ts +34 -0
- package/src/hooks/use-fixed-window-list/{index.js → index.ts} +46 -37
- package/src/hooks/use-focus-return/{index.js → index.ts} +26 -21
- package/src/hooks/use-keyboard-shortcut/{index.js → index.ts} +43 -28
- package/src/hooks/use-reduced-motion/{index.js → index.ts} +2 -2
- package/src/hooks/use-throttle/{index.js → index.ts} +12 -6
- package/src/hooks/use-viewport-match/{index.js → index.ts} +30 -29
- package/src/hooks/use-warn-on-change/index.ts +42 -0
- package/src/hooks/use-dragging/index.js +0 -74
- package/src/hooks/use-warn-on-change/index.js +0 -43
- /package/src/hooks/use-isomorphic-layout-effect/{index.js → index.ts} +0 -0
|
@@ -3,21 +3,13 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import useRefEffect from '../use-ref-effect';
|
|
5
5
|
import useEvent from '../use-event';
|
|
6
|
+
import type { UseDropZoneProps } from './types';
|
|
6
7
|
|
|
7
8
|
/**
|
|
8
9
|
* A hook to facilitate drag and drop handling.
|
|
9
10
|
*
|
|
10
|
-
* @param {
|
|
11
|
-
* @
|
|
12
|
-
* @param {boolean} [props.isDisabled] Whether or not to disable the drop zone.
|
|
13
|
-
* @param {(e: DragEvent) => void} [props.onDragStart] Called when dragging has started.
|
|
14
|
-
* @param {(e: DragEvent) => void} [props.onDragEnter] Called when the zone is entered.
|
|
15
|
-
* @param {(e: DragEvent) => void} [props.onDragOver] Called when the zone is moved within.
|
|
16
|
-
* @param {(e: DragEvent) => void} [props.onDragLeave] Called when the zone is left.
|
|
17
|
-
* @param {(e: MouseEvent) => void} [props.onDragEnd] Called when dragging has ended.
|
|
18
|
-
* @param {(e: DragEvent) => void} [props.onDrop] Called when dropping in the zone.
|
|
19
|
-
*
|
|
20
|
-
* @return {React.RefCallback<HTMLElement>} Ref callback to be passed to the drop zone element.
|
|
11
|
+
* @param {UseDropZoneProps} props Hook options
|
|
12
|
+
* @return Ref callback to be passed to the drop zone element.
|
|
21
13
|
*/
|
|
22
14
|
export default function useDropZone( {
|
|
23
15
|
dropZoneElement,
|
|
@@ -28,7 +20,7 @@ export default function useDropZone( {
|
|
|
28
20
|
onDragLeave: _onDragLeave,
|
|
29
21
|
onDragEnd: _onDragEnd,
|
|
30
22
|
onDragOver: _onDragOver,
|
|
31
|
-
} ) {
|
|
23
|
+
}: UseDropZoneProps ): React.RefCallback< HTMLElement > {
|
|
32
24
|
const onDropEvent = useEvent( _onDrop );
|
|
33
25
|
const onDragStartEvent = useEvent( _onDragStart );
|
|
34
26
|
const onDragEnterEvent = useEvent( _onDragEnter );
|
|
@@ -37,7 +29,7 @@ export default function useDropZone( {
|
|
|
37
29
|
const onDragOverEvent = useEvent( _onDragOver );
|
|
38
30
|
|
|
39
31
|
return useRefEffect(
|
|
40
|
-
( elem ) => {
|
|
32
|
+
( elem: HTMLElement ) => {
|
|
41
33
|
if ( isDisabled ) {
|
|
42
34
|
return;
|
|
43
35
|
}
|
|
@@ -58,7 +50,9 @@ export default function useDropZone( {
|
|
|
58
50
|
*
|
|
59
51
|
* @return {boolean} True if in drop zone, false if not.
|
|
60
52
|
*/
|
|
61
|
-
function isElementInZone(
|
|
53
|
+
function isElementInZone(
|
|
54
|
+
targetToCheck: EventTarget | null
|
|
55
|
+
): boolean {
|
|
62
56
|
const { defaultView } = ownerDocument;
|
|
63
57
|
|
|
64
58
|
if (
|
|
@@ -70,8 +64,7 @@ export default function useDropZone( {
|
|
|
70
64
|
return false;
|
|
71
65
|
}
|
|
72
66
|
|
|
73
|
-
|
|
74
|
-
let elementToCheck = targetToCheck;
|
|
67
|
+
let elementToCheck: HTMLElement | null = targetToCheck;
|
|
75
68
|
|
|
76
69
|
do {
|
|
77
70
|
if ( elementToCheck.dataset.isDropZone ) {
|
|
@@ -82,7 +75,7 @@ export default function useDropZone( {
|
|
|
82
75
|
return false;
|
|
83
76
|
}
|
|
84
77
|
|
|
85
|
-
function maybeDragStart(
|
|
78
|
+
function maybeDragStart( event: DragEvent ): void {
|
|
86
79
|
if ( isDragging ) {
|
|
87
80
|
return;
|
|
88
81
|
}
|
|
@@ -101,18 +94,14 @@ export default function useDropZone( {
|
|
|
101
94
|
}
|
|
102
95
|
}
|
|
103
96
|
|
|
104
|
-
function onDragEnter(
|
|
97
|
+
function onDragEnter( event: DragEvent ): void {
|
|
105
98
|
event.preventDefault();
|
|
106
99
|
|
|
107
100
|
// The `dragenter` event will also fire when entering child
|
|
108
101
|
// elements, but we only want to call `onDragEnter` when
|
|
109
102
|
// entering the drop zone, which means the `relatedTarget`
|
|
110
103
|
// (element that has been left) should be outside the drop zone.
|
|
111
|
-
if (
|
|
112
|
-
element.contains(
|
|
113
|
-
/** @type {Node} */ ( event.relatedTarget )
|
|
114
|
-
)
|
|
115
|
-
) {
|
|
104
|
+
if ( element.contains( event.relatedTarget as Node ) ) {
|
|
116
105
|
return;
|
|
117
106
|
}
|
|
118
107
|
|
|
@@ -121,18 +110,15 @@ export default function useDropZone( {
|
|
|
121
110
|
}
|
|
122
111
|
}
|
|
123
112
|
|
|
124
|
-
function onDragOver(
|
|
125
|
-
// Only call onDragOver for the innermost hovered drop zones.
|
|
113
|
+
function onDragOver( event: DragEvent ): void {
|
|
126
114
|
if ( ! event.defaultPrevented && _onDragOver ) {
|
|
127
115
|
onDragOverEvent( event );
|
|
128
116
|
}
|
|
129
117
|
|
|
130
|
-
// Prevent the browser default while also signalling to parent
|
|
131
|
-
// drop zones that `onDragOver` is already handled.
|
|
132
118
|
event.preventDefault();
|
|
133
119
|
}
|
|
134
120
|
|
|
135
|
-
function onDragLeave(
|
|
121
|
+
function onDragLeave( event: DragEvent ): void {
|
|
136
122
|
// The `dragleave` event will also fire when leaving child
|
|
137
123
|
// elements, but we only want to call `onDragLeave` when
|
|
138
124
|
// leaving the drop zone, which means the `relatedTarget`
|
|
@@ -150,7 +136,7 @@ export default function useDropZone( {
|
|
|
150
136
|
}
|
|
151
137
|
}
|
|
152
138
|
|
|
153
|
-
function onDrop(
|
|
139
|
+
function onDrop( event: DragEvent ): void {
|
|
154
140
|
// Don't handle drop if an inner drop zone already handled it.
|
|
155
141
|
if ( event.defaultPrevented ) {
|
|
156
142
|
return;
|
|
@@ -173,7 +159,7 @@ export default function useDropZone( {
|
|
|
173
159
|
maybeDragEnd( event );
|
|
174
160
|
}
|
|
175
161
|
|
|
176
|
-
function maybeDragEnd(
|
|
162
|
+
function maybeDragEnd( event: Event ): void {
|
|
177
163
|
if ( ! isDragging ) {
|
|
178
164
|
return;
|
|
179
165
|
}
|
|
@@ -184,7 +170,7 @@ export default function useDropZone( {
|
|
|
184
170
|
ownerDocument.removeEventListener( 'mousemove', maybeDragEnd );
|
|
185
171
|
|
|
186
172
|
if ( _onDragEnd ) {
|
|
187
|
-
onDragEndEvent( event );
|
|
173
|
+
onDragEndEvent( event as MouseEvent );
|
|
188
174
|
}
|
|
189
175
|
}
|
|
190
176
|
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export interface UseDropZoneProps {
|
|
2
|
+
/**
|
|
3
|
+
* Optional element to be used as the drop zone.
|
|
4
|
+
*/
|
|
5
|
+
dropZoneElement?: HTMLElement | null;
|
|
6
|
+
/**
|
|
7
|
+
* Whether or not to disable the drop zone.
|
|
8
|
+
*/
|
|
9
|
+
isDisabled?: boolean;
|
|
10
|
+
/**
|
|
11
|
+
* Called when dragging has started.
|
|
12
|
+
*/
|
|
13
|
+
onDragStart?: ( e: DragEvent ) => void;
|
|
14
|
+
/**
|
|
15
|
+
* Called when the zone is entered.
|
|
16
|
+
*/
|
|
17
|
+
onDragEnter?: ( e: DragEvent ) => void;
|
|
18
|
+
/**
|
|
19
|
+
* Called when the zone is moved within.
|
|
20
|
+
*/
|
|
21
|
+
onDragOver?: ( e: DragEvent ) => void;
|
|
22
|
+
/**
|
|
23
|
+
* Called when the zone is left.
|
|
24
|
+
*/
|
|
25
|
+
onDragLeave?: ( e: DragEvent ) => void;
|
|
26
|
+
/**
|
|
27
|
+
* Called when dragging has ended.
|
|
28
|
+
*/
|
|
29
|
+
onDragEnd?: ( e: MouseEvent ) => void;
|
|
30
|
+
/**
|
|
31
|
+
* Called when dropping in the zone.
|
|
32
|
+
*/
|
|
33
|
+
onDrop?: ( e: DragEvent ) => void;
|
|
34
|
+
}
|
|
@@ -12,49 +12,58 @@ import { debounce } from '../../utils/debounce';
|
|
|
12
12
|
|
|
13
13
|
const DEFAULT_INIT_WINDOW_SIZE = 30;
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
*/
|
|
15
|
+
interface FixedWindowList {
|
|
16
|
+
/** Items visible in the current viewport */
|
|
17
|
+
visibleItems: number;
|
|
18
|
+
/** Start index of the window */
|
|
19
|
+
start: number;
|
|
20
|
+
/** End index of the window */
|
|
21
|
+
end: number;
|
|
22
|
+
/** Returns true if item is in the window */
|
|
23
|
+
itemInView: ( index: number ) => boolean;
|
|
24
|
+
}
|
|
23
25
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
*/
|
|
26
|
+
interface FixedWindowListOptions {
|
|
27
|
+
/** Renders windowOverscan number of items before and after the calculated visible window. */
|
|
28
|
+
windowOverscan?: number;
|
|
29
|
+
/** When false avoids calculating the window size */
|
|
30
|
+
useWindowing?: boolean;
|
|
31
|
+
/** Initial window size to use on first render before we can calculate the window size. */
|
|
32
|
+
initWindowSize?: number;
|
|
33
|
+
/** Used to recalculate the window size when the expanded state of a list changes. */
|
|
34
|
+
expandedState?: any;
|
|
35
|
+
}
|
|
32
36
|
|
|
33
37
|
/**
|
|
34
38
|
*
|
|
35
|
-
* @param
|
|
36
|
-
* @param
|
|
37
|
-
* @param
|
|
38
|
-
* @param
|
|
39
|
-
* @return
|
|
39
|
+
* @param elementRef Used to find the closest scroll container that contains element.
|
|
40
|
+
* @param itemHeight Fixed item height in pixels
|
|
41
|
+
* @param totalItems Total items in list
|
|
42
|
+
* @param [options] Options object
|
|
43
|
+
* @return Array with the fixed window list and setter
|
|
40
44
|
*/
|
|
41
45
|
export default function useFixedWindowList(
|
|
42
|
-
elementRef
|
|
43
|
-
itemHeight,
|
|
44
|
-
totalItems,
|
|
45
|
-
options
|
|
46
|
-
)
|
|
46
|
+
elementRef: React.RefObject< HTMLElement >,
|
|
47
|
+
itemHeight: number,
|
|
48
|
+
totalItems: number,
|
|
49
|
+
options?: FixedWindowListOptions
|
|
50
|
+
): [
|
|
51
|
+
FixedWindowList,
|
|
52
|
+
React.Dispatch< React.SetStateAction< FixedWindowList > >,
|
|
53
|
+
] {
|
|
47
54
|
const initWindowSize = options?.initWindowSize ?? DEFAULT_INIT_WINDOW_SIZE;
|
|
48
55
|
const useWindowing = options?.useWindowing ?? true;
|
|
49
56
|
|
|
50
|
-
const [ fixedListWindow, setFixedListWindow ] = useState(
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
57
|
+
const [ fixedListWindow, setFixedListWindow ] = useState< FixedWindowList >(
|
|
58
|
+
{
|
|
59
|
+
visibleItems: initWindowSize,
|
|
60
|
+
start: 0,
|
|
61
|
+
end: initWindowSize,
|
|
62
|
+
itemInView: ( index: number ) => {
|
|
63
|
+
return index >= 0 && index <= initWindowSize;
|
|
64
|
+
},
|
|
65
|
+
}
|
|
66
|
+
);
|
|
58
67
|
|
|
59
68
|
useLayoutEffect( () => {
|
|
60
69
|
if ( ! useWindowing ) {
|
|
@@ -67,7 +76,7 @@ export default function useFixedWindowList(
|
|
|
67
76
|
* @param {boolean} [initRender] Indicates if this is the initial render
|
|
68
77
|
* @return {void}
|
|
69
78
|
*/
|
|
70
|
-
const measureWindow = ( initRender ) => {
|
|
79
|
+
const measureWindow = ( initRender?: boolean ) => {
|
|
71
80
|
if ( ! scrollContainer ) {
|
|
72
81
|
return;
|
|
73
82
|
}
|
|
@@ -91,7 +100,7 @@ export default function useFixedWindowList(
|
|
|
91
100
|
visibleItems,
|
|
92
101
|
start,
|
|
93
102
|
end,
|
|
94
|
-
itemInView: (
|
|
103
|
+
itemInView: ( index: number ) => {
|
|
95
104
|
return start <= index && index <= end;
|
|
96
105
|
},
|
|
97
106
|
};
|
|
@@ -144,7 +153,7 @@ export default function useFixedWindowList(
|
|
|
144
153
|
return;
|
|
145
154
|
}
|
|
146
155
|
const scrollContainer = getScrollContainer( elementRef.current );
|
|
147
|
-
const handleKeyDown = (
|
|
156
|
+
const handleKeyDown = ( event: KeyboardEvent ) => {
|
|
148
157
|
switch ( event.keyCode ) {
|
|
149
158
|
case HOME: {
|
|
150
159
|
return scrollContainer?.scrollTo( { top: 0 } );
|
|
@@ -3,18 +3,17 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import { useRef, useEffect, useCallback } from '@wordpress/element';
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
let origin = null;
|
|
6
|
+
let origin: Element | null = null;
|
|
8
7
|
|
|
9
8
|
/**
|
|
10
9
|
* Adds the unmount behavior of returning focus to the element which had it
|
|
11
10
|
* previously as is expected for roles like menus or dialogs.
|
|
12
11
|
*
|
|
13
|
-
* @param
|
|
14
|
-
* @return
|
|
12
|
+
* @param onFocusReturn Overrides the default return behavior.
|
|
13
|
+
* @return Element Ref.
|
|
15
14
|
*
|
|
16
15
|
* @example
|
|
17
|
-
* ```
|
|
16
|
+
* ```ts
|
|
18
17
|
* import { useFocusReturn } from '@wordpress/compose';
|
|
19
18
|
*
|
|
20
19
|
* const WithFocusReturn = () => {
|
|
@@ -28,23 +27,26 @@ let origin = null;
|
|
|
28
27
|
* }
|
|
29
28
|
* ```
|
|
30
29
|
*/
|
|
31
|
-
function useFocusReturn(
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
const
|
|
36
|
-
const onFocusReturnRef = useRef(
|
|
30
|
+
function useFocusReturn(
|
|
31
|
+
onFocusReturn?: () => void
|
|
32
|
+
): React.RefCallback< HTMLElement > {
|
|
33
|
+
const ref = useRef< HTMLElement | null >( null );
|
|
34
|
+
const focusedBeforeMountRef = useRef< Element | null >( null );
|
|
35
|
+
const onFocusReturnRef = useRef< ( () => void ) | undefined >(
|
|
36
|
+
onFocusReturn
|
|
37
|
+
);
|
|
38
|
+
|
|
37
39
|
useEffect( () => {
|
|
38
40
|
onFocusReturnRef.current = onFocusReturn;
|
|
39
41
|
}, [ onFocusReturn ] );
|
|
40
42
|
|
|
41
|
-
return useCallback( ( node ) => {
|
|
43
|
+
return useCallback( ( node: HTMLElement | null ) => {
|
|
42
44
|
if ( node ) {
|
|
43
45
|
// Set ref to be used when unmounting.
|
|
44
46
|
ref.current = node;
|
|
45
47
|
|
|
46
48
|
// Only set when the node mounts.
|
|
47
|
-
if (
|
|
49
|
+
if ( focusedBeforeMountRef.current ) {
|
|
48
50
|
return;
|
|
49
51
|
}
|
|
50
52
|
|
|
@@ -54,14 +56,15 @@ function useFocusReturn( onFocusReturn ) {
|
|
|
54
56
|
? node.ownerDocument.activeElement.contentDocument
|
|
55
57
|
: node.ownerDocument;
|
|
56
58
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
+
focusedBeforeMountRef.current =
|
|
60
|
+
activeDocument?.activeElement ?? null;
|
|
61
|
+
} else if ( focusedBeforeMountRef.current ) {
|
|
59
62
|
const isFocused = ref.current?.contains(
|
|
60
|
-
ref.current?.ownerDocument.activeElement
|
|
63
|
+
ref.current?.ownerDocument.activeElement ?? null
|
|
61
64
|
);
|
|
62
65
|
|
|
63
66
|
if ( ref.current?.isConnected && ! isFocused ) {
|
|
64
|
-
origin ??=
|
|
67
|
+
origin ??= focusedBeforeMountRef.current;
|
|
65
68
|
return;
|
|
66
69
|
}
|
|
67
70
|
|
|
@@ -72,11 +75,13 @@ function useFocusReturn( onFocusReturn ) {
|
|
|
72
75
|
if ( onFocusReturnRef.current ) {
|
|
73
76
|
onFocusReturnRef.current();
|
|
74
77
|
} else {
|
|
75
|
-
|
|
76
|
-
!
|
|
78
|
+
const elementToFocus = (
|
|
79
|
+
! focusedBeforeMountRef.current.isConnected
|
|
77
80
|
? origin
|
|
78
|
-
:
|
|
79
|
-
)
|
|
81
|
+
: focusedBeforeMountRef.current
|
|
82
|
+
) as HTMLElement | SVGElement | null;
|
|
83
|
+
|
|
84
|
+
elementToFocus?.focus();
|
|
80
85
|
}
|
|
81
86
|
origin = null;
|
|
82
87
|
}
|
|
@@ -3,44 +3,59 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import Mousetrap from 'mousetrap';
|
|
5
5
|
import 'mousetrap/plugins/global-bind/mousetrap-global-bind.js';
|
|
6
|
+
import type { ExtendedKeyboardEvent } from 'mousetrap';
|
|
6
7
|
|
|
7
8
|
/**
|
|
8
9
|
* WordPress dependencies
|
|
9
10
|
*/
|
|
11
|
+
import type { RefObject } from '@wordpress/element';
|
|
10
12
|
import { useEffect, useRef } from '@wordpress/element';
|
|
11
13
|
import { isAppleOS } from '@wordpress/keycodes';
|
|
12
14
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
15
|
+
type KeyboardShortcutConfig = {
|
|
16
|
+
/**
|
|
17
|
+
* Handle keyboard events anywhere including inside textarea/input fields.
|
|
18
|
+
*/
|
|
19
|
+
bindGlobal: boolean;
|
|
20
|
+
/**
|
|
21
|
+
* Event name used to trigger the handler, defaults to keydown.
|
|
22
|
+
*/
|
|
23
|
+
eventName: string;
|
|
24
|
+
/**
|
|
25
|
+
* Disables the keyboard handler if the value is true.
|
|
26
|
+
*/
|
|
27
|
+
isDisabled: boolean;
|
|
28
|
+
/**
|
|
29
|
+
* React reference to the DOM element used to catch the keyboard event.
|
|
30
|
+
*/
|
|
31
|
+
target: RefObject< HTMLElement >;
|
|
32
|
+
};
|
|
23
33
|
|
|
24
34
|
/**
|
|
25
35
|
* Attach a keyboard shortcut handler.
|
|
26
36
|
*
|
|
27
37
|
* @see https://craig.is/killing/mice#api.bind for information about the `callback` parameter.
|
|
28
38
|
*
|
|
29
|
-
* @param
|
|
30
|
-
* @param
|
|
31
|
-
* @param
|
|
39
|
+
* @param shortcuts Keyboard Shortcuts.
|
|
40
|
+
* @param callback Shortcut callback.
|
|
41
|
+
* @param options Shortcut options.
|
|
42
|
+
* @param options.bindGlobal
|
|
43
|
+
* @param options.eventName
|
|
44
|
+
* @param options.isDisabled
|
|
45
|
+
* @param options.target
|
|
32
46
|
*/
|
|
33
47
|
function useKeyboardShortcut(
|
|
34
|
-
shortcuts,
|
|
35
|
-
callback,
|
|
48
|
+
shortcuts: string[] | string,
|
|
49
|
+
callback: ( e: ExtendedKeyboardEvent, combo: string ) => void,
|
|
36
50
|
{
|
|
37
51
|
bindGlobal = false,
|
|
38
52
|
eventName = 'keydown',
|
|
39
|
-
isDisabled = false,
|
|
53
|
+
isDisabled = false,
|
|
40
54
|
target,
|
|
41
|
-
} = {}
|
|
55
|
+
}: Partial< KeyboardShortcutConfig > = {}
|
|
42
56
|
) {
|
|
43
57
|
const currentCallbackRef = useRef( callback );
|
|
58
|
+
|
|
44
59
|
useEffect( () => {
|
|
45
60
|
currentCallbackRef.current = callback;
|
|
46
61
|
}, [ callback ] );
|
|
@@ -49,29 +64,28 @@ function useKeyboardShortcut(
|
|
|
49
64
|
if ( isDisabled ) {
|
|
50
65
|
return;
|
|
51
66
|
}
|
|
67
|
+
|
|
52
68
|
const mousetrap = new Mousetrap(
|
|
53
69
|
target && target.current
|
|
54
70
|
? target.current
|
|
55
71
|
: // We were passing `document` here previously, so to successfully cast it to Element we must cast it first to `unknown`.
|
|
56
72
|
// Not sure if this is a mistake but it was the behavior previous to the addition of types so we're just doing what's
|
|
57
73
|
// necessary to maintain the existing behavior.
|
|
58
|
-
|
|
74
|
+
( document as unknown as Element )
|
|
59
75
|
);
|
|
76
|
+
|
|
60
77
|
const shortcutsArray = Array.isArray( shortcuts )
|
|
61
78
|
? shortcuts
|
|
62
79
|
: [ shortcuts ];
|
|
63
|
-
|
|
80
|
+
|
|
81
|
+
shortcutsArray.forEach( ( shortcut: string ) => {
|
|
64
82
|
const keys = shortcut.split( '+' );
|
|
65
|
-
// Determines whether a key is a modifier by the length of the string.
|
|
66
|
-
// E.g. if I add a pass a shortcut Shift+Cmd+M, it'll determine that
|
|
67
|
-
// the modifiers are Shift and Cmd because they're not a single character.
|
|
68
83
|
const modifiers = new Set(
|
|
69
84
|
keys.filter( ( value ) => value.length > 1 )
|
|
70
85
|
);
|
|
71
86
|
const hasAlt = modifiers.has( 'alt' );
|
|
72
87
|
const hasShift = modifiers.has( 'shift' );
|
|
73
88
|
|
|
74
|
-
// This should be better moved to the shortcut registration instead.
|
|
75
89
|
if (
|
|
76
90
|
isAppleOS() &&
|
|
77
91
|
( ( modifiers.size === 1 && hasAlt ) ||
|
|
@@ -83,12 +97,13 @@ function useKeyboardShortcut(
|
|
|
83
97
|
}
|
|
84
98
|
|
|
85
99
|
const bindFn = bindGlobal ? 'bindGlobal' : 'bind';
|
|
86
|
-
//
|
|
87
|
-
|
|
100
|
+
// `@ts-expect-error` Mousetrap typings don’t include `bindGlobal`
|
|
101
|
+
// It exists at runtime via the plugin
|
|
102
|
+
// `any` is used here to allow dynamic access via `bindFn`
|
|
103
|
+
( mousetrap as any )[ bindFn ](
|
|
88
104
|
shortcut,
|
|
89
|
-
(
|
|
90
|
-
|
|
91
|
-
) => currentCallbackRef.current( ...args ),
|
|
105
|
+
( ...args: [ Mousetrap.ExtendedKeyboardEvent, string ] ) =>
|
|
106
|
+
currentCallbackRef.current( ...args ),
|
|
92
107
|
eventName
|
|
93
108
|
);
|
|
94
109
|
} );
|
|
@@ -6,9 +6,9 @@ import useMediaQuery from '../use-media-query';
|
|
|
6
6
|
/**
|
|
7
7
|
* Hook returning whether the user has a preference for reduced motion.
|
|
8
8
|
*
|
|
9
|
-
* @return
|
|
9
|
+
* @return Reduced motion preference value.
|
|
10
10
|
*/
|
|
11
|
-
const useReducedMotion = () =>
|
|
11
|
+
const useReducedMotion = (): boolean =>
|
|
12
12
|
useMediaQuery( '(prefers-reduced-motion: reduce)' );
|
|
13
13
|
|
|
14
14
|
export default useReducedMotion;
|
|
@@ -12,6 +12,8 @@ import { useEffect } from '@wordpress/element';
|
|
|
12
12
|
* Internal dependencies
|
|
13
13
|
*/
|
|
14
14
|
import { throttle } from '../../utils/throttle';
|
|
15
|
+
import type { ThrottleOptions } from '../../utils/throttle';
|
|
16
|
+
import type { DebouncedFunc } from '../../utils/debounce';
|
|
15
17
|
|
|
16
18
|
/**
|
|
17
19
|
* Throttles a function similar to Lodash's `throttle`. A new throttled function will
|
|
@@ -21,14 +23,18 @@ import { throttle } from '../../utils/throttle';
|
|
|
21
23
|
*
|
|
22
24
|
* @see https://lodash.com/docs/4#throttle
|
|
23
25
|
*
|
|
24
|
-
* @template
|
|
26
|
+
* @template TFunc
|
|
25
27
|
*
|
|
26
|
-
* @param
|
|
27
|
-
* @param
|
|
28
|
-
* @param
|
|
29
|
-
* @return
|
|
28
|
+
* @param fn The function to throttle.
|
|
29
|
+
* @param wait The number of milliseconds to throttle invocations to.
|
|
30
|
+
* @param options The options object.
|
|
31
|
+
* @return Throttled function.
|
|
30
32
|
*/
|
|
31
|
-
export default function useThrottle(
|
|
33
|
+
export default function useThrottle< TFunc extends ( ...args: any[] ) => void >(
|
|
34
|
+
fn: TFunc,
|
|
35
|
+
wait?: number,
|
|
36
|
+
options?: ThrottleOptions
|
|
37
|
+
): DebouncedFunc< TFunc > {
|
|
32
38
|
const throttled = useMemoOne(
|
|
33
39
|
() => throttle( fn, wait ?? 0, options ),
|
|
34
40
|
[ fn, wait, options ]
|