@gryt/ui-native 0.6.1 → 0.8.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.
@@ -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
+ };
@@ -15,10 +15,15 @@ export interface ScrollAreaProps extends Omit<ScrollViewProps, "style"> {
15
15
  * device. So the component stays for call-site parity and does almost nothing,
16
16
  * which is the honest version.
17
17
  *
18
- * Its one job was defaulting the indicator off, since the web version hides the
19
- * native scrollbar too. It has a second now: it publishes a drag lock, so a
20
- * control inside it can hold it still while it is being dragged. See
21
- * internal/dragLock a Slider is unusable in a scrolling screen without it.
18
+ * Its one job is defaulting the indicator off, since the web version hides the
19
+ * native scrollbar too.
20
+ *
21
+ * It used to have a second: publishing a drag lock that a Slider could claim to
22
+ * switch this list off for the duration of a drag. That existed because
23
+ * `PanResponder` could not tell the native scroll recogniser anything, so the
24
+ * only way to stop the list moving under a slider was to disable it. The Slider
25
+ * declares `activeOffsetX` now and the two gestures settle it between them, so
26
+ * there is nothing to hold still and nothing to leak if a drag is interrupted.
22
27
  */
23
- export declare function ScrollArea({ children, horizontal, style, contentStyle, showsVerticalScrollIndicator, showsHorizontalScrollIndicator, scrollEnabled, ...rest }: ScrollAreaProps): import("react").JSX.Element;
28
+ export declare function ScrollArea({ children, horizontal, style, contentStyle, showsVerticalScrollIndicator, showsHorizontalScrollIndicator, ...rest }: ScrollAreaProps): import("react").JSX.Element;
24
29
  //# sourceMappingURL=ScrollArea.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ScrollArea.d.ts","sourceRoot":"","sources":["../../../src/components/ScrollArea/ScrollArea.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAkC,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AACvE,OAAO,EAAc,KAAK,eAAe,EAAE,KAAK,SAAS,EAAE,KAAK,SAAS,EAAE,MAAM,cAAc,CAAC;AAIhG,MAAM,WAAW,eAAgB,SAAQ,IAAI,CAAC,eAAe,EAAE,OAAO,CAAC;IACrE,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC7B,YAAY,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;CACrC;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,UAAU,CAAC,EACzB,QAAQ,EACR,UAAkB,EAClB,KAAK,EACL,YAAY,EACZ,4BAAoC,EACpC,8BAAsC,EACtC,aAAa,EACb,GAAG,IAAI,EACR,EAAE,eAAe,+BAgCjB"}
1
+ {"version":3,"file":"ScrollArea.d.ts","sourceRoot":"","sources":["../../../src/components/ScrollArea/ScrollArea.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AACvC,OAAO,EAAc,KAAK,eAAe,EAAE,KAAK,SAAS,EAAE,KAAK,SAAS,EAAE,MAAM,cAAc,CAAC;AAEhG,MAAM,WAAW,eAAgB,SAAQ,IAAI,CAAC,eAAe,EAAE,OAAO,CAAC;IACrE,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC7B,YAAY,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;CACrC;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,UAAU,CAAC,EACzB,QAAQ,EACR,UAAkB,EAClB,KAAK,EACL,YAAY,EACZ,4BAAoC,EACpC,8BAAsC,EACtC,GAAG,IAAI,EACR,EAAE,eAAe,+BAajB"}
@@ -1,7 +1,5 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
- import { useCallback, useMemo, useState } from "react";
3
2
  import { ScrollView } from "react-native";
4
- import { DragLockContext } from "../internal/dragLock.js";
5
3
  /**
6
4
  * A thin pass over ScrollView.
7
5
  *
@@ -11,22 +9,16 @@ import { DragLockContext } from "../internal/dragLock.js";
11
9
  * device. So the component stays for call-site parity and does almost nothing,
12
10
  * which is the honest version.
13
11
  *
14
- * Its one job was defaulting the indicator off, since the web version hides the
15
- * native scrollbar too. It has a second now: it publishes a drag lock, so a
16
- * control inside it can hold it still while it is being dragged. See
17
- * internal/dragLock a Slider is unusable in a scrolling screen without it.
12
+ * Its one job is defaulting the indicator off, since the web version hides the
13
+ * native scrollbar too.
14
+ *
15
+ * It used to have a second: publishing a drag lock that a Slider could claim to
16
+ * switch this list off for the duration of a drag. That existed because
17
+ * `PanResponder` could not tell the native scroll recogniser anything, so the
18
+ * only way to stop the list moving under a slider was to disable it. The Slider
19
+ * declares `activeOffsetX` now and the two gestures settle it between them, so
20
+ * there is nothing to hold still and nothing to leak if a drag is interrupted.
18
21
  */
19
- export function ScrollArea({ children, horizontal = false, style, contentStyle, showsVerticalScrollIndicator = false, showsHorizontalScrollIndicator = false, scrollEnabled, ...rest }) {
20
- const [locks, setLocks] = useState(0);
21
- // Counted rather than a boolean. Two controls can overlap in principle — a
22
- // drag interrupted by a second one starting — and the first to finish would
23
- // otherwise unlock the list while the second is still going.
24
- const lock = useCallback(() => setLocks((n) => n + 1), []);
25
- const unlock = useCallback(() => setLocks((n) => Math.max(0, n - 1)), []);
26
- const dragLock = useMemo(() => ({ locked: locks > 0, lock, unlock }), [locks, lock, unlock]);
27
- return (_jsx(DragLockContext.Provider, { value: dragLock, children: _jsx(ScrollView, { horizontal: horizontal, style: style, contentContainerStyle: contentStyle, showsVerticalScrollIndicator: showsVerticalScrollIndicator, showsHorizontalScrollIndicator: showsHorizontalScrollIndicator,
28
- // Only overridden while something is actually dragging, so a caller
29
- // that passes `scrollEnabled={false}` still gets a list that never
30
- // scrolls.
31
- scrollEnabled: scrollEnabled === false ? false : locks === 0, ...rest, children: children }) }));
22
+ export function ScrollArea({ children, horizontal = false, style, contentStyle, showsVerticalScrollIndicator = false, showsHorizontalScrollIndicator = false, ...rest }) {
23
+ return (_jsx(ScrollView, { horizontal: horizontal, style: style, contentContainerStyle: contentStyle, showsVerticalScrollIndicator: showsVerticalScrollIndicator, showsHorizontalScrollIndicator: showsHorizontalScrollIndicator, ...rest, children: children }));
32
24
  }
@@ -18,12 +18,23 @@ export interface SliderProps {
18
18
  /**
19
19
  * Dragging, on a surface where a drag might belong to something else.
20
20
  *
21
- * The web gets pointer capture: once the thumb is grabbed, every move belongs to
22
- * the slider until release. React Native has no equivalent, so this claims the
23
- * gesture through PanResponder and holds it, which is what stops a horizontal
24
- * drag being read as a scroll by whatever list the slider is sitting in.
21
+ * The web gets pointer capture: once the thumb is grabbed, every move belongs
22
+ * to the slider until release. React Native has no equivalent, and volume
23
+ * sliders live in settings lists, so a horizontal drag has to be told apart
24
+ * from the scroll it is sitting in.
25
25
  *
26
- * Volume sliders live in scroll views, so this matters more here than it looks.
26
+ * `activeOffsetX` is that distinction, declared rather than fought over. The
27
+ * pan claims the gesture once the finger has clearly gone sideways, and a
28
+ * vertical drag never activates it at all, so the scroll view keeps it. That
29
+ * replaces `onPanResponderTerminationRequest: () => false` plus the `DragLock`
30
+ * that had to switch the scroll view off, because refusing to hand back a JS
31
+ * responder says nothing to the native recogniser that actually does the
32
+ * scrolling on iOS.
33
+ *
34
+ * Both gestures run on the JS thread. The Drawer's stay worklets because they
35
+ * only move a shared value; every callback here has to reach React state and
36
+ * the consumer's `onValueChange`, so the hop is happening either way and
37
+ * pretending otherwise would only add a way to get it wrong.
27
38
  */
28
- export declare function Slider({ value: controlled, defaultValue, onValueChange, onValueCommit, min, max, step, disabled, tone, style, accessibilityLabel, }: SliderProps): import("react").JSX.Element;
39
+ export declare function Slider({ value: controlled, defaultValue, onValueChange, onValueCommit, min, max, step, disabled, tone, style, accessibilityLabel }: SliderProps): import("react").JSX.Element;
29
40
  //# sourceMappingURL=Slider.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"Slider.d.ts","sourceRoot":"","sources":["../../../src/components/Slider/Slider.tsx"],"names":[],"mappings":"AACA,OAAO,EAIL,KAAK,SAAS,EACd,KAAK,SAAS,EACf,MAAM,cAAc,CAAC;AAMtB,OAAO,EAAsB,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;AAIrE,MAAM,MAAM,UAAU,GAAG,OAAO,CAAC,aAAa,EAAE,SAAS,GAAG,WAAW,GAAG,SAAS,GAAG,QAAQ,CAAC,CAAC;AAEhG,MAAM,WAAW,WAAW;IAC1B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC,+DAA+D;IAC/D,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC7B,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC7B;AAKD;;;;;;;;;GASG;AACH,wBAAgB,MAAM,CAAC,EACrB,KAAK,EAAE,UAAU,EACjB,YAAgB,EAChB,aAAa,EACb,aAAa,EACb,GAAO,EACP,GAAS,EACT,IAAQ,EACR,QAAQ,EACR,IAAgB,EAChB,KAAK,EACL,kBAAkB,GACnB,EAAE,WAAW,+BAuKb"}
1
+ {"version":3,"file":"Slider.d.ts","sourceRoot":"","sources":["../../../src/components/Slider/Slider.tsx"],"names":[],"mappings":"AACA,OAAO,EAGL,KAAK,SAAS,EACd,KAAK,SAAS,EACf,MAAM,cAAc,CAAC;AAUtB,OAAO,EAAsB,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;AAGrE,MAAM,MAAM,UAAU,GAAG,OAAO,CAC9B,aAAa,EACb,SAAS,GAAG,WAAW,GAAG,SAAS,GAAG,QAAQ,CAC/C,CAAC;AAEF,MAAM,WAAW,WAAW;IAC1B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC,+DAA+D;IAC/D,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC7B,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC7B;AAKD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,MAAM,CAAC,EACrB,KAAK,EAAE,UAAU,EACjB,YAAgB,EAChB,aAAa,EACb,aAAa,EACb,GAAO,EACP,GAAS,EACT,IAAQ,EACR,QAAQ,EACR,IAAgB,EAChB,KAAK,EACL,kBAAkB,EACnB,EAAE,WAAW,+BA4Jb"}
@@ -1,29 +1,37 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { useEffect, useRef, useState } from "react";
3
- import { PanResponder, View, } from "react-native";
2
+ import { useEffect, useMemo, useRef, useState } from "react";
3
+ import { View } from "react-native";
4
+ import { Gesture, GestureDetector } from "react-native-gesture-handler";
4
5
  import Animated, { useAnimatedStyle, useSharedValue } from "react-native-reanimated";
5
6
  import { grytScaleSteps } from "@gryt/theme";
6
7
  import { springy } from "../../motion/index.js";
7
8
  import { toneRamp, useTheme } from "../../theme/index.js";
8
- import { useDragLock } from "../internal/dragLock.js";
9
9
  import { valueAt } from "./sliderValue.js";
10
10
  const TRACK_HEIGHT = 4;
11
11
  const THUMB = 20;
12
12
  /**
13
13
  * Dragging, on a surface where a drag might belong to something else.
14
14
  *
15
- * The web gets pointer capture: once the thumb is grabbed, every move belongs to
16
- * the slider until release. React Native has no equivalent, so this claims the
17
- * gesture through PanResponder and holds it, which is what stops a horizontal
18
- * drag being read as a scroll by whatever list the slider is sitting in.
15
+ * The web gets pointer capture: once the thumb is grabbed, every move belongs
16
+ * to the slider until release. React Native has no equivalent, and volume
17
+ * sliders live in settings lists, so a horizontal drag has to be told apart
18
+ * from the scroll it is sitting in.
19
19
  *
20
- * Volume sliders live in scroll views, so this matters more here than it looks.
20
+ * `activeOffsetX` is that distinction, declared rather than fought over. The
21
+ * pan claims the gesture once the finger has clearly gone sideways, and a
22
+ * vertical drag never activates it at all, so the scroll view keeps it. That
23
+ * replaces `onPanResponderTerminationRequest: () => false` plus the `DragLock`
24
+ * that had to switch the scroll view off, because refusing to hand back a JS
25
+ * responder says nothing to the native recogniser that actually does the
26
+ * scrolling on iOS.
27
+ *
28
+ * Both gestures run on the JS thread. The Drawer's stay worklets because they
29
+ * only move a shared value; every callback here has to reach React state and
30
+ * the consumer's `onValueChange`, so the hop is happening either way and
31
+ * pretending otherwise would only add a way to get it wrong.
21
32
  */
22
- export function Slider({ value: controlled, defaultValue = 0, onValueChange, onValueCommit, min = 0, max = 100, step = 1, disabled, tone = "primary", style, accessibilityLabel, }) {
33
+ export function Slider({ value: controlled, defaultValue = 0, onValueChange, onValueCommit, min = 0, max = 100, step = 1, disabled, tone = "primary", style, accessibilityLabel }) {
23
34
  const theme = useTheme();
24
- // Held for the duration of a drag, so the list this is sitting in stays put.
25
- // A no-op outside a ScrollView from this package — see useDragLock.
26
- const dragLock = useDragLock();
27
35
  const [uncontrolled, setUncontrolled] = useState(defaultValue);
28
36
  const value = controlled ?? uncontrolled;
29
37
  const [width, setWidth] = useState(0);
@@ -35,11 +43,6 @@ export function Slider({ value: controlled, defaultValue = 0, onValueChange, onV
35
43
  // fire from touch events, which is always after an effect has run.
36
44
  const state = useRef({ width, value, min, max, step, disabled });
37
45
  const emit = useRef({ onValueChange, onValueCommit, controlled });
38
- // Same reason as the others: the responder callbacks are created once, and
39
- // the lock's identity changes whenever the count does.
40
- const lockRef = useRef(dragLock);
41
- /** Where the current gesture started, in track coordinates. */
42
- const origin = useRef(0);
43
46
  /**
44
47
  * `active:scale-[0.94]` on the web. `hover:scale-[1.12]` has no touch
45
48
  * equivalent, so only the press half ports.
@@ -54,9 +57,6 @@ export function Slider({ value: controlled, defaultValue = 0, onValueChange, onV
54
57
  useEffect(() => {
55
58
  emit.current = { onValueChange, onValueCommit, controlled };
56
59
  }, [onValueChange, onValueCommit, controlled]);
57
- useEffect(() => {
58
- lockRef.current = dragLock;
59
- }, [dragLock]);
60
60
  const valueFromX = (x) => {
61
61
  const s = state.current;
62
62
  // Before layout there is no position to read, so hold what we have rather
@@ -70,90 +70,89 @@ export function Slider({ value: controlled, defaultValue = 0, onValueChange, onV
70
70
  setUncontrolled(next);
71
71
  emit.current.onValueChange?.(next);
72
72
  };
73
- // useState with an initialiser rather than useRef(...).current, which counts
74
- // as reading a ref during render. Created once either way.
75
- //
76
- // The rule is disabled for the block below because it reads what the closures
77
- // mention, not what runs. Every state.current here executes inside a touch
78
- // callback, long after render, and none of it decides what is drawn the
79
- // rendered position comes from `value` and `width` directly. The thing the
80
- // rule protects against, a component rendering from a ref and not updating,
81
- // cannot happen here.
82
- /* eslint-disable react-hooks/refs */
83
- const [responder] = useState(() => PanResponder.create({
84
- onStartShouldSetPanResponder: () => !state.current.disabled,
85
- // Claimed on move as well, so a drag that begins as a scroll still lands
86
- // here once it is clearly horizontal.
87
- onMoveShouldSetPanResponder: () => !state.current.disabled,
88
- onPanResponderGrant: (e) => {
89
- lockRef.current.lock();
90
- // Where the finger landed, kept for the drag to offset from.
91
- origin.current = e.nativeEvent.locationX;
73
+ /**
74
+ * Tap to seek, drag to scrub, and nothing at all if the finger goes down the
75
+ * page.
76
+ *
77
+ * Two gestures rather than one because they want opposite thresholds. A tap
78
+ * has to work with no movement; a drag must not claim anything until it is
79
+ * clearly sideways, or every attempt to scroll past a slider moves it. `Race`
80
+ * lets whichever qualifies win, and only one ever does.
81
+ */
82
+ const gesture = useMemo(() => {
83
+ const seek = (x) => {
84
+ const s = state.current;
85
+ if (s.disabled)
86
+ return;
87
+ // Absolute position in the track, not accumulated translation.
88
+ //
89
+ // The old version anchored on where the gesture started and added
90
+ // `gesture.dx`, which is the distance from the *start* rather than from
91
+ // the last event — so offsetting the live value by it added the whole
92
+ // travel again every move and the thumb accelerated away from the
93
+ // finger. Reading the position directly cannot express that bug.
94
+ apply(valueFromX(x));
95
+ };
96
+ const pan = Gesture.Pan()
97
+ .runOnJS(true)
98
+ .enabled(!disabled)
99
+ // Sideways, and clearly so. Below this the scroll view keeps the gesture,
100
+ // which is the whole reason the lock is gone.
101
+ .activeOffsetX([-4, 4])
102
+ .onBegin(() => {
103
+ // On touch down rather than on activation: the press feedback should
104
+ // answer the finger, not wait to find out where it is going.
92
105
  thumbScale.value = springy(grytScaleSteps.sliderThumb.press);
93
- apply(valueFromX(e.nativeEvent.locationX));
94
- },
95
- // `gesture.dx` is the distance from where the gesture *started*, not from
96
- // the previous event. Offsetting the live value by it therefore adds the
97
- // whole travel again on every move, and the thumb accelerates away from
98
- // the finger — 200px wide, 0-100, dragging to x=50 then 60 then 70 gave
99
- // 25, then 55, then 90. Tapping was always fine, because grant uses
100
- // locationX directly, which is what made it look like a rendering
101
- // problem rather than an arithmetic one.
102
- //
103
- // Anchoring on the grant position is also what makes the drag continue
104
- // from the finger: grant has already seeked the thumb there.
105
- onPanResponderMove: (_e, gesture) => {
106
- apply(valueFromX(origin.current + gesture.dx));
107
- },
108
- // The fix for GRYT-390's "the slider still lets me scroll the page".
109
- //
110
- // The comment above says this "claims the gesture and holds it". It did
111
- // not: `onPanResponderTerminationRequest` defaults to *granting*, so the
112
- // enclosing ScrollView asked for the responder as soon as the finger
113
- // moved and got it. Every drag inside a scrolling screen scrolled the
114
- // screen, which is every drag — volume sliders live in settings lists.
115
- //
116
- // Saying no is the whole fix. `onShouldBlockNativeResponder` stops the
117
- // native scroll view taking over underneath on Android, where the JS
118
- // answer alone is not enough.
119
- onPanResponderTerminationRequest: () => false,
120
- // A refused termination can still happen — the system can take the
121
- // responder away regardless, on a call or a notification. Without this
122
- // the lock leaks and the screen never scrolls again, which is a far
123
- // worse bug than the one being fixed.
124
- onPanResponderTerminate: () => {
125
- lockRef.current.unlock();
126
- thumbScale.value = springy(1);
127
- },
128
- onShouldBlockNativeResponder: () => true,
129
- onPanResponderRelease: () => {
130
- lockRef.current.unlock();
106
+ })
107
+ .onStart((event) => seek(event.x))
108
+ .onUpdate((event) => seek(event.x))
109
+ .onEnd(() => emit.current.onValueCommit?.(state.current.value))
110
+ .onFinalize(() => {
131
111
  thumbScale.value = springy(1);
112
+ });
113
+ const tap = Gesture.Tap()
114
+ .runOnJS(true)
115
+ .enabled(!disabled)
116
+ .onEnd((event, success) => {
117
+ if (!success)
118
+ return;
119
+ seek(event.x);
132
120
  emit.current.onValueCommit?.(state.current.value);
133
- },
134
- }));
135
- /* eslint-enable react-hooks/refs */
121
+ });
122
+ return Gesture.Race(pan, tap);
123
+ // `apply` and `valueFromX` read refs that are written in effects, so they
124
+ // do not need to be dependencies — and listing them would rebuild the
125
+ // gesture on every render.
126
+ // eslint-disable-next-line react-hooks/exhaustive-deps
127
+ }, [disabled, thumbScale]);
136
128
  const ratio = (value - min) / (max - min || 1);
137
- return (_jsxs(View, { accessibilityRole: "adjustable", accessibilityLabel: accessibilityLabel, accessibilityValue: { min, max, now: value }, onLayout: (e) => setWidth(e.nativeEvent.layout.width), style: [{ height: THUMB, justifyContent: "center", opacity: disabled ? 0.5 : 1 }, style], ...responder.panHandlers, children: [_jsx(View, { style: {
138
- height: TRACK_HEIGHT,
139
- borderRadius: TRACK_HEIGHT / 2,
140
- backgroundColor: theme.scales.neutral[4],
141
- }, children: _jsx(View, { style: {
142
- width: `${ratio * 100}%`,
143
- height: "100%",
129
+ return (_jsx(GestureDetector, { gesture: gesture, children: _jsxs(View, { accessibilityRole: "adjustable", accessibilityLabel: accessibilityLabel, accessibilityValue: { min, max, now: value }, onLayout: (e) => setWidth(e.nativeEvent.layout.width), style: [
130
+ {
131
+ height: THUMB,
132
+ justifyContent: "center",
133
+ opacity: disabled ? 0.5 : 1
134
+ },
135
+ style
136
+ ], children: [_jsx(View, { style: {
137
+ height: TRACK_HEIGHT,
144
138
  borderRadius: TRACK_HEIGHT / 2,
145
- backgroundColor: ramp[8],
146
- } }) }), _jsx(Animated.View, { pointerEvents: "none", style: [
147
- {
148
- position: "absolute",
149
- left: Math.max(0, ratio * width - THUMB / 2),
150
- width: THUMB,
151
- height: THUMB,
152
- borderRadius: THUMB / 2,
153
- backgroundColor: ramp[8],
154
- borderWidth: 2,
155
- borderColor: theme.color.bg,
156
- },
157
- thumbStyle,
158
- ] })] }));
139
+ backgroundColor: theme.scales.neutral[4]
140
+ }, children: _jsx(View, { style: {
141
+ width: `${ratio * 100}%`,
142
+ height: "100%",
143
+ borderRadius: TRACK_HEIGHT / 2,
144
+ backgroundColor: ramp[8]
145
+ } }) }), _jsx(Animated.View, { pointerEvents: "none", style: [
146
+ {
147
+ position: "absolute",
148
+ left: Math.max(0, ratio * width - THUMB / 2),
149
+ width: THUMB,
150
+ height: THUMB,
151
+ borderRadius: THUMB / 2,
152
+ backgroundColor: ramp[8],
153
+ borderWidth: 2,
154
+ borderColor: theme.color.bg
155
+ },
156
+ thumbStyle
157
+ ] })] }) }));
159
158
  }
package/dist/index.d.ts CHANGED
@@ -31,7 +31,6 @@ export { NumberField, type NumberFieldProps } from "./components/NumberField/Num
31
31
  export { OtpField, type OtpFieldProps } from "./components/OtpField/OtpField.js";
32
32
  export { ScrollArea, type ScrollAreaProps } from "./components/ScrollArea/ScrollArea.js";
33
33
  export { Sheet, type SheetCloseProps, type SheetContentProps, type SheetProps, SheetProvider, type SheetProviderProps, type SheetTitleProps, type SheetTriggerProps, } from "./components/Sheet/Sheet.js";
34
- export { useDragLock, type DragLock } from "./components/internal/dragLock.js";
35
34
  export { ToastProvider, useToast, type ToastOptions, type ToastSeverity, } from "./components/Toast/Toast.js";
36
35
  export { Toolbar, ToolbarSeparator, type ToolbarProps, type ToolbarSeparatorProps, } from "./components/Toolbar/Toolbar.js";
37
36
  export { AnchoredPopup, type AnchoredPopupProps } from "./overlay/AnchoredPopup.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,iBAAiB,EACjB,SAAS,EACT,iBAAiB,EACjB,UAAU,EACV,QAAQ,EACR,KAAK,cAAc,EACnB,KAAK,sBAAsB,EAC3B,KAAK,WAAW,EAChB,KAAK,kBAAkB,EACvB,KAAK,IAAI,EACT,QAAQ,EACR,KAAK,aAAa,GACnB,MAAM,SAAS,CAAC;AAEjB,OAAO,EACL,WAAW,EACX,KAAK,qBAAqB,EAC1B,KAAK,oBAAoB,GAC1B,MAAM,sCAAsC,CAAC;AAC9C,OAAO,EAAE,KAAK,EAAE,KAAK,UAAU,EAAE,KAAK,aAAa,EAAE,MAAM,0BAA0B,CAAC;AACtF,OAAO,EAAE,MAAM,EAAE,KAAK,WAAW,EAAE,KAAK,UAAU,EAAE,MAAM,4BAA4B,CAAC;AACvF,OAAO,EAAE,KAAK,EAAE,KAAK,UAAU,EAAE,KAAK,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAClF,OAAO,EAAE,MAAM,EAAE,KAAK,WAAW,EAAE,KAAK,UAAU,EAAE,KAAK,UAAU,EAAE,MAAM,4BAA4B,CAAC;AACxG,OAAO,EACL,IAAI,EACJ,WAAW,EACX,WAAW,EACX,UAAU,EACV,KAAK,eAAe,EACpB,KAAK,SAAS,GACf,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,QAAQ,EAAE,KAAK,aAAa,EAAE,KAAK,YAAY,EAAE,MAAM,gCAAgC,CAAC;AACjG,OAAO,EAAE,IAAI,EAAE,KAAK,SAAS,EAAE,KAAK,QAAQ,EAAE,KAAK,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAC/F,OAAO,EACL,MAAM,EACN,KAAK,gBAAgB,EACrB,KAAK,eAAe,EACpB,KAAK,kBAAkB,GACxB,MAAM,4BAA4B,CAAC;AACpC,OAAO,EACL,WAAW,EACX,KAAK,oBAAoB,GAC1B,MAAM,sCAAsC,CAAC;AAC9C,OAAO,EAAE,OAAO,EAAE,KAAK,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAC1E,OAAO,EAAE,IAAI,EAAE,KAAK,aAAa,EAAE,KAAK,cAAc,EAAE,KAAK,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAC3G,OAAO,EAAE,KAAK,EAAE,KAAK,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAClE,OAAO,EAAE,OAAO,EAAE,KAAK,iBAAiB,EAAE,KAAK,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AACtG,OAAO,EAAE,QAAQ,EAAE,KAAK,aAAa,EAAE,MAAM,gCAAgC,CAAC;AAC9E,OAAO,EAAE,KAAK,EAAE,UAAU,EAAE,KAAK,eAAe,EAAE,KAAK,UAAU,EAAE,KAAK,SAAS,EAAE,MAAM,0BAA0B,CAAC;AACpH,OAAO,EACL,MAAM,EACN,KAAK,YAAY,EACjB,KAAK,WAAW,EAChB,KAAK,UAAU,GAChB,MAAM,4BAA4B,CAAC;AACpC,OAAO,EACL,MAAM,EACN,KAAK,WAAW,EAChB,KAAK,UAAU,GAChB,MAAM,4BAA4B,CAAC;AACpC,OAAO,EAAE,QAAQ,EAAE,KAAK,aAAa,EAAE,MAAM,gCAAgC,CAAC;AAC9E,OAAO,EAAE,OAAO,EAAE,KAAK,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAC1E,OAAO,EAAE,OAAO,EAAE,KAAK,YAAY,EAAE,KAAK,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAC7F,OAAO,EAAE,MAAM,EAAE,KAAK,WAAW,EAAE,KAAK,UAAU,EAAE,MAAM,4BAA4B,CAAC;AACvF,OAAO,EACL,GAAG,EACH,IAAI,EACJ,KAAK,QAAQ,EACb,KAAK,aAAa,EAClB,KAAK,cAAc,EACnB,KAAK,SAAS,GACf,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,SAAS,EAAE,KAAK,cAAc,EAAE,KAAK,aAAa,EAAE,MAAM,kCAAkC,CAAC;AACtG,OAAO,EAAE,MAAM,EAAE,KAAK,WAAW,EAAE,KAAK,UAAU,EAAE,KAAK,UAAU,EAAE,MAAM,4BAA4B,CAAC;AACxG,OAAO,EACL,OAAO,EACP,eAAe,EACf,KAAK,iBAAiB,EACtB,KAAK,gBAAgB,EACrB,KAAK,mBAAmB,GACzB,MAAM,8BAA8B,CAAC;AACtC,OAAO,EACL,SAAS,EACT,KAAK,kBAAkB,EACvB,KAAK,kBAAkB,GACxB,MAAM,kCAAkC,CAAC;AAC1C,OAAO,EACL,MAAM,EACN,KAAK,gBAAgB,EACrB,KAAK,eAAe,EACpB,KAAK,UAAU,GAChB,MAAM,4BAA4B,CAAC;AACpC,OAAO,EAAE,WAAW,EAAE,KAAK,gBAAgB,EAAE,MAAM,sCAAsC,CAAC;AAC1F,OAAO,EAAE,QAAQ,EAAE,KAAK,aAAa,EAAE,MAAM,gCAAgC,CAAC;AAC9E,OAAO,EAAE,UAAU,EAAE,KAAK,eAAe,EAAE,MAAM,oCAAoC,CAAC;AACtF,OAAO,EACL,KAAK,EACL,KAAK,eAAe,EACpB,KAAK,iBAAiB,EACtB,KAAK,UAAU,EACf,aAAa,EACb,KAAK,kBAAkB,EACvB,KAAK,eAAe,EACpB,KAAK,iBAAiB,GACvB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAE,WAAW,EAAE,KAAK,QAAQ,EAAE,MAAM,gCAAgC,CAAC;AAC5E,OAAO,EACL,aAAa,EACb,QAAQ,EACR,KAAK,YAAY,EACjB,KAAK,aAAa,GACnB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EACL,OAAO,EACP,gBAAgB,EAChB,KAAK,YAAY,EACjB,KAAK,qBAAqB,GAC3B,MAAM,8BAA8B,CAAC;AACtC,OAAO,EAAE,aAAa,EAAE,KAAK,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AACjF,OAAO,EACL,UAAU,EACV,KAAK,KAAK,EACV,KAAK,aAAa,EAClB,KAAK,UAAU,EACf,KAAK,cAAc,EACnB,KAAK,IAAI,GACV,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,YAAY,EAAE,KAAK,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAC3E,OAAO,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAC5D,OAAO,EACL,SAAS,EACT,UAAU,EACV,eAAe,EACf,IAAI,EACJ,OAAO,EACP,MAAM,EACN,aAAa,EACb,KAAK,UAAU,EAChB,MAAM,mBAAmB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -31,7 +31,6 @@ export { NumberField } from "./components/NumberField/NumberField.js";
31
31
  export { OtpField } from "./components/OtpField/OtpField.js";
32
32
  export { ScrollArea } from "./components/ScrollArea/ScrollArea.js";
33
33
  export { Sheet, SheetProvider, } from "./components/Sheet/Sheet.js";
34
- export { useDragLock } from "./components/internal/dragLock.js";
35
34
  export { ToastProvider, useToast, } from "./components/Toast/Toast.js";
36
35
  export { Toolbar, ToolbarSeparator, } from "./components/Toolbar/Toolbar.js";
37
36
  export { AnchoredPopup } from "./overlay/AnchoredPopup.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gryt/ui-native",
3
- "version": "0.6.1",
3
+ "version": "0.8.0",
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.",
@@ -1,43 +0,0 @@
1
- /**
2
- * A control holding a scrolling ancestor still while it is being dragged.
3
- *
4
- * This exists because of one bug, and the obvious fixes all fail in ways that
5
- * look like they worked, so it is worth writing down.
6
- *
7
- * Dragging a `Slider` inside a scrolling screen scrolled the screen. The first
8
- * fix — `onPanResponderTerminationRequest: () => false` — was necessary and not
9
- * sufficient: it stops the ScrollView taking the responder away, so the thumb
10
- * follows your finger now, and the page still scrolls underneath. On iOS the
11
- * scroll is driven by a native `UIPanGestureRecognizer` that is not part of
12
- * React Native's JS responder system, so refusing to hand over the responder
13
- * answers a question the native recogniser never asked.
14
- * `onShouldBlockNativeResponder` is the knob for that, and it is Android-only.
15
- *
16
- * What works is `scrollEnabled` — and the control cannot reach the scroll view
17
- * to set it, since they are separated by however much app markup. So the scroll
18
- * view publishes a lock, the control claims it, and neither has to know where
19
- * the other is.
20
- *
21
- * `react-native-gesture-handler` solves this properly, by negotiating with the
22
- * native recogniser rather than switching it off. That is the better fix and it
23
- * is blocked on GRYT-393: this package ships prebuilt JavaScript, Reanimated's
24
- * babel plugin does not run over `node_modules`, and gesture callbacks are
25
- * therefore never worklets. Measured on a device, not assumed. When that is
26
- * solved, this should probably go.
27
- */
28
- export interface DragLock {
29
- locked: boolean;
30
- lock: () => void;
31
- unlock: () => void;
32
- }
33
- export declare const DragLockContext: import("react").Context<DragLock | null>;
34
- /**
35
- * Claimed by a control for the duration of a drag.
36
- *
37
- * No-ops outside a `ScrollArea`, which is the normal case for a control that is
38
- * not in a scrolling screen. A control must not require the provider: a slider
39
- * in a fixed panel has nothing to lock, and throwing there would be inventing a
40
- * dependency on a component it does not need.
41
- */
42
- export declare function useDragLock(): DragLock;
43
- //# sourceMappingURL=dragLock.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"dragLock.d.ts","sourceRoot":"","sources":["../../../src/components/internal/dragLock.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,MAAM,WAAW,QAAQ;IACvB,MAAM,EAAE,OAAO,CAAC;IAChB,IAAI,EAAE,MAAM,IAAI,CAAC;IACjB,MAAM,EAAE,MAAM,IAAI,CAAC;CACpB;AAED,eAAO,MAAM,eAAe,0CAAuC,CAAC;AAQpE;;;;;;;GAOG;AACH,wBAAgB,WAAW,IAAI,QAAQ,CAEtC"}
@@ -1,18 +0,0 @@
1
- import { createContext, useContext } from "react";
2
- export const DragLockContext = createContext(null);
3
- const NO_LOCK = {
4
- locked: false,
5
- lock: () => { },
6
- unlock: () => { },
7
- };
8
- /**
9
- * Claimed by a control for the duration of a drag.
10
- *
11
- * No-ops outside a `ScrollArea`, which is the normal case for a control that is
12
- * not in a scrolling screen. A control must not require the provider: a slider
13
- * in a fixed panel has nothing to lock, and throwing there would be inventing a
14
- * dependency on a component it does not need.
15
- */
16
- export function useDragLock() {
17
- return useContext(DragLockContext) ?? NO_LOCK;
18
- }