@tamagui/dialog 1.130.8 → 1.131.1

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/src/Dialog.tsx CHANGED
@@ -1,25 +1,31 @@
1
- import { Adapt, AdaptParent, AdaptPortalContents, useAdaptIsActive } from '@tamagui/adapt'
1
+ import {
2
+ Adapt,
3
+ AdaptParent,
4
+ AdaptPortalContents,
5
+ ProvideAdaptContext,
6
+ useAdaptContext,
7
+ useAdaptIsActive,
8
+ } from '@tamagui/adapt'
2
9
  import { AnimatePresence } from '@tamagui/animate-presence'
3
10
  import { hideOthers } from '@tamagui/aria-hidden'
4
11
  import { useComposedRefs } from '@tamagui/compose-refs'
5
- import { isWeb } from '@tamagui/constants'
6
- import type { GetProps, StackProps, TamaguiElement } from '@tamagui/core'
12
+ import { isAndroid, isIos, isWeb } from '@tamagui/constants'
13
+ import type { GetProps, TamaguiElement, ViewProps } from '@tamagui/core'
7
14
  import {
8
- Theme,
9
- View,
15
+ createStyledContext,
10
16
  getExpandedShorthand,
11
- spacedChildren,
12
17
  styled,
18
+ Theme,
13
19
  useThemeName,
20
+ View,
14
21
  } from '@tamagui/core'
15
- import type { Scope } from '@tamagui/create-context'
16
- import { createContext, createContextScope } from '@tamagui/create-context'
22
+ import { createContext } from '@tamagui/create-context'
17
23
  import type { DismissableProps } from '@tamagui/dismissable'
18
24
  import { Dismissable } from '@tamagui/dismissable'
19
25
  import type { FocusScopeProps } from '@tamagui/focus-scope'
20
26
  import { FocusScope, FocusScopeController } from '@tamagui/focus-scope'
21
27
  import { composeEventHandlers, withStaticProperties } from '@tamagui/helpers'
22
- import { Portal, PortalItem, resolveViewZIndex } from '@tamagui/portal'
28
+ import { Portal, PortalItem, resolveViewZIndex, USE_NATIVE_PORTAL } from '@tamagui/portal'
23
29
  import { RemoveScroll } from '@tamagui/remove-scroll'
24
30
  import { Overlay, Sheet, SheetController } from '@tamagui/sheet'
25
31
  import type { YStackProps } from '@tamagui/stacks'
@@ -29,15 +35,11 @@ import { useControllableState } from '@tamagui/use-controllable-state'
29
35
  import { StackZIndexContext } from '@tamagui/z-index-stack'
30
36
  import * as React from 'react'
31
37
 
32
- const DIALOG_NAME = 'Dialog'
33
-
34
- type ScopedProps<P> = P & { __scopeDialog?: Scope }
35
-
36
- const [createDialogContext, createDialogScope] = createContextScope(DIALOG_NAME)
38
+ export type DialogScopes = string
37
39
 
38
- type RemoveScrollProps = React.ComponentProps<typeof RemoveScroll>
40
+ type ScopedProps<P> = P & { scope?: DialogScopes }
39
41
 
