MCPcopy Create free account
hub / github.com/adobe/react-spectrum / useDialog

Function useDialog

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

Source from the content-addressed store, hash-verified

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

Callers 8

ExampleFunction · 0.90
AlertDialogExampleFunction · 0.90
RegularDialogExampleFunction · 0.90
Dialog.tsxFile · 0.90
ComboBoxTrayFunction · 0.90
SearchAutocompleteTrayFunction · 0.90
Dialog.tsxFile · 0.90
DialogFunction · 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 3

ExampleFunction · 0.72
AlertDialogExampleFunction · 0.72
RegularDialogExampleFunction · 0.72