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
package/dist/contracts.d.ts
CHANGED
|
@@ -115,9 +115,13 @@ export type VisualStyle = Partial<{
|
|
|
115
115
|
export type PointerEventsValue = "auto" | "none" | "box-none" | "box-only";
|
|
116
116
|
export type BehavioralStyle = Partial<{
|
|
117
117
|
pointerEvents: PointerEventsValue;
|
|
118
|
+
zIndex: number;
|
|
118
119
|
}>;
|
|
119
120
|
export type FlatStyle = LayoutStyle & VisualStyle & BehavioralStyle;
|
|
120
121
|
export type StyleProp<T = FlatStyle> = T | null | undefined | false | ReadonlyArray<StyleProp<T>>;
|
|
122
|
+
export type ViewStyle = FlatStyle;
|
|
123
|
+
export type TextStyle = FlatStyle;
|
|
124
|
+
export type ImageStyle = FlatStyle;
|
|
121
125
|
export type SplitStyle = {
|
|
122
126
|
layout: LayoutStyle;
|
|
123
127
|
visual: VisualStyle;
|
package/dist/contracts.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"contracts.js","sourceRoot":"","sources":["../src/contracts.ts"],"names":[],"mappings":"AAAA,wEAAwE;AACxE,sEAAsE","sourcesContent":["// Shared contracts between the layout engine, the style system, the API\n// modules and the components — the one module every layer may import.\n\n// --- style ------------------------------------------------------------------\n\nexport type DimensionValue = number | `${number}%` | \"auto\"\n\nexport type FlexAlignType =\n \"flex-start\" | \"flex-end\" | \"center\" | \"stretch\" | \"baseline\"\n\n// Layout-affecting props: classified by the style system, consumed by the\n// layout engine, which maps them onto Yoga node setters.\nexport type LayoutStyle = Partial<{\n alignContent:\n | \"flex-start\"\n | \"flex-end\"\n | \"center\"\n | \"stretch\"\n | \"space-between\"\n | \"space-around\"\n alignItems: FlexAlignType\n alignSelf: \"auto\" | FlexAlignType\n aspectRatio: number | string\n bottom: DimensionValue\n columnGap: number\n direction: \"inherit\" | \"ltr\" | \"rtl\"\n display: \"none\" | \"flex\"\n flex: number\n flexBasis: DimensionValue\n flexDirection: \"row\" | \"row-reverse\" | \"column\" | \"column-reverse\"\n flexGrow: number\n flexShrink: number\n flexWrap: \"wrap\" | \"nowrap\" | \"wrap-reverse\"\n gap: number\n height: DimensionValue\n justifyContent:\n | \"flex-start\"\n | \"flex-end\"\n | \"center\"\n | \"space-between\"\n | \"space-around\"\n | \"space-evenly\"\n left: DimensionValue\n margin: DimensionValue\n marginBottom: DimensionValue\n marginHorizontal: DimensionValue\n marginLeft: DimensionValue\n marginRight: DimensionValue\n marginTop: DimensionValue\n marginVertical: DimensionValue\n maxHeight: DimensionValue\n maxWidth: DimensionValue\n minHeight: DimensionValue\n minWidth: DimensionValue\n overflow: \"visible\" | \"hidden\" | \"scroll\"\n padding: DimensionValue\n paddingBottom: DimensionValue\n paddingHorizontal: DimensionValue\n paddingLeft: DimensionValue\n paddingRight: DimensionValue\n paddingTop: DimensionValue\n paddingVertical: DimensionValue\n position: \"absolute\" | \"relative\" | \"static\"\n right: DimensionValue\n rowGap: number\n top: DimensionValue\n width: DimensionValue\n}>\n\nexport type Angle = `${number}deg` | `${number}rad`\n\nexport type TransformPart =\n | { translateX: number }\n | { translateY: number }\n | { scale: number }\n | { scaleX: number }\n | { scaleY: number }\n | { rotate: Angle }\n // RN's 2D alias for `rotate` (rotation about the Z axis).\n | { rotateZ: Angle }\n\n// A 2D affine transform in the component order GSK and cairo use:\n// x' = xx * x + xy * y + dx\n// y' = yx * x + yy * y + dy\n// This is exactly the argument order of gsk_transform_matrix2d(), so a\n// composed TransformPart[] reaches GTK in a single FFI call.\nexport type Transform2D = {\n xx: number\n yx: number\n xy: number\n yy: number\n dx: number\n dy: number\n}\n\n// Visual props: classified by the style system, compiled into GTK CSS\n// classes via the bridge `css` helper. Text-specific props apply to <Text>.\n// One shadow of a `boxShadow`, field for field react-native's own\n// `BoxShadowValue` (RN 0.76+). Lengths are RN DIPs when numeric; the string\n// form accepts a bare number or `px` and nothing else, which is exactly what\n// RN's processBoxShadow.js accepts.\nexport type BoxShadowValue = {\n offsetX: number | string\n offsetY: number | string\n color?: string\n blurRadius?: number | string\n spreadDistance?: number | string\n inset?: boolean\n}\n\nexport type TextDecorationLine =\n \"none\" | \"underline\" | \"line-through\" | \"underline line-through\"\n\nexport type VisualStyle = Partial<{\n backgroundColor: string\n // RN 0.76+. Both forms RN takes: a CSS `box-shadow` string or the\n // structured array. Compiled by style/box-shadow.ts rather than forwarded,\n // so colours go through the same normalizer as every other colour prop.\n boxShadow: string | readonly BoxShadowValue[]\n borderBottomColor: string\n borderBottomLeftRadius: number\n borderBottomRightRadius: number\n borderBottomWidth: number\n borderColor: string\n borderLeftColor: string\n borderLeftWidth: number\n borderRadius: number\n borderRightColor: string\n borderRightWidth: number\n borderStyle: \"solid\" | \"dotted\" | \"dashed\"\n borderTopColor: string\n borderTopLeftRadius: number\n borderTopRightRadius: number\n borderTopWidth: number\n borderWidth: number\n color: string\n fontFamily: string\n fontSize: number\n fontStyle: \"normal\" | \"italic\"\n fontWeight:\n | \"normal\"\n | \"bold\"\n | \"100\"\n | \"200\"\n | \"300\"\n | \"400\"\n | \"500\"\n | \"600\"\n | \"700\"\n | \"800\"\n | \"900\"\n letterSpacing: number\n lineHeight: number\n opacity: number\n // RN 0.77+. A ring drawn around the border box that takes no layout space\n // — CSS `outline`, which GTK4 implements natively and Adwaita itself uses\n // for every focus ring in the theme.\n outlineColor: string\n outlineOffset: number\n outlineStyle: \"solid\" | \"dotted\" | \"dashed\"\n outlineWidth: number\n // NOT emitted to CSS (no such GTK CSS property): the Text component applies\n // it via label props using the pure style/text-align helper.\n textAlign: \"auto\" | \"left\" | \"right\" | \"center\" | \"justify\"\n // NOT emitted to CSS either — GTK4 CSS has no widget `text-decoration`.\n // Pango carries it, so the Text component turns it into the label's\n // attribute list using the pure style/text-decoration helper.\n textDecorationLine: TextDecorationLine\n // NOT emitted to CSS (GTK4 has no widget `transform` property): composed\n // into a Transform2D by style/transform.ts and applied as the GskTransform\n // of the child's allocation by the container's layout manager.\n transform: TransformPart[]\n}>\n\n// RN pointerEvents (also allowed in styles since RN 0.71); behavioral, so\n// it belongs to neither the layout nor the visual bucket — splitStyle\n// consumes it without routing it and View reads it from the flattened style.\nexport type PointerEventsValue = \"auto\" | \"none\" | \"box-none\" | \"box-only\"\n\n// The third bucket, named rather than inlined into FlatStyle below: props\n// that are supported but consumed by the component that owns the behavior,\n// not by either half of the style pipeline. Having a name for it is what\n// lets splitStyle state which keys it deliberately routes nowhere, instead\n// of reporting them as unknown — the drift this type was born from.\nexport type BehavioralStyle = Partial<{\n pointerEvents: PointerEventsValue\n}>\n\nexport type FlatStyle = LayoutStyle & VisualStyle & BehavioralStyle\n\n// Style prop as components accept it: single object, array (with falsy holes).\nexport type StyleProp<T = FlatStyle> =\n T | null | undefined | false | ReadonlyArray<StyleProp<T>>\n\n// Result of classifying a flattened style (produced by the style system):\nexport type SplitStyle = {\n layout: LayoutStyle\n visual: VisualStyle\n}\n\n// --- layout engine ----------------------------------------------------------\n\nexport type Rect = {\n x: number\n y: number\n width: number\n height: number\n}\n\nexport type MeasureConstraintMode = \"undefined\" | \"exactly\" | \"at-most\"\n\nexport type MeasureSize = { width: number; height: number }\n\n// Measure callback for leaf nodes (text, images): implemented by components\n// using bridge probes; called by the engine during Yoga layout passes.\nexport type MeasureFn = (\n width: number,\n widthMode: MeasureConstraintMode,\n height: number,\n heightMode: MeasureConstraintMode,\n) => MeasureSize\n\n// Handle the layout engine gives every mounted component.\nexport interface LayoutNodeApi {\n setStyle(style: LayoutStyle): void\n setMeasureFn(measure: MeasureFn | null): void\n markDirty(): void\n getRect(): Rect | null\n setOnLayout(callback: ((rect: Rect) => void) | null): void\n}\n\n// --- subscriptions ----------------------------------------------------------\n\nexport type SubscriptionHandle = { remove(): void }\n"]}
|
|
1
|
+
{"version":3,"file":"contracts.js","sourceRoot":"","sources":["../src/contracts.ts"],"names":[],"mappings":"AAAA,wEAAwE;AACxE,sEAAsE","sourcesContent":["// Shared contracts between the layout engine, the style system, the API\n// modules and the components — the one module every layer may import.\n\n// --- style ------------------------------------------------------------------\n\nexport type DimensionValue = number | `${number}%` | \"auto\"\n\nexport type FlexAlignType =\n \"flex-start\" | \"flex-end\" | \"center\" | \"stretch\" | \"baseline\"\n\n// Layout-affecting props: classified by the style system, consumed by the\n// layout engine, which maps them onto Yoga node setters.\nexport type LayoutStyle = Partial<{\n alignContent:\n | \"flex-start\"\n | \"flex-end\"\n | \"center\"\n | \"stretch\"\n | \"space-between\"\n | \"space-around\"\n alignItems: FlexAlignType\n alignSelf: \"auto\" | FlexAlignType\n aspectRatio: number | string\n bottom: DimensionValue\n columnGap: number\n direction: \"inherit\" | \"ltr\" | \"rtl\"\n display: \"none\" | \"flex\"\n flex: number\n flexBasis: DimensionValue\n flexDirection: \"row\" | \"row-reverse\" | \"column\" | \"column-reverse\"\n flexGrow: number\n flexShrink: number\n flexWrap: \"wrap\" | \"nowrap\" | \"wrap-reverse\"\n gap: number\n height: DimensionValue\n justifyContent:\n | \"flex-start\"\n | \"flex-end\"\n | \"center\"\n | \"space-between\"\n | \"space-around\"\n | \"space-evenly\"\n left: DimensionValue\n margin: DimensionValue\n marginBottom: DimensionValue\n marginHorizontal: DimensionValue\n marginLeft: DimensionValue\n marginRight: DimensionValue\n marginTop: DimensionValue\n marginVertical: DimensionValue\n maxHeight: DimensionValue\n maxWidth: DimensionValue\n minHeight: DimensionValue\n minWidth: DimensionValue\n overflow: \"visible\" | \"hidden\" | \"scroll\"\n padding: DimensionValue\n paddingBottom: DimensionValue\n paddingHorizontal: DimensionValue\n paddingLeft: DimensionValue\n paddingRight: DimensionValue\n paddingTop: DimensionValue\n paddingVertical: DimensionValue\n position: \"absolute\" | \"relative\" | \"static\"\n right: DimensionValue\n rowGap: number\n top: DimensionValue\n width: DimensionValue\n}>\n\nexport type Angle = `${number}deg` | `${number}rad`\n\nexport type TransformPart =\n | { translateX: number }\n | { translateY: number }\n | { scale: number }\n | { scaleX: number }\n | { scaleY: number }\n | { rotate: Angle }\n // RN's 2D alias for `rotate` (rotation about the Z axis).\n | { rotateZ: Angle }\n\n// A 2D affine transform in the component order GSK and cairo use:\n// x' = xx * x + xy * y + dx\n// y' = yx * x + yy * y + dy\n// This is exactly the argument order of gsk_transform_matrix2d(), so a\n// composed TransformPart[] reaches GTK in a single FFI call.\nexport type Transform2D = {\n xx: number\n yx: number\n xy: number\n yy: number\n dx: number\n dy: number\n}\n\n// Visual props: classified by the style system, compiled into GTK CSS\n// classes via the bridge `css` helper. Text-specific props apply to <Text>.\n// One shadow of a `boxShadow`, field for field react-native's own\n// `BoxShadowValue` (RN 0.76+). Lengths are RN DIPs when numeric; the string\n// form accepts a bare number or `px` and nothing else, which is exactly what\n// RN's processBoxShadow.js accepts.\nexport type BoxShadowValue = {\n offsetX: number | string\n offsetY: number | string\n color?: string\n blurRadius?: number | string\n spreadDistance?: number | string\n inset?: boolean\n}\n\nexport type TextDecorationLine =\n \"none\" | \"underline\" | \"line-through\" | \"underline line-through\"\n\nexport type VisualStyle = Partial<{\n backgroundColor: string\n // RN 0.76+. Both forms RN takes: a CSS `box-shadow` string or the\n // structured array. Compiled by style/box-shadow.ts rather than forwarded,\n // so colours go through the same normalizer as every other colour prop.\n boxShadow: string | readonly BoxShadowValue[]\n borderBottomColor: string\n borderBottomLeftRadius: number\n borderBottomRightRadius: number\n borderBottomWidth: number\n borderColor: string\n borderLeftColor: string\n borderLeftWidth: number\n borderRadius: number\n borderRightColor: string\n borderRightWidth: number\n borderStyle: \"solid\" | \"dotted\" | \"dashed\"\n borderTopColor: string\n borderTopLeftRadius: number\n borderTopRightRadius: number\n borderTopWidth: number\n borderWidth: number\n color: string\n fontFamily: string\n fontSize: number\n fontStyle: \"normal\" | \"italic\"\n fontWeight:\n | \"normal\"\n | \"bold\"\n | \"100\"\n | \"200\"\n | \"300\"\n | \"400\"\n | \"500\"\n | \"600\"\n | \"700\"\n | \"800\"\n | \"900\"\n letterSpacing: number\n lineHeight: number\n opacity: number\n // RN 0.77+. A ring drawn around the border box that takes no layout space\n // — CSS `outline`, which GTK4 implements natively and Adwaita itself uses\n // for every focus ring in the theme.\n outlineColor: string\n outlineOffset: number\n outlineStyle: \"solid\" | \"dotted\" | \"dashed\"\n outlineWidth: number\n // NOT emitted to CSS (no such GTK CSS property): the Text component applies\n // it via label props using the pure style/text-align helper.\n textAlign: \"auto\" | \"left\" | \"right\" | \"center\" | \"justify\"\n // NOT emitted to CSS either — GTK4 CSS has no widget `text-decoration`.\n // Pango carries it, so the Text component turns it into the label's\n // attribute list using the pure style/text-decoration helper.\n textDecorationLine: TextDecorationLine\n // NOT emitted to CSS (GTK4 has no widget `transform` property): composed\n // into a Transform2D by style/transform.ts and applied as the GskTransform\n // of the child's allocation by the container's layout manager.\n transform: TransformPart[]\n}>\n\n// RN pointerEvents (also allowed in styles since RN 0.71); behavioral, so\n// it belongs to neither the layout nor the visual bucket — splitStyle\n// consumes it without routing it and View reads it from the flattened style.\nexport type PointerEventsValue = \"auto\" | \"none\" | \"box-none\" | \"box-only\"\n\n// The third bucket, named rather than inlined into FlatStyle below: props\n// that are supported but consumed by the component that owns the behavior,\n// not by either half of the style pipeline. Having a name for it is what\n// lets splitStyle state which keys it deliberately routes nowhere, instead\n// of reporting them as unknown — the drift this type was born from.\nexport type BehavioralStyle = Partial<{\n pointerEvents: PointerEventsValue\n // RN's paint order among siblings. Not a Yoga property (it changes nothing\n // about where anything is laid out) and not a CSS one (GTK4 has no z-order\n // declaration): the container widget sorts its snapshot and its picking by\n // it — see gtkx/bridge/view-box.ts.\n zIndex: number\n}>\n\nexport type FlatStyle = LayoutStyle & VisualStyle & BehavioralStyle\n\n// Style prop as components accept it: single object, array (with falsy holes).\nexport type StyleProp<T = FlatStyle> =\n T | null | undefined | false | ReadonlyArray<StyleProp<T>>\n\n// RN's three style bags, as the names ordinary React Native code writes\n// them: `StyleProp<ViewStyle>`, `StyleProp<TextStyle>`, `StyleProp<ImageStyle>`.\n//\n// They are all `FlatStyle` here, and deliberately so. RN splits the bag\n// three ways to keep `fontWeight` off a `View` and `resizeMode` off a\n// `Text`; this platform's split is a different one (layout → Yoga, visual →\n// GTK CSS, see style/split-style.ts) and it does not follow component\n// boundaries. Narrowing these three to subsets would reject styles that\n// work — and the names exist so that a file shared with iOS and Android\n// compiles, not so that this platform can re-police RN's taxonomy.\n//\n// Found by porting react-native-reanimated-dnd's example app, where\n// `StyleProp<ViewStyle>` on a `Droppable`'s `activeStyle` is the ordinary\n// way to type it and there was no name to import.\nexport type ViewStyle = FlatStyle\nexport type TextStyle = FlatStyle\nexport type ImageStyle = FlatStyle\n\n// Result of classifying a flattened style (produced by the style system):\nexport type SplitStyle = {\n layout: LayoutStyle\n visual: VisualStyle\n}\n\n// --- layout engine ----------------------------------------------------------\n\nexport type Rect = {\n x: number\n y: number\n width: number\n height: number\n}\n\nexport type MeasureConstraintMode = \"undefined\" | \"exactly\" | \"at-most\"\n\nexport type MeasureSize = { width: number; height: number }\n\n// Measure callback for leaf nodes (text, images): implemented by components\n// using bridge probes; called by the engine during Yoga layout passes.\nexport type MeasureFn = (\n width: number,\n widthMode: MeasureConstraintMode,\n height: number,\n heightMode: MeasureConstraintMode,\n) => MeasureSize\n\n// Handle the layout engine gives every mounted component.\nexport interface LayoutNodeApi {\n setStyle(style: LayoutStyle): void\n setMeasureFn(measure: MeasureFn | null): void\n markDirty(): void\n getRect(): Rect | null\n setOnLayout(callback: ((rect: Rect) => void) | null): void\n}\n\n// --- subscriptions ----------------------------------------------------------\n\nexport type SubscriptionHandle = { remove(): void }\n"]}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { type ReactNode, type RefObject } from "react";
|
|
2
|
+
import type { MeasureHandle } from "../components/measure";
|
|
3
|
+
import type { DroppedItemsMap, DropProviderProps } from "./types";
|
|
4
|
+
export type DraggingPayload = {
|
|
5
|
+
x: number;
|
|
6
|
+
y: number;
|
|
7
|
+
tx: number;
|
|
8
|
+
ty: number;
|
|
9
|
+
itemData: unknown;
|
|
10
|
+
};
|
|
11
|
+
/** A drag this provider has seen begin and not yet seen end. */
|
|
12
|
+
export type ActiveDrag = {
|
|
13
|
+
draggableId: string;
|
|
14
|
+
data: unknown;
|
|
15
|
+
/** The dragged view's own origin, in the provider view's coordinates. */
|
|
16
|
+
originX: number;
|
|
17
|
+
originY: number;
|
|
18
|
+
/** Where the pointer was when the drag began, same coordinates — the
|
|
19
|
+
* origin `tx`/`ty` are measured from. */
|
|
20
|
+
startX: number;
|
|
21
|
+
startY: number;
|
|
22
|
+
onDragging?: (payload: DraggingPayload) => void;
|
|
23
|
+
};
|
|
24
|
+
export type DropRegistration = {
|
|
25
|
+
droppableId: string;
|
|
26
|
+
capacity: number;
|
|
27
|
+
};
|
|
28
|
+
export type DropContextValue = {
|
|
29
|
+
/** Register a draggable's payload so a drop can resolve the id back to the
|
|
30
|
+
* data it stands for. */
|
|
31
|
+
registerDraggable: (draggableId: string, data: unknown) => void;
|
|
32
|
+
unregisterDraggable: (draggableId: string) => void;
|
|
33
|
+
dataFor: (draggableId: string) => {
|
|
34
|
+
found: boolean;
|
|
35
|
+
data: unknown;
|
|
36
|
+
};
|
|
37
|
+
registerDroppable: (registration: DropRegistration) => void;
|
|
38
|
+
unregisterDroppable: (droppableId: string) => void;
|
|
39
|
+
hasAvailableCapacity: (droppableId: string) => boolean;
|
|
40
|
+
registerDroppedItem: (draggableId: string, droppableId: string, data: unknown) => void;
|
|
41
|
+
unregisterDroppedItem: (draggableId: string) => void;
|
|
42
|
+
getDroppedItems: () => DroppedItemsMap;
|
|
43
|
+
/** Called by a `Draggable` from `drag-begin` / `drag-end`. */
|
|
44
|
+
beginDrag: (drag: ActiveDrag) => void;
|
|
45
|
+
endDrag: () => void;
|
|
46
|
+
/** The provider's own view, so a draggable can measure its origin in the
|
|
47
|
+
* same coordinates the motion controller reports. Null when a draggable
|
|
48
|
+
* is used outside any provider. */
|
|
49
|
+
providerView: RefObject<MeasureHandle | null> | null;
|
|
50
|
+
};
|
|
51
|
+
export declare const DropContext: React.Context<DropContextValue>;
|
|
52
|
+
export declare const useDropContext: () => DropContextValue;
|
|
53
|
+
/**
|
|
54
|
+
* Scopes a set of draggables and droppables, and reports the drag as it
|
|
55
|
+
* happens.
|
|
56
|
+
*
|
|
57
|
+
* ```tsx
|
|
58
|
+
* <DropProvider>
|
|
59
|
+
* <Droppable onDrop={(task) => assign(task)}>…</Droppable>
|
|
60
|
+
* <Draggable data={task}>…</Draggable>
|
|
61
|
+
* </DropProvider>
|
|
62
|
+
* ```
|
|
63
|
+
*
|
|
64
|
+
* Unlike upstream this renders a `View` of its own, because `onDragging`
|
|
65
|
+
* needs a widget to hang a `GtkDropControllerMotion` on. It is a plain
|
|
66
|
+
* `flex: 1` box unless `style` says otherwise, so it fills its parent the
|
|
67
|
+
* way the fragment upstream renders would have.
|
|
68
|
+
*/
|
|
69
|
+
export declare const DropProvider: ({ children, onDroppedItemsUpdate, onDragStart, onDragEnd, onDragging, style, testID, ref, }: DropProviderProps) => ReactNode;
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
// `DropProvider` — the one piece of this module that is not a GTK controller.
|
|
3
|
+
//
|
|
4
|
+
// GDK does the hit testing, so unlike upstream this provider keeps no slot
|
|
5
|
+
// rectangles and needs no layout pass. What it does keep is the three things
|
|
6
|
+
// GDK cannot carry for us:
|
|
7
|
+
//
|
|
8
|
+
// 1. the map from a draggable's id to its `data` (see payload.ts: a GValue
|
|
9
|
+
// cannot hold arbitrary JavaScript, so only the id travels);
|
|
10
|
+
// 2. the registry of droppables, for `capacity` and `getDroppedItems`;
|
|
11
|
+
// 3. the in-flight drag record, which is what makes `onDragging`
|
|
12
|
+
// reconstructable — see the GtkDropControllerMotion below.
|
|
13
|
+
import { createContext, useCallback, useContext, useImperativeHandle, useMemo, useRef, } from "react";
|
|
14
|
+
import { View } from "../components/view";
|
|
15
|
+
import { Controllers } from "../gtk/controllers";
|
|
16
|
+
import { GtkDropControllerMotion } from "../gtkx/bridge/index";
|
|
17
|
+
const noop = () => { };
|
|
18
|
+
// A default, so a `Draggable` used without a provider still drags — it just
|
|
19
|
+
// has nowhere to record a drop. Upstream throws instead; a bare draggable is
|
|
20
|
+
// a legitimate thing to demo, so this quietly does nothing.
|
|
21
|
+
const ORPHAN = {
|
|
22
|
+
registerDraggable: noop,
|
|
23
|
+
unregisterDraggable: noop,
|
|
24
|
+
dataFor: () => ({ found: false, data: undefined }),
|
|
25
|
+
registerDroppable: noop,
|
|
26
|
+
unregisterDroppable: noop,
|
|
27
|
+
hasAvailableCapacity: () => true,
|
|
28
|
+
registerDroppedItem: noop,
|
|
29
|
+
unregisterDroppedItem: noop,
|
|
30
|
+
getDroppedItems: () => ({}),
|
|
31
|
+
beginDrag: noop,
|
|
32
|
+
endDrag: noop,
|
|
33
|
+
providerView: null,
|
|
34
|
+
};
|
|
35
|
+
export const DropContext = createContext(ORPHAN);
|
|
36
|
+
export const useDropContext = () => useContext(DropContext);
|
|
37
|
+
const defaultStyle = { flex: 1 };
|
|
38
|
+
/**
|
|
39
|
+
* Scopes a set of draggables and droppables, and reports the drag as it
|
|
40
|
+
* happens.
|
|
41
|
+
*
|
|
42
|
+
* ```tsx
|
|
43
|
+
* <DropProvider>
|
|
44
|
+
* <Droppable onDrop={(task) => assign(task)}>…</Droppable>
|
|
45
|
+
* <Draggable data={task}>…</Draggable>
|
|
46
|
+
* </DropProvider>
|
|
47
|
+
* ```
|
|
48
|
+
*
|
|
49
|
+
* Unlike upstream this renders a `View` of its own, because `onDragging`
|
|
50
|
+
* needs a widget to hang a `GtkDropControllerMotion` on. It is a plain
|
|
51
|
+
* `flex: 1` box unless `style` says otherwise, so it fills its parent the
|
|
52
|
+
* way the fragment upstream renders would have.
|
|
53
|
+
*/
|
|
54
|
+
export const DropProvider = ({ children, onDroppedItemsUpdate, onDragStart, onDragEnd, onDragging, style, testID, ref, }) => {
|
|
55
|
+
const draggables = useRef(new Map());
|
|
56
|
+
const droppables = useRef(new Map());
|
|
57
|
+
const dropped = useRef({});
|
|
58
|
+
const active = useRef(null);
|
|
59
|
+
const providerView = useRef(null);
|
|
60
|
+
const notifyDropped = useCallback(() => {
|
|
61
|
+
onDroppedItemsUpdate?.({ ...dropped.current });
|
|
62
|
+
}, [onDroppedItemsUpdate]);
|
|
63
|
+
const value = useMemo(() => ({
|
|
64
|
+
registerDraggable: (draggableId, data) => {
|
|
65
|
+
draggables.current.set(draggableId, data);
|
|
66
|
+
},
|
|
67
|
+
unregisterDraggable: (draggableId) => {
|
|
68
|
+
draggables.current.delete(draggableId);
|
|
69
|
+
},
|
|
70
|
+
dataFor: (draggableId) => ({
|
|
71
|
+
found: draggables.current.has(draggableId),
|
|
72
|
+
data: draggables.current.get(draggableId),
|
|
73
|
+
}),
|
|
74
|
+
registerDroppable: (registration) => {
|
|
75
|
+
droppables.current.set(registration.droppableId, registration);
|
|
76
|
+
},
|
|
77
|
+
unregisterDroppable: (droppableId) => {
|
|
78
|
+
droppables.current.delete(droppableId);
|
|
79
|
+
},
|
|
80
|
+
hasAvailableCapacity: (droppableId) => {
|
|
81
|
+
const registration = droppables.current.get(droppableId);
|
|
82
|
+
if (!registration ||
|
|
83
|
+
registration.capacity === Number.POSITIVE_INFINITY) {
|
|
84
|
+
return true;
|
|
85
|
+
}
|
|
86
|
+
const held = Object.values(dropped.current).filter((entry) => entry.droppableId === droppableId).length;
|
|
87
|
+
return held < registration.capacity;
|
|
88
|
+
},
|
|
89
|
+
registerDroppedItem: (draggableId, droppableId, data) => {
|
|
90
|
+
dropped.current = {
|
|
91
|
+
...dropped.current,
|
|
92
|
+
[draggableId]: { droppableId, data },
|
|
93
|
+
};
|
|
94
|
+
notifyDropped();
|
|
95
|
+
},
|
|
96
|
+
unregisterDroppedItem: (draggableId) => {
|
|
97
|
+
if (!(draggableId in dropped.current)) {
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
const next = { ...dropped.current };
|
|
101
|
+
delete next[draggableId];
|
|
102
|
+
dropped.current = next;
|
|
103
|
+
notifyDropped();
|
|
104
|
+
},
|
|
105
|
+
getDroppedItems: () => ({ ...dropped.current }),
|
|
106
|
+
beginDrag: (drag) => {
|
|
107
|
+
active.current = drag;
|
|
108
|
+
onDragStart?.(drag.data);
|
|
109
|
+
},
|
|
110
|
+
endDrag: () => {
|
|
111
|
+
const drag = active.current;
|
|
112
|
+
active.current = null;
|
|
113
|
+
if (drag) {
|
|
114
|
+
onDragEnd?.(drag.data);
|
|
115
|
+
}
|
|
116
|
+
},
|
|
117
|
+
providerView,
|
|
118
|
+
}), [notifyDropped, onDragStart, onDragEnd]);
|
|
119
|
+
useImperativeHandle(ref, () => ({
|
|
120
|
+
// See types.ts: a no-op because GDK re-hit-tests on every motion.
|
|
121
|
+
requestPositionUpdate: noop,
|
|
122
|
+
getDroppedItems: () => ({ ...dropped.current }),
|
|
123
|
+
}), []);
|
|
124
|
+
// The whole reason this provider owns a view. `GtkDragSource` goes quiet
|
|
125
|
+
// between `drag-begin` and `drag-end`: once the drag starts the source is
|
|
126
|
+
// out of the loop and the compositor owns the icon. `GtkDropControllerMotion`
|
|
127
|
+
// is the controller that tracks pointer motion DURING a drag, over any
|
|
128
|
+
// widget, so one here sees the drag cross the provider's whole area — which
|
|
129
|
+
// is exactly what upstream's `onDragging` reports.
|
|
130
|
+
const reportMotion = useCallback((x, y) => {
|
|
131
|
+
const drag = active.current;
|
|
132
|
+
if (!drag) {
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
const payload = {
|
|
136
|
+
x: drag.originX,
|
|
137
|
+
y: drag.originY,
|
|
138
|
+
// The translation the view WOULD have had: how far the pointer has
|
|
139
|
+
// travelled since the drag began.
|
|
140
|
+
tx: x - drag.startX,
|
|
141
|
+
ty: y - drag.startY,
|
|
142
|
+
itemData: drag.data,
|
|
143
|
+
};
|
|
144
|
+
drag.onDragging?.(payload);
|
|
145
|
+
onDragging?.(payload);
|
|
146
|
+
}, [onDragging]);
|
|
147
|
+
return (_jsx(DropContext.Provider, { value: value, children: _jsxs(View, { ref: providerView, testID: testID, style: style ?? defaultStyle, children: [_jsx(Controllers, { children: _jsx(GtkDropControllerMotion, { onEnter: reportMotion, onMotion: reportMotion }) }), children] }) }));
|
|
148
|
+
};
|
|
149
|
+
//# sourceMappingURL=context.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context.js","sourceRoot":"","sources":["../../src/dnd/context.tsx"],"names":[],"mappings":"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","sourcesContent":["// `DropProvider` — the one piece of this module that is not a GTK controller.\n//\n// GDK does the hit testing, so unlike upstream this provider keeps no slot\n// rectangles and needs no layout pass. What it does keep is the three things\n// GDK cannot carry for us:\n//\n// 1. the map from a draggable's id to its `data` (see payload.ts: a GValue\n// cannot hold arbitrary JavaScript, so only the id travels);\n// 2. the registry of droppables, for `capacity` and `getDroppedItems`;\n// 3. the in-flight drag record, which is what makes `onDragging`\n// reconstructable — see the GtkDropControllerMotion below.\nimport {\n createContext,\n useCallback,\n useContext,\n useImperativeHandle,\n useMemo,\n useRef,\n type ReactNode,\n type RefObject,\n} from \"react\"\nimport type { MeasureHandle } from \"../components/measure\"\nimport { View } from \"../components/view\"\nimport { Controllers } from \"../gtk/controllers\"\nimport { GtkDropControllerMotion } from \"../gtkx/bridge/index\"\nimport type {\n DroppedItemsMap,\n DropProviderProps,\n DropProviderRef,\n} from \"./types\"\n\nexport type DraggingPayload = {\n x: number\n y: number\n tx: number\n ty: number\n itemData: unknown\n}\n\n/** A drag this provider has seen begin and not yet seen end. */\nexport type ActiveDrag = {\n draggableId: string\n data: unknown\n /** The dragged view's own origin, in the provider view's coordinates. */\n originX: number\n originY: number\n /** Where the pointer was when the drag began, same coordinates — the\n * origin `tx`/`ty` are measured from. */\n startX: number\n startY: number\n onDragging?: (payload: DraggingPayload) => void\n}\n\nexport type DropRegistration = { droppableId: string; capacity: number }\n\nexport type DropContextValue = {\n /** Register a draggable's payload so a drop can resolve the id back to the\n * data it stands for. */\n registerDraggable: (draggableId: string, data: unknown) => void\n unregisterDraggable: (draggableId: string) => void\n dataFor: (draggableId: string) => { found: boolean; data: unknown }\n\n registerDroppable: (registration: DropRegistration) => void\n unregisterDroppable: (droppableId: string) => void\n hasAvailableCapacity: (droppableId: string) => boolean\n\n registerDroppedItem: (\n draggableId: string,\n droppableId: string,\n data: unknown,\n ) => void\n unregisterDroppedItem: (draggableId: string) => void\n getDroppedItems: () => DroppedItemsMap\n\n /** Called by a `Draggable` from `drag-begin` / `drag-end`. */\n beginDrag: (drag: ActiveDrag) => void\n endDrag: () => void\n\n /** The provider's own view, so a draggable can measure its origin in the\n * same coordinates the motion controller reports. Null when a draggable\n * is used outside any provider. */\n providerView: RefObject<MeasureHandle | null> | null\n}\n\nconst noop = (): void => {}\n\n// A default, so a `Draggable` used without a provider still drags — it just\n// has nowhere to record a drop. Upstream throws instead; a bare draggable is\n// a legitimate thing to demo, so this quietly does nothing.\nconst ORPHAN: DropContextValue = {\n registerDraggable: noop,\n unregisterDraggable: noop,\n dataFor: () => ({ found: false, data: undefined }),\n registerDroppable: noop,\n unregisterDroppable: noop,\n hasAvailableCapacity: () => true,\n registerDroppedItem: noop,\n unregisterDroppedItem: noop,\n getDroppedItems: () => ({}),\n beginDrag: noop,\n endDrag: noop,\n providerView: null,\n}\n\nexport const DropContext = createContext<DropContextValue>(ORPHAN)\n\nexport const useDropContext = (): DropContextValue => useContext(DropContext)\n\nconst defaultStyle = { flex: 1 } as const\n\n/**\n * Scopes a set of draggables and droppables, and reports the drag as it\n * happens.\n *\n * ```tsx\n * <DropProvider>\n * <Droppable onDrop={(task) => assign(task)}>…</Droppable>\n * <Draggable data={task}>…</Draggable>\n * </DropProvider>\n * ```\n *\n * Unlike upstream this renders a `View` of its own, because `onDragging`\n * needs a widget to hang a `GtkDropControllerMotion` on. It is a plain\n * `flex: 1` box unless `style` says otherwise, so it fills its parent the\n * way the fragment upstream renders would have.\n */\nexport const DropProvider = ({\n children,\n onDroppedItemsUpdate,\n onDragStart,\n onDragEnd,\n onDragging,\n style,\n testID,\n ref,\n}: DropProviderProps): ReactNode => {\n const draggables = useRef(new Map<string, unknown>())\n const droppables = useRef(new Map<string, DropRegistration>())\n const dropped = useRef<DroppedItemsMap>({})\n const active = useRef<ActiveDrag | null>(null)\n const providerView = useRef<MeasureHandle | null>(null)\n\n const notifyDropped = useCallback(() => {\n onDroppedItemsUpdate?.({ ...dropped.current })\n }, [onDroppedItemsUpdate])\n\n const value = useMemo<DropContextValue>(\n () => ({\n registerDraggable: (draggableId, data) => {\n draggables.current.set(draggableId, data)\n },\n unregisterDraggable: (draggableId) => {\n draggables.current.delete(draggableId)\n },\n dataFor: (draggableId) => ({\n found: draggables.current.has(draggableId),\n data: draggables.current.get(draggableId),\n }),\n\n registerDroppable: (registration) => {\n droppables.current.set(registration.droppableId, registration)\n },\n unregisterDroppable: (droppableId) => {\n droppables.current.delete(droppableId)\n },\n hasAvailableCapacity: (droppableId) => {\n const registration = droppables.current.get(droppableId)\n if (\n !registration ||\n registration.capacity === Number.POSITIVE_INFINITY\n ) {\n return true\n }\n const held = Object.values(dropped.current).filter(\n (entry) => entry.droppableId === droppableId,\n ).length\n return held < registration.capacity\n },\n\n registerDroppedItem: (draggableId, droppableId, data) => {\n dropped.current = {\n ...dropped.current,\n [draggableId]: { droppableId, data },\n }\n notifyDropped()\n },\n unregisterDroppedItem: (draggableId) => {\n if (!(draggableId in dropped.current)) {\n return\n }\n const next = { ...dropped.current }\n delete next[draggableId]\n dropped.current = next\n notifyDropped()\n },\n getDroppedItems: () => ({ ...dropped.current }),\n\n beginDrag: (drag) => {\n active.current = drag\n onDragStart?.(drag.data)\n },\n endDrag: () => {\n const drag = active.current\n active.current = null\n if (drag) {\n onDragEnd?.(drag.data)\n }\n },\n\n providerView,\n }),\n [notifyDropped, onDragStart, onDragEnd],\n )\n\n useImperativeHandle(\n ref,\n (): DropProviderRef => ({\n // See types.ts: a no-op because GDK re-hit-tests on every motion.\n requestPositionUpdate: noop,\n getDroppedItems: () => ({ ...dropped.current }),\n }),\n [],\n )\n\n // The whole reason this provider owns a view. `GtkDragSource` goes quiet\n // between `drag-begin` and `drag-end`: once the drag starts the source is\n // out of the loop and the compositor owns the icon. `GtkDropControllerMotion`\n // is the controller that tracks pointer motion DURING a drag, over any\n // widget, so one here sees the drag cross the provider's whole area — which\n // is exactly what upstream's `onDragging` reports.\n const reportMotion = useCallback(\n (x: number, y: number) => {\n const drag = active.current\n if (!drag) {\n return\n }\n const payload: DraggingPayload = {\n x: drag.originX,\n y: drag.originY,\n // The translation the view WOULD have had: how far the pointer has\n // travelled since the drag began.\n tx: x - drag.startX,\n ty: y - drag.startY,\n itemData: drag.data,\n }\n drag.onDragging?.(payload)\n onDragging?.(payload)\n },\n [onDragging],\n )\n\n return (\n <DropContext.Provider value={value}>\n <View\n ref={providerView}\n testID={testID}\n style={style ?? defaultStyle}\n >\n <Controllers>\n <GtkDropControllerMotion\n onEnter={reportMotion}\n onMotion={reportMotion}\n />\n </Controllers>\n {children}\n </View>\n </DropContext.Provider>\n )\n}\n"]}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { type ReactNode, type RefObject } from "react";
|
|
2
|
+
import type { MeasureHandle } from "../components/measure";
|
|
3
|
+
import { type DraggableHandleProps, type DraggableProps, type UseDraggableOptions, type UseDraggableReturn } from "./types";
|
|
4
|
+
type DraggableContextValue = {
|
|
5
|
+
/** The controllers a handle mounts on its own widget instead of the
|
|
6
|
+
* item's. */
|
|
7
|
+
source: ReactNode;
|
|
8
|
+
registerHandle: (registered: boolean) => void;
|
|
9
|
+
};
|
|
10
|
+
export declare const DraggableContext: React.Context<DraggableContextValue | null>;
|
|
11
|
+
export type UseDraggableResult = UseDraggableReturn & {
|
|
12
|
+
/** The view the drag source belongs to must be measurable, because
|
|
13
|
+
* `onDragging` reports the item's origin. Upstream returns an
|
|
14
|
+
* `useAnimatedRef` under this name for the same reason. */
|
|
15
|
+
animatedViewRef: RefObject<MeasureHandle | null>;
|
|
16
|
+
/** The GTK controllers themselves, so a caller that renders its own handle
|
|
17
|
+
* can place them. `animatedViewProps.children` is this, or null when a
|
|
18
|
+
* handle has claimed it. */
|
|
19
|
+
dragControllers: ReactNode;
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* The drag half of the module, as a hook, for a component that owns its own
|
|
23
|
+
* view.
|
|
24
|
+
*
|
|
25
|
+
* `animatedViewProps` keeps upstream's name for spread compatibility. What
|
|
26
|
+
* it carries differs: upstream's is a Reanimated `style`, ours is the GTK
|
|
27
|
+
* controllers as `children`, because on this platform the drag is a property
|
|
28
|
+
* of the widget rather than of a transform.
|
|
29
|
+
*/
|
|
30
|
+
export declare const useDraggable: <TData>(options: UseDraggableOptions<TData>) => UseDraggableResult;
|
|
31
|
+
/**
|
|
32
|
+
* Makes its children draggable, carrying `data` to whatever `Droppable`
|
|
33
|
+
* accepts it.
|
|
34
|
+
*
|
|
35
|
+
* ```tsx
|
|
36
|
+
* <Draggable data={task}>
|
|
37
|
+
* <View style={styles.card}>
|
|
38
|
+
* <Text>{task.title}</Text>
|
|
39
|
+
* </View>
|
|
40
|
+
* </Draggable>
|
|
41
|
+
* ```
|
|
42
|
+
*
|
|
43
|
+
* With a `Draggable.Handle` inside it the drag starts only from the handle —
|
|
44
|
+
* and on this platform that is literal: the `GtkDragSource` is attached to
|
|
45
|
+
* the handle's own widget, so the rest of the item stays free to scroll,
|
|
46
|
+
* press and select.
|
|
47
|
+
*/
|
|
48
|
+
export declare const Draggable: {
|
|
49
|
+
<TData>({ children, style, testID, ...options }: DraggableProps<TData>): ReactNode;
|
|
50
|
+
Handle: ({ children, style, testID, }: DraggableHandleProps) => ReactNode;
|
|
51
|
+
};
|
|
52
|
+
/**
|
|
53
|
+
* The region a drag may start from. Everything outside it stays inert.
|
|
54
|
+
*
|
|
55
|
+
* Upstream's handle gates a pan gesture; here it owns the `GtkDragSource`
|
|
56
|
+
* outright, which is a closer match to how GTK's own list rows work.
|
|
57
|
+
*/
|
|
58
|
+
export declare const DraggableHandle: ({ children, style, testID, }: DraggableHandleProps) => ReactNode;
|
|
59
|
+
export {};
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
// `Draggable`, `Draggable.Handle` and `useDraggable`.
|
|
3
|
+
//
|
|
4
|
+
// Upstream's draggable moves itself: a Reanimated transform follows the
|
|
5
|
+
// finger and springs back, or into the drop slot, at the end. Here the view
|
|
6
|
+
// never moves — GDK carries a picture of it (a `Gtk.WidgetPaintable`) above
|
|
7
|
+
// every window, with the theme's own cursors. That single difference is
|
|
8
|
+
// where `dragAxis`, `dragBoundsRef` and `animationFunction` go, and it is
|
|
9
|
+
// also why a drag here looks like the rest of the desktop instead of like a
|
|
10
|
+
// mobile app. docs/research/drag-and-drop.md argues the trade.
|
|
11
|
+
import { createContext, useCallback, useContext, useEffect, useMemo, useRef, useState, } from "react";
|
|
12
|
+
import { View } from "../components/view";
|
|
13
|
+
import { useDropContext } from "./context";
|
|
14
|
+
import { DragSourceControllers } from "./gtk-controllers";
|
|
15
|
+
import { FREE_SCOPE, nextDraggableId } from "./payload";
|
|
16
|
+
import { DraggableState, } from "./types";
|
|
17
|
+
export const DraggableContext = createContext(null);
|
|
18
|
+
/**
|
|
19
|
+
* The drag half of the module, as a hook, for a component that owns its own
|
|
20
|
+
* view.
|
|
21
|
+
*
|
|
22
|
+
* `animatedViewProps` keeps upstream's name for spread compatibility. What
|
|
23
|
+
* it carries differs: upstream's is a Reanimated `style`, ours is the GTK
|
|
24
|
+
* controllers as `children`, because on this platform the drag is a property
|
|
25
|
+
* of the widget rather than of a transform.
|
|
26
|
+
*/
|
|
27
|
+
export const useDraggable = (options) => {
|
|
28
|
+
const { data, draggableId, dragDisabled = false, onDragStart, onDragEnd, onDragging, onStateChange, } = options;
|
|
29
|
+
const context = useDropContext();
|
|
30
|
+
const viewRef = useRef(null);
|
|
31
|
+
const grab = useRef({ x: 0, y: 0 });
|
|
32
|
+
const [state, setState] = useState(DraggableState.IDLE);
|
|
33
|
+
const [hasHandle, setHasHandle] = useState(false);
|
|
34
|
+
// Stable for the component's lifetime, as upstream's is: the id is what
|
|
35
|
+
// travels through GDK, so it cannot change under an in-flight drag. Lazy
|
|
36
|
+
// initial state rather than a ref, because a ref may not be read during
|
|
37
|
+
// render.
|
|
38
|
+
const [generatedId] = useState(nextDraggableId);
|
|
39
|
+
const id = draggableId ?? generatedId;
|
|
40
|
+
// The registry is what turns the id back into `data` on drop, so it tracks
|
|
41
|
+
// every change to `data`, not just the mount.
|
|
42
|
+
useEffect(() => {
|
|
43
|
+
context.registerDraggable(id, data);
|
|
44
|
+
return () => context.unregisterDraggable(id);
|
|
45
|
+
}, [context, id, data]);
|
|
46
|
+
useEffect(() => {
|
|
47
|
+
onStateChange?.(state);
|
|
48
|
+
}, [state, onStateChange]);
|
|
49
|
+
const registerHandle = useCallback((registered) => {
|
|
50
|
+
setHasHandle(registered);
|
|
51
|
+
}, []);
|
|
52
|
+
const handleGrab = useCallback((x, y) => {
|
|
53
|
+
grab.current = { x, y };
|
|
54
|
+
}, []);
|
|
55
|
+
const handleDragBegin = useCallback(() => {
|
|
56
|
+
// Measured now rather than on layout: the origin only matters while a
|
|
57
|
+
// drag is in flight, and the moment it starts is the one time it is
|
|
58
|
+
// guaranteed current. `measureLayout` reports against the provider's own
|
|
59
|
+
// view, which is the coordinate space its motion controller uses.
|
|
60
|
+
let originX = 0;
|
|
61
|
+
let originY = 0;
|
|
62
|
+
const provider = context.providerView?.current;
|
|
63
|
+
if (provider && viewRef.current) {
|
|
64
|
+
viewRef.current.measureLayout(provider, (left, top) => {
|
|
65
|
+
originX = left;
|
|
66
|
+
originY = top;
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
context.beginDrag({
|
|
70
|
+
draggableId: id,
|
|
71
|
+
data,
|
|
72
|
+
originX,
|
|
73
|
+
originY,
|
|
74
|
+
startX: originX + grab.current.x,
|
|
75
|
+
startY: originY + grab.current.y,
|
|
76
|
+
onDragging: onDragging,
|
|
77
|
+
});
|
|
78
|
+
setState(DraggableState.DRAGGING);
|
|
79
|
+
onDragStart?.(data);
|
|
80
|
+
}, [context, id, data, onDragStart, onDragging]);
|
|
81
|
+
const handleDragEnd = useCallback((dropped) => {
|
|
82
|
+
context.endDrag();
|
|
83
|
+
// DROPPED is sticky until the next drag begins, which is as close as
|
|
84
|
+
// this platform gets to upstream's meaning ("the item is sitting in a
|
|
85
|
+
// slot"): the item never left, so there is no return journey to end it.
|
|
86
|
+
setState(dropped ? DraggableState.DROPPED : DraggableState.IDLE);
|
|
87
|
+
onDragEnd?.(data);
|
|
88
|
+
}, [context, data, onDragEnd]);
|
|
89
|
+
// A handle registers in a passive effect, so on the very first commit
|
|
90
|
+
// `hasHandle` is still false and the item would mount a second drag source
|
|
91
|
+
// that the next commit immediately removes. Deferring the item's own copy
|
|
92
|
+
// by one commit removes the flicker entirely: both state updates land in
|
|
93
|
+
// the same re-render. The delay is unobservable for the same reason
|
|
94
|
+
// `Controllers` gives for attaching late — no pointer reaches a widget in
|
|
95
|
+
// its first frame.
|
|
96
|
+
const [mounted, setMounted] = useState(false);
|
|
97
|
+
// eslint-disable-next-line react-hooks/set-state-in-effect
|
|
98
|
+
useEffect(() => setMounted(true), []);
|
|
99
|
+
const dragControllers = dragDisabled ? null : (_jsx(DragSourceControllers, { payload: { scope: FREE_SCOPE, id }, onGrab: handleGrab, onDragBegin: handleDragBegin, onDragEnd: handleDragEnd }));
|
|
100
|
+
return {
|
|
101
|
+
animatedViewProps: {
|
|
102
|
+
children: mounted && !hasHandle ? dragControllers : null,
|
|
103
|
+
},
|
|
104
|
+
state,
|
|
105
|
+
hasHandle,
|
|
106
|
+
registerHandle,
|
|
107
|
+
animatedViewRef: viewRef,
|
|
108
|
+
dragControllers,
|
|
109
|
+
};
|
|
110
|
+
};
|
|
111
|
+
/**
|
|
112
|
+
* Makes its children draggable, carrying `data` to whatever `Droppable`
|
|
113
|
+
* accepts it.
|
|
114
|
+
*
|
|
115
|
+
* ```tsx
|
|
116
|
+
* <Draggable data={task}>
|
|
117
|
+
* <View style={styles.card}>
|
|
118
|
+
* <Text>{task.title}</Text>
|
|
119
|
+
* </View>
|
|
120
|
+
* </Draggable>
|
|
121
|
+
* ```
|
|
122
|
+
*
|
|
123
|
+
* With a `Draggable.Handle` inside it the drag starts only from the handle —
|
|
124
|
+
* and on this platform that is literal: the `GtkDragSource` is attached to
|
|
125
|
+
* the handle's own widget, so the rest of the item stays free to scroll,
|
|
126
|
+
* press and select.
|
|
127
|
+
*/
|
|
128
|
+
export const Draggable = ({ children, style, testID, ...options }) => {
|
|
129
|
+
const { animatedViewProps, animatedViewRef, dragControllers, registerHandle, } = useDraggable(options);
|
|
130
|
+
const contextValue = useMemo(() => ({ source: dragControllers, registerHandle }), [dragControllers, registerHandle]);
|
|
131
|
+
return (_jsx(DraggableContext.Provider, { value: contextValue, children: _jsxs(View, { ref: animatedViewRef, style: style, testID: testID, children: [animatedViewProps.children, children] }) }));
|
|
132
|
+
};
|
|
133
|
+
/**
|
|
134
|
+
* The region a drag may start from. Everything outside it stays inert.
|
|
135
|
+
*
|
|
136
|
+
* Upstream's handle gates a pan gesture; here it owns the `GtkDragSource`
|
|
137
|
+
* outright, which is a closer match to how GTK's own list rows work.
|
|
138
|
+
*/
|
|
139
|
+
export const DraggableHandle = ({ children, style, testID, }) => {
|
|
140
|
+
const context = useContext(DraggableContext);
|
|
141
|
+
const registerHandle = context?.registerHandle;
|
|
142
|
+
useEffect(() => {
|
|
143
|
+
registerHandle?.(true);
|
|
144
|
+
return () => registerHandle?.(false);
|
|
145
|
+
}, [registerHandle]);
|
|
146
|
+
return (_jsxs(View, { style: style, testID: testID, children: [context?.source, children] }));
|
|
147
|
+
};
|
|
148
|
+
Draggable.Handle = DraggableHandle;
|
|
149
|
+
//# sourceMappingURL=draggable.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"draggable.js","sourceRoot":"","sources":["../../src/dnd/draggable.tsx"],"names":[],"mappings":"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","sourcesContent":["// `Draggable`, `Draggable.Handle` and `useDraggable`.\n//\n// Upstream's draggable moves itself: a Reanimated transform follows the\n// finger and springs back, or into the drop slot, at the end. Here the view\n// never moves — GDK carries a picture of it (a `Gtk.WidgetPaintable`) above\n// every window, with the theme's own cursors. That single difference is\n// where `dragAxis`, `dragBoundsRef` and `animationFunction` go, and it is\n// also why a drag here looks like the rest of the desktop instead of like a\n// mobile app. docs/research/drag-and-drop.md argues the trade.\nimport {\n createContext,\n useCallback,\n useContext,\n useEffect,\n useMemo,\n useRef,\n useState,\n type ReactNode,\n type RefObject,\n} from \"react\"\nimport type { MeasureHandle } from \"../components/measure\"\nimport { View } from \"../components/view\"\nimport { useDropContext, type DraggingPayload } from \"./context\"\nimport { DragSourceControllers } from \"./gtk-controllers\"\nimport { FREE_SCOPE, nextDraggableId } from \"./payload\"\nimport {\n DraggableState,\n type DraggableHandleProps,\n type DraggableProps,\n type UseDraggableOptions,\n type UseDraggableReturn,\n} from \"./types\"\n\ntype DraggableContextValue = {\n /** The controllers a handle mounts on its own widget instead of the\n * item's. */\n source: ReactNode\n registerHandle: (registered: boolean) => void\n}\n\nexport const DraggableContext = createContext<DraggableContextValue | null>(\n null,\n)\n\nexport type UseDraggableResult = UseDraggableReturn & {\n /** The view the drag source belongs to must be measurable, because\n * `onDragging` reports the item's origin. Upstream returns an\n * `useAnimatedRef` under this name for the same reason. */\n animatedViewRef: RefObject<MeasureHandle | null>\n /** The GTK controllers themselves, so a caller that renders its own handle\n * can place them. `animatedViewProps.children` is this, or null when a\n * handle has claimed it. */\n dragControllers: ReactNode\n}\n\n/**\n * The drag half of the module, as a hook, for a component that owns its own\n * view.\n *\n * `animatedViewProps` keeps upstream's name for spread compatibility. What\n * it carries differs: upstream's is a Reanimated `style`, ours is the GTK\n * controllers as `children`, because on this platform the drag is a property\n * of the widget rather than of a transform.\n */\nexport const useDraggable = <TData,>(\n options: UseDraggableOptions<TData>,\n): UseDraggableResult => {\n const {\n data,\n draggableId,\n dragDisabled = false,\n onDragStart,\n onDragEnd,\n onDragging,\n onStateChange,\n } = options\n\n const context = useDropContext()\n const viewRef = useRef<MeasureHandle | null>(null)\n const grab = useRef({ x: 0, y: 0 })\n const [state, setState] = useState(DraggableState.IDLE)\n const [hasHandle, setHasHandle] = useState(false)\n\n // Stable for the component's lifetime, as upstream's is: the id is what\n // travels through GDK, so it cannot change under an in-flight drag. Lazy\n // initial state rather than a ref, because a ref may not be read during\n // render.\n const [generatedId] = useState(nextDraggableId)\n const id = draggableId ?? generatedId\n\n // The registry is what turns the id back into `data` on drop, so it tracks\n // every change to `data`, not just the mount.\n useEffect(() => {\n context.registerDraggable(id, data)\n return () => context.unregisterDraggable(id)\n }, [context, id, data])\n\n useEffect(() => {\n onStateChange?.(state)\n }, [state, onStateChange])\n\n const registerHandle = useCallback((registered: boolean) => {\n setHasHandle(registered)\n }, [])\n\n const handleGrab = useCallback((x: number, y: number) => {\n grab.current = { x, y }\n }, [])\n\n const handleDragBegin = useCallback(() => {\n // Measured now rather than on layout: the origin only matters while a\n // drag is in flight, and the moment it starts is the one time it is\n // guaranteed current. `measureLayout` reports against the provider's own\n // view, which is the coordinate space its motion controller uses.\n let originX = 0\n let originY = 0\n const provider = context.providerView?.current\n if (provider && viewRef.current) {\n viewRef.current.measureLayout(provider, (left, top) => {\n originX = left\n originY = top\n })\n }\n context.beginDrag({\n draggableId: id,\n data,\n originX,\n originY,\n startX: originX + grab.current.x,\n startY: originY + grab.current.y,\n onDragging: onDragging as ((p: DraggingPayload) => void) | undefined,\n })\n setState(DraggableState.DRAGGING)\n onDragStart?.(data)\n }, [context, id, data, onDragStart, onDragging])\n\n const handleDragEnd = useCallback(\n (dropped: boolean) => {\n context.endDrag()\n // DROPPED is sticky until the next drag begins, which is as close as\n // this platform gets to upstream's meaning (\"the item is sitting in a\n // slot\"): the item never left, so there is no return journey to end it.\n setState(dropped ? DraggableState.DROPPED : DraggableState.IDLE)\n onDragEnd?.(data)\n },\n [context, data, onDragEnd],\n )\n\n // A handle registers in a passive effect, so on the very first commit\n // `hasHandle` is still false and the item would mount a second drag source\n // that the next commit immediately removes. Deferring the item's own copy\n // by one commit removes the flicker entirely: both state updates land in\n // the same re-render. The delay is unobservable for the same reason\n // `Controllers` gives for attaching late — no pointer reaches a widget in\n // its first frame.\n const [mounted, setMounted] = useState(false)\n // eslint-disable-next-line react-hooks/set-state-in-effect\n useEffect(() => setMounted(true), [])\n\n const dragControllers = dragDisabled ? null : (\n <DragSourceControllers\n payload={{ scope: FREE_SCOPE, id }}\n onGrab={handleGrab}\n onDragBegin={handleDragBegin}\n onDragEnd={handleDragEnd}\n />\n )\n\n return {\n animatedViewProps: {\n children: mounted && !hasHandle ? dragControllers : null,\n },\n state,\n hasHandle,\n registerHandle,\n animatedViewRef: viewRef,\n dragControllers,\n }\n}\n\n/**\n * Makes its children draggable, carrying `data` to whatever `Droppable`\n * accepts it.\n *\n * ```tsx\n * <Draggable data={task}>\n * <View style={styles.card}>\n * <Text>{task.title}</Text>\n * </View>\n * </Draggable>\n * ```\n *\n * With a `Draggable.Handle` inside it the drag starts only from the handle —\n * and on this platform that is literal: the `GtkDragSource` is attached to\n * the handle's own widget, so the rest of the item stays free to scroll,\n * press and select.\n */\nexport const Draggable = <TData,>({\n children,\n style,\n testID,\n ...options\n}: DraggableProps<TData>): ReactNode => {\n const {\n animatedViewProps,\n animatedViewRef,\n dragControllers,\n registerHandle,\n } = useDraggable<TData>(options)\n\n const contextValue = useMemo<DraggableContextValue>(\n () => ({ source: dragControllers, registerHandle }),\n [dragControllers, registerHandle],\n )\n\n return (\n <DraggableContext.Provider value={contextValue}>\n <View\n ref={animatedViewRef}\n style={style}\n testID={testID}\n >\n {animatedViewProps.children}\n {children}\n </View>\n </DraggableContext.Provider>\n )\n}\n\n/**\n * The region a drag may start from. Everything outside it stays inert.\n *\n * Upstream's handle gates a pan gesture; here it owns the `GtkDragSource`\n * outright, which is a closer match to how GTK's own list rows work.\n */\nexport const DraggableHandle = ({\n children,\n style,\n testID,\n}: DraggableHandleProps): ReactNode => {\n const context = useContext(DraggableContext)\n const registerHandle = context?.registerHandle\n\n useEffect(() => {\n registerHandle?.(true)\n return () => registerHandle?.(false)\n }, [registerHandle])\n\n return (\n <View\n style={style}\n testID={testID}\n >\n {context?.source}\n {children}\n </View>\n )\n}\n\nDraggable.Handle = DraggableHandle\n"]}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { type ReactNode } from "react";
|
|
2
|
+
import type { DroppableProps, UseDroppableOptions, UseDroppableReturn } from "./types";
|
|
3
|
+
/** The drop half of the module, as a hook, for a component that owns its own
|
|
4
|
+
* view. `viewProps.children` are the GTK controllers; spread them into the
|
|
5
|
+
* view the zone should cover. */
|
|
6
|
+
export declare const useDroppable: <TData>(options: UseDroppableOptions<TData>) => UseDroppableReturn;
|
|
7
|
+
/**
|
|
8
|
+
* A zone that accepts a `Draggable`'s `data`.
|
|
9
|
+
*
|
|
10
|
+
* ```tsx
|
|
11
|
+
* <Droppable
|
|
12
|
+
* onDrop={(task) => assign(task)}
|
|
13
|
+
* activeStyle={{ borderColor: "accent" }}
|
|
14
|
+
* >
|
|
15
|
+
* <View style={styles.zone}>
|
|
16
|
+
* <Text>Assign to me</Text>
|
|
17
|
+
* </View>
|
|
18
|
+
* </Droppable>
|
|
19
|
+
* ```
|
|
20
|
+
*
|
|
21
|
+
* `capacity` is enforced at GDK level: once the zone is full it refuses the
|
|
22
|
+
* drop, so the cursor says so before the user lets go.
|
|
23
|
+
*/
|
|
24
|
+
export declare const Droppable: <TData>({ children, style, testID, ...options }: DroppableProps<TData>) => ReactNode;
|