react-native-gtkx 0.2.0-alpha.2 → 0.3.0-alpha.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.
- package/README.md +5 -2
- package/dist/aliases/index.d.ts +84 -0
- package/dist/aliases/index.js +208 -0
- package/dist/aliases/index.js.map +1 -0
- package/dist/animated/index.d.ts +2 -0
- package/dist/animated/index.js +5 -0
- package/dist/animated/index.js.map +1 -1
- package/dist/animated/types.d.ts +1 -0
- package/dist/animated/types.js.map +1 -1
- package/dist/animated/value-animation.js +17 -4
- package/dist/animated/value-animation.js.map +1 -1
- package/dist/apis/host.gtkx.js +9 -12
- package/dist/apis/host.gtkx.js.map +1 -1
- package/dist/apis/index.d.ts +4 -2
- package/dist/apis/index.js +2 -0
- package/dist/apis/index.js.map +1 -1
- package/dist/apis/keyboard.d.ts +60 -0
- package/dist/apis/keyboard.js +63 -0
- package/dist/apis/keyboard.js.map +1 -0
- package/dist/apis/log-box.d.ts +15 -0
- package/dist/apis/log-box.js +46 -0
- package/dist/apis/log-box.js.map +1 -0
- package/dist/apis/platform.d.ts +18 -1
- package/dist/apis/platform.js.map +1 -1
- package/dist/common/index.d.ts +0 -1
- package/dist/common/index.js +24 -1
- package/dist/common/index.js.map +1 -1
- package/dist/components/animated.d.ts +70 -6
- package/dist/components/animated.js +559 -46
- package/dist/components/animated.js.map +1 -1
- package/dist/components/driven-size.d.ts +27 -0
- package/dist/components/driven-size.js +85 -0
- package/dist/components/driven-size.js.map +1 -0
- package/dist/components/find-node-handle.d.ts +11 -0
- package/dist/components/find-node-handle.js +53 -0
- package/dist/components/find-node-handle.js.map +1 -0
- package/dist/components/flat-list.d.ts +3 -2
- package/dist/components/flat-list.js.map +1 -1
- package/dist/components/frame-scheduler.d.ts +16 -0
- package/dist/components/frame-scheduler.js +37 -0
- package/dist/components/frame-scheduler.js.map +1 -0
- package/dist/components/image.d.ts +6 -1
- package/dist/components/image.js +15 -4
- package/dist/components/image.js.map +1 -1
- package/dist/components/index.d.ts +7 -5
- package/dist/components/index.js +5 -3
- package/dist/components/index.js.map +1 -1
- package/dist/components/measure.d.ts +51 -1
- package/dist/components/measure.js +101 -2
- package/dist/components/measure.js.map +1 -1
- package/dist/components/pressable.d.ts +61 -1
- package/dist/components/pressable.js +151 -4
- package/dist/components/pressable.js.map +1 -1
- package/dist/components/rect-store.d.ts +58 -0
- package/dist/components/rect-store.js +124 -3
- package/dist/components/rect-store.js.map +1 -1
- package/dist/components/root.js +6 -1
- package/dist/components/root.js.map +1 -1
- package/dist/components/scroll-phase.d.ts +35 -0
- package/dist/components/scroll-phase.js +26 -0
- package/dist/components/scroll-phase.js.map +1 -0
- package/dist/components/scroll-view.d.ts +39 -3
- package/dist/components/scroll-view.js +378 -43
- package/dist/components/scroll-view.js.map +1 -1
- package/dist/components/text-input.js +9 -25
- package/dist/components/text-input.js.map +1 -1
- package/dist/components/text.d.ts +6 -2
- package/dist/components/text.js +43 -8
- package/dist/components/text.js.map +1 -1
- package/dist/components/use-focus.d.ts +27 -0
- package/dist/components/use-focus.js +116 -0
- package/dist/components/use-focus.js.map +1 -0
- package/dist/components/use-layout-child.d.ts +13 -1
- package/dist/components/use-layout-child.js +188 -5
- package/dist/components/use-layout-child.js.map +1 -1
- package/dist/components/view.d.ts +5 -2
- package/dist/components/view.js +18 -13
- package/dist/components/view.js.map +1 -1
- package/dist/components/virtualized-list.d.ts +45 -5
- package/dist/components/virtualized-list.js +90 -24
- package/dist/components/virtualized-list.js.map +1 -1
- package/dist/components/wheel-scroll-session.d.ts +7 -0
- package/dist/components/wheel-scroll-session.js +53 -0
- package/dist/components/wheel-scroll-session.js.map +1 -0
- package/dist/components/widget-retention.d.ts +35 -0
- package/dist/components/widget-retention.js +169 -0
- package/dist/components/widget-retention.js.map +1 -0
- package/dist/contracts.d.ts +4 -0
- package/dist/contracts.js.map +1 -1
- package/dist/dnd/context.d.ts +69 -0
- package/dist/dnd/context.js +149 -0
- package/dist/dnd/context.js.map +1 -0
- package/dist/dnd/draggable.d.ts +59 -0
- package/dist/dnd/draggable.js +149 -0
- package/dist/dnd/draggable.js.map +1 -0
- package/dist/dnd/droppable.d.ts +24 -0
- package/dist/dnd/droppable.js +74 -0
- package/dist/dnd/droppable.js.map +1 -0
- package/dist/dnd/gtk-controllers.d.ts +39 -0
- package/dist/dnd/gtk-controllers.js +110 -0
- package/dist/dnd/gtk-controllers.js.map +1 -0
- package/dist/dnd/index.d.ts +6 -0
- package/dist/dnd/index.js +31 -0
- package/dist/dnd/index.js.map +1 -0
- package/dist/dnd/order.d.ts +7 -0
- package/dist/dnd/order.js +20 -0
- package/dist/dnd/order.js.map +1 -0
- package/dist/dnd/payload.d.ts +14 -0
- package/dist/dnd/payload.js +41 -0
- package/dist/dnd/payload.js.map +1 -0
- package/dist/dnd/sortable.d.ts +49 -0
- package/dist/dnd/sortable.js +307 -0
- package/dist/dnd/sortable.js.map +1 -0
- package/dist/dnd/types.d.ts +290 -0
- package/dist/dnd/types.js +25 -0
- package/dist/dnd/types.js.map +1 -0
- package/dist/gesture-handler-compat/builder.d.ts +251 -0
- package/dist/gesture-handler-compat/builder.js +513 -0
- package/dist/gesture-handler-compat/builder.js.map +1 -0
- package/dist/gesture-handler-compat/composition.d.ts +23 -0
- package/dist/gesture-handler-compat/composition.js +111 -0
- package/dist/gesture-handler-compat/composition.js.map +1 -0
- package/dist/gesture-handler-compat/deciders.d.ts +3 -0
- package/dist/gesture-handler-compat/deciders.js +47 -0
- package/dist/gesture-handler-compat/deciders.js.map +1 -0
- package/dist/gesture-handler-compat/detector-runtime.d.ts +44 -0
- package/dist/gesture-handler-compat/detector-runtime.js +374 -0
- package/dist/gesture-handler-compat/detector-runtime.js.map +1 -0
- package/dist/gesture-handler-compat/detector.d.ts +22 -0
- package/dist/gesture-handler-compat/detector.js +104 -0
- package/dist/gesture-handler-compat/detector.js.map +1 -0
- package/dist/gesture-handler-compat/fling.d.ts +24 -0
- package/dist/gesture-handler-compat/fling.js +123 -0
- package/dist/gesture-handler-compat/fling.js.map +1 -0
- package/dist/gesture-handler-compat/force-touch.d.ts +13 -0
- package/dist/gesture-handler-compat/force-touch.js +40 -0
- package/dist/gesture-handler-compat/force-touch.js.map +1 -0
- package/dist/gesture-handler-compat/hooks.d.ts +157 -0
- package/dist/gesture-handler-compat/hooks.js +268 -0
- package/dist/gesture-handler-compat/hooks.js.map +1 -0
- package/dist/gesture-handler-compat/hover.d.ts +2 -0
- package/dist/gesture-handler-compat/hover.js +22 -0
- package/dist/gesture-handler-compat/hover.js.map +1 -0
- package/dist/gesture-handler-compat/index.d.ts +98 -0
- package/dist/gesture-handler-compat/index.js +255 -0
- package/dist/gesture-handler-compat/index.js.map +1 -0
- package/dist/gesture-handler-compat/long-press.d.ts +6 -0
- package/dist/gesture-handler-compat/long-press.js +21 -0
- package/dist/gesture-handler-compat/long-press.js.map +1 -0
- package/dist/gesture-handler-compat/manual.d.ts +2 -0
- package/dist/gesture-handler-compat/manual.js +18 -0
- package/dist/gesture-handler-compat/manual.js.map +1 -0
- package/dist/gesture-handler-compat/native.d.ts +7 -0
- package/dist/gesture-handler-compat/native.js +34 -0
- package/dist/gesture-handler-compat/native.js.map +1 -0
- package/dist/gesture-handler-compat/orchestrator.d.ts +77 -0
- package/dist/gesture-handler-compat/orchestrator.js +284 -0
- package/dist/gesture-handler-compat/orchestrator.js.map +1 -0
- package/dist/gesture-handler-compat/pan.d.ts +23 -0
- package/dist/gesture-handler-compat/pan.js +116 -0
- package/dist/gesture-handler-compat/pan.js.map +1 -0
- package/dist/gesture-handler-compat/recognizer.d.ts +268 -0
- package/dist/gesture-handler-compat/recognizer.js +1056 -0
- package/dist/gesture-handler-compat/recognizer.js.map +1 -0
- package/dist/gesture-handler-compat/relations.d.ts +43 -0
- package/dist/gesture-handler-compat/relations.js +128 -0
- package/dist/gesture-handler-compat/relations.js.map +1 -0
- package/dist/gesture-handler-compat/tap.d.ts +6 -0
- package/dist/gesture-handler-compat/tap.js +29 -0
- package/dist/gesture-handler-compat/tap.js.map +1 -0
- package/dist/gesture-handler-compat/touchpad.d.ts +41 -0
- package/dist/gesture-handler-compat/touchpad.js +64 -0
- package/dist/gesture-handler-compat/touchpad.js.map +1 -0
- package/dist/gesture-handler-compat/types.d.ts +441 -0
- package/dist/gesture-handler-compat/types.js +167 -0
- package/dist/gesture-handler-compat/types.js.map +1 -0
- package/dist/gtk/controllers.d.ts +39 -0
- package/dist/gtk/controllers.js +92 -0
- package/dist/gtk/controllers.js.map +1 -0
- package/dist/gtk/index.d.ts +2 -1
- package/dist/gtk/index.js +7 -1
- package/dist/gtk/index.js.map +1 -1
- package/dist/gtkx/bridge/geometry.js +6 -4
- package/dist/gtkx/bridge/geometry.js.map +1 -1
- package/dist/gtkx/bridge/index.d.ts +5 -3
- package/dist/gtkx/bridge/index.js +6 -3
- package/dist/gtkx/bridge/index.js.map +1 -1
- package/dist/gtkx/bridge/slot-portal.js.map +1 -1
- package/dist/gtkx/bridge/use-signal.js +1 -1
- package/dist/gtkx/bridge/use-signal.js.map +1 -1
- package/dist/gtkx/bridge/view-box.d.ts +56 -0
- package/dist/gtkx/bridge/view-box.js +394 -9
- package/dist/gtkx/bridge/view-box.js.map +1 -1
- package/dist/gtkx/bridge/widget-css.d.ts +17 -0
- package/dist/gtkx/bridge/widget-css.js +72 -0
- package/dist/gtkx/bridge/widget-css.js.map +1 -0
- package/dist/index.d.ts +3 -3
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/layout/driven-size.d.ts +12 -0
- package/dist/layout/driven-size.js +70 -0
- package/dist/layout/driven-size.js.map +1 -0
- package/dist/layout/engine.d.ts +17 -1
- package/dist/layout/engine.js +3 -20
- package/dist/layout/engine.js.map +1 -1
- package/dist/layout/node.d.ts +13 -0
- package/dist/layout/node.js +14 -0
- package/dist/layout/node.js.map +1 -1
- package/dist/layout/yoga.d.ts +3 -2
- package/dist/layout/yoga.js +5 -2
- package/dist/layout/yoga.js.map +1 -1
- package/dist/mcp/data/generated.d.ts +257 -60
- package/dist/mcp/data/generated.js +306 -62
- package/dist/mcp/data/generated.js.map +1 -1
- package/dist/metro/index.d.ts +10 -0
- package/dist/metro/index.js +21 -12
- package/dist/metro/index.js.map +1 -1
- package/dist/navigation/sidebar.d.ts +16 -0
- package/dist/navigation/sidebar.js +65 -1
- package/dist/navigation/sidebar.js.map +1 -1
- package/dist/reanimated-compat/animated-ref.d.ts +32 -0
- package/dist/reanimated-compat/animated-ref.js +57 -0
- package/dist/reanimated-compat/animated-ref.js.map +1 -0
- package/dist/reanimated-compat/animation.d.ts +177 -0
- package/dist/reanimated-compat/animation.js +540 -0
- package/dist/reanimated-compat/animation.js.map +1 -0
- package/dist/reanimated-compat/color.d.ts +28 -0
- package/dist/reanimated-compat/color.js +249 -0
- package/dist/reanimated-compat/color.js.map +1 -0
- package/dist/reanimated-compat/decay.d.ts +39 -0
- package/dist/reanimated-compat/decay.js +96 -0
- package/dist/reanimated-compat/decay.js.map +1 -0
- package/dist/reanimated-compat/easing.d.ts +30 -0
- package/dist/reanimated-compat/easing.js +76 -0
- package/dist/reanimated-compat/easing.js.map +1 -0
- package/dist/reanimated-compat/hooks.d.ts +14 -0
- package/dist/reanimated-compat/hooks.js +217 -0
- package/dist/reanimated-compat/hooks.js.map +1 -0
- package/dist/reanimated-compat/index.d.ts +258 -0
- package/dist/reanimated-compat/index.js +464 -0
- package/dist/reanimated-compat/index.js.map +1 -0
- package/dist/reanimated-compat/interpolation.d.ts +18 -0
- package/dist/reanimated-compat/interpolation.js +122 -0
- package/dist/reanimated-compat/interpolation.js.map +1 -0
- package/dist/reanimated-compat/layout-animation-config.d.ts +43 -0
- package/dist/reanimated-compat/layout-animation-config.js +78 -0
- package/dist/reanimated-compat/layout-animation-config.js.map +1 -0
- package/dist/reanimated-compat/layout-animation-presets.d.ts +117 -0
- package/dist/reanimated-compat/layout-animation-presets.js +498 -0
- package/dist/reanimated-compat/layout-animation-presets.js.map +1 -0
- package/dist/reanimated-compat/layout-animation-runtime.d.ts +21 -0
- package/dist/reanimated-compat/layout-animation-runtime.js +311 -0
- package/dist/reanimated-compat/layout-animation-runtime.js.map +1 -0
- package/dist/reanimated-compat/layout-animation-view.d.ts +27 -0
- package/dist/reanimated-compat/layout-animation-view.js +266 -0
- package/dist/reanimated-compat/layout-animation-view.js.map +1 -0
- package/dist/reanimated-compat/layout-animation.d.ts +171 -0
- package/dist/reanimated-compat/layout-animation.js +469 -0
- package/dist/reanimated-compat/layout-animation.js.map +1 -0
- package/dist/reanimated-compat/layout-transitions.d.ts +83 -0
- package/dist/reanimated-compat/layout-transitions.js +350 -0
- package/dist/reanimated-compat/layout-transitions.js.map +1 -0
- package/dist/reanimated-compat/mutable.d.ts +29 -0
- package/dist/reanimated-compat/mutable.js +139 -0
- package/dist/reanimated-compat/mutable.js.map +1 -0
- package/dist/reanimated-compat/props.d.ts +18 -0
- package/dist/reanimated-compat/props.js +111 -0
- package/dist/reanimated-compat/props.js.map +1 -0
- package/dist/reanimated-compat/scroll-handler.d.ts +119 -0
- package/dist/reanimated-compat/scroll-handler.js +274 -0
- package/dist/reanimated-compat/scroll-handler.js.map +1 -0
- package/dist/reanimated-compat/scroll-offset.d.ts +12 -0
- package/dist/reanimated-compat/scroll-offset.js +104 -0
- package/dist/reanimated-compat/scroll-offset.js.map +1 -0
- package/dist/reanimated-compat/style.d.ts +90 -0
- package/dist/reanimated-compat/style.js +452 -0
- package/dist/reanimated-compat/style.js.map +1 -0
- package/dist/reanimated-compat/test-timers.d.ts +28 -0
- package/dist/reanimated-compat/test-timers.js +136 -0
- package/dist/reanimated-compat/test-timers.js.map +1 -0
- package/dist/reanimated-compat/threads.d.ts +13 -0
- package/dist/reanimated-compat/threads.js +41 -0
- package/dist/reanimated-compat/threads.js.map +1 -0
- package/dist/reanimated-compat/tracking.d.ts +41 -0
- package/dist/reanimated-compat/tracking.js +106 -0
- package/dist/reanimated-compat/tracking.js.map +1 -0
- package/dist/reanimated-compat/updater-animations.d.ts +36 -0
- package/dist/reanimated-compat/updater-animations.js +273 -0
- package/dist/reanimated-compat/updater-animations.js.map +1 -0
- package/dist/responder/system.d.ts +80 -7
- package/dist/responder/system.js +143 -23
- package/dist/responder/system.js.map +1 -1
- package/dist/responder/use-responder.d.ts +11 -0
- package/dist/responder/use-responder.js +225 -6
- package/dist/responder/use-responder.js.map +1 -1
- package/dist/runner/host-dev.js +1 -1
- package/dist/runner/host-dev.js.map +1 -1
- package/dist/runner/host.js +1 -1
- package/dist/runner/host.js.map +1 -1
- package/dist/runner/index.js +3 -3
- package/dist/runner/index.js.map +1 -1
- package/dist/sea/gtkx-config-module.js +1 -1
- package/dist/sea/gtkx-config-module.js.map +1 -1
- package/dist/style/absolute-insets.d.ts +26 -0
- package/dist/style/absolute-insets.js +113 -0
- package/dist/style/absolute-insets.js.map +1 -0
- package/dist/style/animated-size.d.ts +25 -0
- package/dist/style/animated-size.js +225 -0
- package/dist/style/animated-size.js.map +1 -0
- package/dist/style/colors.d.ts +17 -0
- package/dist/style/colors.js +25 -6
- package/dist/style/colors.js.map +1 -1
- package/dist/style/imperative-css.d.ts +21 -0
- package/dist/style/imperative-css.js +63 -0
- package/dist/style/imperative-css.js.map +1 -0
- package/dist/style/index.d.ts +2 -1
- package/dist/style/index.js +2 -1
- package/dist/style/index.js.map +1 -1
- package/dist/style/split-style.js +5 -0
- package/dist/style/split-style.js.map +1 -1
- package/dist/testing/index.js.map +1 -1
- package/dist/unsupported-export.d.ts +8 -0
- package/dist/unsupported-export.js +66 -0
- package/dist/unsupported-export.js.map +1 -0
- package/dist/vite/index.d.ts +21 -8
- package/dist/vite/index.js +127 -82
- package/dist/vite/index.js.map +1 -1
- package/dist/vitest/index.d.ts +2 -2
- package/dist/vitest/index.js.map +1 -1
- package/dist/worklets-compat/index.d.ts +4 -0
- package/dist/worklets-compat/index.js +28 -0
- package/dist/worklets-compat/index.js.map +1 -0
- package/dist/worklets-compat/surface.d.ts +110 -0
- package/dist/worklets-compat/surface.js +153 -0
- package/dist/worklets-compat/surface.js.map +1 -0
- package/package.json +25 -9
- package/types.d.ts +9 -0
- package/dist/common/list.d.ts +0 -82
- package/dist/common/list.js +0 -166
- package/dist/common/list.js.map +0 -1
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
// `Sortable`, `SortableItem`, `useSortable` and `useSortableList` — the
|
|
3
|
+
// drag-to-reorder list, which is the shape most apps actually reach for.
|
|
4
|
+
//
|
|
5
|
+
// The reorder is LIVE: crossing another row moves the dragged row into its
|
|
6
|
+
// place immediately, so the list rearranges under the drag icon. That is
|
|
7
|
+
// upstream's behaviour too — its `onMove` fires as rows cross, not at the
|
|
8
|
+
// end — minus the spring, because here the rows are laid out by Yoga rather
|
|
9
|
+
// than transformed.
|
|
10
|
+
//
|
|
11
|
+
// The component owns the order, exactly as upstream requires ("do NOT update
|
|
12
|
+
// external state in `onMove`"). An app reads the settled order from
|
|
13
|
+
// `onDrop`'s `allPositions`.
|
|
14
|
+
import { createContext, useCallback, useContext, useEffect, useMemo, useRef, useState, } from "react";
|
|
15
|
+
import { ScrollView } from "../components/scroll-view";
|
|
16
|
+
import { View } from "../components/view";
|
|
17
|
+
import { DraggableContext, DraggableHandle } from "./draggable";
|
|
18
|
+
import { DragSourceControllers, DropTargetControllers } from "./gtk-controllers";
|
|
19
|
+
import { listToObject } from "./order";
|
|
20
|
+
import { nextDraggableId } from "./payload";
|
|
21
|
+
import { ScrollDirection, SortableDirection, } from "./types";
|
|
22
|
+
// Upstream exports these from the same entry point as the components, so
|
|
23
|
+
// they are re-exported here rather than moved; the implementations live in
|
|
24
|
+
// ./order, which imports nothing from GTK.
|
|
25
|
+
export { clamp, listToObject, objectMove } from "./order";
|
|
26
|
+
const SortableContext = createContext(null);
|
|
27
|
+
const keyOf = (item, index, extractor) => extractor?.(item, index) ?? item.id;
|
|
28
|
+
/** The order state, shared by `useSortableList` and `Sortable`. */
|
|
29
|
+
const useOrder = (data, itemKeyExtractor) => {
|
|
30
|
+
// Lazy initial state, not a ref: stable for the list's lifetime, and a ref
|
|
31
|
+
// may not be read during render.
|
|
32
|
+
const [scope] = useState(() => `sortable-${nextDraggableId()}`);
|
|
33
|
+
const incoming = useMemo(() => data.map((item, index) => keyOf(item, index, itemKeyExtractor)), [data, itemKeyExtractor]);
|
|
34
|
+
const [order, setOrder] = useState(incoming);
|
|
35
|
+
// The list owns the ORDER; the app owns the SET. Adding or removing an item
|
|
36
|
+
// has to reach the order without discarding a reorder the user already
|
|
37
|
+
// made — so new ids go to the end, departed ids drop out, and a pure
|
|
38
|
+
// reorder of `data` by the app is ignored (upstream's contract too: the
|
|
39
|
+
// component owns the order).
|
|
40
|
+
//
|
|
41
|
+
// Adjusted DURING RENDER rather than in an effect. React documents this as
|
|
42
|
+
// the way to derive state from changed props, and it matters here: an
|
|
43
|
+
// effect would paint one frame in the stale order every time the app
|
|
44
|
+
// appends an item.
|
|
45
|
+
const signature = incoming.join(" ");
|
|
46
|
+
const [seenSignature, setSeenSignature] = useState(signature);
|
|
47
|
+
if (seenSignature !== signature) {
|
|
48
|
+
setSeenSignature(signature);
|
|
49
|
+
const known = new Set(incoming);
|
|
50
|
+
const next = [
|
|
51
|
+
...order.filter((id) => known.has(id)),
|
|
52
|
+
...incoming.filter((id) => !order.includes(id)),
|
|
53
|
+
];
|
|
54
|
+
if (next.length !== order.length ||
|
|
55
|
+
next.some((id, index) => id !== order[index])) {
|
|
56
|
+
setOrder(next);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
const items = useMemo(() => {
|
|
60
|
+
const byId = new Map();
|
|
61
|
+
data.forEach((item, index) => {
|
|
62
|
+
byId.set(keyOf(item, index, itemKeyExtractor), item);
|
|
63
|
+
});
|
|
64
|
+
return order
|
|
65
|
+
.map((id) => byId.get(id))
|
|
66
|
+
.filter((item) => item !== undefined);
|
|
67
|
+
}, [order, data, itemKeyExtractor]);
|
|
68
|
+
return { scope, order, setOrder, items };
|
|
69
|
+
};
|
|
70
|
+
/** The `SharedValue`-shaped boxes upstream hands to `renderItem`. Real,
|
|
71
|
+
* readable and writable here — just not animated. See `SharedValueLike`. */
|
|
72
|
+
const useSharedBoxes = (order) => {
|
|
73
|
+
// Rebuilt when the order changes rather than mutated in place. Upstream's
|
|
74
|
+
// box keeps its identity and changes its `.value`; here the identity tracks
|
|
75
|
+
// the value, which is the closest thing available without a mutable cell
|
|
76
|
+
// React allows to be written during a render. The practical difference is
|
|
77
|
+
// that an app using `positions` as an effect dependency re-runs on a
|
|
78
|
+
// reorder — which is what it wanted from a SharedValue anyway.
|
|
79
|
+
const positions = useMemo(() => ({
|
|
80
|
+
value: listToObject(order),
|
|
81
|
+
}), [order]);
|
|
82
|
+
// These three this platform never writes: there is no UI-thread scroll
|
|
83
|
+
// position, no autoscroll during a drag (see docs/research/drag-and-drop.md)
|
|
84
|
+
// and no measured row heights, because Yoga lays rows out at their natural
|
|
85
|
+
// height. Stable, so forwarding them is free.
|
|
86
|
+
const idle = useMemo(() => ({
|
|
87
|
+
scrollY: { value: 0 },
|
|
88
|
+
autoScroll: { value: ScrollDirection.None },
|
|
89
|
+
itemHeights: { value: {} },
|
|
90
|
+
}), []);
|
|
91
|
+
const itemsCount = order.length;
|
|
92
|
+
return useMemo(() => ({
|
|
93
|
+
positions,
|
|
94
|
+
...idle,
|
|
95
|
+
plumbing: () => ({
|
|
96
|
+
positions,
|
|
97
|
+
lowerBound: idle.scrollY,
|
|
98
|
+
autoScrollDirection: idle.autoScroll,
|
|
99
|
+
itemHeights: idle.itemHeights,
|
|
100
|
+
itemsCount,
|
|
101
|
+
isDynamicHeight: false,
|
|
102
|
+
}),
|
|
103
|
+
}), [positions, idle, itemsCount]);
|
|
104
|
+
};
|
|
105
|
+
/**
|
|
106
|
+
* Owns the order of a sortable list, for an app rendering its own container.
|
|
107
|
+
*
|
|
108
|
+
* **The order half only.** Upstream's rows get their drag wiring through the
|
|
109
|
+
* props `getItemProps` hands them; here it travels in context, which only
|
|
110
|
+
* `Sortable` provides — so rows still have to be inside a `Sortable`, and a
|
|
111
|
+
* `SortableItem` outside one throws rather than quietly not dragging. This
|
|
112
|
+
* hook is for reading the order and the `SharedValueLike` boxes, which is
|
|
113
|
+
* what most callers of it actually want.
|
|
114
|
+
*/
|
|
115
|
+
export const useSortableList = (options) => {
|
|
116
|
+
const { order, items } = useOrder(options.data, options.itemKeyExtractor);
|
|
117
|
+
const boxes = useSharedBoxes(order);
|
|
118
|
+
const dropProviderRef = useRef(null);
|
|
119
|
+
const noop = useCallback(() => { }, []);
|
|
120
|
+
return {
|
|
121
|
+
positions: boxes.positions,
|
|
122
|
+
scrollY: boxes.scrollY,
|
|
123
|
+
autoScroll: boxes.autoScroll,
|
|
124
|
+
itemHeights: boxes.itemHeights,
|
|
125
|
+
dropProviderRef,
|
|
126
|
+
handleScroll: noop,
|
|
127
|
+
handleScrollEnd: noop,
|
|
128
|
+
contentHeight: 0,
|
|
129
|
+
isDynamicHeight: false,
|
|
130
|
+
items,
|
|
131
|
+
getItemProps: boxes.plumbing,
|
|
132
|
+
};
|
|
133
|
+
};
|
|
134
|
+
/**
|
|
135
|
+
* A list whose rows reorder by dragging.
|
|
136
|
+
*
|
|
137
|
+
* ```tsx
|
|
138
|
+
* <Sortable
|
|
139
|
+
* data={tasks}
|
|
140
|
+
* onDrop={(id, position, all) => save(all)}
|
|
141
|
+
* renderItem={({ item, id, ...rest }) => (
|
|
142
|
+
* <SortableItem key={id} id={id} data={item} {...rest}>
|
|
143
|
+
* <View style={styles.row}>
|
|
144
|
+
* <Text>{item.title}</Text>
|
|
145
|
+
* <SortableItem.Handle>
|
|
146
|
+
* <Text>⠿</Text>
|
|
147
|
+
* </SortableItem.Handle>
|
|
148
|
+
* </View>
|
|
149
|
+
* </SortableItem>
|
|
150
|
+
* )}
|
|
151
|
+
* />
|
|
152
|
+
* ```
|
|
153
|
+
*
|
|
154
|
+
* The rows carry a real GTK drag icon — a picture of the row itself, lifted
|
|
155
|
+
* at the point it was grabbed — and the list rearranges live underneath it.
|
|
156
|
+
*/
|
|
157
|
+
export const Sortable = ({ data, renderItem, itemKeyExtractor, style, contentContainerStyle, testID, direction = SortableDirection.Vertical, onMove, onDragStart, onDrop, onDragging, }) => {
|
|
158
|
+
const { scope, order, setOrder, items } = useOrder(data, itemKeyExtractor);
|
|
159
|
+
const boxes = useSharedBoxes(order);
|
|
160
|
+
// The order as it was when the drag began, so a cancelled drag puts the
|
|
161
|
+
// list back rather than leaving it wherever the pointer happened to be.
|
|
162
|
+
const beforeDrag = useRef(null);
|
|
163
|
+
// The drag callbacks below run from GTK signals, long after any commit, so
|
|
164
|
+
// reading the order through a ref updated in an effect is both current and
|
|
165
|
+
// free of the stale-closure problem `useCallback` would otherwise have.
|
|
166
|
+
const orderRef = useRef(order);
|
|
167
|
+
useEffect(() => {
|
|
168
|
+
orderRef.current = order;
|
|
169
|
+
}, [order]);
|
|
170
|
+
const beginDrag = useCallback((draggedId) => {
|
|
171
|
+
beforeDrag.current = orderRef.current;
|
|
172
|
+
onDragStart?.(draggedId, orderRef.current.indexOf(draggedId));
|
|
173
|
+
}, [onDragStart]);
|
|
174
|
+
const moveOnto = useCallback((draggedId, targetId) => {
|
|
175
|
+
setOrder((current) => {
|
|
176
|
+
const from = current.indexOf(draggedId);
|
|
177
|
+
const to = current.indexOf(targetId);
|
|
178
|
+
if (from === -1 || to === -1 || from === to) {
|
|
179
|
+
return current;
|
|
180
|
+
}
|
|
181
|
+
const next = [...current];
|
|
182
|
+
next.splice(from, 1);
|
|
183
|
+
next.splice(to, 0, draggedId);
|
|
184
|
+
onMove?.(draggedId, from, to);
|
|
185
|
+
return next;
|
|
186
|
+
});
|
|
187
|
+
}, [setOrder, onMove]);
|
|
188
|
+
const endDrag = useCallback((draggedId, cancelled) => {
|
|
189
|
+
const restore = beforeDrag.current;
|
|
190
|
+
beforeDrag.current = null;
|
|
191
|
+
if (cancelled) {
|
|
192
|
+
if (restore) {
|
|
193
|
+
setOrder(() => restore);
|
|
194
|
+
}
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
const settled = orderRef.current;
|
|
198
|
+
onDrop?.(draggedId, settled.indexOf(draggedId), listToObject(settled));
|
|
199
|
+
}, [setOrder, onDrop]);
|
|
200
|
+
const contextValue = useMemo(() => ({ scope, moveOnto, beginDrag, endDrag, onDragging }), [scope, moveOnto, beginDrag, endDrag, onDragging]);
|
|
201
|
+
if (direction === SortableDirection.Horizontal) {
|
|
202
|
+
// Loud rather than silent: a horizontal list that lays out vertically is
|
|
203
|
+
// a bug report waiting to happen. See docs/research/drag-and-drop.md for
|
|
204
|
+
// why the horizontal surface is deferred rather than approximated.
|
|
205
|
+
throw new Error("react-native-gtkx/dnd: SortableDirection.Horizontal is not implemented on Linux");
|
|
206
|
+
}
|
|
207
|
+
return (_jsx(SortableContext.Provider, { value: contextValue, children: _jsx(ScrollView, { style: style,
|
|
208
|
+
// No `alignItems: "stretch"` override here any more. It used to be
|
|
209
|
+
// needed because this platform's ScrollView content container
|
|
210
|
+
// defaulted to `flex-start`, which shrank every row to its intrinsic
|
|
211
|
+
// width and collapsed the `flex: 1` text column inside it. That
|
|
212
|
+
// default was a parity bug — RN's content container is a plain
|
|
213
|
+
// `View`, whose default is `stretch` — and it is fixed at the source
|
|
214
|
+
// now (components/scroll-view.tsx), so a `Sortable` gets full-width
|
|
215
|
+
// rows the same way any other RN list does.
|
|
216
|
+
contentContainerStyle: contentContainerStyle, testID: testID, children: items.map((item, index) => renderItem({
|
|
217
|
+
item,
|
|
218
|
+
index,
|
|
219
|
+
id: keyOf(item, index, itemKeyExtractor),
|
|
220
|
+
direction,
|
|
221
|
+
...boxes.plumbing(),
|
|
222
|
+
})) }) }));
|
|
223
|
+
};
|
|
224
|
+
/**
|
|
225
|
+
* The reorder half of the module, as a hook, for a row that owns its own
|
|
226
|
+
* view. Render `children` inside that view.
|
|
227
|
+
*/
|
|
228
|
+
export const useSortable = (options) => {
|
|
229
|
+
const { id, onDragging } = options;
|
|
230
|
+
const list = useContext(SortableContext);
|
|
231
|
+
if (list === null) {
|
|
232
|
+
// Loudly, because a silent no-op is the worst failure mode this repo has
|
|
233
|
+
// met: docs/research/gestures.md records `Animated.View` accepting the
|
|
234
|
+
// responder props and ignoring them, which compiled, ran, and did
|
|
235
|
+
// nothing. Unlike upstream — where a row gets its wiring through the
|
|
236
|
+
// props `getItemProps` hands it — the wiring here is in context, so a row
|
|
237
|
+
// outside its list has no drag at all rather than a degraded one.
|
|
238
|
+
throw new Error("react-native-gtkx/dnd: SortableItem/useSortable must be inside a Sortable");
|
|
239
|
+
}
|
|
240
|
+
const [isMoving, setIsMoving] = useState(false);
|
|
241
|
+
const [hasHandle, setHasHandle] = useState(false);
|
|
242
|
+
const registerHandle = useCallback((registered) => {
|
|
243
|
+
setHasHandle(registered);
|
|
244
|
+
}, []);
|
|
245
|
+
// Same reasoning as draggable.tsx: a handle registers one commit late, so
|
|
246
|
+
// the row's own copy waits for it rather than briefly mounting a second
|
|
247
|
+
// drag source.
|
|
248
|
+
const [mounted, setMounted] = useState(false);
|
|
249
|
+
// eslint-disable-next-line react-hooks/set-state-in-effect
|
|
250
|
+
useEffect(() => setMounted(true), []);
|
|
251
|
+
// Same list only — and INCLUDING this row itself. Refusing the self-drop
|
|
252
|
+
// looks right and is wrong: because the reorder happens live on `::enter`,
|
|
253
|
+
// by the time the pointer settles it is over the DRAGGED row, so a row that
|
|
254
|
+
// refuses its own payload leaves GDK with no target, cancels the drag, and
|
|
255
|
+
// the list snaps back to where it started. Accepting is also the honest
|
|
256
|
+
// reading: dropping a row on its own current position means "leave it
|
|
257
|
+
// here", which is a completed drag, not an abandoned one.
|
|
258
|
+
const accepts = useCallback((payload) => payload.scope === list.scope, [list]);
|
|
259
|
+
const dragControllers = (_jsx(DragSourceControllers, { payload: { scope: list.scope, id }, onDragBegin: () => {
|
|
260
|
+
setIsMoving(true);
|
|
261
|
+
list.beginDrag(id);
|
|
262
|
+
}, onDragEnd: (dropped) => {
|
|
263
|
+
setIsMoving(false);
|
|
264
|
+
list.endDrag(id, !dropped);
|
|
265
|
+
} }));
|
|
266
|
+
const dropControllers = (_jsx(DropTargetControllers, { accepts: accepts,
|
|
267
|
+
// The reorder happens HERE, not on drop: crossing this row is what
|
|
268
|
+
// moves the dragged row into its place, so the list rearranges under
|
|
269
|
+
// the drag icon the way upstream's animated gaps do.
|
|
270
|
+
onEnter: (payload) => list.moveOnto(payload.id, id), onMotion: (payload, _x, y) => {
|
|
271
|
+
list.onDragging?.(payload.id, id, y);
|
|
272
|
+
onDragging?.(payload.id, id, y);
|
|
273
|
+
},
|
|
274
|
+
// Nothing left to do: the order is already right, and `drag-end` on the
|
|
275
|
+
// source is what settles it.
|
|
276
|
+
onDrop: () => { } }));
|
|
277
|
+
return {
|
|
278
|
+
animatedStyle: undefined,
|
|
279
|
+
isMoving,
|
|
280
|
+
hasHandle,
|
|
281
|
+
registerHandle,
|
|
282
|
+
dragControllers,
|
|
283
|
+
children: (_jsxs(_Fragment, { children: [mounted && !hasHandle ? dragControllers : null, dropControllers] })),
|
|
284
|
+
};
|
|
285
|
+
};
|
|
286
|
+
/** One row of a {@link Sortable}. Both a drag source and a drop target: the
|
|
287
|
+
* row you drag onto is the position the dragged row takes. */
|
|
288
|
+
export const SortableItem = ({ id, data, children, style, animatedStyle, testID, onMove, onDragStart, onDrop, onDragging, }) => {
|
|
289
|
+
const sortable = useSortable({
|
|
290
|
+
id,
|
|
291
|
+
data,
|
|
292
|
+
onMove,
|
|
293
|
+
onDragStart,
|
|
294
|
+
onDrop,
|
|
295
|
+
onDragging,
|
|
296
|
+
});
|
|
297
|
+
// `SortableItem.Handle` is `Draggable.Handle`: the same context carries the
|
|
298
|
+
// drag source to whichever view claims it, so one implementation serves
|
|
299
|
+
// both components.
|
|
300
|
+
const handleContext = useMemo(() => ({
|
|
301
|
+
source: sortable.dragControllers,
|
|
302
|
+
registerHandle: sortable.registerHandle,
|
|
303
|
+
}), [sortable.dragControllers, sortable.registerHandle]);
|
|
304
|
+
return (_jsx(View, { style: animatedStyle ? [style, animatedStyle] : style, testID: testID, children: _jsxs(DraggableContext.Provider, { value: handleContext, children: [sortable.children, children] }) }));
|
|
305
|
+
};
|
|
306
|
+
SortableItem.Handle = DraggableHandle;
|
|
307
|
+
//# sourceMappingURL=sortable.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sortable.js","sourceRoot":"","sources":["../../src/dnd/sortable.tsx"],"names":[],"mappings":"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","sourcesContent":["// `Sortable`, `SortableItem`, `useSortable` and `useSortableList` — the\n// drag-to-reorder list, which is the shape most apps actually reach for.\n//\n// The reorder is LIVE: crossing another row moves the dragged row into its\n// place immediately, so the list rearranges under the drag icon. That is\n// upstream's behaviour too — its `onMove` fires as rows cross, not at the\n// end — minus the spring, because here the rows are laid out by Yoga rather\n// than transformed.\n//\n// The component owns the order, exactly as upstream requires (\"do NOT update\n// external state in `onMove`\"). An app reads the settled order from\n// `onDrop`'s `allPositions`.\nimport {\n createContext,\n useCallback,\n useContext,\n useEffect,\n useMemo,\n useRef,\n useState,\n type ReactNode,\n} from \"react\"\nimport { ScrollView } from \"../components/scroll-view\"\nimport { View } from \"../components/view\"\nimport { DraggableContext, DraggableHandle } from \"./draggable\"\nimport { DragSourceControllers, DropTargetControllers } from \"./gtk-controllers\"\nimport { listToObject } from \"./order\"\nimport { nextDraggableId, type DragPayload } from \"./payload\"\nimport {\n ScrollDirection,\n SortableDirection,\n type SharedValueLike,\n type SortableData,\n type SortableItemPlumbing,\n type SortableItemProps,\n type SortableProps,\n type UseSortableListOptions,\n type UseSortableListReturn,\n type UseSortableOptions,\n type UseSortableReturn,\n} from \"./types\"\n\n// Upstream exports these from the same entry point as the components, so\n// they are re-exported here rather than moved; the implementations live in\n// ./order, which imports nothing from GTK.\nexport { clamp, listToObject, objectMove } from \"./order\"\n\n// --- the list ---------------------------------------------------------------\n\ntype SortableContextValue = {\n /** Namespaces this list's payloads, so dragging a row does not light up\n * every `Droppable` on the screen. */\n scope: string\n /** Called by a row when the dragged row crosses it. */\n moveOnto: (draggedId: string, targetId: string) => void\n beginDrag: (draggedId: string) => void\n endDrag: (draggedId: string, cancelled: boolean) => void\n onDragging?: (id: string, overItemId: string | null, y: number) => void\n}\n\nconst SortableContext = createContext<SortableContextValue | null>(null)\n\nconst keyOf = <TData extends SortableData>(\n item: TData,\n index: number,\n extractor?: (item: TData, index: number) => string,\n): string => extractor?.(item, index) ?? item.id\n\n/** The order state, shared by `useSortableList` and `Sortable`. */\nconst useOrder = <TData extends SortableData>(\n data: TData[],\n itemKeyExtractor?: (item: TData, index: number) => string,\n) => {\n // Lazy initial state, not a ref: stable for the list's lifetime, and a ref\n // may not be read during render.\n const [scope] = useState(() => `sortable-${nextDraggableId()}`)\n\n const incoming = useMemo(\n () => data.map((item, index) => keyOf(item, index, itemKeyExtractor)),\n [data, itemKeyExtractor],\n )\n const [order, setOrder] = useState<string[]>(incoming)\n\n // The list owns the ORDER; the app owns the SET. Adding or removing an item\n // has to reach the order without discarding a reorder the user already\n // made — so new ids go to the end, departed ids drop out, and a pure\n // reorder of `data` by the app is ignored (upstream's contract too: the\n // component owns the order).\n //\n // Adjusted DURING RENDER rather than in an effect. React documents this as\n // the way to derive state from changed props, and it matters here: an\n // effect would paint one frame in the stale order every time the app\n // appends an item.\n const signature = incoming.join(\" \")\n const [seenSignature, setSeenSignature] = useState(signature)\n if (seenSignature !== signature) {\n setSeenSignature(signature)\n const known = new Set(incoming)\n const next = [\n ...order.filter((id) => known.has(id)),\n ...incoming.filter((id) => !order.includes(id)),\n ]\n if (\n next.length !== order.length ||\n next.some((id, index) => id !== order[index])\n ) {\n setOrder(next)\n }\n }\n\n const items = useMemo(() => {\n const byId = new Map<string, TData>()\n data.forEach((item, index) => {\n byId.set(keyOf(item, index, itemKeyExtractor), item)\n })\n return order\n .map((id) => byId.get(id))\n .filter((item): item is TData => item !== undefined)\n }, [order, data, itemKeyExtractor])\n\n return { scope, order, setOrder, items }\n}\n\n/** The `SharedValue`-shaped boxes upstream hands to `renderItem`. Real,\n * readable and writable here — just not animated. See `SharedValueLike`. */\nconst useSharedBoxes = (order: string[]) => {\n // Rebuilt when the order changes rather than mutated in place. Upstream's\n // box keeps its identity and changes its `.value`; here the identity tracks\n // the value, which is the closest thing available without a mutable cell\n // React allows to be written during a render. The practical difference is\n // that an app using `positions` as an effect dependency re-runs on a\n // reorder — which is what it wanted from a SharedValue anyway.\n const positions = useMemo(\n (): SharedValueLike<Record<string, number>> => ({\n value: listToObject(order),\n }),\n [order],\n )\n // These three this platform never writes: there is no UI-thread scroll\n // position, no autoscroll during a drag (see docs/research/drag-and-drop.md)\n // and no measured row heights, because Yoga lays rows out at their natural\n // height. Stable, so forwarding them is free.\n const idle = useMemo(\n () => ({\n scrollY: { value: 0 },\n autoScroll: { value: ScrollDirection.None as ScrollDirection },\n itemHeights: { value: {} as Record<string, number> },\n }),\n [],\n )\n\n const itemsCount = order.length\n\n return useMemo(\n () => ({\n positions,\n ...idle,\n plumbing: (): SortableItemPlumbing => ({\n positions,\n lowerBound: idle.scrollY,\n autoScrollDirection: idle.autoScroll,\n itemHeights: idle.itemHeights,\n itemsCount,\n isDynamicHeight: false,\n }),\n }),\n [positions, idle, itemsCount],\n )\n}\n\n/**\n * Owns the order of a sortable list, for an app rendering its own container.\n *\n * **The order half only.** Upstream's rows get their drag wiring through the\n * props `getItemProps` hands them; here it travels in context, which only\n * `Sortable` provides — so rows still have to be inside a `Sortable`, and a\n * `SortableItem` outside one throws rather than quietly not dragging. This\n * hook is for reading the order and the `SharedValueLike` boxes, which is\n * what most callers of it actually want.\n */\nexport const useSortableList = <TData extends SortableData>(\n options: UseSortableListOptions<TData>,\n): UseSortableListReturn<TData> => {\n const { order, items } = useOrder(options.data, options.itemKeyExtractor)\n const boxes = useSharedBoxes(order)\n const dropProviderRef = useRef(null)\n const noop = useCallback(() => {}, [])\n\n return {\n positions: boxes.positions,\n scrollY: boxes.scrollY,\n autoScroll: boxes.autoScroll,\n itemHeights: boxes.itemHeights,\n dropProviderRef,\n handleScroll: noop,\n handleScrollEnd: noop,\n contentHeight: 0,\n isDynamicHeight: false,\n items,\n getItemProps: boxes.plumbing,\n }\n}\n\n/**\n * A list whose rows reorder by dragging.\n *\n * ```tsx\n * <Sortable\n * data={tasks}\n * onDrop={(id, position, all) => save(all)}\n * renderItem={({ item, id, ...rest }) => (\n * <SortableItem key={id} id={id} data={item} {...rest}>\n * <View style={styles.row}>\n * <Text>{item.title}</Text>\n * <SortableItem.Handle>\n * <Text>⠿</Text>\n * </SortableItem.Handle>\n * </View>\n * </SortableItem>\n * )}\n * />\n * ```\n *\n * The rows carry a real GTK drag icon — a picture of the row itself, lifted\n * at the point it was grabbed — and the list rearranges live underneath it.\n */\nexport const Sortable = <TData extends SortableData>({\n data,\n renderItem,\n itemKeyExtractor,\n style,\n contentContainerStyle,\n testID,\n direction = SortableDirection.Vertical,\n onMove,\n onDragStart,\n onDrop,\n onDragging,\n}: SortableProps<TData>): ReactNode => {\n const { scope, order, setOrder, items } = useOrder(data, itemKeyExtractor)\n const boxes = useSharedBoxes(order)\n\n // The order as it was when the drag began, so a cancelled drag puts the\n // list back rather than leaving it wherever the pointer happened to be.\n const beforeDrag = useRef<string[] | null>(null)\n // The drag callbacks below run from GTK signals, long after any commit, so\n // reading the order through a ref updated in an effect is both current and\n // free of the stale-closure problem `useCallback` would otherwise have.\n const orderRef = useRef(order)\n useEffect(() => {\n orderRef.current = order\n }, [order])\n\n const beginDrag = useCallback(\n (draggedId: string) => {\n beforeDrag.current = orderRef.current\n onDragStart?.(draggedId, orderRef.current.indexOf(draggedId))\n },\n [onDragStart],\n )\n\n const moveOnto = useCallback(\n (draggedId: string, targetId: string) => {\n setOrder((current) => {\n const from = current.indexOf(draggedId)\n const to = current.indexOf(targetId)\n if (from === -1 || to === -1 || from === to) {\n return current\n }\n const next = [...current]\n next.splice(from, 1)\n next.splice(to, 0, draggedId)\n onMove?.(draggedId, from, to)\n return next\n })\n },\n [setOrder, onMove],\n )\n\n const endDrag = useCallback(\n (draggedId: string, cancelled: boolean) => {\n const restore = beforeDrag.current\n beforeDrag.current = null\n if (cancelled) {\n if (restore) {\n setOrder(() => restore)\n }\n return\n }\n const settled = orderRef.current\n onDrop?.(draggedId, settled.indexOf(draggedId), listToObject(settled))\n },\n [setOrder, onDrop],\n )\n\n const contextValue = useMemo<SortableContextValue>(\n () => ({ scope, moveOnto, beginDrag, endDrag, onDragging }),\n [scope, moveOnto, beginDrag, endDrag, onDragging],\n )\n\n if (direction === SortableDirection.Horizontal) {\n // Loud rather than silent: a horizontal list that lays out vertically is\n // a bug report waiting to happen. See docs/research/drag-and-drop.md for\n // why the horizontal surface is deferred rather than approximated.\n throw new Error(\n \"react-native-gtkx/dnd: SortableDirection.Horizontal is not implemented on Linux\",\n )\n }\n\n return (\n <SortableContext.Provider value={contextValue}>\n <ScrollView\n style={style}\n // No `alignItems: \"stretch\"` override here any more. It used to be\n // needed because this platform's ScrollView content container\n // defaulted to `flex-start`, which shrank every row to its intrinsic\n // width and collapsed the `flex: 1` text column inside it. That\n // default was a parity bug — RN's content container is a plain\n // `View`, whose default is `stretch` — and it is fixed at the source\n // now (components/scroll-view.tsx), so a `Sortable` gets full-width\n // rows the same way any other RN list does.\n contentContainerStyle={contentContainerStyle}\n testID={testID}\n >\n {items.map((item, index) =>\n renderItem({\n item,\n index,\n id: keyOf(item, index, itemKeyExtractor),\n direction,\n ...boxes.plumbing(),\n }),\n )}\n </ScrollView>\n </SortableContext.Provider>\n )\n}\n\n/**\n * The reorder half of the module, as a hook, for a row that owns its own\n * view. Render `children` inside that view.\n */\nexport const useSortable = <TData,>(\n options: UseSortableOptions<TData>,\n): UseSortableReturn => {\n const { id, onDragging } = options\n const list = useContext(SortableContext)\n if (list === null) {\n // Loudly, because a silent no-op is the worst failure mode this repo has\n // met: docs/research/gestures.md records `Animated.View` accepting the\n // responder props and ignoring them, which compiled, ran, and did\n // nothing. Unlike upstream — where a row gets its wiring through the\n // props `getItemProps` hands it — the wiring here is in context, so a row\n // outside its list has no drag at all rather than a degraded one.\n throw new Error(\n \"react-native-gtkx/dnd: SortableItem/useSortable must be inside a Sortable\",\n )\n }\n const [isMoving, setIsMoving] = useState(false)\n const [hasHandle, setHasHandle] = useState(false)\n\n const registerHandle = useCallback((registered: boolean) => {\n setHasHandle(registered)\n }, [])\n\n // Same reasoning as draggable.tsx: a handle registers one commit late, so\n // the row's own copy waits for it rather than briefly mounting a second\n // drag source.\n const [mounted, setMounted] = useState(false)\n // eslint-disable-next-line react-hooks/set-state-in-effect\n useEffect(() => setMounted(true), [])\n\n // Same list only — and INCLUDING this row itself. Refusing the self-drop\n // looks right and is wrong: because the reorder happens live on `::enter`,\n // by the time the pointer settles it is over the DRAGGED row, so a row that\n // refuses its own payload leaves GDK with no target, cancels the drag, and\n // the list snaps back to where it started. Accepting is also the honest\n // reading: dropping a row on its own current position means \"leave it\n // here\", which is a completed drag, not an abandoned one.\n const accepts = useCallback(\n (payload: DragPayload) => payload.scope === list.scope,\n [list],\n )\n\n const dragControllers = (\n <DragSourceControllers\n payload={{ scope: list.scope, id }}\n onDragBegin={() => {\n setIsMoving(true)\n list.beginDrag(id)\n }}\n onDragEnd={(dropped) => {\n setIsMoving(false)\n list.endDrag(id, !dropped)\n }}\n />\n )\n\n const dropControllers = (\n <DropTargetControllers\n accepts={accepts}\n // The reorder happens HERE, not on drop: crossing this row is what\n // moves the dragged row into its place, so the list rearranges under\n // the drag icon the way upstream's animated gaps do.\n onEnter={(payload) => list.moveOnto(payload.id, id)}\n onMotion={(payload, _x, y) => {\n list.onDragging?.(payload.id, id, y)\n onDragging?.(payload.id, id, y)\n }}\n // Nothing left to do: the order is already right, and `drag-end` on the\n // source is what settles it.\n onDrop={() => {}}\n />\n )\n\n return {\n animatedStyle: undefined,\n isMoving,\n hasHandle,\n registerHandle,\n dragControllers,\n children: (\n <>\n {mounted && !hasHandle ? dragControllers : null}\n {dropControllers}\n </>\n ),\n }\n}\n\n/** One row of a {@link Sortable}. Both a drag source and a drop target: the\n * row you drag onto is the position the dragged row takes. */\nexport const SortableItem = <TData,>({\n id,\n data,\n children,\n style,\n animatedStyle,\n testID,\n onMove,\n onDragStart,\n onDrop,\n onDragging,\n}: SortableItemProps<TData>): ReactNode => {\n const sortable = useSortable<TData>({\n id,\n data,\n onMove,\n onDragStart,\n onDrop,\n onDragging,\n } as UseSortableOptions<TData>)\n\n // `SortableItem.Handle` is `Draggable.Handle`: the same context carries the\n // drag source to whichever view claims it, so one implementation serves\n // both components.\n const handleContext = useMemo(\n () => ({\n source: sortable.dragControllers,\n registerHandle: sortable.registerHandle,\n }),\n [sortable.dragControllers, sortable.registerHandle],\n )\n\n return (\n <View\n style={animatedStyle ? [style, animatedStyle] : style}\n testID={testID}\n >\n <DraggableContext.Provider value={handleContext}>\n {sortable.children}\n {children}\n </DraggableContext.Provider>\n </View>\n )\n}\n\nSortableItem.Handle = DraggableHandle\n"]}
|
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
import type { ReactNode, RefObject } from "react";
|
|
2
|
+
import type { StyleProp } from "../contracts";
|
|
3
|
+
/**
|
|
4
|
+
* What `SharedValue<T>` degrades to here.
|
|
5
|
+
*
|
|
6
|
+
* Reanimated's `SharedValue<T>` is `{ value: T }` plus the worklet plumbing
|
|
7
|
+
* that makes writes cross to the UI thread. There is no UI thread on this
|
|
8
|
+
* platform, so this is the same box without the crossing: **reads and
|
|
9
|
+
* writes both work**, they just do not drive an animation.
|
|
10
|
+
*
|
|
11
|
+
* That matters more than it looks. `positions`, `lowerBound`,
|
|
12
|
+
* `autoScrollDirection` and `itemHeights` appear in
|
|
13
|
+
* `SortableRenderItemProps` — public API an app forwards with `{...rest}`
|
|
14
|
+
* and, occasionally, reads. Typing them as an opaque token would break the
|
|
15
|
+
* reads; typing them as this keeps them working.
|
|
16
|
+
*/
|
|
17
|
+
export type SharedValueLike<T> = {
|
|
18
|
+
value: T;
|
|
19
|
+
};
|
|
20
|
+
export declare enum DraggableState {
|
|
21
|
+
IDLE = "IDLE",
|
|
22
|
+
DRAGGING = "DRAGGING",
|
|
23
|
+
DROPPED = "DROPPED"
|
|
24
|
+
}
|
|
25
|
+
export type AnimationFunction = (toValue: number) => number;
|
|
26
|
+
export type CollisionAlgorithm = "center" | "intersect" | "contain";
|
|
27
|
+
export interface UseDraggableOptions<TData = unknown> {
|
|
28
|
+
data: TData;
|
|
29
|
+
draggableId?: string;
|
|
30
|
+
dragDisabled?: boolean;
|
|
31
|
+
onDragStart?: (data: TData) => void;
|
|
32
|
+
onDragEnd?: (data: TData) => void;
|
|
33
|
+
onDragging?: (payload: {
|
|
34
|
+
x: number;
|
|
35
|
+
y: number;
|
|
36
|
+
tx: number;
|
|
37
|
+
ty: number;
|
|
38
|
+
itemData: TData;
|
|
39
|
+
}) => void;
|
|
40
|
+
onStateChange?: (state: DraggableState) => void;
|
|
41
|
+
/**
|
|
42
|
+
* Accepted and ignored. Upstream uses it to tell a tap from a drag; GDK
|
|
43
|
+
* already does that with `gtk-dnd-drag-threshold` before it starts a drag
|
|
44
|
+
* at all, so honouring it would only make dragging feel slower.
|
|
45
|
+
*/
|
|
46
|
+
preDragDelay?: number;
|
|
47
|
+
/**
|
|
48
|
+
* Accepted and ignored. GDK hit-tests the pointer against the real widget
|
|
49
|
+
* tree; of the three upstream algorithms `"center"` is closest, since the
|
|
50
|
+
* drag icon is carried at the point it was grabbed.
|
|
51
|
+
*/
|
|
52
|
+
collisionAlgorithm?: CollisionAlgorithm;
|
|
53
|
+
/** Unsupported: the compositor carries the drag icon and nothing here can
|
|
54
|
+
* constrain its path. Kept in the type so a shared file still compiles
|
|
55
|
+
* for iOS and Android. */
|
|
56
|
+
dragAxis?: "x" | "y" | "both";
|
|
57
|
+
/** Unsupported, same reason as `dragAxis`. */
|
|
58
|
+
dragBoundsRef?: RefObject<unknown>;
|
|
59
|
+
/** Unsupported: there is no return animation, because the view never
|
|
60
|
+
* left — GDK drew a picture of it instead. */
|
|
61
|
+
animationFunction?: AnimationFunction;
|
|
62
|
+
}
|
|
63
|
+
export interface UseDraggableReturn {
|
|
64
|
+
/** Spread onto the view that should be draggable. Upstream's name for it
|
|
65
|
+
* is `animatedViewProps`; the props are a `style` and the drag
|
|
66
|
+
* controllers, which on this platform are React children rather than a
|
|
67
|
+
* style. */
|
|
68
|
+
animatedViewProps: {
|
|
69
|
+
style?: StyleProp;
|
|
70
|
+
children: ReactNode;
|
|
71
|
+
};
|
|
72
|
+
state: DraggableState;
|
|
73
|
+
hasHandle: boolean;
|
|
74
|
+
registerHandle: (registered: boolean) => void;
|
|
75
|
+
}
|
|
76
|
+
export interface DraggableProps<TData = unknown> extends UseDraggableOptions<TData> {
|
|
77
|
+
style?: StyleProp;
|
|
78
|
+
children: ReactNode;
|
|
79
|
+
testID?: string;
|
|
80
|
+
}
|
|
81
|
+
export interface DraggableHandleProps {
|
|
82
|
+
children: ReactNode;
|
|
83
|
+
style?: StyleProp;
|
|
84
|
+
testID?: string;
|
|
85
|
+
}
|
|
86
|
+
export type DropAlignment = "center" | "top-left" | "top-center" | "top-right" | "center-left" | "center-right" | "bottom-left" | "bottom-center" | "bottom-right";
|
|
87
|
+
export interface DropOffset {
|
|
88
|
+
x: number;
|
|
89
|
+
y: number;
|
|
90
|
+
}
|
|
91
|
+
export interface UseDroppableOptions<TData = unknown> {
|
|
92
|
+
onDrop: (data: TData) => void;
|
|
93
|
+
dropDisabled?: boolean;
|
|
94
|
+
onActiveChange?: (isActive: boolean) => void;
|
|
95
|
+
activeStyle?: StyleProp;
|
|
96
|
+
droppableId?: string;
|
|
97
|
+
/** How many draggables this zone accepts. When it is full GDK is told to
|
|
98
|
+
* REFUSE the drop, so the pointer shows the no-drop cursor — upstream
|
|
99
|
+
* silently skips the slot instead. */
|
|
100
|
+
capacity?: number;
|
|
101
|
+
/** Unsupported: both of these place the dragged view inside the zone
|
|
102
|
+
* after the drop, and the view never moved. */
|
|
103
|
+
dropAlignment?: DropAlignment;
|
|
104
|
+
dropOffset?: DropOffset;
|
|
105
|
+
}
|
|
106
|
+
export interface UseDroppableReturn {
|
|
107
|
+
viewProps: {
|
|
108
|
+
style?: StyleProp;
|
|
109
|
+
children: ReactNode;
|
|
110
|
+
};
|
|
111
|
+
isActive: boolean;
|
|
112
|
+
activeStyle?: StyleProp;
|
|
113
|
+
}
|
|
114
|
+
export interface DroppableProps<TData = unknown> extends UseDroppableOptions<TData> {
|
|
115
|
+
style?: StyleProp;
|
|
116
|
+
children: ReactNode;
|
|
117
|
+
testID?: string;
|
|
118
|
+
}
|
|
119
|
+
export interface DroppedItemsMap<TData = unknown> {
|
|
120
|
+
[draggableId: string]: {
|
|
121
|
+
droppableId: string;
|
|
122
|
+
data: TData;
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
export interface DropProviderProps {
|
|
126
|
+
children: ReactNode;
|
|
127
|
+
onDroppedItemsUpdate?: (droppedItems: DroppedItemsMap) => void;
|
|
128
|
+
onDragStart?: (data: unknown) => void;
|
|
129
|
+
onDragEnd?: (data: unknown) => void;
|
|
130
|
+
onDragging?: (payload: {
|
|
131
|
+
x: number;
|
|
132
|
+
y: number;
|
|
133
|
+
tx: number;
|
|
134
|
+
ty: number;
|
|
135
|
+
itemData: unknown;
|
|
136
|
+
}) => void;
|
|
137
|
+
/** Accepted and ignored: there is no layout pass to complete, because
|
|
138
|
+
* nothing caches a slot rectangle. See `requestPositionUpdate`. */
|
|
139
|
+
onLayoutUpdateComplete?: () => void;
|
|
140
|
+
style?: StyleProp;
|
|
141
|
+
testID?: string;
|
|
142
|
+
ref?: RefObject<DropProviderRef | null>;
|
|
143
|
+
}
|
|
144
|
+
export interface DropProviderRef {
|
|
145
|
+
/**
|
|
146
|
+
* A no-op **because GDK re-hit-tests on every motion**. Upstream needs it
|
|
147
|
+
* to refresh cached slot rectangles after a scroll or a relayout; there
|
|
148
|
+
* are no cached rectangles here, so calling it is harmless and
|
|
149
|
+
* unnecessary. Kept so an app that calls it after a list update compiles.
|
|
150
|
+
*/
|
|
151
|
+
requestPositionUpdate: () => void;
|
|
152
|
+
getDroppedItems: () => DroppedItemsMap;
|
|
153
|
+
}
|
|
154
|
+
export interface SortableData {
|
|
155
|
+
id: string;
|
|
156
|
+
}
|
|
157
|
+
export declare enum ScrollDirection {
|
|
158
|
+
None = "none",
|
|
159
|
+
Up = "up",
|
|
160
|
+
Down = "down"
|
|
161
|
+
}
|
|
162
|
+
export declare enum HorizontalScrollDirection {
|
|
163
|
+
None = "none",
|
|
164
|
+
Left = "left",
|
|
165
|
+
Right = "right"
|
|
166
|
+
}
|
|
167
|
+
export declare enum SortableDirection {
|
|
168
|
+
Vertical = "vertical",
|
|
169
|
+
Horizontal = "horizontal"
|
|
170
|
+
}
|
|
171
|
+
/** The plumbing `Sortable` hands `renderItem` and `renderItem` hands
|
|
172
|
+
* `SortableItem` — opaque in upstream's own examples, which destructure
|
|
173
|
+
* `item`/`index`/`id` and spread the rest. */
|
|
174
|
+
export interface SortableItemPlumbing {
|
|
175
|
+
positions: SharedValueLike<Record<string, number>>;
|
|
176
|
+
lowerBound: SharedValueLike<number>;
|
|
177
|
+
autoScrollDirection: SharedValueLike<ScrollDirection>;
|
|
178
|
+
itemHeights: SharedValueLike<Record<string, number>>;
|
|
179
|
+
itemsCount: number;
|
|
180
|
+
/** Accepted and ignored: Yoga lays rows out at their natural height, so
|
|
181
|
+
* a height hint has nothing to correct. */
|
|
182
|
+
itemHeight?: number;
|
|
183
|
+
/** Accepted and ignored, same reason. */
|
|
184
|
+
estimatedItemHeight?: number;
|
|
185
|
+
/**
|
|
186
|
+
* Accepted and ignored. Upstream needs the container's height to know
|
|
187
|
+
* when a dragged row has reached an edge and autoscroll should start;
|
|
188
|
+
* there is no autoscroll here (docs/research/drag-and-drop.md), and the
|
|
189
|
+
* rows are in flow layout inside a real `ScrollView` that already knows
|
|
190
|
+
* its own size.
|
|
191
|
+
*
|
|
192
|
+
* Kept because upstream's own example app passes it on every
|
|
193
|
+
* `SortableItem` — a mirror that drops it turns a literal port into an
|
|
194
|
+
* edit, which is the one thing this module exists to avoid.
|
|
195
|
+
*/
|
|
196
|
+
containerHeight?: number;
|
|
197
|
+
/** Accepted and ignored, same reason. */
|
|
198
|
+
isDynamicHeight?: boolean;
|
|
199
|
+
scheduleHeightUpdate?: (id: string, height: number) => void;
|
|
200
|
+
}
|
|
201
|
+
export interface SortableCallbacks {
|
|
202
|
+
onMove?: (id: string, from: number, to: number) => void;
|
|
203
|
+
onDragStart?: (id: string, position: number) => void;
|
|
204
|
+
onDrop?: (id: string, position: number, allPositions?: Record<string, number>) => void;
|
|
205
|
+
onDragging?: (id: string, overItemId: string | null, yPosition: number) => void;
|
|
206
|
+
}
|
|
207
|
+
export interface UseSortableOptions<TData = unknown> extends SortableCallbacks, SortableItemPlumbing {
|
|
208
|
+
id: string;
|
|
209
|
+
data?: TData;
|
|
210
|
+
}
|
|
211
|
+
export interface UseSortableReturn {
|
|
212
|
+
/** Upstream returns a Reanimated style here. This platform reorders by
|
|
213
|
+
* re-rendering, so the row needs no transform — the field stays for
|
|
214
|
+
* spread compatibility. */
|
|
215
|
+
animatedStyle: StyleProp;
|
|
216
|
+
isMoving: boolean;
|
|
217
|
+
hasHandle: boolean;
|
|
218
|
+
registerHandle: (registered: boolean) => void;
|
|
219
|
+
/** Everything the row's view must contain: the drop target always, and the
|
|
220
|
+
* drag source unless a handle has claimed it. */
|
|
221
|
+
children: ReactNode;
|
|
222
|
+
/** The drag source on its own, for a caller placing its own handle. */
|
|
223
|
+
dragControllers: ReactNode;
|
|
224
|
+
}
|
|
225
|
+
export interface SortableItemProps<TData = unknown> extends Partial<SortableItemPlumbing>, SortableCallbacks {
|
|
226
|
+
id: string;
|
|
227
|
+
data?: TData;
|
|
228
|
+
children: ReactNode;
|
|
229
|
+
style?: StyleProp;
|
|
230
|
+
/** Accepted for spread compatibility; upstream's own transform style. */
|
|
231
|
+
animatedStyle?: StyleProp;
|
|
232
|
+
direction?: SortableDirection;
|
|
233
|
+
testID?: string;
|
|
234
|
+
}
|
|
235
|
+
export interface SortableRenderItemProps<TData extends SortableData> extends SortableItemPlumbing {
|
|
236
|
+
item: TData;
|
|
237
|
+
index: number;
|
|
238
|
+
id: string;
|
|
239
|
+
direction?: SortableDirection;
|
|
240
|
+
}
|
|
241
|
+
export interface SortableProps<TData extends SortableData> extends SortableCallbacks {
|
|
242
|
+
data: TData[];
|
|
243
|
+
renderItem: (props: SortableRenderItemProps<TData>) => ReactNode;
|
|
244
|
+
itemKeyExtractor?: (item: TData, index: number) => string;
|
|
245
|
+
style?: StyleProp;
|
|
246
|
+
contentContainerStyle?: StyleProp;
|
|
247
|
+
testID?: string;
|
|
248
|
+
/** Accepted and ignored — see `SortableItemPlumbing`. */
|
|
249
|
+
itemHeight?: number | number[] | ((item: TData, index: number) => number);
|
|
250
|
+
/** Accepted and ignored. */
|
|
251
|
+
estimatedItemHeight?: number;
|
|
252
|
+
/** Accepted and ignored. */
|
|
253
|
+
enableDynamicHeights?: boolean;
|
|
254
|
+
/** Accepted and ignored. */
|
|
255
|
+
useFlatList?: boolean;
|
|
256
|
+
/** Accepted and ignored. */
|
|
257
|
+
onHeightsMeasured?: (heights: Record<string, number>) => void;
|
|
258
|
+
/** `SortableDirection.Horizontal` is not implemented — see
|
|
259
|
+
* docs/research/drag-and-drop.md. Passing it throws, rather than
|
|
260
|
+
* silently laying out vertically. */
|
|
261
|
+
direction?: SortableDirection;
|
|
262
|
+
}
|
|
263
|
+
export interface SortableHandleProps {
|
|
264
|
+
children: ReactNode;
|
|
265
|
+
style?: StyleProp;
|
|
266
|
+
testID?: string;
|
|
267
|
+
}
|
|
268
|
+
export interface UseSortableListOptions<TData extends SortableData> {
|
|
269
|
+
data: TData[];
|
|
270
|
+
itemKeyExtractor?: (item: TData, index: number) => string;
|
|
271
|
+
itemHeight?: number | number[] | ((item: TData, index: number) => number);
|
|
272
|
+
enableDynamicHeights?: boolean;
|
|
273
|
+
estimatedItemHeight?: number;
|
|
274
|
+
onHeightsMeasured?: (heights: Record<string, number>) => void;
|
|
275
|
+
}
|
|
276
|
+
export interface UseSortableListReturn<TData extends SortableData> {
|
|
277
|
+
positions: SharedValueLike<Record<string, number>>;
|
|
278
|
+
scrollY: SharedValueLike<number>;
|
|
279
|
+
autoScroll: SharedValueLike<ScrollDirection>;
|
|
280
|
+
itemHeights: SharedValueLike<Record<string, number>>;
|
|
281
|
+
dropProviderRef: RefObject<DropProviderRef | null>;
|
|
282
|
+
handleScroll: () => void;
|
|
283
|
+
handleScrollEnd: () => void;
|
|
284
|
+
contentHeight: number;
|
|
285
|
+
isDynamicHeight: boolean;
|
|
286
|
+
/** The current order, which this hook owns — upstream keeps the same
|
|
287
|
+
* contract ("do NOT update external state in `onMove`"). */
|
|
288
|
+
items: TData[];
|
|
289
|
+
getItemProps: (item: TData, index: number) => SortableItemPlumbing;
|
|
290
|
+
}
|