@tamagui/dialog 2.0.0-rc.9 → 2.0.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,142 +17,171 @@ import { StackZIndexContext } from "@tamagui/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.TAMAGUI_HEADLESS === "1"
80
77
  }
81
- }),
82
- needsRepropagation = needsPortalRepropagation(),
83
- DialogPortalItem = ({
84
- context,
78
+ },
79
+ defaultVariants: {
80
+ unstyled: process.env.TAMAGUI_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?.());
132
+ if (!(node instanceof HTMLDialogElement)) return;
133
+ if (isVisible) {
134
+ node.show?.();
135
+ } else {
136
+ node.close?.();
137
+ }
126
138
  }, [isVisible]);
127
- const handleExitComplete = React.useCallback(() => {
128
- setIsFullyHidden(!0);
129
- }, []),
130
- zIndex = getExpandedShorthand("zIndex", props),
131
- contents = /* @__PURE__ */jsx(StackZIndexContext, {
132
- zIndex: resolveViewZIndex(zIndex),
133
- children: /* @__PURE__ */jsx(Animate, {
134
- type: "presence",
135
- present: !!context.open,
136
- keepChildrenMounted: !!keepMounted,
137
- onExitComplete: handleExitComplete,
138
- passThrough: isAdapted,
139
- children
140
- })
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
141
176
  }),
142
- framedContents = isFullyHidden && !keepMounted && !isAdapted ? null : /* @__PURE__ */jsx(LayoutMeasurementController, {
143
- disable: !context.open,
144
- children: /* @__PURE__ */jsx(DialogPortalFrame, {
145
- ref,
146
- ...(isWeb && context.open && {
147
- "aria-modal": !0
148
- }),
149
- pointerEvents: context.open ? "auto" : "none",
150
- ...frameProps,
151
- className: "_no_backdrop " + (frameProps.className || ""),
152
- children: contents
153
- })
154
- });
155
- 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, {
156
185
  zIndex,
157
186
  stackZIndex: 1e5,
158
187
  passThrough: isAdapted,
@@ -160,395 +189,465 @@ const DialogContext = createStyledContext(
160
189
  passThrough: isAdapted,
161
190
  children: framedContents
162
191
  })
