@tamagui/dialog 1.130.7 → 1.131.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.
- package/dist/cjs/Dialog.cjs +94 -113
- package/dist/cjs/Dialog.js +133 -123
- package/dist/cjs/Dialog.js.map +1 -1
- package/dist/cjs/Dialog.native.js +64 -67
- package/dist/cjs/Dialog.native.js.map +2 -2
- package/dist/cjs/index.cjs +1 -1
- package/dist/esm/Dialog.js +148 -131
- package/dist/esm/Dialog.js.map +1 -1
- package/dist/esm/Dialog.mjs +97 -118
- package/dist/esm/Dialog.mjs.map +1 -1
- package/dist/esm/Dialog.native.js +99 -121
- package/dist/esm/Dialog.native.js.map +1 -1
- package/dist/jsx/Dialog.js +148 -131
- package/dist/jsx/Dialog.js.map +1 -1
- package/dist/jsx/Dialog.mjs +97 -118
- package/dist/jsx/Dialog.mjs.map +1 -1
- package/dist/jsx/Dialog.native.js +66 -71
- package/dist/jsx/Dialog.native.js.map +2 -2
- package/package.json +21 -21
- package/src/Dialog.tsx +265 -275
- package/types/Dialog.d.ts +390 -41
- package/types/Dialog.d.ts.map +1 -1
package/src/Dialog.tsx
CHANGED
|
@@ -1,25 +1,31 @@
|
|
|
1
|
-
import {
|
|
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,
|
|
12
|
+
import { isAndroid, isIos, isWeb } from '@tamagui/constants'
|
|
13
|
+
import type { GetProps, TamaguiElement, ViewProps } from '@tamagui/core'
|
|
7
14
|
import {
|
|
8
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
40
|
+
type ScopedProps<P> = P & { scope?: DialogScopes }
|
|
39
41
|
|
|
40
|
-
|
|
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
|
-
|
|
67
|
-
|
|
69
|
+
dialogScope: DialogScopes
|
|
70
|
+
adaptScope: string
|
|
68
71
|
}
|
|
69
72
|
|
|
70
|
-
const
|
|
71
|
-
|
|
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:
|
|
87
|
+
name: 'DialogTrigger',
|
|
81
88
|
})
|
|
82
89
|
|
|
83
|
-
|
|
90
|
+
type DialogTriggerProps = ScopedProps<ViewProps>
|
|
84
91
|
|
|
85
|
-
const DialogTrigger = DialogTriggerFrame.styleable(
|
|
86
|
-
props
|
|
87
|
-
|
|
88
|
-
)
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
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
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
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
|
|
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
|
|
158
|
-
const
|
|
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
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
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.
|
|
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
|
|
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(
|
|
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
|
-
<
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
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
|
|
245
|
+
<DialogPortalItem context={context}>{framedContents}</DialogPortalItem>
|
|
248
246
|
)
|
|
249
247
|
}
|
|
250
248
|
|
|
251
|
-
const PassthroughTheme = ({
|
|
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
|
-
|
|
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.
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
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
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
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
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
const
|
|
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
|
-
|
|
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
|
|
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
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
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
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
513
|
+
/**
|
|
514
|
+
* Event handler called when auto-focusing on open.
|
|
515
|
+
* Can be prevented.
|
|
516
|
+
*/
|
|
517
|
+
onOpenAutoFocus?: FocusScopeProps['onMountAutoFocus']
|
|
522
518
|
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
519
|
+
/**
|
|
520
|
+
* Event handler called when auto-focusing on close.
|
|
521
|
+
* Can be prevented.
|
|
522
|
+
*/
|
|
523
|
+
onCloseAutoFocus?: FocusScopeProps['onUnmountAutoFocus']
|
|
528
524
|
|
|
529
|
-
|
|
530
|
-
|
|
525
|
+
context: DialogContextValue
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
type DialogContentImplProps = DialogContentFrameProps & DialogContentImplExtraProps
|
|
531
529
|
|
|
532
530
|
const DialogContentImpl = React.forwardRef<TamaguiElement, DialogContentImplProps>(
|
|
533
|
-
(props
|
|
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
|
|
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
|
|
625
|
+
type DialogTitleExtraProps = ScopedProps<{}>
|
|
626
|
+
type DialogTitleProps = DialogTitleExtraProps & GetProps<typeof DialogTitleFrame>
|
|
626
627
|
|
|
627
|
-
const DialogTitle = DialogTitleFrame.styleable(
|
|
628
|
-
props
|
|
629
|
-
|
|
630
|
-
)
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
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
|
|
645
|
-
|
|
646
|
-
|
|
644
|
+
type DialogDescriptionExtraProps = ScopedProps<{}>
|
|
645
|
+
type DialogDescriptionProps = DialogDescriptionExtraProps &
|
|
646
|
+
GetProps<typeof DialogDescriptionFrame>
|
|
647
647
|
|
|
648
|
-
const DialogDescription = DialogDescriptionFrame.styleable(
|
|
649
|
-
props
|
|
650
|
-
|
|
651
|
-
)
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
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
|
|
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
|
|
682
|
-
const {
|
|
683
|
-
const context = useDialogContext(
|
|
684
|
-
|
|
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>(
|
|
789
|
-
props
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
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
|
|
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(
|
|
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,
|