@fluentui/react-aria 9.11.0 → 9.11.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +24 -2
- package/lib/AriaLiveAnnouncer/useAriaLiveAnnouncer.js +12 -114
- package/lib/AriaLiveAnnouncer/useAriaLiveAnnouncer.js.map +1 -1
- package/lib/AriaLiveAnnouncer/useAriaNotifyAnnounce.js +26 -0
- package/lib/AriaLiveAnnouncer/useAriaNotifyAnnounce.js.map +1 -0
- package/lib/AriaLiveAnnouncer/useDomAnnounce.js +121 -0
- package/lib/AriaLiveAnnouncer/useDomAnnounce.js.map +1 -0
- package/lib-commonjs/AriaLiveAnnouncer/useAriaLiveAnnouncer.js +12 -114
- package/lib-commonjs/AriaLiveAnnouncer/useAriaLiveAnnouncer.js.map +1 -1
- package/lib-commonjs/AriaLiveAnnouncer/useAriaNotifyAnnounce.js +37 -0
- package/lib-commonjs/AriaLiveAnnouncer/useAriaNotifyAnnounce.js.map +1 -0
- package/lib-commonjs/AriaLiveAnnouncer/useDomAnnounce.js +132 -0
- package/lib-commonjs/AriaLiveAnnouncer/useDomAnnounce.js.map +1 -0
- package/package.json +5 -5
package/CHANGELOG.md
CHANGED
|
@@ -1,12 +1,34 @@
|
|
|
1
1
|
# Change Log - @fluentui/react-aria
|
|
2
2
|
|
|
3
|
-
This log was last generated on Thu,
|
|
3
|
+
This log was last generated on Thu, 09 May 2024 19:31:57 GMT and should not be manually modified.
|
|
4
4
|
|
|
5
5
|
<!-- Start content -->
|
|
6
6
|
|
|
7
|
+
## [9.11.2](https://github.com/microsoft/fluentui/tree/@fluentui/react-aria_v9.11.2)
|
|
8
|
+
|
|
9
|
+
Thu, 09 May 2024 19:31:57 GMT
|
|
10
|
+
[Compare changes](https://github.com/microsoft/fluentui/compare/@fluentui/react-aria_v9.11.1..@fluentui/react-aria_v9.11.2)
|
|
11
|
+
|
|
12
|
+
### Patches
|
|
13
|
+
|
|
14
|
+
- Bump @fluentui/react-tabster to v9.21.2 ([PR #31321](https://github.com/microsoft/fluentui/pull/31321) by beachball)
|
|
15
|
+
|
|
16
|
+
## [9.11.1](https://github.com/microsoft/fluentui/tree/@fluentui/react-aria_v9.11.1)
|
|
17
|
+
|
|
18
|
+
Mon, 06 May 2024 12:55:00 GMT
|
|
19
|
+
[Compare changes](https://github.com/microsoft/fluentui/compare/@fluentui/react-aria_v9.11.0..@fluentui/react-aria_v9.11.1)
|
|
20
|
+
|
|
21
|
+
### Patches
|
|
22
|
+
|
|
23
|
+
- feat: use ariaNotify in AriaLiveAnnouncer when available ([PR #31251](https://github.com/microsoft/fluentui/pull/31251) by sarah.higley@microsoft.com)
|
|
24
|
+
- Bump @fluentui/react-shared-contexts to v9.18.0 ([PR #31271](https://github.com/microsoft/fluentui/pull/31271) by beachball)
|
|
25
|
+
- Bump @fluentui/react-jsx-runtime to v9.0.37 ([PR #31271](https://github.com/microsoft/fluentui/pull/31271) by beachball)
|
|
26
|
+
- Bump @fluentui/react-tabster to v9.21.1 ([PR #31271](https://github.com/microsoft/fluentui/pull/31271) by beachball)
|
|
27
|
+
- Bump @fluentui/react-utilities to v9.18.8 ([PR #31271](https://github.com/microsoft/fluentui/pull/31271) by beachball)
|
|
28
|
+
|
|
7
29
|
## [9.11.0](https://github.com/microsoft/fluentui/tree/@fluentui/react-aria_v9.11.0)
|
|
8
30
|
|
|
9
|
-
Thu, 02 May 2024 11:
|
|
31
|
+
Thu, 02 May 2024 11:36:29 GMT
|
|
10
32
|
[Compare changes](https://github.com/microsoft/fluentui/compare/@fluentui/react-aria_v9.10.5..@fluentui/react-aria_v9.11.0)
|
|
11
33
|
|
|
12
34
|
### Minor changes
|
|
@@ -1,121 +1,19 @@
|
|
|
1
|
-
import { useFluent_unstable as useFluent } from '@fluentui/react-shared-contexts';
|
|
2
|
-
import { createPriorityQueue, useTimeout } from '@fluentui/react-utilities';
|
|
3
1
|
import * as React from 'react';
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
height: '1px',
|
|
8
|
-
margin: '-1px',
|
|
9
|
-
width: '1px',
|
|
10
|
-
position: 'absolute',
|
|
11
|
-
overflow: 'hidden',
|
|
12
|
-
textWrap: 'nowrap'
|
|
13
|
-
};
|
|
2
|
+
import { useFluent_unstable as useFluent } from '@fluentui/react-shared-contexts';
|
|
3
|
+
import { useDomAnnounce_unstable } from './useDomAnnounce';
|
|
4
|
+
import { useAriaNotifyAnnounce_unstable } from './useAriaNotifyAnnounce';
|
|
14
5
|
export const useAriaLiveAnnouncer_unstable = (props)=>{
|
|
15
6
|
const { targetDocument } = useFluent();
|
|
16
|
-
const
|
|
17
|
-
const
|
|
18
|
-
const
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
const batchMessages = React.useRef([]);
|
|
23
|
-
const [messageQueue] = React.useState(()=>createPriorityQueue((a, b)=>{
|
|
24
|
-
if (a.priority !== b.priority) {
|
|
25
|
-
return b.priority - a.priority;
|
|
26
|
-
}
|
|
27
|
-
return a.createdAt - b.createdAt;
|
|
28
|
-
}));
|
|
29
|
-
const queueMessage = React.useCallback(()=>{
|
|
30
|
-
if (timeoutRef.current || !elementRef.current) {
|
|
31
|
-
return;
|
|
32
|
-
}
|
|
33
|
-
const runCycle = ()=>{
|
|
34
|
-
if (!elementRef.current) {
|
|
35
|
-
return;
|
|
36
|
-
}
|
|
37
|
-
if (targetDocument && messageQueue.peek()) {
|
|
38
|
-
// need a wrapping element for Narrator/Edge, which currently does not pick up text-only live region changes
|
|
39
|
-
// consistently
|
|
40
|
-
// if this is fixed, we can set textContent to the string directly
|
|
41
|
-
const wrappingEl = targetDocument.createElement('span');
|
|
42
|
-
wrappingEl.innerText = messageQueue.all().filter((msg)=>msg.message.trim().length > 0).reduce((prevText, currMsg)=>prevText + currMsg.message + '. ', '');
|
|
43
|
-
elementRef.current.innerText = '';
|
|
44
|
-
elementRef.current.appendChild(wrappingEl);
|
|
45
|
-
messageQueue.clear();
|
|
46
|
-
batchMessages.current = [];
|
|
47
|
-
// begin new cycle to clear (or update) messages
|
|
48
|
-
timeoutRef.current = setAnnounceTimeout(()=>{
|
|
49
|
-
runCycle();
|
|
50
|
-
}, MESSAGE_DURATION);
|
|
51
|
-
} else {
|
|
52
|
-
elementRef.current.textContent = '';
|
|
53
|
-
clearAnnounceTimeout();
|
|
54
|
-
timeoutRef.current = undefined;
|
|
55
|
-
}
|
|
56
|
-
};
|
|
57
|
-
runCycle();
|
|
58
|
-
}, [
|
|
59
|
-
clearAnnounceTimeout,
|
|
60
|
-
messageQueue,
|
|
61
|
-
setAnnounceTimeout,
|
|
62
|
-
targetDocument
|
|
63
|
-
]);
|
|
64
|
-
const announce = React.useMemo(()=>(message, options = {})=>{
|
|
65
|
-
const { alert = false, priority = 0, batchId } = options;
|
|
66
|
-
// check if message is an alert
|
|
67
|
-
if (alert) {
|
|
68
|
-
// TODO: alert implementation
|
|
69
|
-
// setAlertList([...alertList, message]);
|
|
70
|
-
}
|
|
71
|
-
const liveMessage = {
|
|
72
|
-
message,
|
|
73
|
-
createdAt: order.current++,
|
|
74
|
-
priority,
|
|
75
|
-
batchId
|
|
76
|
-
};
|
|
77
|
-
// check if batchId exists
|
|
78
|
-
if (batchId) {
|
|
79
|
-
// update associated msg if it does
|
|
80
|
-
const batchMessage = batchMessages.current.find((msg)=>msg.batchId === batchId);
|
|
81
|
-
if (batchMessage) {
|
|
82
|
-
// replace existing message in queue
|
|
83
|
-
messageQueue.remove(batchMessage.message);
|
|
84
|
-
// update list of existing batchIds w/ most recent message
|
|
85
|
-
batchMessage.message = liveMessage;
|
|
86
|
-
} else {
|
|
87
|
-
// update list of existing batchIds, add new if doesn't already exist
|
|
88
|
-
batchMessages.current = [
|
|
89
|
-
...batchMessages.current,
|
|
90
|
-
{
|
|
91
|
-
batchId,
|
|
92
|
-
message: liveMessage
|
|
93
|
-
}
|
|
94
|
-
];
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
// add new message
|
|
98
|
-
messageQueue.enqueue(liveMessage);
|
|
99
|
-
queueMessage();
|
|
100
|
-
}, [
|
|
101
|
-
messageQueue,
|
|
102
|
-
queueMessage
|
|
103
|
-
]);
|
|
104
|
-
React.useEffect(()=>{
|
|
105
|
-
if (!targetDocument) {
|
|
106
|
-
return;
|
|
107
|
-
}
|
|
108
|
-
const element = targetDocument.createElement('div');
|
|
109
|
-
element.setAttribute('aria-live', 'assertive');
|
|
110
|
-
Object.assign(element.style, VISUALLY_HIDDEN_STYLES);
|
|
111
|
-
targetDocument.body.append(element);
|
|
112
|
-
elementRef.current = element;
|
|
113
|
-
return ()=>{
|
|
114
|
-
element.remove();
|
|
115
|
-
elementRef.current = null;
|
|
116
|
-
};
|
|
7
|
+
const domAnnounce = useDomAnnounce_unstable();
|
|
8
|
+
const ariaNotifyAnnounce = useAriaNotifyAnnounce_unstable();
|
|
9
|
+
const announce = React.useMemo(()=>{
|
|
10
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
11
|
+
const supportsAriaNotify = typeof (targetDocument === null || targetDocument === void 0 ? void 0 : targetDocument.ariaNotify) === 'function';
|
|
12
|
+
return supportsAriaNotify ? ariaNotifyAnnounce : domAnnounce;
|
|
117
13
|
}, [
|
|
118
|
-
targetDocument
|
|
14
|
+
targetDocument,
|
|
15
|
+
ariaNotifyAnnounce,
|
|
16
|
+
domAnnounce
|
|
119
17
|
]);
|
|
120
18
|
return {
|
|
121
19
|
announce,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["useAriaLiveAnnouncer.ts"],"sourcesContent":["import { useFluent_unstable as useFluent } from '@fluentui/react-shared-contexts';\nimport {
|
|
1
|
+
{"version":3,"sources":["useAriaLiveAnnouncer.ts"],"sourcesContent":["import * as React from 'react';\nimport { useFluent_unstable as useFluent } from '@fluentui/react-shared-contexts';\nimport { useDomAnnounce_unstable } from './useDomAnnounce';\nimport { useAriaNotifyAnnounce_unstable } from './useAriaNotifyAnnounce';\n\nimport type { AriaLiveAnnouncerState, AriaLiveAnnouncerProps } from './AriaLiveAnnouncer.types';\n\nexport const useAriaLiveAnnouncer_unstable = (props: AriaLiveAnnouncerProps): AriaLiveAnnouncerState => {\n const { targetDocument } = useFluent();\n const domAnnounce = useDomAnnounce_unstable();\n const ariaNotifyAnnounce = useAriaNotifyAnnounce_unstable();\n\n const announce = React.useMemo(() => {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const supportsAriaNotify = typeof (targetDocument as any)?.ariaNotify === 'function';\n return supportsAriaNotify ? ariaNotifyAnnounce : domAnnounce;\n }, [targetDocument, ariaNotifyAnnounce, domAnnounce]);\n\n return {\n announce,\n children: props.children,\n };\n};\n"],"names":["React","useFluent_unstable","useFluent","useDomAnnounce_unstable","useAriaNotifyAnnounce_unstable","useAriaLiveAnnouncer_unstable","props","targetDocument","domAnnounce","ariaNotifyAnnounce","announce","useMemo","supportsAriaNotify","ariaNotify","children"],"mappings":"AAAA,YAAYA,WAAW,QAAQ;AAC/B,SAASC,sBAAsBC,SAAS,QAAQ,kCAAkC;AAClF,SAASC,uBAAuB,QAAQ,mBAAmB;AAC3D,SAASC,8BAA8B,QAAQ,0BAA0B;AAIzE,OAAO,MAAMC,gCAAgC,CAACC;IAC5C,MAAM,EAAEC,cAAc,EAAE,GAAGL;IAC3B,MAAMM,cAAcL;IACpB,MAAMM,qBAAqBL;IAE3B,MAAMM,WAAWV,MAAMW,OAAO,CAAC;QAC7B,8DAA8D;QAC9D,MAAMC,qBAAqB,QAAQL,2BAAAA,qCAAD,AAACA,eAAwBM,UAAU,MAAK;QAC1E,OAAOD,qBAAqBH,qBAAqBD;IACnD,GAAG;QAACD;QAAgBE;QAAoBD;KAAY;IAEpD,OAAO;QACLE;QACAI,UAAUR,MAAMQ,QAAQ;IAC1B;AACF,EAAE"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { useFluent_unstable as useFluent } from '@fluentui/react-shared-contexts';
|
|
2
|
+
import * as React from 'react';
|
|
3
|
+
/* INTERNAL: implementation of the announcer using the ariaNotify API */ export const useAriaNotifyAnnounce_unstable = ()=>{
|
|
4
|
+
const { targetDocument } = useFluent();
|
|
5
|
+
const announce = React.useCallback((message, options = {})=>{
|
|
6
|
+
if (!targetDocument) {
|
|
7
|
+
return;
|
|
8
|
+
}
|
|
9
|
+
const { alert = false, polite, batchId } = options;
|
|
10
|
+
// default priority to 0 if polite, 2 if alert, and 1 by default
|
|
11
|
+
// used to set both ariaNotify's priority and interrupt
|
|
12
|
+
const defaultPriority = polite ? 0 : alert ? 2 : 1;
|
|
13
|
+
var _options_priority;
|
|
14
|
+
const priority = (_options_priority = options.priority) !== null && _options_priority !== void 0 ? _options_priority : defaultPriority;
|
|
15
|
+
// map fluent announce options to ariaNotify options
|
|
16
|
+
const ariaNotifyOptions = {
|
|
17
|
+
notificationID: batchId,
|
|
18
|
+
priority: priority > 1 ? 'important' : 'none',
|
|
19
|
+
interrupt: batchId ? priority > 0 ? 'all' : 'pending' : 'none'
|
|
20
|
+
};
|
|
21
|
+
targetDocument.ariaNotify(message, ariaNotifyOptions);
|
|
22
|
+
}, [
|
|
23
|
+
targetDocument
|
|
24
|
+
]);
|
|
25
|
+
return announce;
|
|
26
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["useAriaNotifyAnnounce.ts"],"sourcesContent":["import { useFluent_unstable as useFluent } from '@fluentui/react-shared-contexts';\nimport type { AnnounceOptions } from '@fluentui/react-shared-contexts';\nimport * as React from 'react';\n\nimport type { AriaLiveAnnounceFn } from './AriaLiveAnnouncer.types';\n\ntype AriaNotifyOptions = {\n notificationID?: string;\n priority?: 'none' | 'important';\n interrupt?: 'all' | 'pending' | 'none';\n};\n\ntype DocumentWithAriaNotify = Document & {\n ariaNotify: (message: string, options: AriaNotifyOptions) => void;\n};\n\n/* INTERNAL: implementation of the announcer using the ariaNotify API */\nexport const useAriaNotifyAnnounce_unstable = (): AriaLiveAnnounceFn => {\n const { targetDocument } = useFluent();\n\n const announce: AriaLiveAnnounceFn = React.useCallback(\n (message: string, options: AnnounceOptions = {}) => {\n if (!targetDocument) {\n return;\n }\n\n const { alert = false, polite, batchId } = options;\n\n // default priority to 0 if polite, 2 if alert, and 1 by default\n // used to set both ariaNotify's priority and interrupt\n const defaultPriority = polite ? 0 : alert ? 2 : 1;\n const priority = options.priority ?? defaultPriority;\n\n // map fluent announce options to ariaNotify options\n const ariaNotifyOptions: AriaNotifyOptions = {\n notificationID: batchId,\n priority: priority > 1 ? 'important' : 'none',\n interrupt: batchId ? (priority > 0 ? 'all' : 'pending') : 'none',\n };\n\n (targetDocument as DocumentWithAriaNotify).ariaNotify(message, ariaNotifyOptions);\n },\n [targetDocument],\n );\n\n return announce;\n};\n"],"names":["useFluent_unstable","useFluent","React","useAriaNotifyAnnounce_unstable","targetDocument","announce","useCallback","message","options","alert","polite","batchId","defaultPriority","priority","ariaNotifyOptions","notificationID","interrupt","ariaNotify"],"mappings":"AAAA,SAASA,sBAAsBC,SAAS,QAAQ,kCAAkC;AAElF,YAAYC,WAAW,QAAQ;AAc/B,sEAAsE,GACtE,OAAO,MAAMC,iCAAiC;IAC5C,MAAM,EAAEC,cAAc,EAAE,GAAGH;IAE3B,MAAMI,WAA+BH,MAAMI,WAAW,CACpD,CAACC,SAAiBC,UAA2B,CAAC,CAAC;QAC7C,IAAI,CAACJ,gBAAgB;YACnB;QACF;QAEA,MAAM,EAAEK,QAAQ,KAAK,EAAEC,MAAM,EAAEC,OAAO,EAAE,GAAGH;QAE3C,gEAAgE;QAChE,uDAAuD;QACvD,MAAMI,kBAAkBF,SAAS,IAAID,QAAQ,IAAI;YAChCD;QAAjB,MAAMK,WAAWL,CAAAA,oBAAAA,QAAQK,QAAQ,cAAhBL,+BAAAA,oBAAoBI;QAErC,oDAAoD;QACpD,MAAME,oBAAuC;YAC3CC,gBAAgBJ;YAChBE,UAAUA,WAAW,IAAI,cAAc;YACvCG,WAAWL,UAAWE,WAAW,IAAI,QAAQ,YAAa;QAC5D;QAECT,eAA0Ca,UAAU,CAACV,SAASO;IACjE,GACA;QAACV;KAAe;IAGlB,OAAOC;AACT,EAAE"}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { useFluent_unstable as useFluent } from '@fluentui/react-shared-contexts';
|
|
2
|
+
import { createPriorityQueue, useTimeout } from '@fluentui/react-utilities';
|
|
3
|
+
import * as React from 'react';
|
|
4
|
+
/** The duration the message needs to be in present in DOM for screen readers to register a change and announce */ const MESSAGE_DURATION = 500;
|
|
5
|
+
const VISUALLY_HIDDEN_STYLES = {
|
|
6
|
+
clip: 'rect(0px, 0px, 0px, 0px)',
|
|
7
|
+
height: '1px',
|
|
8
|
+
margin: '-1px',
|
|
9
|
+
width: '1px',
|
|
10
|
+
position: 'absolute',
|
|
11
|
+
overflow: 'hidden',
|
|
12
|
+
textWrap: 'nowrap'
|
|
13
|
+
};
|
|
14
|
+
/* INTERNAL: implementation of the announcer using a live region element */ export const useDomAnnounce_unstable = ()=>{
|
|
15
|
+
const { targetDocument } = useFluent();
|
|
16
|
+
const timeoutRef = React.useRef(undefined);
|
|
17
|
+
const [setAnnounceTimeout, clearAnnounceTimeout] = useTimeout();
|
|
18
|
+
const elementRef = React.useRef(null);
|
|
19
|
+
const order = React.useRef(0);
|
|
20
|
+
// investigate alert implementation later
|
|
21
|
+
// const [alertList, setAlertList] = React.useState<string[]>([]);
|
|
22
|
+
const batchMessages = React.useRef([]);
|
|
23
|
+
const [messageQueue] = React.useState(()=>createPriorityQueue((a, b)=>{
|
|
24
|
+
if (a.priority !== b.priority) {
|
|
25
|
+
return b.priority - a.priority;
|
|
26
|
+
}
|
|
27
|
+
return a.createdAt - b.createdAt;
|
|
28
|
+
}));
|
|
29
|
+
const queueMessage = React.useCallback(()=>{
|
|
30
|
+
if (timeoutRef.current || !elementRef.current) {
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
const runCycle = ()=>{
|
|
34
|
+
if (!elementRef.current) {
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
if (targetDocument && messageQueue.peek()) {
|
|
38
|
+
// need a wrapping element for Narrator/Edge, which currently does not pick up text-only live region changes
|
|
39
|
+
// consistently
|
|
40
|
+
// if this is fixed, we can set textContent to the string directly
|
|
41
|
+
const wrappingEl = targetDocument.createElement('span');
|
|
42
|
+
wrappingEl.innerText = messageQueue.all().filter((msg)=>msg.message.trim().length > 0).reduce((prevText, currMsg)=>prevText + currMsg.message + '. ', '');
|
|
43
|
+
elementRef.current.innerText = '';
|
|
44
|
+
elementRef.current.appendChild(wrappingEl);
|
|
45
|
+
messageQueue.clear();
|
|
46
|
+
batchMessages.current = [];
|
|
47
|
+
// begin new cycle to clear (or update) messages
|
|
48
|
+
timeoutRef.current = setAnnounceTimeout(()=>{
|
|
49
|
+
runCycle();
|
|
50
|
+
}, MESSAGE_DURATION);
|
|
51
|
+
} else {
|
|
52
|
+
elementRef.current.textContent = '';
|
|
53
|
+
clearAnnounceTimeout();
|
|
54
|
+
timeoutRef.current = undefined;
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
runCycle();
|
|
58
|
+
}, [
|
|
59
|
+
clearAnnounceTimeout,
|
|
60
|
+
messageQueue,
|
|
61
|
+
setAnnounceTimeout,
|
|
62
|
+
targetDocument
|
|
63
|
+
]);
|
|
64
|
+
const announce = React.useCallback((message, options = {})=>{
|
|
65
|
+
const { alert = false, priority = 0, batchId } = options;
|
|
66
|
+
// check if message is an alert
|
|
67
|
+
if (alert) {
|
|
68
|
+
// TODO: alert implementation
|
|
69
|
+
// setAlertList([...alertList, message]);
|
|
70
|
+
}
|
|
71
|
+
const liveMessage = {
|
|
72
|
+
message,
|
|
73
|
+
createdAt: order.current++,
|
|
74
|
+
priority,
|
|
75
|
+
batchId
|
|
76
|
+
};
|
|
77
|
+
// check if batchId exists
|
|
78
|
+
if (batchId) {
|
|
79
|
+
// update associated msg if it does
|
|
80
|
+
const batchMessage = batchMessages.current.find((msg)=>msg.batchId === batchId);
|
|
81
|
+
if (batchMessage) {
|
|
82
|
+
// replace existing message in queue
|
|
83
|
+
messageQueue.remove(batchMessage.message);
|
|
84
|
+
// update list of existing batchIds w/ most recent message
|
|
85
|
+
batchMessage.message = liveMessage;
|
|
86
|
+
} else {
|
|
87
|
+
// update list of existing batchIds, add new if doesn't already exist
|
|
88
|
+
batchMessages.current = [
|
|
89
|
+
...batchMessages.current,
|
|
90
|
+
{
|
|
91
|
+
batchId,
|
|
92
|
+
message: liveMessage
|
|
93
|
+
}
|
|
94
|
+
];
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
// add new message
|
|
98
|
+
messageQueue.enqueue(liveMessage);
|
|
99
|
+
queueMessage();
|
|
100
|
+
}, [
|
|
101
|
+
messageQueue,
|
|
102
|
+
queueMessage
|
|
103
|
+
]);
|
|
104
|
+
React.useEffect(()=>{
|
|
105
|
+
if (!targetDocument) {
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
const element = targetDocument.createElement('div');
|
|
109
|
+
element.setAttribute('aria-live', 'assertive');
|
|
110
|
+
Object.assign(element.style, VISUALLY_HIDDEN_STYLES);
|
|
111
|
+
targetDocument.body.append(element);
|
|
112
|
+
elementRef.current = element;
|
|
113
|
+
return ()=>{
|
|
114
|
+
element.remove();
|
|
115
|
+
elementRef.current = null;
|
|
116
|
+
};
|
|
117
|
+
}, [
|
|
118
|
+
targetDocument
|
|
119
|
+
]);
|
|
120
|
+
return announce;
|
|
121
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["useDomAnnounce.ts"],"sourcesContent":["import { useFluent_unstable as useFluent } from '@fluentui/react-shared-contexts';\nimport type { AnnounceOptions } from '@fluentui/react-shared-contexts';\nimport { createPriorityQueue, useTimeout } from '@fluentui/react-utilities';\nimport * as React from 'react';\n\nimport type { AriaLiveAnnounceFn, AriaLiveMessage } from './AriaLiveAnnouncer.types';\n\n/** The duration the message needs to be in present in DOM for screen readers to register a change and announce */\nconst MESSAGE_DURATION = 500;\n\nconst VISUALLY_HIDDEN_STYLES = {\n clip: 'rect(0px, 0px, 0px, 0px)',\n height: '1px',\n margin: '-1px',\n width: '1px',\n position: 'absolute',\n overflow: 'hidden',\n textWrap: 'nowrap',\n};\n\n/* INTERNAL: implementation of the announcer using a live region element */\nexport const useDomAnnounce_unstable = (): AriaLiveAnnounceFn => {\n const { targetDocument } = useFluent();\n\n const timeoutRef = React.useRef<number | undefined>(undefined);\n const [setAnnounceTimeout, clearAnnounceTimeout] = useTimeout();\n\n const elementRef = React.useRef<HTMLDivElement | null>(null);\n\n const order = React.useRef(0);\n\n // investigate alert implementation later\n // const [alertList, setAlertList] = React.useState<string[]>([]);\n\n const batchMessages = React.useRef<{ batchId: string; message: AriaLiveMessage }[]>([]);\n\n const [messageQueue] = React.useState(() =>\n createPriorityQueue<AriaLiveMessage>((a, b) => {\n if (a.priority !== b.priority) {\n return b.priority - a.priority;\n }\n\n return a.createdAt - b.createdAt;\n }),\n );\n\n const queueMessage = React.useCallback(() => {\n if (timeoutRef.current || !elementRef.current) {\n return;\n }\n\n const runCycle = () => {\n if (!elementRef.current) {\n return;\n }\n\n if (targetDocument && messageQueue.peek()) {\n // need a wrapping element for Narrator/Edge, which currently does not pick up text-only live region changes\n // consistently\n // if this is fixed, we can set textContent to the string directly\n\n const wrappingEl = targetDocument.createElement('span');\n\n wrappingEl.innerText = messageQueue\n .all()\n .filter(msg => msg.message.trim().length > 0)\n .reduce((prevText, currMsg) => prevText + currMsg.message + '. ', '');\n\n elementRef.current.innerText = '';\n elementRef.current.appendChild(wrappingEl);\n\n messageQueue.clear();\n batchMessages.current = [];\n\n // begin new cycle to clear (or update) messages\n timeoutRef.current = setAnnounceTimeout(() => {\n runCycle();\n }, MESSAGE_DURATION);\n } else {\n elementRef.current.textContent = '';\n clearAnnounceTimeout();\n\n timeoutRef.current = undefined;\n }\n };\n\n runCycle();\n }, [clearAnnounceTimeout, messageQueue, setAnnounceTimeout, targetDocument]);\n\n const announce: AriaLiveAnnounceFn = React.useCallback(\n (message: string, options: AnnounceOptions = {}) => {\n const { alert = false, priority = 0, batchId } = options;\n\n // check if message is an alert\n if (alert) {\n // TODO: alert implementation\n // setAlertList([...alertList, message]);\n }\n\n const liveMessage: AriaLiveMessage = {\n message,\n createdAt: order.current++,\n priority,\n batchId,\n };\n\n // check if batchId exists\n if (batchId) {\n // update associated msg if it does\n const batchMessage = batchMessages.current.find(msg => msg.batchId === batchId);\n\n if (batchMessage) {\n // replace existing message in queue\n messageQueue.remove(batchMessage.message);\n\n // update list of existing batchIds w/ most recent message\n batchMessage.message = liveMessage;\n } else {\n // update list of existing batchIds, add new if doesn't already exist\n batchMessages.current = [...batchMessages.current, { batchId, message: liveMessage }];\n }\n }\n\n // add new message\n messageQueue.enqueue(liveMessage);\n queueMessage();\n },\n [messageQueue, queueMessage],\n );\n\n React.useEffect(() => {\n if (!targetDocument) {\n return;\n }\n\n const element = targetDocument.createElement('div');\n element.setAttribute('aria-live', 'assertive');\n\n Object.assign(element.style, VISUALLY_HIDDEN_STYLES);\n targetDocument.body.append(element);\n\n elementRef.current = element;\n\n return () => {\n element.remove();\n elementRef.current = null;\n };\n }, [targetDocument]);\n\n return announce;\n};\n"],"names":["useFluent_unstable","useFluent","createPriorityQueue","useTimeout","React","MESSAGE_DURATION","VISUALLY_HIDDEN_STYLES","clip","height","margin","width","position","overflow","textWrap","useDomAnnounce_unstable","targetDocument","timeoutRef","useRef","undefined","setAnnounceTimeout","clearAnnounceTimeout","elementRef","order","batchMessages","messageQueue","useState","a","b","priority","createdAt","queueMessage","useCallback","current","runCycle","peek","wrappingEl","createElement","innerText","all","filter","msg","message","trim","length","reduce","prevText","currMsg","appendChild","clear","textContent","announce","options","alert","batchId","liveMessage","batchMessage","find","remove","enqueue","useEffect","element","setAttribute","Object","assign","style","body","append"],"mappings":"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"}
|
|
@@ -9,124 +9,22 @@ Object.defineProperty(exports, "useAriaLiveAnnouncer_unstable", {
|
|
|
9
9
|
}
|
|
10
10
|
});
|
|
11
11
|
const _interop_require_wildcard = require("@swc/helpers/_/_interop_require_wildcard");
|
|
12
|
-
const _reactsharedcontexts = require("@fluentui/react-shared-contexts");
|
|
13
|
-
const _reactutilities = require("@fluentui/react-utilities");
|
|
14
12
|
const _react = /*#__PURE__*/ _interop_require_wildcard._(require("react"));
|
|
15
|
-
|
|
16
|
-
const
|
|
17
|
-
|
|
18
|
-
height: '1px',
|
|
19
|
-
margin: '-1px',
|
|
20
|
-
width: '1px',
|
|
21
|
-
position: 'absolute',
|
|
22
|
-
overflow: 'hidden',
|
|
23
|
-
textWrap: 'nowrap'
|
|
24
|
-
};
|
|
13
|
+
const _reactsharedcontexts = require("@fluentui/react-shared-contexts");
|
|
14
|
+
const _useDomAnnounce = require("./useDomAnnounce");
|
|
15
|
+
const _useAriaNotifyAnnounce = require("./useAriaNotifyAnnounce");
|
|
25
16
|
const useAriaLiveAnnouncer_unstable = (props)=>{
|
|
26
17
|
const { targetDocument } = (0, _reactsharedcontexts.useFluent_unstable)();
|
|
27
|
-
const
|
|
28
|
-
const
|
|
29
|
-
const
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
const batchMessages = _react.useRef([]);
|
|
34
|
-
const [messageQueue] = _react.useState(()=>(0, _reactutilities.createPriorityQueue)((a, b)=>{
|
|
35
|
-
if (a.priority !== b.priority) {
|
|
36
|
-
return b.priority - a.priority;
|
|
37
|
-
}
|
|
38
|
-
return a.createdAt - b.createdAt;
|
|
39
|
-
}));
|
|
40
|
-
const queueMessage = _react.useCallback(()=>{
|
|
41
|
-
if (timeoutRef.current || !elementRef.current) {
|
|
42
|
-
return;
|
|
43
|
-
}
|
|
44
|
-
const runCycle = ()=>{
|
|
45
|
-
if (!elementRef.current) {
|
|
46
|
-
return;
|
|
47
|
-
}
|
|
48
|
-
if (targetDocument && messageQueue.peek()) {
|
|
49
|
-
// need a wrapping element for Narrator/Edge, which currently does not pick up text-only live region changes
|
|
50
|
-
// consistently
|
|
51
|
-
// if this is fixed, we can set textContent to the string directly
|
|
52
|
-
const wrappingEl = targetDocument.createElement('span');
|
|
53
|
-
wrappingEl.innerText = messageQueue.all().filter((msg)=>msg.message.trim().length > 0).reduce((prevText, currMsg)=>prevText + currMsg.message + '. ', '');
|
|
54
|
-
elementRef.current.innerText = '';
|
|
55
|
-
elementRef.current.appendChild(wrappingEl);
|
|
56
|
-
messageQueue.clear();
|
|
57
|
-
batchMessages.current = [];
|
|
58
|
-
// begin new cycle to clear (or update) messages
|
|
59
|
-
timeoutRef.current = setAnnounceTimeout(()=>{
|
|
60
|
-
runCycle();
|
|
61
|
-
}, MESSAGE_DURATION);
|
|
62
|
-
} else {
|
|
63
|
-
elementRef.current.textContent = '';
|
|
64
|
-
clearAnnounceTimeout();
|
|
65
|
-
timeoutRef.current = undefined;
|
|
66
|
-
}
|
|
67
|
-
};
|
|
68
|
-
runCycle();
|
|
69
|
-
}, [
|
|
70
|
-
clearAnnounceTimeout,
|
|
71
|
-
messageQueue,
|
|
72
|
-
setAnnounceTimeout,
|
|
73
|
-
targetDocument
|
|
74
|
-
]);
|
|
75
|
-
const announce = _react.useMemo(()=>(message, options = {})=>{
|
|
76
|
-
const { alert = false, priority = 0, batchId } = options;
|
|
77
|
-
// check if message is an alert
|
|
78
|
-
if (alert) {
|
|
79
|
-
// TODO: alert implementation
|
|
80
|
-
// setAlertList([...alertList, message]);
|
|
81
|
-
}
|
|
82
|
-
const liveMessage = {
|
|
83
|
-
message,
|
|
84
|
-
createdAt: order.current++,
|
|
85
|
-
priority,
|
|
86
|
-
batchId
|
|
87
|
-
};
|
|
88
|
-
// check if batchId exists
|
|
89
|
-
if (batchId) {
|
|
90
|
-
// update associated msg if it does
|
|
91
|
-
const batchMessage = batchMessages.current.find((msg)=>msg.batchId === batchId);
|
|
92
|
-
if (batchMessage) {
|
|
93
|
-
// replace existing message in queue
|
|
94
|
-
messageQueue.remove(batchMessage.message);
|
|
95
|
-
// update list of existing batchIds w/ most recent message
|
|
96
|
-
batchMessage.message = liveMessage;
|
|
97
|
-
} else {
|
|
98
|
-
// update list of existing batchIds, add new if doesn't already exist
|
|
99
|
-
batchMessages.current = [
|
|
100
|
-
...batchMessages.current,
|
|
101
|
-
{
|
|
102
|
-
batchId,
|
|
103
|
-
message: liveMessage
|
|
104
|
-
}
|
|
105
|
-
];
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
// add new message
|
|
109
|
-
messageQueue.enqueue(liveMessage);
|
|
110
|
-
queueMessage();
|
|
111
|
-
}, [
|
|
112
|
-
messageQueue,
|
|
113
|
-
queueMessage
|
|
114
|
-
]);
|
|
115
|
-
_react.useEffect(()=>{
|
|
116
|
-
if (!targetDocument) {
|
|
117
|
-
return;
|
|
118
|
-
}
|
|
119
|
-
const element = targetDocument.createElement('div');
|
|
120
|
-
element.setAttribute('aria-live', 'assertive');
|
|
121
|
-
Object.assign(element.style, VISUALLY_HIDDEN_STYLES);
|
|
122
|
-
targetDocument.body.append(element);
|
|
123
|
-
elementRef.current = element;
|
|
124
|
-
return ()=>{
|
|
125
|
-
element.remove();
|
|
126
|
-
elementRef.current = null;
|
|
127
|
-
};
|
|
18
|
+
const domAnnounce = (0, _useDomAnnounce.useDomAnnounce_unstable)();
|
|
19
|
+
const ariaNotifyAnnounce = (0, _useAriaNotifyAnnounce.useAriaNotifyAnnounce_unstable)();
|
|
20
|
+
const announce = _react.useMemo(()=>{
|
|
21
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
22
|
+
const supportsAriaNotify = typeof (targetDocument === null || targetDocument === void 0 ? void 0 : targetDocument.ariaNotify) === 'function';
|
|
23
|
+
return supportsAriaNotify ? ariaNotifyAnnounce : domAnnounce;
|
|
128
24
|
}, [
|
|
129
|
-
targetDocument
|
|
25
|
+
targetDocument,
|
|
26
|
+
ariaNotifyAnnounce,
|
|
27
|
+
domAnnounce
|
|
130
28
|
]);
|
|
131
29
|
return {
|
|
132
30
|
announce,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["useAriaLiveAnnouncer.js"],"sourcesContent":["import
|
|
1
|
+
{"version":3,"sources":["useAriaLiveAnnouncer.js"],"sourcesContent":["import * as React from 'react';\nimport { useFluent_unstable as useFluent } from '@fluentui/react-shared-contexts';\nimport { useDomAnnounce_unstable } from './useDomAnnounce';\nimport { useAriaNotifyAnnounce_unstable } from './useAriaNotifyAnnounce';\nexport const useAriaLiveAnnouncer_unstable = (props)=>{\n const { targetDocument } = useFluent();\n const domAnnounce = useDomAnnounce_unstable();\n const ariaNotifyAnnounce = useAriaNotifyAnnounce_unstable();\n const announce = React.useMemo(()=>{\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const supportsAriaNotify = typeof (targetDocument === null || targetDocument === void 0 ? void 0 : targetDocument.ariaNotify) === 'function';\n return supportsAriaNotify ? ariaNotifyAnnounce : domAnnounce;\n }, [\n targetDocument,\n ariaNotifyAnnounce,\n domAnnounce\n ]);\n return {\n announce,\n children: props.children\n };\n};\n"],"names":["useAriaLiveAnnouncer_unstable","props","targetDocument","useFluent","domAnnounce","useDomAnnounce_unstable","ariaNotifyAnnounce","useAriaNotifyAnnounce_unstable","announce","React","useMemo","supportsAriaNotify","ariaNotify","children"],"mappings":";;;;+BAIaA;;;eAAAA;;;;iEAJU;qCACyB;gCACR;uCACO;AACxC,MAAMA,gCAAgC,CAACC;IAC1C,MAAM,EAAEC,cAAc,EAAE,GAAGC,IAAAA,uCAAS;IACpC,MAAMC,cAAcC,IAAAA,uCAAuB;IAC3C,MAAMC,qBAAqBC,IAAAA,qDAA8B;IACzD,MAAMC,WAAWC,OAAMC,OAAO,CAAC;QAC3B,8DAA8D;QAC9D,MAAMC,qBAAqB,OAAQT,CAAAA,mBAAmB,QAAQA,mBAAmB,KAAK,IAAI,KAAK,IAAIA,eAAeU,UAAU,AAAD,MAAO;QAClI,OAAOD,qBAAqBL,qBAAqBF;IACrD,GAAG;QACCF;QACAI;QACAF;KACH;IACD,OAAO;QACHI;QACAK,UAAUZ,MAAMY,QAAQ;IAC5B;AACJ"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", {
|
|
3
|
+
value: true
|
|
4
|
+
});
|
|
5
|
+
Object.defineProperty(exports, "useAriaNotifyAnnounce_unstable", {
|
|
6
|
+
enumerable: true,
|
|
7
|
+
get: function() {
|
|
8
|
+
return useAriaNotifyAnnounce_unstable;
|
|
9
|
+
}
|
|
10
|
+
});
|
|
11
|
+
const _interop_require_wildcard = require("@swc/helpers/_/_interop_require_wildcard");
|
|
12
|
+
const _reactsharedcontexts = require("@fluentui/react-shared-contexts");
|
|
13
|
+
const _react = /*#__PURE__*/ _interop_require_wildcard._(require("react"));
|
|
14
|
+
const useAriaNotifyAnnounce_unstable = ()=>{
|
|
15
|
+
const { targetDocument } = (0, _reactsharedcontexts.useFluent_unstable)();
|
|
16
|
+
const announce = _react.useCallback((message, options = {})=>{
|
|
17
|
+
if (!targetDocument) {
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
const { alert = false, polite, batchId } = options;
|
|
21
|
+
// default priority to 0 if polite, 2 if alert, and 1 by default
|
|
22
|
+
// used to set both ariaNotify's priority and interrupt
|
|
23
|
+
const defaultPriority = polite ? 0 : alert ? 2 : 1;
|
|
24
|
+
var _options_priority;
|
|
25
|
+
const priority = (_options_priority = options.priority) !== null && _options_priority !== void 0 ? _options_priority : defaultPriority;
|
|
26
|
+
// map fluent announce options to ariaNotify options
|
|
27
|
+
const ariaNotifyOptions = {
|
|
28
|
+
notificationID: batchId,
|
|
29
|
+
priority: priority > 1 ? 'important' : 'none',
|
|
30
|
+
interrupt: batchId ? priority > 0 ? 'all' : 'pending' : 'none'
|
|
31
|
+
};
|
|
32
|
+
targetDocument.ariaNotify(message, ariaNotifyOptions);
|
|
33
|
+
}, [
|
|
34
|
+
targetDocument
|
|
35
|
+
]);
|
|
36
|
+
return announce;
|
|
37
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["useAriaNotifyAnnounce.js"],"sourcesContent":["import { useFluent_unstable as useFluent } from '@fluentui/react-shared-contexts';\nimport * as React from 'react';\n/* INTERNAL: implementation of the announcer using the ariaNotify API */ export const useAriaNotifyAnnounce_unstable = ()=>{\n const { targetDocument } = useFluent();\n const announce = React.useCallback((message, options = {})=>{\n if (!targetDocument) {\n return;\n }\n const { alert = false, polite, batchId } = options;\n // default priority to 0 if polite, 2 if alert, and 1 by default\n // used to set both ariaNotify's priority and interrupt\n const defaultPriority = polite ? 0 : alert ? 2 : 1;\n var _options_priority;\n const priority = (_options_priority = options.priority) !== null && _options_priority !== void 0 ? _options_priority : defaultPriority;\n // map fluent announce options to ariaNotify options\n const ariaNotifyOptions = {\n notificationID: batchId,\n priority: priority > 1 ? 'important' : 'none',\n interrupt: batchId ? priority > 0 ? 'all' : 'pending' : 'none'\n };\n targetDocument.ariaNotify(message, ariaNotifyOptions);\n }, [\n targetDocument\n ]);\n return announce;\n};\n"],"names":["useAriaNotifyAnnounce_unstable","targetDocument","useFluent","announce","React","useCallback","message","options","alert","polite","batchId","defaultPriority","_options_priority","priority","ariaNotifyOptions","notificationID","interrupt","ariaNotify"],"mappings":";;;;+BAEsFA;;;eAAAA;;;;qCAFtC;iEACzB;AACyD,MAAMA,iCAAiC;IACnH,MAAM,EAAEC,cAAc,EAAE,GAAGC,IAAAA,uCAAS;IACpC,MAAMC,WAAWC,OAAMC,WAAW,CAAC,CAACC,SAASC,UAAU,CAAC,CAAC;QACrD,IAAI,CAACN,gBAAgB;YACjB;QACJ;QACA,MAAM,EAAEO,QAAQ,KAAK,EAAEC,MAAM,EAAEC,OAAO,EAAE,GAAGH;QAC3C,gEAAgE;QAChE,uDAAuD;QACvD,MAAMI,kBAAkBF,SAAS,IAAID,QAAQ,IAAI;QACjD,IAAII;QACJ,MAAMC,WAAW,AAACD,CAAAA,oBAAoBL,QAAQM,QAAQ,AAAD,MAAO,QAAQD,sBAAsB,KAAK,IAAIA,oBAAoBD;QACvH,oDAAoD;QACpD,MAAMG,oBAAoB;YACtBC,gBAAgBL;YAChBG,UAAUA,WAAW,IAAI,cAAc;YACvCG,WAAWN,UAAUG,WAAW,IAAI,QAAQ,YAAY;QAC5D;QACAZ,eAAegB,UAAU,CAACX,SAASQ;IACvC,GAAG;QACCb;KACH;IACD,OAAOE;AACX"}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", {
|
|
3
|
+
value: true
|
|
4
|
+
});
|
|
5
|
+
Object.defineProperty(exports, "useDomAnnounce_unstable", {
|
|
6
|
+
enumerable: true,
|
|
7
|
+
get: function() {
|
|
8
|
+
return useDomAnnounce_unstable;
|
|
9
|
+
}
|
|
10
|
+
});
|
|
11
|
+
const _interop_require_wildcard = require("@swc/helpers/_/_interop_require_wildcard");
|
|
12
|
+
const _reactsharedcontexts = require("@fluentui/react-shared-contexts");
|
|
13
|
+
const _reactutilities = require("@fluentui/react-utilities");
|
|
14
|
+
const _react = /*#__PURE__*/ _interop_require_wildcard._(require("react"));
|
|
15
|
+
/** The duration the message needs to be in present in DOM for screen readers to register a change and announce */ const MESSAGE_DURATION = 500;
|
|
16
|
+
const VISUALLY_HIDDEN_STYLES = {
|
|
17
|
+
clip: 'rect(0px, 0px, 0px, 0px)',
|
|
18
|
+
height: '1px',
|
|
19
|
+
margin: '-1px',
|
|
20
|
+
width: '1px',
|
|
21
|
+
position: 'absolute',
|
|
22
|
+
overflow: 'hidden',
|
|
23
|
+
textWrap: 'nowrap'
|
|
24
|
+
};
|
|
25
|
+
const useDomAnnounce_unstable = ()=>{
|
|
26
|
+
const { targetDocument } = (0, _reactsharedcontexts.useFluent_unstable)();
|
|
27
|
+
const timeoutRef = _react.useRef(undefined);
|
|
28
|
+
const [setAnnounceTimeout, clearAnnounceTimeout] = (0, _reactutilities.useTimeout)();
|
|
29
|
+
const elementRef = _react.useRef(null);
|
|
30
|
+
const order = _react.useRef(0);
|
|
31
|
+
// investigate alert implementation later
|
|
32
|
+
// const [alertList, setAlertList] = React.useState<string[]>([]);
|
|
33
|
+
const batchMessages = _react.useRef([]);
|
|
34
|
+
const [messageQueue] = _react.useState(()=>(0, _reactutilities.createPriorityQueue)((a, b)=>{
|
|
35
|
+
if (a.priority !== b.priority) {
|
|
36
|
+
return b.priority - a.priority;
|
|
37
|
+
}
|
|
38
|
+
return a.createdAt - b.createdAt;
|
|
39
|
+
}));
|
|
40
|
+
const queueMessage = _react.useCallback(()=>{
|
|
41
|
+
if (timeoutRef.current || !elementRef.current) {
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
const runCycle = ()=>{
|
|
45
|
+
if (!elementRef.current) {
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
if (targetDocument && messageQueue.peek()) {
|
|
49
|
+
// need a wrapping element for Narrator/Edge, which currently does not pick up text-only live region changes
|
|
50
|
+
// consistently
|
|
51
|
+
// if this is fixed, we can set textContent to the string directly
|
|
52
|
+
const wrappingEl = targetDocument.createElement('span');
|
|
53
|
+
wrappingEl.innerText = messageQueue.all().filter((msg)=>msg.message.trim().length > 0).reduce((prevText, currMsg)=>prevText + currMsg.message + '. ', '');
|
|
54
|
+
elementRef.current.innerText = '';
|
|
55
|
+
elementRef.current.appendChild(wrappingEl);
|
|
56
|
+
messageQueue.clear();
|
|
57
|
+
batchMessages.current = [];
|
|
58
|
+
// begin new cycle to clear (or update) messages
|
|
59
|
+
timeoutRef.current = setAnnounceTimeout(()=>{
|
|
60
|
+
runCycle();
|
|
61
|
+
}, MESSAGE_DURATION);
|
|
62
|
+
} else {
|
|
63
|
+
elementRef.current.textContent = '';
|
|
64
|
+
clearAnnounceTimeout();
|
|
65
|
+
timeoutRef.current = undefined;
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
runCycle();
|
|
69
|
+
}, [
|
|
70
|
+
clearAnnounceTimeout,
|
|
71
|
+
messageQueue,
|
|
72
|
+
setAnnounceTimeout,
|
|
73
|
+
targetDocument
|
|
74
|
+
]);
|
|
75
|
+
const announce = _react.useCallback((message, options = {})=>{
|
|
76
|
+
const { alert = false, priority = 0, batchId } = options;
|
|
77
|
+
// check if message is an alert
|
|
78
|
+
if (alert) {
|
|
79
|
+
// TODO: alert implementation
|
|
80
|
+
// setAlertList([...alertList, message]);
|
|
81
|
+
}
|
|
82
|
+
const liveMessage = {
|
|
83
|
+
message,
|
|
84
|
+
createdAt: order.current++,
|
|
85
|
+
priority,
|
|
86
|
+
batchId
|
|
87
|
+
};
|
|
88
|
+
// check if batchId exists
|
|
89
|
+
if (batchId) {
|
|
90
|
+
// update associated msg if it does
|
|
91
|
+
const batchMessage = batchMessages.current.find((msg)=>msg.batchId === batchId);
|
|
92
|
+
if (batchMessage) {
|
|
93
|
+
// replace existing message in queue
|
|
94
|
+
messageQueue.remove(batchMessage.message);
|
|
95
|
+
// update list of existing batchIds w/ most recent message
|
|
96
|
+
batchMessage.message = liveMessage;
|
|
97
|
+
} else {
|
|
98
|
+
// update list of existing batchIds, add new if doesn't already exist
|
|
99
|
+
batchMessages.current = [
|
|
100
|
+
...batchMessages.current,
|
|
101
|
+
{
|
|
102
|
+
batchId,
|
|
103
|
+
message: liveMessage
|
|
104
|
+
}
|
|
105
|
+
];
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
// add new message
|
|
109
|
+
messageQueue.enqueue(liveMessage);
|
|
110
|
+
queueMessage();
|
|
111
|
+
}, [
|
|
112
|
+
messageQueue,
|
|
113
|
+
queueMessage
|
|
114
|
+
]);
|
|
115
|
+
_react.useEffect(()=>{
|
|
116
|
+
if (!targetDocument) {
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
const element = targetDocument.createElement('div');
|
|
120
|
+
element.setAttribute('aria-live', 'assertive');
|
|
121
|
+
Object.assign(element.style, VISUALLY_HIDDEN_STYLES);
|
|
122
|
+
targetDocument.body.append(element);
|
|
123
|
+
elementRef.current = element;
|
|
124
|
+
return ()=>{
|
|
125
|
+
element.remove();
|
|
126
|
+
elementRef.current = null;
|
|
127
|
+
};
|
|
128
|
+
}, [
|
|
129
|
+
targetDocument
|
|
130
|
+
]);
|
|
131
|
+
return announce;
|
|
132
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["useDomAnnounce.js"],"sourcesContent":["import { useFluent_unstable as useFluent } from '@fluentui/react-shared-contexts';\nimport { createPriorityQueue, useTimeout } from '@fluentui/react-utilities';\nimport * as React from 'react';\n/** The duration the message needs to be in present in DOM for screen readers to register a change and announce */ const MESSAGE_DURATION = 500;\nconst VISUALLY_HIDDEN_STYLES = {\n clip: 'rect(0px, 0px, 0px, 0px)',\n height: '1px',\n margin: '-1px',\n width: '1px',\n position: 'absolute',\n overflow: 'hidden',\n textWrap: 'nowrap'\n};\n/* INTERNAL: implementation of the announcer using a live region element */ export const useDomAnnounce_unstable = ()=>{\n const { targetDocument } = useFluent();\n const timeoutRef = React.useRef(undefined);\n const [setAnnounceTimeout, clearAnnounceTimeout] = useTimeout();\n const elementRef = React.useRef(null);\n const order = React.useRef(0);\n // investigate alert implementation later\n // const [alertList, setAlertList] = React.useState<string[]>([]);\n const batchMessages = React.useRef([]);\n const [messageQueue] = React.useState(()=>createPriorityQueue((a, b)=>{\n if (a.priority !== b.priority) {\n return b.priority - a.priority;\n }\n return a.createdAt - b.createdAt;\n }));\n const queueMessage = React.useCallback(()=>{\n if (timeoutRef.current || !elementRef.current) {\n return;\n }\n const runCycle = ()=>{\n if (!elementRef.current) {\n return;\n }\n if (targetDocument && messageQueue.peek()) {\n // need a wrapping element for Narrator/Edge, which currently does not pick up text-only live region changes\n // consistently\n // if this is fixed, we can set textContent to the string directly\n const wrappingEl = targetDocument.createElement('span');\n wrappingEl.innerText = messageQueue.all().filter((msg)=>msg.message.trim().length > 0).reduce((prevText, currMsg)=>prevText + currMsg.message + '. ', '');\n elementRef.current.innerText = '';\n elementRef.current.appendChild(wrappingEl);\n messageQueue.clear();\n batchMessages.current = [];\n // begin new cycle to clear (or update) messages\n timeoutRef.current = setAnnounceTimeout(()=>{\n runCycle();\n }, MESSAGE_DURATION);\n } else {\n elementRef.current.textContent = '';\n clearAnnounceTimeout();\n timeoutRef.current = undefined;\n }\n };\n runCycle();\n }, [\n clearAnnounceTimeout,\n messageQueue,\n setAnnounceTimeout,\n targetDocument\n ]);\n const announce = React.useCallback((message, options = {})=>{\n const { alert = false, priority = 0, batchId } = options;\n // check if message is an alert\n if (alert) {\n // TODO: alert implementation\n // setAlertList([...alertList, message]);\n }\n const liveMessage = {\n message,\n createdAt: order.current++,\n priority,\n batchId\n };\n // check if batchId exists\n if (batchId) {\n // update associated msg if it does\n const batchMessage = batchMessages.current.find((msg)=>msg.batchId === batchId);\n if (batchMessage) {\n // replace existing message in queue\n messageQueue.remove(batchMessage.message);\n // update list of existing batchIds w/ most recent message\n batchMessage.message = liveMessage;\n } else {\n // update list of existing batchIds, add new if doesn't already exist\n batchMessages.current = [\n ...batchMessages.current,\n {\n batchId,\n message: liveMessage\n }\n ];\n }\n }\n // add new message\n messageQueue.enqueue(liveMessage);\n queueMessage();\n }, [\n messageQueue,\n queueMessage\n ]);\n React.useEffect(()=>{\n if (!targetDocument) {\n return;\n }\n const element = targetDocument.createElement('div');\n element.setAttribute('aria-live', 'assertive');\n Object.assign(element.style, VISUALLY_HIDDEN_STYLES);\n targetDocument.body.append(element);\n elementRef.current = element;\n return ()=>{\n element.remove();\n elementRef.current = null;\n };\n }, [\n targetDocument\n ]);\n return announce;\n};\n"],"names":["useDomAnnounce_unstable","MESSAGE_DURATION","VISUALLY_HIDDEN_STYLES","clip","height","margin","width","position","overflow","textWrap","targetDocument","useFluent","timeoutRef","React","useRef","undefined","setAnnounceTimeout","clearAnnounceTimeout","useTimeout","elementRef","order","batchMessages","messageQueue","useState","createPriorityQueue","a","b","priority","createdAt","queueMessage","useCallback","current","runCycle","peek","wrappingEl","createElement","innerText","all","filter","msg","message","trim","length","reduce","prevText","currMsg","appendChild","clear","textContent","announce","options","alert","batchId","liveMessage","batchMessage","find","remove","enqueue","useEffect","element","setAttribute","Object","assign","style","body","append"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fluentui/react-aria",
|
|
3
|
-
"version": "9.11.
|
|
3
|
+
"version": "9.11.2",
|
|
4
4
|
"description": "React helper to ensure ARIA",
|
|
5
5
|
"main": "lib-commonjs/index.js",
|
|
6
6
|
"module": "lib/index.js",
|
|
@@ -34,10 +34,10 @@
|
|
|
34
34
|
},
|
|
35
35
|
"dependencies": {
|
|
36
36
|
"@fluentui/keyboard-keys": "^9.0.7",
|
|
37
|
-
"@fluentui/react-shared-contexts": "^9.
|
|
38
|
-
"@fluentui/react-jsx-runtime": "^9.0.
|
|
39
|
-
"@fluentui/react-tabster": "^9.21.
|
|
40
|
-
"@fluentui/react-utilities": "^9.18.
|
|
37
|
+
"@fluentui/react-shared-contexts": "^9.18.0",
|
|
38
|
+
"@fluentui/react-jsx-runtime": "^9.0.37",
|
|
39
|
+
"@fluentui/react-tabster": "^9.21.2",
|
|
40
|
+
"@fluentui/react-utilities": "^9.18.8",
|
|
41
41
|
"@swc/helpers": "^0.5.1"
|
|
42
42
|
},
|
|
43
43
|
"peerDependencies": {
|