40
- interface DialogProps {
42
+ type DialogProps = ScopedProps<{
41
43
  children?: React.ReactNode
42
44
  open?: boolean
43
45
  defaultOpen?: boolean
@@ -48,11 +50,12 @@ interface DialogProps {
48
50
  * Used to disable the remove scroll functionality when open
49
51
  */
50
52
  disableRemoveScroll?: boolean
51
- }
53
+ }>
52
54
 
53
55
  type NonNull<A> = Exclude<A, void | null>
54
56
 
55
57
  type DialogContextValue = {
58
+ forceMount?: boolean
56
59
  disableRemoveScroll?: boolean
57
60
  triggerRef: React.RefObject<TamaguiElement | null>
58
61
  contentRef: React.RefObject<TamaguiElement | null>
@@ -63,48 +66,51 @@ type DialogContextValue = {
63
66
  open: NonNull<DialogProps['open']>
64
67
  onOpenChange: NonNull<DialogProps['onOpenChange']>
65
68
  modal: NonNull<DialogProps['modal']>
66
- scopeKey: string
67
- adaptName: string
69
+ dialogScope: DialogScopes
70
+ adaptScope: string
68
71
  }
69
72
 
70
- const [DialogProvider, useDialogContext] =
71
- createDialogContext<DialogContextValue>(DIALOG_NAME)
73
+ export const DialogContext = createStyledContext<DialogContextValue>(
74
+ // since we always provide this we can avoid setting here
75
+ {} as DialogContextValue,
76
+ 'Dialog__'
77
+ )
78
+
79
+ export const { useStyledContext: useDialogContext, Provider: DialogProvider } =
80
+ DialogContext
72
81
 
73
82
  /* -------------------------------------------------------------------------------------------------
74
83
  * DialogTrigger
75
84
  * -----------------------------------------------------------------------------------------------*/
76
85
 
77
- const TRIGGER_NAME = 'DialogTrigger'
78
-
79
86
  const DialogTriggerFrame = styled(View, {
80
- name: TRIGGER_NAME,
87
+ name: 'DialogTrigger',
81
88
  })
82
89
 
83
- interface DialogTriggerProps extends StackProps {}
90
+ type DialogTriggerProps = ScopedProps<ViewProps>
84
91
 
85
- const DialogTrigger = DialogTriggerFrame.styleable(function DialogTrigger(
86
- props: ScopedProps<DialogTriggerProps>,
87
- forwardedRef
88
- ) {
89
- const { __scopeDialog, ...triggerProps } = props
90
- const isInsideButton = React.useContext(ButtonNestingContext)
91
- const context = useDialogContext(TRIGGER_NAME, __scopeDialog)
92
- const composedTriggerRef = useComposedRefs(forwardedRef, context.triggerRef)
93
- return (
94
- <ButtonNestingContext.Provider value={true}>
95
- <DialogTriggerFrame
96
- tag={isInsideButton ? 'span' : 'button'}
97
- aria-haspopup="dialog"
98
- aria-expanded={context.open}
99
- aria-controls={context.contentId}
100
- data-state={getState(context.open)}
101
- {...triggerProps}
102
- ref={composedTriggerRef}
103
- onPress={composeEventHandlers(props.onPress as any, context.onOpenToggle)}
104
- />
105
- </ButtonNestingContext.Provider>
106
- )
107
- })
92
+ const DialogTrigger = DialogTriggerFrame.styleable<ScopedProps<{}>>(
93
+ function DialogTrigger(props, forwardedRef) {
94
+ const { scope, ...triggerProps } = props
95
+ const isInsideButton = React.useContext(ButtonNestingContext)
96
+ const context = useDialogContext(scope)
97
+ const composedTriggerRef = useComposedRefs(forwardedRef, context.triggerRef)
98
+ return (
99
+ <ButtonNestingContext.Provider value={true}>
100
+ <DialogTriggerFrame
101
+ tag={isInsideButton ? 'span' : 'button'}
102
+ aria-haspopup="dialog"
103
+ aria-expanded={context.open}
104
+ aria-controls={context.contentId}
105
+ data-state={getState(context.open)}
106
+ {...triggerProps}
107
+ ref={composedTriggerRef}
108
+ onPress={composeEventHandlers(props.onPress as any, context.onOpenToggle)}
109
+ />
110
+ </ButtonNestingContext.Provider>
111
+ )
112
+ }
113
+ )
108
114
 
109
115
  /* -------------------------------------------------------------------------------------------------
110
116
  * DialogPortal
@@ -112,21 +118,15 @@ const DialogTrigger = DialogTriggerFrame.styleable(function DialogTrigger(
112
118
 
113
119
  const PORTAL_NAME = 'DialogPortal'
114
120
 
115
- type PortalContextValue = { forceMount?: true }
116
- const [PortalProvider, usePortalContext] = createDialogContext<PortalContextValue>(
117
- PORTAL_NAME,
118
- {
119
- forceMount: undefined,
121
+ type DialogPortalProps = ScopedProps<
122
+ YStackProps & {
123
+ /**
124
+ * Used to force mounting when more control is needed. Useful when
125
+ * controlling animation with React animation libraries.
126
+ */
127
+ forceMount?: true
120
128
  }
121
- )
122
-
123
- type DialogPortalProps = YStackProps & {
124
- /**
125
- * Used to force mounting when more control is needed. Useful when
126
- * controlling animation with React animation libraries.
127
- */
128
- forceMount?: true
129
- }
129
+ >
130
130
 