163
- }) : isAdapted ? framedContents : /* @__PURE__ */jsx(DialogPortalItem, {
164
- context,
165
- children: framedContents
166
- });
167
- }),
168
- PassthroughTheme = ({
169
- children,
170
- passThrough
171
- }) => {
172
- const themeName = useThemeName();
173
- return /* @__PURE__ */jsx(Theme, {
174
- passThrough,
175
- name: themeName,
176
- forceClassName: !0,
177
- children
178
192
  });
179
- },
180
- OVERLAY_NAME = "DialogOverlay",
181
- DialogOverlayFrame = styled(YStack, {
182
- name: OVERLAY_NAME,
183
- variants: {
184
- open: {
185
- true: {
186
- pointerEvents: "auto"
187
- },
188
- false: {
189
- pointerEvents: "none"
190
- }
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"
191
219
  },
192
- unstyled: {
193
- false: {
194
- fullscreen: !0,
195
- position: "absolute",
196
- backgroundColor: "$background",
197
- zIndex: 99999,
198
- pointerEvents: "auto"
199
- }
220
+ false: {
221
+ pointerEvents: "none"
200
222
  }
201
223
  },
202
- defaultVariants: {
203
- unstyled: process.env.TAMAGUI_HEADLESS === "1"
224
+ unstyled: {
225
+ false: {
226
+ fullscreen: true,
227
+ position: "absolute",
228
+ backgroundColor: "$background",
229
+ pointerEvents: "auto"
230
+ }
204
231
  }
205
- }),
206
- DialogOverlay = DialogOverlayFrame.styleable(function ({
207
- scope,
208
- ...props
209
- }, forwardedRef) {
210
- const context = useDialogContext(scope),
211
- {
212
- forceMount = context.forceMount,
213
- ...overlayProps
214
- } = props,
215
- isAdapted = useAdaptIsActive(context.adaptScope);
216
- return !forceMount && (!context.modal || isAdapted) ? null : /* @__PURE__ */jsx(DialogOverlayFrame, {
217
- "data-state": getState(context.open),
218
- pointerEvents: context.open ? "auto" : "none",
219
- ...overlayProps,
220
- ref: forwardedRef
221
- });
222
- }),
223
- CONTENT_NAME = "DialogContent",
224
- DialogContentFrame = styled(ThemeableStack, {
225
- name: CONTENT_NAME,
226
- variants: {
227
- size: {
228
- "...size": (val, extras) => ({})
229
- },
230
- unstyled: {
231
- false: {
232
- position: "relative",
233
- backgroundColor: "$background",
234
- borderWidth: 1,
235
- borderColor: "$borderColor",
236
- padding: "$true",
237
- borderRadius: "$true",
238
- elevate: !0,
239
- zIndex: 1e5,
240
- // Ensure content receives pointer events (fixes React 19 + display:contents inheritance)
241
- pointerEvents: "auto"
242
- }
232
+ },
233
+ defaultVariants: {
234
+ unstyled: process.env.TAMAGUI_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 {};
243
267
  }
244
268
  },
245
- defaultVariants: {
246
- size: "$true",
247
- unstyled: process.env.TAMAGUI_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
+ }
248
281
  }
249
- }),
250
- DialogContent = DialogContentFrame.styleable(function ({
251
- scope,
252
- ...props
253
- }, forwardedRef) {
254
- const context = useDialogContext(scope),
255
- contents = /* @__PURE__ */jsx(Fragment, {
256
- children: context.modal ? /* @__PURE__ */jsx(DialogContentModal, {
257
- context,
258
- ...props,
259
- ref: forwardedRef
260
- }) : /* @__PURE__ */jsx(DialogContentNonModal, {
261
- context,
262
- ...props,
263
- ref: forwardedRef
264
- })
265
- });
266
- return !isWeb || context.disableRemoveScroll ? contents : /* @__PURE__ */jsx(RemoveScroll, {
267
- enabled: context.open,
268
- children: /* @__PURE__ */jsx("div", {
269
- "data-remove-scroll-container": !0,
270
- className: "_dsp_contents",
271
- children: contents
272
- })
273
- });
274
- }),
275
- DialogContentModal = React.forwardRef(({
276
- children,
277
- context,
278
- ...props
279
- }, forwardedRef) => {
280
- const contentRef = React.useRef(null),
281
- composedRefs = useComposedRefs(forwardedRef, context.contentRef, contentRef);
282
- return /* @__PURE__ */jsx(DialogContentImpl, {
282
+ },
283
+ defaultVariants: {
284
+ size: "$true",
285
+ unstyled: process.env.TAMAGUI_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,
283
296
  ...props,
297
+ ref: forwardedRef
298
+ }) : /* @__PURE__ */jsx(DialogContentNonModal, {
284
299
  context,
285
- ref: composedRefs,
286
- trapFocus: context.open,
287
- disableOutsidePointerEvents: !0,
288
- onCloseAutoFocus: composeEventHandlers(props.onCloseAutoFocus, event => {
289
- event.preventDefault(), context.triggerRef.current?.focus();
290
- }),
291
- onPointerDownOutside: composeEventHandlers(props.onPointerDownOutside, event => {
292
- const originalEvent = event.detail.originalEvent,
293
- ctrlLeftClick = originalEvent.button === 0 && originalEvent.ctrlKey === !0;
294
- (originalEvent.button === 2 || ctrlLeftClick) && event.preventDefault();
295
- }),
296
- onFocusOutside: composeEventHandlers(props.onFocusOutside, event => event.preventDefault()),
297
- ...(!props.unstyled && {
298
- outlineStyle: "none"
299
- }),
300
- children
301
- });
302
- }),
303
- DialogContentNonModal = React.forwardRef((props, forwardedRef) => {
304
- const hasInteractedOutsideRef = React.useRef(!1);
305
- return /* @__PURE__ */jsx(DialogContentImpl, {
306
300
  ...props,
307
- ref: forwardedRef,
308
- trapFocus: !1,
309
- disableOutsidePointerEvents: !1,
310
- onCloseAutoFocus: event => {
311
- props.onCloseAutoFocus?.(event), event.defaultPrevented || (hasInteractedOutsideRef.current || props.context.triggerRef.current?.focus(), event.preventDefault()), hasInteractedOutsideRef.current = !1;
312
- },
313
- onInteractOutside: event => {
314
- props.onInteractOutside?.(event), event.defaultPrevented || (hasInteractedOutsideRef.current = !0);
315
- const target = event.target,
316
- trigger = props.context.triggerRef.current;
317
- if (!(trigger instanceof HTMLElement)) return;
318
- 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();
319
360
  }
320
- });
321
- }),
322
- DialogContentImpl = React.forwardRef((props, forwardedRef) => {
323
- const {
324
- trapFocus,
325
- onOpenAutoFocus,
326
- onCloseAutoFocus,
327
- disableOutsidePointerEvents,
328
- onEscapeKeyDown,
329
- onPointerDownOutside,
330
- onFocusOutside,
331
- onInteractOutside,
332
- context,
333
- ...contentProps
334
- } = props,
335
- contentRef = React.useRef(null),
336
- composedRefs = useComposedRefs(forwardedRef, contentRef);
337
- 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, {
338
396
  context,
339
397
  children: contentProps.children
340
398
  });
341
- const contents = /* @__PURE__ */jsx(DialogContentFrame, {
342
- ref: composedRefs,
343
- id: context.contentId,
344
- role: "dialog",
345
- "aria-modal": context.modal,
346
- "aria-describedby": context.descriptionId,
347
- "aria-labelledby": context.titleId,
348
- "data-state": getState(context.open),
349
- pointerEvents: context.open ? "auto" : "none",
350
- ...contentProps
351
- });
352
- return isWeb ? /* @__PURE__ */jsxs(Fragment, {
353
- children: [/* @__PURE__ */jsx(Dismissable, {
354
- 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,
355
428
  forceUnmount: !context.open,
356
- onEscapeKeyDown,
357
- onPointerDownOutside,
358
- onFocusOutside,
359
- onInteractOutside,
360
- onDismiss: () => context?.onOpenChange?.(!1),
361
- children: /* @__PURE__ */jsx(FocusScope, {
362
- loop: !0,
363
- enabled: context.open,
364
- trapped: trapFocus,
365
- onMountAutoFocus: onOpenAutoFocus,
366
- forceUnmount: !context.open,
367
- onUnmountAutoFocus: onCloseAutoFocus,
368
- children: contents
369
- })
370
- }), process.env.NODE_ENV === "development" && /* @__PURE__ */jsxs(Fragment, {
371
- children: [/* @__PURE__ */jsx(TitleWarning, {
372
- titleId: context.titleId
373
- }), /* @__PURE__ */jsx(DescriptionWarning, {
374
- contentRef,
375
- descriptionId: context.descriptionId
376
- })]
377
- })]
378
- }) : contents;
379
- }),
380
- DialogTitleFrame = styled(H2, {
381
- name: "DialogTitle"
382
- }),
383
- DialogTitle = DialogTitleFrame.styleable(function (props, forwardedRef) {
384
- const {
385
- scope,
386
- ...titleProps
387
- } = props,
388
- context = useDialogContext(scope);
389
- return /* @__PURE__ */jsx(DialogTitleFrame, {
390
- id: context.titleId,
391
- ...titleProps,
392
- ref: forwardedRef
393
- });
394
- }),
395
- DialogDescriptionFrame = styled(Paragraph, {
396
- name: "DialogDescription"
397
- }),
398
- DialogDescription = DialogDescriptionFrame.styleable(function (props, forwardedRef) {
399
- const {
400
- scope,
401
- ...descriptionProps
402
- } = props,
403
- context = useDialogContext(scope);
404
- return /* @__PURE__ */jsx(DialogDescriptionFrame, {
405
- id: context.descriptionId,
406
- ...descriptionProps,
407
- ref: forwardedRef
408
- });
409
- }),
410
- CLOSE_NAME = "DialogClose",
411
- DialogCloseFrame = styled(View, {
412
- name: CLOSE_NAME,
413
- render: "button"
414
- }),
415
- DialogClose = DialogCloseFrame.styleable((props, forwardedRef) => {
416
- const {
417
- scope,
418
- displayWhenAdapted,
419
- ...closeProps
420
- } = props,
421
- context = useDialogContext(scope),
422
- isAdapted = useAdaptIsActive(context.adaptScope),
423
- isInsideButton = React.useContext(ButtonNestingContext);
424
- return isAdapted && !displayWhenAdapted ? null : /* @__PURE__ */jsx(DialogCloseFrame, {
425
- "aria-label": "Dialog Close",
426
- render: isInsideButton ? "span" : "button",
427
- ...closeProps,
428
- ref: forwardedRef,
429
- onPress: composeEventHandlers(props.onPress, () => {
430
- context.onOpenChange(!1);
429
+ onUnmountAutoFocus: onCloseAutoFocus,
430
+ children: contents
431
431
  })
432
- });
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
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
433
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
+ });
434
499
  function getState(open) {
435
500
  return open ? "open" : "closed";
436
501
  }
