@rc-component/util 1.8.1 → 1.9.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/es/Dom/focus.d.ts +4 -2
- package/es/Dom/focus.js +47 -4
- package/lib/Dom/focus.d.ts +4 -2
- package/lib/Dom/focus.js +47 -4
- package/package.json +1 -1
package/es/Dom/focus.d.ts
CHANGED
|
@@ -9,11 +9,13 @@ export declare function triggerFocus(element?: HTMLElement, option?: InputFocusO
|
|
|
9
9
|
/**
|
|
10
10
|
* Lock focus in the element.
|
|
11
11
|
* It will force back to the first focusable element when focus leaves the element.
|
|
12
|
+
* @param id - A stable ID for this lock instance
|
|
12
13
|
*/
|
|
13
|
-
export declare function lockFocus(element: HTMLElement): VoidFunction;
|
|
14
|
+
export declare function lockFocus(element: HTMLElement, id: string): VoidFunction;
|
|
14
15
|
/**
|
|
15
16
|
* Lock focus within an element.
|
|
16
17
|
* When locked, focus will be restricted to focusable elements within the specified element.
|
|
17
18
|
* If multiple elements are locked, only the last locked element will be effective.
|
|
19
|
+
* @returns A function to mark an element as ignored, which will temporarily allow focus on that element even if it's outside the locked area.
|
|
18
20
|
*/
|
|
19
|
-
export declare function useLockFocus(lock: boolean, getElement: () => HTMLElement | null): void;
|
|
21
|
+
export declare function useLockFocus(lock: boolean, getElement: () => HTMLElement | null): [ignoreElement: (ele: HTMLElement) => void];
|
package/es/Dom/focus.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { useEffect } from 'react';
|
|
2
2
|
import isVisible from "./isVisible";
|
|
3
|
+
import useId from "../hooks/useId";
|
|
3
4
|
function focusable(node, includePositive = false) {
|
|
4
5
|
if (isVisible(node)) {
|
|
5
6
|
const nodeName = node.nodeName.toLowerCase();
|
|
@@ -72,9 +73,29 @@ export function triggerFocus(element, option) {
|
|
|
72
73
|
// ======================================================
|
|
73
74
|
let lastFocusElement = null;
|
|
74
75
|
let focusElements = [];
|
|
76
|
+
// Map stable ID to lock element
|
|
77
|
+
const idToElementMap = new Map();
|
|
78
|
+
// Map stable ID to ignored element
|
|
79
|
+
const ignoredElementMap = new Map();
|
|
75
80
|
function getLastElement() {
|
|
76
81
|
return focusElements[focusElements.length - 1];
|
|
77
82
|
}
|
|
83
|
+
function isIgnoredElement(element) {
|
|
84
|
+
const lastElement = getLastElement();
|
|
85
|
+
if (element && lastElement) {
|
|
86
|
+
// Find the ID that maps to the last element
|
|
87
|
+
let lockId;
|
|
88
|
+
for (const [id, ele] of idToElementMap.entries()) {
|
|
89
|
+
if (ele === lastElement) {
|
|
90
|
+
lockId = id;
|
|
91
|
+
break;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
const ignoredEle = ignoredElementMap.get(lockId);
|
|
95
|
+
return !!ignoredEle && (ignoredEle === element || ignoredEle.contains(element));
|
|
96
|
+
}
|
|
97
|
+
return false;
|
|
98
|
+
}
|
|
78
99
|
function hasFocus(element) {
|
|
79
100
|
const {
|
|
80
101
|
activeElement
|
|
@@ -86,10 +107,17 @@ function syncFocus() {
|
|
|
86
107
|
const {
|
|
87
108
|
activeElement
|
|
88
109
|
} = document;
|
|
110
|
+
|
|
111
|
+
// If current focus is on an ignored element, don't force it back
|
|
112
|
+
if (isIgnoredElement(activeElement)) {
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
89
115
|
if (lastElement && !hasFocus(lastElement)) {
|
|
90
116
|
const focusableList = getFocusNodeList(lastElement);
|
|
91
117
|
const matchElement = focusableList.includes(lastFocusElement) ? lastFocusElement : focusableList[0];
|
|
92
|
-
matchElement?.focus(
|
|
118
|
+
matchElement?.focus({
|
|
119
|
+
preventScroll: true
|
|
120
|
+
});
|
|
93
121
|
} else {
|
|
94
122
|
lastFocusElement = activeElement;
|
|
95
123
|
}
|
|
@@ -115,9 +143,13 @@ function onWindowKeyDown(e) {
|
|
|
115
143
|
/**
|
|
116
144
|
* Lock focus in the element.
|
|
117
145
|
* It will force back to the first focusable element when focus leaves the element.
|
|
146
|
+
* @param id - A stable ID for this lock instance
|
|
118
147
|
*/
|
|
119
|
-
export function lockFocus(element) {
|
|
148
|
+
export function lockFocus(element, id) {
|
|
120
149
|
if (element) {
|
|
150
|
+
// Store the mapping between ID and element
|
|
151
|
+
idToElementMap.set(id, element);
|
|
152
|
+
|
|
121
153
|
// Refresh focus elements
|
|
122
154
|
focusElements = focusElements.filter(ele => ele !== element);
|
|
123
155
|
focusElements.push(element);
|
|
@@ -132,6 +164,8 @@ export function lockFocus(element) {
|
|
|
132
164
|
return () => {
|
|
133
165
|
lastFocusElement = null;
|
|
134
166
|
focusElements = focusElements.filter(ele => ele !== element);
|
|
167
|
+
idToElementMap.delete(id);
|
|
168
|
+
ignoredElementMap.delete(id);
|
|
135
169
|
if (focusElements.length === 0) {
|
|
136
170
|
window.removeEventListener('focusin', syncFocus);
|
|
137
171
|
window.removeEventListener('keydown', onWindowKeyDown, true);
|
|
@@ -143,14 +177,23 @@ export function lockFocus(element) {
|
|
|
143
177
|
* Lock focus within an element.
|
|
144
178
|
* When locked, focus will be restricted to focusable elements within the specified element.
|
|
145
179
|
* If multiple elements are locked, only the last locked element will be effective.
|
|
180
|
+
* @returns A function to mark an element as ignored, which will temporarily allow focus on that element even if it's outside the locked area.
|
|
146
181
|
*/
|
|
147
182
|
export function useLockFocus(lock, getElement) {
|
|
183
|
+
const id = useId();
|
|
148
184
|
useEffect(() => {
|
|
149
185
|
if (lock) {
|
|
150
186
|
const element = getElement();
|
|
151
187
|
if (element) {
|
|
152
|
-
return lockFocus(element);
|
|
188
|
+
return lockFocus(element, id);
|
|
153
189
|
}
|
|
154
190
|
}
|
|
155
|
-
}, [lock]);
|
|
191
|
+
}, [lock, id]);
|
|
192
|
+
const ignoreElement = ele => {
|
|
193
|
+
if (ele) {
|
|
194
|
+
// Set the ignored element using stable ID
|
|
195
|
+
ignoredElementMap.set(id, ele);
|
|
196
|
+
}
|
|
197
|
+
};
|
|
198
|
+
return [ignoreElement];
|
|
156
199
|
}
|
package/lib/Dom/focus.d.ts
CHANGED
|
@@ -9,11 +9,13 @@ export declare function triggerFocus(element?: HTMLElement, option?: InputFocusO
|
|
|
9
9
|
/**
|
|
10
10
|
* Lock focus in the element.
|
|
11
11
|
* It will force back to the first focusable element when focus leaves the element.
|
|
12
|
+
* @param id - A stable ID for this lock instance
|
|
12
13
|
*/
|
|
13
|
-
export declare function lockFocus(element: HTMLElement): VoidFunction;
|
|
14
|
+
export declare function lockFocus(element: HTMLElement, id: string): VoidFunction;
|
|
14
15
|
/**
|
|
15
16
|
* Lock focus within an element.
|
|
16
17
|
* When locked, focus will be restricted to focusable elements within the specified element.
|
|
17
18
|
* If multiple elements are locked, only the last locked element will be effective.
|
|
19
|
+
* @returns A function to mark an element as ignored, which will temporarily allow focus on that element even if it's outside the locked area.
|
|
18
20
|
*/
|
|
19
|
-
export declare function useLockFocus(lock: boolean, getElement: () => HTMLElement | null): void;
|
|
21
|
+
export declare function useLockFocus(lock: boolean, getElement: () => HTMLElement | null): [ignoreElement: (ele: HTMLElement) => void];
|
package/lib/Dom/focus.js
CHANGED
|
@@ -9,6 +9,7 @@ exports.triggerFocus = triggerFocus;
|
|
|
9
9
|
exports.useLockFocus = useLockFocus;
|
|
10
10
|
var _react = require("react");
|
|
11
11
|
var _isVisible = _interopRequireDefault(require("./isVisible"));
|
|
12
|
+
var _useId = _interopRequireDefault(require("../hooks/useId"));
|
|
12
13
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
13
14
|
function focusable(node, includePositive = false) {
|
|
14
15
|
if ((0, _isVisible.default)(node)) {
|
|
@@ -82,9 +83,29 @@ function triggerFocus(element, option) {
|
|
|
82
83
|
// ======================================================
|
|
83
84
|
let lastFocusElement = null;
|
|
84
85
|
let focusElements = [];
|
|
86
|
+
// Map stable ID to lock element
|
|
87
|
+
const idToElementMap = new Map();
|
|
88
|
+
// Map stable ID to ignored element
|
|
89
|
+
const ignoredElementMap = new Map();
|
|
85
90
|
function getLastElement() {
|
|
86
91
|
return focusElements[focusElements.length - 1];
|
|
87
92
|
}
|
|
93
|
+
function isIgnoredElement(element) {
|
|
94
|
+
const lastElement = getLastElement();
|
|
95
|
+
if (element && lastElement) {
|
|
96
|
+
// Find the ID that maps to the last element
|
|
97
|
+
let lockId;
|
|
98
|
+
for (const [id, ele] of idToElementMap.entries()) {
|
|
99
|
+
if (ele === lastElement) {
|
|
100
|
+
lockId = id;
|
|
101
|
+
break;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
const ignoredEle = ignoredElementMap.get(lockId);
|
|
105
|
+
return !!ignoredEle && (ignoredEle === element || ignoredEle.contains(element));
|
|
106
|
+
}
|
|
107
|
+
return false;
|
|
108
|
+
}
|
|
88
109
|
function hasFocus(element) {
|
|
89
110
|
const {
|
|
90
111
|
activeElement
|
|
@@ -96,10 +117,17 @@ function syncFocus() {
|
|
|
96
117
|
const {
|
|
97
118
|
activeElement
|
|
98
119
|
} = document;
|
|
120
|
+
|
|
121
|
+
// If current focus is on an ignored element, don't force it back
|
|
122
|
+
if (isIgnoredElement(activeElement)) {
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
99
125
|
if (lastElement && !hasFocus(lastElement)) {
|
|
100
126
|
const focusableList = getFocusNodeList(lastElement);
|
|
101
127
|
const matchElement = focusableList.includes(lastFocusElement) ? lastFocusElement : focusableList[0];
|
|
102
|
-
matchElement?.focus(
|
|
128
|
+
matchElement?.focus({
|
|
129
|
+
preventScroll: true
|
|
130
|
+
});
|
|
103
131
|
} else {
|
|
104
132
|
lastFocusElement = activeElement;
|
|
105
133
|
}
|
|
@@ -125,9 +153,13 @@ function onWindowKeyDown(e) {
|
|
|
125
153
|
/**
|
|
126
154
|
* Lock focus in the element.
|
|
127
155
|
* It will force back to the first focusable element when focus leaves the element.
|
|
156
|
+
* @param id - A stable ID for this lock instance
|
|
128
157
|
*/
|
|
129
|
-
function lockFocus(element) {
|
|
158
|
+
function lockFocus(element, id) {
|
|
130
159
|
if (element) {
|
|
160
|
+
// Store the mapping between ID and element
|
|
161
|
+
idToElementMap.set(id, element);
|
|
162
|
+
|
|
131
163
|
// Refresh focus elements
|
|
132
164
|
focusElements = focusElements.filter(ele => ele !== element);
|
|
133
165
|
focusElements.push(element);
|
|
@@ -142,6 +174,8 @@ function lockFocus(element) {
|
|
|
142
174
|
return () => {
|
|
143
175
|
lastFocusElement = null;
|
|
144
176
|
focusElements = focusElements.filter(ele => ele !== element);
|
|
177
|
+
idToElementMap.delete(id);
|
|
178
|
+
ignoredElementMap.delete(id);
|
|
145
179
|
if (focusElements.length === 0) {
|
|
146
180
|
window.removeEventListener('focusin', syncFocus);
|
|
147
181
|
window.removeEventListener('keydown', onWindowKeyDown, true);
|
|
@@ -153,14 +187,23 @@ function lockFocus(element) {
|
|
|
153
187
|
* Lock focus within an element.
|
|
154
188
|
* When locked, focus will be restricted to focusable elements within the specified element.
|
|
155
189
|
* If multiple elements are locked, only the last locked element will be effective.
|
|
190
|
+
* @returns A function to mark an element as ignored, which will temporarily allow focus on that element even if it's outside the locked area.
|
|
156
191
|
*/
|
|
157
192
|
function useLockFocus(lock, getElement) {
|
|
193
|
+
const id = (0, _useId.default)();
|
|
158
194
|
(0, _react.useEffect)(() => {
|
|
159
195
|
if (lock) {
|
|
160
196
|
const element = getElement();
|
|
161
197
|
if (element) {
|
|
162
|
-
return lockFocus(element);
|
|
198
|
+
return lockFocus(element, id);
|
|
163
199
|
}
|
|
164
200
|
}
|
|
165
|
-
}, [lock]);
|
|
201
|
+
}, [lock, id]);
|
|
202
|
+
const ignoreElement = ele => {
|
|
203
|
+
if (ele) {
|
|
204
|
+
// Set the ignored element using stable ID
|
|
205
|
+
ignoredElementMap.set(id, ele);
|
|
206
|
+
}
|
|
207
|
+
};
|
|
208
|
+
return [ignoreElement];
|
|
166
209
|
}
|