@wordpress/compose 5.0.1 → 5.0.5
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 +12 -3
- package/build/higher-order/if-condition/index.js.map +1 -1
- package/build/higher-order/pure/index.js.map +1 -1
- package/build/higher-order/with-instance-id/index.js.map +1 -1
- package/build/higher-order/with-safe-timeout/index.js.map +1 -1
- package/build/hooks/use-constrained-tabbing/index.js +46 -43
- package/build/hooks/use-constrained-tabbing/index.js.map +1 -1
- package/build/hooks/use-debounce/index.js +1 -1
- package/build/hooks/use-debounce/index.js.map +1 -1
- package/build/hooks/use-drop-zone/index.js +12 -6
- package/build/hooks/use-drop-zone/index.js.map +1 -1
- package/build/hooks/use-fixed-window-list/index.js +175 -0
- package/build/hooks/use-fixed-window-list/index.js.map +1 -0
- package/build/hooks/use-focusable-iframe/index.js +50 -0
- package/build/hooks/use-focusable-iframe/index.js.map +1 -0
- package/build/hooks/use-previous/index.js +3 -14
- package/build/hooks/use-previous/index.js.map +1 -1
- package/build/hooks/use-reduced-motion/index.js +1 -7
- package/build/hooks/use-reduced-motion/index.js.map +1 -1
- package/build/hooks/use-throttle/index.js +1 -1
- package/build/hooks/use-throttle/index.js.map +1 -1
- package/build/index.js +16 -0
- package/build/index.js.map +1 -1
- package/build-module/higher-order/if-condition/index.js +1 -1
- package/build-module/higher-order/if-condition/index.js.map +1 -1
- package/build-module/higher-order/pure/index.js +1 -1
- package/build-module/higher-order/pure/index.js.map +1 -1
- package/build-module/higher-order/with-instance-id/index.js +1 -2
- package/build-module/higher-order/with-instance-id/index.js.map +1 -1
- package/build-module/higher-order/with-safe-timeout/index.js +1 -1
- package/build-module/higher-order/with-safe-timeout/index.js.map +1 -1
- package/build-module/hooks/use-constrained-tabbing/index.js +41 -40
- package/build-module/hooks/use-constrained-tabbing/index.js.map +1 -1
- package/build-module/hooks/use-debounce/index.js +1 -1
- package/build-module/hooks/use-debounce/index.js.map +1 -1
- package/build-module/hooks/use-drop-zone/index.js +12 -6
- package/build-module/hooks/use-drop-zone/index.js.map +1 -1
- package/build-module/hooks/use-fixed-window-list/index.js +164 -0
- package/build-module/hooks/use-fixed-window-list/index.js.map +1 -0
- package/build-module/hooks/use-focusable-iframe/index.js +40 -0
- package/build-module/hooks/use-focusable-iframe/index.js.map +1 -0
- package/build-module/hooks/use-previous/index.js +3 -14
- package/build-module/hooks/use-previous/index.js.map +1 -1
- package/build-module/hooks/use-reduced-motion/index.js +2 -8
- package/build-module/hooks/use-reduced-motion/index.js.map +1 -1
- package/build-module/hooks/use-throttle/index.js +1 -1
- package/build-module/hooks/use-throttle/index.js.map +1 -1
- package/build-module/index.js +2 -0
- package/build-module/index.js.map +1 -1
- package/build-types/higher-order/if-condition/index.d.ts.map +1 -1
- package/build-types/higher-order/pure/index.d.ts.map +1 -1
- package/build-types/higher-order/with-instance-id/index.d.ts.map +1 -1
- package/build-types/higher-order/with-safe-timeout/index.d.ts.map +1 -1
- package/build-types/hooks/use-constrained-tabbing/index.d.ts.map +1 -1
- package/build-types/hooks/use-debounce/index.d.ts +2 -2
- package/build-types/hooks/use-debounce/index.d.ts.map +1 -1
- package/build-types/hooks/use-drop-zone/index.d.ts.map +1 -1
- package/build-types/hooks/use-fixed-window-list/index.d.ts +57 -0
- package/build-types/hooks/use-fixed-window-list/index.d.ts.map +1 -0
- package/build-types/hooks/use-focus-outside/index.d.ts +3 -3
- package/build-types/hooks/use-focus-outside/index.d.ts.map +1 -1
- package/build-types/hooks/use-focusable-iframe/index.d.ts +8 -0
- package/build-types/hooks/use-focusable-iframe/index.d.ts.map +1 -0
- package/build-types/hooks/use-previous/index.d.ts +2 -4
- package/build-types/hooks/use-previous/index.d.ts.map +1 -1
- package/build-types/hooks/use-reduced-motion/index.d.ts +5 -0
- package/build-types/hooks/use-reduced-motion/index.d.ts.map +1 -1
- package/build-types/hooks/use-throttle/index.d.ts +2 -2
- package/build-types/hooks/use-throttle/index.d.ts.map +1 -1
- package/build-types/hooks/use-viewport-match/index.d.ts +2 -2
- package/build-types/hooks/use-viewport-match/index.d.ts.map +1 -1
- package/build-types/index.d.ts +2 -0
- package/package.json +9 -9
- package/src/higher-order/if-condition/index.tsx +0 -1
- package/src/higher-order/pure/index.tsx +0 -1
- package/src/higher-order/with-instance-id/index.tsx +0 -1
- package/src/higher-order/with-safe-timeout/index.tsx +0 -1
- package/src/hooks/use-constrained-tabbing/index.js +39 -33
- package/src/hooks/use-debounce/index.js +1 -1
- package/src/hooks/use-drop-zone/index.js +11 -9
- package/src/hooks/use-fixed-window-list/index.js +174 -0
- package/src/hooks/use-focusable-iframe/README.md +29 -0
- package/src/hooks/use-focusable-iframe/index.js +34 -0
- package/src/hooks/use-previous/index.ts +24 -0
- package/src/hooks/use-reduced-motion/index.js +2 -13
- package/src/hooks/use-throttle/index.js +1 -1
- package/src/index.js +2 -0
- package/tsconfig.tsbuildinfo +1 -1694
- package/src/hooks/use-previous/index.js +0 -31
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wordpress/compose",
|
|
3
|
-
"version": "5.0.
|
|
3
|
+
"version": "5.0.5",
|
|
4
4
|
"description": "WordPress higher-order components (HOCs).",
|
|
5
5
|
"author": "The WordPress Contributors",
|
|
6
6
|
"license": "GPL-2.0-or-later",
|
|
@@ -30,15 +30,15 @@
|
|
|
30
30
|
"sideEffects": false,
|
|
31
31
|
"dependencies": {
|
|
32
32
|
"@babel/runtime": "^7.13.10",
|
|
33
|
-
"@types/lodash": "4.14.
|
|
33
|
+
"@types/lodash": "^4.14.172",
|
|
34
34
|
"@types/mousetrap": "^1.6.8",
|
|
35
|
-
"@wordpress/deprecated": "^3.2.
|
|
36
|
-
"@wordpress/dom": "^3.2.
|
|
37
|
-
"@wordpress/element": "^4.0.
|
|
35
|
+
"@wordpress/deprecated": "^3.2.2",
|
|
36
|
+
"@wordpress/dom": "^3.2.6",
|
|
37
|
+
"@wordpress/element": "^4.0.3",
|
|
38
38
|
"@wordpress/is-shallow-equal": "^4.2.0",
|
|
39
|
-
"@wordpress/keycodes": "^3.2.
|
|
40
|
-
"@wordpress/priority-queue": "^2.2.
|
|
41
|
-
"clipboard": "^2.0.
|
|
39
|
+
"@wordpress/keycodes": "^3.2.3",
|
|
40
|
+
"@wordpress/priority-queue": "^2.2.2",
|
|
41
|
+
"clipboard": "^2.0.8",
|
|
42
42
|
"lodash": "^4.17.21",
|
|
43
43
|
"mousetrap": "^1.6.5",
|
|
44
44
|
"react-resize-aware": "^3.1.0",
|
|
@@ -47,5 +47,5 @@
|
|
|
47
47
|
"publishConfig": {
|
|
48
48
|
"access": "public"
|
|
49
49
|
},
|
|
50
|
-
"gitHead": "
|
|
50
|
+
"gitHead": "393c2b5533837fd637e998d23f0124c081a10df0"
|
|
51
51
|
}
|
|
@@ -8,7 +8,6 @@ import { Component } from '@wordpress/element';
|
|
|
8
8
|
* Internal dependencies
|
|
9
9
|
*/
|
|
10
10
|
import createHigherOrderComponent from '../../utils/create-higher-order-component';
|
|
11
|
-
// eslint-disable-next-line no-duplicate-imports
|
|
12
11
|
import type { SimpleHigherOrderComponent } from '../../utils/create-higher-order-component';
|
|
13
12
|
|
|
14
13
|
/**
|
|
@@ -8,7 +8,6 @@ import type { ComponentType } from 'react';
|
|
|
8
8
|
* Internal dependencies
|
|
9
9
|
*/
|
|
10
10
|
import createHigherOrderComponent from '../../utils/create-higher-order-component';
|
|
11
|
-
// eslint-disable-next-line no-duplicate-imports
|
|
12
11
|
import type { PropInjectingHigherOrderComponent } from '../../utils/create-higher-order-component';
|
|
13
12
|
import useInstanceId from '../../hooks/use-instance-id';
|
|
14
13
|
|
|
@@ -14,7 +14,6 @@ import { Component } from '@wordpress/element';
|
|
|
14
14
|
* Internal dependencies
|
|
15
15
|
*/
|
|
16
16
|
import createHigherOrderComponent from '../../utils/create-higher-order-component';
|
|
17
|
-
// eslint-disable-next-line no-duplicate-imports
|
|
18
17
|
import type { PropInjectingHigherOrderComponent } from '../../utils/create-higher-order-component';
|
|
19
18
|
|
|
20
19
|
/**
|
|
@@ -3,7 +3,11 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import { TAB } from '@wordpress/keycodes';
|
|
5
5
|
import { focus } from '@wordpress/dom';
|
|
6
|
-
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Internal dependencies
|
|
9
|
+
*/
|
|
10
|
+
import useRefEffect from '../use-ref-effect';
|
|
7
11
|
|
|
8
12
|
/**
|
|
9
13
|
* In Dialogs/modals, the tabbing must be constrained to the content of
|
|
@@ -27,46 +31,48 @@ import { useCallback } from '@wordpress/element';
|
|
|
27
31
|
* ```
|
|
28
32
|
*/
|
|
29
33
|
function useConstrainedTabbing() {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
return;
|
|
37
|
-
}
|
|
34
|
+
return useRefEffect( ( /** @type {HTMLElement} */ node ) => {
|
|
35
|
+
/** @type {number|undefined} */
|
|
36
|
+
let timeoutId;
|
|
37
|
+
|
|
38
|
+
function onKeyDown( /** @type {KeyboardEvent} */ event ) {
|
|
39
|
+
const { keyCode, shiftKey, target } = event;
|
|
38
40
|
|
|
39
|
-
if (
|
|
41
|
+
if ( keyCode !== TAB ) {
|
|
40
42
|
return;
|
|
41
43
|
}
|
|
42
44
|
|
|
43
|
-
const
|
|
44
|
-
|
|
45
|
+
const action = shiftKey ? 'findPrevious' : 'findNext';
|
|
46
|
+
const nextElement =
|
|
47
|
+
focus.tabbable[ action ](
|
|
48
|
+
/** @type {HTMLElement} */ ( target )
|
|
49
|
+
) || null;
|
|
50
|
+
|
|
51
|
+
// If the element that is about to receive focus is outside the
|
|
52
|
+
// area, move focus to a div and insert it at the start or end of
|
|
53
|
+
// the area, depending on the direction. Without preventing default
|
|
54
|
+
// behaviour, the browser will then move focus to the next element.
|
|
55
|
+
if ( node.contains( nextElement ) ) {
|
|
45
56
|
return;
|
|
46
57
|
}
|
|
47
|
-
const firstTabbable = tabbables[ 0 ];
|
|
48
|
-
const lastTabbable = tabbables[ tabbables.length - 1 ];
|
|
49
58
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
} else if ( ! event.shiftKey && event.target === lastTabbable ) {
|
|
54
|
-
event.preventDefault();
|
|
55
|
-
/** @type {HTMLElement} */ ( firstTabbable ).focus();
|
|
56
|
-
/*
|
|
57
|
-
* When pressing Tab and none of the tabbables has focus, the keydown
|
|
58
|
-
* event happens on the wrapper div: move focus on the first tabbable.
|
|
59
|
-
*/
|
|
60
|
-
} else if (
|
|
61
|
-
! tabbables.includes( /** @type {Element} */ ( event.target ) )
|
|
62
|
-
) {
|
|
63
|
-
event.preventDefault();
|
|
64
|
-
/** @type {HTMLElement} */ ( firstTabbable ).focus();
|
|
65
|
-
}
|
|
66
|
-
} );
|
|
67
|
-
}, [] );
|
|
59
|
+
const domAction = shiftKey ? 'append' : 'prepend';
|
|
60
|
+
const { ownerDocument } = node;
|
|
61
|
+
const trap = ownerDocument.createElement( 'div' );
|
|
68
62
|
|
|
69
|
-
|
|
63
|
+
trap.tabIndex = -1;
|
|
64
|
+
node[ domAction ]( trap );
|
|
65
|
+
trap.focus();
|
|
66
|
+
// Remove after the browser moves focus to the next element.
|
|
67
|
+
timeoutId = setTimeout( () => node.removeChild( trap ) );
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
node.addEventListener( 'keydown', onKeyDown );
|
|
71
|
+
return () => {
|
|
72
|
+
node.removeEventListener( 'keydown', onKeyDown );
|
|
73
|
+
clearTimeout( timeoutId );
|
|
74
|
+
};
|
|
75
|
+
}, [] );
|
|
70
76
|
}
|
|
71
77
|
|
|
72
78
|
export default useConstrainedTabbing;
|
|
@@ -23,7 +23,7 @@ import { useEffect } from '@wordpress/element';
|
|
|
23
23
|
* @param {TFunc} fn The function to debounce.
|
|
24
24
|
* @param {number} [wait] The number of milliseconds to delay.
|
|
25
25
|
* @param {import('lodash').DebounceSettings} [options] The options object.
|
|
26
|
-
* @return {
|
|
26
|
+
* @return {import('lodash').DebouncedFunc<TFunc>} Debounced function.
|
|
27
27
|
*/
|
|
28
28
|
export default function useDebounce( fn, wait, options ) {
|
|
29
29
|
/* eslint-enable jsdoc/valid-types */
|
|
@@ -73,18 +73,24 @@ export default function useDropZone( {
|
|
|
73
73
|
/**
|
|
74
74
|
* Checks if an element is in the drop zone.
|
|
75
75
|
*
|
|
76
|
-
* @param {
|
|
76
|
+
* @param {EventTarget|null} targetToCheck
|
|
77
77
|
*
|
|
78
78
|
* @return {boolean} True if in drop zone, false if not.
|
|
79
79
|
*/
|
|
80
|
-
function isElementInZone(
|
|
80
|
+
function isElementInZone( targetToCheck ) {
|
|
81
|
+
const { defaultView } = ownerDocument;
|
|
81
82
|
if (
|
|
82
|
-
!
|
|
83
|
-
!
|
|
83
|
+
! targetToCheck ||
|
|
84
|
+
! defaultView ||
|
|
85
|
+
! ( targetToCheck instanceof defaultView.HTMLElement ) ||
|
|
86
|
+
! element.contains( targetToCheck )
|
|
84
87
|
) {
|
|
85
88
|
return false;
|
|
86
89
|
}
|
|
87
90
|
|
|
91
|
+
/** @type {HTMLElement|null} */
|
|
92
|
+
let elementToCheck = targetToCheck;
|
|
93
|
+
|
|
88
94
|
do {
|
|
89
95
|
if ( elementToCheck.dataset.isDropZone ) {
|
|
90
96
|
return elementToCheck === element;
|
|
@@ -155,11 +161,7 @@ export default function useDropZone( {
|
|
|
155
161
|
// leaving the drop zone, which means the `relatedTarget`
|
|
156
162
|
// (element that has been entered) should be outside the drop
|
|
157
163
|
// zone.
|
|
158
|
-
if (
|
|
159
|
-
isElementInZone(
|
|
160
|
-
/** @type {HTMLElement|null} */ ( event.relatedTarget )
|
|
161
|
-
)
|
|
162
|
-
) {
|
|
164
|
+
if ( isElementInZone( event.relatedTarget ) ) {
|
|
163
165
|
return;
|
|
164
166
|
}
|
|
165
167
|
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* External dependencies
|
|
3
|
+
*/
|
|
4
|
+
import { debounce } from 'lodash';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* WordPress dependencies
|
|
8
|
+
*/
|
|
9
|
+
import { useState, useLayoutEffect } from '@wordpress/element';
|
|
10
|
+
import { getScrollContainer } from '@wordpress/dom';
|
|
11
|
+
import { PAGEUP, PAGEDOWN, HOME, END } from '@wordpress/keycodes';
|
|
12
|
+
|
|
13
|
+
const DEFAULT_INIT_WINDOW_SIZE = 30;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* @typedef {Object} WPFixedWindowList
|
|
17
|
+
*
|
|
18
|
+
* @property {number} visibleItems Items visible in the current viewport
|
|
19
|
+
* @property {number} start Start index of the window
|
|
20
|
+
* @property {number} end End index of the window
|
|
21
|
+
* @property {(index:number)=>boolean} itemInView Returns true if item is in the window
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* @typedef {Object} WPFixedWindowListOptions
|
|
26
|
+
*
|
|
27
|
+
* @property {number} [windowOverscan] Renders windowOverscan number of items before and after the calculated visible window.
|
|
28
|
+
* @property {boolean} [useWindowing] When false avoids calculating the window size
|
|
29
|
+
* @property {number} [initWindowSize] Initial window size to use on first render before we can calculate the window size.
|
|
30
|
+
*/
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
*
|
|
34
|
+
* @param {import('react').RefObject<HTMLElement>} elementRef Used to find the closest scroll container that contains element.
|
|
35
|
+
* @param { number } itemHeight Fixed item height in pixels
|
|
36
|
+
* @param { number } totalItems Total items in list
|
|
37
|
+
* @param { WPFixedWindowListOptions } [options] Options object
|
|
38
|
+
* @return {[ WPFixedWindowList, setFixedListWindow:(nextWindow:WPFixedWindowList)=>void]} Array with the fixed window list and setter
|
|
39
|
+
*/
|
|
40
|
+
export default function useFixedWindowList(
|
|
41
|
+
elementRef,
|
|
42
|
+
itemHeight,
|
|
43
|
+
totalItems,
|
|
44
|
+
options
|
|
45
|
+
) {
|
|
46
|
+
const initWindowSize = options?.initWindowSize ?? DEFAULT_INIT_WINDOW_SIZE;
|
|
47
|
+
const useWindowing = options?.useWindowing ?? true;
|
|
48
|
+
|
|
49
|
+
const [ fixedListWindow, setFixedListWindow ] = useState( {
|
|
50
|
+
visibleItems: initWindowSize,
|
|
51
|
+
start: 0,
|
|
52
|
+
end: initWindowSize,
|
|
53
|
+
itemInView: ( /** @type {number} */ index ) => {
|
|
54
|
+
return index >= 0 && index <= initWindowSize;
|
|
55
|
+
},
|
|
56
|
+
} );
|
|
57
|
+
|
|
58
|
+
useLayoutEffect( () => {
|
|
59
|
+
if ( ! useWindowing ) {
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
const scrollContainer = getScrollContainer( elementRef.current );
|
|
63
|
+
const measureWindow = (
|
|
64
|
+
/** @type {boolean | undefined} */ initRender
|
|
65
|
+
) => {
|
|
66
|
+
if ( ! scrollContainer ) {
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
const visibleItems = Math.ceil(
|
|
70
|
+
scrollContainer.clientHeight / itemHeight
|
|
71
|
+
);
|
|
72
|
+
// Aim to keep opening list view fast, afterward we can optimize for scrolling
|
|
73
|
+
const windowOverscan = initRender
|
|
74
|
+
? visibleItems
|
|
75
|
+
: options?.windowOverscan ?? visibleItems;
|
|
76
|
+
const firstViewableIndex = Math.floor(
|
|
77
|
+
scrollContainer.scrollTop / itemHeight
|
|
78
|
+
);
|
|
79
|
+
const start = Math.max( 0, firstViewableIndex - windowOverscan );
|
|
80
|
+
const end = Math.min(
|
|
81
|
+
totalItems - 1,
|
|
82
|
+
firstViewableIndex + visibleItems + windowOverscan
|
|
83
|
+
);
|
|
84
|
+
setFixedListWindow( ( lastWindow ) => {
|
|
85
|
+
const nextWindow = {
|
|
86
|
+
visibleItems,
|
|
87
|
+
start,
|
|
88
|
+
end,
|
|
89
|
+
itemInView: ( /** @type {number} */ index ) => {
|
|
90
|
+
return start <= index && index <= end;
|
|
91
|
+
},
|
|
92
|
+
};
|
|
93
|
+
if (
|
|
94
|
+
lastWindow.start !== nextWindow.start ||
|
|
95
|
+
lastWindow.end !== nextWindow.end ||
|
|
96
|
+
lastWindow.visibleItems !== nextWindow.visibleItems
|
|
97
|
+
) {
|
|
98
|
+
return nextWindow;
|
|
99
|
+
}
|
|
100
|
+
return lastWindow;
|
|
101
|
+
} );
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
measureWindow( true );
|
|
105
|
+
const debounceMeasureList = debounce( () => {
|
|
106
|
+
measureWindow();
|
|
107
|
+
}, 16 );
|
|
108
|
+
scrollContainer?.addEventListener( 'scroll', debounceMeasureList );
|
|
109
|
+
scrollContainer?.ownerDocument?.defaultView?.addEventListener(
|
|
110
|
+
'resize',
|
|
111
|
+
debounceMeasureList
|
|
112
|
+
);
|
|
113
|
+
scrollContainer?.ownerDocument?.defaultView?.addEventListener(
|
|
114
|
+
'resize',
|
|
115
|
+
debounceMeasureList
|
|
116
|
+
);
|
|
117
|
+
|
|
118
|
+
return () => {
|
|
119
|
+
scrollContainer?.removeEventListener(
|
|
120
|
+
'scroll',
|
|
121
|
+
debounceMeasureList
|
|
122
|
+
);
|
|
123
|
+
scrollContainer?.ownerDocument?.defaultView?.removeEventListener(
|
|
124
|
+
'resize',
|
|
125
|
+
debounceMeasureList
|
|
126
|
+
);
|
|
127
|
+
};
|
|
128
|
+
}, [ itemHeight, elementRef, totalItems ] );
|
|
129
|
+
|
|
130
|
+
useLayoutEffect( () => {
|
|
131
|
+
if ( ! useWindowing ) {
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
const scrollContainer = getScrollContainer( elementRef.current );
|
|
135
|
+
const handleKeyDown = ( /** @type {KeyboardEvent} */ event ) => {
|
|
136
|
+
switch ( event.keyCode ) {
|
|
137
|
+
case HOME: {
|
|
138
|
+
return scrollContainer?.scrollTo( { top: 0 } );
|
|
139
|
+
}
|
|
140
|
+
case END: {
|
|
141
|
+
return scrollContainer?.scrollTo( {
|
|
142
|
+
top: totalItems * itemHeight,
|
|
143
|
+
} );
|
|
144
|
+
}
|
|
145
|
+
case PAGEUP: {
|
|
146
|
+
return scrollContainer?.scrollTo( {
|
|
147
|
+
top:
|
|
148
|
+
scrollContainer.scrollTop -
|
|
149
|
+
fixedListWindow.visibleItems * itemHeight,
|
|
150
|
+
} );
|
|
151
|
+
}
|
|
152
|
+
case PAGEDOWN: {
|
|
153
|
+
return scrollContainer?.scrollTo( {
|
|
154
|
+
top:
|
|
155
|
+
scrollContainer.scrollTop +
|
|
156
|
+
fixedListWindow.visibleItems * itemHeight,
|
|
157
|
+
} );
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
};
|
|
161
|
+
scrollContainer?.ownerDocument?.defaultView?.addEventListener(
|
|
162
|
+
'keydown',
|
|
163
|
+
handleKeyDown
|
|
164
|
+
);
|
|
165
|
+
return () => {
|
|
166
|
+
scrollContainer?.ownerDocument?.defaultView?.removeEventListener(
|
|
167
|
+
'keydown',
|
|
168
|
+
handleKeyDown
|
|
169
|
+
);
|
|
170
|
+
};
|
|
171
|
+
}, [ totalItems, itemHeight, elementRef, fixedListWindow.visibleItems ] );
|
|
172
|
+
|
|
173
|
+
return [ fixedListWindow, setFixedListWindow ];
|
|
174
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# useFocusableIframe
|
|
2
|
+
|
|
3
|
+
By default, it is not possible to detect when an iframe is focused or clicked
|
|
4
|
+
within. This hook uses a technique which checks whether the target of a window
|
|
5
|
+
`blur` event is the iframe, inferring that this has resulted in the focus of the
|
|
6
|
+
iframe. It dispatches an emulated
|
|
7
|
+
[`FocusEvent`](https://developer.mozilla.org/en-US/docs/Web/API/FocusEvent) on
|
|
8
|
+
the iframe element with event bubbling, so a parent component binding its own
|
|
9
|
+
`onFocus` event will account for focus transitioning within the iframe.
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
Use with an `iframe`. You may pass `onFocus` directly as the callback to be
|
|
14
|
+
invoked when the iframe receives focus, or on an ancestor component since the
|
|
15
|
+
event will bubble.
|
|
16
|
+
|
|
17
|
+
```jsx
|
|
18
|
+
import { useFocusableIframe } from '@wordpress/compose';
|
|
19
|
+
|
|
20
|
+
const MyFocusableIframe = () => {
|
|
21
|
+
return(
|
|
22
|
+
<iframe
|
|
23
|
+
ref={ useFocusableIframe() }
|
|
24
|
+
src="/my-iframe-url"
|
|
25
|
+
onFocus={ () => console.log( 'iframe is focused' ) }
|
|
26
|
+
/>
|
|
27
|
+
);
|
|
28
|
+
};
|
|
29
|
+
```
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Internal dependencies
|
|
3
|
+
*/
|
|
4
|
+
import useRefEffect from '../use-ref-effect';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Dispatches a bubbling focus event when the iframe receives focus. Use
|
|
8
|
+
* `onFocus` as usual on the iframe or a parent element.
|
|
9
|
+
*
|
|
10
|
+
* @return {Object} Ref to pass to the iframe.
|
|
11
|
+
*/
|
|
12
|
+
export default function useFocusableIframe() {
|
|
13
|
+
return useRefEffect( ( element ) => {
|
|
14
|
+
const { ownerDocument } = element;
|
|
15
|
+
if ( ! ownerDocument ) return;
|
|
16
|
+
const { defaultView } = ownerDocument;
|
|
17
|
+
if ( ! defaultView ) return;
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Checks whether the iframe is the activeElement, inferring that it has
|
|
21
|
+
* then received focus, and dispatches a focus event.
|
|
22
|
+
*/
|
|
23
|
+
function checkFocus() {
|
|
24
|
+
if ( ownerDocument && ownerDocument.activeElement === element ) {
|
|
25
|
+
/** @type {HTMLElement} */ ( element ).focus();
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
defaultView.addEventListener( 'blur', checkFocus );
|
|
30
|
+
return () => {
|
|
31
|
+
defaultView.removeEventListener( 'blur', checkFocus );
|
|
32
|
+
};
|
|
33
|
+
}, [] );
|
|
34
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WordPress dependencies
|
|
3
|
+
*/
|
|
4
|
+
import { useEffect, useRef } from '@wordpress/element';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Use something's value from the previous render.
|
|
8
|
+
* Based on https://usehooks.com/usePrevious/.
|
|
9
|
+
*
|
|
10
|
+
* @param value The value to track.
|
|
11
|
+
*
|
|
12
|
+
* @return The value from the previous render.
|
|
13
|
+
*/
|
|
14
|
+
export default function usePrevious< T >( value: T ): T | undefined {
|
|
15
|
+
const ref = useRef< T >();
|
|
16
|
+
|
|
17
|
+
// Store current value in ref.
|
|
18
|
+
useEffect( () => {
|
|
19
|
+
ref.current = value;
|
|
20
|
+
}, [ value ] ); // Re-run when value changes.
|
|
21
|
+
|
|
22
|
+
// Return previous value (happens before update in useEffect above).
|
|
23
|
+
return ref.current;
|
|
24
|
+
}
|
|
@@ -3,23 +3,12 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import useMediaQuery from '../use-media-query';
|
|
5
5
|
|
|
6
|
-
/**
|
|
7
|
-
* Whether or not the user agent is Internet Explorer.
|
|
8
|
-
*
|
|
9
|
-
* @type {boolean}
|
|
10
|
-
*/
|
|
11
|
-
const IS_IE =
|
|
12
|
-
typeof window !== 'undefined' &&
|
|
13
|
-
window.navigator.userAgent.indexOf( 'Trident' ) >= 0;
|
|
14
|
-
|
|
15
6
|
/**
|
|
16
7
|
* Hook returning whether the user has a preference for reduced motion.
|
|
17
8
|
*
|
|
18
9
|
* @return {boolean} Reduced motion preference value.
|
|
19
10
|
*/
|
|
20
|
-
const useReducedMotion =
|
|
21
|
-
|
|
22
|
-
? () => true
|
|
23
|
-
: () => useMediaQuery( '(prefers-reduced-motion: reduce)' );
|
|
11
|
+
const useReducedMotion = () =>
|
|
12
|
+
useMediaQuery( '(prefers-reduced-motion: reduce)' );
|
|
24
13
|
|
|
25
14
|
export default useReducedMotion;
|
|
@@ -22,7 +22,7 @@ import { useEffect } from '@wordpress/element';
|
|
|
22
22
|
* @param {TFunc} fn The function to throttle.
|
|
23
23
|
* @param {number} [wait] The number of milliseconds to throttle invocations to.
|
|
24
24
|
* @param {import('lodash').ThrottleSettings} [options] The options object. See linked documentation for details.
|
|
25
|
-
* @return {
|
|
25
|
+
* @return {import('lodash').DebouncedFunc<TFunc>} Throttled function.
|
|
26
26
|
*/
|
|
27
27
|
export default function useThrottle( fn, wait, options ) {
|
|
28
28
|
const throttled = useMemoOne( () => throttle( fn, wait, options ), [
|
package/src/index.js
CHANGED
|
@@ -36,3 +36,5 @@ export { default as useThrottle } from './hooks/use-throttle';
|
|
|
36
36
|
export { default as useMergeRefs } from './hooks/use-merge-refs';
|
|
37
37
|
export { default as useRefEffect } from './hooks/use-ref-effect';
|
|
38
38
|
export { default as __experimentalUseDropZone } from './hooks/use-drop-zone';
|
|
39
|
+
export { default as useFocusableIframe } from './hooks/use-focusable-iframe';
|
|
40
|
+
export { default as __experimentalUseFixedWindowList } from './hooks/use-fixed-window-list';
|