@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,EAKf,MAAM,OAAO,CAAC;AACf,OAAO,EAIL,KAAK,SAAS,EACd,KAAK,SAAS,EACf,MAAM,cAAc,CAAC;AAYtB,OAAO,EAAgB,KAAK,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAG/E,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,OAAO,GAAG,QAAQ,CAAC;AAgBrD,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,+BA0UlB;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;AAED,eAAO,MAAM,MAAM;;;;;;CAA0C,CAAC"}
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
- export const Drawer = { Root, Trigger, Portal, Popup, Close };
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,+BA8LtD;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"}
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, easeSpring } from "../../motion/index.js";
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: easeSpring,
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
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gryt/ui-native",
3
- "version": "0.7.0",
3
+ "version": "0.8.1",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "description": "Gryt's design system on React Native. Same tokens as @gryt/ui, a different renderer.",