@oxyhq/bloom 0.14.0 → 0.15.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/lib/commonjs/dialog/CenteredDialog.js +2 -0
- package/lib/commonjs/dialog/CenteredDialog.js.map +1 -1
- package/lib/commonjs/dialog/CenteredDialog.web.js +2 -0
- package/lib/commonjs/dialog/CenteredDialog.web.js.map +1 -1
- package/lib/commonjs/dialog/Dialog.js +337 -43
- package/lib/commonjs/dialog/Dialog.js.map +1 -1
- package/lib/commonjs/dialog/Dialog.web.js +414 -63
- package/lib/commonjs/dialog/Dialog.web.js.map +1 -1
- package/lib/commonjs/dialog/index.js +6 -0
- package/lib/commonjs/dialog/index.js.map +1 -1
- package/lib/commonjs/dialog/index.web.js +6 -0
- package/lib/commonjs/dialog/index.web.js.map +1 -1
- package/lib/commonjs/dialog/placement.js +104 -0
- package/lib/commonjs/dialog/placement.js.map +1 -0
- package/lib/commonjs/index.js +7 -0
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/index.web.js +7 -0
- package/lib/commonjs/index.web.js.map +1 -1
- package/lib/commonjs/responsive-sheet/ResponsiveSheet.js +63 -266
- package/lib/commonjs/responsive-sheet/ResponsiveSheet.js.map +1 -1
- package/lib/commonjs/responsive-sheet/ResponsiveSheet.web.js +84 -0
- package/lib/commonjs/responsive-sheet/ResponsiveSheet.web.js.map +1 -0
- package/lib/commonjs/responsive-sheet/index.web.js +3 -3
- package/lib/commonjs/responsive-sheet/index.web.js.map +1 -1
- package/lib/module/dialog/CenteredDialog.js +2 -0
- package/lib/module/dialog/CenteredDialog.js.map +1 -1
- package/lib/module/dialog/CenteredDialog.web.js +2 -0
- package/lib/module/dialog/CenteredDialog.web.js.map +1 -1
- package/lib/module/dialog/Dialog.js +336 -46
- package/lib/module/dialog/Dialog.js.map +1 -1
- package/lib/module/dialog/Dialog.web.js +412 -65
- package/lib/module/dialog/Dialog.web.js.map +1 -1
- package/lib/module/dialog/index.js +1 -1
- package/lib/module/dialog/index.js.map +1 -1
- package/lib/module/dialog/index.web.js +1 -1
- package/lib/module/dialog/index.web.js.map +1 -1
- package/lib/module/dialog/placement.js +100 -0
- package/lib/module/dialog/placement.js.map +1 -0
- package/lib/module/index.js +1 -1
- package/lib/module/index.js.map +1 -1
- package/lib/module/index.web.js +1 -1
- package/lib/module/index.web.js.map +1 -1
- package/lib/module/responsive-sheet/ResponsiveSheet.js +63 -266
- package/lib/module/responsive-sheet/ResponsiveSheet.js.map +1 -1
- package/lib/module/responsive-sheet/ResponsiveSheet.web.js +77 -0
- package/lib/module/responsive-sheet/ResponsiveSheet.web.js.map +1 -0
- package/lib/module/responsive-sheet/index.web.js +7 -9
- package/lib/module/responsive-sheet/index.web.js.map +1 -1
- package/lib/typescript/commonjs/dialog/CenteredDialog.d.ts +2 -0
- package/lib/typescript/commonjs/dialog/CenteredDialog.d.ts.map +1 -1
- package/lib/typescript/commonjs/dialog/CenteredDialog.web.d.ts +2 -0
- package/lib/typescript/commonjs/dialog/CenteredDialog.web.d.ts.map +1 -1
- package/lib/typescript/commonjs/dialog/Dialog.d.ts +18 -15
- package/lib/typescript/commonjs/dialog/Dialog.d.ts.map +1 -1
- package/lib/typescript/commonjs/dialog/Dialog.web.d.ts +18 -5
- package/lib/typescript/commonjs/dialog/Dialog.web.d.ts.map +1 -1
- package/lib/typescript/commonjs/dialog/centered-dialog-types.d.ts +4 -0
- package/lib/typescript/commonjs/dialog/centered-dialog-types.d.ts.map +1 -1
- package/lib/typescript/commonjs/dialog/index.d.ts +2 -2
- package/lib/typescript/commonjs/dialog/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/dialog/index.web.d.ts +2 -2
- package/lib/typescript/commonjs/dialog/index.web.d.ts.map +1 -1
- package/lib/typescript/commonjs/dialog/placement.d.ts +71 -0
- package/lib/typescript/commonjs/dialog/placement.d.ts.map +1 -0
- package/lib/typescript/commonjs/dialog/types.d.ts +66 -3
- package/lib/typescript/commonjs/dialog/types.d.ts.map +1 -1
- package/lib/typescript/commonjs/index.d.ts +2 -2
- package/lib/typescript/commonjs/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/index.web.d.ts +2 -2
- package/lib/typescript/commonjs/index.web.d.ts.map +1 -1
- package/lib/typescript/commonjs/responsive-sheet/ResponsiveSheet.d.ts +15 -19
- package/lib/typescript/commonjs/responsive-sheet/ResponsiveSheet.d.ts.map +1 -1
- package/lib/typescript/commonjs/responsive-sheet/ResponsiveSheet.web.d.ts +16 -0
- package/lib/typescript/commonjs/responsive-sheet/ResponsiveSheet.web.d.ts.map +1 -0
- package/lib/typescript/commonjs/responsive-sheet/index.web.d.ts +1 -1
- package/lib/typescript/commonjs/responsive-sheet/index.web.d.ts.map +1 -1
- package/lib/typescript/commonjs/responsive-sheet/types.d.ts +5 -2
- package/lib/typescript/commonjs/responsive-sheet/types.d.ts.map +1 -1
- package/lib/typescript/module/dialog/CenteredDialog.d.ts +2 -0
- package/lib/typescript/module/dialog/CenteredDialog.d.ts.map +1 -1
- package/lib/typescript/module/dialog/CenteredDialog.web.d.ts +2 -0
- package/lib/typescript/module/dialog/CenteredDialog.web.d.ts.map +1 -1
- package/lib/typescript/module/dialog/Dialog.d.ts +18 -15
- package/lib/typescript/module/dialog/Dialog.d.ts.map +1 -1
- package/lib/typescript/module/dialog/Dialog.web.d.ts +18 -5
- package/lib/typescript/module/dialog/Dialog.web.d.ts.map +1 -1
- package/lib/typescript/module/dialog/centered-dialog-types.d.ts +4 -0
- package/lib/typescript/module/dialog/centered-dialog-types.d.ts.map +1 -1
- package/lib/typescript/module/dialog/index.d.ts +2 -2
- package/lib/typescript/module/dialog/index.d.ts.map +1 -1
- package/lib/typescript/module/dialog/index.web.d.ts +2 -2
- package/lib/typescript/module/dialog/index.web.d.ts.map +1 -1
- package/lib/typescript/module/dialog/placement.d.ts +71 -0
- package/lib/typescript/module/dialog/placement.d.ts.map +1 -0
- package/lib/typescript/module/dialog/types.d.ts +66 -3
- package/lib/typescript/module/dialog/types.d.ts.map +1 -1
- package/lib/typescript/module/index.d.ts +2 -2
- package/lib/typescript/module/index.d.ts.map +1 -1
- package/lib/typescript/module/index.web.d.ts +2 -2
- package/lib/typescript/module/index.web.d.ts.map +1 -1
- package/lib/typescript/module/responsive-sheet/ResponsiveSheet.d.ts +15 -19
- package/lib/typescript/module/responsive-sheet/ResponsiveSheet.d.ts.map +1 -1
- package/lib/typescript/module/responsive-sheet/ResponsiveSheet.web.d.ts +16 -0
- package/lib/typescript/module/responsive-sheet/ResponsiveSheet.web.d.ts.map +1 -0
- package/lib/typescript/module/responsive-sheet/index.web.d.ts +1 -1
- package/lib/typescript/module/responsive-sheet/index.web.d.ts.map +1 -1
- package/lib/typescript/module/responsive-sheet/types.d.ts +5 -2
- package/lib/typescript/module/responsive-sheet/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/__tests__/DialogPlacement.test.tsx +144 -0
- package/src/__tests__/ResponsiveSheet.test.tsx +36 -52
- package/src/dialog/CenteredDialog.tsx +2 -0
- package/src/dialog/CenteredDialog.web.tsx +2 -0
- package/src/dialog/Dialog.tsx +407 -50
- package/src/dialog/Dialog.web.tsx +490 -75
- package/src/dialog/centered-dialog-types.ts +4 -0
- package/src/dialog/index.ts +4 -1
- package/src/dialog/index.web.ts +4 -1
- package/src/dialog/placement.ts +113 -0
- package/src/dialog/types.ts +69 -3
- package/src/index.ts +4 -0
- package/src/index.web.ts +4 -0
- package/src/responsive-sheet/ResponsiveSheet.tsx +58 -297
- package/src/responsive-sheet/ResponsiveSheet.web.tsx +76 -0
- package/src/responsive-sheet/index.web.ts +7 -9
- package/src/responsive-sheet/types.ts +5 -2
- package/lib/commonjs/responsive-sheet/ResponsiveSheet.native.js +0 -226
- package/lib/commonjs/responsive-sheet/ResponsiveSheet.native.js.map +0 -1
- package/lib/commonjs/responsive-sheet/const.js +0 -60
- package/lib/commonjs/responsive-sheet/const.js.map +0 -1
- package/lib/module/responsive-sheet/ResponsiveSheet.native.js +0 -219
- package/lib/module/responsive-sheet/ResponsiveSheet.native.js.map +0 -1
- package/lib/module/responsive-sheet/const.js +0 -56
- package/lib/module/responsive-sheet/const.js.map +0 -1
- package/lib/typescript/commonjs/responsive-sheet/ResponsiveSheet.native.d.ts +0 -20
- package/lib/typescript/commonjs/responsive-sheet/ResponsiveSheet.native.d.ts.map +0 -1
- package/lib/typescript/commonjs/responsive-sheet/const.d.ts +0 -43
- package/lib/typescript/commonjs/responsive-sheet/const.d.ts.map +0 -1
- package/lib/typescript/module/responsive-sheet/ResponsiveSheet.native.d.ts +0 -20
- package/lib/typescript/module/responsive-sheet/ResponsiveSheet.native.d.ts.map +0 -1
- package/lib/typescript/module/responsive-sheet/const.d.ts +0 -43
- package/lib/typescript/module/responsive-sheet/const.d.ts.map +0 -1
- package/src/responsive-sheet/ResponsiveSheet.native.tsx +0 -257
- package/src/responsive-sheet/const.ts +0 -53
|
@@ -1,327 +1,88 @@
|
|
|
1
|
-
import React
|
|
2
|
-
useCallback,
|
|
3
|
-
useEffect,
|
|
4
|
-
useMemo,
|
|
5
|
-
useRef,
|
|
6
|
-
useState,
|
|
7
|
-
} from 'react';
|
|
8
|
-
import {
|
|
9
|
-
Pressable,
|
|
10
|
-
StyleSheet,
|
|
11
|
-
useWindowDimensions,
|
|
12
|
-
View,
|
|
13
|
-
type ViewStyle,
|
|
14
|
-
} from 'react-native';
|
|
1
|
+
import React from 'react';
|
|
15
2
|
|
|
16
|
-
import {
|
|
17
|
-
import { useTheme } from '../theme/use-theme';
|
|
3
|
+
import { Dialog } from '../dialog';
|
|
18
4
|
import {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
EASE_OUT,
|
|
27
|
-
HANDLE_HEIGHT,
|
|
28
|
-
HANDLE_RADIUS,
|
|
29
|
-
HANDLE_WIDTH,
|
|
30
|
-
PANEL_RADIUS,
|
|
31
|
-
SIDE_SHEET_MIN_GUTTER,
|
|
32
|
-
} from './const';
|
|
5
|
+
BREAKPOINT_LG,
|
|
6
|
+
BREAKPOINT_MD,
|
|
7
|
+
BREAKPOINT_SM,
|
|
8
|
+
BREAKPOINT_XL,
|
|
9
|
+
DIALOG_SHEET_BACKDROP_TESTID,
|
|
10
|
+
type ResponsiveDialogPlacement,
|
|
11
|
+
} from '../dialog/placement';
|
|
33
12
|
import type { ResponsiveSheetProps } from './types';
|
|
34
13
|
|
|
35
|
-
/** Stable testID for the dimmed backdrop dismiss button. */
|
|
36
|
-
export const RESPONSIVE_SHEET_BACKDROP_TESTID = 'bloom-responsive-sheet-backdrop';
|
|
37
|
-
|
|
38
14
|
/**
|
|
39
|
-
*
|
|
15
|
+
* @deprecated Use `<Dialog placement={{ base: 'bottom', md: 'left' }}>` instead.
|
|
40
16
|
*
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
* here would break every web bundler and downstream type-check. The native
|
|
47
|
-
* fork (`ResponsiveSheet.native.tsx`, selected only by Metro) drives the same
|
|
48
|
-
* motion with reanimated.
|
|
17
|
+
* `ResponsiveSheet` is now a thin wrapper that configures the canonical
|
|
18
|
+
* `<Dialog>` — an anchored side-sheet on wide screens collapsing to a
|
|
19
|
+
* bottom-sheet below the breakpoint. It holds ZERO motion/layout logic of its
|
|
20
|
+
* own; it maps its props onto a responsive `Dialog` `placement`. The
|
|
21
|
+
* `@oxyhq/bloom/responsive-sheet` subpath is retained for the migration window.
|
|
49
22
|
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
* open AND close directions animate while the node stays mounted through the
|
|
54
|
-
* exit. We deliberately do NOT use reanimated `exiting` on web — concurrent
|
|
55
|
-
* React unmounts there throw `Failed to execute 'removeChild' on 'Node'`, the
|
|
56
|
-
* exact bug this component exists to avoid.
|
|
23
|
+
* This file is BOTH the package default entry (resolved by downstream `tsc`
|
|
24
|
+
* and non-Metro web bundlers via the `browser` condition through `index.web`)
|
|
25
|
+
* AND a clean delegation, so it carries no platform-specific imports.
|
|
57
26
|
*/
|
|
58
27
|
export function ResponsiveSheet({
|
|
59
28
|
open,
|
|
60
29
|
onClose,
|
|
61
|
-
side =
|
|
62
|
-
breakpoint =
|
|
63
|
-
width
|
|
64
|
-
maxHeightRatio
|
|
30
|
+
side = 'left',
|
|
31
|
+
breakpoint = BREAKPOINT_MD,
|
|
32
|
+
width,
|
|
33
|
+
maxHeightRatio,
|
|
65
34
|
inset,
|
|
66
|
-
showHandle
|
|
67
|
-
dismissOnBackdrop
|
|
35
|
+
showHandle,
|
|
36
|
+
dismissOnBackdrop,
|
|
68
37
|
panelStyle,
|
|
69
38
|
panelClassName,
|
|
70
39
|
containerStyle,
|
|
71
40
|
containerClassName,
|
|
72
|
-
label
|
|
41
|
+
label,
|
|
73
42
|
children,
|
|
74
43
|
}: ResponsiveSheetProps) {
|
|
75
|
-
const theme = useTheme();
|
|
76
|
-
const { width: viewportWidth, height: viewportHeight } = useWindowDimensions();
|
|
77
|
-
|
|
78
|
-
// Presence: `mounted` keeps the node in the tree through the exit transition;
|
|
79
|
-
// `shown` drives the enter/exit transform & opacity. On open we mount first,
|
|
80
|
-
// then flip `shown` on the next frame so the browser registers the start
|
|
81
|
-
// state and animates to the end. On close we flip `shown` off, then unmount
|
|
82
|
-
// after the transition duration.
|
|
83
|
-
const [mounted, setMounted] = useState(open);
|
|
84
|
-
const [shown, setShown] = useState(false);
|
|
85
|
-
const closeTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
|
86
|
-
const frameRef = useRef<FrameToken | null>(null);
|
|
87
|
-
|
|
88
|
-
useEffect(() => {
|
|
89
|
-
if (open) {
|
|
90
|
-
if (closeTimerRef.current) {
|
|
91
|
-
clearTimeout(closeTimerRef.current);
|
|
92
|
-
closeTimerRef.current = null;
|
|
93
|
-
}
|
|
94
|
-
setMounted(true);
|
|
95
|
-
// Defer to the next frame so the entry start state paints before we
|
|
96
|
-
// transition to the shown state. Two frames guarantees the start
|
|
97
|
-
// transform has committed before the toggle (a single frame can be
|
|
98
|
-
// coalesced with the mount paint in some browsers).
|
|
99
|
-
frameRef.current = scheduleFrame(() => {
|
|
100
|
-
frameRef.current = scheduleFrame(() => setShown(true));
|
|
101
|
-
});
|
|
102
|
-
return () => {
|
|
103
|
-
if (frameRef.current !== null) {
|
|
104
|
-
cancelFrame(frameRef.current);
|
|
105
|
-
frameRef.current = null;
|
|
106
|
-
}
|
|
107
|
-
};
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
// Closing: only run the exit if something is currently mounted.
|
|
111
|
-
setShown(false);
|
|
112
|
-
if (!mounted) return;
|
|
113
|
-
closeTimerRef.current = setTimeout(() => {
|
|
114
|
-
setMounted(false);
|
|
115
|
-
closeTimerRef.current = null;
|
|
116
|
-
}, ANIMATION_DURATION);
|
|
117
|
-
return () => {
|
|
118
|
-
if (closeTimerRef.current) {
|
|
119
|
-
clearTimeout(closeTimerRef.current);
|
|
120
|
-
closeTimerRef.current = null;
|
|
121
|
-
}
|
|
122
|
-
};
|
|
123
|
-
}, [open, mounted]);
|
|
124
|
-
|
|
125
|
-
// Escape-to-close while open (web only; document is undefined elsewhere).
|
|
126
|
-
useEffect(() => {
|
|
127
|
-
if (!open || typeof document === 'undefined') return;
|
|
128
|
-
const handler = (e: KeyboardEvent) => {
|
|
129
|
-
if (e.key === 'Escape') {
|
|
130
|
-
e.stopPropagation();
|
|
131
|
-
onClose();
|
|
132
|
-
}
|
|
133
|
-
};
|
|
134
|
-
document.addEventListener('keydown', handler);
|
|
135
|
-
return () => document.removeEventListener('keydown', handler);
|
|
136
|
-
}, [open, onClose]);
|
|
137
|
-
|
|
138
|
-
const handleBackdropPress = useCallback(() => {
|
|
139
|
-
if (dismissOnBackdrop) onClose();
|
|
140
|
-
}, [dismissOnBackdrop, onClose]);
|
|
141
|
-
|
|
142
|
-
const isBottom = viewportWidth < breakpoint;
|
|
143
|
-
const panelBackground = theme.colors.background;
|
|
144
|
-
|
|
145
|
-
const panelTransition = useMemo<ViewStyle>(
|
|
146
|
-
() =>
|
|
147
|
-
({
|
|
148
|
-
transitionProperty: 'transform, opacity',
|
|
149
|
-
transitionDuration: `${ANIMATION_DURATION}ms`,
|
|
150
|
-
transitionTimingFunction: EASE_OUT,
|
|
151
|
-
}) as ViewStyle,
|
|
152
|
-
[],
|
|
153
|
-
);
|
|
154
|
-
|
|
155
|
-
const backdropTransition = useMemo<ViewStyle>(
|
|
156
|
-
() =>
|
|
157
|
-
({
|
|
158
|
-
transitionProperty: 'opacity',
|
|
159
|
-
transitionDuration: `${ANIMATION_DURATION}ms`,
|
|
160
|
-
transitionTimingFunction: EASE_OUT,
|
|
161
|
-
}) as ViewStyle,
|
|
162
|
-
[],
|
|
163
|
-
);
|
|
164
|
-
|
|
165
|
-
const panelGeometry = useMemo<ViewStyle>(() => {
|
|
166
|
-
if (isBottom) {
|
|
167
|
-
return {
|
|
168
|
-
left: 0,
|
|
169
|
-
right: 0,
|
|
170
|
-
bottom: 0,
|
|
171
|
-
maxHeight: Math.round(viewportHeight * maxHeightRatio),
|
|
172
|
-
borderTopLeftRadius: PANEL_RADIUS,
|
|
173
|
-
borderTopRightRadius: PANEL_RADIUS,
|
|
174
|
-
transform: [{ translateY: shown ? 0 : '100%' }],
|
|
175
|
-
opacity: shown ? 1 : 0,
|
|
176
|
-
} as ViewStyle;
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
// Side-sheet: full height minus top/bottom inset, anchored to `side`,
|
|
180
|
-
// offset by the matching horizontal inset. Width is capped so the panel
|
|
181
|
-
// always leaves a gutter on the opposite edge.
|
|
182
|
-
const insetTop = inset?.top ?? 0;
|
|
183
|
-
const insetBottom = inset?.bottom ?? 0;
|
|
184
|
-
const insetLeft = inset?.left ?? 0;
|
|
185
|
-
const insetRight = inset?.right ?? 0;
|
|
186
|
-
const anchorInset = side === 'left' ? insetLeft : insetRight;
|
|
187
|
-
const oppositeInset = side === 'left' ? insetRight : insetLeft;
|
|
188
|
-
const available = viewportWidth - anchorInset - oppositeInset - SIDE_SHEET_MIN_GUTTER;
|
|
189
|
-
const cappedWidth = Math.max(0, Math.min(width, available));
|
|
190
|
-
const hiddenSign = side === 'left' ? '-100%' : '100%';
|
|
191
|
-
|
|
192
|
-
return {
|
|
193
|
-
top: insetTop,
|
|
194
|
-
bottom: insetBottom,
|
|
195
|
-
[side]: anchorInset,
|
|
196
|
-
width: cappedWidth,
|
|
197
|
-
borderRadius: PANEL_RADIUS,
|
|
198
|
-
transform: [{ translateX: shown ? 0 : hiddenSign }],
|
|
199
|
-
opacity: shown ? 1 : 0,
|
|
200
|
-
} as ViewStyle;
|
|
201
|
-
}, [isBottom, shown, side, width, inset, viewportWidth, viewportHeight, maxHeightRatio]);
|
|
202
|
-
|
|
203
|
-
if (!mounted) return null;
|
|
204
|
-
|
|
205
44
|
return (
|
|
206
|
-
<
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
{
|
|
211
|
-
|
|
45
|
+
<Dialog
|
|
46
|
+
open={open}
|
|
47
|
+
onClose={onClose}
|
|
48
|
+
placement={placementForSide(side, breakpoint)}
|
|
49
|
+
width={width}
|
|
50
|
+
maxHeightRatio={maxHeightRatio}
|
|
51
|
+
inset={inset}
|
|
52
|
+
showHandle={showHandle}
|
|
53
|
+
dismissOnBackdrop={dismissOnBackdrop}
|
|
54
|
+
panelStyle={panelStyle}
|
|
55
|
+
panelClassName={panelClassName}
|
|
56
|
+
containerStyle={containerStyle}
|
|
57
|
+
containerClassName={containerClassName}
|
|
58
|
+
label={label}
|
|
212
59
|
>
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
accessibilityRole="button"
|
|
216
|
-
accessibilityLabel={`Dismiss ${label}`}
|
|
217
|
-
onPress={handleBackdropPress}
|
|
218
|
-
disabled={!dismissOnBackdrop}
|
|
219
|
-
style={[
|
|
220
|
-
styles.backdrop,
|
|
221
|
-
backdropTransition,
|
|
222
|
-
{ opacity: shown ? BACKDROP_OPACITY : 0 },
|
|
223
|
-
isWeb ? webBackdropBlur : null,
|
|
224
|
-
]}
|
|
225
|
-
/>
|
|
226
|
-
|
|
227
|
-
<View
|
|
228
|
-
accessibilityViewIsModal
|
|
229
|
-
aria-label={label}
|
|
230
|
-
{...(panelClassName ? ({ className: panelClassName } as Record<string, string>) : {})}
|
|
231
|
-
style={[
|
|
232
|
-
styles.panel,
|
|
233
|
-
{ backgroundColor: panelBackground, shadowColor: theme.colors.shadow },
|
|
234
|
-
panelGeometry,
|
|
235
|
-
panelTransition,
|
|
236
|
-
panelStyle,
|
|
237
|
-
]}
|
|
238
|
-
pointerEvents="auto"
|
|
239
|
-
>
|
|
240
|
-
{isBottom && showHandle ? (
|
|
241
|
-
<View style={styles.handleRow} pointerEvents="none">
|
|
242
|
-
<View style={[styles.handle, { backgroundColor: theme.colors.border }]} />
|
|
243
|
-
</View>
|
|
244
|
-
) : null}
|
|
245
|
-
{children}
|
|
246
|
-
</View>
|
|
247
|
-
</View>
|
|
60
|
+
{children}
|
|
61
|
+
</Dialog>
|
|
248
62
|
);
|
|
249
63
|
}
|
|
250
64
|
|
|
251
|
-
// This module is the web/default impl, but it is also the fallback resolved by
|
|
252
|
-
// a downstream consumer's `tsc` for the native target, so guard the web-only
|
|
253
|
-
// `backdrop-blur` style behind a runtime check rather than assuming web.
|
|
254
|
-
const isWeb = typeof document !== 'undefined';
|
|
255
|
-
|
|
256
65
|
/**
|
|
257
|
-
*
|
|
258
|
-
*
|
|
259
|
-
*
|
|
66
|
+
* Build the `Dialog` `placement` map for a `ResponsiveSheet`: a bottom-sheet
|
|
67
|
+
* below `breakpoint`, the anchored `side` at and above it. The `breakpoint`
|
|
68
|
+
* (px) is bucketed to the nearest Tailwind key so the side-sheet engages at the
|
|
69
|
+
* matching width.
|
|
260
70
|
*/
|
|
261
|
-
|
|
71
|
+
function placementForSide(
|
|
72
|
+
side: 'left' | 'right',
|
|
73
|
+
breakpoint: number,
|
|
74
|
+
): ResponsiveDialogPlacement {
|
|
75
|
+
if (breakpoint <= BREAKPOINT_SM) return { base: 'bottom', sm: side };
|
|
76
|
+
if (breakpoint <= BREAKPOINT_MD) return { base: 'bottom', md: side };
|
|
77
|
+
if (breakpoint <= BREAKPOINT_LG) return { base: 'bottom', lg: side };
|
|
78
|
+
if (breakpoint <= BREAKPOINT_XL) return { base: 'bottom', xl: side };
|
|
79
|
+
return { base: 'bottom', xl: side };
|
|
80
|
+
}
|
|
262
81
|
|
|
263
82
|
/**
|
|
264
|
-
*
|
|
265
|
-
*
|
|
266
|
-
* non-DOM test envs) so the enter transition's `shown` flip still fires.
|
|
83
|
+
* @deprecated Stable testID for the dimmed backdrop dismiss button — now
|
|
84
|
+
* provided by the underlying `<Dialog>`'s side/bottom backdrop.
|
|
267
85
|
*/
|
|
268
|
-
|
|
269
|
-
if (typeof requestAnimationFrame === 'function') {
|
|
270
|
-
return { raf: requestAnimationFrame(cb) };
|
|
271
|
-
}
|
|
272
|
-
return { timer: setTimeout(cb, 0) };
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
function cancelFrame(token: FrameToken): void {
|
|
276
|
-
if ('raf' in token) {
|
|
277
|
-
if (typeof cancelAnimationFrame === 'function') cancelAnimationFrame(token.raf);
|
|
278
|
-
return;
|
|
279
|
-
}
|
|
280
|
-
clearTimeout(token.timer);
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
// `backdrop-filter` is web-only; react-native-web passes it through to the DOM.
|
|
284
|
-
const webBackdropBlur = { backdropFilter: 'blur(2px)' } as unknown as ViewStyle;
|
|
285
|
-
|
|
286
|
-
const styles = StyleSheet.create({
|
|
287
|
-
root: {
|
|
288
|
-
// `position: fixed` pins the overlay to the viewport on web; react-native
|
|
289
|
-
// does not type `'fixed'`, so cast to the RN-accepted `'absolute'`. On
|
|
290
|
-
// native the `.native` fork uses `StyleSheet.absoluteFill` instead.
|
|
291
|
-
position: 'fixed' as 'absolute',
|
|
292
|
-
top: 0,
|
|
293
|
-
left: 0,
|
|
294
|
-
right: 0,
|
|
295
|
-
bottom: 0,
|
|
296
|
-
zIndex: Z_INDEX.fullscreen,
|
|
297
|
-
},
|
|
298
|
-
backdrop: {
|
|
299
|
-
position: 'absolute',
|
|
300
|
-
top: 0,
|
|
301
|
-
left: 0,
|
|
302
|
-
right: 0,
|
|
303
|
-
bottom: 0,
|
|
304
|
-
backgroundColor: '#000',
|
|
305
|
-
},
|
|
306
|
-
panel: {
|
|
307
|
-
position: 'absolute',
|
|
308
|
-
overflow: 'hidden',
|
|
309
|
-
shadowOpacity: 0.18,
|
|
310
|
-
shadowRadius: 24,
|
|
311
|
-
shadowOffset: { width: 0, height: 8 },
|
|
312
|
-
},
|
|
313
|
-
handleRow: {
|
|
314
|
-
width: '100%',
|
|
315
|
-
alignItems: 'center',
|
|
316
|
-
paddingTop: 8,
|
|
317
|
-
paddingBottom: 4,
|
|
318
|
-
},
|
|
319
|
-
handle: {
|
|
320
|
-
width: HANDLE_WIDTH,
|
|
321
|
-
height: HANDLE_HEIGHT,
|
|
322
|
-
borderRadius: HANDLE_RADIUS,
|
|
323
|
-
opacity: 0.5,
|
|
324
|
-
},
|
|
325
|
-
});
|
|
86
|
+
export const RESPONSIVE_SHEET_BACKDROP_TESTID = DIALOG_SHEET_BACKDROP_TESTID;
|
|
326
87
|
|
|
327
88
|
export default ResponsiveSheet;
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
import { Dialog } from '../dialog/index.web';
|
|
4
|
+
import {
|
|
5
|
+
BREAKPOINT_LG,
|
|
6
|
+
BREAKPOINT_MD,
|
|
7
|
+
BREAKPOINT_SM,
|
|
8
|
+
BREAKPOINT_XL,
|
|
9
|
+
DIALOG_SHEET_BACKDROP_TESTID,
|
|
10
|
+
type ResponsiveDialogPlacement,
|
|
11
|
+
} from '../dialog/placement';
|
|
12
|
+
import type { ResponsiveSheetProps } from './types';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* @deprecated Use `<Dialog placement={{ base: 'bottom', md: 'left' }}>` instead.
|
|
16
|
+
*
|
|
17
|
+
* Web fork of the deprecated `ResponsiveSheet` wrapper. Identical delegation to
|
|
18
|
+
* the default fork, but imports the web `Dialog` explicitly so non-Metro web
|
|
19
|
+
* bundlers never follow the native (reanimated) `Dialog` sibling.
|
|
20
|
+
*/
|
|
21
|
+
export function ResponsiveSheet({
|
|
22
|
+
open,
|
|
23
|
+
onClose,
|
|
24
|
+
side = 'left',
|
|
25
|
+
breakpoint = BREAKPOINT_MD,
|
|
26
|
+
width,
|
|
27
|
+
maxHeightRatio,
|
|
28
|
+
inset,
|
|
29
|
+
showHandle,
|
|
30
|
+
dismissOnBackdrop,
|
|
31
|
+
panelStyle,
|
|
32
|
+
panelClassName,
|
|
33
|
+
containerStyle,
|
|
34
|
+
containerClassName,
|
|
35
|
+
label,
|
|
36
|
+
children,
|
|
37
|
+
}: ResponsiveSheetProps) {
|
|
38
|
+
return (
|
|
39
|
+
<Dialog
|
|
40
|
+
open={open}
|
|
41
|
+
onClose={onClose}
|
|
42
|
+
placement={placementForSide(side, breakpoint)}
|
|
43
|
+
width={width}
|
|
44
|
+
maxHeightRatio={maxHeightRatio}
|
|
45
|
+
inset={inset}
|
|
46
|
+
showHandle={showHandle}
|
|
47
|
+
dismissOnBackdrop={dismissOnBackdrop}
|
|
48
|
+
panelStyle={panelStyle}
|
|
49
|
+
panelClassName={panelClassName}
|
|
50
|
+
containerStyle={containerStyle}
|
|
51
|
+
containerClassName={containerClassName}
|
|
52
|
+
label={label}
|
|
53
|
+
>
|
|
54
|
+
{children}
|
|
55
|
+
</Dialog>
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function placementForSide(
|
|
60
|
+
side: 'left' | 'right',
|
|
61
|
+
breakpoint: number,
|
|
62
|
+
): ResponsiveDialogPlacement {
|
|
63
|
+
if (breakpoint <= BREAKPOINT_SM) return { base: 'bottom', sm: side };
|
|
64
|
+
if (breakpoint <= BREAKPOINT_MD) return { base: 'bottom', md: side };
|
|
65
|
+
if (breakpoint <= BREAKPOINT_LG) return { base: 'bottom', lg: side };
|
|
66
|
+
if (breakpoint <= BREAKPOINT_XL) return { base: 'bottom', xl: side };
|
|
67
|
+
return { base: 'bottom', xl: side };
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* @deprecated Stable testID for the dimmed backdrop dismiss button — now
|
|
72
|
+
* provided by the underlying `<Dialog>`'s side/bottom backdrop.
|
|
73
|
+
*/
|
|
74
|
+
export const RESPONSIVE_SHEET_BACKDROP_TESTID = DIALOG_SHEET_BACKDROP_TESTID;
|
|
75
|
+
|
|
76
|
+
export default ResponsiveSheet;
|
|
@@ -1,14 +1,12 @@
|
|
|
1
1
|
// Web variant of the `./responsive-sheet` barrel.
|
|
2
2
|
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
// `
|
|
8
|
-
//
|
|
9
|
-
|
|
10
|
-
// Types are platform-agnostic, so they come straight from `./types`.
|
|
11
|
-
export { ResponsiveSheet, RESPONSIVE_SHEET_BACKDROP_TESTID } from './ResponsiveSheet';
|
|
3
|
+
// `ResponsiveSheet` is now a deprecated thin wrapper over `<Dialog>`. The
|
|
4
|
+
// default barrel (`./index.ts`) imports the default `Dialog`; THIS web barrel
|
|
5
|
+
// re-exports from `./ResponsiveSheet.web`, which imports the web `Dialog`
|
|
6
|
+
// explicitly so a non-Metro bundler never follows the native (reanimated)
|
|
7
|
+
// `Dialog` sibling. Web bundlers select this file via the `"browser"` export
|
|
8
|
+
// condition in `package.json`'s `exports['./responsive-sheet']`.
|
|
9
|
+
export { ResponsiveSheet, RESPONSIVE_SHEET_BACKDROP_TESTID } from './ResponsiveSheet.web';
|
|
12
10
|
export type {
|
|
13
11
|
ResponsiveSheetProps,
|
|
14
12
|
ResponsiveSheetSide,
|
|
@@ -17,11 +17,14 @@ export interface ResponsiveSheetInset {
|
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
/**
|
|
20
|
+
* @deprecated Use `<Dialog placement={{ base: 'bottom', md: 'left' }}>` from
|
|
21
|
+
* `@oxyhq/bloom/dialog` instead. `ResponsiveSheet` is now a thin wrapper over
|
|
22
|
+
* `<Dialog>` retained for the migration window.
|
|
23
|
+
*
|
|
20
24
|
* A responsive overlay sheet: an anchored side-sheet on wide screens and a
|
|
21
25
|
* bottom-sheet (with drag handle) on small screens, switching by viewport
|
|
22
26
|
* width. It is a plain controlled component — the host drives `open`/`onClose`
|
|
23
|
-
* from its own state.
|
|
24
|
-
* inside a themed/positioned subtree.
|
|
27
|
+
* from its own state.
|
|
25
28
|
*/
|
|
26
29
|
export interface ResponsiveSheetProps {
|
|
27
30
|
/** Whether the sheet is open. Controlled by the host. */
|