@gryt/ui-native 0.7.0 → 0.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { type ReactNode } from "react";
|
|
1
|
+
import { type ComponentProps, type ReactNode } from "react";
|
|
2
2
|
import { type StyleProp, type ViewStyle } from "react-native";
|
|
3
|
+
import { FlatList as GestureFlatList, ScrollView as GestureScrollView } from "react-native-gesture-handler";
|
|
3
4
|
import { type OpenStateProps } from "../../overlay/useOpenState.js";
|
|
4
5
|
export type DrawerSide = "left" | "right" | "bottom";
|
|
5
6
|
export interface DrawerRootProps extends OpenStateProps {
|
|
@@ -26,12 +27,28 @@ declare function Close({ children, style }: {
|
|
|
26
27
|
children?: ReactNode;
|
|
27
28
|
style?: StyleProp<ViewStyle>;
|
|
28
29
|
}): import("react").JSX.Element;
|
|
30
|
+
export type DrawerScrollViewProps = ComponentProps<typeof GestureScrollView>;
|
|
31
|
+
/**
|
|
32
|
+
* The scroll view to use inside a drawer.
|
|
33
|
+
*
|
|
34
|
+
* React Native's own will not scroll in there: the drawer's pan and the scroll
|
|
35
|
+
* view's native recogniser both want the touch, and gesture-handler settles
|
|
36
|
+
* that by reference — which means the two have to know about each other. This
|
|
37
|
+
* is that introduction, and it is why the drawer hands you a scrollable rather
|
|
38
|
+
* than taking a prop pointing at one.
|
|
39
|
+
*/
|
|
40
|
+
declare function DrawerScrollView(props: DrawerScrollViewProps): import("react").JSX.Element;
|
|
41
|
+
export type DrawerFlatListProps<ItemT> = ComponentProps<typeof GestureFlatList<ItemT>>;
|
|
42
|
+
/** The same, for a list long enough to deserve one. */
|
|
43
|
+
declare function DrawerFlatList<ItemT>(props: DrawerFlatListProps<ItemT>): import("react").JSX.Element;
|
|
29
44
|
export declare const Drawer: {
|
|
30
45
|
Root: typeof Root;
|
|
31
46
|
Trigger: typeof Trigger;
|
|
32
47
|
Portal: typeof Portal;
|
|
33
48
|
Popup: typeof Popup;
|
|
34
49
|
Close: typeof Close;
|
|
50
|
+
ScrollView: typeof DrawerScrollView;
|
|
51
|
+
FlatList: typeof DrawerFlatList;
|
|
35
52
|
};
|
|
36
53
|
export {};
|
|
37
54
|
//# sourceMappingURL=Drawer.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Drawer.d.ts","sourceRoot":"","sources":["../../../src/components/Drawer/Drawer.tsx"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,SAAS,
|
|
1
|
+
{"version":3,"file":"Drawer.d.ts","sourceRoot":"","sources":["../../../src/components/Drawer/Drawer.tsx"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,cAAc,EAEnB,KAAK,SAAS,EAOf,MAAM,OAAO,CAAC;AACf,OAAO,EAIL,KAAK,SAAS,EACd,KAAK,SAAS,EACf,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,QAAQ,IAAI,eAAe,EAG3B,UAAU,IAAI,iBAAiB,EAChC,MAAM,8BAA8B,CAAC;AAWtC,OAAO,EAAgB,KAAK,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAG/E,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,OAAO,GAAG,QAAQ,CAAC;AA6CrD,MAAM,WAAW,eAAgB,SAAQ,cAAc;IACrD,QAAQ,CAAC,EAAE,SAAS,CAAC;CACtB;AAED,iBAAS,IAAI,CAAC,EAAE,QAAQ,EAAE,GAAG,SAAS,EAAE,EAAE,eAAe,+BAKxD;AAED,iBAAS,OAAO,CAAC,EACf,QAAQ,EACR,KAAK,EACN,EAAE;IACD,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;CAC9B,+BAWA;AAED,iBAAS,MAAM,CAAC,EAAE,QAAQ,EAAE,EAAE;IAAE,QAAQ,CAAC,EAAE,SAAS,CAAA;CAAE,+BAErD;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB,sCAAsC;IACtC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;CAC9B;AAgBD,iBAAS,KAAK,CAAC,EACb,QAAQ,EACR,IAAa,EACb,IAAU,EACV,WAAkB,EAClB,KAAK,EACN,EAAE,gBAAgB,+BA+WlB;AAED,iBAAS,KAAK,CAAC,EACb,QAAQ,EACR,KAAK,EACN,EAAE;IACD,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;CAC9B,+BAWA;AAuBD,MAAM,MAAM,qBAAqB,GAAG,cAAc,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAE7E;;;;;;;;GAQG;AACH,iBAAS,gBAAgB,CAAC,KAAK,EAAE,qBAAqB,+BAIrD;AAED,MAAM,MAAM,mBAAmB,CAAC,KAAK,IAAI,cAAc,CACrD,OAAO,eAAe,CAAC,KAAK,CAAC,CAC9B,CAAC;AAEF,uDAAuD;AACvD,iBAAS,cAAc,CAAC,KAAK,EAAE,KAAK,EAAE,mBAAmB,CAAC,KAAK,CAAC,+BAI/D;AAED,eAAO,MAAM,MAAM;;;;;;;;CAQlB,CAAC"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
-
import { createContext, useContext, useEffect, useMemo, useState } from "react";
|
|
2
|
+
import { createContext, useContext, useEffect, useMemo, useRef, useState } from "react";
|
|
3
3
|
import { Dimensions, Modal, Pressable } from "react-native";
|
|
4
|
-
import { Gesture, GestureDetector } from "react-native-gesture-handler";
|
|
4
|
+
import { FlatList as GestureFlatList, Gesture, GestureDetector, ScrollView as GestureScrollView } from "react-native-gesture-handler";
|
|
5
5
|
import Animated, { runOnJS, useAnimatedStyle, useSharedValue } from "react-native-reanimated";
|
|
6
6
|
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
|
7
7
|
import { useReducedMotion } from "../../hooks/useReducedMotion.js";
|
|
@@ -10,6 +10,7 @@ import { durations, travel as travelTo } from "../../motion/index.js";
|
|
|
10
10
|
import { useOpenState } from "../../overlay/useOpenState.js";
|
|
11
11
|
import { useTheme } from "../../theme/index.js";
|
|
12
12
|
const DrawerContext = createContext(null);
|
|
13
|
+
const DrawerScrollContext = createContext(null);
|
|
13
14
|
function useDrawer(part) {
|
|
14
15
|
const value = useContext(DrawerContext);
|
|
15
16
|
if (!value)
|
|
@@ -206,6 +207,20 @@ function Popup({ children, side = "left", size = 0.8, dismissible = true, style
|
|
|
206
207
|
* simpler and would give up the whole point: the panel tracks the finger on
|
|
207
208
|
* the UI thread, with nothing to be blocked by a busy bridge.
|
|
208
209
|
*/
|
|
210
|
+
/**
|
|
211
|
+
* Scrollables that have announced themselves, and the pan is rebuilt when one
|
|
212
|
+
* does.
|
|
213
|
+
*
|
|
214
|
+
* State rather than a ref precisely so it rebuilds: gesture-handler resolves
|
|
215
|
+
* these when the gesture is attached, and a child's ref is still empty at the
|
|
216
|
+
* detector's first attach. Registering flips this, the memo below runs again,
|
|
217
|
+
* and the detector re-attaches with something to point at.
|
|
218
|
+
*/
|
|
219
|
+
const [scrollables, setScrollables] = useState([]);
|
|
220
|
+
const scrollRegistry = useMemo(() => ({
|
|
221
|
+
register: (ref) => setScrollables((current) => current.includes(ref) ? current : [...current, ref]),
|
|
222
|
+
unregister: (ref) => setScrollables((current) => current.filter((r) => r !== ref))
|
|
223
|
+
}), []);
|
|
209
224
|
const pan = useMemo(() => {
|
|
210
225
|
const closingSign = side === "left" ? -1 : 1;
|
|
211
226
|
const gesture = Gesture.Pan().enabled(dismissible);
|
|
@@ -221,6 +236,17 @@ function Popup({ children, side = "left", size = 0.8, dismissible = true, style
|
|
|
221
236
|
else {
|
|
222
237
|
gesture.activeOffsetX(closingSign * 8).failOffsetY([-12, 12]);
|
|
223
238
|
}
|
|
239
|
+
/* Let the scrollables run. Without this the drawer's recogniser competes
|
|
240
|
+
* with the scroll view's and wins, so a list inside the panel cannot move —
|
|
241
|
+
* which it could not, on either `PanResponder` or a bare pan (GRYT-431).
|
|
242
|
+
*
|
|
243
|
+
* "Simultaneous" overstates what happens: the axis rules above mean this
|
|
244
|
+
* pan does not activate on a vertical drag at all, so in practice the
|
|
245
|
+
* scroll view has it alone. What this removes is the blocking that was
|
|
246
|
+
* happening before either of them had decided anything. */
|
|
247
|
+
if (scrollables.length > 0) {
|
|
248
|
+
gesture.simultaneousWithExternalGesture(...scrollables);
|
|
249
|
+
}
|
|
224
250
|
return gesture
|
|
225
251
|
.onUpdate((event) => {
|
|
226
252
|
"worklet";
|
|
@@ -259,7 +285,7 @@ function Popup({ children, side = "left", size = 0.8, dismissible = true, style
|
|
|
259
285
|
if (!success)
|
|
260
286
|
drag.value = travelTo(0, { duration: durations.springSoft });
|
|
261
287
|
});
|
|
262
|
-
}, [dismissible, drag, extent, setOpen, side, vertical]);
|
|
288
|
+
}, [dismissible, drag, extent, scrollables, setOpen, side, vertical]);
|
|
263
289
|
/**
|
|
264
290
|
* Any leftover drag goes back as the panel opens, and is cleared once it is
|
|
265
291
|
* gone.
|
|
@@ -320,10 +346,58 @@ function Popup({ children, side = "left", size = 0.8, dismissible = true, style
|
|
|
320
346
|
paddingBottom: insets.bottom
|
|
321
347
|
},
|
|
322
348
|
style
|
|
323
|
-
], children: children }) }) }) }) }));
|
|
349
|
+
], children: _jsx(DrawerScrollContext.Provider, { value: scrollRegistry, children: children }) }) }) }) }) }));
|
|
324
350
|
}
|
|
325
351
|
function Close({ children, style }) {
|
|
326
352
|
const { setOpen } = useDrawer("Close");
|
|
327
353
|
return (_jsx(Pressable, { accessibilityRole: "button", onPress: () => setOpen(false), style: style, children: children }));
|
|
328
354
|
}
|
|
329
|
-
|
|
355
|
+
/**
|
|
356
|
+
* Announce a scrollable to the drawer around it, for as long as it is mounted.
|
|
357
|
+
*
|
|
358
|
+
* Silently does nothing outside a `Drawer.Popup`, which is the right answer for
|
|
359
|
+
* a component that is only ever a scroll view with one extra job — throwing
|
|
360
|
+
* would make it unusable in any shared piece that might or might not be in a
|
|
361
|
+
* drawer.
|
|
362
|
+
*/
|
|
363
|
+
function useRegisterScrollable(ref) {
|
|
364
|
+
const asExternal = ref;
|
|
365
|
+
const registry = useContext(DrawerScrollContext);
|
|
366
|
+
const register = registry?.register;
|
|
367
|
+
const unregister = registry?.unregister;
|
|
368
|
+
useEffect(() => {
|
|
369
|
+
if (!register || !unregister)
|
|
370
|
+
return;
|
|
371
|
+
register(asExternal);
|
|
372
|
+
return () => unregister(asExternal);
|
|
373
|
+
}, [asExternal, register, unregister]);
|
|
374
|
+
}
|
|
375
|
+
/**
|
|
376
|
+
* The scroll view to use inside a drawer.
|
|
377
|
+
*
|
|
378
|
+
* React Native's own will not scroll in there: the drawer's pan and the scroll
|
|
379
|
+
* view's native recogniser both want the touch, and gesture-handler settles
|
|
380
|
+
* that by reference — which means the two have to know about each other. This
|
|
381
|
+
* is that introduction, and it is why the drawer hands you a scrollable rather
|
|
382
|
+
* than taking a prop pointing at one.
|
|
383
|
+
*/
|
|
384
|
+
function DrawerScrollView(props) {
|
|
385
|
+
const ref = useRef(null);
|
|
386
|
+
useRegisterScrollable(ref);
|
|
387
|
+
return _jsx(GestureScrollView, { ref: ref, ...props });
|
|
388
|
+
}
|
|
389
|
+
/** The same, for a list long enough to deserve one. */
|
|
390
|
+
function DrawerFlatList(props) {
|
|
391
|
+
const ref = useRef(null);
|
|
392
|
+
useRegisterScrollable(ref);
|
|
393
|
+
return _jsx(GestureFlatList, { ref: ref, ...props });
|
|
394
|
+
}
|
|
395
|
+
export const Drawer = {
|
|
396
|
+
Root,
|
|
397
|
+
Trigger,
|
|
398
|
+
Portal,
|
|
399
|
+
Popup,
|
|
400
|
+
Close,
|
|
401
|
+
ScrollView: DrawerScrollView,
|
|
402
|
+
FlatList: DrawerFlatList
|
|
403
|
+
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Sheet.d.ts","sourceRoot":"","sources":["../../../src/components/Sheet/Sheet.tsx"],"names":[],"mappings":"AAAA,OAAO,EAOL,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AACf,OAAO,EAAyB,KAAK,SAAS,EAAE,KAAK,SAAS,EAAE,MAAM,cAAc,CAAC;AAerF,OAAO,EAAgB,KAAK,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAG/E;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AAEH,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,EAAE,SAAS,CAAC;CACtB;AAED,0EAA0E;AAC1E,wBAAgB,aAAa,CAAC,EAAE,QAAQ,EAAE,EAAE,kBAAkB,+BAE7D;AAeD,MAAM,WAAW,UAAW,SAAQ,cAAc;IAChD;;;;;OAKG;IACH,UAAU,CAAC,EAAE,CAAC,MAAM,GAAG,MAAM,CAAC,EAAE,CAAC;IACjC,QAAQ,CAAC,EAAE,SAAS,CAAC;CACtB;AAED;;;;;;;;;;GAUG;AACH,iBAAS,IAAI,CAAC,EACZ,UAAoB,EACpB,QAAQ,EACR,GAAG,SAAS,EACb,EAAE,UAAU,+BAqBZ;AAWD,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;CAC9B;AAED;;;;;GAKG;AACH,iBAAS,OAAO,CAAC,EAAE,QAAQ,EAAE,KAAK,EAAE,EAAE,iBAAiB,+BAOtD;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;CAC9B;AAED,iBAAS,OAAO,CAAC,EAAE,QAAQ,EAAE,KAAK,EAAE,EAAE,iBAAiB,+
|
|
1
|
+
{"version":3,"file":"Sheet.d.ts","sourceRoot":"","sources":["../../../src/components/Sheet/Sheet.tsx"],"names":[],"mappings":"AAAA,OAAO,EAOL,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AACf,OAAO,EAAyB,KAAK,SAAS,EAAE,KAAK,SAAS,EAAE,MAAM,cAAc,CAAC;AAerF,OAAO,EAAgB,KAAK,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAG/E;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AAEH,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,EAAE,SAAS,CAAC;CACtB;AAED,0EAA0E;AAC1E,wBAAgB,aAAa,CAAC,EAAE,QAAQ,EAAE,EAAE,kBAAkB,+BAE7D;AAeD,MAAM,WAAW,UAAW,SAAQ,cAAc;IAChD;;;;;OAKG;IACH,UAAU,CAAC,EAAE,CAAC,MAAM,GAAG,MAAM,CAAC,EAAE,CAAC;IACjC,QAAQ,CAAC,EAAE,SAAS,CAAC;CACtB;AAED;;;;;;;;;;GAUG;AACH,iBAAS,IAAI,CAAC,EACZ,UAAoB,EACpB,QAAQ,EACR,GAAG,SAAS,EACb,EAAE,UAAU,+BAqBZ;AAWD,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;CAC9B;AAED;;;;;GAKG;AACH,iBAAS,OAAO,CAAC,EAAE,QAAQ,EAAE,KAAK,EAAE,EAAE,iBAAiB,+BAOtD;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;CAC9B;AAED,iBAAS,OAAO,CAAC,EAAE,QAAQ,EAAE,KAAK,EAAE,EAAE,iBAAiB,+BA0MtD;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;CAC9B;AAED,iBAAS,KAAK,CAAC,EAAE,QAAQ,EAAE,KAAK,EAAE,EAAE,eAAe,+BAOlD;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;CAC9B;AAED,iBAAS,KAAK,CAAC,EAAE,QAAQ,EAAE,KAAK,EAAE,EAAE,eAAe,+BAiBlD;AAED,eAAO,MAAM,KAAK;;;;;CAA0D,CAAC"}
|
|
@@ -4,7 +4,7 @@ import { Pressable, Text, View } from "react-native";
|
|
|
4
4
|
import { BottomSheetBackdrop, BottomSheetModal, BottomSheetModalProvider, BottomSheetView, useBottomSheetTimingConfigs, } from "@gorhom/bottom-sheet";
|
|
5
5
|
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
|
6
6
|
import { grytDrawerBleed } from "@gryt/theme";
|
|
7
|
-
import { durations,
|
|
7
|
+
import { durations, easeSpringTight } from "../../motion/index.js";
|
|
8
8
|
import { useOpenState } from "../../overlay/useOpenState.js";
|
|
9
9
|
import { useTheme } from "../../theme/index.js";
|
|
10
10
|
/** Mount once, at the root. See the note above for why it is required. */
|
|
@@ -147,10 +147,22 @@ function Content({ children, style }) {
|
|
|
147
147
|
* bottom edge and covers nearly all of it. The same duration over a longer
|
|
148
148
|
* distance is a faster animation, and at 700 this arrived quickly enough to
|
|
149
149
|
* read as a snap rather than a slide.
|
|
150
|
+
*
|
|
151
|
+
* **`easeSpringTight`, not `easeSpring`.** This used the loose curve and
|
|
152
|
+
* bounced, and the two are labelled in `easing.ts` in a way that decides it:
|
|
153
|
+
* `easeSpring` "overshoots ~12%, for things that scale in place", and
|
|
154
|
+
* `easeSpringTight` is "critically damped, no overshoot, for things that
|
|
155
|
+
* travel inside bounds". A sheet sliding up from the bottom edge to a snap
|
|
156
|
+
* point is the second thing, not the first — so this was the wrong curve by
|
|
157
|
+
* the package's own rule, and it is the curve the Drawer already uses.
|
|
158
|
+
*
|
|
159
|
+
* Overshoot on a sheet is worse than on a button, too: the thing that
|
|
160
|
+
* overshoots is the top edge, so it travels past its snap point and comes
|
|
161
|
+
* back, which reads as the sheet failing to land rather than as bounce.
|
|
150
162
|
*/
|
|
151
163
|
const animationConfigs = useBottomSheetTimingConfigs({
|
|
152
164
|
duration: durations.springSlow,
|
|
153
|
-
easing:
|
|
165
|
+
easing: easeSpringTight,
|
|
154
166
|
});
|
|
155
167
|
const renderBackdrop = useCallback((props) => (_jsx(BottomSheetBackdrop, { ...props, appearsOnIndex: 0, disappearsOnIndex: -1,
|
|
156
168
|
// Tracks the drag, so dragging the sheet down lightens the scrim
|