131
131
  export const DialogPortalFrame = styled(YStack, {
132
132
  pointerEvents: 'none',
@@ -150,37 +150,34 @@ export const DialogPortalFrame = styled(YStack, {
150
150
  },
151
151
  })
152
152
 
153
- const DialogPortalItem = (props: ScopedProps<DialogPortalProps>) => {
154
- const { __scopeDialog, children, space, spaceDirection, separator } = props
153
+ const needsRepropagation = isAndroid || (isIos && !USE_NATIVE_PORTAL)
155
154
 
155
+ const DialogPortalItem = ({
156
+ context,
157
+ children,
158
+ }: { context: DialogContextValue; children: React.ReactNode }) => {
156
159
  const themeName = useThemeName()
157
- const context = useDialogContext(PORTAL_NAME, props.__scopeDialog)
158
- const isAdapted = useAdaptIsActive()
159
-
160
- let childrenSpaced = children
161
-
162
- if (space || separator) {
163
- childrenSpaced = spacedChildren({
164
- children,
165
- separator,
166
- space,
167
- direction: spaceDirection,
168
- })
169
- }
160
+ const isAdapted = useAdaptIsActive(context.adaptScope)
161
+ const adaptContext = useAdaptContext(context.adaptScope)
170
162
 
171
- const content = (
172
- <DialogProvider scope={__scopeDialog} {...context}>
173
- <Theme name={themeName}>{childrenSpaced}</Theme>
174
- </DialogProvider>
175
- )
163
+ let content = <Theme name={themeName}>{children}</Theme>
164
+
165
+ // not just adapted - both sheet and portal for modal need it
166
+ if (needsRepropagation) {
167
+ content = (
168
+ <ProvideAdaptContext {...adaptContext}>
169
+ <DialogProvider {...context}>{content}</DialogProvider>
170
+ </ProvideAdaptContext>
171
+ )
172
+ }
176
173
 
177
174
  // until we can use react-native portals natively
178
175
  // have to re-propogate context, sketch
179
176
  // when adapted we portal to the adapt, when not we portal to root modal if needed
180
177
  return isAdapted ? (
181
- <AdaptPortalContents>{content}</AdaptPortalContents>
178
+ <AdaptPortalContents scope={context.adaptScope}>{content}</AdaptPortalContents>
182
179
  ) : context.modal ? (
183
- <PortalItem hostName={context.modal ? 'root' : context.adaptName}>
180
+ <PortalItem hostName={context.modal ? 'root' : context.adaptScope}>
184
181
  {content}
185
182
  </PortalItem>
186
183
  ) : (
@@ -188,15 +185,13 @@ const DialogPortalItem = (props: ScopedProps<DialogPortalProps>) => {
188
185
  )
189
186
  }
190
187
 
191
- const DialogPortal: React.FC<DialogPortalProps> = (
192
- props: ScopedProps<DialogPortalProps>
193
- ) => {
194
- const { __scopeDialog, forceMount, children, ...frameProps } = props
188
+ const DialogPortal: React.FC<DialogPortalProps> = (props) => {
189
+ const { scope, forceMount, children, ...frameProps } = props
195
190
 
196
- const context = useDialogContext(PORTAL_NAME, __scopeDialog)
191
+ const context = useDialogContext(scope)
197
192
  const isShowing = forceMount || context.open
198
193
  const [isFullyHidden, setIsFullyHidden] = React.useState(!isShowing)
199
- const isAdapted = useAdaptIsActive()
194
+ const isAdapted = useAdaptIsActive(context.adaptScope)
200
195
 
201
196
  if (isShowing && isFullyHidden) {
202
197
  setIsFullyHidden(false)
@@ -210,7 +205,7 @@ const DialogPortal: React.FC<DialogPortalProps> = (
210
205
 
211
206
  const contents = (
212
207
  <StackZIndexContext zIndex={resolveViewZIndex(zIndex)}>
213
- <AnimatePresence onExitComplete={handleExitComplete}>
208
+ <AnimatePresence passThrough={isAdapted} onExitComplete={handleExitComplete}>
214
209
  {isShowing || isAdapted ? children : null}
215
210
  </AnimatePresence>
216
211
  </StackZIndexContext>
@@ -221,11 +216,13 @@ const DialogPortal: React.FC<DialogPortalProps> = (
221
216
  }
222
217
 
223
218
  const framedContents = (
224
- <PortalProvider scope={__scopeDialog} forceMount={forceMount}>
225
- <DialogPortalFrame pointerEvents={isShowing ? 'auto' : 'none'} {...frameProps}>
226
- {contents}
227
- </DialogPortalFrame>
228
- </PortalProvider>
219
+ <DialogPortalFrame
220
+ // passThrough={isAdapted}
221
+ pointerEvents={isShowing ? 'auto' : 'none'}
222
+ {...frameProps}
223
+ >
224
+ {contents}
225
+ </DialogPortalFrame>
229
226
  )
230
227
 
231
228
  if (isWeb) {
@@ -235,8 +232,9 @@ const DialogPortal: React.FC<DialogPortalProps> = (
235
232
  // set to 1000 which "boosts" it 1000 above baseline for current context
236
233
  // this makes sure its above (this first 1k) popovers on the same layer
237
234
  stackZIndex={1000}
235
+ passThrough={isAdapted}
238
236
  >
239
- <PassthroughTheme>{framedContents}</PassthroughTheme>
237
+ <PassthroughTheme passThrough={isAdapted}>{framedContents}</PassthroughTheme>
240
238
  </Portal>
241
239
  )
242
240
  }
@@ -244,15 +242,21 @@ const DialogPortal: React.FC<DialogPortalProps> = (
244
242
  return isAdapted ? (
245
243
  framedContents
246
244
  ) : (
247
- <DialogPortalItem __scopeDialog={__scopeDialog}>{framedContents}</DialogPortalItem>
245
+ <DialogPortalItem context={context}>{framedContents}</DialogPortalItem>
248
246
  )
249
247
  }
250
248
 
251
- const PassthroughTheme = ({ children }) => {
249
+ const PassthroughTheme = ({
250
+ children,
251
+ passThrough,
252
+ }: {
253
+ passThrough?: boolean
254
+ children?: React.ReactNode
255
+ }) => {
252
256
  const themeName = useThemeName()
253
257
 
254
258
  return (
255
- <Theme name={themeName} forceClassName>
259
+ <Theme passThrough={passThrough} name={themeName} forceClassName>
256
260
  {children}
257
261
  </Theme>
258
262
  )
@@ -271,23 +275,21 @@ export const DialogOverlayFrame = styled(Overlay, {
271
275
  name: OVERLAY_NAME,
272
276
  })
273
277
 
274
- interface DialogOverlayProps extends YStackProps {
278
+ export type DialogOverlayExtraProps = ScopedProps<{
275
279
  /**
276
280
  * Used to force mounting when more control is needed. Useful when
277
281
  * controlling animation with React animation libraries.
278
282
  */
279
283
  forceMount?: true
280
- }
284
+ }>
285
+
286
+ type DialogOverlayProps = YStackProps & DialogOverlayExtraProps
281
287
 
282
- const DialogOverlay = DialogOverlayFrame.extractable(
283
- React.forwardRef<TamaguiElement, DialogOverlayProps>(function DialogOverlay(
284
- { __scopeDialog, ...props }: ScopedProps<DialogOverlayProps>,
285
- forwardedRef
286
- ) {
287
- const portalContext = usePortalContext(OVERLAY_NAME, __scopeDialog)
288
- const { forceMount = portalContext.forceMount, ...overlayProps } = props
289
- const context = useDialogContext(OVERLAY_NAME, __scopeDialog)
290
- const isAdapted = useAdaptIsActive()
288
+ const DialogOverlay = DialogOverlayFrame.styleable<DialogOverlayExtraProps>(
289
+ function DialogOverlay({ scope, ...props }, forwardedRef) {
290
+ const context = useDialogContext(scope)
291
+ const { forceMount = context.forceMount, ...overlayProps } = props
292
+ const isAdapted = useAdaptIsActive(context.adaptScope)
291
293
 
292
294
  if (!forceMount) {
293
295
  if (!context.modal || isAdapted) {
@@ -311,7 +313,7 @@ const DialogOverlay = DialogOverlayFrame.extractable(
311
313
  ref={forwardedRef}
312
314
  />
313
315
  )
314
- })
316
+ }
315
317
  )
316
318
 
317
319
  /* -------------------------------------------------------------------------------------------------
@@ -351,24 +353,22 @@ const DialogContentFrame = styled(ThemeableStack, {
351
353
 
352
354
  type DialogContentFrameProps = GetProps<typeof DialogContentFrame>
353
355
 
354
- interface DialogContentProps
355
- extends DialogContentFrameProps,
356
- Omit<DialogContentTypeProps, 'context' | 'onPointerDownCapture'> {
357
- /**
358
- * Used to force mounting when more control is needed. Useful when
359
- * controlling animation with React animation libraries.
360
- */
361
- forceMount?: true
362
- }
356
+ type DialogContentExtraProps = ScopedProps<
357
+ Omit<DialogContentTypeProps, 'context' | 'onPointerDownCapture'> & {
358
+ /**
359
+ * Used to force mounting when more control is needed. Useful when
360
+ * controlling animation with React animation libraries.
361
+ */
362
+ forceMount?: true
363
+ }
364
+ >
363
365
 
364
- const DialogContent = DialogContentFrame.extractable(
365
- React.forwardRef<TamaguiElement, DialogContentProps>(function DialogContent(
366
- { __scopeDialog, ...props }: ScopedProps<DialogContentProps>,
367
- forwardedRef
368
- ) {
369
- const portalContext = usePortalContext(CONTENT_NAME, __scopeDialog)
370
- const { forceMount = portalContext.forceMount, ...contentProps } = props
371
- const context = useDialogContext(CONTENT_NAME, __scopeDialog)
366
+ type DialogContentProps = DialogContentFrameProps & DialogContentExtraProps
367
+
368
+ const DialogContent = DialogContentFrame.styleable<DialogContentExtraProps>(
369
+ function DialogContent({ scope, ...props }, forwardedRef) {
370
+ const context = useDialogContext(scope)
371
+ const { forceMount = context.forceMount, ...contentProps } = props
372
372
 
373
373
  const contents = (
374
374
  <>
@@ -391,20 +391,17 @@ const DialogContent = DialogContentFrame.extractable(
391
391
  </div>
392
392
  </RemoveScroll>
393
393
  )
394
- })
394
+ }
395
395
  )
396
396
 
397
397
  /* -----------------------------------------------------------------------------------------------*/
398
398
 
399
- interface DialogContentTypeProps extends DialogContentImplProps {
399
+ type DialogContentTypeProps = DialogContentImplProps & {
400
400
  context: DialogContextValue
401
401
  }
402
402
 
403
403
  const DialogContentModal = React.forwardRef<TamaguiElement, DialogContentTypeProps>(
404
- (
405
- { children, context, ...props }: ScopedProps<DialogContentTypeProps>,
406
- forwardedRef
407
- ) => {
404
+ ({ children, context, ...props }, forwardedRef) => {
408
405
  const contentRef = React.useRef<HTMLDivElement>(null)
409
406
  const composedRefs = useComposedRefs(forwardedRef, context.contentRef, contentRef)
410
407
 
@@ -459,7 +456,7 @@ const DialogContentModal = React.forwardRef<TamaguiElement, DialogContentTypePro
459
456
  /* -----------------------------------------------------------------------------------------------*/
460
457
 
461
458
  const DialogContentNonModal = React.forwardRef<TamaguiElement, DialogContentTypeProps>(
462
- (props: ScopedProps<DialogContentTypeProps>, forwardedRef) => {
459
+ (props, forwardedRef) => {
463
460
  const hasInteractedOutsideRef = React.useRef(false)
464
461
 
465
462
  return (
@@ -505,34 +502,34 @@ const DialogContentNonModal = React.forwardRef<TamaguiElement, DialogContentType
505
502
 
506
503
  /* -----------------------------------------------------------------------------------------------*/
507
504
 
508
- type DialogContentImplProps = DialogContentFrameProps &
509
- Omit<DismissableProps, 'onDismiss'> & {
510
- /**
511
- * When `true`, focus cannot escape the `Content` via keyboard,
512
- * pointer, or a programmatic focus.
513
- * @defaultValue false
514
- */
515
- trapFocus?: FocusScopeProps['trapped']
505
+ type DialogContentImplExtraProps = Omit<DismissableProps, 'onDismiss'> & {
506
+ /**
507
+ * When `true`, focus cannot escape the `Content` via keyboard,
508
+ * pointer, or a programmatic focus.
509
+ * @defaultValue false
510
+ */
511
+ trapFocus?: FocusScopeProps['trapped']
516
512
 
517
- /**
518
- * Event handler called when auto-focusing on open.
519
- * Can be prevented.
520
- */
521
- onOpenAutoFocus?: FocusScopeProps['onMountAutoFocus']
513
+ /**
514
+ * Event handler called when auto-focusing on open.
515
+ * Can be prevented.
516
+ */
517
+ onOpenAutoFocus?: FocusScopeProps['onMountAutoFocus']
522
518
 
523
- /**
524
- * Event handler called when auto-focusing on close.
525
- * Can be prevented.
526
- */
527
- onCloseAutoFocus?: FocusScopeProps['onUnmountAutoFocus']
519
+ /**
520
+ * Event handler called when auto-focusing on close.
521
+ * Can be prevented.
522
+ */
523
+ onCloseAutoFocus?: FocusScopeProps['onUnmountAutoFocus']
528
524
 
529
- context: DialogContextValue
530
- }
525
+ context: DialogContextValue
526
+ }
527
+
528
+ type DialogContentImplProps = DialogContentFrameProps & DialogContentImplExtraProps
531
529
 
532
530
  const DialogContentImpl = React.forwardRef<TamaguiElement, DialogContentImplProps>(
533
- (props: ScopedProps<DialogContentImplProps>, forwardedRef) => {
531
+ (props, forwardedRef) => {
534
532
  const {
535
- __scopeDialog,
536
533
  trapFocus,
537
534
  onOpenAutoFocus,
538
535
  onCloseAutoFocus,
@@ -550,7 +547,7 @@ const DialogContentImpl = React.forwardRef<TamaguiElement, DialogContentImplProp
550
547
  undefined as unknown as HTMLDivElement
551
548
  )
552
549
  const composedRefs = useComposedRefs(forwardedRef, contentRef)
553
- const isAdapted = useAdaptIsActive()
550
+ const isAdapted = useAdaptIsActive(context.adaptScope)
554
551
 
555
552
  // TODO this will re-parent, ideally we would not change tree structure
556
553
 
@@ -559,7 +556,9 @@ const DialogContentImpl = React.forwardRef<TamaguiElement, DialogContentImplProp
559
556
  return null
560
557
  }
561
558
 
562
- return <DialogPortalItem>{contentProps.children}</DialogPortalItem>
559
+ return (
560
+ <DialogPortalItem context={context}>{contentProps.children}</DialogPortalItem>
561
+ )
563
562
  }
564
563
 
565
564
  const contents = (
@@ -600,6 +599,7 @@ const DialogContentImpl = React.forwardRef<TamaguiElement, DialogContentImplProp
600
599
  {contents}
601
600
  </FocusScope>
602
601
  </Dismissable>
602
+
603
603
  {process.env.NODE_ENV === 'development' && (
604
604
  <>
605
605
  <TitleWarning titleId={context.titleId} />
@@ -622,16 +622,16 @@ const DialogTitleFrame = styled(H2, {
622
622
  name: 'DialogTitle',
623
623
  })
624
624
 
625
- type DialogTitleProps = GetProps<typeof DialogTitleFrame>
625
+ type DialogTitleExtraProps = ScopedProps<{}>
626
+ type DialogTitleProps = DialogTitleExtraProps & GetProps<typeof DialogTitleFrame>
626
627
 
627
- const DialogTitle = DialogTitleFrame.styleable(function DialogTitle(
628
- props: ScopedProps<DialogTitleProps>,
629
- forwardedRef
630
- ) {
631
- const { __scopeDialog, ...titleProps } = props
632
- const context = useDialogContext('DialogTitle', __scopeDialog)
633
- return <DialogTitleFrame id={context.titleId} {...titleProps} ref={forwardedRef} />
634
- })
628
+ const DialogTitle = DialogTitleFrame.styleable<DialogTitleExtraProps>(
629
+ function DialogTitle(props, forwardedRef) {
630
+ const { scope, ...titleProps } = props
631
+ const context = useDialogContext(scope)
632
+ return <DialogTitleFrame id={context.titleId} {...titleProps} ref={forwardedRef} />
633
+ }
634
+ )
635
635
 
636
636
  /* -------------------------------------------------------------------------------------------------
637
637
  * DialogDescription
@@ -641,24 +641,23 @@ const DialogDescriptionFrame = styled(Paragraph, {
641
641
  name: 'DialogDescription',
642
642
  })
643
643
 
644
- type DialogDescriptionProps = GetProps<typeof DialogDescriptionFrame>
645
-
646
- const DESCRIPTION_NAME = 'DialogDescription'
644
+ type DialogDescriptionExtraProps = ScopedProps<{}>
645
+ type DialogDescriptionProps = DialogDescriptionExtraProps &
646
+ GetProps<typeof DialogDescriptionFrame>
647
647
 
648
- const DialogDescription = DialogDescriptionFrame.styleable(function DialogDescription(
649
- props: ScopedProps<DialogDescriptionProps>,
650
- forwardedRef
651
- ) {
652
- const { __scopeDialog, ...descriptionProps } = props
653
- const context = useDialogContext(DESCRIPTION_NAME, __scopeDialog)
654
- return (
655
- <DialogDescriptionFrame
656
- id={context.descriptionId}
657
- {...descriptionProps}
658
- ref={forwardedRef}
659
- />
660
- )
661
- })
648
+ const DialogDescription = DialogDescriptionFrame.styleable<DialogDescriptionExtraProps>(
649
+ function DialogDescription(props, forwardedRef) {
650
+ const { scope, ...descriptionProps } = props
651
+ const context = useDialogContext(scope)
652
+ return (
653
+ <DialogDescriptionFrame
654
+ id={context.descriptionId}
655
+ {...descriptionProps}
656
+ ref={forwardedRef}
657
+ />
658
+ )
659
+ }
660
+ )
662
661
 
663
662
  /* -------------------------------------------------------------------------------------------------
664
663
  * DialogClose
@@ -671,20 +670,17 @@ const DialogCloseFrame = styled(View, {
671
670
  tag: 'button',
672
671
  })
673
672
 
674
- export interface DialogCloseExtraProps {
673
+ export type DialogCloseExtraProps = ScopedProps<{
675
674
  displayWhenAdapted?: boolean
676
- }
675
+ }>
677
676
 
678
677
  type DialogCloseProps = GetProps<typeof DialogCloseFrame> & DialogCloseExtraProps
679
678
 
680
679
  const DialogClose = DialogCloseFrame.styleable<DialogCloseExtraProps>(
681
- (props: ScopedProps<DialogCloseProps>, forwardedRef) => {
682
- const { __scopeDialog, displayWhenAdapted, ...closeProps } = props
683
- const context = useDialogContext(CLOSE_NAME, __scopeDialog, {
684
- warn: false,
685
- fallback: {},
686
- })
687
- const isAdapted = useAdaptIsActive()
680
+ (props, forwardedRef) => {
681
+ const { scope, displayWhenAdapted, ...closeProps } = props
682
+ const context = useDialogContext(scope)
683
+ const isAdapted = useAdaptIsActive(context.adaptScope)
688
684
  const isInsideButton = React.useContext(ButtonNestingContext)
689
685
 
690
686
  if (isAdapted && !displayWhenAdapted) {
@@ -785,79 +781,78 @@ export type DialogHandle = {
785
781
  }
786
782
 
787
783
  const Dialog = withStaticProperties(
788
- React.forwardRef<{ open: (val: boolean) => void }, DialogProps>(function Dialog(
789
- props: ScopedProps<DialogProps>,
790
- ref
791
- ) {
792
- const {
793
- __scopeDialog,
794
- children,
795
- open: openProp,
796
- defaultOpen = false,
797
- onOpenChange,
798
- modal = true,
799
- disableRemoveScroll = false,
800
- } = props
801
-
802
- const baseId = React.useId()
803
- const scopeId = `scope-${baseId}`
804
- const contentId = `content-${baseId}`
805
- const titleId = `title-${baseId}`
806
- const descriptionId = `description-${baseId}`
807
- const scopeKey = __scopeDialog ? Object.keys(__scopeDialog)[0] : scopeId
808
- const adaptName = getAdaptName({ scopeKey, contentId })
809
- const triggerRef = React.useRef<HTMLButtonElement>(null)
810
- const contentRef = React.useRef<TamaguiElement>(null)
811
-
812
- const [open, setOpen] = useControllableState({
813
- prop: openProp,
814
- defaultProp: defaultOpen,
815
- onChange: onOpenChange,
816
- })
817
-
818
- const onOpenToggle = React.useCallback(() => {
819
- setOpen((prevOpen) => !prevOpen)
820
- }, [setOpen])
821
-
822
- const context = {
823
- scope: __scopeDialog,
824
- scopeKey,
825
- triggerRef,
826
- contentRef,
827
- contentId,
828
- titleId,
829
- descriptionId,
830
- open,
831
- onOpenChange: setOpen,
832
- onOpenToggle,
833
- modal,
834
- disableRemoveScroll,
835
- adaptName,
784
+ React.forwardRef<{ open: (val: boolean) => void }, DialogProps>(
785
+ function Dialog(props, ref) {
786
+ const {
787
+ scope = '',
788
+ children,
789
+ open: openProp,
790
+ defaultOpen = false,
791
+ onOpenChange,
792
+ modal = true,
793
+ disableRemoveScroll = false,
794
+ } = props
795
+
796
+ const baseId = React.useId()
797
+ const dialogId = `Dialog-${scope}-${baseId}`
798
+ const contentId = `${dialogId}-content`
799
+ const titleId = `${dialogId}-title`
800
+ const descriptionId = `${dialogId}-description`
801
+
802
+ const triggerRef = React.useRef<HTMLButtonElement>(null)
803
+ const contentRef = React.useRef<TamaguiElement>(null)
804
+
805
+ const [open, setOpen] = useControllableState({
806
+ prop: openProp,
807
+ defaultProp: defaultOpen,
808
+ onChange: onOpenChange,
809
+ })
810
+
811
+ const onOpenToggle = React.useCallback(() => {
812
+ setOpen((prevOpen) => !prevOpen)
813
+ }, [setOpen])
814
+
815
+ const adaptScope = `DialogAdapt${scope}`
816
+
817
+ const context = {
818
+ dialogScope: scope,
819
+ adaptScope,
820
+ triggerRef,
821
+ contentRef,
822
+ contentId,
823
+ titleId,
824
+ descriptionId,
825
+ open,
826
+ onOpenChange: setOpen,
827
+ onOpenToggle,
828
+ modal,
829
+ disableRemoveScroll,
830
+ } satisfies DialogContextValue
831
+
832
+ React.useImperativeHandle(
833
+ ref,
834
+ () => ({
835
+ open: setOpen,
836
+ }),
837
+ [setOpen]
838
+ )
839
+
840
+ return (
841
+ <AdaptParent
842
+ scope={adaptScope}
843
+ portal={{
844
+ forwardProps: props,
845
+ }}
846
+ >
847
+ <DialogProvider scope={scope} {...context}>
848
+ <DialogSheetController onOpenChange={setOpen} scope={scope}>
849
+ {children}
850
+ </DialogSheetController>
851
+ </DialogProvider>
852
+ </AdaptParent>
853
+ )
836
854
  }
837
-
838
- React.useImperativeHandle(
839
- ref,
840
- () => ({
841
- open: setOpen,
842
- }),
843
- [setOpen]
844
- )
845
-
846
- return (
847
- <AdaptParent
848
- scope={adaptName}
849
- portal={{
850
- forwardProps: props,
851
- }}
852
- >
853
- <DialogProvider {...context}>
854
- <DialogSheetController onOpenChange={setOpen} __scopeDialog={__scopeDialog}>
855
- {children}
856
- </DialogSheetController>
857
- </DialogProvider>
858
- </AdaptParent>
859
- )
860
- }),
855
+ ),
861
856
  {
862
857
  Trigger: DialogTrigger,
863
858
  Portal: DialogPortal,
@@ -872,11 +867,7 @@ const Dialog = withStaticProperties(
872
867
  }
873
868
  )
874
869
 
875
- const getAdaptName = ({
876
- scopeKey,
877
- contentId,
878
- }: Pick<DialogContextValue, 'scopeKey' | 'contentId'>) =>
879
- `${scopeKey || contentId}DialogAdapt`
870
+ const getAdaptScope = (dialogScope: string) => `DialogAdapt${dialogScope}`
880
871
 
881
872
  const DialogSheetController = (
882
873
  props: ScopedProps<{
@@ -884,8 +875,8 @@ const DialogSheetController = (
884
875
  onOpenChange: React.Dispatch<React.SetStateAction<boolean>>
885
876
  }>
886
877
  ) => {
887
- const context = useDialogContext('DialogSheetController', props.__scopeDialog)
888
- const isAdapted = useAdaptIsActive()
878
+ const context = useDialogContext(props.scope)
879
+ const isAdapted = useAdaptIsActive(context.adaptScope)
889
880
 
890
881
  return (
891
882
  <SheetController
@@ -914,7 +905,6 @@ export {
914
905
  DialogTrigger,
915
906
  //
916
907
  DialogWarningProvider,
917
- createDialogScope,
918
908
  }
919
909
  export type {
920
910
  DialogCloseProps,