MCPcopy
hub / github.com/adobe/react-spectrum / useDialog

Function useDialog

packages/react-aria/src/dialog/useDialog.ts:48–132  ·  view source on GitHub ↗
(
  props: AriaDialogProps,
  ref: RefObject<FocusableElement | null>
)

Source from the content-addressed store, hash-verified

46 * A dialog is an overlay shown above other content in an application.
47 */
48export function useDialog(
49 props: AriaDialogProps,
50 ref: RefObject<FocusableElement | null>
51): DialogAria {
52 let {role = 'dialog'} = props;
53 let titleId: string | undefined = useSlotId();
54 titleId = props['aria-label'] ? undefined : titleId;
55
56 let isRefocusing = useRef(false);
57
58 // Focus the dialog itself on mount, unless a child element is already focused.
59 useEffect(() => {
60 if (ref.current && !isFocusWithin(ref.current)) {
61 focusSafely(ref.current);
62
63 // Safari on iOS does not move the VoiceOver cursor to the dialog
64 // or announce that it has opened until it has rendered. A workaround
65 // is to wait for half a second, then blur and re-focus the dialog.
66 let timeout = setTimeout(() => {
67 // Check that the dialog is still focused, or focused was lost to the body.
68 if (getActiveElement() === ref.current || getActiveElement() === document.body) {
69 isRefocusing.current = true;
70 if (ref.current) {
71 ref.current.blur();
72 focusSafely(ref.current);
73 }
74 isRefocusing.current = false;
75 }
76 }, 500);
77
78 return () => {
79 clearTimeout(timeout);
80 };
81 }
82 }, [ref]);
83
84 useOverlayFocusContain();
85
86 // Warn in dev mode if the dialog has no accessible title.
87 // This catches a common mistake where useDialog and useOverlayTriggerState
88 // are used in the same component, causing the title element to not be
89 // in the DOM when useSlotId queries for it.
90 // Check the DOM element directly since aria-labelledby may be added by
91 // wrapper components (e.g. RAC Dialog uses trigger ID as a fallback).
92 let hasWarned = useRef(false);
93 useEffect(() => {
94 if (process.env.NODE_ENV !== 'production' && !hasWarned.current && ref.current) {
95 let el = ref.current;
96 let hasAriaLabel = el.hasAttribute('aria-label');
97 let hasAriaLabelledby = el.hasAttribute('aria-labelledby');
98 if (!hasAriaLabel && !hasAriaLabelledby) {
99 console.warn(
100 'A dialog must have a title for accessibility. ' +
101 'Either provide an aria-label or aria-labelledby prop, or render a heading element inside the dialog.'
102 );
103 hasWarned.current = true;
104 }
105 }

Callers 5

ExampleFunction · 0.90
Dialog.tsxFile · 0.90
ComboBoxTrayFunction · 0.90
SearchAutocompleteTrayFunction · 0.90
Dialog.tsxFile · 0.90

Calls 7

useSlotIdFunction · 0.90
isFocusWithinFunction · 0.90
focusSafelyFunction · 0.90
getActiveElementFunction · 0.90
useOverlayFocusContainFunction · 0.90
filterDOMPropsFunction · 0.90
hasAttributeMethod · 0.80

Tested by 1

ExampleFunction · 0.72