panelui-native 0.32.0 → 0.34.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/module/components/button/index.js +106 -11
- package/lib/module/components/button/index.js.map +1 -1
- package/lib/module/components/drawer/index.js +477 -0
- package/lib/module/components/drawer/index.js.map +1 -0
- package/lib/module/components/panelside/index.js +1024 -0
- package/lib/module/components/panelside/index.js.map +1 -0
- package/lib/module/components/select/index.js +14 -3
- package/lib/module/components/select/index.js.map +1 -1
- package/lib/module/icons/index.js +20 -0
- package/lib/module/icons/index.js.map +1 -1
- package/lib/module/index.js +3 -1
- package/lib/module/index.js.map +1 -1
- package/lib/module/native/index.js +30 -0
- package/lib/module/native/index.js.map +1 -1
- package/lib/typescript/src/components/button/index.d.ts +22 -1
- package/lib/typescript/src/components/button/index.d.ts.map +1 -1
- package/lib/typescript/src/components/drawer/index.d.ts +146 -0
- package/lib/typescript/src/components/drawer/index.d.ts.map +1 -0
- package/lib/typescript/src/components/panelside/index.d.ts +354 -0
- package/lib/typescript/src/components/panelside/index.d.ts.map +1 -0
- package/lib/typescript/src/components/select/index.d.ts.map +1 -1
- package/lib/typescript/src/icons/index.d.ts +1 -0
- package/lib/typescript/src/icons/index.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts +3 -1
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/native/index.d.ts +20 -0
- package/lib/typescript/src/native/index.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/components/button/index.tsx +140 -8
- package/src/components/drawer/index.tsx +662 -0
- package/src/components/panelside/index.tsx +1358 -0
- package/src/components/select/index.tsx +23 -3
- package/src/icons/index.tsx +14 -0
- package/src/index.ts +33 -0
- package/src/native/index.ts +51 -0
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
3
|
import { forwardRef } from 'react';
|
|
4
|
+
import { View } from 'react-native';
|
|
4
5
|
import { tv } from 'tailwind-variants';
|
|
5
6
|
import { useCSSVariable } from 'uniwind';
|
|
6
7
|
import { AnimatedPressable } from "../../primitives/animated-pressable.js";
|
|
7
8
|
import { Text, textChildren } from "../../primitives/text.js";
|
|
8
9
|
import { IconColorProvider } from "../../icons/index.js";
|
|
9
|
-
import { getNativeUI } from "../../native/index.js";
|
|
10
|
+
import { getNativeUI, getSwiftUIModifiers } from "../../native/index.js";
|
|
10
11
|
import { Spinner } from "../spinner/index.js";
|
|
11
|
-
import { jsx as _jsx,
|
|
12
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
12
13
|
const buttonVariants = tv({
|
|
13
14
|
slots: {
|
|
14
15
|
root: 'flex-row items-center justify-center gap-2 rounded-lg border border-transparent',
|
|
@@ -121,6 +122,50 @@ const NATIVE_HEIGHT = {
|
|
|
121
122
|
icon: 44
|
|
122
123
|
};
|
|
123
124
|
|
|
125
|
+
/**
|
|
126
|
+
* Room around the glyph in a native icon button, applied to the glyph itself.
|
|
127
|
+
*
|
|
128
|
+
* Not to the button, and that distinction is the whole thing. SwiftUI's
|
|
129
|
+
* `padding` on a Button pads *outside* its background — the chrome stays the
|
|
130
|
+
* size it was and moves inward — and `frame` only sets the layout box, leaving
|
|
131
|
+
* a glyph-sized background centred in a larger invisible one. Neither grows
|
|
132
|
+
* the button. What the background is drawn around is the *label*, and the
|
|
133
|
+
* label here is a React Native view we host ourselves, so padding it in React
|
|
134
|
+
* is both the simplest lever and the only one that works.
|
|
135
|
+
*/
|
|
136
|
+
const NATIVE_ICON_PADDING = 6;
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* The frame a native icon button is given, which must contain the padded glyph
|
|
140
|
+
* above with room to spare.
|
|
141
|
+
*
|
|
142
|
+
* It exists to end a measurement, not to set a look. The chain is
|
|
143
|
+
* `Host(matchContents)` → platform button → `RNHostView(matchContents)` → our
|
|
144
|
+
* view: every link sized by its contents, and nothing anywhere with a size of
|
|
145
|
+
* its own. Something has to be definite or the two layout systems ask each
|
|
146
|
+
* other the same question forever, and that fails down in the platform, where
|
|
147
|
+
* there is nothing for a JavaScript `try` to catch.
|
|
148
|
+
*
|
|
149
|
+
*/
|
|
150
|
+
const NATIVE_ICON_FRAME = 44;
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Our sizes on the platform's own control scale, which is how a native button
|
|
154
|
+
* is made bigger: it scales the room the style leaves around the label, and the
|
|
155
|
+
* label with it.
|
|
156
|
+
*
|
|
157
|
+
* Only ever sent for a *string* label. A hosted label is the case that has
|
|
158
|
+
* taken this app down twice, and while the cause turned out to be a missing
|
|
159
|
+
* frame rather than this modifier, a labelled button has no hosted view and no
|
|
160
|
+
* measurement to get wrong — so there is no reason to find out on both at once.
|
|
161
|
+
*/
|
|
162
|
+
const NATIVE_CONTROL_SIZE = {
|
|
163
|
+
sm: 'small',
|
|
164
|
+
md: 'regular',
|
|
165
|
+
lg: 'large',
|
|
166
|
+
icon: 'regular'
|
|
167
|
+
};
|
|
168
|
+
|
|
124
169
|
/** PanelUI variants mapped onto the platform button styles. */
|
|
125
170
|
const NATIVE_VARIANT = {
|
|
126
171
|
primary: 'filled',
|
|
@@ -142,6 +187,7 @@ export const Button = /*#__PURE__*/forwardRef(({
|
|
|
142
187
|
startContent,
|
|
143
188
|
endContent,
|
|
144
189
|
native,
|
|
190
|
+
glass = false,
|
|
145
191
|
...props
|
|
146
192
|
}, ref) => {
|
|
147
193
|
const isDisabled = disabled || loading;
|
|
@@ -169,25 +215,74 @@ export const Button = /*#__PURE__*/forwardRef(({
|
|
|
169
215
|
RNHostView
|
|
170
216
|
} = nativeUI;
|
|
171
217
|
const isStringLabel = typeof children === 'string';
|
|
218
|
+
const prominent = variant === 'primary' || variant === 'destructive';
|
|
219
|
+
|
|
220
|
+
/*
|
|
221
|
+
* Looks the portable props cannot ask for.
|
|
222
|
+
*
|
|
223
|
+
* `buttonStyle` because Liquid Glass has no cross-platform variant to
|
|
224
|
+
* map onto. `glassProminent` is the tinted one — it keeps the accent
|
|
225
|
+
* fill a prominent button is supposed to have, which drawing the
|
|
226
|
+
* material by hand over a plain button throws away. A supplied
|
|
227
|
+
* `buttonStyle` replaces the one the variant would have set, so this is
|
|
228
|
+
* a substitution rather than a layer on top.
|
|
229
|
+
*
|
|
230
|
+
* `buttonBorderShape` because an icon button is round and the platform's
|
|
231
|
+
* default capsule is not — a lone glyph in a capsule reads as a text
|
|
232
|
+
* button somebody forgot to label. It shapes the glass too.
|
|
233
|
+
*/
|
|
234
|
+
const swiftUI = getSwiftUIModifiers();
|
|
235
|
+
const nativeModifiers = swiftUI ? [glass ? swiftUI.buttonStyle(prominent ? 'glassProminent' : 'glass') : null, size === 'icon' ? swiftUI.buttonBorderShape('circle') : null, isStringLabel ? swiftUI.controlSize(NATIVE_CONTROL_SIZE[size ?? 'md']) : null].filter(Boolean) : [];
|
|
236
|
+
|
|
237
|
+
/*
|
|
238
|
+
* The platform paints the background, not the theme — so a hosted icon
|
|
239
|
+
* cannot read its colour from a token the way it does in the styled
|
|
240
|
+
* button. On a tinted button that is white, whatever the theme thinks its
|
|
241
|
+
* own primary foreground is; on the rest, including plain glass, the
|
|
242
|
+
* material is clear enough to leave the page's own foreground legible.
|
|
243
|
+
*/
|
|
244
|
+
const nativeContent = prominent ? '#ffffff' : contentColor;
|
|
172
245
|
return /*#__PURE__*/_jsx(Host, {
|
|
173
246
|
matchContents: true,
|
|
174
247
|
children: /*#__PURE__*/_jsx(NativeButton, {
|
|
175
248
|
label: isStringLabel ? children : undefined,
|
|
176
249
|
variant: NATIVE_VARIANT[variant ?? 'primary'],
|
|
177
|
-
disabled: isDisabled
|
|
178
|
-
|
|
250
|
+
disabled: isDisabled
|
|
251
|
+
/*
|
|
252
|
+
* A square for an icon button, a height for a labelled one — and
|
|
253
|
+
* in both cases a definite number, which is what stops the
|
|
254
|
+
* measurement chain above from having no fixed point in it.
|
|
255
|
+
*
|
|
256
|
+
* The frame does not decide how big the *button* looks; the padded
|
|
257
|
+
* label does, because that is what the platform draws its
|
|
258
|
+
* background around. The frame only has to be large enough to hold
|
|
259
|
+
* the result.
|
|
260
|
+
*/,
|
|
261
|
+
style: size === 'icon' ? {
|
|
262
|
+
width: NATIVE_ICON_FRAME,
|
|
263
|
+
height: NATIVE_ICON_FRAME
|
|
264
|
+
} : {
|
|
179
265
|
height: NATIVE_HEIGHT[size ?? 'md']
|
|
180
266
|
},
|
|
267
|
+
modifiers: nativeModifiers.length ? nativeModifiers : undefined,
|
|
181
268
|
onPress: props.onPress,
|
|
182
269
|
children: isStringLabel ? undefined : /*#__PURE__*/_jsx(RNHostView, {
|
|
183
270
|
matchContents: true,
|
|
184
|
-
children: /*#__PURE__*/_jsx(
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
271
|
+
children: /*#__PURE__*/_jsx(View, {
|
|
272
|
+
style: size === 'icon' ? {
|
|
273
|
+
padding: NATIVE_ICON_PADDING,
|
|
274
|
+
alignItems: 'center',
|
|
275
|
+
justifyContent: 'center'
|
|
276
|
+
} : undefined,
|
|
277
|
+
children: /*#__PURE__*/_jsx(IconColorProvider, {
|
|
278
|
+
color: nativeContent,
|
|
279
|
+
children: textChildren(children, text => /*#__PURE__*/_jsx(Text, {
|
|
280
|
+
className: label({
|
|
281
|
+
className: labelClassName
|
|
282
|
+
}),
|
|
283
|
+
children: text
|
|
284
|
+
}))
|
|
285
|
+
})
|
|
191
286
|
})
|
|
192
287
|
})
|
|
193
288
|
})
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["forwardRef","tv","useCSSVariable","AnimatedPressable","Text","textChildren","IconColorProvider","getNativeUI","Spinner","jsx","_jsx","
|
|
1
|
+
{"version":3,"names":["forwardRef","View","tv","useCSSVariable","AnimatedPressable","Text","textChildren","IconColorProvider","getNativeUI","getSwiftUIModifiers","Spinner","jsx","_jsx","jsxs","_jsxs","buttonVariants","slots","root","label","spinner","variants","variant","primary","secondary","outline","ghost","destructive","social","size","sm","md","lg","icon","fullWidth","true","disabled","defaultVariants","SPINNER_SIZE","CONTENT_COLOR_VAR","NATIVE_HEIGHT","NATIVE_ICON_PADDING","NATIVE_ICON_FRAME","NATIVE_CONTROL_SIZE","NATIVE_VARIANT","Button","children","className","labelClassName","loading","startContent","endContent","native","glass","props","ref","isDisabled","nativeUI","themedColor","contentColor","undefined","Host","NativeButton","RNHostView","isStringLabel","prominent","swiftUI","nativeModifiers","buttonStyle","buttonBorderShape","controlSize","filter","Boolean","nativeContent","matchContents","style","width","height","modifiers","length","onPress","padding","alignItems","justifyContent","color","text","accessibilityRole","accessibilityState","busy","displayName"],"sourceRoot":"../../../../src","sources":["components/button/index.tsx"],"mappings":"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","ignoreList":[]}
|
|
@@ -0,0 +1,477 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Drawer — a panel that comes in from an edge of the screen and covers the app
|
|
5
|
+
* until it is dismissed.
|
|
6
|
+
*
|
|
7
|
+
* ```tsx
|
|
8
|
+
* <Drawer>
|
|
9
|
+
* <Drawer.Trigger>
|
|
10
|
+
* <Button variant="outline">Menu</Button>
|
|
11
|
+
* </Drawer.Trigger>
|
|
12
|
+
* <Drawer.Content>
|
|
13
|
+
* <Drawer.Header title="Workspace" description="Switch or manage" />
|
|
14
|
+
* <Drawer.Body>
|
|
15
|
+
* <Item title="Projects" />
|
|
16
|
+
* <Item title="Members" />
|
|
17
|
+
* </Drawer.Body>
|
|
18
|
+
* <Drawer.Footer>
|
|
19
|
+
* <Button className="flex-1">Settings</Button>
|
|
20
|
+
* </Drawer.Footer>
|
|
21
|
+
* </Drawer.Content>
|
|
22
|
+
* </Drawer>
|
|
23
|
+
* ```
|
|
24
|
+
*
|
|
25
|
+
* ## Why the sides are `start` and `end`
|
|
26
|
+
*
|
|
27
|
+
* A drawer is the one overlay whose whole identity is the edge it belongs to,
|
|
28
|
+
* and in a right-to-left app "the navigation edge" is the right one. Naming the
|
|
29
|
+
* sides `left` and `right` would bake a reading direction into the API and
|
|
30
|
+
* force every caller in an RTL app to invert it themselves, so the sides are
|
|
31
|
+
* logical: `start` is the edge text begins at, `end` the edge it runs toward.
|
|
32
|
+
* Both follow the enclosing `<Direction>`, and `top` and `bottom` mean what
|
|
33
|
+
* they say because the vertical axis does not mirror.
|
|
34
|
+
*
|
|
35
|
+
* Yoga mirrors the panel's own position, because that is laid out from `start-0`
|
|
36
|
+
* / `end-0`. It does not mirror the drag, which is measured in raw pixels — so
|
|
37
|
+
* the gesture consults the direction and negates itself, which is what keeps a
|
|
38
|
+
* swipe *outward* dismissing in both directions rather than only one.
|
|
39
|
+
*
|
|
40
|
+
* ## Why it is not a BottomSheet with a different edge
|
|
41
|
+
*
|
|
42
|
+
* A sheet is sized by its content and dragged along the axis its scroller runs
|
|
43
|
+
* on, which is why so much of it is about sharing one drag between the two. A
|
|
44
|
+
* side drawer is sized by the screen and dragged across its scroller's axis, so
|
|
45
|
+
* the two gestures never compete: the cross-axis drag simply fails and the list
|
|
46
|
+
* keeps it. That difference is the reason this is a separate component and not
|
|
47
|
+
* a prop on the sheet.
|
|
48
|
+
*/
|
|
49
|
+
import { cloneElement, createContext, isValidElement, useCallback, useContext, useEffect, useMemo, useState } from 'react';
|
|
50
|
+
import { Pressable, ScrollView, useWindowDimensions, View } from 'react-native';
|
|
51
|
+
import { Gesture, GestureDetector } from 'react-native-gesture-handler';
|
|
52
|
+
import Animated, { SlideInDown, SlideInLeft, SlideInRight, SlideInUp, SlideOutDown, SlideOutLeft, SlideOutRight, SlideOutUp, runOnJS, useAnimatedStyle, useSharedValue, withSpring, withTiming } from 'react-native-reanimated';
|
|
53
|
+
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
|
54
|
+
import { tv } from 'tailwind-variants';
|
|
55
|
+
import { useCSSVariable } from 'uniwind';
|
|
56
|
+
import { XIcon } from "../../icons/index.js";
|
|
57
|
+
import { Portal } from "../../primitives/portal.js";
|
|
58
|
+
import { Scrim } from "../../primitives/scrim.js";
|
|
59
|
+
import { Text, textChildren } from "../../primitives/text.js";
|
|
60
|
+
import { useBackHandler } from "../../hooks/use-back-handler.js";
|
|
61
|
+
import { DirectionContext, useDirection, useDirectionSign } from "../../hooks/use-direction.js";
|
|
62
|
+
import { cn } from "../../utils/cn.js";
|
|
63
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
64
|
+
const SPRING = {
|
|
65
|
+
damping: 24,
|
|
66
|
+
stiffness: 300,
|
|
67
|
+
mass: 0.7
|
|
68
|
+
};
|
|
69
|
+
/** How far a drag has to travel outward before releasing it dismisses. */
|
|
70
|
+
const DISMISS_DISTANCE = 80;
|
|
71
|
+
/** A flick this fast dismisses regardless of how far it got. */
|
|
72
|
+
const DISMISS_VELOCITY = 650;
|
|
73
|
+
/**
|
|
74
|
+
* How much of the screen each size asks for, and the cap it is never allowed
|
|
75
|
+
* past on a wide screen.
|
|
76
|
+
*
|
|
77
|
+
* The cap is the whole point. A fraction alone reads correctly on a phone and
|
|
78
|
+
* absurdly on a tablet, where 78% of the width is a navigation list with a
|
|
79
|
+
* column of whitespace beside it. Capping turns the fraction into "no wider
|
|
80
|
+
* than it needs to be", which is what a drawer is.
|
|
81
|
+
*/
|
|
82
|
+
const WIDTH = {
|
|
83
|
+
sm: {
|
|
84
|
+
fraction: 0.62,
|
|
85
|
+
max: 280
|
|
86
|
+
},
|
|
87
|
+
md: {
|
|
88
|
+
fraction: 0.78,
|
|
89
|
+
max: 320
|
|
90
|
+
},
|
|
91
|
+
lg: {
|
|
92
|
+
fraction: 0.88,
|
|
93
|
+
max: 400
|
|
94
|
+
},
|
|
95
|
+
// Not 1: an edge of the app left showing is what says it is still there
|
|
96
|
+
// behind the drawer, and it is also the only thing left to tap to dismiss.
|
|
97
|
+
full: {
|
|
98
|
+
fraction: 0.94,
|
|
99
|
+
max: Infinity
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
/** The same idea on the vertical axis, where there is no sensible cap. */
|
|
104
|
+
const HEIGHT = {
|
|
105
|
+
sm: 0.3,
|
|
106
|
+
md: 0.45,
|
|
107
|
+
lg: 0.62,
|
|
108
|
+
full: 0.94
|
|
109
|
+
};
|
|
110
|
+
const panelVariants = tv({
|
|
111
|
+
base: 'absolute border-border bg-popover shadow-lg',
|
|
112
|
+
variants: {
|
|
113
|
+
side: {
|
|
114
|
+
// Three real edges each. The fourth is the screen edge the drawer is
|
|
115
|
+
// docked to, and drawing a border or a radius along it would be a line
|
|
116
|
+
// through the middle of nothing.
|
|
117
|
+
start: 'bottom-0 start-0 top-0 rounded-e-3xl border-e',
|
|
118
|
+
end: 'bottom-0 end-0 top-0 rounded-s-3xl border-s',
|
|
119
|
+
top: 'end-0 start-0 top-0 rounded-b-3xl border-b',
|
|
120
|
+
bottom: 'bottom-0 end-0 start-0 rounded-t-3xl border-t'
|
|
121
|
+
}
|
|
122
|
+
},
|
|
123
|
+
defaultVariants: {
|
|
124
|
+
side: 'start'
|
|
125
|
+
}
|
|
126
|
+
});
|
|
127
|
+
const DrawerContext = /*#__PURE__*/createContext(null);
|
|
128
|
+
function useDrawer(component) {
|
|
129
|
+
const context = useContext(DrawerContext);
|
|
130
|
+
if (!context) {
|
|
131
|
+
throw new Error(`${component} must be used within a <Drawer>`);
|
|
132
|
+
}
|
|
133
|
+
return context;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* What the panel knows and its parts need: which corner the close button took,
|
|
138
|
+
* so a header can leave it clear rather than wrap underneath it.
|
|
139
|
+
*/
|
|
140
|
+
|
|
141
|
+
const DrawerSurfaceContext = /*#__PURE__*/createContext(null);
|
|
142
|
+
function DrawerRoot({
|
|
143
|
+
children,
|
|
144
|
+
open: controlledOpen,
|
|
145
|
+
onOpenChange,
|
|
146
|
+
defaultOpen = false
|
|
147
|
+
}) {
|
|
148
|
+
const [uncontrolledOpen, setUncontrolledOpen] = useState(defaultOpen);
|
|
149
|
+
const controlled = controlledOpen !== undefined;
|
|
150
|
+
const open = controlled ? controlledOpen : uncontrolledOpen;
|
|
151
|
+
const setOpen = useCallback(next => {
|
|
152
|
+
if (!controlled) setUncontrolledOpen(next);
|
|
153
|
+
onOpenChange?.(next);
|
|
154
|
+
}, [controlled, onOpenChange]);
|
|
155
|
+
const context = useMemo(() => ({
|
|
156
|
+
open,
|
|
157
|
+
setOpen
|
|
158
|
+
}), [open, setOpen]);
|
|
159
|
+
return /*#__PURE__*/_jsx(DrawerContext.Provider, {
|
|
160
|
+
value: context,
|
|
161
|
+
children: children
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
function DrawerTrigger({
|
|
165
|
+
children
|
|
166
|
+
}) {
|
|
167
|
+
const {
|
|
168
|
+
setOpen
|
|
169
|
+
} = useDrawer('Drawer.Trigger');
|
|
170
|
+
if (! /*#__PURE__*/isValidElement(children)) return children;
|
|
171
|
+
return /*#__PURE__*/cloneElement(children, {
|
|
172
|
+
onPress: (...args) => {
|
|
173
|
+
children.props.onPress?.(...args);
|
|
174
|
+
setOpen(true);
|
|
175
|
+
}
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
function DrawerClose({
|
|
179
|
+
children
|
|
180
|
+
}) {
|
|
181
|
+
const {
|
|
182
|
+
setOpen
|
|
183
|
+
} = useDrawer('Drawer.Close');
|
|
184
|
+
if (! /*#__PURE__*/isValidElement(children)) return children;
|
|
185
|
+
return /*#__PURE__*/cloneElement(children, {
|
|
186
|
+
onPress: (...args) => {
|
|
187
|
+
children.props.onPress?.(...args);
|
|
188
|
+
setOpen(false);
|
|
189
|
+
}
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
function DrawerContent({
|
|
193
|
+
className,
|
|
194
|
+
side = 'start',
|
|
195
|
+
size = 'md',
|
|
196
|
+
dismissible = true,
|
|
197
|
+
swipeToDismiss = true,
|
|
198
|
+
showClose = true,
|
|
199
|
+
blur = false,
|
|
200
|
+
children,
|
|
201
|
+
...props
|
|
202
|
+
}) {
|
|
203
|
+
const {
|
|
204
|
+
open,
|
|
205
|
+
setOpen
|
|
206
|
+
} = useDrawer('Drawer.Content');
|
|
207
|
+
const {
|
|
208
|
+
width: screenWidth,
|
|
209
|
+
height: screenHeight
|
|
210
|
+
} = useWindowDimensions();
|
|
211
|
+
const insets = useSafeAreaInsets();
|
|
212
|
+
const dir = useDirection();
|
|
213
|
+
const sign = useDirectionSign();
|
|
214
|
+
const closeTint = useCSSVariable('--color-muted-foreground');
|
|
215
|
+
|
|
216
|
+
// Memoised for the reason `Direction` memoises its own: this is the object
|
|
217
|
+
// that invalidates layout for the whole panel, and a fresh one every render
|
|
218
|
+
// would re-run Yoga over it for a value that almost never changes.
|
|
219
|
+
const directionStyle = useMemo(() => ({
|
|
220
|
+
direction: dir
|
|
221
|
+
}), [dir]);
|
|
222
|
+
const close = useCallback(() => setOpen(false), [setOpen]);
|
|
223
|
+
|
|
224
|
+
// An open drawer catches the Android back button, closing itself instead of
|
|
225
|
+
// popping the screen behind it.
|
|
226
|
+
useBackHandler(open, close);
|
|
227
|
+
const horizontal = side === 'start' || side === 'end';
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* How far the panel has to travel to be fully off-screen — the distance a
|
|
231
|
+
* dismissing drag animates out to, and the axis the drag is measured on.
|
|
232
|
+
*/
|
|
233
|
+
const extent = horizontal ? Math.min(screenWidth * WIDTH[size].fraction, WIDTH[size].max) : screenHeight * HEIGHT[size];
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Which way "outward" points in raw pixels.
|
|
237
|
+
*
|
|
238
|
+
* Yoga has already mirrored where the panel sits, but a transform is not
|
|
239
|
+
* laid out — it is applied to whatever Yoga decided — so this is the one
|
|
240
|
+
* place that has to know the reading direction and negate itself. A `start`
|
|
241
|
+
* drawer leaves toward negative X in a left-to-right app and positive X in a
|
|
242
|
+
* right-to-left one; `end` is the mirror of that.
|
|
243
|
+
*/
|
|
244
|
+
const outward = horizontal ? (side === 'start' ? -1 : 1) * sign : side === 'top' ? -1 : 1;
|
|
245
|
+
|
|
246
|
+
/** Outward drag distance in points. Negative means dragged further in. */
|
|
247
|
+
const travel = useSharedValue(0);
|
|
248
|
+
|
|
249
|
+
/*
|
|
250
|
+
* Parked at zero on every open. A swipe-dismiss leaves the panel a full
|
|
251
|
+
* extent outward, and without this the next open would draw a lit backdrop
|
|
252
|
+
* over an app with the drawer still off-screen behind it — dimmed, blocking,
|
|
253
|
+
* and with nothing on it to close.
|
|
254
|
+
*/
|
|
255
|
+
useEffect(() => {
|
|
256
|
+
if (open) travel.value = 0;
|
|
257
|
+
}, [open, travel]);
|
|
258
|
+
const pan = useMemo(() => {
|
|
259
|
+
const gesture = Gesture.Pan().enabled(swipeToDismiss).onChange(event => {
|
|
260
|
+
const delta = (horizontal ? event.changeX : event.changeY) * outward;
|
|
261
|
+
const next = travel.value + delta;
|
|
262
|
+
// Follow the finger outward; rubber-band the pull further in, which
|
|
263
|
+
// has nowhere to go.
|
|
264
|
+
travel.value = next > 0 ? next : next / 3;
|
|
265
|
+
}).onEnd(event => {
|
|
266
|
+
const velocity = (horizontal ? event.velocityX : event.velocityY) * outward;
|
|
267
|
+
if (travel.value > DISMISS_DISTANCE || velocity > DISMISS_VELOCITY) {
|
|
268
|
+
travel.value = withTiming(extent, {
|
|
269
|
+
duration: 180
|
|
270
|
+
}, finished => {
|
|
271
|
+
if (finished) runOnJS(close)();
|
|
272
|
+
});
|
|
273
|
+
} else {
|
|
274
|
+
travel.value = withSpring(0, SPRING);
|
|
275
|
+
}
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
/*
|
|
279
|
+
* The drag has to give way to whatever the content is doing on the other
|
|
280
|
+
* axis. A side drawer holds a vertical list, so a mostly-vertical drag
|
|
281
|
+
* fails here and the list keeps it — which is why a side drawer needs none
|
|
282
|
+
* of the shared-gesture machinery a bottom sheet does.
|
|
283
|
+
*/
|
|
284
|
+
return horizontal ? gesture.activeOffsetX([-12, 12]).failOffsetY([-18, 18]) : gesture.activeOffsetY([-12, 12]).failOffsetX([-18, 18]);
|
|
285
|
+
}, [close, extent, horizontal, outward, swipeToDismiss, travel]);
|
|
286
|
+
const panelStyle = useAnimatedStyle(() => horizontal ? {
|
|
287
|
+
transform: [{
|
|
288
|
+
translateX: travel.value * outward
|
|
289
|
+
}]
|
|
290
|
+
} : {
|
|
291
|
+
transform: [{
|
|
292
|
+
translateY: travel.value * outward
|
|
293
|
+
}]
|
|
294
|
+
});
|
|
295
|
+
const surface = useMemo(() => ({
|
|
296
|
+
side,
|
|
297
|
+
showClose
|
|
298
|
+
}), [side, showClose]);
|
|
299
|
+
if (!open) return null;
|
|
300
|
+
|
|
301
|
+
/*
|
|
302
|
+
* The slide has to be given a physical direction, since the animation
|
|
303
|
+
* presets are physical. This is the same mirroring Yoga did for the panel's
|
|
304
|
+
* position, applied by hand to the one thing Yoga does not own.
|
|
305
|
+
*/
|
|
306
|
+
const physical = horizontal ? side === 'start' === (dir === 'ltr') ? 'left' : 'right' : side;
|
|
307
|
+
const entering = {
|
|
308
|
+
left: SlideInLeft,
|
|
309
|
+
right: SlideInRight,
|
|
310
|
+
top: SlideInUp,
|
|
311
|
+
bottom: SlideInDown
|
|
312
|
+
}[physical];
|
|
313
|
+
const exiting = {
|
|
314
|
+
left: SlideOutLeft,
|
|
315
|
+
right: SlideOutRight,
|
|
316
|
+
top: SlideOutUp,
|
|
317
|
+
bottom: SlideOutDown
|
|
318
|
+
}[physical];
|
|
319
|
+
|
|
320
|
+
/*
|
|
321
|
+
* The inset and the panel's own padding stack rather than compete.
|
|
322
|
+
*
|
|
323
|
+
* A drawer that reaches the top of the screen is drawn behind the status bar
|
|
324
|
+
* on purpose — a surface the app disappears under should not stop short of
|
|
325
|
+
* the edge — but the clock and the battery are drawn *in* that band, so the
|
|
326
|
+
* content clears the whole of it and then takes its padding on top. `Math.max`
|
|
327
|
+
* gave the two the same slot: on a 59-point inset it resolved to 59, leaving
|
|
328
|
+
* the header and the close button flush against the clock with nothing
|
|
329
|
+
* between them.
|
|
330
|
+
*
|
|
331
|
+
* `bottom` is the one side whose top edge is nowhere near the status bar, and
|
|
332
|
+
* `top` the one side ending in open screen rather than at the home indicator.
|
|
333
|
+
* Each takes plain padding on the edge it does not meet.
|
|
334
|
+
*/
|
|
335
|
+
const padding = {
|
|
336
|
+
paddingTop: side === 'bottom' ? 16 : insets.top + 16,
|
|
337
|
+
paddingBottom: side === 'top' ? 16 : Math.max(insets.bottom, 16)
|
|
338
|
+
};
|
|
339
|
+
return /*#__PURE__*/_jsx(Portal, {
|
|
340
|
+
children: /*#__PURE__*/_jsx(DrawerContext.Provider, {
|
|
341
|
+
value: {
|
|
342
|
+
open,
|
|
343
|
+
setOpen
|
|
344
|
+
},
|
|
345
|
+
children: /*#__PURE__*/_jsx(DirectionContext.Provider, {
|
|
346
|
+
value: dir,
|
|
347
|
+
children: /*#__PURE__*/_jsxs(View, {
|
|
348
|
+
className: "absolute inset-0",
|
|
349
|
+
style: directionStyle,
|
|
350
|
+
children: [/*#__PURE__*/_jsxs(View, {
|
|
351
|
+
className: "absolute inset-0",
|
|
352
|
+
children: [/*#__PURE__*/_jsx(Scrim, {
|
|
353
|
+
blur: blur
|
|
354
|
+
}), /*#__PURE__*/_jsx(Pressable, {
|
|
355
|
+
accessibilityLabel: "Close drawer",
|
|
356
|
+
className: "flex-1",
|
|
357
|
+
onPress: dismissible ? close : undefined
|
|
358
|
+
})]
|
|
359
|
+
}), /*#__PURE__*/_jsx(GestureDetector, {
|
|
360
|
+
gesture: pan,
|
|
361
|
+
children: /*#__PURE__*/_jsx(Animated.View, {
|
|
362
|
+
entering: entering.springify().damping(24).stiffness(260).mass(0.7),
|
|
363
|
+
exiting: exiting.duration(200),
|
|
364
|
+
accessibilityViewIsModal: true,
|
|
365
|
+
className: panelVariants({
|
|
366
|
+
side,
|
|
367
|
+
className
|
|
368
|
+
}),
|
|
369
|
+
...props,
|
|
370
|
+
// After the spread, and folding the caller's own `style` in
|
|
371
|
+
// rather than replacing the array: spread last, a caller
|
|
372
|
+
// passing `style` would silently drop the drag transform and
|
|
373
|
+
// the safe-area padding with it.
|
|
374
|
+
style: [panelStyle, padding, horizontal ? {
|
|
375
|
+
width: extent
|
|
376
|
+
} : {
|
|
377
|
+
height: extent
|
|
378
|
+
}, props.style],
|
|
379
|
+
children: /*#__PURE__*/_jsxs(DrawerSurfaceContext.Provider, {
|
|
380
|
+
value: surface,
|
|
381
|
+
children: [textChildren(children), showClose ? /*#__PURE__*/_jsx(Pressable, {
|
|
382
|
+
accessibilityRole: "button",
|
|
383
|
+
accessibilityLabel: "Close",
|
|
384
|
+
onPress: close,
|
|
385
|
+
hitSlop: 8,
|
|
386
|
+
className: cn('absolute z-10 h-8 w-8 items-center justify-center rounded-full bg-muted active:opacity-70', side === 'end' ? 'start-4' : 'end-4'),
|
|
387
|
+
style: {
|
|
388
|
+
top: padding.paddingTop
|
|
389
|
+
},
|
|
390
|
+
children: /*#__PURE__*/_jsx(XIcon, {
|
|
391
|
+
size: 16,
|
|
392
|
+
color: typeof closeTint === 'string' ? closeTint : undefined
|
|
393
|
+
})
|
|
394
|
+
}) : null]
|
|
395
|
+
})
|
|
396
|
+
})
|
|
397
|
+
})]
|
|
398
|
+
})
|
|
399
|
+
})
|
|
400
|
+
})
|
|
401
|
+
});
|
|
402
|
+
}
|
|
403
|
+
function DrawerHeader({
|
|
404
|
+
className,
|
|
405
|
+
title,
|
|
406
|
+
description,
|
|
407
|
+
children,
|
|
408
|
+
...props
|
|
409
|
+
}) {
|
|
410
|
+
const surface = useContext(DrawerSurfaceContext);
|
|
411
|
+
|
|
412
|
+
/*
|
|
413
|
+
* Padding on whichever side the close button took, so a long title wraps
|
|
414
|
+
* above it rather than running underneath it. Nothing to clear when the
|
|
415
|
+
* button was turned off, and a header used outside a panel keeps its plain
|
|
416
|
+
* padding rather than reserving a corner for a button that is not there.
|
|
417
|
+
*/
|
|
418
|
+
const clearance = !surface?.showClose ? undefined : surface.side === 'end' ? 'ps-14' : 'pe-14';
|
|
419
|
+
return /*#__PURE__*/_jsxs(View, {
|
|
420
|
+
className: cn('gap-1 px-5 pb-3', clearance, className),
|
|
421
|
+
...props,
|
|
422
|
+
children: [typeof title === 'string' ? /*#__PURE__*/_jsx(Text, {
|
|
423
|
+
size: "lg",
|
|
424
|
+
weight: "semibold",
|
|
425
|
+
children: title
|
|
426
|
+
}) : title, typeof description === 'string' ? /*#__PURE__*/_jsx(Text, {
|
|
427
|
+
className: "text-sm text-muted-foreground",
|
|
428
|
+
children: description
|
|
429
|
+
}) : description, textChildren(children)]
|
|
430
|
+
});
|
|
431
|
+
}
|
|
432
|
+
function DrawerBody({
|
|
433
|
+
className,
|
|
434
|
+
scrollable = true,
|
|
435
|
+
children,
|
|
436
|
+
contentContainerStyle,
|
|
437
|
+
...props
|
|
438
|
+
}) {
|
|
439
|
+
if (!scrollable) {
|
|
440
|
+
return /*#__PURE__*/_jsx(View, {
|
|
441
|
+
className: cn('flex-1 px-5', className),
|
|
442
|
+
children: textChildren(children)
|
|
443
|
+
});
|
|
444
|
+
}
|
|
445
|
+
return /*#__PURE__*/_jsx(ScrollView, {
|
|
446
|
+
className: cn('flex-1', className),
|
|
447
|
+
showsVerticalScrollIndicator: false
|
|
448
|
+
// The drawer's own drag runs across this axis and fails out of a mostly
|
|
449
|
+
// vertical touch, so the two never have to be reconciled.
|
|
450
|
+
,
|
|
451
|
+
contentContainerStyle: [{
|
|
452
|
+
paddingHorizontal: 20
|
|
453
|
+
}, contentContainerStyle],
|
|
454
|
+
...props,
|
|
455
|
+
children: textChildren(children)
|
|
456
|
+
});
|
|
457
|
+
}
|
|
458
|
+
function DrawerFooter({
|
|
459
|
+
className,
|
|
460
|
+
children,
|
|
461
|
+
...props
|
|
462
|
+
}) {
|
|
463
|
+
return /*#__PURE__*/_jsx(View, {
|
|
464
|
+
className: cn('flex-row items-center gap-2 border-t border-border px-5 pt-3', className),
|
|
465
|
+
...props,
|
|
466
|
+
children: textChildren(children)
|
|
467
|
+
});
|
|
468
|
+
}
|
|
469
|
+
export const Drawer = Object.assign(DrawerRoot, {
|
|
470
|
+
Trigger: DrawerTrigger,
|
|
471
|
+
Content: DrawerContent,
|
|
472
|
+
Header: DrawerHeader,
|
|
473
|
+
Body: DrawerBody,
|
|
474
|
+
Footer: DrawerFooter,
|
|
475
|
+
Close: DrawerClose
|
|
476
|
+
});
|
|
477
|
+
//# sourceMappingURL=index.js.map
|