@hanzogui/dialog 3.0.2 → 7.3.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.
@@ -17,156 +17,171 @@ import { StackZIndexContext } from "@hanzogui/z-index-stack";
17
17
  import * as React from "react";
18
18
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
19
19
  const DialogContext = createStyledContext(
20
- // since we always provide this we can avoid setting here
21
- {}, "Dialog__"),
22
- {
23
- useStyledContext: useDialogContext,
24
- Provider: DialogProvider
25
- } = DialogContext,
26
- DialogTriggerFrame = styled(View, {
27
- name: "DialogTrigger"
28
- }),
29
- DialogTrigger = DialogTriggerFrame.styleable(function (props, forwardedRef) {
30
- const {
31
- scope,
32
- ...triggerProps
33
- } = props,
34
- isInsideButton = React.useContext(ButtonNestingContext),
35
- context = useDialogContext(scope),
36
- composedTriggerRef = useComposedRefs(forwardedRef, context.triggerRef);
37
- return /* @__PURE__ */jsx(ButtonNestingContext.Provider, {
38
- value: !0,
39
- children: /* @__PURE__ */jsx(DialogTriggerFrame, {
40
- render: isInsideButton ? "span" : "button",
41
- "aria-haspopup": "dialog",
42
- "aria-expanded": context.open,
43
- "aria-controls": context.contentId,
44
- "data-state": getState(context.open),
45
- ...triggerProps,
46
- ref: composedTriggerRef,
47
- onPress: composeEventHandlers(props.onPress, context.onOpenToggle)
48
- })
49
- });
50
- }),
51
- DialogPortalFrame = styled(YStack, {
52
- pointerEvents: "none",
53
- render: "dialog",
54
- variants: {
55
- unstyled: {
56
- false: {
57
- alignItems: "center",
58
- justifyContent: "center",
59
- fullscreen: !0,
60
- "$platform-web": {
61
- // undo dialog styles
62
- borderWidth: 0,
63
- backgroundColor: "transparent",
64
- color: "inherit",
65
- maxInlineSize: "none",
66
- margin: 0,
67
- width: "auto",
68
- height: "auto",
69
- // ensure always in frame and right height
70
- maxHeight: "100vh",
71
- position: "fixed",
72
- // ensure dialog inherits stacking context from portal wrapper
73
- zIndex: 1
74
- }
20
+ // since we always provide this we can avoid setting here
21
+ {}, "Dialog__");
22
+ const {
23
+ useStyledContext: useDialogContext,
24
+ Provider: DialogProvider
25
+ } = DialogContext;
26
+ const DialogAdaptHiddenContext = React.createContext(true);
27
+ const DialogTriggerFrame = styled(View, {
28
+ name: "DialogTrigger"
29
+ });
30
+ const DialogTrigger = DialogTriggerFrame.styleable(function DialogTrigger2(props, forwardedRef) {
31
+ const {
32
+ scope,
33
+ ...triggerProps
34
+ } = props;
35
+ const isInsideButton = React.useContext(ButtonNestingContext);
36
+ const context = useDialogContext(scope);
37
+ const composedTriggerRef = useComposedRefs(forwardedRef, context.triggerRef);
38
+ return /* @__PURE__ */jsx(ButtonNestingContext.Provider, {
39
+ value: true,
40
+ children: /* @__PURE__ */jsx(DialogTriggerFrame, {
41
+ render: isInsideButton ? "span" : "button",
42
+ "aria-haspopup": "dialog",
43
+ "aria-expanded": context.open,
44
+ "aria-controls": context.contentId,
45
+ "data-state": getState(context.open),
46
+ ...triggerProps,
47
+ ref: composedTriggerRef,
48
+ onPress: composeEventHandlers(props.onPress, context.onOpenToggle)
49
+ })
50
+ });
51
+ });
52
+ const DialogPortalFrame = styled(YStack, {
53
+ pointerEvents: "none",
54
+ render: "dialog",
55
+ variants: {
56
+ unstyled: {
57
+ false: {
58
+ alignItems: "center",
59
+ justifyContent: "center",
60
+ fullscreen: true,
61
+ "$platform-web": {
62
+ // undo dialog styles
63
+ borderWidth: 0,
64
+ backgroundColor: "transparent",
65
+ color: "inherit",
66
+ maxInlineSize: "none",
67
+ margin: 0,
68
+ width: "auto",
69
+ height: "auto",
70
+ // ensure always in frame and right height
71
+ maxHeight: "100vh",
72
+ position: "fixed",
73
+ // ensure dialog inherits stacking context from portal wrapper
74
+ zIndex: 1
75
75
  }
76
76
  }
77
- },
78
- defaultVariants: {
79
- unstyled: process.env.HANZO_GUI_HEADLESS === "1"
80
77
  }
81
- }),
82
- needsRepropagation = needsPortalRepropagation(),
83
- DialogPortalItem = ({
84
- context,
78
+ },
79
+ defaultVariants: {
80
+ unstyled: process.env.GUI_HEADLESS === "1"
81
+ }
82
+ });
83
+ const needsRepropagation = needsPortalRepropagation();
84
+ const DialogPortalItem = ({
85
+ context,
86
+ children
87
+ }) => {
88
+ const themeName = useThemeName();
89
+ const isAdapted = useAdaptIsActive(context.adaptScope);
90
+ const adaptContext = useAdaptContext(context.adaptScope);
91
+ let content = /* @__PURE__ */jsx(Theme, {
92
+ name: themeName,
85
93
  children
86
- }) => {
87
- const themeName = useThemeName(),
88
- isAdapted = useAdaptIsActive(context.adaptScope),
89
- adaptContext = useAdaptContext(context.adaptScope);
90
- let content = /* @__PURE__ */jsx(Theme, {
91
- name: themeName,
92
- children
93
- });
94
- return needsRepropagation && (content = /* @__PURE__ */jsx(ProvideAdaptContext, {
94
+ });
95
+ if (needsRepropagation) {
96
+ content = /* @__PURE__ */jsx(ProvideAdaptContext, {
95
97
  ...adaptContext,
96
98
  children: /* @__PURE__ */jsx(DialogProvider, {
97
99
  ...context,
98
100
  children: content
99
101
  })
100
- })), isAdapted ? /* @__PURE__ */jsx(AdaptPortalContents, {
101
- scope: context.adaptScope,
102
- children: content
103
- }) : context.modal ? /* @__PURE__ */jsx(PortalItem, {
104
- hostName: context.modal ? "root" : context.adaptScope,
105
- children: content
106
- }) : content;
107
- },
108
- DialogPortal = React.forwardRef((props, forwardRef) => {
109
- const {
110
- scope,
111
- forceMount,
112
- children,
113
- ...frameProps
114
- } = props,
115
- dialogRef = React.useRef(null),
116
- ref = composeRefs(dialogRef, forwardRef),
117
- context = useDialogContext(scope),
118
- keepMounted = forceMount || context.keepChildrenMounted,
119
- isAdapted = useAdaptIsActive(context.adaptScope),
120
- [isFullyHidden, setIsFullyHidden] = React.useState(!context.open);
121
- context.open && isFullyHidden && setIsFullyHidden(!1);
122
- const isVisible = context.open ? !0 : !isFullyHidden;
123
- isWeb && useIsomorphicLayoutEffect(() => {
102
+ });
103
+ }
104
+ return isAdapted ? /* @__PURE__ */jsx(AdaptPortalContents, {
105
+ scope: context.adaptScope,
106
+ children: content
107
+ }) : context.modal ? /* @__PURE__ */jsx(PortalItem, {
108
+ hostName: context.modal ? "root" : context.adaptScope,
109
+ children: content
110
+ }) : content;
111
+ };
112
+ const DialogPortal = React.forwardRef((props, forwardRef) => {
113
+ const {
114
+ scope,
115
+ forceMount,
116
+ children,
117
+ ...frameProps
118
+ } = props;
119
+ const dialogRef = React.useRef(null);
120
+ const ref = composeRefs(dialogRef, forwardRef);
121
+ const context = useDialogContext(scope);
122
+ const keepMounted = forceMount || context.keepChildrenMounted;
123
+ const isAdapted = useAdaptIsActive(context.adaptScope);
124
+ const [isFullyHidden, setIsFullyHidden] = React.useState(!context.open);
125
+ if (context.open && isFullyHidden) {
126
+ setIsFullyHidden(false);
127
+ }
128
+ const isVisible = context.open ? true : !isFullyHidden;
129
+ if (isWeb) {
130
+ useIsomorphicLayoutEffect(() => {
124
131
  const node = dialogRef.current;
125
- node instanceof HTMLDialogElement && (isVisible ? node.show?.() : node.close?.());
126
- }, [isVisible]);
127
- const onAnimationCompleteRef = React.useRef(context.onAnimationComplete);
128
- onAnimationCompleteRef.current = context.onAnimationComplete;
129
- const handleExitComplete = React.useCallback(() => {
130
- setIsFullyHidden(!0), onAnimationCompleteRef.current?.({
131
- open: !1
132
- });
133
- }, []);
134
- React.useEffect(() => {
135
- if (context.open && !isAdapted && onAnimationCompleteRef.current) {
136
- const tm = setTimeout(() => {
137
- onAnimationCompleteRef.current?.({
138
- open: !0
139
- });
140
- }, 350);
141
- return () => clearTimeout(tm);
132
+ if (!(node instanceof HTMLDialogElement)) return;
133
+ if (isVisible) {
134
+ node.show?.();
135
+ } else {
136
+ node.close?.();
142
137
  }
143
- }, [context.open, isAdapted]);
144
- const zIndex = getExpandedShorthand("zIndex", props),
145
- contents = /* @__PURE__ */jsx(StackZIndexContext, {
146
- zIndex: resolveViewZIndex(zIndex),
147
- children: /* @__PURE__ */jsx(Animate, {
148
- type: "presence",
149
- present: !!context.open,
150
- keepChildrenMounted: !!keepMounted,
151
- onExitComplete: handleExitComplete,
152
- passThrough: isAdapted,
153
- children
154
- })
138
+ }, [isVisible]);
139
+ }
140
+ const onAnimationCompleteRef = React.useRef(context.onAnimationComplete);
141
+ onAnimationCompleteRef.current = context.onAnimationComplete;
142
+ const handleExitComplete = React.useCallback(() => {
143
+ setIsFullyHidden(true);
144
+ onAnimationCompleteRef.current?.({
145
+ open: false
146
+ });
147
+ }, []);
148
+ React.useEffect(() => {
149
+ if (context.open && !isAdapted && onAnimationCompleteRef.current) {
150
+ const tm = setTimeout(() => {
151
+ onAnimationCompleteRef.current?.({
152
+ open: true
153
+ });
154
+ }, 350);
155
+ return () => clearTimeout(tm);
156
+ }
157
+ }, [context.open, isAdapted]);
158
+ const zIndex = getExpandedShorthand("zIndex", props);
159
+ const contents = /* @__PURE__ */jsx(StackZIndexContext, {
160
+ zIndex: resolveViewZIndex(zIndex),
161
+ children: /* @__PURE__ */jsx(Animate, {
162
+ type: "presence",
163
+ present: Boolean(context.open),
164
+ keepChildrenMounted: Boolean(keepMounted),
165
+ onExitComplete: handleExitComplete,
166
+ passThrough: isAdapted,
167
+ children
168
+ })
169
+ });
170
+ const framedContents = isFullyHidden && !keepMounted && !isAdapted ? null : /* @__PURE__ */jsx(LayoutMeasurementController, {
171
+ disable: !context.open,
172
+ children: /* @__PURE__ */jsx(DialogPortalFrame, {
173
+ ref,
174
+ ...(isWeb && context.open && {
175
+ "aria-modal": true
155
176
  }),
156
- framedContents = isFullyHidden && !keepMounted && !isAdapted ? null : /* @__PURE__ */jsx(LayoutMeasurementController, {
157
- disable: !context.open,
158
- children: /* @__PURE__ */jsx(DialogPortalFrame, {
159
- ref,
160
- ...(isWeb && context.open && {
161
- "aria-modal": !0
162
- }),
163
- pointerEvents: context.open ? "auto" : "none",
164
- ...frameProps,
165
- className: "_no_backdrop " + (frameProps.className || ""),
166
- children: contents
167
- })
168
- });
169
- return isWeb ? /* @__PURE__ */jsx(Portal, {
177
+ pointerEvents: context.open ? "auto" : "none",
178
+ ...frameProps,
179
+ className: `_no_backdrop ` + (frameProps.className || ""),
180
+ children: contents
181
+ })
182
+ });
183
+ if (isWeb) {
184
+ return /* @__PURE__ */jsx(Portal, {
170
185
  zIndex,
171
186
  stackZIndex: 1e5,
172
187
  passThrough: isAdapted,
@@ -174,397 +189,465 @@ const DialogContext = createStyledContext(
174
189
  passThrough: isAdapted,
175
190
  children: framedContents
176
191
  })
177
- }) : isAdapted ? framedContents : /* @__PURE__ */jsx(DialogPortalItem, {
178
- context,
179
- children: framedContents
180
- });
181
- }),
182
- PassthroughTheme = ({
183
- children,
184
- passThrough
185
- }) => {
186
- const themeName = useThemeName();
187
- return /* @__PURE__ */jsx(Theme, {
188
- passThrough,
189
- name: themeName,
190
- forceClassName: !0,
191
- children
192
192
  });
193
- },
194
- OVERLAY_NAME = "DialogOverlay",
195
- DialogOverlayFrame = styled(YStack, {
196
- name: OVERLAY_NAME,
197
- zIndex: 1,
198
- variants: {
199
- open: {
200
- true: {
201
- pointerEvents: "auto"
202
- },
203
- false: {
204
- pointerEvents: "none"
205
- }
193
+ }
194
+ return isAdapted ? framedContents : /* @__PURE__ */jsx(DialogPortalItem, {
195
+ context,
196
+ children: framedContents
197
+ });
198
+ });
199
+ const PassthroughTheme = ({
200
+ children,
201
+ passThrough
202
+ }) => {
203
+ const themeName = useThemeName();
204
+ return /* @__PURE__ */jsx(Theme, {
205
+ passThrough,
206
+ name: themeName,
207
+ forceClassName: true,
208
+ children
209
+ });
210
+ };
211
+ const OVERLAY_NAME = "DialogOverlay";
212
+ const DialogOverlayFrame = styled(YStack, {
213
+ name: OVERLAY_NAME,
214
+ zIndex: 1,
215
+ variants: {
216
+ open: {
217
+ true: {
218
+ pointerEvents: "auto"
206
219
  },
207
- unstyled: {
208
- false: {
209
- fullscreen: !0,
210
- position: "absolute",
211
- backgroundColor: "$background",
212
- pointerEvents: "auto"
213
- }
220
+ false: {
221
+ pointerEvents: "none"
214
222
  }
215
223
  },
216
- defaultVariants: {
217
- unstyled: process.env.HANZO_GUI_HEADLESS === "1"
224
+ unstyled: {
225
+ false: {
226
+ fullscreen: true,
227
+ position: "absolute",
228
+ backgroundColor: "$background",
229
+ pointerEvents: "auto"
230
+ }
218
231
  }
219
- }),
220
- DialogOverlay = DialogOverlayFrame.styleable(function ({
221
- scope,
222
- ...props
223
- }, forwardedRef) {
224
- const context = useDialogContext(scope),
225
- {
226
- forceMount = context.forceMount,
227
- ...overlayProps
228
- } = props,
229
- isAdapted = useAdaptIsActive(context.adaptScope);
230
- return !forceMount && (!context.modal || isAdapted) ? null : /* @__PURE__ */jsx(DialogOverlayFrame, {
231
- "data-state": getState(context.open),
232
- pointerEvents: context.open ? "auto" : "none",
233
- ...overlayProps,
234
- ref: forwardedRef
235
- });
236
- }),
237
- CONTENT_NAME = "DialogContent",
238
- DialogContentFrame = styled(ThemeableStack, {
239
- name: CONTENT_NAME,
240
- zIndex: 2,
241
- variants: {
242
- size: {
243
- "...size": (val, extras) => ({})
244
- },
245
- unstyled: {
246
- false: {
247
- position: "relative",
248
- backgroundColor: "$background",
249
- borderWidth: 1,
250
- borderColor: "$borderColor",
251
- padding: "$true",
252
- borderRadius: "$true",
253
- elevate: !0,
254
- // Ensure content receives pointer events (fixes React 19 + display:contents inheritance)
255
- pointerEvents: "auto"
256
- }
232
+ },
233
+ defaultVariants: {
234
+ unstyled: process.env.GUI_HEADLESS === "1"
235
+ }
236
+ });
237
+ const DialogOverlay = DialogOverlayFrame.styleable(function DialogOverlay2({
238
+ scope,
239
+ ...props
240
+ }, forwardedRef) {
241
+ const context = useDialogContext(scope);
242
+ const {
243
+ forceMount = context.forceMount,
244
+ ...overlayProps
245
+ } = props;
246
+ const isAdapted = useAdaptIsActive(context.adaptScope);
247
+ if (!forceMount) {
248
+ if (!context.modal || isAdapted) {
249
+ return null;
250
+ }
251
+ }
252
+ return /* @__PURE__ */jsx(DialogOverlayFrame, {
253
+ "data-state": getState(context.open),
254
+ pointerEvents: context.open ? "auto" : "none",
255
+ ...overlayProps,
256
+ ref: forwardedRef
257
+ });
258
+ });
259
+ const CONTENT_NAME = "DialogContent";
260
+ const DialogContentFrame = styled(ThemeableStack, {
261
+ name: CONTENT_NAME,
262
+ zIndex: 2,
263
+ variants: {
264
+ size: {
265
+ "...size": (val, extras) => {
266
+ return {};
257
267
  }
258
268
  },
259
- defaultVariants: {
260
- size: "$true",
261
- unstyled: process.env.HANZO_GUI_HEADLESS === "1"
269
+ unstyled: {
270
+ false: {
271
+ position: "relative",
272
+ backgroundColor: "$background",
273
+ borderWidth: 1,
274
+ borderColor: "$borderColor",
275
+ padding: "$true",
276
+ borderRadius: "$true",
277
+ elevate: true,
278
+ // Ensure content receives pointer events (fixes React 19 + display:contents inheritance)
279
+ pointerEvents: "auto"
280
+ }
262
281
  }
263
- }),
264
- DialogContent = DialogContentFrame.styleable(function ({
265
- scope,
266
- ...props
267
- }, forwardedRef) {
268
- const context = useDialogContext(scope),
269
- contents = /* @__PURE__ */jsx(Fragment, {
270
- children: context.modal ? /* @__PURE__ */jsx(DialogContentModal, {
271
- context,
272
- ...props,
273
- ref: forwardedRef
274
- }) : /* @__PURE__ */jsx(DialogContentNonModal, {
275
- context,
276
- ...props,
277
- ref: forwardedRef
278
- })
279
- });
280
- return !isWeb || context.disableRemoveScroll ? contents : /* @__PURE__ */jsx(RemoveScroll, {
281
- enabled: context.open,
282
- children: /* @__PURE__ */jsx("div", {
283
- "data-remove-scroll-container": !0,
284
- className: "_dsp_contents",
285
- children: contents
286
- })
287
- });
288
- }),
289
- DialogContentModal = React.forwardRef(({
290
- children,
291
- context,
292
- ...props
293
- }, forwardedRef) => {
294
- const contentRef = React.useRef(null),
295
- composedRefs = useComposedRefs(forwardedRef, context.contentRef, contentRef);
296
- return /* @__PURE__ */jsx(DialogContentImpl, {
282
+ },
283
+ defaultVariants: {
284
+ size: "$true",
285
+ unstyled: process.env.GUI_HEADLESS === "1"
286
+ }
287
+ });
288
+ const DialogContent = DialogContentFrame.styleable(function DialogContent2({
289
+ scope,
290
+ ...props
291
+ }, forwardedRef) {
292
+ const context = useDialogContext(scope);
293
+ const contents = /* @__PURE__ */jsx(Fragment, {
294
+ children: context.modal ? /* @__PURE__ */jsx(DialogContentModal, {
295
+ context,
297
296
  ...props,
297
+ ref: forwardedRef
298
+ }) : /* @__PURE__ */jsx(DialogContentNonModal, {
298
299
  context,
299
- ref: composedRefs,
300
- trapFocus: context.open,
301
- disableOutsidePointerEvents: !0,
302
- onCloseAutoFocus: composeEventHandlers(props.onCloseAutoFocus, event => {
303
- event.preventDefault(), context.triggerRef.current?.focus();
304
- }),
305
- onPointerDownOutside: composeEventHandlers(props.onPointerDownOutside, event => {
306
- const originalEvent = event.detail.originalEvent,
307
- ctrlLeftClick = originalEvent.button === 0 && originalEvent.ctrlKey === !0;
308
- (originalEvent.button === 2 || ctrlLeftClick) && event.preventDefault();
309
- }),
310
- onFocusOutside: composeEventHandlers(props.onFocusOutside, event => event.preventDefault()),
311
- ...(!props.unstyled && {
312
- outlineStyle: "none"
313
- }),
314
- children
315
- });
316
- }),
317
- DialogContentNonModal = React.forwardRef((props, forwardedRef) => {
318
- const hasInteractedOutsideRef = React.useRef(!1);
319
- return /* @__PURE__ */jsx(DialogContentImpl, {
320
300
  ...props,
321
- ref: forwardedRef,
322
- trapFocus: !1,
323
- disableOutsidePointerEvents: !1,
324
- onCloseAutoFocus: event => {
325
- props.onCloseAutoFocus?.(event), event.defaultPrevented || (hasInteractedOutsideRef.current || props.context.triggerRef.current?.focus(), event.preventDefault()), hasInteractedOutsideRef.current = !1;
326
- },
327
- onInteractOutside: event => {
328
- props.onInteractOutside?.(event), event.defaultPrevented || (hasInteractedOutsideRef.current = !0);
329
- const target = event.target,
330
- trigger = props.context.triggerRef.current;
331
- if (!(trigger instanceof HTMLElement)) return;
332
- trigger.contains(target) && event.preventDefault();
301
+ ref: forwardedRef
302
+ })
303
+ });
304
+ if (!isWeb || context.disableRemoveScroll) {
305
+ return contents;
306
+ }
307
+ return /* @__PURE__ */jsx(RemoveScroll, {
308
+ enabled: context.open,
309
+ children: /* @__PURE__ */jsx("div", {
310
+ "data-remove-scroll-container": true,
311
+ className: "_dsp_contents",
312
+ children: contents
313
+ })
314
+ });
315
+ });
316
+ const DialogContentModal = React.forwardRef(({
317
+ children,
318
+ context,
319
+ ...props
320
+ }, forwardedRef) => {
321
+ const contentRef = React.useRef(null);
322
+ const composedRefs = useComposedRefs(forwardedRef, context.contentRef, contentRef);
323
+ return /* @__PURE__ */jsx(DialogContentImpl, {
324
+ ...props,
325
+ context,
326
+ ref: composedRefs,
327
+ trapFocus: context.open,
328
+ disableOutsidePointerEvents: true,
329
+ onCloseAutoFocus: composeEventHandlers(props.onCloseAutoFocus, event => {
330
+ event.preventDefault();
331
+ context.triggerRef.current?.focus();
332
+ }),
333
+ onPointerDownOutside: composeEventHandlers(props.onPointerDownOutside, event => {
334
+ const originalEvent = event["detail"].originalEvent;
335
+ const ctrlLeftClick = originalEvent.button === 0 && originalEvent.ctrlKey === true;
336
+ const isRightClick = originalEvent.button === 2 || ctrlLeftClick;
337
+ if (isRightClick) event.preventDefault();
338
+ }),
339
+ onFocusOutside: composeEventHandlers(props.onFocusOutside, event => event.preventDefault()),
340
+ ...(!props.unstyled && {
341
+ outlineStyle: "none"
342
+ }),
343
+ children
344
+ });
345
+ });
346
+ const DialogContentNonModal = React.forwardRef((props, forwardedRef) => {
347
+ const hasInteractedOutsideRef = React.useRef(false);
348
+ return /* @__PURE__ */jsx(DialogContentImpl, {
349
+ ...props,
350
+ ref: forwardedRef,
351
+ trapFocus: false,
352
+ disableOutsidePointerEvents: false,
353
+ onCloseAutoFocus: event => {
354
+ props.onCloseAutoFocus?.(event);
355
+ if (!event.defaultPrevented) {
356
+ if (!hasInteractedOutsideRef.current) {
357
+ props.context.triggerRef.current?.focus();
358
+ }
359
+ event.preventDefault();
333
360
  }
334
- });
335
- }),
336
- DialogContentImpl = React.forwardRef((props, forwardedRef) => {
337
- const {
338
- trapFocus,
339
- onOpenAutoFocus,
340
- onCloseAutoFocus,
341
- disableOutsidePointerEvents,
342
- onEscapeKeyDown,
343
- onPointerDownOutside,
344
- onFocusOutside,
345
- onInteractOutside,
346
- context,
347
- ...contentProps
348
- } = props,
349
- contentRef = React.useRef(null),
350
- composedRefs = useComposedRefs(forwardedRef, contentRef);
351
- if (useAdaptIsActive(context.adaptScope)) return !isWeb && !context.open ? null : /* @__PURE__ */jsx(DialogPortalItem, {
361
+ hasInteractedOutsideRef.current = false;
362
+ },
363
+ onInteractOutside: event => {
364
+ props.onInteractOutside?.(event);
365
+ if (!event.defaultPrevented) hasInteractedOutsideRef.current = true;
366
+ const target = event.target;
367
+ const trigger = props.context.triggerRef.current;
368
+ if (!(trigger instanceof HTMLElement)) return;
369
+ const targetIsTrigger = trigger.contains(target);
370
+ if (targetIsTrigger) event.preventDefault();
371
+ }
372
+ });
373
+ });
374
+ const DialogContentImpl = React.forwardRef((props, forwardedRef) => {
375
+ const {
376
+ trapFocus,
377
+ onOpenAutoFocus,
378
+ onCloseAutoFocus,
379
+ disableOutsidePointerEvents,
380
+ onEscapeKeyDown,
381
+ onPointerDownOutside,
382
+ onFocusOutside,
383
+ onInteractOutside,
384
+ context,
385
+ ...contentProps
386
+ } = props;
387
+ const contentRef = React.useRef(null);
388
+ const composedRefs = useComposedRefs(forwardedRef, contentRef);
389
+ const isAdapted = useAdaptIsActive(context.adaptScope);
390
+ const isAdaptFullyHidden = React.useContext(DialogAdaptHiddenContext);
391
+ if (isAdapted) {
392
+ if (!context.open && !context.keepChildrenMounted && isAdaptFullyHidden) {
393
+ return null;
394
+ }
395
+ return /* @__PURE__ */jsx(DialogPortalItem, {
352
396
  context,
353
397
  children: contentProps.children
354
398
  });
355
- const contents = /* @__PURE__ */jsx(DialogContentFrame, {
356
- ref: composedRefs,
357
- id: context.contentId,
358
- role: "dialog",
359
- "aria-modal": context.modal,
360
- "aria-describedby": context.descriptionId,
361
- "aria-labelledby": context.titleId,
362
- "data-state": getState(context.open),
363
- pointerEvents: context.open ? "auto" : "none",
364
- ...contentProps
365
- });
366
- return isWeb ? /* @__PURE__ */jsxs(Fragment, {
367
- children: [/* @__PURE__ */jsx(Dismissable, {
368
- disableOutsidePointerEvents: context.open && disableOutsidePointerEvents,
399
+ }
400
+ const contents = /* @__PURE__ */jsx(DialogContentFrame, {
401
+ ref: composedRefs,
402
+ id: context.contentId,
403
+ role: "dialog",
404
+ "aria-modal": context.modal,
405
+ "aria-describedby": context.descriptionId,
406
+ "aria-labelledby": context.titleId,
407
+ "data-state": getState(context.open),
408
+ pointerEvents: context.open ? "auto" : "none",
409
+ ...contentProps
410
+ });
411
+ if (!isWeb) {
412
+ return contents;
413
+ }
414
+ return /* @__PURE__ */jsxs(Fragment, {
415
+ children: [/* @__PURE__ */jsx(Dismissable, {
416
+ disableOutsidePointerEvents: context.open && disableOutsidePointerEvents,
417
+ forceUnmount: !context.open,
418
+ onEscapeKeyDown,
419
+ onPointerDownOutside,
420
+ onFocusOutside,
421
+ onInteractOutside,
422
+ onDismiss: () => context?.onOpenChange?.(false),
423
+ children: /* @__PURE__ */jsx(FocusScope, {
424
+ loop: true,
425
+ enabled: context.open,
426
+ trapped: trapFocus,
427
+ onMountAutoFocus: onOpenAutoFocus,
369
428
  forceUnmount: !context.open,
370
- onEscapeKeyDown,
371
- onPointerDownOutside,
372
- onFocusOutside,
373
- onInteractOutside,
374
- onDismiss: () => context?.onOpenChange?.(!1),
375
- children: /* @__PURE__ */jsx(FocusScope, {
376
- loop: !0,
377
- enabled: context.open,
378
- trapped: trapFocus,
379
- onMountAutoFocus: onOpenAutoFocus,
380
- forceUnmount: !context.open,
381
- onUnmountAutoFocus: onCloseAutoFocus,
382
- children: contents
383
- })
384
- }), process.env.NODE_ENV === "development" && /* @__PURE__ */jsxs(Fragment, {
385
- children: [/* @__PURE__ */jsx(TitleWarning, {
386
- titleId: context.titleId
387
- }), /* @__PURE__ */jsx(DescriptionWarning, {
388
- contentRef,
389
- descriptionId: context.descriptionId
390
- })]
391
- })]
392
- }) : contents;
393
- }),
394
- DialogTitleFrame = styled(H2, {
395
- name: "DialogTitle"
396
- }),
397
- DialogTitle = DialogTitleFrame.styleable(function (props, forwardedRef) {
398
- const {
399
- scope,
400
- ...titleProps
401
- } = props,
402
- context = useDialogContext(scope);
403
- return /* @__PURE__ */jsx(DialogTitleFrame, {
404
- id: context.titleId,
405
- ...titleProps,
406
- ref: forwardedRef
407
- });
408
- }),
409
- DialogDescriptionFrame = styled(Paragraph, {
410
- name: "DialogDescription"
411
- }),
412
- DialogDescription = DialogDescriptionFrame.styleable(function (props, forwardedRef) {
413
- const {
414
- scope,
415
- ...descriptionProps
416
- } = props,
417
- context = useDialogContext(scope);
418
- return /* @__PURE__ */jsx(DialogDescriptionFrame, {
419
- id: context.descriptionId,
420
- ...descriptionProps,
421
- ref: forwardedRef
422
- });
423
- }),
424
- CLOSE_NAME = "DialogClose",
425
- DialogCloseFrame = styled(View, {
426
- name: CLOSE_NAME,
427
- render: "button"
428
- }),
429
- DialogClose = DialogCloseFrame.styleable((props, forwardedRef) => {
430
- const {
431
- scope,
432
- displayWhenAdapted,
433
- ...closeProps
434
- } = props,
435
- context = useDialogContext(scope),
436
- isAdapted = useAdaptIsActive(context.adaptScope),
437
- isInsideButton = React.useContext(ButtonNestingContext);
438
- return isAdapted && !displayWhenAdapted ? null : /* @__PURE__ */jsx(DialogCloseFrame, {
439
- "aria-label": "Dialog Close",
440
- render: isInsideButton ? "span" : "button",
441
- ...closeProps,
442
- ref: forwardedRef,
443
- onPress: composeEventHandlers(props.onPress, () => {
444
- context.onOpenChange(!1);
429
+ onUnmountAutoFocus: onCloseAutoFocus,
430
+ children: contents
445
431
  })
446
- });
432
+ }), process.env.NODE_ENV === "development" && /* @__PURE__ */jsxs(Fragment, {
433
+ children: [/* @__PURE__ */jsx(TitleWarning, {
434
+ titleId: context.titleId
435
+ }), /* @__PURE__ */jsx(DescriptionWarning, {
436
+ contentRef,
437
+ descriptionId: context.descriptionId
438
+ })]
439
+ })]
440
+ });
441
+ });
442
+ const DialogTitleFrame = styled(H2, {
443
+ name: "DialogTitle"
444
+ });
445
+ const DialogTitle = DialogTitleFrame.styleable(function DialogTitle2(props, forwardedRef) {
446
+ const {
447
+ scope,
448
+ ...titleProps
449
+ } = props;
450
+ const context = useDialogContext(scope);
451
+ return /* @__PURE__ */jsx(DialogTitleFrame, {
452
+ id: context.titleId,
453
+ ...titleProps,
454
+ ref: forwardedRef
447
455
  });
456
+ });
457
+ const DialogDescriptionFrame = styled(Paragraph, {
458
+ name: "DialogDescription"
459
+ });
460
+ const DialogDescription = DialogDescriptionFrame.styleable(function DialogDescription2(props, forwardedRef) {
461
+ const {
462
+ scope,
463
+ ...descriptionProps
464
+ } = props;
465
+ const context = useDialogContext(scope);
466
+ return /* @__PURE__ */jsx(DialogDescriptionFrame, {
467
+ id: context.descriptionId,
468
+ ...descriptionProps,
469
+ ref: forwardedRef
470
+ });
471
+ });
472
+ const CLOSE_NAME = "DialogClose";
473
+ const DialogCloseFrame = styled(View, {
474
+ name: CLOSE_NAME,
475
+ render: "button"
476
+ });
477
+ const DialogClose = DialogCloseFrame.styleable((props, forwardedRef) => {
478
+ const {
479
+ scope,
480
+ displayWhenAdapted,
481
+ ...closeProps
482
+ } = props;
483
+ const context = useDialogContext(scope);
484
+ const isAdapted = useAdaptIsActive(context.adaptScope);
485
+ const isInsideButton = React.useContext(ButtonNestingContext);
486
+ if (isAdapted && !displayWhenAdapted) {
487
+ return null;
488
+ }
489
+ return /* @__PURE__ */jsx(DialogCloseFrame, {
490
+ "aria-label": "Dialog Close",
491
+ render: isInsideButton ? "span" : "button",
492
+ ...closeProps,
493
+ ref: forwardedRef,
494
+ onPress: composeEventHandlers(props.onPress, () => {
495
+ context.onOpenChange(false);
496
+ })
497
+ });
498
+ });
448
499
  function getState(open) {
449
500
  return open ? "open" : "closed";
450
501
  }
451
- const TITLE_WARNING_NAME = "DialogTitleWarning",
452
- [DialogWarningProvider, useWarningContext] = createContext(TITLE_WARNING_NAME, {
453
- contentName: CONTENT_NAME,
454
- titleName: "DialogTitle",
455
- docsSlug: "dialog"
456
- }),
457
- TitleWarning = ({
458
- titleId
459
- }) => {
460
- if (process.env.NODE_ENV === "development") {
461
- const titleWarningContext = useWarningContext(TITLE_WARNING_NAME),
462
- MESSAGE = `\`${titleWarningContext.contentName}\` wants a \`${titleWarningContext.titleName}\` to be accessible. If you want to hide the \`${titleWarningContext.titleName}\`, wrap it with <VisuallyHidden />.`;
463
- React.useEffect(() => {
464
- isWeb && titleId && (document.getElementById(titleId) || console.warn(MESSAGE));
465
- }, [MESSAGE, titleId]);
466
- }
467
- return null;
468
- },
469
- DESCRIPTION_WARNING_NAME = "DialogDescriptionWarning",
470
- DescriptionWarning = ({
502
+ const TITLE_WARNING_NAME = "DialogTitleWarning";
503
+ const [DialogWarningProvider, useWarningContext] = createContext(TITLE_WARNING_NAME, {
504
+ contentName: CONTENT_NAME,
505
+ titleName: "DialogTitle",
506
+ docsSlug: "dialog"
507
+ });
508
+ const TitleWarning = ({
509
+ titleId
510
+ }) => {
511
+ if (process.env.NODE_ENV === "development") {
512
+ const titleWarningContext = useWarningContext(TITLE_WARNING_NAME);
513
+ const MESSAGE = `\`${titleWarningContext.contentName}\` wants a \`${titleWarningContext.titleName}\` to be accessible. If you want to hide the \`${titleWarningContext.titleName}\`, wrap it with <VisuallyHidden />.`;
514
+ React.useEffect(() => {
515
+ if (!isWeb) return;
516
+ if (titleId) {
517
+ const hasTitle = document.getElementById(titleId);
518
+ if (!hasTitle) {
519
+ console.warn(MESSAGE);
520
+ }
521
+ }
522
+ }, [MESSAGE, titleId]);
523
+ }
524
+ return null;
525
+ };
526
+ const DESCRIPTION_WARNING_NAME = "DialogDescriptionWarning";
527
+ const DescriptionWarning = ({
528
+ contentRef,
529
+ descriptionId
530
+ }) => {
531
+ if (process.env.NODE_ENV === "development") {
532
+ const descriptionWarningContext = useWarningContext(DESCRIPTION_WARNING_NAME);
533
+ const MESSAGE = `Warning: Missing \`Description\` or \`aria-describedby={undefined}\` for {${descriptionWarningContext.contentName}}.`;
534
+ React.useEffect(() => {
535
+ if (!isWeb) return;
536
+ const contentNode = contentRef.current;
537
+ if (!(contentNode instanceof HTMLElement)) {
538
+ return;
539
+ }
540
+ const describedById = contentNode.getAttribute("aria-describedby");
541
+ if (descriptionId && describedById) {
542
+ const hasDescription = document.getElementById(descriptionId);
543
+ if (!hasDescription) {
544
+ console.warn(MESSAGE);
545
+ }
546
+ }
547
+ }, [MESSAGE, contentRef, descriptionId]);
548
+ }
549
+ return null;
550
+ };
551
+ const Dialog = withStaticProperties(React.forwardRef(function Dialog2(props, ref) {
552
+ const {
553
+ scope = "",
554
+ children,
555
+ open: openProp,
556
+ defaultOpen = false,
557
+ onOpenChange,
558
+ modal = true,
559
+ keepChildrenMounted,
560
+ disableRemoveScroll = false,
561
+ onAnimationComplete
562
+ } = props;
563
+ const baseId = React.useId();
564
+ const dialogId = `Dialog-${scope}-${baseId}`;
565
+ const contentId = `${dialogId}-content`;
566
+ const titleId = `${dialogId}-title`;
567
+ const descriptionId = `${dialogId}-description`;
568
+ const triggerRef = React.useRef(null);
569
+ const contentRef = React.useRef(null);
570
+ const [open, setOpen] = useControllableState({
571
+ prop: openProp,
572
+ defaultProp: defaultOpen,
573
+ onChange: onOpenChange
574
+ });
575
+ const onOpenToggle = React.useCallback(() => {
576
+ setOpen(prevOpen => !prevOpen);
577
+ }, [setOpen]);
578
+ const adaptScope = `DialogAdapt${scope}`;
579
+ const context = {
580
+ dialogScope: scope,
581
+ adaptScope,
582
+ triggerRef,
471
583
  contentRef,
472
- descriptionId
473
- }) => {
474
- if (process.env.NODE_ENV === "development") {
475
- const MESSAGE = `Warning: Missing \`Description\` or \`aria-describedby={undefined}\` for {${useWarningContext(DESCRIPTION_WARNING_NAME).contentName}}.`;
476
- React.useEffect(() => {
477
- if (!isWeb) return;
478
- const contentNode = contentRef.current;
479
- if (!(contentNode instanceof HTMLElement)) return;
480
- const describedById = contentNode.getAttribute("aria-describedby");
481
- descriptionId && describedById && (document.getElementById(descriptionId) || console.warn(MESSAGE));
482
- }, [MESSAGE, contentRef, descriptionId]);
483
- }
484
- return null;
485
- },
486
- Dialog = withStaticProperties(React.forwardRef(function (props, ref) {
487
- const {
488
- scope = "",
489
- children,
490
- open: openProp,
491
- defaultOpen = !1,
492
- onOpenChange,
493
- modal = !0,
494
- keepChildrenMounted,
495
- disableRemoveScroll = !1,
496
- onAnimationComplete
497
- } = props,
498
- baseId = React.useId(),
499
- dialogId = `Dialog-${scope}-${baseId}`,
500
- contentId = `${dialogId}-content`,
501
- titleId = `${dialogId}-title`,
502
- descriptionId = `${dialogId}-description`,
503
- triggerRef = React.useRef(null),
504
- contentRef = React.useRef(null),
505
- [open, setOpen] = useControllableState({
506
- prop: openProp,
507
- defaultProp: defaultOpen,
508
- onChange: onOpenChange
509
- }),
510
- onOpenToggle = React.useCallback(() => {
511
- setOpen(prevOpen => !prevOpen);
512
- }, [setOpen]),
513
- adaptScope = `DialogAdapt${scope}`,
514
- context = {
515
- dialogScope: scope,
516
- adaptScope,
517
- triggerRef,
518
- contentRef,
519
- contentId,
520
- titleId,
521
- descriptionId,
522
- open,
584
+ contentId,
585
+ titleId,
586
+ descriptionId,
587
+ open,
588
+ onOpenChange: setOpen,
589
+ onOpenToggle,
590
+ modal,
591
+ keepChildrenMounted,
592
+ disableRemoveScroll,
593
+ onAnimationComplete
594
+ };
595
+ React.useImperativeHandle(ref, () => ({
596
+ open: setOpen
597
+ }), [setOpen]);
598
+ return /* @__PURE__ */jsx(AdaptParent, {
599
+ scope: adaptScope,
600
+ portal: {
601
+ forwardProps: props
602
+ },
603
+ children: /* @__PURE__ */jsx(DialogProvider, {
604
+ scope,
605
+ ...context,
606
+ children: /* @__PURE__ */jsx(DialogSheetController, {
523
607
  onOpenChange: setOpen,
524
- onOpenToggle,
525
- modal,
526
- keepChildrenMounted,
527
- disableRemoveScroll,
528
- onAnimationComplete
529
- };
530
- return React.useImperativeHandle(ref, () => ({
531
- open: setOpen
532
- }), [setOpen]), /* @__PURE__ */jsx(AdaptParent, {
533
- scope: adaptScope,
534
- portal: {
535
- forwardProps: props
536
- },
537
- children: /* @__PURE__ */jsx(DialogProvider, {
538
608
  scope,
539
- ...context,
540
- children: /* @__PURE__ */jsx(DialogSheetController, {
541
- onOpenChange: setOpen,
542
- scope,
543
- children
544
- })
609
+ children
545
610
  })
546
- });
547
- }), {
548
- Trigger: DialogTrigger,
549
- Portal: DialogPortal,
550
- Overlay: DialogOverlay,
551
- Content: DialogContent,
552
- Title: DialogTitle,
553
- Description: DialogDescription,
554
- Close: DialogClose,
555
- FocusScope: FocusScopeController,
556
- Adapt
611
+ })
557
612
  });
613
+ }), {
614
+ Trigger: DialogTrigger,
615
+ Portal: DialogPortal,
616
+ Overlay: DialogOverlay,
617
+ Content: DialogContent,
618
+ Title: DialogTitle,
619
+ Description: DialogDescription,
620
+ Close: DialogClose,
621
+ FocusScope: FocusScopeController,
622
+ Adapt
623
+ });
558
624
  const DialogSheetController = props => {
559
- const context = useDialogContext(props.scope),
560
- isAdapted = useAdaptIsActive(context.adaptScope);
625
+ const context = useDialogContext(props.scope);
626
+ const isAdapted = useAdaptIsActive(context.adaptScope);
627
+ const [isAdaptFullyHidden, setIsAdaptFullyHidden] = React.useState(!context.open);
628
+ if (context.open && isAdaptFullyHidden) {
629
+ setIsAdaptFullyHidden(false);
630
+ }
631
+ const handleSheetAnimationComplete = React.useCallback(({
632
+ open
633
+ }) => {
634
+ if (!open) {
635
+ setIsAdaptFullyHidden(true);
636
+ }
637
+ }, []);
561
638
  return /* @__PURE__ */jsx(SheetController, {
562
639
  onOpenChange: val => {
563
- isAdapted && props.onOpenChange?.(val);
640
+ if (isAdapted) {
641
+ props.onOpenChange?.(val);
642
+ }
564
643
  },
644
+ onAnimationComplete: handleSheetAnimationComplete,
565
645
  open: context.open,
566
646
  hidden: !isAdapted,
567
- children: props.children
647
+ children: /* @__PURE__ */jsx(DialogAdaptHiddenContext.Provider, {
648
+ value: isAdaptFullyHidden,
649
+ children: props.children
650
+ })
568
651
  });
569
652
  };
570
653
  export { Dialog, DialogClose, DialogContent, DialogContext, DialogDescription, DialogOverlay, DialogOverlayFrame, DialogPortal, DialogPortalFrame, DialogProvider, DialogTitle, DialogTrigger, DialogWarningProvider, useDialogContext };