437
- const TITLE_WARNING_NAME = "DialogTitleWarning",
438
- [DialogWarningProvider, useWarningContext] = createContext(TITLE_WARNING_NAME, {
439
- contentName: CONTENT_NAME,
440
- titleName: "DialogTitle",
441
- docsSlug: "dialog"
442
- }),
443
- TitleWarning = ({
444
- titleId
445
- }) => {
446
- if (process.env.NODE_ENV === "development") {
447
- const titleWarningContext = useWarningContext(TITLE_WARNING_NAME),
448
- MESSAGE = `\`${titleWarningContext.contentName}\` wants a \`${titleWarningContext.titleName}\` to be accessible. If you want to hide the \`${titleWarningContext.titleName}\`, wrap it with <VisuallyHidden />.`;
449
- React.useEffect(() => {
450
- isWeb && titleId && (document.getElementById(titleId) || console.warn(MESSAGE));
451
- }, [MESSAGE, titleId]);
452
- }
453
- return null;
454
- },
455
- DESCRIPTION_WARNING_NAME = "DialogDescriptionWarning",
456
- 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,
457
583
  contentRef,
458
- descriptionId
459
- }) => {
460
- if (process.env.NODE_ENV === "development") {
461
- const MESSAGE = `Warning: Missing \`Description\` or \`aria-describedby={undefined}\` for {${useWarningContext(DESCRIPTION_WARNING_NAME).contentName}}.`;
462
- React.useEffect(() => {
463
- if (!isWeb) return;
464
- const contentNode = contentRef.current;
465
- if (!(contentNode instanceof HTMLElement)) return;
466
- const describedById = contentNode.getAttribute("aria-describedby");
467
- descriptionId && describedById && (document.getElementById(descriptionId) || console.warn(MESSAGE));
468
- }, [MESSAGE, contentRef, descriptionId]);
469
- }
470
- return null;
471
- },
472
- Dialog = withStaticProperties(React.forwardRef(function (props, ref) {
473
- const {
474
- scope = "",
475
- children,
476
- open: openProp,
477
- defaultOpen = !1,
478
- onOpenChange,
479
- modal = !0,
480
- keepChildrenMounted,
481
- disableRemoveScroll = !1
482
- } = props,
483
- baseId = React.useId(),
484
- dialogId = `Dialog-${scope}-${baseId}`,
485
- contentId = `${dialogId}-content`,
486
- titleId = `${dialogId}-title`,
487
- descriptionId = `${dialogId}-description`,
488
- triggerRef = React.useRef(null),
489
- contentRef = React.useRef(null),
490
- [open, setOpen] = useControllableState({
491
- prop: openProp,
492
- defaultProp: defaultOpen,
493
- onChange: onOpenChange
494
- }),
495
- onOpenToggle = React.useCallback(() => {
496
- setOpen(prevOpen => !prevOpen);
497
- }, [setOpen]),
498
- adaptScope = `DialogAdapt${scope}`,
499
- context = {
500
- dialogScope: scope,
501
- adaptScope,
502
- triggerRef,
503
- contentRef,
504
- contentId,
505
- titleId,
506
- descriptionId,
507
- 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, {
508
607
  onOpenChange: setOpen,
509
- onOpenToggle,
510
- modal,
511
- keepChildrenMounted,
512
- disableRemoveScroll
513
- };
514
- return React.useImperativeHandle(ref, () => ({
515
- open: setOpen
516
- }), [setOpen]), /* @__PURE__ */jsx(AdaptParent, {
517
- scope: adaptScope,
518
- portal: {
519
- forwardProps: props
520
- },
521
- children: /* @__PURE__ */jsx(DialogProvider, {
522
608
  scope,
523
- ...context,
524
- children: /* @__PURE__ */jsx(DialogSheetController, {
525
- onOpenChange: setOpen,
526
- scope,
527
- children
528
- })
609
+ children
529
610
  })
530
- });
531
- }), {
532
- Trigger: DialogTrigger,
533
- Portal: DialogPortal,
534
- Overlay: DialogOverlay,
535
- Content: DialogContent,
536
- Title: DialogTitle,
537
- Description: DialogDescription,
538
- Close: DialogClose,
539
- FocusScope: FocusScopeController,
540
- Adapt
611
+ })
541
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
+ });
542
624
  const DialogSheetController = props => {
543
- const context = useDialogContext(props.scope),
544
- 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
+ }, []);
545
638
  return /* @__PURE__ */jsx(SheetController, {
546
639
  onOpenChange: val => {
547
- isAdapted && props.onOpenChange?.(val);
640
+ if (isAdapted) {
641
+ props.onOpenChange?.(val);
642
+ }
548
643
  },
644
+ onAnimationComplete: handleSheetAnimationComplete,
549
645
  open: context.open,
550
646
  hidden: !isAdapted,
551
- children: props.children
647
+ children: /* @__PURE__ */jsx(DialogAdaptHiddenContext.Provider, {
648
+ value: isAdaptFullyHidden,
649
+ children: props.children
650
+ })
552
651
  });
553
652
  };
554
653
  export { Dialog, DialogClose, DialogContent, DialogContext, DialogDescription, DialogOverlay, DialogOverlayFrame, DialogPortal, DialogPortalFrame, DialogProvider, DialogTitle, DialogTrigger, DialogWarningProvider, useDialogContext };