@cdx-ui/components 0.1.2 → 0.1.4
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/coverage.svg +1 -1
- package/lib/commonjs/components/Avatar/index.js +30 -8
- package/lib/commonjs/components/Avatar/index.js.map +1 -1
- package/lib/commonjs/components/Avatar/styles.js +163 -7
- package/lib/commonjs/components/Avatar/styles.js.map +1 -1
- package/lib/commonjs/components/CLAUDE.md +2 -1
- package/lib/commonjs/components/Chip/styles.js +1 -1
- package/lib/commonjs/components/Chip/styles.js.map +1 -1
- package/lib/commonjs/components/Drawer/animation.js +13 -0
- package/lib/commonjs/components/Drawer/animation.js.map +1 -0
- package/lib/commonjs/components/Drawer/index.js +739 -0
- package/lib/commonjs/components/Drawer/index.js.map +1 -0
- package/lib/commonjs/components/Drawer/styles.js +185 -0
- package/lib/commonjs/components/Drawer/styles.js.map +1 -0
- package/lib/commonjs/components/Drawer/utils.js +95 -0
- package/lib/commonjs/components/Drawer/utils.js.map +1 -0
- package/lib/commonjs/components/InsuranceDisclosure/styles.js +1 -1
- package/lib/commonjs/components/InsuranceDisclosure/styles.js.map +1 -1
- package/lib/commonjs/components/Menu/index.js +187 -61
- package/lib/commonjs/components/Menu/index.js.map +1 -1
- package/lib/commonjs/components/Menu/styles.js +32 -37
- package/lib/commonjs/components/Menu/styles.js.map +1 -1
- package/lib/commonjs/components/Select/index.js +2 -10
- package/lib/commonjs/components/Select/index.js.map +1 -1
- package/lib/commonjs/components/Tabs/styles.js +9 -10
- package/lib/commonjs/components/Tabs/styles.js.map +1 -1
- package/lib/commonjs/components/Toast/styles.js +1 -1
- package/lib/commonjs/components/Toast/styles.js.map +1 -1
- package/lib/commonjs/components/index.js +12 -0
- package/lib/commonjs/components/index.js.map +1 -1
- package/lib/commonjs/figma/Avatar.figma.js +18 -4
- package/lib/commonjs/figma/Avatar.figma.js.map +1 -1
- package/lib/commonjs/figma/Drawer.Content.figma.js +29 -0
- package/lib/commonjs/figma/Drawer.Content.figma.js.map +1 -0
- package/lib/commonjs/figma/Drawer.Description.figma.js +20 -0
- package/lib/commonjs/figma/Drawer.Description.figma.js.map +1 -0
- package/lib/commonjs/figma/Drawer.Footer.figma.js +41 -0
- package/lib/commonjs/figma/Drawer.Footer.figma.js.map +1 -0
- package/lib/commonjs/figma/Drawer.Header.figma.js +45 -0
- package/lib/commonjs/figma/Drawer.Header.figma.js.map +1 -0
- package/lib/commonjs/figma/Drawer.Title.figma.js +20 -0
- package/lib/commonjs/figma/Drawer.Title.figma.js.map +1 -0
- package/lib/commonjs/figma/Drawer.figma.js +41 -0
- package/lib/commonjs/figma/Drawer.figma.js.map +1 -0
- package/lib/commonjs/figma/Menu.Item.figma.js +33 -5
- package/lib/commonjs/figma/Menu.Item.figma.js.map +1 -1
- package/lib/commonjs/figma/icons.figma.batch.json +262 -2
- package/lib/module/components/Avatar/index.js +30 -8
- package/lib/module/components/Avatar/index.js.map +1 -1
- package/lib/module/components/Avatar/styles.js +163 -7
- package/lib/module/components/Avatar/styles.js.map +1 -1
- package/lib/module/components/CLAUDE.md +2 -1
- package/lib/module/components/Chip/styles.js +1 -1
- package/lib/module/components/Chip/styles.js.map +1 -1
- package/lib/module/components/Drawer/animation.js +7 -0
- package/lib/module/components/Drawer/animation.js.map +1 -0
- package/lib/module/components/Drawer/index.js +724 -0
- package/lib/module/components/Drawer/index.js.map +1 -0
- package/lib/module/components/Drawer/styles.js +181 -0
- package/lib/module/components/Drawer/styles.js.map +1 -0
- package/lib/module/components/Drawer/utils.js +86 -0
- package/lib/module/components/Drawer/utils.js.map +1 -0
- package/lib/module/components/InsuranceDisclosure/styles.js +1 -1
- package/lib/module/components/InsuranceDisclosure/styles.js.map +1 -1
- package/lib/module/components/Menu/index.js +178 -52
- package/lib/module/components/Menu/index.js.map +1 -1
- package/lib/module/components/Menu/styles.js +31 -36
- package/lib/module/components/Menu/styles.js.map +1 -1
- package/lib/module/components/Select/index.js +3 -11
- package/lib/module/components/Select/index.js.map +1 -1
- package/lib/module/components/Tabs/styles.js +9 -10
- package/lib/module/components/Tabs/styles.js.map +1 -1
- package/lib/module/components/Toast/styles.js +1 -1
- package/lib/module/components/Toast/styles.js.map +1 -1
- package/lib/module/components/index.js +1 -0
- package/lib/module/components/index.js.map +1 -1
- package/lib/module/figma/Avatar.figma.js +18 -4
- package/lib/module/figma/Avatar.figma.js.map +1 -1
- package/lib/module/figma/Drawer.Content.figma.js +23 -0
- package/lib/module/figma/Drawer.Content.figma.js.map +1 -0
- package/lib/module/figma/Drawer.Description.figma.js +14 -0
- package/lib/module/figma/Drawer.Description.figma.js.map +1 -0
- package/lib/module/figma/Drawer.Footer.figma.js +35 -0
- package/lib/module/figma/Drawer.Footer.figma.js.map +1 -0
- package/lib/module/figma/Drawer.Header.figma.js +39 -0
- package/lib/module/figma/Drawer.Header.figma.js.map +1 -0
- package/lib/module/figma/Drawer.Title.figma.js +14 -0
- package/lib/module/figma/Drawer.Title.figma.js.map +1 -0
- package/lib/module/figma/Drawer.figma.js +35 -0
- package/lib/module/figma/Drawer.figma.js.map +1 -0
- package/lib/module/figma/Menu.Item.figma.js +33 -5
- package/lib/module/figma/Menu.Item.figma.js.map +1 -1
- package/lib/module/figma/icons.figma.batch.json +262 -2
- package/lib/typescript/components/Avatar/index.d.ts +19 -1
- package/lib/typescript/components/Avatar/index.d.ts.map +1 -1
- package/lib/typescript/components/Avatar/styles.d.ts +10 -1
- package/lib/typescript/components/Avatar/styles.d.ts.map +1 -1
- package/lib/typescript/components/Drawer/animation.d.ts +9 -0
- package/lib/typescript/components/Drawer/animation.d.ts.map +1 -0
- package/lib/typescript/components/Drawer/index.d.ts +106 -0
- package/lib/typescript/components/Drawer/index.d.ts.map +1 -0
- package/lib/typescript/components/Drawer/styles.d.ts +32 -0
- package/lib/typescript/components/Drawer/styles.d.ts.map +1 -0
- package/lib/typescript/components/Drawer/utils.d.ts +23 -0
- package/lib/typescript/components/Drawer/utils.d.ts.map +1 -0
- package/lib/typescript/components/InsuranceDisclosure/styles.d.ts.map +1 -1
- package/lib/typescript/components/Menu/index.d.ts +29 -2
- package/lib/typescript/components/Menu/index.d.ts.map +1 -1
- package/lib/typescript/components/Menu/styles.d.ts +6 -2
- package/lib/typescript/components/Menu/styles.d.ts.map +1 -1
- package/lib/typescript/components/Select/index.d.ts.map +1 -1
- package/lib/typescript/components/Tabs/styles.d.ts.map +1 -1
- package/lib/typescript/components/index.d.ts +1 -0
- package/lib/typescript/components/index.d.ts.map +1 -1
- package/lib/typescript/figma/Avatar.figma.d.ts.map +1 -1
- package/lib/typescript/figma/Drawer.Content.figma.d.ts +8 -0
- package/lib/typescript/figma/Drawer.Content.figma.d.ts.map +1 -0
- package/lib/typescript/figma/Drawer.Description.figma.d.ts +8 -0
- package/lib/typescript/figma/Drawer.Description.figma.d.ts.map +1 -0
- package/lib/typescript/figma/Drawer.Footer.figma.d.ts +11 -0
- package/lib/typescript/figma/Drawer.Footer.figma.d.ts.map +1 -0
- package/lib/typescript/figma/Drawer.Header.figma.d.ts +8 -0
- package/lib/typescript/figma/Drawer.Header.figma.d.ts.map +1 -0
- package/lib/typescript/figma/Drawer.Title.figma.d.ts +8 -0
- package/lib/typescript/figma/Drawer.Title.figma.d.ts.map +1 -0
- package/lib/typescript/figma/Drawer.figma.d.ts +8 -0
- package/lib/typescript/figma/Drawer.figma.d.ts.map +1 -0
- package/lib/typescript/figma/Menu.Item.figma.d.ts.map +1 -1
- package/package.json +5 -5
- package/src/components/Avatar/index.tsx +45 -9
- package/src/components/Avatar/styles.ts +106 -23
- package/src/components/CLAUDE.md +2 -1
- package/src/components/Chip/styles.ts +1 -1
- package/src/components/Drawer/animation.ts +11 -0
- package/src/components/Drawer/index.tsx +896 -0
- package/src/components/Drawer/styles.ts +188 -0
- package/src/components/Drawer/utils.ts +83 -0
- package/src/components/InsuranceDisclosure/styles.ts +1 -3
- package/src/components/Menu/index.tsx +204 -62
- package/src/components/Menu/styles.ts +44 -54
- package/src/components/Select/index.tsx +7 -9
- package/src/components/Tabs/styles.ts +9 -10
- package/src/components/Toast/styles.ts +1 -1
- package/src/components/index.ts +1 -0
|
@@ -0,0 +1,896 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Children,
|
|
3
|
+
forwardRef,
|
|
4
|
+
isValidElement,
|
|
5
|
+
useCallback,
|
|
6
|
+
useEffect,
|
|
7
|
+
useMemo,
|
|
8
|
+
useRef,
|
|
9
|
+
useState,
|
|
10
|
+
type ComponentRef,
|
|
11
|
+
type ReactNode,
|
|
12
|
+
} from 'react';
|
|
13
|
+
import {
|
|
14
|
+
BackHandler,
|
|
15
|
+
Platform,
|
|
16
|
+
Pressable,
|
|
17
|
+
ScrollView,
|
|
18
|
+
StyleSheet,
|
|
19
|
+
useWindowDimensions,
|
|
20
|
+
View,
|
|
21
|
+
type ScrollViewProps,
|
|
22
|
+
type TextProps,
|
|
23
|
+
type ViewProps,
|
|
24
|
+
} from 'react-native';
|
|
25
|
+
import Animated, { useAnimatedStyle, useSharedValue, withTiming } from 'react-native-reanimated';
|
|
26
|
+
import { FocusScope } from '@react-native-aria/focus';
|
|
27
|
+
import { Close as CloseIcon } from '@cdx-ui/icons';
|
|
28
|
+
import {
|
|
29
|
+
DrawerClose as DrawerClosePrimitive,
|
|
30
|
+
DrawerOverlay as DrawerOverlayPrimitive,
|
|
31
|
+
DrawerPortal as DrawerPortalPrimitive,
|
|
32
|
+
DrawerRoot as DrawerRootPrimitive,
|
|
33
|
+
FullWindowOverlay,
|
|
34
|
+
MODAL_OVERLAY_Z_INDEX,
|
|
35
|
+
Slot,
|
|
36
|
+
dataAttributes,
|
|
37
|
+
mergeDataAttributes,
|
|
38
|
+
useDrawer,
|
|
39
|
+
useEscapeKey,
|
|
40
|
+
useOverlayStackRegistration,
|
|
41
|
+
type IDrawerCloseProps,
|
|
42
|
+
type IDrawerRootProps,
|
|
43
|
+
} from '@cdx-ui/primitives';
|
|
44
|
+
import { cn, mergeRefs, ParentContext, useParentContext, useStyleContext } from '@cdx-ui/utils';
|
|
45
|
+
import {
|
|
46
|
+
usePopupOverlayAnimation,
|
|
47
|
+
usePopupRootAnimation,
|
|
48
|
+
type AnimationRootDisableAll,
|
|
49
|
+
} from '../../animation';
|
|
50
|
+
import {
|
|
51
|
+
AnimationSettingsProvider,
|
|
52
|
+
useAnimationSettings,
|
|
53
|
+
} from '../../providers/animation-settings';
|
|
54
|
+
import { Heading } from '../Heading';
|
|
55
|
+
import { Icon } from '../Icon';
|
|
56
|
+
import { Text } from '../Text';
|
|
57
|
+
import { DrawerAnimationProvider, useDrawerAnimation } from './animation';
|
|
58
|
+
import {
|
|
59
|
+
type DrawerFooterVariantProps,
|
|
60
|
+
type DrawerHeaderVariantProps,
|
|
61
|
+
type DrawerPlacement,
|
|
62
|
+
type DrawerVariantProps,
|
|
63
|
+
drawerCloseIconVariants,
|
|
64
|
+
drawerCloseVariants,
|
|
65
|
+
drawerContentInnerVariants,
|
|
66
|
+
drawerContentVariants,
|
|
67
|
+
drawerDescriptionVariants,
|
|
68
|
+
drawerFooterVariants,
|
|
69
|
+
drawerHeaderContentVariants,
|
|
70
|
+
drawerHeaderVariants,
|
|
71
|
+
drawerLeadingSlotVariants,
|
|
72
|
+
drawerOverlayVariants,
|
|
73
|
+
drawerPanelVariants,
|
|
74
|
+
drawerPortalVariants,
|
|
75
|
+
drawerTitleVariants,
|
|
76
|
+
} from './styles';
|
|
77
|
+
import {
|
|
78
|
+
DRAWER_EDGE_PADDING,
|
|
79
|
+
DRAWER_ENTER_EASING,
|
|
80
|
+
DRAWER_ENTER_MS,
|
|
81
|
+
DRAWER_EXIT_EASING,
|
|
82
|
+
DRAWER_EXIT_MS,
|
|
83
|
+
DRAWER_WIDE_BREAKPOINT,
|
|
84
|
+
WEB_TRANSITION_SAFETY_MS,
|
|
85
|
+
getDrawerSlideKeyframes,
|
|
86
|
+
isHorizontalDrawerPlacement,
|
|
87
|
+
resolveDrawerPlacement,
|
|
88
|
+
useClampedDrawerExtent,
|
|
89
|
+
useSafeInsets,
|
|
90
|
+
} from './utils';
|
|
91
|
+
|
|
92
|
+
export type { DrawerPlacement } from './styles';
|
|
93
|
+
|
|
94
|
+
export { useDrawer } from '@cdx-ui/primitives';
|
|
95
|
+
|
|
96
|
+
const IS_WEB = Platform.OS === 'web';
|
|
97
|
+
|
|
98
|
+
type DrawerDataState = 'entering' | 'open' | 'closed';
|
|
99
|
+
|
|
100
|
+
// =============================================================================
|
|
101
|
+
// STYLE CONTEXT
|
|
102
|
+
// =============================================================================
|
|
103
|
+
|
|
104
|
+
const SCOPE = 'DRAWER';
|
|
105
|
+
|
|
106
|
+
const useDrawerStyleContext = () => useStyleContext(SCOPE) as DrawerVariantProps;
|
|
107
|
+
|
|
108
|
+
// =============================================================================
|
|
109
|
+
// ANIMATED PRIMITIVES
|
|
110
|
+
// =============================================================================
|
|
111
|
+
|
|
112
|
+
const AnimatedOverlay = Animated.createAnimatedComponent(DrawerOverlayPrimitive);
|
|
113
|
+
|
|
114
|
+
// =============================================================================
|
|
115
|
+
// STYLED ROOT
|
|
116
|
+
// =============================================================================
|
|
117
|
+
|
|
118
|
+
export interface DrawerRootProps
|
|
119
|
+
extends
|
|
120
|
+
Omit<ViewProps, 'children' | 'role'>,
|
|
121
|
+
IDrawerRootProps,
|
|
122
|
+
Omit<DrawerVariantProps, 'placement'> {
|
|
123
|
+
className?: string;
|
|
124
|
+
animation?: AnimationRootDisableAll;
|
|
125
|
+
/**
|
|
126
|
+
* Edge the panel attaches to. Applies to both `modal` and `permanent`.
|
|
127
|
+
* @default 'right'
|
|
128
|
+
*/
|
|
129
|
+
placement?: DrawerPlacement;
|
|
130
|
+
/**
|
|
131
|
+
* Stacking order for the modal overlay layer (backdrop + panel).
|
|
132
|
+
* Defaults to {@link MODAL_OVERLAY_Z_INDEX} (10000).
|
|
133
|
+
*/
|
|
134
|
+
zIndex?: number;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const DrawerRoot = forwardRef<View, DrawerRootProps>(
|
|
138
|
+
(
|
|
139
|
+
{
|
|
140
|
+
variant = 'permanent',
|
|
141
|
+
size = 'default',
|
|
142
|
+
placement: placementProp,
|
|
143
|
+
fullBleed = false,
|
|
144
|
+
animation,
|
|
145
|
+
className,
|
|
146
|
+
children,
|
|
147
|
+
style,
|
|
148
|
+
isOpen,
|
|
149
|
+
defaultOpen,
|
|
150
|
+
onOpenChange,
|
|
151
|
+
closeOnBackdropPress = true,
|
|
152
|
+
closeOnEscKey = true,
|
|
153
|
+
zIndex = MODAL_OVERLAY_Z_INDEX,
|
|
154
|
+
...props
|
|
155
|
+
},
|
|
156
|
+
ref,
|
|
157
|
+
) => {
|
|
158
|
+
const placement = resolveDrawerPlacement(placementProp);
|
|
159
|
+
const parent = useParentContext();
|
|
160
|
+
const ctx = useMemo(
|
|
161
|
+
() => ({ ...parent, [SCOPE]: { variant, size, placement, fullBleed } }),
|
|
162
|
+
[parent, variant, size, placement, fullBleed],
|
|
163
|
+
);
|
|
164
|
+
|
|
165
|
+
const { progress, isAllAnimationsDisabled } = usePopupRootAnimation({ animation });
|
|
166
|
+
const panelExtent = useClampedDrawerExtent(size, placement);
|
|
167
|
+
|
|
168
|
+
const animationContextValue = useMemo(() => ({ progress }), [progress]);
|
|
169
|
+
|
|
170
|
+
const animationSettingsContextValue = useMemo(
|
|
171
|
+
() => ({ isAllAnimationsDisabled }),
|
|
172
|
+
[isAllAnimationsDisabled],
|
|
173
|
+
);
|
|
174
|
+
|
|
175
|
+
if (variant === 'permanent') {
|
|
176
|
+
return (
|
|
177
|
+
<AnimationSettingsProvider value={animationSettingsContextValue}>
|
|
178
|
+
<ParentContext.Provider value={ctx}>
|
|
179
|
+
<DrawerRootPrimitive
|
|
180
|
+
ref={ref}
|
|
181
|
+
isOpen={isOpen}
|
|
182
|
+
defaultOpen={defaultOpen ?? true}
|
|
183
|
+
onOpenChange={onOpenChange}
|
|
184
|
+
closeOnBackdropPress={false}
|
|
185
|
+
closeOnEscKey={false}
|
|
186
|
+
className={cn('self-stretch overflow-hidden', className)}
|
|
187
|
+
style={style}
|
|
188
|
+
{...props}
|
|
189
|
+
{...mergeDataAttributes(props, dataAttributes({ slot: 'drawer', placement }))}
|
|
190
|
+
>
|
|
191
|
+
<DrawerPermanentPanel size={size} placement={placement} panelExtent={panelExtent}>
|
|
192
|
+
{children}
|
|
193
|
+
</DrawerPermanentPanel>
|
|
194
|
+
</DrawerRootPrimitive>
|
|
195
|
+
</ParentContext.Provider>
|
|
196
|
+
</AnimationSettingsProvider>
|
|
197
|
+
);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
return (
|
|
201
|
+
<AnimationSettingsProvider value={animationSettingsContextValue}>
|
|
202
|
+
<DrawerAnimationProvider value={animationContextValue}>
|
|
203
|
+
<ParentContext.Provider value={ctx}>
|
|
204
|
+
<DrawerRootPrimitive
|
|
205
|
+
ref={ref}
|
|
206
|
+
isOpen={isOpen}
|
|
207
|
+
defaultOpen={defaultOpen}
|
|
208
|
+
onOpenChange={onOpenChange}
|
|
209
|
+
closeOnBackdropPress={closeOnBackdropPress}
|
|
210
|
+
closeOnEscKey={closeOnEscKey}
|
|
211
|
+
className={className}
|
|
212
|
+
style={style}
|
|
213
|
+
{...props}
|
|
214
|
+
{...mergeDataAttributes(props, dataAttributes({ slot: 'drawer', placement }))}
|
|
215
|
+
>
|
|
216
|
+
<DrawerPopup zIndex={zIndex}>{children}</DrawerPopup>
|
|
217
|
+
</DrawerRootPrimitive>
|
|
218
|
+
</ParentContext.Provider>
|
|
219
|
+
</DrawerAnimationProvider>
|
|
220
|
+
</AnimationSettingsProvider>
|
|
221
|
+
);
|
|
222
|
+
},
|
|
223
|
+
);
|
|
224
|
+
|
|
225
|
+
DrawerRoot.displayName = 'Drawer';
|
|
226
|
+
|
|
227
|
+
interface DrawerPermanentPanelProps {
|
|
228
|
+
size: DrawerVariantProps['size'];
|
|
229
|
+
placement: DrawerPlacement;
|
|
230
|
+
panelExtent: number;
|
|
231
|
+
children?: ReactNode;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
const DrawerPermanentPanel = ({
|
|
235
|
+
size,
|
|
236
|
+
placement,
|
|
237
|
+
panelExtent,
|
|
238
|
+
children,
|
|
239
|
+
}: DrawerPermanentPanelProps) => {
|
|
240
|
+
const { open } = useDrawer();
|
|
241
|
+
const { isAllAnimationsDisabled } = useAnimationSettings();
|
|
242
|
+
const isHorizontal = isHorizontalDrawerPlacement(placement);
|
|
243
|
+
const extent = useSharedValue(open ? panelExtent : 0);
|
|
244
|
+
const hasMounted = useRef(false);
|
|
245
|
+
|
|
246
|
+
useEffect(() => {
|
|
247
|
+
const target = open ? panelExtent : 0;
|
|
248
|
+
|
|
249
|
+
if (!hasMounted.current) {
|
|
250
|
+
hasMounted.current = true;
|
|
251
|
+
extent.set(target);
|
|
252
|
+
return;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
if (isAllAnimationsDisabled) {
|
|
256
|
+
extent.set(target);
|
|
257
|
+
return;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
extent.set(
|
|
261
|
+
withTiming(target, {
|
|
262
|
+
duration: open ? DRAWER_ENTER_MS : DRAWER_EXIT_MS,
|
|
263
|
+
easing: open ? DRAWER_ENTER_EASING : DRAWER_EXIT_EASING,
|
|
264
|
+
}),
|
|
265
|
+
);
|
|
266
|
+
}, [extent, isAllAnimationsDisabled, open, panelExtent]);
|
|
267
|
+
|
|
268
|
+
const animatedStyle = useAnimatedStyle(() => {
|
|
269
|
+
const value = extent.get();
|
|
270
|
+
return isHorizontal ? { width: value } : { height: value };
|
|
271
|
+
}, [isHorizontal]);
|
|
272
|
+
|
|
273
|
+
return (
|
|
274
|
+
<Animated.View
|
|
275
|
+
className={drawerPanelVariants({ size, variant: 'permanent', placement })}
|
|
276
|
+
pointerEvents={open ? 'auto' : 'none'}
|
|
277
|
+
style={animatedStyle}
|
|
278
|
+
>
|
|
279
|
+
<View
|
|
280
|
+
className="h-full"
|
|
281
|
+
style={isHorizontal ? { width: panelExtent } : { height: panelExtent }}
|
|
282
|
+
>
|
|
283
|
+
{open ? children : null}
|
|
284
|
+
</View>
|
|
285
|
+
</Animated.View>
|
|
286
|
+
);
|
|
287
|
+
};
|
|
288
|
+
|
|
289
|
+
// =============================================================================
|
|
290
|
+
// INTERNAL POPUP (portal + overlay + panel — not a Figma slot)
|
|
291
|
+
// =============================================================================
|
|
292
|
+
|
|
293
|
+
interface DrawerPopupProps {
|
|
294
|
+
className?: string;
|
|
295
|
+
children?: ReactNode;
|
|
296
|
+
disableFullWindowOverlay?: boolean;
|
|
297
|
+
zIndex?: number;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
const DrawerPopup = forwardRef<View, DrawerPopupProps>(
|
|
301
|
+
(
|
|
302
|
+
{ className, children, disableFullWindowOverlay = false, zIndex = MODAL_OVERLAY_Z_INDEX },
|
|
303
|
+
ref,
|
|
304
|
+
) => {
|
|
305
|
+
const { size, placement: placementProp } = useDrawerStyleContext();
|
|
306
|
+
const placement = resolveDrawerPlacement(placementProp);
|
|
307
|
+
const { open, onOpenChange, closeOnEscKey, nativeID } = useDrawer();
|
|
308
|
+
const { width: windowWidth, height: windowHeight } = useWindowDimensions();
|
|
309
|
+
const panelExtent = useClampedDrawerExtent(size, placement);
|
|
310
|
+
const parentContext = useParentContext();
|
|
311
|
+
|
|
312
|
+
const animationContext = useDrawerAnimation();
|
|
313
|
+
const parentAnimationSettings = useAnimationSettings();
|
|
314
|
+
const { isAllAnimationsDisabled } = parentAnimationSettings;
|
|
315
|
+
const panelMotion = useMemo(
|
|
316
|
+
() => getDrawerSlideKeyframes(placement, panelExtent),
|
|
317
|
+
[panelExtent, placement],
|
|
318
|
+
);
|
|
319
|
+
|
|
320
|
+
const fallbackIsDragging = useSharedValue(false);
|
|
321
|
+
const fallbackIsGestureReleaseAnimationRunning = useSharedValue(false);
|
|
322
|
+
|
|
323
|
+
const progress = animationContext.progress;
|
|
324
|
+
|
|
325
|
+
useEffect(() => {
|
|
326
|
+
if (open) {
|
|
327
|
+
progress.set(1);
|
|
328
|
+
}
|
|
329
|
+
}, [open, progress]);
|
|
330
|
+
|
|
331
|
+
const {
|
|
332
|
+
rContainerStyle,
|
|
333
|
+
entering: overlayEntering,
|
|
334
|
+
exiting: overlayExiting,
|
|
335
|
+
} = usePopupOverlayAnimation({
|
|
336
|
+
progress,
|
|
337
|
+
isDragging: fallbackIsDragging,
|
|
338
|
+
isGestureReleaseAnimationRunning: fallbackIsGestureReleaseAnimationRunning,
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
const panelRef = useRef<View | null>(null);
|
|
342
|
+
const prevOpenRef = useRef(open);
|
|
343
|
+
const [isExiting, setIsExiting] = useState(false);
|
|
344
|
+
const [webState, setWebState] = useState<DrawerDataState>(open ? 'entering' : 'closed');
|
|
345
|
+
|
|
346
|
+
const handleClose = useCallback(() => {
|
|
347
|
+
onOpenChange(false);
|
|
348
|
+
}, [onOpenChange]);
|
|
349
|
+
|
|
350
|
+
useEscapeKey(open && closeOnEscKey, handleClose, {
|
|
351
|
+
id: nativeID,
|
|
352
|
+
groupId: nativeID,
|
|
353
|
+
layer: 'modal',
|
|
354
|
+
});
|
|
355
|
+
const { isTopmost } = useOverlayStackRegistration({
|
|
356
|
+
active: open && Platform.OS !== 'web',
|
|
357
|
+
capabilities: ['back'],
|
|
358
|
+
id: nativeID,
|
|
359
|
+
groupId: nativeID,
|
|
360
|
+
layer: 'modal',
|
|
361
|
+
});
|
|
362
|
+
|
|
363
|
+
useEffect(() => {
|
|
364
|
+
if (!open || Platform.OS === 'web') return;
|
|
365
|
+
|
|
366
|
+
const backHandler = BackHandler.addEventListener('hardwareBackPress', () => {
|
|
367
|
+
if (!isTopmost('back')) return false;
|
|
368
|
+
handleClose();
|
|
369
|
+
return true;
|
|
370
|
+
});
|
|
371
|
+
|
|
372
|
+
return () => {
|
|
373
|
+
backHandler.remove();
|
|
374
|
+
};
|
|
375
|
+
}, [handleClose, isTopmost, open]);
|
|
376
|
+
|
|
377
|
+
if (IS_WEB && prevOpenRef.current && !open && !isExiting) {
|
|
378
|
+
setIsExiting(true);
|
|
379
|
+
setWebState('closed');
|
|
380
|
+
}
|
|
381
|
+
if (IS_WEB && !prevOpenRef.current && open) {
|
|
382
|
+
setIsExiting(false);
|
|
383
|
+
setWebState('entering');
|
|
384
|
+
}
|
|
385
|
+
prevOpenRef.current = open;
|
|
386
|
+
|
|
387
|
+
useEffect(() => {
|
|
388
|
+
if (!IS_WEB || !open) return;
|
|
389
|
+
const raf = requestAnimationFrame(() => {
|
|
390
|
+
setWebState('open');
|
|
391
|
+
});
|
|
392
|
+
return () => cancelAnimationFrame(raf);
|
|
393
|
+
}, [open]);
|
|
394
|
+
|
|
395
|
+
useEffect(() => {
|
|
396
|
+
if (!IS_WEB || !isExiting) return;
|
|
397
|
+
|
|
398
|
+
const node = panelRef.current as unknown as HTMLElement | null;
|
|
399
|
+
let done = false;
|
|
400
|
+
|
|
401
|
+
const finish = () => {
|
|
402
|
+
if (done) return;
|
|
403
|
+
done = true;
|
|
404
|
+
setIsExiting(false);
|
|
405
|
+
};
|
|
406
|
+
|
|
407
|
+
const safetyTimer = setTimeout(finish, WEB_TRANSITION_SAFETY_MS);
|
|
408
|
+
|
|
409
|
+
if (!node || typeof node.addEventListener !== 'function') {
|
|
410
|
+
return () => {
|
|
411
|
+
clearTimeout(safetyTimer);
|
|
412
|
+
};
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
const handler = (event: Event) => {
|
|
416
|
+
if (event.target !== node) return;
|
|
417
|
+
const propertyName = (event as unknown as { propertyName?: string }).propertyName ?? '';
|
|
418
|
+
if (propertyName === 'opacity' || propertyName === 'transform') {
|
|
419
|
+
finish();
|
|
420
|
+
}
|
|
421
|
+
};
|
|
422
|
+
node.addEventListener('transitionend', handler);
|
|
423
|
+
|
|
424
|
+
return () => {
|
|
425
|
+
clearTimeout(safetyTimer);
|
|
426
|
+
node.removeEventListener('transitionend', handler);
|
|
427
|
+
};
|
|
428
|
+
}, [isExiting]);
|
|
429
|
+
|
|
430
|
+
const dataState: DrawerDataState = webState;
|
|
431
|
+
const shouldMount = IS_WEB ? open || isExiting : open;
|
|
432
|
+
|
|
433
|
+
if (!shouldMount) {
|
|
434
|
+
return null;
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
const portalClassName = cn(drawerPortalVariants());
|
|
438
|
+
const overlayClassName = cn(drawerOverlayVariants());
|
|
439
|
+
const panelClassName = cn(
|
|
440
|
+
drawerPanelVariants({ size, variant: 'modal', placement }),
|
|
441
|
+
className,
|
|
442
|
+
);
|
|
443
|
+
const panelSizeStyle = isHorizontalDrawerPlacement(placement)
|
|
444
|
+
? { width: panelExtent, height: windowHeight }
|
|
445
|
+
: { width: windowWidth, height: panelExtent };
|
|
446
|
+
const nativePanelEntering =
|
|
447
|
+
IS_WEB || isAllAnimationsDisabled ? undefined : panelMotion.entering;
|
|
448
|
+
const nativePanelExiting = IS_WEB || isAllAnimationsDisabled ? undefined : panelMotion.exiting;
|
|
449
|
+
|
|
450
|
+
return (
|
|
451
|
+
<DrawerPortalPrimitive forceMount>
|
|
452
|
+
<ParentContext.Provider value={parentContext}>
|
|
453
|
+
<AnimationSettingsProvider value={parentAnimationSettings}>
|
|
454
|
+
<DrawerAnimationProvider value={animationContext}>
|
|
455
|
+
<FullWindowOverlay
|
|
456
|
+
disableFullWindowOverlay={disableFullWindowOverlay}
|
|
457
|
+
unstable_accessibilityContainerViewIsModal
|
|
458
|
+
>
|
|
459
|
+
<View
|
|
460
|
+
className={portalClassName}
|
|
461
|
+
style={{ zIndex }}
|
|
462
|
+
pointerEvents="box-none"
|
|
463
|
+
{...(IS_WEB ? dataAttributes({ state: dataState }) : undefined)}
|
|
464
|
+
>
|
|
465
|
+
<Animated.View
|
|
466
|
+
entering={overlayEntering}
|
|
467
|
+
exiting={overlayExiting}
|
|
468
|
+
style={StyleSheet.absoluteFill}
|
|
469
|
+
>
|
|
470
|
+
<AnimatedOverlay
|
|
471
|
+
forceMount
|
|
472
|
+
testID="drawer-overlay"
|
|
473
|
+
className={overlayClassName}
|
|
474
|
+
style={IS_WEB ? undefined : rContainerStyle}
|
|
475
|
+
{...mergeDataAttributes(
|
|
476
|
+
{},
|
|
477
|
+
dataAttributes({ slot: 'drawer-overlay' }),
|
|
478
|
+
IS_WEB ? dataAttributes({ state: dataState }) : undefined,
|
|
479
|
+
)}
|
|
480
|
+
/>
|
|
481
|
+
</Animated.View>
|
|
482
|
+
|
|
483
|
+
<Animated.View
|
|
484
|
+
entering={nativePanelEntering}
|
|
485
|
+
exiting={nativePanelExiting}
|
|
486
|
+
pointerEvents="box-none"
|
|
487
|
+
style={[internalStyles[placement], panelSizeStyle]}
|
|
488
|
+
>
|
|
489
|
+
<FocusScope contain={open} restoreFocus autoFocus={open}>
|
|
490
|
+
<View
|
|
491
|
+
ref={mergeRefs(ref, panelRef)}
|
|
492
|
+
nativeID={nativeID}
|
|
493
|
+
role="dialog"
|
|
494
|
+
aria-modal={true}
|
|
495
|
+
aria-labelledby={`${nativeID}-title`}
|
|
496
|
+
aria-describedby={`${nativeID}-description`}
|
|
497
|
+
className={panelClassName}
|
|
498
|
+
style={panelSizeStyle}
|
|
499
|
+
{...mergeDataAttributes(
|
|
500
|
+
{},
|
|
501
|
+
dataAttributes({ slot: 'drawer', placement }),
|
|
502
|
+
IS_WEB ? dataAttributes({ state: dataState }) : undefined,
|
|
503
|
+
)}
|
|
504
|
+
>
|
|
505
|
+
{children}
|
|
506
|
+
</View>
|
|
507
|
+
</FocusScope>
|
|
508
|
+
</Animated.View>
|
|
509
|
+
</View>
|
|
510
|
+
</FullWindowOverlay>
|
|
511
|
+
</DrawerAnimationProvider>
|
|
512
|
+
</AnimationSettingsProvider>
|
|
513
|
+
</ParentContext.Provider>
|
|
514
|
+
</DrawerPortalPrimitive>
|
|
515
|
+
);
|
|
516
|
+
},
|
|
517
|
+
);
|
|
518
|
+
|
|
519
|
+
DrawerPopup.displayName = 'DrawerPopup';
|
|
520
|
+
|
|
521
|
+
export interface DrawerCloseProps extends IDrawerCloseProps {
|
|
522
|
+
className?: string;
|
|
523
|
+
/** Replaces the default close glyph. */
|
|
524
|
+
children?: ReactNode;
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
const DrawerClose = forwardRef<ComponentRef<typeof Pressable>, DrawerCloseProps>(
|
|
528
|
+
({ className, accessibilityLabel = 'Close', children, style, ...props }, ref) => (
|
|
529
|
+
<DrawerClosePrimitive
|
|
530
|
+
ref={ref}
|
|
531
|
+
className={cn(drawerCloseVariants(), className)}
|
|
532
|
+
style={style}
|
|
533
|
+
hitSlop={12}
|
|
534
|
+
accessibilityRole="button"
|
|
535
|
+
accessibilityLabel={accessibilityLabel}
|
|
536
|
+
{...props}
|
|
537
|
+
{...mergeDataAttributes(props, dataAttributes({ slot: 'drawer-close' }))}
|
|
538
|
+
>
|
|
539
|
+
{children ?? <Icon as={CloseIcon} className={drawerCloseIconVariants()} />}
|
|
540
|
+
</DrawerClosePrimitive>
|
|
541
|
+
),
|
|
542
|
+
);
|
|
543
|
+
|
|
544
|
+
DrawerClose.displayName = 'Drawer.Close';
|
|
545
|
+
|
|
546
|
+
// =============================================================================
|
|
547
|
+
// STYLED LEADING SLOT
|
|
548
|
+
// =============================================================================
|
|
549
|
+
|
|
550
|
+
export interface DrawerLeadingSlotProps extends ViewProps {
|
|
551
|
+
asChild?: boolean;
|
|
552
|
+
className?: string;
|
|
553
|
+
'aria-hidden'?: boolean;
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
const DrawerLeadingSlot = forwardRef<View, DrawerLeadingSlotProps>(
|
|
557
|
+
({ asChild, className, style, 'aria-hidden': ariaHidden, ...props }, ref) => {
|
|
558
|
+
const Comp = asChild ? Slot : View;
|
|
559
|
+
return (
|
|
560
|
+
<Comp
|
|
561
|
+
ref={ref}
|
|
562
|
+
accessibilityElementsHidden={ariaHidden !== false}
|
|
563
|
+
aria-hidden={ariaHidden ?? true}
|
|
564
|
+
className={cn(drawerLeadingSlotVariants(), className)}
|
|
565
|
+
style={style}
|
|
566
|
+
{...props}
|
|
567
|
+
{...mergeDataAttributes(props, dataAttributes({ slot: 'drawer-leading' }))}
|
|
568
|
+
/>
|
|
569
|
+
);
|
|
570
|
+
},
|
|
571
|
+
);
|
|
572
|
+
|
|
573
|
+
DrawerLeadingSlot.displayName = 'Drawer.LeadingSlot';
|
|
574
|
+
|
|
575
|
+
// =============================================================================
|
|
576
|
+
// STYLED HEADER
|
|
577
|
+
// =============================================================================
|
|
578
|
+
|
|
579
|
+
/**
|
|
580
|
+
* Props for `Drawer.Header`.
|
|
581
|
+
*
|
|
582
|
+
* The header lays out its direct children as a fixed row regardless of JSX
|
|
583
|
+
* order: the first `Drawer.LeadingSlot` at the start, every other child
|
|
584
|
+
* (`Drawer.Title`, `Drawer.Description`, custom content) in a text column, and
|
|
585
|
+
* the close control at the end. Additional `Drawer.LeadingSlot` or
|
|
586
|
+
* `Drawer.Close` children are ignored.
|
|
587
|
+
*
|
|
588
|
+
* `Drawer.LeadingSlot` and `Drawer.Close` must be direct children. They are
|
|
589
|
+
* matched by component identity, so wrapping them in `memo()`, an HOC, or a
|
|
590
|
+
* custom component hides them from the header and they render in the text
|
|
591
|
+
* column instead.
|
|
592
|
+
*/
|
|
593
|
+
export interface DrawerHeaderProps extends ViewProps, DrawerHeaderVariantProps {
|
|
594
|
+
/**
|
|
595
|
+
* Substitute the header element with the single child. The child is rendered
|
|
596
|
+
* unchanged: no row partition and no close control are applied, and `close`
|
|
597
|
+
* is ignored.
|
|
598
|
+
*/
|
|
599
|
+
asChild?: boolean;
|
|
600
|
+
/**
|
|
601
|
+
* Close control rendered at the end of the row.
|
|
602
|
+
*
|
|
603
|
+
* - omitted: renders a composed `Drawer.Close` child if present, otherwise a
|
|
604
|
+
* default `<Drawer.Close />`
|
|
605
|
+
* - `null`: renders no close control
|
|
606
|
+
* - a node: rendered in place of the default and any `Drawer.Close` child
|
|
607
|
+
*/
|
|
608
|
+
close?: ReactNode;
|
|
609
|
+
className?: string;
|
|
610
|
+
children?: ReactNode;
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
const renderDrawerHeaderRow = (children: ReactNode, close: ReactNode) => {
|
|
614
|
+
const childArray = Children.toArray(children);
|
|
615
|
+
const leadingChild = childArray.find(
|
|
616
|
+
(child) => isValidElement(child) && child.type === DrawerLeadingSlot,
|
|
617
|
+
);
|
|
618
|
+
const closeChild = childArray.find(
|
|
619
|
+
(child) => isValidElement(child) && child.type === DrawerClose,
|
|
620
|
+
);
|
|
621
|
+
const textChildren = childArray.filter(
|
|
622
|
+
(child) =>
|
|
623
|
+
!isValidElement(child) || (child.type !== DrawerLeadingSlot && child.type !== DrawerClose),
|
|
624
|
+
);
|
|
625
|
+
const closeElement = close === undefined ? (closeChild ?? <DrawerClose />) : close;
|
|
626
|
+
|
|
627
|
+
return (
|
|
628
|
+
<>
|
|
629
|
+
{leadingChild}
|
|
630
|
+
{textChildren.length > 0 ? (
|
|
631
|
+
<View className={drawerHeaderContentVariants()}>{textChildren}</View>
|
|
632
|
+
) : null}
|
|
633
|
+
{closeElement}
|
|
634
|
+
</>
|
|
635
|
+
);
|
|
636
|
+
};
|
|
637
|
+
|
|
638
|
+
const DrawerHeader = forwardRef<View, DrawerHeaderProps>(
|
|
639
|
+
({ asChild, close, showSeparator = true, className, children, style, ...props }, ref) => {
|
|
640
|
+
const insets = useSafeInsets();
|
|
641
|
+
const { placement: placementProp, variant } = useDrawerStyleContext();
|
|
642
|
+
const placement = resolveDrawerPlacement(placementProp);
|
|
643
|
+
const paddingTop =
|
|
644
|
+
variant === 'modal' && placement !== 'bottom'
|
|
645
|
+
? Math.max(insets.top, DRAWER_EDGE_PADDING)
|
|
646
|
+
: DRAWER_EDGE_PADDING;
|
|
647
|
+
const computedClassName = cn(drawerHeaderVariants({ showSeparator }), className);
|
|
648
|
+
const Comp = asChild ? Slot : View;
|
|
649
|
+
|
|
650
|
+
return (
|
|
651
|
+
<Comp
|
|
652
|
+
ref={ref}
|
|
653
|
+
className={computedClassName}
|
|
654
|
+
style={[{ paddingTop }, style]}
|
|
655
|
+
{...props}
|
|
656
|
+
{...mergeDataAttributes(props, dataAttributes({ slot: 'drawer-header' }))}
|
|
657
|
+
>
|
|
658
|
+
{asChild ? children : renderDrawerHeaderRow(children, close)}
|
|
659
|
+
</Comp>
|
|
660
|
+
);
|
|
661
|
+
},
|
|
662
|
+
);
|
|
663
|
+
|
|
664
|
+
DrawerHeader.displayName = 'Drawer.Header';
|
|
665
|
+
|
|
666
|
+
// =============================================================================
|
|
667
|
+
// STYLED TITLE
|
|
668
|
+
// =============================================================================
|
|
669
|
+
|
|
670
|
+
export interface DrawerTitleProps extends TextProps {
|
|
671
|
+
asChild?: boolean;
|
|
672
|
+
className?: string;
|
|
673
|
+
children?: ReactNode;
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
const DrawerTitle = forwardRef<View, DrawerTitleProps>(
|
|
677
|
+
({ asChild, className, children, style, ...props }, ref) => {
|
|
678
|
+
const { nativeID } = useDrawer();
|
|
679
|
+
const computedClassName = cn(drawerTitleVariants(), className);
|
|
680
|
+
|
|
681
|
+
if (asChild) {
|
|
682
|
+
return (
|
|
683
|
+
<Slot
|
|
684
|
+
ref={ref}
|
|
685
|
+
nativeID={`${nativeID}-title`}
|
|
686
|
+
className={computedClassName}
|
|
687
|
+
style={style}
|
|
688
|
+
{...props}
|
|
689
|
+
{...mergeDataAttributes(props, dataAttributes({ slot: 'drawer-title' }))}
|
|
690
|
+
>
|
|
691
|
+
{children}
|
|
692
|
+
</Slot>
|
|
693
|
+
);
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
return (
|
|
697
|
+
<Heading
|
|
698
|
+
ref={ref}
|
|
699
|
+
size="sm"
|
|
700
|
+
nativeID={`${nativeID}-title`}
|
|
701
|
+
className={computedClassName}
|
|
702
|
+
style={style}
|
|
703
|
+
{...props}
|
|
704
|
+
{...mergeDataAttributes(props, dataAttributes({ slot: 'drawer-title' }))}
|
|
705
|
+
>
|
|
706
|
+
{children}
|
|
707
|
+
</Heading>
|
|
708
|
+
);
|
|
709
|
+
},
|
|
710
|
+
);
|
|
711
|
+
|
|
712
|
+
DrawerTitle.displayName = 'Drawer.Title';
|
|
713
|
+
|
|
714
|
+
// =============================================================================
|
|
715
|
+
// STYLED DESCRIPTION
|
|
716
|
+
// =============================================================================
|
|
717
|
+
|
|
718
|
+
export interface DrawerDescriptionProps extends TextProps {
|
|
719
|
+
asChild?: boolean;
|
|
720
|
+
className?: string;
|
|
721
|
+
children?: ReactNode;
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
const DrawerDescription = forwardRef<View, DrawerDescriptionProps>(
|
|
725
|
+
({ asChild, className, children, style, ...props }, ref) => {
|
|
726
|
+
const { nativeID } = useDrawer();
|
|
727
|
+
const computedClassName = cn(drawerDescriptionVariants(), className);
|
|
728
|
+
const Comp = asChild ? Slot : Text;
|
|
729
|
+
|
|
730
|
+
return (
|
|
731
|
+
<Comp
|
|
732
|
+
ref={ref}
|
|
733
|
+
nativeID={`${nativeID}-description`}
|
|
734
|
+
size="sm"
|
|
735
|
+
className={computedClassName}
|
|
736
|
+
style={style}
|
|
737
|
+
{...props}
|
|
738
|
+
{...mergeDataAttributes(props, dataAttributes({ slot: 'drawer-description' }))}
|
|
739
|
+
>
|
|
740
|
+
{children}
|
|
741
|
+
</Comp>
|
|
742
|
+
);
|
|
743
|
+
},
|
|
744
|
+
);
|
|
745
|
+
|
|
746
|
+
DrawerDescription.displayName = 'Drawer.Description';
|
|
747
|
+
|
|
748
|
+
// =============================================================================
|
|
749
|
+
// STYLED CONTENT (Figma Drawer.Content — scroll region)
|
|
750
|
+
// =============================================================================
|
|
751
|
+
|
|
752
|
+
export interface DrawerContentProps extends ScrollViewProps {
|
|
753
|
+
className?: string;
|
|
754
|
+
contentClassName?: string;
|
|
755
|
+
children?: ReactNode;
|
|
756
|
+
fullBleed?: boolean;
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
const DrawerContent = forwardRef<ScrollView, DrawerContentProps>(
|
|
760
|
+
(
|
|
761
|
+
{
|
|
762
|
+
className,
|
|
763
|
+
contentClassName,
|
|
764
|
+
children,
|
|
765
|
+
style,
|
|
766
|
+
contentContainerStyle,
|
|
767
|
+
fullBleed: fullBleedProp,
|
|
768
|
+
...props
|
|
769
|
+
},
|
|
770
|
+
ref,
|
|
771
|
+
) => {
|
|
772
|
+
const { fullBleed: contextFullBleed } = useDrawerStyleContext();
|
|
773
|
+
const fullBleed = fullBleedProp ?? contextFullBleed ?? false;
|
|
774
|
+
|
|
775
|
+
return (
|
|
776
|
+
<ScrollView
|
|
777
|
+
ref={ref}
|
|
778
|
+
className={cn(drawerContentVariants({ fullBleed }), className)}
|
|
779
|
+
style={style}
|
|
780
|
+
contentContainerClassName={cn(drawerContentInnerVariants(), contentClassName)}
|
|
781
|
+
contentContainerStyle={contentContainerStyle}
|
|
782
|
+
{...props}
|
|
783
|
+
{...mergeDataAttributes(props, dataAttributes({ slot: 'drawer-content' }))}
|
|
784
|
+
>
|
|
785
|
+
{children}
|
|
786
|
+
</ScrollView>
|
|
787
|
+
);
|
|
788
|
+
},
|
|
789
|
+
);
|
|
790
|
+
|
|
791
|
+
DrawerContent.displayName = 'Drawer.Content';
|
|
792
|
+
|
|
793
|
+
// =============================================================================
|
|
794
|
+
// STYLED FOOTER
|
|
795
|
+
// =============================================================================
|
|
796
|
+
|
|
797
|
+
export interface DrawerFooterProps extends ViewProps, DrawerFooterVariantProps {
|
|
798
|
+
asChild?: boolean;
|
|
799
|
+
className?: string;
|
|
800
|
+
children?: ReactNode;
|
|
801
|
+
direction?: 'row' | 'column';
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
const DrawerFooter = forwardRef<View, DrawerFooterProps>(
|
|
805
|
+
(
|
|
806
|
+
{
|
|
807
|
+
asChild,
|
|
808
|
+
showSeparator = false,
|
|
809
|
+
direction: directionProp,
|
|
810
|
+
className,
|
|
811
|
+
children,
|
|
812
|
+
style,
|
|
813
|
+
...props
|
|
814
|
+
},
|
|
815
|
+
ref,
|
|
816
|
+
) => {
|
|
817
|
+
const { width } = useWindowDimensions();
|
|
818
|
+
const insets = useSafeInsets();
|
|
819
|
+
const { placement: placementProp, variant } = useDrawerStyleContext();
|
|
820
|
+
const placement = resolveDrawerPlacement(placementProp);
|
|
821
|
+
const direction = directionProp ?? (width >= DRAWER_WIDE_BREAKPOINT ? 'row' : 'column');
|
|
822
|
+
const computedClassName = cn(drawerFooterVariants({ showSeparator, direction }), className);
|
|
823
|
+
const Comp = asChild ? Slot : View;
|
|
824
|
+
const paddingBottom =
|
|
825
|
+
variant === 'modal' && placement !== 'top'
|
|
826
|
+
? Math.max(insets.bottom, DRAWER_EDGE_PADDING)
|
|
827
|
+
: DRAWER_EDGE_PADDING;
|
|
828
|
+
|
|
829
|
+
return (
|
|
830
|
+
<Comp
|
|
831
|
+
ref={ref}
|
|
832
|
+
className={computedClassName}
|
|
833
|
+
style={[{ paddingBottom }, style]}
|
|
834
|
+
{...props}
|
|
835
|
+
{...mergeDataAttributes(props, dataAttributes({ slot: 'drawer-footer' }))}
|
|
836
|
+
>
|
|
837
|
+
{children}
|
|
838
|
+
</Comp>
|
|
839
|
+
);
|
|
840
|
+
},
|
|
841
|
+
);
|
|
842
|
+
|
|
843
|
+
DrawerFooter.displayName = 'Drawer.Footer';
|
|
844
|
+
|
|
845
|
+
// =============================================================================
|
|
846
|
+
// COMPOUND COMPONENT
|
|
847
|
+
// =============================================================================
|
|
848
|
+
|
|
849
|
+
type DrawerCompoundComponent = typeof DrawerRoot & {
|
|
850
|
+
Header: typeof DrawerHeader;
|
|
851
|
+
LeadingSlot: typeof DrawerLeadingSlot;
|
|
852
|
+
Close: typeof DrawerClose;
|
|
853
|
+
Title: typeof DrawerTitle;
|
|
854
|
+
Description: typeof DrawerDescription;
|
|
855
|
+
Content: typeof DrawerContent;
|
|
856
|
+
Footer: typeof DrawerFooter;
|
|
857
|
+
};
|
|
858
|
+
|
|
859
|
+
export const Drawer = Object.assign(DrawerRoot, {
|
|
860
|
+
Header: DrawerHeader,
|
|
861
|
+
LeadingSlot: DrawerLeadingSlot,
|
|
862
|
+
Close: DrawerClose,
|
|
863
|
+
Title: DrawerTitle,
|
|
864
|
+
Description: DrawerDescription,
|
|
865
|
+
Content: DrawerContent,
|
|
866
|
+
Footer: DrawerFooter,
|
|
867
|
+
}) as DrawerCompoundComponent;
|
|
868
|
+
|
|
869
|
+
const internalStyles = StyleSheet.create({
|
|
870
|
+
right: {
|
|
871
|
+
position: 'absolute',
|
|
872
|
+
top: 0,
|
|
873
|
+
bottom: 0,
|
|
874
|
+
right: 0,
|
|
875
|
+
},
|
|
876
|
+
left: {
|
|
877
|
+
position: 'absolute',
|
|
878
|
+
top: 0,
|
|
879
|
+
bottom: 0,
|
|
880
|
+
left: 0,
|
|
881
|
+
},
|
|
882
|
+
top: {
|
|
883
|
+
position: 'absolute',
|
|
884
|
+
top: 0,
|
|
885
|
+
left: 0,
|
|
886
|
+
right: 0,
|
|
887
|
+
},
|
|
888
|
+
bottom: {
|
|
889
|
+
position: 'absolute',
|
|
890
|
+
bottom: 0,
|
|
891
|
+
left: 0,
|
|
892
|
+
right: 0,
|
|
893
|
+
},
|
|
894
|
+
});
|
|
895
|
+
|
|
896
|
+
export { MODAL_OVERLAY_Z_INDEX };
|