@oxyhq/bloom 0.15.0 → 0.16.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/lib/commonjs/alert-dialog/AlertDialog.js +35 -27
- package/lib/commonjs/alert-dialog/AlertDialog.js.map +1 -1
- package/lib/commonjs/alert-dialog/index.js +1 -1
- package/lib/commonjs/alert-dialog/index.js.map +1 -1
- package/lib/commonjs/alert-dialog/index.web.js +1 -1
- package/lib/commonjs/alert-dialog/index.web.js.map +1 -1
- package/lib/commonjs/command/Command.js +27 -16
- package/lib/commonjs/command/Command.js.map +1 -1
- package/lib/commonjs/command/index.js +1 -1
- package/lib/commonjs/command/index.js.map +1 -1
- package/lib/commonjs/command/index.web.js +1 -1
- package/lib/commonjs/command/index.web.js.map +1 -1
- package/lib/commonjs/dialog/Dialog.js +39 -152
- package/lib/commonjs/dialog/Dialog.js.map +1 -1
- package/lib/commonjs/dialog/Dialog.web.js +61 -196
- package/lib/commonjs/dialog/Dialog.web.js.map +1 -1
- package/lib/commonjs/dialog/DialogBottomSheet.js +205 -0
- package/lib/commonjs/dialog/DialogBottomSheet.js.map +1 -0
- package/lib/commonjs/dialog/DialogContent.js +155 -0
- package/lib/commonjs/dialog/DialogContent.js.map +1 -0
- package/lib/commonjs/dialog/index.js +0 -13
- package/lib/commonjs/dialog/index.js.map +1 -1
- package/lib/commonjs/dialog/index.web.js +0 -19
- package/lib/commonjs/dialog/index.web.js.map +1 -1
- package/lib/commonjs/index.js +0 -29
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/index.web.js +6 -35
- package/lib/commonjs/index.web.js.map +1 -1
- package/lib/module/alert-dialog/AlertDialog.js +35 -27
- package/lib/module/alert-dialog/AlertDialog.js.map +1 -1
- package/lib/module/alert-dialog/index.js +2 -2
- package/lib/module/alert-dialog/index.js.map +1 -1
- package/lib/module/alert-dialog/index.web.js +2 -2
- package/lib/module/alert-dialog/index.web.js.map +1 -1
- package/lib/module/command/Command.js +27 -16
- package/lib/module/command/Command.js.map +1 -1
- package/lib/module/command/index.js +2 -2
- package/lib/module/command/index.js.map +1 -1
- package/lib/module/command/index.web.js +2 -2
- package/lib/module/command/index.web.js.map +1 -1
- package/lib/module/dialog/Dialog.js +42 -155
- package/lib/module/dialog/Dialog.js.map +1 -1
- package/lib/module/dialog/Dialog.web.js +63 -198
- package/lib/module/dialog/Dialog.web.js.map +1 -1
- package/lib/module/dialog/DialogBottomSheet.js +200 -0
- package/lib/module/dialog/DialogBottomSheet.js.map +1 -0
- package/lib/module/dialog/DialogContent.js +148 -0
- package/lib/module/dialog/DialogContent.js.map +1 -0
- package/lib/module/dialog/index.js +0 -1
- package/lib/module/dialog/index.js.map +1 -1
- package/lib/module/dialog/index.web.js +0 -1
- package/lib/module/dialog/index.web.js.map +1 -1
- package/lib/module/index.js +1 -3
- package/lib/module/index.js.map +1 -1
- package/lib/module/index.web.js +1 -3
- package/lib/module/index.web.js.map +1 -1
- package/lib/typescript/commonjs/alert-dialog/AlertDialog.d.ts +13 -12
- package/lib/typescript/commonjs/alert-dialog/AlertDialog.d.ts.map +1 -1
- package/lib/typescript/commonjs/alert-dialog/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/alert-dialog/index.web.d.ts.map +1 -1
- package/lib/typescript/commonjs/command/Command.d.ts +8 -8
- package/lib/typescript/commonjs/command/Command.d.ts.map +1 -1
- package/lib/typescript/commonjs/command/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/command/index.web.d.ts.map +1 -1
- package/lib/typescript/commonjs/dialog/Dialog.d.ts +2 -3
- package/lib/typescript/commonjs/dialog/Dialog.d.ts.map +1 -1
- package/lib/typescript/commonjs/dialog/Dialog.web.d.ts +11 -8
- package/lib/typescript/commonjs/dialog/Dialog.web.d.ts.map +1 -1
- package/lib/typescript/commonjs/dialog/DialogBottomSheet.d.ts +29 -0
- package/lib/typescript/commonjs/dialog/DialogBottomSheet.d.ts.map +1 -0
- package/lib/typescript/commonjs/dialog/DialogContent.d.ts +32 -0
- package/lib/typescript/commonjs/dialog/DialogContent.d.ts.map +1 -0
- package/lib/typescript/commonjs/dialog/index.d.ts +0 -2
- package/lib/typescript/commonjs/dialog/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/dialog/index.web.d.ts +0 -2
- package/lib/typescript/commonjs/dialog/index.web.d.ts.map +1 -1
- package/lib/typescript/commonjs/index.d.ts +2 -4
- package/lib/typescript/commonjs/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/index.web.d.ts +2 -4
- package/lib/typescript/commonjs/index.web.d.ts.map +1 -1
- package/lib/typescript/module/alert-dialog/AlertDialog.d.ts +13 -12
- package/lib/typescript/module/alert-dialog/AlertDialog.d.ts.map +1 -1
- package/lib/typescript/module/alert-dialog/index.d.ts.map +1 -1
- package/lib/typescript/module/alert-dialog/index.web.d.ts.map +1 -1
- package/lib/typescript/module/command/Command.d.ts +8 -8
- package/lib/typescript/module/command/Command.d.ts.map +1 -1
- package/lib/typescript/module/command/index.d.ts.map +1 -1
- package/lib/typescript/module/command/index.web.d.ts.map +1 -1
- package/lib/typescript/module/dialog/Dialog.d.ts +2 -3
- package/lib/typescript/module/dialog/Dialog.d.ts.map +1 -1
- package/lib/typescript/module/dialog/Dialog.web.d.ts +11 -8
- package/lib/typescript/module/dialog/Dialog.web.d.ts.map +1 -1
- package/lib/typescript/module/dialog/DialogBottomSheet.d.ts +29 -0
- package/lib/typescript/module/dialog/DialogBottomSheet.d.ts.map +1 -0
- package/lib/typescript/module/dialog/DialogContent.d.ts +32 -0
- package/lib/typescript/module/dialog/DialogContent.d.ts.map +1 -0
- package/lib/typescript/module/dialog/index.d.ts +0 -2
- package/lib/typescript/module/dialog/index.d.ts.map +1 -1
- package/lib/typescript/module/dialog/index.web.d.ts +0 -2
- package/lib/typescript/module/dialog/index.web.d.ts.map +1 -1
- package/lib/typescript/module/index.d.ts +2 -4
- package/lib/typescript/module/index.d.ts.map +1 -1
- package/lib/typescript/module/index.web.d.ts +2 -4
- package/lib/typescript/module/index.web.d.ts.map +1 -1
- package/package.json +1 -18
- package/src/__tests__/AlertDialog.test.tsx +122 -0
- package/src/__tests__/BottomSheetGesture.test.tsx +135 -0
- package/src/__tests__/Command.test.tsx +107 -0
- package/src/__tests__/DialogBottomSheet.test.tsx +130 -0
- package/src/alert-dialog/AlertDialog.tsx +46 -36
- package/src/alert-dialog/index.ts +2 -2
- package/src/alert-dialog/index.web.ts +2 -2
- package/src/command/Command.tsx +30 -19
- package/src/command/index.ts +2 -2
- package/src/command/index.web.ts +2 -2
- package/src/dialog/Dialog.tsx +35 -168
- package/src/dialog/Dialog.web.tsx +52 -209
- package/src/dialog/DialogBottomSheet.tsx +235 -0
- package/src/dialog/DialogContent.tsx +157 -0
- package/src/dialog/index.ts +0 -2
- package/src/dialog/index.web.ts +0 -6
- package/src/index.ts +0 -11
- package/src/index.web.ts +0 -11
- package/lib/commonjs/dialog/CenteredDialog.js +0 -252
- package/lib/commonjs/dialog/CenteredDialog.js.map +0 -1
- package/lib/commonjs/dialog/CenteredDialog.web.js +0 -330
- package/lib/commonjs/dialog/CenteredDialog.web.js.map +0 -1
- package/lib/commonjs/dialog/centered-dialog-tokens.js +0 -62
- package/lib/commonjs/dialog/centered-dialog-tokens.js.map +0 -1
- package/lib/commonjs/dialog/centered-dialog-types.js +0 -6
- package/lib/commonjs/dialog/centered-dialog-types.js.map +0 -1
- package/lib/commonjs/responsive-sheet/ResponsiveSheet.js +0 -97
- package/lib/commonjs/responsive-sheet/ResponsiveSheet.js.map +0 -1
- package/lib/commonjs/responsive-sheet/ResponsiveSheet.web.js +0 -84
- package/lib/commonjs/responsive-sheet/ResponsiveSheet.web.js.map +0 -1
- package/lib/commonjs/responsive-sheet/index.js +0 -19
- package/lib/commonjs/responsive-sheet/index.js.map +0 -1
- package/lib/commonjs/responsive-sheet/index.web.js +0 -19
- package/lib/commonjs/responsive-sheet/index.web.js.map +0 -1
- package/lib/commonjs/responsive-sheet/types.js +0 -6
- package/lib/commonjs/responsive-sheet/types.js.map +0 -1
- package/lib/module/dialog/CenteredDialog.js +0 -246
- package/lib/module/dialog/CenteredDialog.js.map +0 -1
- package/lib/module/dialog/CenteredDialog.web.js +0 -324
- package/lib/module/dialog/CenteredDialog.web.js.map +0 -1
- package/lib/module/dialog/centered-dialog-tokens.js +0 -58
- package/lib/module/dialog/centered-dialog-tokens.js.map +0 -1
- package/lib/module/dialog/centered-dialog-types.js +0 -4
- package/lib/module/dialog/centered-dialog-types.js.map +0 -1
- package/lib/module/responsive-sheet/ResponsiveSheet.js +0 -90
- package/lib/module/responsive-sheet/ResponsiveSheet.js.map +0 -1
- package/lib/module/responsive-sheet/ResponsiveSheet.web.js +0 -77
- package/lib/module/responsive-sheet/ResponsiveSheet.web.js.map +0 -1
- package/lib/module/responsive-sheet/index.js +0 -4
- package/lib/module/responsive-sheet/index.js.map +0 -1
- package/lib/module/responsive-sheet/index.web.js +0 -12
- package/lib/module/responsive-sheet/index.web.js.map +0 -1
- package/lib/module/responsive-sheet/types.js +0 -4
- package/lib/module/responsive-sheet/types.js.map +0 -1
- package/lib/typescript/commonjs/dialog/CenteredDialog.d.ts +0 -22
- package/lib/typescript/commonjs/dialog/CenteredDialog.d.ts.map +0 -1
- package/lib/typescript/commonjs/dialog/CenteredDialog.web.d.ts +0 -31
- package/lib/typescript/commonjs/dialog/CenteredDialog.web.d.ts.map +0 -1
- package/lib/typescript/commonjs/dialog/centered-dialog-tokens.d.ts +0 -47
- package/lib/typescript/commonjs/dialog/centered-dialog-tokens.d.ts.map +0 -1
- package/lib/typescript/commonjs/dialog/centered-dialog-types.d.ts +0 -74
- package/lib/typescript/commonjs/dialog/centered-dialog-types.d.ts.map +0 -1
- package/lib/typescript/commonjs/responsive-sheet/ResponsiveSheet.d.ts +0 -22
- package/lib/typescript/commonjs/responsive-sheet/ResponsiveSheet.d.ts.map +0 -1
- package/lib/typescript/commonjs/responsive-sheet/ResponsiveSheet.web.d.ts +0 -16
- package/lib/typescript/commonjs/responsive-sheet/ResponsiveSheet.web.d.ts.map +0 -1
- package/lib/typescript/commonjs/responsive-sheet/index.d.ts +0 -3
- package/lib/typescript/commonjs/responsive-sheet/index.d.ts.map +0 -1
- package/lib/typescript/commonjs/responsive-sheet/index.web.d.ts +0 -3
- package/lib/typescript/commonjs/responsive-sheet/index.web.d.ts.map +0 -1
- package/lib/typescript/commonjs/responsive-sheet/types.d.ts +0 -58
- package/lib/typescript/commonjs/responsive-sheet/types.d.ts.map +0 -1
- package/lib/typescript/module/dialog/CenteredDialog.d.ts +0 -22
- package/lib/typescript/module/dialog/CenteredDialog.d.ts.map +0 -1
- package/lib/typescript/module/dialog/CenteredDialog.web.d.ts +0 -31
- package/lib/typescript/module/dialog/CenteredDialog.web.d.ts.map +0 -1
- package/lib/typescript/module/dialog/centered-dialog-tokens.d.ts +0 -47
- package/lib/typescript/module/dialog/centered-dialog-tokens.d.ts.map +0 -1
- package/lib/typescript/module/dialog/centered-dialog-types.d.ts +0 -74
- package/lib/typescript/module/dialog/centered-dialog-types.d.ts.map +0 -1
- package/lib/typescript/module/responsive-sheet/ResponsiveSheet.d.ts +0 -22
- package/lib/typescript/module/responsive-sheet/ResponsiveSheet.d.ts.map +0 -1
- package/lib/typescript/module/responsive-sheet/ResponsiveSheet.web.d.ts +0 -16
- package/lib/typescript/module/responsive-sheet/ResponsiveSheet.web.d.ts.map +0 -1
- package/lib/typescript/module/responsive-sheet/index.d.ts +0 -3
- package/lib/typescript/module/responsive-sheet/index.d.ts.map +0 -1
- package/lib/typescript/module/responsive-sheet/index.web.d.ts +0 -3
- package/lib/typescript/module/responsive-sheet/index.web.d.ts.map +0 -1
- package/lib/typescript/module/responsive-sheet/types.d.ts +0 -58
- package/lib/typescript/module/responsive-sheet/types.d.ts.map +0 -1
- package/src/__tests__/CenteredDialog.test.tsx +0 -108
- package/src/__tests__/ResponsiveSheet.test.tsx +0 -88
- package/src/dialog/CenteredDialog.stories.tsx +0 -125
- package/src/dialog/CenteredDialog.tsx +0 -305
- package/src/dialog/CenteredDialog.web.tsx +0 -374
- package/src/dialog/centered-dialog-tokens.ts +0 -68
- package/src/dialog/centered-dialog-types.ts +0 -74
- package/src/responsive-sheet/ResponsiveSheet.stories.tsx +0 -54
- package/src/responsive-sheet/ResponsiveSheet.tsx +0 -88
- package/src/responsive-sheet/ResponsiveSheet.web.tsx +0 -76
- package/src/responsive-sheet/index.ts +0 -6
- package/src/responsive-sheet/index.web.ts +0 -14
- package/src/responsive-sheet/types.ts +0 -60
|
@@ -10,11 +10,8 @@ import React, {
|
|
|
10
10
|
} from 'react';
|
|
11
11
|
import {
|
|
12
12
|
Pressable,
|
|
13
|
-
Text,
|
|
14
|
-
TouchableOpacity,
|
|
15
13
|
useWindowDimensions,
|
|
16
14
|
View,
|
|
17
|
-
type GestureResponderEvent,
|
|
18
15
|
type StyleProp,
|
|
19
16
|
type ViewStyle,
|
|
20
17
|
} from 'react-native';
|
|
@@ -22,29 +19,24 @@ import { RemoveScrollBar } from 'react-remove-scroll-bar';
|
|
|
22
19
|
|
|
23
20
|
import { Portal } from '../portal/index.web';
|
|
24
21
|
import { createOverlayZIndex } from '../styles/z-index';
|
|
25
|
-
import type { ThemeColors } from '../theme/types';
|
|
26
22
|
import { useTheme } from '../theme/use-theme';
|
|
27
|
-
import { Context,
|
|
23
|
+
import { Context, useDialogControl } from './context';
|
|
24
|
+
import { DialogBody } from './DialogContent';
|
|
25
|
+
import { DialogBottomSheet } from './DialogBottomSheet';
|
|
28
26
|
import {
|
|
29
27
|
ANIMATION_DURATION,
|
|
30
28
|
CENTER_FADE_OUT_DURATION,
|
|
31
29
|
DEFAULT_CENTER_MAX_WIDTH,
|
|
32
|
-
DEFAULT_MAX_HEIGHT_RATIO,
|
|
33
30
|
DEFAULT_SIDE_WIDTH,
|
|
34
31
|
DIALOG_SHEET_BACKDROP_TESTID,
|
|
35
32
|
EASE_OUT,
|
|
36
|
-
HANDLE_HEIGHT,
|
|
37
|
-
HANDLE_RADIUS,
|
|
38
|
-
HANDLE_WIDTH,
|
|
39
33
|
PANEL_RADIUS,
|
|
40
34
|
SHEET_BACKDROP_OPACITY,
|
|
41
35
|
SIDE_SHEET_MIN_GUTTER,
|
|
42
36
|
useResolvedPlacement,
|
|
43
|
-
type DialogPlacement,
|
|
44
37
|
} from './placement';
|
|
45
38
|
import type {
|
|
46
39
|
DialogAction,
|
|
47
|
-
DialogActionColor,
|
|
48
40
|
DialogControlProps,
|
|
49
41
|
DialogInset,
|
|
50
42
|
DialogProps,
|
|
@@ -61,9 +53,10 @@ const ClosingContext = createContext(false);
|
|
|
61
53
|
* Web variant of `<Dialog>`.
|
|
62
54
|
*
|
|
63
55
|
* A centered modal card (default) rendered into the bloom Portal at the end of
|
|
64
|
-
* `document.body`,
|
|
65
|
-
*
|
|
66
|
-
*
|
|
56
|
+
* `document.body`, an anchored side-sheet for `left`/`right`, or — for
|
|
57
|
+
* `bottom` — bloom's cross-platform `BottomSheet` (the SAME component native
|
|
58
|
+
* uses), so the bottom placement drags-to-dismiss on web too. Same prop API as
|
|
59
|
+
* native, so call sites are platform agnostic.
|
|
67
60
|
*
|
|
68
61
|
* Open/close is driven by EITHER the imperative `control` (legacy) OR the
|
|
69
62
|
* controlled `open` prop. When `open` is provided it wins.
|
|
@@ -75,10 +68,32 @@ const ClosingContext = createContext(false);
|
|
|
75
68
|
*
|
|
76
69
|
* Web motion never uses reanimated `exiting` (which throws `removeChild` on
|
|
77
70
|
* concurrent React unmounts): the centered card uses CSS keyframes and the
|
|
78
|
-
* side
|
|
79
|
-
*
|
|
71
|
+
* side modes use self-contained CSS transitions on a mounted-through-exit
|
|
72
|
+
* node. The bottom placement animates via `BottomSheet`'s shared-value/gesture
|
|
73
|
+
* animation (no `exiting` layout animation).
|
|
80
74
|
*/
|
|
81
|
-
export function Dialog({
|
|
75
|
+
export function Dialog({ placement, ...rest }: DialogProps) {
|
|
76
|
+
const resolvedPlacement = useResolvedPlacement(placement);
|
|
77
|
+
|
|
78
|
+
// `bottom` routes to the shared cross-platform `BottomSheet` surface — the
|
|
79
|
+
// SAME component native uses — so drag-to-dismiss works on web too and there
|
|
80
|
+
// is no duplicated CSS slide-up sheet. `center` (default) and the side-sheets
|
|
81
|
+
// keep their DOM-portal implementation below. Each branch is its own
|
|
82
|
+
// component so the dispatcher only ever calls `useResolvedPlacement`, keeping
|
|
83
|
+
// the rules-of-hooks contract intact across a responsive placement change.
|
|
84
|
+
if (resolvedPlacement === 'bottom') {
|
|
85
|
+
return <DialogBottomSheet {...rest} />;
|
|
86
|
+
}
|
|
87
|
+
return <CenterOrSideDialog {...rest} placement={resolvedPlacement} />;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Web `center` (default) and `left`/`right` placements. The centered card is a
|
|
92
|
+
* DOM-portal modal; the side placements are CSS-transition sheets. The `bottom`
|
|
93
|
+
* placement is handled separately by `DialogBottomSheet` (bloom's cross-platform
|
|
94
|
+
* `BottomSheet`).
|
|
95
|
+
*/
|
|
96
|
+
function CenterOrSideDialog({
|
|
82
97
|
control,
|
|
83
98
|
open: controlledOpen,
|
|
84
99
|
onClose,
|
|
@@ -89,9 +104,7 @@ export function Dialog({
|
|
|
89
104
|
placement,
|
|
90
105
|
width = DEFAULT_SIDE_WIDTH,
|
|
91
106
|
maxWidth = DEFAULT_CENTER_MAX_WIDTH,
|
|
92
|
-
maxHeightRatio = DEFAULT_MAX_HEIGHT_RATIO,
|
|
93
107
|
inset,
|
|
94
|
-
showHandle = true,
|
|
95
108
|
dismissOnBackdrop = true,
|
|
96
109
|
style,
|
|
97
110
|
panelStyle,
|
|
@@ -100,11 +113,11 @@ export function Dialog({
|
|
|
100
113
|
containerClassName,
|
|
101
114
|
label,
|
|
102
115
|
children,
|
|
103
|
-
}: DialogProps) {
|
|
116
|
+
}: Omit<DialogProps, 'placement'> & { placement: 'center' | 'left' | 'right' }) {
|
|
104
117
|
// Controlled mode is opt-in: when `open` is a boolean the host owns the
|
|
105
118
|
// visible state; otherwise the legacy imperative `control` path drives it.
|
|
106
119
|
const isControlled = controlledOpen !== undefined;
|
|
107
|
-
const resolvedPlacement =
|
|
120
|
+
const resolvedPlacement = placement;
|
|
108
121
|
|
|
109
122
|
const [isOpen, setIsOpen] = useState(false);
|
|
110
123
|
const [isClosing, setIsClosing] = useState(false);
|
|
@@ -184,9 +197,12 @@ export function Dialog({
|
|
|
184
197
|
|
|
185
198
|
// Escape-to-close while open. The listener is intentionally scoped to the
|
|
186
199
|
// open lifetime so stacked dialogs don't fight for the keydown — the
|
|
187
|
-
// top-most one wins via document-level event order.
|
|
200
|
+
// top-most one wins via document-level event order. Escape honors
|
|
201
|
+
// `dismissOnBackdrop`: a blocking dialog (e.g. an unanswered confirm) is not
|
|
202
|
+
// dismissible by Escape, matching the backdrop's behavior. The default
|
|
203
|
+
// (`dismissOnBackdrop` true) is unchanged — Escape still closes.
|
|
188
204
|
useEffect(() => {
|
|
189
|
-
if (!isOpen || typeof document === 'undefined') return;
|
|
205
|
+
if (!isOpen || !dismissOnBackdrop || typeof document === 'undefined') return;
|
|
190
206
|
const handler = (e: KeyboardEvent) => {
|
|
191
207
|
if (e.key === 'Escape') {
|
|
192
208
|
e.stopPropagation();
|
|
@@ -195,7 +211,7 @@ export function Dialog({
|
|
|
195
211
|
};
|
|
196
212
|
document.addEventListener('keydown', handler);
|
|
197
213
|
return () => document.removeEventListener('keydown', handler);
|
|
198
|
-
}, [close, isOpen]);
|
|
214
|
+
}, [close, dismissOnBackdrop, isOpen]);
|
|
199
215
|
|
|
200
216
|
useImperativeHandle(
|
|
201
217
|
control?.ref,
|
|
@@ -269,9 +285,7 @@ export function Dialog({
|
|
|
269
285
|
placement={resolvedPlacement}
|
|
270
286
|
shown={!isClosing}
|
|
271
287
|
width={width}
|
|
272
|
-
maxHeightRatio={maxHeightRatio}
|
|
273
288
|
inset={inset}
|
|
274
|
-
showHandle={showHandle}
|
|
275
289
|
dismissOnBackdrop={dismissOnBackdrop}
|
|
276
290
|
onDismiss={close}
|
|
277
291
|
panelStyle={panelStyle}
|
|
@@ -288,64 +302,6 @@ export function Dialog({
|
|
|
288
302
|
);
|
|
289
303
|
}
|
|
290
304
|
|
|
291
|
-
/**
|
|
292
|
-
* Renders the dialog's body: optional declarative title + description, any
|
|
293
|
-
* `children`, then the action row. Shared by the centered panel and the
|
|
294
|
-
* side/bottom sheet so all placements present identical content. The
|
|
295
|
-
* `titleId`/`descriptionId` are wired by the caller onto the `role="dialog"`
|
|
296
|
-
* element for `aria-labelledby` / `aria-describedby`.
|
|
297
|
-
*/
|
|
298
|
-
function DialogBody({
|
|
299
|
-
titleId,
|
|
300
|
-
descriptionId,
|
|
301
|
-
title,
|
|
302
|
-
description,
|
|
303
|
-
actions,
|
|
304
|
-
children,
|
|
305
|
-
}: {
|
|
306
|
-
titleId: string;
|
|
307
|
-
descriptionId: string;
|
|
308
|
-
title?: string;
|
|
309
|
-
description?: string;
|
|
310
|
-
actions?: DialogAction[];
|
|
311
|
-
children?: React.ReactNode;
|
|
312
|
-
}) {
|
|
313
|
-
const theme = useTheme();
|
|
314
|
-
return (
|
|
315
|
-
<>
|
|
316
|
-
{title ? (
|
|
317
|
-
<Text
|
|
318
|
-
nativeID={titleId}
|
|
319
|
-
style={{
|
|
320
|
-
fontSize: 22,
|
|
321
|
-
fontWeight: '600',
|
|
322
|
-
color: theme.colors.text,
|
|
323
|
-
paddingBottom: description ? 4 : 16,
|
|
324
|
-
lineHeight: 30,
|
|
325
|
-
}}
|
|
326
|
-
>
|
|
327
|
-
{title}
|
|
328
|
-
</Text>
|
|
329
|
-
) : null}
|
|
330
|
-
{description ? (
|
|
331
|
-
<Text
|
|
332
|
-
nativeID={descriptionId}
|
|
333
|
-
style={{
|
|
334
|
-
fontSize: 16,
|
|
335
|
-
color: theme.colors.textSecondary,
|
|
336
|
-
paddingBottom: 16,
|
|
337
|
-
lineHeight: 22,
|
|
338
|
-
}}
|
|
339
|
-
>
|
|
340
|
-
{description}
|
|
341
|
-
</Text>
|
|
342
|
-
) : null}
|
|
343
|
-
{children}
|
|
344
|
-
{actions && actions.length > 0 ? <ActionRow actions={actions} /> : null}
|
|
345
|
-
</>
|
|
346
|
-
);
|
|
347
|
-
}
|
|
348
|
-
|
|
349
305
|
function DialogPanel({
|
|
350
306
|
testID,
|
|
351
307
|
label,
|
|
@@ -419,10 +375,13 @@ function DialogPanel({
|
|
|
419
375
|
export { DIALOG_SHEET_BACKDROP_TESTID };
|
|
420
376
|
|
|
421
377
|
/**
|
|
422
|
-
* Side-sheet
|
|
423
|
-
*
|
|
424
|
-
* node stays mounted through the exit while a `shown` flag drives the
|
|
378
|
+
* Side-sheet surface for the `left`/`right` placements. Pure CSS transitions:
|
|
379
|
+
* the node stays mounted through the exit while a `shown` flag drives the
|
|
425
380
|
* transform/opacity, so both directions animate without reanimated `exiting`.
|
|
381
|
+
*
|
|
382
|
+
* The `bottom` placement is NOT handled here — it routes through
|
|
383
|
+
* `DialogBottomSheet` (bloom's cross-platform `BottomSheet`) so it shares one
|
|
384
|
+
* implementation with native and supports drag-to-dismiss on web.
|
|
426
385
|
*/
|
|
427
386
|
function SheetSurface({
|
|
428
387
|
testID,
|
|
@@ -433,9 +392,7 @@ function SheetSurface({
|
|
|
433
392
|
placement,
|
|
434
393
|
shown,
|
|
435
394
|
width,
|
|
436
|
-
maxHeightRatio,
|
|
437
395
|
inset,
|
|
438
|
-
showHandle,
|
|
439
396
|
dismissOnBackdrop,
|
|
440
397
|
onDismiss,
|
|
441
398
|
panelStyle,
|
|
@@ -450,12 +407,10 @@ function SheetSurface({
|
|
|
450
407
|
title?: string;
|
|
451
408
|
description?: string;
|
|
452
409
|
actions?: DialogAction[];
|
|
453
|
-
placement:
|
|
410
|
+
placement: 'left' | 'right';
|
|
454
411
|
shown: boolean;
|
|
455
412
|
width: number;
|
|
456
|
-
maxHeightRatio: number;
|
|
457
413
|
inset?: DialogInset;
|
|
458
|
-
showHandle: boolean;
|
|
459
414
|
dismissOnBackdrop: boolean;
|
|
460
415
|
onDismiss: () => void;
|
|
461
416
|
panelStyle?: StyleProp<ViewStyle>;
|
|
@@ -468,7 +423,7 @@ function SheetSurface({
|
|
|
468
423
|
const theme = useTheme();
|
|
469
424
|
const titleId = useId();
|
|
470
425
|
const descriptionId = useId();
|
|
471
|
-
const { width: viewportWidth
|
|
426
|
+
const { width: viewportWidth } = useWindowDimensions();
|
|
472
427
|
|
|
473
428
|
// Defer the entry transition by a frame so the start state paints before the
|
|
474
429
|
// browser animates to `shown`. `entered` is false on the first committed
|
|
@@ -486,7 +441,6 @@ function SheetSurface({
|
|
|
486
441
|
}, []);
|
|
487
442
|
|
|
488
443
|
const visible = shown && entered;
|
|
489
|
-
const isBottom = placement === 'bottom';
|
|
490
444
|
|
|
491
445
|
const panelTransition = useMemo<ViewStyle>(
|
|
492
446
|
() =>
|
|
@@ -509,19 +463,6 @@ function SheetSurface({
|
|
|
509
463
|
);
|
|
510
464
|
|
|
511
465
|
const panelGeometry = useMemo<ViewStyle>(() => {
|
|
512
|
-
if (isBottom) {
|
|
513
|
-
return {
|
|
514
|
-
left: 0,
|
|
515
|
-
right: 0,
|
|
516
|
-
bottom: 0,
|
|
517
|
-
maxHeight: Math.round(viewportHeight * maxHeightRatio),
|
|
518
|
-
borderTopLeftRadius: PANEL_RADIUS,
|
|
519
|
-
borderTopRightRadius: PANEL_RADIUS,
|
|
520
|
-
transform: [{ translateY: visible ? 0 : '100%' }],
|
|
521
|
-
opacity: visible ? 1 : 0,
|
|
522
|
-
} as ViewStyle;
|
|
523
|
-
}
|
|
524
|
-
|
|
525
466
|
const insetTop = inset?.top ?? 0;
|
|
526
467
|
const insetBottom = inset?.bottom ?? 0;
|
|
527
468
|
const insetLeft = inset?.left ?? 0;
|
|
@@ -541,7 +482,7 @@ function SheetSurface({
|
|
|
541
482
|
transform: [{ translateX: visible ? 0 : hiddenSign }],
|
|
542
483
|
opacity: visible ? 1 : 0,
|
|
543
484
|
} as ViewStyle;
|
|
544
|
-
}, [
|
|
485
|
+
}, [visible, placement, width, inset, viewportWidth]);
|
|
545
486
|
|
|
546
487
|
const handleBackdropPress = useCallback(() => {
|
|
547
488
|
if (dismissOnBackdrop) onDismiss();
|
|
@@ -587,11 +528,6 @@ function SheetSurface({
|
|
|
587
528
|
]}
|
|
588
529
|
pointerEvents="auto"
|
|
589
530
|
>
|
|
590
|
-
{isBottom && showHandle ? (
|
|
591
|
-
<View style={sheetStyles.handleRow} pointerEvents="none">
|
|
592
|
-
<View style={[sheetStyles.handle, { backgroundColor: theme.colors.border }]} />
|
|
593
|
-
</View>
|
|
594
|
-
) : null}
|
|
595
531
|
<View style={sheetStyles.body}>
|
|
596
532
|
<DialogBody
|
|
597
533
|
titleId={titleId}
|
|
@@ -629,88 +565,6 @@ function cancelFrame(token: FrameToken): void {
|
|
|
629
565
|
clearTimeout(token.timer);
|
|
630
566
|
}
|
|
631
567
|
|
|
632
|
-
function ActionRow({ actions }: { actions: DialogAction[] }) {
|
|
633
|
-
return (
|
|
634
|
-
<View style={{ width: '100%', gap: 8, justifyContent: 'flex-end' }}>
|
|
635
|
-
{actions.map((action, idx) => (
|
|
636
|
-
<ActionButton
|
|
637
|
-
key={`${action.label}-${idx}`}
|
|
638
|
-
action={action}
|
|
639
|
-
/>
|
|
640
|
-
))}
|
|
641
|
-
</View>
|
|
642
|
-
);
|
|
643
|
-
}
|
|
644
|
-
|
|
645
|
-
function ActionButton({ action }: { action: DialogAction }) {
|
|
646
|
-
const { close } = useDialogContext();
|
|
647
|
-
const theme = useTheme();
|
|
648
|
-
const color: DialogActionColor = action.color ?? 'default';
|
|
649
|
-
const shouldCloseOnPress = action.shouldCloseOnPress ?? true;
|
|
650
|
-
|
|
651
|
-
const { background, foreground } = getActionPalette(color, theme.colors);
|
|
652
|
-
|
|
653
|
-
const handlePress = useCallback(
|
|
654
|
-
(e: GestureResponderEvent) => {
|
|
655
|
-
const onPress = action.onPress;
|
|
656
|
-
if (color === 'cancel') {
|
|
657
|
-
close(onPress ? () => onPress(e) : undefined);
|
|
658
|
-
return;
|
|
659
|
-
}
|
|
660
|
-
if (shouldCloseOnPress) {
|
|
661
|
-
close(onPress ? () => onPress(e) : undefined);
|
|
662
|
-
} else {
|
|
663
|
-
onPress?.(e);
|
|
664
|
-
}
|
|
665
|
-
},
|
|
666
|
-
[action.onPress, close, color, shouldCloseOnPress],
|
|
667
|
-
);
|
|
668
|
-
|
|
669
|
-
return (
|
|
670
|
-
<TouchableOpacity
|
|
671
|
-
style={{
|
|
672
|
-
borderRadius: 9999,
|
|
673
|
-
alignItems: 'center',
|
|
674
|
-
justifyContent: 'center',
|
|
675
|
-
backgroundColor: background,
|
|
676
|
-
opacity: action.disabled ? 0.5 : 1,
|
|
677
|
-
paddingVertical: 12,
|
|
678
|
-
paddingHorizontal: 24,
|
|
679
|
-
}}
|
|
680
|
-
onPress={handlePress}
|
|
681
|
-
disabled={action.disabled}
|
|
682
|
-
activeOpacity={0.7}
|
|
683
|
-
testID={action.testID}
|
|
684
|
-
>
|
|
685
|
-
<Text style={{ fontSize: 16, fontWeight: '500', color: foreground }}>
|
|
686
|
-
{action.label}
|
|
687
|
-
</Text>
|
|
688
|
-
</TouchableOpacity>
|
|
689
|
-
);
|
|
690
|
-
}
|
|
691
|
-
|
|
692
|
-
function getActionPalette(
|
|
693
|
-
color: DialogActionColor,
|
|
694
|
-
colors: ThemeColors,
|
|
695
|
-
): { background: string; foreground: string } {
|
|
696
|
-
switch (color) {
|
|
697
|
-
case 'destructive':
|
|
698
|
-
return {
|
|
699
|
-
background: colors.negative,
|
|
700
|
-
foreground: colors.negativeForeground,
|
|
701
|
-
};
|
|
702
|
-
case 'cancel':
|
|
703
|
-
return { background: colors.contrast50, foreground: colors.text };
|
|
704
|
-
case 'default':
|
|
705
|
-
return { background: colors.primary, foreground: colors.primaryForeground };
|
|
706
|
-
/* c8 ignore next 3 -- TS exhaustiveness guard */
|
|
707
|
-
default: {
|
|
708
|
-
const _exhaustive: never = color;
|
|
709
|
-
return { background: colors.primary, foreground: colors.primaryForeground };
|
|
710
|
-
}
|
|
711
|
-
}
|
|
712
|
-
}
|
|
713
|
-
|
|
714
568
|
function DialogBackdrop({ isClosing }: { isClosing: boolean }) {
|
|
715
569
|
const style: ViewStyle[] = [
|
|
716
570
|
{
|
|
@@ -787,18 +641,6 @@ const sheetStyles = {
|
|
|
787
641
|
shadowOffset: { width: 0, height: 8 },
|
|
788
642
|
zIndex: dialogZIndex.surface,
|
|
789
643
|
} as ViewStyle,
|
|
790
|
-
handleRow: {
|
|
791
|
-
width: '100%',
|
|
792
|
-
alignItems: 'center',
|
|
793
|
-
paddingTop: 8,
|
|
794
|
-
paddingBottom: 4,
|
|
795
|
-
} as ViewStyle,
|
|
796
|
-
handle: {
|
|
797
|
-
width: HANDLE_WIDTH,
|
|
798
|
-
height: HANDLE_HEIGHT,
|
|
799
|
-
borderRadius: HANDLE_RADIUS,
|
|
800
|
-
opacity: 0.5,
|
|
801
|
-
} as ViewStyle,
|
|
802
644
|
body: {
|
|
803
645
|
padding: 20,
|
|
804
646
|
} as ViewStyle,
|
|
@@ -815,8 +657,9 @@ const sheetStyles = {
|
|
|
815
657
|
* @keyframes bloomDialogZoomFadeOut { from { opacity: 1; transform: scale(1); } to { opacity: 0; transform: scale(0.95); } }
|
|
816
658
|
* ```
|
|
817
659
|
*
|
|
818
|
-
* The `left`/`right
|
|
819
|
-
*
|
|
660
|
+
* The `left`/`right` placements do NOT depend on this — they animate via
|
|
661
|
+
* self-contained inline CSS transitions and need no keyframe injection. The
|
|
662
|
+
* `bottom` placement uses bloom's `BottomSheet` (reanimated) and needs none.
|
|
820
663
|
*/
|
|
821
664
|
export const BLOOM_DIALOG_CSS = `
|
|
822
665
|
@keyframes bloomDialogFadeIn { from { opacity: 0; } to { opacity: 1; } }
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
import React, {
|
|
2
|
+
useCallback,
|
|
3
|
+
useEffect,
|
|
4
|
+
useId,
|
|
5
|
+
useImperativeHandle,
|
|
6
|
+
useMemo,
|
|
7
|
+
useRef,
|
|
8
|
+
} from 'react';
|
|
9
|
+
import { View, type StyleProp, type ViewStyle } from 'react-native';
|
|
10
|
+
|
|
11
|
+
import { BottomSheet, type BottomSheetRef } from '../bottom-sheet';
|
|
12
|
+
import { useTheme } from '../theme/use-theme';
|
|
13
|
+
import { Context } from './context';
|
|
14
|
+
import { DialogBody } from './DialogContent';
|
|
15
|
+
import {
|
|
16
|
+
DEFAULT_MAX_HEIGHT_RATIO,
|
|
17
|
+
PANEL_RADIUS,
|
|
18
|
+
SHEET_BACKDROP_OPACITY,
|
|
19
|
+
} from './placement';
|
|
20
|
+
import type { DialogControlProps, DialogProps } from './types';
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Padding of the bottom-sheet content container (px). Matches the side-sheet
|
|
24
|
+
* and centered-panel body padding so every placement reads identically.
|
|
25
|
+
*/
|
|
26
|
+
const SHEET_CONTENT_PADDING = 20;
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Shared `BottomSheet`-backed surface for the `bottom` placement, used by BOTH
|
|
30
|
+
* `Dialog.tsx` (native) and `Dialog.web.tsx` (web). `BottomSheet` is itself a
|
|
31
|
+
* single cross-platform implementation (react-native-gesture-handler +
|
|
32
|
+
* reanimated, drag-to-dismiss on web and native alike), so routing the bottom
|
|
33
|
+
* placement through here gives ONE code path with drag working everywhere — no
|
|
34
|
+
* duplicated CSS slide-up sheet.
|
|
35
|
+
*
|
|
36
|
+
* This component owns the entire open↔present/dismiss bridge and the Dialog →
|
|
37
|
+
* BottomSheet prop mapping in one place:
|
|
38
|
+
*
|
|
39
|
+
* - `open` (controlled) / `control` (imperative) → `present()` / `dismiss()`
|
|
40
|
+
* - `onClose` → fired once the sheet settles
|
|
41
|
+
* - `maxHeightRatio` → sheet `maxHeight`
|
|
42
|
+
* - `showHandle` / `dismissOnBackdrop` / `label` → forwarded to the surface
|
|
43
|
+
* - `panelStyle` → paints the sheet surface
|
|
44
|
+
* - `containerStyle` → wraps the content subtree
|
|
45
|
+
* so a host's CSS-var theme scope (`vars()`) still applies to descendants
|
|
46
|
+
* - `title` / `description` / `actions` / `children` → declarative body
|
|
47
|
+
*/
|
|
48
|
+
export function DialogBottomSheet({
|
|
49
|
+
control,
|
|
50
|
+
open: controlledOpen,
|
|
51
|
+
onClose,
|
|
52
|
+
testID,
|
|
53
|
+
title,
|
|
54
|
+
description,
|
|
55
|
+
actions,
|
|
56
|
+
maxHeightRatio = DEFAULT_MAX_HEIGHT_RATIO,
|
|
57
|
+
showHandle = true,
|
|
58
|
+
dismissOnBackdrop = true,
|
|
59
|
+
style,
|
|
60
|
+
panelStyle,
|
|
61
|
+
panelClassName,
|
|
62
|
+
containerStyle,
|
|
63
|
+
containerClassName,
|
|
64
|
+
label,
|
|
65
|
+
children,
|
|
66
|
+
}: DialogBottomSheetProps) {
|
|
67
|
+
const isControlled = controlledOpen !== undefined;
|
|
68
|
+
const theme = useTheme();
|
|
69
|
+
const ref = useRef<BottomSheetRef>(null);
|
|
70
|
+
const closeCallbacks = useRef<(() => void)[]>([]);
|
|
71
|
+
const titleId = useId();
|
|
72
|
+
const descriptionId = useId();
|
|
73
|
+
|
|
74
|
+
// Read the latest controlled flag / `onClose` inside stable callbacks without
|
|
75
|
+
// re-binding them (the context + imperative handle depend on `close` staying
|
|
76
|
+
// referentially stable).
|
|
77
|
+
const isControlledRef = useRef(isControlled);
|
|
78
|
+
isControlledRef.current = isControlled;
|
|
79
|
+
const onCloseRef = useRef(onClose);
|
|
80
|
+
onCloseRef.current = onClose;
|
|
81
|
+
|
|
82
|
+
// Drain queued close callbacks atomically — capturing the list and resetting
|
|
83
|
+
// it before invocation ensures a callback that synchronously re-opens the
|
|
84
|
+
// dialog (and queues fresh callbacks) does not see the old ones replayed
|
|
85
|
+
// against the new session.
|
|
86
|
+
const callQueuedCallbacks = useCallback(() => {
|
|
87
|
+
const queued = closeCallbacks.current;
|
|
88
|
+
closeCallbacks.current = [];
|
|
89
|
+
for (const cb of queued) {
|
|
90
|
+
try {
|
|
91
|
+
cb();
|
|
92
|
+
} catch (e) {
|
|
93
|
+
if (typeof console !== 'undefined' && console.error) {
|
|
94
|
+
console.error('Dialog close callback error:', e);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}, []);
|
|
99
|
+
|
|
100
|
+
const open = useCallback(() => {
|
|
101
|
+
ref.current?.present();
|
|
102
|
+
}, []);
|
|
103
|
+
|
|
104
|
+
// A dismissal request (backdrop, pan-to-close, action button). In controlled
|
|
105
|
+
// mode it asks the host to close via `onClose` (the host flips `open`); in
|
|
106
|
+
// imperative mode it dismisses the surface directly. The optional callback
|
|
107
|
+
// runs once the dialog has finished closing.
|
|
108
|
+
const close = useCallback<DialogControlProps['close']>((cb) => {
|
|
109
|
+
if (typeof cb === 'function') {
|
|
110
|
+
closeCallbacks.current.push(cb);
|
|
111
|
+
}
|
|
112
|
+
if (isControlledRef.current) {
|
|
113
|
+
onCloseRef.current?.();
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
ref.current?.dismiss();
|
|
117
|
+
}, []);
|
|
118
|
+
|
|
119
|
+
// Fired when the sheet has finished closing. Drains queued callbacks, then
|
|
120
|
+
// fires `onClose` ONLY in imperative mode — controlled mode already requested
|
|
121
|
+
// the close through `onClose` (the host then flipped `open`), so firing it
|
|
122
|
+
// again here would double-call it.
|
|
123
|
+
const handleDismiss = useCallback(() => {
|
|
124
|
+
callQueuedCallbacks();
|
|
125
|
+
if (!isControlledRef.current) onCloseRef.current?.();
|
|
126
|
+
}, [callQueuedCallbacks]);
|
|
127
|
+
|
|
128
|
+
// In controlled mode, mirror the `open` prop onto the underlying sheet so both
|
|
129
|
+
// modes share one lifecycle.
|
|
130
|
+
useEffect(() => {
|
|
131
|
+
if (!isControlled) return;
|
|
132
|
+
if (controlledOpen) {
|
|
133
|
+
ref.current?.present();
|
|
134
|
+
} else {
|
|
135
|
+
ref.current?.dismiss();
|
|
136
|
+
}
|
|
137
|
+
}, [isControlled, controlledOpen]);
|
|
138
|
+
|
|
139
|
+
useImperativeHandle(control?.ref, () => ({ open, close }), [open, close]);
|
|
140
|
+
|
|
141
|
+
const context = useMemo(() => ({ close, isWithinDialog: true }), [close]);
|
|
142
|
+
|
|
143
|
+
// Backdrop press → dismiss, unless the host disabled it. Returning `false`
|
|
144
|
+
// from `onDismissAttempt` blocks the BottomSheet's own backdrop/pan dismissal,
|
|
145
|
+
// which is how `dismissOnBackdrop: false` maps onto the single sheet API.
|
|
146
|
+
const onDismissAttempt = useCallback(
|
|
147
|
+
() => dismissOnBackdrop,
|
|
148
|
+
[dismissOnBackdrop],
|
|
149
|
+
);
|
|
150
|
+
|
|
151
|
+
// `panelStyle` paints the sheet surface (e.g. a brand background). It composes
|
|
152
|
+
// AFTER the BottomSheet's internal background so a host palette wins; the
|
|
153
|
+
// shape (radius, max height) is supplied here and remains overridable.
|
|
154
|
+
const sheetStyle = useMemo<StyleProp<ViewStyle>>(() => {
|
|
155
|
+
const maxHeightPercent: `${number}%` = `${Math.round(maxHeightRatio * 100)}%`;
|
|
156
|
+
return [
|
|
157
|
+
{
|
|
158
|
+
backgroundColor: theme.colors.background,
|
|
159
|
+
borderTopLeftRadius: PANEL_RADIUS + 4,
|
|
160
|
+
borderTopRightRadius: PANEL_RADIUS + 4,
|
|
161
|
+
maxHeight: maxHeightPercent,
|
|
162
|
+
},
|
|
163
|
+
panelStyle,
|
|
164
|
+
];
|
|
165
|
+
}, [theme.colors.background, maxHeightRatio, panelStyle]);
|
|
166
|
+
|
|
167
|
+
return (
|
|
168
|
+
<BottomSheet
|
|
169
|
+
ref={ref}
|
|
170
|
+
onDismiss={handleDismiss}
|
|
171
|
+
onDismissAttempt={onDismissAttempt}
|
|
172
|
+
enablePanDownToClose
|
|
173
|
+
showHandle={showHandle}
|
|
174
|
+
// Stronger dim than a lone sheet so an underlying sheet's handle/content
|
|
175
|
+
// doesn't bleed through when a Dialog is stacked over one.
|
|
176
|
+
backdropOpacity={SHEET_BACKDROP_OPACITY + 0.3}
|
|
177
|
+
style={sheetStyle}
|
|
178
|
+
>
|
|
179
|
+
<Context.Provider value={context}>
|
|
180
|
+
<View
|
|
181
|
+
testID={testID}
|
|
182
|
+
accessibilityLabel={label}
|
|
183
|
+
aria-labelledby={title ? titleId : undefined}
|
|
184
|
+
aria-describedby={description ? descriptionId : undefined}
|
|
185
|
+
{...(containerClassName ? ({ className: containerClassName } as Record<string, string>) : {})}
|
|
186
|
+
{...(panelClassName ? ({ className: panelClassName } as Record<string, string>) : {})}
|
|
187
|
+
style={[
|
|
188
|
+
{ padding: SHEET_CONTENT_PADDING },
|
|
189
|
+
// `containerStyle` carries the host's CSS-var theme scope; it wraps
|
|
190
|
+
// the content subtree so descendants read the scoped palette.
|
|
191
|
+
containerStyle,
|
|
192
|
+
style,
|
|
193
|
+
panelStyle,
|
|
194
|
+
]}
|
|
195
|
+
>
|
|
196
|
+
<DialogBody
|
|
197
|
+
titleId={titleId}
|
|
198
|
+
descriptionId={descriptionId}
|
|
199
|
+
title={title}
|
|
200
|
+
description={description}
|
|
201
|
+
actions={actions}
|
|
202
|
+
>
|
|
203
|
+
{children}
|
|
204
|
+
</DialogBody>
|
|
205
|
+
</View>
|
|
206
|
+
</Context.Provider>
|
|
207
|
+
</BottomSheet>
|
|
208
|
+
);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Props the shared bottom-sheet surface consumes — the `bottom`-relevant subset
|
|
213
|
+
* of `DialogProps`. `width`, `maxWidth`, and `inset` are placement-specific to
|
|
214
|
+
* the side/centered surfaces and intentionally excluded.
|
|
215
|
+
*/
|
|
216
|
+
export type DialogBottomSheetProps = Pick<
|
|
217
|
+
DialogProps,
|
|
218
|
+
| 'control'
|
|
219
|
+
| 'open'
|
|
220
|
+
| 'onClose'
|
|
221
|
+
| 'testID'
|
|
222
|
+
| 'title'
|
|
223
|
+
| 'description'
|
|
224
|
+
| 'actions'
|
|
225
|
+
| 'maxHeightRatio'
|
|
226
|
+
| 'showHandle'
|
|
227
|
+
| 'dismissOnBackdrop'
|
|
228
|
+
| 'style'
|
|
229
|
+
| 'panelStyle'
|
|
230
|
+
| 'panelClassName'
|
|
231
|
+
| 'containerStyle'
|
|
232
|
+
| 'containerClassName'
|
|
233
|
+
| 'label'
|
|
234
|
+
| 'children'
|
|
235
|
+
>;
|