@ringcentral/spring-base 1.11.1 → 1.12.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/chunks/index.js +234 -0
- package/chunks/twMerge.js +289 -62
- package/hooks.js +118 -38
- package/index.js +2 -1
- package/liveAnnouncer.js +1 -0
- package/package.json +4 -4
- package/src/hooks/index.d.ts +1 -0
- package/src/hooks/useAnnouncer/useAnnouncer.d.ts +5 -2
- package/src/hooks/useAriaHideOutside/useAriaHideOutside.d.ts +2 -0
- package/src/hooks/useGlobalAnnouncer/index.d.ts +1 -0
- package/src/hooks/useGlobalAnnouncer/useGlobalAnnouncer.d.ts +5 -0
- package/src/hooks/useId/useId.d.ts +3 -1
- package/src/hooks/useKeyboardMoveFocus/useKeyboardMoveFocus.d.ts +2 -2
- package/src/hooks/useKeyboardMoveFocus/useOnlyOneFocusable.d.ts +4 -1
- package/src/index.d.ts +1 -0
- package/src/liveAnnouncer/LiveAnnouncer.d.ts +25 -0
- package/src/liveAnnouncer/announce.d.ts +4 -0
- package/src/liveAnnouncer/index.d.ts +3 -0
- package/src/liveAnnouncer/types.d.ts +8 -0
- package/src/liveAnnouncer/visuallyHidden.d.ts +3 -0
- package/src/utils/ariaHideOutside.d.ts +11 -1
- package/src/utils/index.d.ts +2 -1
package/chunks/index.js
ADDED
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
const LIVE_ANNOUNCER_ATTR = 'data-live-announcer';
|
|
2
|
+
const visuallyHiddenStyleProperties = {
|
|
3
|
+
border: '0',
|
|
4
|
+
clip: 'rect(0 0 0 0)',
|
|
5
|
+
clipPath: 'inset(50%)',
|
|
6
|
+
height: '1px',
|
|
7
|
+
margin: '-1px',
|
|
8
|
+
overflow: 'hidden',
|
|
9
|
+
padding: '0',
|
|
10
|
+
position: 'absolute',
|
|
11
|
+
width: '1px',
|
|
12
|
+
whiteSpace: 'nowrap',
|
|
13
|
+
};
|
|
14
|
+
const applyVisuallyHiddenStyles = (el) => {
|
|
15
|
+
Object.assign(el.style, visuallyHiddenStyleProperties);
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
class RepeatStringText {
|
|
19
|
+
last = {};
|
|
20
|
+
usedZwsp = {
|
|
21
|
+
assertive: false,
|
|
22
|
+
polite: false,
|
|
23
|
+
};
|
|
24
|
+
next(politeness, message) {
|
|
25
|
+
const useZwsp = this.last[politeness] === message ? !this.usedZwsp[politeness] : false;
|
|
26
|
+
this.last[politeness] = message;
|
|
27
|
+
this.usedZwsp[politeness] = useZwsp;
|
|
28
|
+
return useZwsp ? `\u200B${message}` : message;
|
|
29
|
+
}
|
|
30
|
+
reset(politeness) {
|
|
31
|
+
const channels = politeness
|
|
32
|
+
? [politeness]
|
|
33
|
+
: ['assertive', 'polite'];
|
|
34
|
+
for (const channel of channels) {
|
|
35
|
+
this.last[channel] = undefined;
|
|
36
|
+
this.usedZwsp[channel] = false;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
class LiveAnnouncer {
|
|
41
|
+
node = null;
|
|
42
|
+
assertiveLog = null;
|
|
43
|
+
politeLog = null;
|
|
44
|
+
doc;
|
|
45
|
+
repeatText = new RepeatStringText();
|
|
46
|
+
ready = false;
|
|
47
|
+
pending = [];
|
|
48
|
+
firstDelayTimerId = null;
|
|
49
|
+
constructor(doc) {
|
|
50
|
+
this.doc = doc;
|
|
51
|
+
if (typeof doc === 'undefined' || !doc.body) {
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
this.node = doc.createElement('div');
|
|
55
|
+
this.node.setAttribute(LIVE_ANNOUNCER_ATTR, 'true');
|
|
56
|
+
applyVisuallyHiddenStyles(this.node);
|
|
57
|
+
this.assertiveLog = this.createLog('assertive');
|
|
58
|
+
this.node.appendChild(this.assertiveLog);
|
|
59
|
+
this.politeLog = this.createLog('polite');
|
|
60
|
+
this.node.appendChild(this.politeLog);
|
|
61
|
+
doc.body.prepend(this.node);
|
|
62
|
+
}
|
|
63
|
+
isAttached() {
|
|
64
|
+
return Boolean(this.node?.isConnected);
|
|
65
|
+
}
|
|
66
|
+
createLog(ariaLive) {
|
|
67
|
+
const node = this.doc.createElement('div');
|
|
68
|
+
node.setAttribute('role', 'log');
|
|
69
|
+
node.setAttribute('aria-live', ariaLive);
|
|
70
|
+
node.setAttribute('aria-relevant', 'additions');
|
|
71
|
+
return node;
|
|
72
|
+
}
|
|
73
|
+
destroy() {
|
|
74
|
+
this.cancelFirstDelay();
|
|
75
|
+
if (!this.node) {
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
const parent = this.node.parentNode;
|
|
79
|
+
if (parent) {
|
|
80
|
+
parent.removeChild(this.node);
|
|
81
|
+
}
|
|
82
|
+
this.node = null;
|
|
83
|
+
this.assertiveLog = null;
|
|
84
|
+
this.politeLog = null;
|
|
85
|
+
this.repeatText.reset();
|
|
86
|
+
}
|
|
87
|
+
schedule(message, politeness = 'assertive', timeout = LIVEREGION_TIMEOUT_DELAY) {
|
|
88
|
+
if (this.ready || isTestAnnounceEnvironment()) {
|
|
89
|
+
this.announce(message, politeness, timeout);
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
this.pending.push({ message, politeness, timeout });
|
|
93
|
+
if (this.firstDelayTimerId != null) {
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
const win = this.doc.defaultView ?? globalThis;
|
|
97
|
+
this.firstDelayTimerId = win.setTimeout(() => {
|
|
98
|
+
this.firstDelayTimerId = null;
|
|
99
|
+
this.ready = true;
|
|
100
|
+
const queued = this.pending;
|
|
101
|
+
this.pending = [];
|
|
102
|
+
for (const item of queued) {
|
|
103
|
+
this.announce(item.message, item.politeness, item.timeout);
|
|
104
|
+
}
|
|
105
|
+
}, FIRST_ANNOUNCE_DELAY_MS);
|
|
106
|
+
}
|
|
107
|
+
announce(message, politeness = 'assertive', timeout = LIVEREGION_TIMEOUT_DELAY) {
|
|
108
|
+
if (!this.node) {
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
const node = this.doc.createElement('div');
|
|
112
|
+
if (typeof message === 'object') {
|
|
113
|
+
node.setAttribute('role', 'img');
|
|
114
|
+
node.setAttribute('aria-labelledby', message['aria-labelledby']);
|
|
115
|
+
}
|
|
116
|
+
else {
|
|
117
|
+
node.textContent = this.repeatText.next(politeness, message);
|
|
118
|
+
}
|
|
119
|
+
if (politeness === 'assertive') {
|
|
120
|
+
this.assertiveLog?.appendChild(node);
|
|
121
|
+
}
|
|
122
|
+
else {
|
|
123
|
+
this.politeLog?.appendChild(node);
|
|
124
|
+
}
|
|
125
|
+
if (message !== '') {
|
|
126
|
+
const win = this.doc.defaultView ?? globalThis;
|
|
127
|
+
win.setTimeout(() => {
|
|
128
|
+
node.remove();
|
|
129
|
+
}, timeout);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
clear(politeness) {
|
|
133
|
+
this.pending = politeness
|
|
134
|
+
? this.pending.filter((item) => item.politeness !== politeness)
|
|
135
|
+
: [];
|
|
136
|
+
if (!this.node) {
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
if ((!politeness || politeness === 'assertive') && this.assertiveLog) {
|
|
140
|
+
this.assertiveLog.innerHTML = '';
|
|
141
|
+
this.repeatText.reset('assertive');
|
|
142
|
+
}
|
|
143
|
+
if ((!politeness || politeness === 'polite') && this.politeLog) {
|
|
144
|
+
this.politeLog.innerHTML = '';
|
|
145
|
+
this.repeatText.reset('polite');
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
cancelFirstDelay() {
|
|
149
|
+
if (this.firstDelayTimerId != null) {
|
|
150
|
+
const win = this.doc.defaultView ?? globalThis;
|
|
151
|
+
win.clearTimeout(this.firstDelayTimerId);
|
|
152
|
+
this.firstDelayTimerId = null;
|
|
153
|
+
}
|
|
154
|
+
this.pending = [];
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
const LIVEREGION_TIMEOUT_DELAY = 7000;
|
|
158
|
+
const FIRST_ANNOUNCE_DELAY_MS = 100;
|
|
159
|
+
const announcersByDocument = new WeakMap();
|
|
160
|
+
const getLiveAnnouncer = (doc) => {
|
|
161
|
+
return announcersByDocument.get(doc) ?? null;
|
|
162
|
+
};
|
|
163
|
+
const ensureLiveAnnouncer = (doc) => {
|
|
164
|
+
const existing = announcersByDocument.get(doc);
|
|
165
|
+
if (existing?.isAttached()) {
|
|
166
|
+
return existing;
|
|
167
|
+
}
|
|
168
|
+
existing?.destroy();
|
|
169
|
+
const announcer = new LiveAnnouncer(doc);
|
|
170
|
+
announcersByDocument.set(doc, announcer);
|
|
171
|
+
return announcer;
|
|
172
|
+
};
|
|
173
|
+
const clearLiveAnnouncerRegistry = (doc) => {
|
|
174
|
+
if (doc) {
|
|
175
|
+
const announcer = announcersByDocument.get(doc);
|
|
176
|
+
announcer?.destroy();
|
|
177
|
+
announcersByDocument.delete(doc);
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
};
|
|
181
|
+
const isTestAnnounceEnvironment = () => {
|
|
182
|
+
const globalObj = globalThis;
|
|
183
|
+
if (typeof globalObj.IS_REACT_ACT_ENVIRONMENT === 'boolean') {
|
|
184
|
+
return globalObj.IS_REACT_ACT_ENVIRONMENT;
|
|
185
|
+
}
|
|
186
|
+
return typeof jest !== 'undefined';
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
const resolveDocument = (doc) => {
|
|
190
|
+
if (doc) {
|
|
191
|
+
return doc;
|
|
192
|
+
}
|
|
193
|
+
if (typeof document !== 'undefined') {
|
|
194
|
+
return document;
|
|
195
|
+
}
|
|
196
|
+
return undefined;
|
|
197
|
+
};
|
|
198
|
+
const resolveTimeoutAndDocument = (timeoutOrOptions) => {
|
|
199
|
+
if (typeof timeoutOrOptions === 'number') {
|
|
200
|
+
return { timeout: timeoutOrOptions };
|
|
201
|
+
}
|
|
202
|
+
if (timeoutOrOptions && typeof timeoutOrOptions === 'object') {
|
|
203
|
+
return {
|
|
204
|
+
timeout: timeoutOrOptions.timeout ?? LIVEREGION_TIMEOUT_DELAY,
|
|
205
|
+
document: timeoutOrOptions.document,
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
return { timeout: LIVEREGION_TIMEOUT_DELAY };
|
|
209
|
+
};
|
|
210
|
+
function announce(message, politeness = 'assertive', timeoutOrOptions) {
|
|
211
|
+
const { timeout, document: optionDocument } = resolveTimeoutAndDocument(timeoutOrOptions);
|
|
212
|
+
const doc = resolveDocument(optionDocument);
|
|
213
|
+
if (!doc?.body) {
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
const liveAnnouncer = ensureLiveAnnouncer(doc);
|
|
217
|
+
liveAnnouncer.schedule(message, politeness, timeout);
|
|
218
|
+
}
|
|
219
|
+
function clearAnnouncer(politeness, doc) {
|
|
220
|
+
const target = resolveDocument(doc);
|
|
221
|
+
if (!target) {
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
getLiveAnnouncer(target)?.clear(politeness);
|
|
225
|
+
}
|
|
226
|
+
function destroyAnnouncer(doc) {
|
|
227
|
+
const target = resolveDocument(doc);
|
|
228
|
+
if (!target) {
|
|
229
|
+
return;
|
|
230
|
+
}
|
|
231
|
+
clearLiveAnnouncerRegistry(target);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
export { LIVEREGION_TIMEOUT_DELAY as L, announce as a, LIVE_ANNOUNCER_ATTR as b, clearAnnouncer as c, destroyAnnouncer as d, applyVisuallyHiddenStyles as e };
|
package/chunks/twMerge.js
CHANGED
|
@@ -7,12 +7,16 @@ import { extendTailwindMerge } from 'tailwind-merge';
|
|
|
7
7
|
const ownerWindow = unstable_ownerWindow;
|
|
8
8
|
|
|
9
9
|
const ARIA_HIDDEN = 'aria-hidden';
|
|
10
|
-
const
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
|
|
10
|
+
const TOP_LAYER_ATTR = 'data-sui-top-layer';
|
|
11
|
+
const alwaysVisibleSelector = `[data-live-announcer="true"], [${TOP_LAYER_ATTR}]`;
|
|
12
|
+
const ARIA_HIDDEN_REF_COUNT_MAP = '__SUI_ARIA_HIDE_OUTSIDE_REF_COUNT_MAP__';
|
|
13
|
+
const INERT_REF_COUNT_MAP = '__SUI_ARIA_HIDE_OUTSIDE_INERT_REF_COUNT_MAP__';
|
|
14
|
+
const getRefCountMap = (window, mechanism) => {
|
|
15
|
+
const key = mechanism === 'inert' ? INERT_REF_COUNT_MAP : ARIA_HIDDEN_REF_COUNT_MAP;
|
|
16
|
+
if (!window[key]) {
|
|
17
|
+
window[key] = new WeakMap();
|
|
14
18
|
}
|
|
15
|
-
return window[
|
|
19
|
+
return window[key];
|
|
16
20
|
};
|
|
17
21
|
const OBSERVER_STACK = '__SUI_ARIA_HIDE_OUTSIDE_OBSERVER_STACK__';
|
|
18
22
|
const getObserverStack = (window) => {
|
|
@@ -21,109 +25,332 @@ const getObserverStack = (window) => {
|
|
|
21
25
|
}
|
|
22
26
|
return window[OBSERVER_STACK];
|
|
23
27
|
};
|
|
24
|
-
const isHTMLOrSVGElement = (node) => {
|
|
25
|
-
return 'dataset' in node;
|
|
26
|
-
};
|
|
27
28
|
const isElement = (node) => {
|
|
28
|
-
return node
|
|
29
|
+
return (typeof node === 'object' &&
|
|
30
|
+
node != null &&
|
|
31
|
+
'nodeType' in node &&
|
|
32
|
+
node.nodeType === 1);
|
|
33
|
+
};
|
|
34
|
+
const isAlwaysVisibleNode = (node) => {
|
|
35
|
+
return (node.getAttribute('data-live-announcer') === 'true' ||
|
|
36
|
+
node.hasAttribute(TOP_LAYER_ATTR));
|
|
29
37
|
};
|
|
30
|
-
const
|
|
31
|
-
const
|
|
38
|
+
const createAriaHideOutsideController = (targets, options = {}) => {
|
|
39
|
+
const isLegacyRoot = isElement(options);
|
|
40
|
+
const configuredRoot = isLegacyRoot
|
|
41
|
+
? options
|
|
42
|
+
: options.root;
|
|
43
|
+
const currentWindow = ownerWindow(configuredRoot ?? targets[0]);
|
|
32
44
|
if (!currentWindow) {
|
|
33
|
-
return
|
|
45
|
+
return {
|
|
46
|
+
addTarget: () => { },
|
|
47
|
+
removeTarget: () => { },
|
|
48
|
+
dispose: () => { },
|
|
49
|
+
};
|
|
34
50
|
}
|
|
51
|
+
const root = configuredRoot ?? currentWindow.document.body;
|
|
52
|
+
const shouldUseInert = !isLegacyRoot &&
|
|
53
|
+
options.shouldUseInert &&
|
|
54
|
+
'inert' in currentWindow.HTMLElement.prototype;
|
|
35
55
|
const visibleNodes = new Set(targets);
|
|
56
|
+
const targetNodes = new Set(targets);
|
|
57
|
+
const automaticVisibleNodes = new Set();
|
|
36
58
|
const hiddenNodes = new Set();
|
|
37
|
-
const
|
|
38
|
-
const
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
59
|
+
const targetPaths = new Map();
|
|
60
|
+
const getTargetPath = (target) => {
|
|
61
|
+
const path = [];
|
|
62
|
+
let node = target;
|
|
63
|
+
while (node && root.contains(node)) {
|
|
64
|
+
path.push(node);
|
|
65
|
+
if (node === root) {
|
|
66
|
+
break;
|
|
44
67
|
}
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
68
|
+
node = node.parentElement;
|
|
69
|
+
}
|
|
70
|
+
return path;
|
|
71
|
+
};
|
|
72
|
+
for (const target of targets) {
|
|
73
|
+
targetPaths.set(target, getTargetPath(target));
|
|
74
|
+
}
|
|
75
|
+
const observerStack = getObserverStack(currentWindow);
|
|
76
|
+
const getHideMechanism = (node) => shouldUseInert && node instanceof currentWindow.HTMLElement
|
|
77
|
+
? 'inert'
|
|
78
|
+
: 'aria-hidden';
|
|
79
|
+
const getHidden = (node, mechanism) => mechanism === 'inert'
|
|
80
|
+
? node.inert
|
|
81
|
+
: node.getAttribute(ARIA_HIDDEN) === 'true';
|
|
82
|
+
const setHidden = (node, mechanism, hidden) => {
|
|
83
|
+
if (mechanism === 'inert') {
|
|
84
|
+
node.inert = hidden;
|
|
85
|
+
}
|
|
86
|
+
else if (hidden) {
|
|
87
|
+
node.setAttribute(ARIA_HIDDEN, 'true');
|
|
88
|
+
}
|
|
89
|
+
else {
|
|
90
|
+
node.removeAttribute(ARIA_HIDDEN);
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
const restoreHiddenNode = (node) => {
|
|
94
|
+
if (!hiddenNodes.delete(node)) {
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
const mechanism = getHideMechanism(node);
|
|
98
|
+
const refCountMap = getRefCountMap(currentWindow, mechanism);
|
|
99
|
+
const count = refCountMap.get(node);
|
|
100
|
+
if (typeof count !== 'number') {
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
if (count === 1) {
|
|
104
|
+
setHidden(node, mechanism, false);
|
|
105
|
+
refCountMap.delete(node);
|
|
106
|
+
}
|
|
107
|
+
else {
|
|
108
|
+
refCountMap.set(node, count - 1);
|
|
109
|
+
}
|
|
110
|
+
};
|
|
57
111
|
const hide = (node) => {
|
|
112
|
+
if (hiddenNodes.has(node)) {
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
const mechanism = getHideMechanism(node);
|
|
116
|
+
const refCountMap = getRefCountMap(currentWindow, mechanism);
|
|
58
117
|
const refCount = refCountMap.get(node) ?? 0;
|
|
59
|
-
if (node
|
|
118
|
+
if (getHidden(node, mechanism) && refCount === 0) {
|
|
60
119
|
return;
|
|
61
120
|
}
|
|
62
121
|
if (refCount === 0) {
|
|
63
|
-
node
|
|
122
|
+
setHidden(node, mechanism, true);
|
|
64
123
|
}
|
|
65
124
|
hiddenNodes.add(node);
|
|
66
125
|
refCountMap.set(node, refCount + 1);
|
|
67
126
|
};
|
|
127
|
+
const addAlwaysVisibleNode = (node) => {
|
|
128
|
+
automaticVisibleNodes.add(node);
|
|
129
|
+
visibleNodes.add(node);
|
|
130
|
+
};
|
|
131
|
+
const addAlwaysVisibleNodes = (node) => {
|
|
132
|
+
if (isAlwaysVisibleNode(node)) {
|
|
133
|
+
addAlwaysVisibleNode(node);
|
|
134
|
+
}
|
|
135
|
+
for (const alwaysVisibleNode of node.querySelectorAll(alwaysVisibleSelector)) {
|
|
136
|
+
addAlwaysVisibleNode(alwaysVisibleNode);
|
|
137
|
+
}
|
|
138
|
+
};
|
|
139
|
+
const removeDisconnectedAlwaysVisibleNodes = (node) => {
|
|
140
|
+
for (const alwaysVisibleNode of automaticVisibleNodes) {
|
|
141
|
+
if (!node.contains(alwaysVisibleNode) ||
|
|
142
|
+
root.contains(alwaysVisibleNode)) {
|
|
143
|
+
continue;
|
|
144
|
+
}
|
|
145
|
+
automaticVisibleNodes.delete(alwaysVisibleNode);
|
|
146
|
+
if (!targetNodes.has(alwaysVisibleNode)) {
|
|
147
|
+
visibleNodes.delete(alwaysVisibleNode);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
};
|
|
151
|
+
const containsNode = (nodes, node) => {
|
|
152
|
+
for (const candidate of nodes) {
|
|
153
|
+
if (candidate.contains(node)) {
|
|
154
|
+
return true;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
return false;
|
|
158
|
+
};
|
|
159
|
+
const containsVisibleNode = (node, nodes) => {
|
|
160
|
+
for (const visibleNode of nodes) {
|
|
161
|
+
if (node.contains(visibleNode)) {
|
|
162
|
+
return true;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
return false;
|
|
166
|
+
};
|
|
167
|
+
const isCoveredByHiddenNode = (node) => {
|
|
168
|
+
let ancestor = node.parentElement;
|
|
169
|
+
while (ancestor && root.contains(ancestor)) {
|
|
170
|
+
if (hiddenNodes.has(ancestor)) {
|
|
171
|
+
return !(ancestor === node.parentElement &&
|
|
172
|
+
ancestor.getAttribute('role') === 'row');
|
|
173
|
+
}
|
|
174
|
+
ancestor = ancestor.parentElement;
|
|
175
|
+
}
|
|
176
|
+
return false;
|
|
177
|
+
};
|
|
178
|
+
const shouldAdoptHiddenNode = (node) => {
|
|
179
|
+
return (root.contains(node) &&
|
|
180
|
+
!hiddenNodes.has(node) &&
|
|
181
|
+
!containsNode(visibleNodes, node) &&
|
|
182
|
+
!containsVisibleNode(node, visibleNodes) &&
|
|
183
|
+
!isCoveredByHiddenNode(node));
|
|
184
|
+
};
|
|
185
|
+
const acceptNode = (node) => {
|
|
186
|
+
if (visibleNodes.has(node) ||
|
|
187
|
+
(node.parentElement &&
|
|
188
|
+
hiddenNodes.has(node.parentElement) &&
|
|
189
|
+
node.parentElement.getAttribute('role') !== 'row')) {
|
|
190
|
+
return currentWindow.NodeFilter.FILTER_REJECT;
|
|
191
|
+
}
|
|
192
|
+
if (containsVisibleNode(node, visibleNodes)) {
|
|
193
|
+
return currentWindow.NodeFilter.FILTER_SKIP;
|
|
194
|
+
}
|
|
195
|
+
return currentWindow.NodeFilter.FILTER_ACCEPT;
|
|
196
|
+
};
|
|
197
|
+
const walk = (walkRoot, includeRoot) => {
|
|
198
|
+
addAlwaysVisibleNodes(walkRoot);
|
|
199
|
+
const rootFilter = acceptNode(walkRoot);
|
|
200
|
+
if (includeRoot && rootFilter === currentWindow.NodeFilter.FILTER_ACCEPT) {
|
|
201
|
+
hide(walkRoot);
|
|
202
|
+
}
|
|
203
|
+
if (rootFilter === currentWindow.NodeFilter.FILTER_REJECT) {
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
const walker = currentWindow.document.createTreeWalker(walkRoot, currentWindow.NodeFilter.SHOW_ELEMENT, { acceptNode });
|
|
207
|
+
let node = walker.nextNode();
|
|
208
|
+
while (node != null) {
|
|
209
|
+
hide(node);
|
|
210
|
+
node = walker.nextNode();
|
|
211
|
+
}
|
|
212
|
+
};
|
|
213
|
+
const addTarget = (target) => {
|
|
214
|
+
if (!root.contains(target)) {
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
targetNodes.add(target);
|
|
218
|
+
visibleNodes.add(target);
|
|
219
|
+
const path = getTargetPath(target);
|
|
220
|
+
const restoredAncestorIndexes = [];
|
|
221
|
+
for (let index = 0; index < path.length; index++) {
|
|
222
|
+
if (index > 0 && hiddenNodes.has(path[index])) {
|
|
223
|
+
restoredAncestorIndexes.push(index);
|
|
224
|
+
}
|
|
225
|
+
restoreHiddenNode(path[index]);
|
|
226
|
+
}
|
|
227
|
+
targetPaths.set(target, path);
|
|
228
|
+
for (const index of restoredAncestorIndexes) {
|
|
229
|
+
const ancestor = path[index];
|
|
230
|
+
const pathChild = path[index - 1];
|
|
231
|
+
for (const child of ancestor.children) {
|
|
232
|
+
if (child !== pathChild) {
|
|
233
|
+
walk(child, true);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
};
|
|
238
|
+
const removeTarget = (target) => {
|
|
239
|
+
if (!targetNodes.delete(target)) {
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
242
|
+
if (!automaticVisibleNodes.has(target)) {
|
|
243
|
+
visibleNodes.delete(target);
|
|
244
|
+
}
|
|
245
|
+
const targetPath = targetPaths.get(target);
|
|
246
|
+
targetPaths.delete(target);
|
|
247
|
+
const connectedAncestor = targetPath?.find((node) => root.contains(node));
|
|
248
|
+
if (!connectedAncestor) {
|
|
249
|
+
return;
|
|
250
|
+
}
|
|
251
|
+
let walkRoot = connectedAncestor;
|
|
252
|
+
while (walkRoot.parentElement &&
|
|
253
|
+
walkRoot.parentElement !== root &&
|
|
254
|
+
!containsVisibleNode(walkRoot.parentElement, visibleNodes)) {
|
|
255
|
+
walkRoot = walkRoot.parentElement;
|
|
256
|
+
}
|
|
257
|
+
walk(walkRoot, true);
|
|
258
|
+
};
|
|
68
259
|
if (observerStack.length) {
|
|
69
260
|
observerStack[observerStack.length - 1].disconnect();
|
|
70
261
|
}
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
hide(node);
|
|
74
|
-
node = walker.nextNode();
|
|
75
|
-
}
|
|
76
|
-
const observer = new currentWindow.MutationObserver((changes) => {
|
|
262
|
+
walk(root, false);
|
|
263
|
+
const processChanges = (changes) => {
|
|
77
264
|
for (const change of changes) {
|
|
78
|
-
if (change.type !== 'childList'
|
|
265
|
+
if (change.type !== 'childList') {
|
|
79
266
|
continue;
|
|
80
267
|
}
|
|
81
|
-
|
|
268
|
+
for (const node of change.removedNodes) {
|
|
269
|
+
if (isElement(node)) {
|
|
270
|
+
removeDisconnectedAlwaysVisibleNodes(node);
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
if (!change.target.isConnected || change.addedNodes.length === 0) {
|
|
274
|
+
continue;
|
|
275
|
+
}
|
|
276
|
+
if (!containsNode(visibleNodes, change.target) &&
|
|
277
|
+
!containsNode(hiddenNodes, change.target)) {
|
|
82
278
|
for (const node of change.addedNodes) {
|
|
83
|
-
if (
|
|
84
|
-
node
|
|
85
|
-
visibleNodes.add(node);
|
|
86
|
-
}
|
|
87
|
-
else if (isElement(node)) {
|
|
88
|
-
hide(node);
|
|
279
|
+
if (isElement(node) && root.contains(node)) {
|
|
280
|
+
walk(node, true);
|
|
89
281
|
}
|
|
90
282
|
}
|
|
91
283
|
}
|
|
92
284
|
}
|
|
93
|
-
}
|
|
285
|
+
};
|
|
286
|
+
const observer = new currentWindow.MutationObserver(processChanges);
|
|
94
287
|
observer.observe(root, { childList: true, subtree: true });
|
|
95
288
|
const observerWrapper = {
|
|
96
289
|
observe() {
|
|
97
290
|
observer.observe(root, { childList: true, subtree: true });
|
|
98
291
|
},
|
|
292
|
+
resume(releasedHiddenNodes, releasedAlwaysVisibleNodes) {
|
|
293
|
+
for (const node of releasedAlwaysVisibleNodes) {
|
|
294
|
+
if (root.contains(node)) {
|
|
295
|
+
addAlwaysVisibleNode(node);
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
for (const node of releasedHiddenNodes) {
|
|
299
|
+
if (shouldAdoptHiddenNode(node)) {
|
|
300
|
+
hide(node);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
observerWrapper.observe();
|
|
304
|
+
},
|
|
99
305
|
disconnect() {
|
|
100
306
|
observer.disconnect();
|
|
101
307
|
},
|
|
102
308
|
};
|
|
103
309
|
observerStack.push(observerWrapper);
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
if (count === 1) {
|
|
109
|
-
node.removeAttribute(ARIA_HIDDEN);
|
|
110
|
-
refCountMap.delete(node);
|
|
111
|
-
}
|
|
112
|
-
else if (typeof count === 'number') {
|
|
113
|
-
refCountMap.set(node, count - 1);
|
|
114
|
-
}
|
|
310
|
+
let isCleanedUp = false;
|
|
311
|
+
const dispose = () => {
|
|
312
|
+
if (isCleanedUp) {
|
|
313
|
+
return;
|
|
115
314
|
}
|
|
315
|
+
isCleanedUp = true;
|
|
316
|
+
const pendingChanges = observer.takeRecords();
|
|
317
|
+
observer.disconnect();
|
|
318
|
+
processChanges(pendingChanges);
|
|
116
319
|
if (observerWrapper === observerStack[observerStack.length - 1]) {
|
|
117
320
|
observerStack.pop();
|
|
118
|
-
|
|
119
|
-
|
|
321
|
+
const previousObserver = observerStack[observerStack.length - 1];
|
|
322
|
+
if (previousObserver?.resume) {
|
|
323
|
+
previousObserver.resume(hiddenNodes, automaticVisibleNodes);
|
|
324
|
+
}
|
|
325
|
+
else {
|
|
326
|
+
previousObserver?.observe();
|
|
120
327
|
}
|
|
121
328
|
}
|
|
122
329
|
else {
|
|
123
330
|
observerStack.splice(observerStack.indexOf(observerWrapper), 1);
|
|
124
331
|
}
|
|
332
|
+
for (const node of [...hiddenNodes]) {
|
|
333
|
+
restoreHiddenNode(node);
|
|
334
|
+
}
|
|
335
|
+
targetPaths.clear();
|
|
336
|
+
targetNodes.clear();
|
|
337
|
+
automaticVisibleNodes.clear();
|
|
338
|
+
};
|
|
339
|
+
return {
|
|
340
|
+
addTarget: (target) => {
|
|
341
|
+
if (!isCleanedUp) {
|
|
342
|
+
addTarget(target);
|
|
343
|
+
}
|
|
344
|
+
},
|
|
345
|
+
removeTarget: (target) => {
|
|
346
|
+
if (!isCleanedUp) {
|
|
347
|
+
removeTarget(target);
|
|
348
|
+
}
|
|
349
|
+
},
|
|
350
|
+
dispose,
|
|
125
351
|
};
|
|
126
352
|
};
|
|
353
|
+
const ariaHideOutside = (targets, options = {}) => createAriaHideOutsideController(targets, options).dispose;
|
|
127
354
|
|
|
128
355
|
const combineProps = (baseProps, newProps, coverPropKeys) => innerCombineProps({
|
|
129
356
|
baseProps,
|
|
@@ -273,4 +500,4 @@ const twMerge = extendTailwindMerge({
|
|
|
273
500
|
},
|
|
274
501
|
});
|
|
275
502
|
|
|
276
|
-
export { ariaHideOutside as a, combineClasses as b, combineProps as c, getRefElement as g, isRef as i, logInDev as l, ownerWindow as o, twMerge as t };
|
|
503
|
+
export { ariaHideOutside as a, combineClasses as b, combineProps as c, createAriaHideOutsideController as d, getRefElement as g, isRef as i, logInDev as l, ownerWindow as o, twMerge as t };
|
package/hooks.js
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { unstable_useEventCallback, unstable_ownerDocument, unstable_Timeout, unstable_useForkRef } from '@mui/utils';
|
|
2
2
|
export { unstable_useControlled as useControlled } from '@mui/utils';
|
|
3
3
|
import * as React from 'react';
|
|
4
|
-
import { useState, useRef, useEffect, useCallback, useLayoutEffect, useMemo } from 'react';
|
|
4
|
+
import React__default, { useState, useRef, useEffect, useCallback, useLayoutEffect, useMemo } from 'react';
|
|
5
5
|
import { useWindowContext, useResponsiveContext, getMatchDetail } from './contexts.js';
|
|
6
|
+
import { b as LIVE_ANNOUNCER_ATTR, e as applyVisuallyHiddenStyles, a as announce } from './chunks/index.js';
|
|
6
7
|
import uniqueId from 'lodash/uniqueId';
|
|
7
8
|
import { useButton as useButton$1 } from '@mui/base/useButton';
|
|
8
|
-
import { c as combineProps,
|
|
9
|
+
import { c as combineProps, d as createAriaHideOutsideController, g as getRefElement, l as logInDev, o as ownerWindow } from './chunks/twMerge.js';
|
|
9
10
|
import './config.js';
|
|
10
11
|
import 'lodash/omit';
|
|
11
12
|
import 'lodash/pick';
|
|
@@ -128,7 +129,7 @@ function useResultRef(cb) {
|
|
|
128
129
|
}
|
|
129
130
|
|
|
130
131
|
const DEFAULT_PREFIX = 'spring-id-';
|
|
131
|
-
function
|
|
132
|
+
function useLegacyId(id, prefixMode) {
|
|
132
133
|
const { current: resultId } = useResultRef(() => {
|
|
133
134
|
if (id) {
|
|
134
135
|
if (prefixMode) {
|
|
@@ -140,27 +141,40 @@ function useId(id, prefixMode) {
|
|
|
140
141
|
});
|
|
141
142
|
return resultId;
|
|
142
143
|
}
|
|
144
|
+
const reactUseId = React__default.useId;
|
|
145
|
+
function useReactId(id, prefixMode) {
|
|
146
|
+
const reactId = reactUseId();
|
|
147
|
+
if (id) {
|
|
148
|
+
return prefixMode ? `${id}-${reactId}` : id;
|
|
149
|
+
}
|
|
150
|
+
return `${DEFAULT_PREFIX}${reactId}`;
|
|
151
|
+
}
|
|
152
|
+
const useId = reactUseId ? useReactId : useLegacyId;
|
|
143
153
|
|
|
144
|
-
const
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
const useAnnouncer = (id) => {
|
|
154
|
+
const isUseAnnouncerOptions = (value) => {
|
|
155
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
156
|
+
};
|
|
157
|
+
function useAnnouncer(idOrOptions, maybeOptions) {
|
|
158
|
+
const id = typeof idOrOptions === 'string' ? idOrOptions : undefined;
|
|
159
|
+
const options = isUseAnnouncerOptions(idOrOptions)
|
|
160
|
+
? idOrOptions
|
|
161
|
+
: maybeOptions;
|
|
162
|
+
const politeness = options?.politeness ?? 'assertive';
|
|
154
163
|
const _id = useId(id ? `rc-announcer-${id}` : 'rc-announcer', !id);
|
|
155
164
|
const { document: targetDocument, externalWindow, globalThis: contextGlobal, } = useWindowContext();
|
|
156
165
|
const win = externalWindow ?? contextGlobal;
|
|
157
166
|
const ref = useRef(null);
|
|
158
167
|
const lastMessageRef = useRef();
|
|
159
168
|
const pendingTimerRef = useRef(null);
|
|
169
|
+
const pendingRemovalRef = useRef(null);
|
|
160
170
|
const clearPendingTimer = useCallback(() => {
|
|
161
171
|
pendingTimerRef.current?.();
|
|
162
172
|
pendingTimerRef.current = null;
|
|
163
173
|
}, []);
|
|
174
|
+
const clearPendingRemoval = useCallback(() => {
|
|
175
|
+
pendingRemovalRef.current?.();
|
|
176
|
+
pendingRemovalRef.current = null;
|
|
177
|
+
}, []);
|
|
164
178
|
useEffect(() => {
|
|
165
179
|
const currentDocument = targetDocument ??
|
|
166
180
|
(typeof contextGlobal.document !== 'undefined'
|
|
@@ -169,18 +183,30 @@ const useAnnouncer = (id) => {
|
|
|
169
183
|
if (!currentDocument?.body) {
|
|
170
184
|
return undefined;
|
|
171
185
|
}
|
|
172
|
-
const
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
el
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
186
|
+
const currentEl = ref.current;
|
|
187
|
+
const canReuseCurrentEl = currentEl?.ownerDocument === currentDocument &&
|
|
188
|
+
currentEl.id === _id &&
|
|
189
|
+
currentDocument.body.contains(currentEl);
|
|
190
|
+
const el = canReuseCurrentEl
|
|
191
|
+
? currentEl
|
|
192
|
+
: currentDocument.createElement('div');
|
|
193
|
+
if (canReuseCurrentEl) {
|
|
194
|
+
clearPendingRemoval();
|
|
195
|
+
el.setAttribute('aria-live', politeness);
|
|
196
|
+
}
|
|
197
|
+
else {
|
|
198
|
+
ref.current = el;
|
|
199
|
+
el.id = _id;
|
|
200
|
+
el.setAttribute('aria-live', politeness);
|
|
201
|
+
el.setAttribute('aria-atomic', 'true');
|
|
202
|
+
el.setAttribute('aria-hidden', 'false');
|
|
203
|
+
el.setAttribute(LIVE_ANNOUNCER_ATTR, 'true');
|
|
204
|
+
applyVisuallyHiddenStyles(el);
|
|
205
|
+
currentDocument.body.appendChild(el);
|
|
206
|
+
}
|
|
181
207
|
return () => {
|
|
182
208
|
clearPendingTimer();
|
|
183
|
-
win.setTimeout(() => {
|
|
209
|
+
const timerId = win.setTimeout(() => {
|
|
184
210
|
const body = currentDocument.body;
|
|
185
211
|
if (body.contains(el)) {
|
|
186
212
|
body.removeChild(el);
|
|
@@ -188,9 +214,24 @@ const useAnnouncer = (id) => {
|
|
|
188
214
|
if (el === ref.current) {
|
|
189
215
|
ref.current = null;
|
|
190
216
|
}
|
|
217
|
+
if (pendingRemovalRef.current === cancelRemoval) {
|
|
218
|
+
pendingRemovalRef.current = null;
|
|
219
|
+
}
|
|
191
220
|
});
|
|
221
|
+
const cancelRemoval = () => {
|
|
222
|
+
win.clearTimeout(timerId);
|
|
223
|
+
};
|
|
224
|
+
pendingRemovalRef.current = cancelRemoval;
|
|
192
225
|
};
|
|
193
|
-
}, [
|
|
226
|
+
}, [
|
|
227
|
+
_id,
|
|
228
|
+
politeness,
|
|
229
|
+
targetDocument,
|
|
230
|
+
contextGlobal,
|
|
231
|
+
win,
|
|
232
|
+
clearPendingTimer,
|
|
233
|
+
clearPendingRemoval,
|
|
234
|
+
]);
|
|
194
235
|
return useCallback((message) => {
|
|
195
236
|
const el = ref.current;
|
|
196
237
|
if (!el)
|
|
@@ -221,7 +262,20 @@ const useAnnouncer = (id) => {
|
|
|
221
262
|
};
|
|
222
263
|
pendingTimerRef.current = cancelTimer;
|
|
223
264
|
}, [clearPendingTimer, win]);
|
|
224
|
-
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
function useGlobalAnnouncer() {
|
|
268
|
+
const { document: targetDocument } = useWindowContext();
|
|
269
|
+
return useCallback((message, politeness = 'assertive', timeoutOrOptions) => {
|
|
270
|
+
const timeout = typeof timeoutOrOptions === 'number'
|
|
271
|
+
? timeoutOrOptions
|
|
272
|
+
: timeoutOrOptions?.timeout;
|
|
273
|
+
announce(message, politeness, {
|
|
274
|
+
timeout,
|
|
275
|
+
document: targetDocument,
|
|
276
|
+
});
|
|
277
|
+
}, [targetDocument]);
|
|
278
|
+
}
|
|
225
279
|
|
|
226
280
|
function useButton(parameters) {
|
|
227
281
|
const muiButtonResult = useButton$1(parameters);
|
|
@@ -241,18 +295,42 @@ function useButton(parameters) {
|
|
|
241
295
|
}
|
|
242
296
|
|
|
243
297
|
const useAriaHideOutside = (props = {}) => {
|
|
244
|
-
const { enable = true } = props;
|
|
245
|
-
const {
|
|
298
|
+
const { enable = true, shouldUseInert = false } = props;
|
|
299
|
+
const { document } = useWindowContext();
|
|
246
300
|
const ariaHideOutsideRef = useRef(null);
|
|
247
|
-
|
|
301
|
+
const branchNodesRef = useRef(new Set());
|
|
302
|
+
const controllerRef = useRef();
|
|
303
|
+
const setAriaHideOutsideTarget = useCallback((node, isActive) => {
|
|
304
|
+
if (isActive) {
|
|
305
|
+
branchNodesRef.current.add(node);
|
|
306
|
+
controllerRef.current?.addTarget(node);
|
|
307
|
+
}
|
|
308
|
+
else {
|
|
309
|
+
branchNodesRef.current.delete(node);
|
|
310
|
+
controllerRef.current?.removeTarget(node);
|
|
311
|
+
}
|
|
312
|
+
}, []);
|
|
313
|
+
useLayoutEffect(() => {
|
|
248
314
|
if (enable && ariaHideOutsideRef.current) {
|
|
249
|
-
|
|
315
|
+
const controller = createAriaHideOutsideController([ariaHideOutsideRef.current, ...branchNodesRef.current], {
|
|
316
|
+
root: document?.body,
|
|
317
|
+
shouldUseInert,
|
|
318
|
+
});
|
|
319
|
+
controllerRef.current = controller;
|
|
320
|
+
return () => {
|
|
321
|
+
if (controllerRef.current === controller) {
|
|
322
|
+
controllerRef.current = undefined;
|
|
323
|
+
}
|
|
324
|
+
controller.dispose();
|
|
325
|
+
};
|
|
250
326
|
}
|
|
327
|
+
controllerRef.current = undefined;
|
|
251
328
|
return undefined;
|
|
252
|
-
}, [enable,
|
|
329
|
+
}, [enable, shouldUseInert, document?.body]);
|
|
253
330
|
return {
|
|
254
331
|
ariaHideOutsideRef: ariaHideOutsideRef,
|
|
255
332
|
ariaHideEnabled: enable,
|
|
333
|
+
setAriaHideOutsideTarget,
|
|
256
334
|
};
|
|
257
335
|
};
|
|
258
336
|
|
|
@@ -263,9 +341,9 @@ const useAudio = (init) => {
|
|
|
263
341
|
return audio;
|
|
264
342
|
});
|
|
265
343
|
useEffect(() => {
|
|
344
|
+
const audio = audioRef.current;
|
|
266
345
|
return () => {
|
|
267
|
-
|
|
268
|
-
audioRef.current = null;
|
|
346
|
+
audio.srcObject = null;
|
|
269
347
|
};
|
|
270
348
|
}, []);
|
|
271
349
|
return audioRef.current;
|
|
@@ -1099,13 +1177,14 @@ const useKeyboardMoveFocus = ({ infinite, options = [], focusedIndexRef, onFocus
|
|
|
1099
1177
|
};
|
|
1100
1178
|
|
|
1101
1179
|
const MOVE_ATTR_KEY = 'data-move-focused';
|
|
1180
|
+
const MOVE_INDEX_ATTR_KEY = 'data-move-index';
|
|
1102
1181
|
const useOnlyOneFocusable = ({ focusedIndexRef, containerRef, }) => {
|
|
1103
|
-
const moveFocusedId = useId(
|
|
1104
|
-
const getIndexElm = (toIndex) => containerRef.current?.querySelector(`[${moveFocusedId}="${toIndex}"]`);
|
|
1182
|
+
const moveFocusedId = useId();
|
|
1183
|
+
const getIndexElm = (toIndex) => containerRef.current?.querySelector(`[${MOVE_ATTR_KEY}="${moveFocusedId}"][${MOVE_INDEX_ATTR_KEY}="${toIndex}"]`);
|
|
1105
1184
|
const setIndexTabIndex = (toIndex) => {
|
|
1106
1185
|
const toElm = getIndexElm(toIndex);
|
|
1107
1186
|
if (toElm) {
|
|
1108
|
-
const prevElm = containerRef.current?.querySelector(`[${moveFocusedId}][tabindex="0"]`);
|
|
1187
|
+
const prevElm = containerRef.current?.querySelector(`[${MOVE_ATTR_KEY}="${moveFocusedId}"][tabindex="0"]`);
|
|
1109
1188
|
prevElm?.setAttribute('tabindex', '-1');
|
|
1110
1189
|
toElm.setAttribute('tabindex', '0');
|
|
1111
1190
|
}
|
|
@@ -1120,13 +1199,14 @@ const useOnlyOneFocusable = ({ focusedIndexRef, containerRef, }) => {
|
|
|
1120
1199
|
setIndexTabIndex,
|
|
1121
1200
|
getItemProps: (index, options) => {
|
|
1122
1201
|
return combineProps({
|
|
1123
|
-
[
|
|
1202
|
+
[MOVE_ATTR_KEY]: moveFocusedId,
|
|
1203
|
+
[MOVE_INDEX_ATTR_KEY]: String(index),
|
|
1124
1204
|
tabIndex: index === focusedIndexRef.current ? 0 : -1,
|
|
1125
1205
|
onFocus: () => {
|
|
1126
1206
|
focusedIndexRef.current = index;
|
|
1127
1207
|
setIndexTabIndex(index);
|
|
1128
1208
|
},
|
|
1129
|
-
}, options);
|
|
1209
|
+
}, options ?? {});
|
|
1130
1210
|
},
|
|
1131
1211
|
};
|
|
1132
1212
|
};
|
|
@@ -1942,4 +2022,4 @@ const useVirtuosoExternalWindow = () => {
|
|
|
1942
2022
|
return undefined;
|
|
1943
2023
|
};
|
|
1944
2024
|
|
|
1945
|
-
export { createGlobalListener, globalListenerEventMap, isElmEqualOrContain, isTouchEvent, moveIndexInTwoDimension, teardown, useA11yKeyEvent, useActive, useAnnouncer, useAriaHideOutside, useAudio, useBodyScrollLock, useButton, useChange, useClickAway, useDebounce, useDepsChange, useEventCallback, useEventListener, useEver, useFocusVisible, useFontLoaded, useForceUpdate, useForkRef, useGlobalListener, useHiddenTabindex, useHover, useHoverDirty, useId, useImgLoaded, useInterval, useIsFocusVisible, useKeyDownOnce, useKeyboardMoveFocus, useLongPress, useMountState, useOnLayoutReRender, useOnReRender, useOnlyOneFocusable, useOverflow, usePrevious, useRefState, useResizeObserver, useResponsiveMatch, useResultRef, useRetry, useSleep, useTabOut, useTestRunnerDetection, useThrottle, useTimeout, useTouchMouseEvent, useVirtuosoExternalWindow
|
|
2025
|
+
export { createGlobalListener, globalListenerEventMap, isElmEqualOrContain, isTouchEvent, moveIndexInTwoDimension, teardown, useA11yKeyEvent, useActive, useAnnouncer, useAriaHideOutside, useAudio, useBodyScrollLock, useButton, useChange, useClickAway, useDebounce, useDepsChange, useEventCallback, useEventListener, useEver, useFocusVisible, useFontLoaded, useForceUpdate, useForkRef, useGlobalAnnouncer, useGlobalListener, useHiddenTabindex, useHover, useHoverDirty, useId, useImgLoaded, useInterval, useIsFocusVisible, useKeyDownOnce, useKeyboardMoveFocus, useLongPress, useMountState, useOnLayoutReRender, useOnReRender, useOnlyOneFocusable, useOverflow, usePrevious, useRefState, useResizeObserver, useResponsiveMatch, useResultRef, useRetry, useSleep, useTabOut, useTestRunnerDetection, useThrottle, useTimeout, useTouchMouseEvent, useVirtuosoExternalWindow };
|
package/index.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
export { configure, rcConfiguration } from './config.js';
|
|
2
2
|
export { RcResponsiveContext, WindowContext, breakpointList, getMatchDetail, useResponsiveContext, useWindowContext } from './contexts.js';
|
|
3
|
-
export { createGlobalListener, globalListenerEventMap, isElmEqualOrContain, isTouchEvent, moveIndexInTwoDimension, teardown, useA11yKeyEvent, useActive, useAnnouncer, useAriaHideOutside, useAudio, useBodyScrollLock, useButton, useChange, useClickAway, useDebounce, useDepsChange, useEventCallback, useEventListener, useEver, useFocusVisible, useFontLoaded, useForceUpdate, useForkRef, useGlobalListener, useHiddenTabindex, useHover, useHoverDirty, useId, useImgLoaded, useInterval, useIsFocusVisible, useKeyDownOnce, useKeyboardMoveFocus, useLongPress, useMountState, useOnLayoutReRender, useOnReRender, useOnlyOneFocusable, useOverflow, usePrevious, useRefState, useResizeObserver, useResponsiveMatch, useResultRef, useRetry, useSleep, useTabOut, useTestRunnerDetection, useThrottle, useTimeout, useTouchMouseEvent, useVirtuosoExternalWindow
|
|
3
|
+
export { createGlobalListener, globalListenerEventMap, isElmEqualOrContain, isTouchEvent, moveIndexInTwoDimension, teardown, useA11yKeyEvent, useActive, useAnnouncer, useAriaHideOutside, useAudio, useBodyScrollLock, useButton, useChange, useClickAway, useDebounce, useDepsChange, useEventCallback, useEventListener, useEver, useFocusVisible, useFontLoaded, useForceUpdate, useForkRef, useGlobalAnnouncer, useGlobalListener, useHiddenTabindex, useHover, useHoverDirty, useId, useImgLoaded, useInterval, useIsFocusVisible, useKeyDownOnce, useKeyboardMoveFocus, useLongPress, useMountState, useOnLayoutReRender, useOnReRender, useOnlyOneFocusable, useOverflow, usePrevious, useRefState, useResizeObserver, useResponsiveMatch, useResultRef, useRetry, useSleep, useTabOut, useTestRunnerDetection, useThrottle, useTimeout, useTouchMouseEvent, useVirtuosoExternalWindow } from './hooks.js';
|
|
4
4
|
export { unstable_ownerDocument as ownerDocument, unstable_useControlled as useControlled } from '@mui/utils';
|
|
5
|
+
export { L as LIVEREGION_TIMEOUT_DELAY, a as announce, c as clearAnnouncer, d as destroyAnnouncer } from './chunks/index.js';
|
|
5
6
|
export { a11yKeyboard, capitalize, checkDefaultPrevented, checkElementName, clearWindowSelection, getSelectionPosition, getTitle, globalStateClasses, isDeleteKeyboardEvent, isSuiElement, isUrl, isWebKit154, omit, outerSize, pick, preloadImg, removeClassName, runKeyHandler, setSelectionPosition, useClasses } from './utils.js';
|
|
6
7
|
export { a as ariaHideOutside, b as combineClasses, c as combineProps, g as getRefElement, i as isRef, l as logInDev, o as ownerWindow, t as twMerge } from './chunks/twMerge.js';
|
|
7
8
|
import 'react';
|
package/liveAnnouncer.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { L as LIVEREGION_TIMEOUT_DELAY, a as announce, c as clearAnnouncer, d as destroyAnnouncer } from './chunks/index.js';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ringcentral/spring-base",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.12.0",
|
|
4
4
|
"author": "RingCentral",
|
|
5
5
|
"description": "Core utilities, hooks, and foundational functionality for Spring UI components, providing essential building blocks for React applications",
|
|
6
6
|
"main": "./index.js",
|
|
@@ -16,9 +16,9 @@
|
|
|
16
16
|
"type-fest": "^2.12.2"
|
|
17
17
|
},
|
|
18
18
|
"peerDependencies": {
|
|
19
|
-
"@types/react": "
|
|
20
|
-
"react": "
|
|
21
|
-
"react-dom": "
|
|
19
|
+
"@types/react": "^17.0.3 || ^18.0.0 || ^19.0.0",
|
|
20
|
+
"react": "^17.0.2 || ^18.0.0 || ^19.0.0",
|
|
21
|
+
"react-dom": "^17.0.2 || ^18.0.0 || ^19.0.0"
|
|
22
22
|
},
|
|
23
23
|
"sideEffects": false,
|
|
24
24
|
"license": "MIT",
|
package/src/hooks/index.d.ts
CHANGED
|
@@ -1,2 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
export
|
|
1
|
+
import type { AnnouncerPoliteness } from '../../liveAnnouncer';
|
|
2
|
+
export type UseAnnouncerOptions = {
|
|
3
|
+
politeness?: AnnouncerPoliteness;
|
|
4
|
+
};
|
|
5
|
+
export declare function useAnnouncer(idOrOptions?: string | UseAnnouncerOptions, maybeOptions?: UseAnnouncerOptions): (message?: string) => void;
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
/// <reference types="react" />
|
|
2
2
|
export type UseAriaHideOutsideProps = {
|
|
3
3
|
enable?: boolean;
|
|
4
|
+
shouldUseInert?: boolean;
|
|
4
5
|
};
|
|
5
6
|
export declare const useAriaHideOutside: (props?: UseAriaHideOutsideProps) => {
|
|
6
7
|
ariaHideOutsideRef: import("react").RefObject<HTMLElement>;
|
|
7
8
|
ariaHideEnabled: boolean;
|
|
9
|
+
setAriaHideOutsideTarget: (node: HTMLElement, isActive: boolean) => void;
|
|
8
10
|
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './useGlobalAnnouncer';
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { AnnounceMessage, AnnouncerPoliteness } from '../../liveAnnouncer/types';
|
|
2
|
+
export type GlobalAnnounceOptions = {
|
|
3
|
+
timeout?: number;
|
|
4
|
+
};
|
|
5
|
+
export declare function useGlobalAnnouncer(): (message: AnnounceMessage, politeness?: AnnouncerPoliteness, timeoutOrOptions?: number | GlobalAnnounceOptions) => void;
|
|
@@ -10,7 +10,7 @@ export type UseKeyboardMoveFocusParams<T> = {
|
|
|
10
10
|
endSearchDebounceTime?: number;
|
|
11
11
|
} & Pick<MoveIndexInTwoDimensionalOptions, 'infinite'>;
|
|
12
12
|
export declare const useKeyboardMoveFocus: <T>({ infinite, options, focusedIndexRef, onFocusedIndexChange, getOptionDisabled, getOptionSearchText, columns, endSearchDebounceTime, }: UseKeyboardMoveFocusParams<T>) => {
|
|
13
|
-
getPrevFocusableOption: (index?: number, isInfinite?:
|
|
14
|
-
getNextFocusableOption: (index?: number, isInfinite?:
|
|
13
|
+
getPrevFocusableOption: (index?: number, isInfinite?: boolean) => number;
|
|
14
|
+
getNextFocusableOption: (index?: number, isInfinite?: boolean) => number;
|
|
15
15
|
onKeyFocusedIndexHandle: (event: React.KeyboardEvent<any>) => void;
|
|
16
16
|
};
|
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
/// <reference types="react" />
|
|
2
|
+
import type { HTMLDataAttribute } from '../../typings';
|
|
2
3
|
export type UseOnlyOneFocusableParams = {
|
|
3
4
|
focusedIndexRef: React.MutableRefObject<number>;
|
|
4
5
|
containerRef: React.RefObject<HTMLElement>;
|
|
5
6
|
};
|
|
7
|
+
type MoveFocusedItemProps = Omit<React.HTMLAttributes<HTMLElement>, 'children'> & HTMLDataAttribute;
|
|
6
8
|
export declare const useOnlyOneFocusable: ({ focusedIndexRef, containerRef, }: UseOnlyOneFocusableParams) => {
|
|
7
9
|
moveFocusedId: string;
|
|
8
10
|
focusIndex: (toIndex: number) => void;
|
|
9
11
|
setIndexTabIndex: (toIndex: number) => void;
|
|
10
|
-
getItemProps: (index: number, options?: Omit<React.HTMLAttributes<HTMLElement>, 'children'>) =>
|
|
12
|
+
getItemProps: (index: number, options?: Omit<React.HTMLAttributes<HTMLElement>, 'children'>) => NonNullable<MoveFocusedItemProps>;
|
|
11
13
|
};
|
|
14
|
+
export {};
|
package/src/index.d.ts
CHANGED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { AnnounceMessage, AnnouncerPoliteness } from './types';
|
|
2
|
+
export declare class LiveAnnouncer {
|
|
3
|
+
node: HTMLElement | null;
|
|
4
|
+
assertiveLog: HTMLElement | null;
|
|
5
|
+
politeLog: HTMLElement | null;
|
|
6
|
+
private readonly doc;
|
|
7
|
+
private readonly repeatText;
|
|
8
|
+
private ready;
|
|
9
|
+
private pending;
|
|
10
|
+
private firstDelayTimerId;
|
|
11
|
+
constructor(doc: Document);
|
|
12
|
+
isAttached(): boolean;
|
|
13
|
+
createLog(ariaLive: AnnouncerPoliteness): HTMLElement;
|
|
14
|
+
destroy(): void;
|
|
15
|
+
schedule(message: AnnounceMessage, politeness?: AnnouncerPoliteness, timeout?: number): void;
|
|
16
|
+
announce(message: AnnounceMessage, politeness?: AnnouncerPoliteness, timeout?: number): void;
|
|
17
|
+
clear(politeness?: AnnouncerPoliteness): void;
|
|
18
|
+
private cancelFirstDelay;
|
|
19
|
+
}
|
|
20
|
+
export declare const LIVEREGION_TIMEOUT_DELAY = 7000;
|
|
21
|
+
export declare const FIRST_ANNOUNCE_DELAY_MS = 100;
|
|
22
|
+
export declare const getLiveAnnouncer: (doc: Document) => LiveAnnouncer | null;
|
|
23
|
+
export declare const ensureLiveAnnouncer: (doc: Document) => LiveAnnouncer;
|
|
24
|
+
export declare const clearLiveAnnouncerRegistry: (doc?: Document) => void;
|
|
25
|
+
export declare const isTestAnnounceEnvironment: () => boolean;
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { AnnounceMessage, AnnounceOptions, AnnouncerPoliteness } from './types';
|
|
2
|
+
export declare function announce(message: AnnounceMessage, politeness?: AnnouncerPoliteness, timeoutOrOptions?: number | AnnounceOptions): void;
|
|
3
|
+
export declare function clearAnnouncer(politeness?: AnnouncerPoliteness, doc?: Document): void;
|
|
4
|
+
export declare function destroyAnnouncer(doc?: Document): void;
|
|
@@ -1 +1,11 @@
|
|
|
1
|
-
export
|
|
1
|
+
export type AriaHideOutsideController = {
|
|
2
|
+
addTarget: (target: Element) => void;
|
|
3
|
+
removeTarget: (target: Element) => void;
|
|
4
|
+
dispose: () => void;
|
|
5
|
+
};
|
|
6
|
+
export type AriaHideOutsideOptions = {
|
|
7
|
+
root?: Element;
|
|
8
|
+
shouldUseInert?: boolean;
|
|
9
|
+
};
|
|
10
|
+
export declare const createAriaHideOutsideController: (targets: Element[], options?: AriaHideOutsideOptions | Element) => AriaHideOutsideController;
|
|
11
|
+
export declare const ariaHideOutside: (targets: Element[], options?: AriaHideOutsideOptions | Element) => () => void;
|
package/src/utils/index.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export * from './a11yKeyboardCode';
|
|
2
|
-
export
|
|
2
|
+
export { ariaHideOutside } from './ariaHideOutside';
|
|
3
|
+
export type { AriaHideOutsideOptions } from './ariaHideOutside';
|
|
3
4
|
export * from './capitalize';
|
|
4
5
|
export * from './checkDefaultPrevented';
|
|
5
6
|
export * from './checkElementName';
|