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,284 @@
|
|
|
1
|
+
// The activation loop: one place where every gesture in an interaction asks
|
|
2
|
+
// whether it may become active, and one place where an activation cancels
|
|
3
|
+
// everything it excludes.
|
|
4
|
+
//
|
|
5
|
+
// THE SECOND LOCK. The responder lock (src/responder) keeps its one job —
|
|
6
|
+
// this interaction belongs to React Native, one holder, one irrevocable GTK
|
|
7
|
+
// `CLAIMED` on the source. This loop is the second lock, at the second level,
|
|
8
|
+
// and it is JS-only by construction: it imports nothing from the platform,
|
|
9
|
+
// takes no widget, and never makes GTK claim anything. Every relation is
|
|
10
|
+
// resolved here BEFORE the responder lock is asked for, which is what makes
|
|
11
|
+
// the model honest given that GTK's `CLAIMED` cannot be taken back.
|
|
12
|
+
//
|
|
13
|
+
// Concretely: the FIRST gesture to activate in an interaction asks for the
|
|
14
|
+
// responder and becomes ACTIVE on the grant, exactly as slice 1 did. A gesture
|
|
15
|
+
// that activates alongside it — the only way that can happen is a simultaneous
|
|
16
|
+
// relation, see `shouldBeCancelledBy` — does NOT ask, because the interaction
|
|
17
|
+
// is already ours and there is nothing left to tell GTK. It becomes ACTIVE in
|
|
18
|
+
// this registry and is driven from the touch props, which fire regardless of
|
|
19
|
+
// responder status.
|
|
20
|
+
//
|
|
21
|
+
// Restated from behaviour, not transcribed: docs/research/gesture-detector.md,
|
|
22
|
+
// "Probe 2: what the orchestrator actually guarantees", has the specification
|
|
23
|
+
// this implements and the five rules it has to honour.
|
|
24
|
+
import { createRelationRegistry } from "./relations";
|
|
25
|
+
import { GESTURE_STATE } from "./types";
|
|
26
|
+
export const createOrchestrator = (relations = createRelationRegistry()) => {
|
|
27
|
+
const entries = new Map();
|
|
28
|
+
/** Recording order, because the broadcast cancel walks it backwards. */
|
|
29
|
+
const recorded = [];
|
|
30
|
+
const awaiting = [];
|
|
31
|
+
let activationIndex = 0;
|
|
32
|
+
const isFinished = (entry) => entry.finishedAs !== null;
|
|
33
|
+
const live = (participant) => {
|
|
34
|
+
const entry = entries.get(participant);
|
|
35
|
+
return entry === undefined || isFinished(entry) ? null : entry;
|
|
36
|
+
};
|
|
37
|
+
const drop = (list, participant) => {
|
|
38
|
+
const index = list.indexOf(participant);
|
|
39
|
+
if (index >= 0) {
|
|
40
|
+
list.splice(index, 1);
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
const unpark = (participant) => {
|
|
44
|
+
const entry = entries.get(participant);
|
|
45
|
+
if (entry) {
|
|
46
|
+
entry.awaiting = false;
|
|
47
|
+
}
|
|
48
|
+
drop(awaiting, participant);
|
|
49
|
+
};
|
|
50
|
+
/**
|
|
51
|
+
* Finished gestures are kept until the next interaction starts, not dropped
|
|
52
|
+
* as they end, because `tryActivate` has to be able to tell a gesture it
|
|
53
|
+
* waits for that already ENDED apart from one that was never here. Upstream
|
|
54
|
+
* keeps them for a microtask and sweeps; sweeping at the next press is the
|
|
55
|
+
* same guarantee without a scheduler, and a press is the only moment that
|
|
56
|
+
* can put a gesture back into play.
|
|
57
|
+
*/
|
|
58
|
+
const sweepFinished = () => {
|
|
59
|
+
for (const participant of [...recorded]) {
|
|
60
|
+
const entry = entries.get(participant);
|
|
61
|
+
if (entry !== undefined && isFinished(entry)) {
|
|
62
|
+
entries.delete(participant);
|
|
63
|
+
drop(recorded, participant);
|
|
64
|
+
drop(awaiting, participant);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
/**
|
|
69
|
+
* A simultaneous relation in EITHER direction exempts the pair, and a
|
|
70
|
+
* gesture is always simultaneous with itself. Declaring it on one side is
|
|
71
|
+
* enough — upstream also copies the relation onto the other gesture, which
|
|
72
|
+
* this question makes unnecessary.
|
|
73
|
+
*/
|
|
74
|
+
const canRunSimultaneously = (one, other) => one === other ||
|
|
75
|
+
relations.shouldRecognizeSimultaneously(one.tag, other.tag) ||
|
|
76
|
+
relations.shouldRecognizeSimultaneously(other.tag, one.tag);
|
|
77
|
+
/**
|
|
78
|
+
* The two spellings of one relation: `requireExternalGestureToFail` on the
|
|
79
|
+
* waiter, and `blocksExternalGesture` on the gesture being waited for.
|
|
80
|
+
*/
|
|
81
|
+
const shouldWaitForOther = (participant, other) => participant !== other &&
|
|
82
|
+
(relations.shouldWaitForFailure(participant.tag, other.tag) ||
|
|
83
|
+
relations.blocks(other.tag, participant.tag));
|
|
84
|
+
/**
|
|
85
|
+
* The one rule the relation registry contributes: a gesture that is already
|
|
86
|
+
* ACTIVE, or parked waiting for another to fail, is cancelled only by an
|
|
87
|
+
* active `Native` handler.
|
|
88
|
+
*
|
|
89
|
+
* Upstream exempts buttons from this too (`isButton()`); the button family —
|
|
90
|
+
* `RectButton`, `BaseButton` and the rest — is refused by name on this
|
|
91
|
+
* platform, so there is nothing for the exemption to apply to and adding a
|
|
92
|
+
* flag nobody can set would be decoration.
|
|
93
|
+
*/
|
|
94
|
+
const cancelledByActive = (other) => other.kind === "native" && entries.get(other)?.active === true;
|
|
95
|
+
/**
|
|
96
|
+
* Should `participant` be cancelled because `winner` just activated?
|
|
97
|
+
*
|
|
98
|
+
* MUTUAL EXCLUSION IS THE DEFAULT and a simultaneous relation is the only
|
|
99
|
+
* exemption. Upstream's third branch — two handlers that share no pointer
|
|
100
|
+
* and sit on different views conflict only if a tracked pointer lies inside
|
|
101
|
+
* both views' bounds — has no reachable case here: there is one pointer,
|
|
102
|
+
* one interaction and one fabricated touch, a gesture is recorded only when
|
|
103
|
+
* that touch reaches it, so every pair of recorded gestures shares it. What
|
|
104
|
+
* upstream computes in that branch, this platform knows by construction.
|
|
105
|
+
*/
|
|
106
|
+
const shouldBeCancelledBy = (participant, winner) => {
|
|
107
|
+
if (canRunSimultaneously(participant, winner)) {
|
|
108
|
+
return false;
|
|
109
|
+
}
|
|
110
|
+
const entry = entries.get(participant);
|
|
111
|
+
if (entry?.active === true || entry?.awaiting === true) {
|
|
112
|
+
return cancelledByActive(winner);
|
|
113
|
+
}
|
|
114
|
+
return true;
|
|
115
|
+
};
|
|
116
|
+
/** The same question asked by the gesture that wants to activate. */
|
|
117
|
+
const blockedFromActivating = (participant) => recorded.some((other) => other !== participant &&
|
|
118
|
+
!canRunSimultaneously(participant, other) &&
|
|
119
|
+
cancelledByActive(other));
|
|
120
|
+
const park = (participant) => {
|
|
121
|
+
const entry = entries.get(participant);
|
|
122
|
+
if (entry === undefined || entry.awaiting) {
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
entry.awaiting = true;
|
|
126
|
+
entry.activationIndex = activationIndex;
|
|
127
|
+
activationIndex += 1;
|
|
128
|
+
awaiting.push(participant);
|
|
129
|
+
};
|
|
130
|
+
const makeActive = (participant) => {
|
|
131
|
+
const entry = entries.get(participant);
|
|
132
|
+
if (entry === undefined) {
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
entry.activationIndex = activationIndex;
|
|
136
|
+
activationIndex += 1;
|
|
137
|
+
// The interaction is already React Native's once another gesture HOLDS
|
|
138
|
+
// it, and `grant()` is a no-op for the GTK claim after the first one
|
|
139
|
+
// anyway. So a second gesture does not ask for the responder — it would be
|
|
140
|
+
// asking for a lock that is single-holder by design, and winning it would
|
|
141
|
+
// take the interaction away from the gesture it was told to run alongside.
|
|
142
|
+
//
|
|
143
|
+
// Holding, not merely being active: an active `Gesture.Native()` holds
|
|
144
|
+
// nothing on purpose, so a pan beside it still has a lock to take.
|
|
145
|
+
const held = recorded.some((other) => other !== participant &&
|
|
146
|
+
entries.get(other)?.active === true &&
|
|
147
|
+
other.holdsResponder());
|
|
148
|
+
participant.authorize(!held);
|
|
149
|
+
};
|
|
150
|
+
const tryActivate = (participant) => {
|
|
151
|
+
const entry = live(participant);
|
|
152
|
+
if (entry === null || entry.active) {
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
// Waiting for a gesture that already ENDED is not waiting, it is losing:
|
|
156
|
+
// the thing this one was deferring to actually happened.
|
|
157
|
+
if (recorded.some((other) => shouldWaitForOther(participant, other) &&
|
|
158
|
+
entries.get(other)?.finishedAs === GESTURE_STATE.END)) {
|
|
159
|
+
unpark(participant);
|
|
160
|
+
participant.cancel();
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
// Rule 1: park in BEGAN, holding nothing, until the other one finishes.
|
|
164
|
+
// This is what `requireExternalGestureToFail` IS.
|
|
165
|
+
if (recorded.some((other) => live(other) !== null && shouldWaitForOther(participant, other))) {
|
|
166
|
+
park(participant);
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
if (blockedFromActivating(participant)) {
|
|
170
|
+
unpark(participant);
|
|
171
|
+
participant.cancel();
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
makeActive(participant);
|
|
175
|
+
};
|
|
176
|
+
const activated = (participant) => {
|
|
177
|
+
const entry = entries.get(participant);
|
|
178
|
+
if (entry === undefined) {
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
entry.active = true;
|
|
182
|
+
unpark(participant);
|
|
183
|
+
// Upstream cancels the losers inside `makeActive`, because activation is
|
|
184
|
+
// synchronous there. Here it is not always: a gesture that has to take the
|
|
185
|
+
// responder first becomes ACTIVE only when the negotiation grants it, and
|
|
186
|
+
// an ancestor can still win. Cancelling from the moment the gesture really
|
|
187
|
+
// is ACTIVE is the same rule applied at the only moment it is true.
|
|
188
|
+
for (let i = recorded.length - 1; i >= 0; i -= 1) {
|
|
189
|
+
const other = recorded[i];
|
|
190
|
+
if (other !== participant && live(other) !== null) {
|
|
191
|
+
if (shouldBeCancelledBy(other, participant)) {
|
|
192
|
+
other.cancel();
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
};
|
|
197
|
+
const finished = (participant, outcome) => {
|
|
198
|
+
const entry = entries.get(participant);
|
|
199
|
+
if (entry === undefined) {
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
entry.finishedAs = outcome;
|
|
203
|
+
entry.active = false;
|
|
204
|
+
unpark(participant);
|
|
205
|
+
// Rule 4: FAILED or CANCELLED releases the waiters, END cancels them.
|
|
206
|
+
for (const other of [...awaiting]) {
|
|
207
|
+
if (!shouldWaitForOther(other, participant)) {
|
|
208
|
+
continue;
|
|
209
|
+
}
|
|
210
|
+
unpark(other);
|
|
211
|
+
if (outcome === GESTURE_STATE.END) {
|
|
212
|
+
other.cancel();
|
|
213
|
+
continue;
|
|
214
|
+
}
|
|
215
|
+
tryActivate(other);
|
|
216
|
+
}
|
|
217
|
+
};
|
|
218
|
+
return {
|
|
219
|
+
relations,
|
|
220
|
+
record: (participant) => {
|
|
221
|
+
// A press is the only thing that puts a gesture back into play, so it is
|
|
222
|
+
// also where the previous interaction's leftovers go.
|
|
223
|
+
sweepFinished();
|
|
224
|
+
const existing = entries.get(participant);
|
|
225
|
+
if (existing !== undefined) {
|
|
226
|
+
existing.active = false;
|
|
227
|
+
existing.awaiting = false;
|
|
228
|
+
existing.finishedAs = null;
|
|
229
|
+
existing.activationIndex = Number.MAX_SAFE_INTEGER;
|
|
230
|
+
drop(awaiting, participant);
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
entries.set(participant, {
|
|
234
|
+
active: false,
|
|
235
|
+
awaiting: false,
|
|
236
|
+
activationIndex: Number.MAX_SAFE_INTEGER,
|
|
237
|
+
finishedAs: null,
|
|
238
|
+
});
|
|
239
|
+
recorded.push(participant);
|
|
240
|
+
},
|
|
241
|
+
forget: (participant) => {
|
|
242
|
+
entries.delete(participant);
|
|
243
|
+
drop(recorded, participant);
|
|
244
|
+
drop(awaiting, participant);
|
|
245
|
+
relations.drop(participant.tag);
|
|
246
|
+
},
|
|
247
|
+
tryActivate,
|
|
248
|
+
activated,
|
|
249
|
+
finished,
|
|
250
|
+
interactionLost: (participant) => {
|
|
251
|
+
for (const other of [...recorded]) {
|
|
252
|
+
if (other !== participant && entries.get(other)?.active === true) {
|
|
253
|
+
other.cancel();
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
},
|
|
257
|
+
isAwaiting: (participant) => entries.get(participant)?.awaiting === true,
|
|
258
|
+
activeTags: () => recorded
|
|
259
|
+
.filter((participant) => entries.get(participant)?.active === true)
|
|
260
|
+
.map((participant) => participant.tag),
|
|
261
|
+
reset: () => {
|
|
262
|
+
entries.clear();
|
|
263
|
+
recorded.length = 0;
|
|
264
|
+
awaiting.length = 0;
|
|
265
|
+
activationIndex = 0;
|
|
266
|
+
relations.reset();
|
|
267
|
+
},
|
|
268
|
+
};
|
|
269
|
+
};
|
|
270
|
+
/**
|
|
271
|
+
* The process-wide loop, which is the right scope for it and needs saying
|
|
272
|
+
* because the responder lock's scope is the question slice 3 had to answer.
|
|
273
|
+
*
|
|
274
|
+
* This registry has NO tree knowledge at all: it is keyed by handler tag and
|
|
275
|
+
* a gesture enters it only when the interaction's pointer reaches it. That is
|
|
276
|
+
* what makes the islands answer simple — see docs/api.md, "Relations across
|
|
277
|
+
* `Root`s". A relation naming a gesture in another `Root` is expressible and
|
|
278
|
+
* simply never has an occasion to apply, because a gesture in another `Root`
|
|
279
|
+
* is never recorded during this interaction. In particular it does not
|
|
280
|
+
* deadlock: parking only ever happens against a gesture that is live in the
|
|
281
|
+
* interaction under way.
|
|
282
|
+
*/
|
|
283
|
+
export const gestureOrchestrator = createOrchestrator();
|
|
284
|
+
//# sourceMappingURL=orchestrator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"orchestrator.js","sourceRoot":"","sources":["../../src/gesture-handler-compat/orchestrator.ts"],"names":[],"mappings":"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","sourcesContent":["// The activation loop: one place where every gesture in an interaction asks\n// whether it may become active, and one place where an activation cancels\n// everything it excludes.\n//\n// THE SECOND LOCK. The responder lock (src/responder) keeps its one job —\n// this interaction belongs to React Native, one holder, one irrevocable GTK\n// `CLAIMED` on the source. This loop is the second lock, at the second level,\n// and it is JS-only by construction: it imports nothing from the platform,\n// takes no widget, and never makes GTK claim anything. Every relation is\n// resolved here BEFORE the responder lock is asked for, which is what makes\n// the model honest given that GTK's `CLAIMED` cannot be taken back.\n//\n// Concretely: the FIRST gesture to activate in an interaction asks for the\n// responder and becomes ACTIVE on the grant, exactly as slice 1 did. A gesture\n// that activates alongside it — the only way that can happen is a simultaneous\n// relation, see `shouldBeCancelledBy` — does NOT ask, because the interaction\n// is already ours and there is nothing left to tell GTK. It becomes ACTIVE in\n// this registry and is driven from the touch props, which fire regardless of\n// responder status.\n//\n// Restated from behaviour, not transcribed: docs/research/gesture-detector.md,\n// \"Probe 2: what the orchestrator actually guarantees\", has the specification\n// this implements and the five rules it has to honour.\nimport { createRelationRegistry, type RelationRegistry } from \"./relations\"\nimport { GESTURE_STATE, type GestureStateValue } from \"./types\"\n\n/** One mounted recognizer, as the loop sees it. */\nexport type Participant = {\n readonly tag: number\n /**\n * The recognizer kind.\n *\n * The loop reads exactly one thing off it — whether this is\n * `Gesture.Native()` — because that is the single rule the relation\n * registry contributes to cancellation, and it is why `Native` is special\n * rather than just another recognizer: an ALREADY ACTIVE or parked gesture\n * is cancelled by nothing except an active native one, and a gesture that\n * wants to activate beside one is refused by it. `@gorhom/bottom-sheet`'s\n * scrollable is the shape that needs both halves.\n */\n readonly kind: string\n /**\n * Cleared to become ACTIVE.\n *\n * `needsResponder` is false when another participant already holds the\n * interaction, and it is where the two locks meet: a gesture told `false`\n * activates in JS without touching the responder lock, which is the only\n * way `Simultaneous` can be expressed on a single-holder lock.\n */\n authorize: (needsResponder: boolean) => void\n /**\n * Whether this gesture is holding the responder right now.\n *\n * Asked rather than assumed, because \"is anything else active\" is the wrong\n * question and `Gesture.Native()` is what makes the difference visible: it\n * is ACTIVE and it deliberately holds nothing, so a pan activating beside it\n * still has an interaction to take. Nor is it a static per-kind fact — a\n * `Pan` that lost the lock to a simultaneous partner is a claiming kind that\n * does not hold it either.\n */\n holdsResponder: () => boolean\n /** Lost the arbitration: end now, unsuccessfully. */\n cancel: () => void\n}\n\ntype Entry = {\n active: boolean\n awaiting: boolean\n /** Upstream's `activationIndex`: the order gestures took their turn in. */\n activationIndex: number\n /** END / FAILED / CANCELLED once it is over, null while it is live. */\n finishedAs: GestureStateValue | null\n}\n\nexport type Orchestrator = {\n readonly relations: RelationRegistry\n /** This gesture reached BEGAN and is part of the interaction under way. */\n record: (participant: Participant) => void\n /** This gesture unmounted. Drops its relations with it. */\n forget: (participant: Participant) => void\n /** This gesture's own criteria are met; may it activate? */\n tryActivate: (participant: Participant) => void\n /** It really is ACTIVE now — the moment mutual exclusion is enforced. */\n activated: (participant: Participant) => void\n finished: (participant: Participant, outcome: GestureStateValue) => void\n /**\n * The participant holding the responder lost it to something outside this\n * registry — an ancestor took the interaction, or the window went away.\n */\n interactionLost: (participant: Participant) => void\n /** Whether the loop is holding this one in BEGAN, waiting on another. */\n isAwaiting: (participant: Participant) => boolean\n /** Every tag ACTIVE right now. `Simultaneous` is a claim about this list. */\n activeTags: () => number[]\n reset: () => void\n}\n\nexport const createOrchestrator = (\n relations: RelationRegistry = createRelationRegistry(),\n): Orchestrator => {\n const entries = new Map<Participant, Entry>()\n /** Recording order, because the broadcast cancel walks it backwards. */\n const recorded: Participant[] = []\n const awaiting: Participant[] = []\n let activationIndex = 0\n\n const isFinished = (entry: Entry): boolean => entry.finishedAs !== null\n\n const live = (participant: Participant): Entry | null => {\n const entry = entries.get(participant)\n return entry === undefined || isFinished(entry) ? null : entry\n }\n\n const drop = (list: Participant[], participant: Participant): void => {\n const index = list.indexOf(participant)\n if (index >= 0) {\n list.splice(index, 1)\n }\n }\n\n const unpark = (participant: Participant): void => {\n const entry = entries.get(participant)\n if (entry) {\n entry.awaiting = false\n }\n drop(awaiting, participant)\n }\n\n /**\n * Finished gestures are kept until the next interaction starts, not dropped\n * as they end, because `tryActivate` has to be able to tell a gesture it\n * waits for that already ENDED apart from one that was never here. Upstream\n * keeps them for a microtask and sweeps; sweeping at the next press is the\n * same guarantee without a scheduler, and a press is the only moment that\n * can put a gesture back into play.\n */\n const sweepFinished = (): void => {\n for (const participant of [...recorded]) {\n const entry = entries.get(participant)\n if (entry !== undefined && isFinished(entry)) {\n entries.delete(participant)\n drop(recorded, participant)\n drop(awaiting, participant)\n }\n }\n }\n\n /**\n * A simultaneous relation in EITHER direction exempts the pair, and a\n * gesture is always simultaneous with itself. Declaring it on one side is\n * enough — upstream also copies the relation onto the other gesture, which\n * this question makes unnecessary.\n */\n const canRunSimultaneously = (\n one: Participant,\n other: Participant,\n ): boolean =>\n one === other ||\n relations.shouldRecognizeSimultaneously(one.tag, other.tag) ||\n relations.shouldRecognizeSimultaneously(other.tag, one.tag)\n\n /**\n * The two spellings of one relation: `requireExternalGestureToFail` on the\n * waiter, and `blocksExternalGesture` on the gesture being waited for.\n */\n const shouldWaitForOther = (\n participant: Participant,\n other: Participant,\n ): boolean =>\n participant !== other &&\n (relations.shouldWaitForFailure(participant.tag, other.tag) ||\n relations.blocks(other.tag, participant.tag))\n\n /**\n * The one rule the relation registry contributes: a gesture that is already\n * ACTIVE, or parked waiting for another to fail, is cancelled only by an\n * active `Native` handler.\n *\n * Upstream exempts buttons from this too (`isButton()`); the button family —\n * `RectButton`, `BaseButton` and the rest — is refused by name on this\n * platform, so there is nothing for the exemption to apply to and adding a\n * flag nobody can set would be decoration.\n */\n const cancelledByActive = (other: Participant): boolean =>\n other.kind === \"native\" && entries.get(other)?.active === true\n\n /**\n * Should `participant` be cancelled because `winner` just activated?\n *\n * MUTUAL EXCLUSION IS THE DEFAULT and a simultaneous relation is the only\n * exemption. Upstream's third branch — two handlers that share no pointer\n * and sit on different views conflict only if a tracked pointer lies inside\n * both views' bounds — has no reachable case here: there is one pointer,\n * one interaction and one fabricated touch, a gesture is recorded only when\n * that touch reaches it, so every pair of recorded gestures shares it. What\n * upstream computes in that branch, this platform knows by construction.\n */\n const shouldBeCancelledBy = (\n participant: Participant,\n winner: Participant,\n ): boolean => {\n if (canRunSimultaneously(participant, winner)) {\n return false\n }\n const entry = entries.get(participant)\n if (entry?.active === true || entry?.awaiting === true) {\n return cancelledByActive(winner)\n }\n return true\n }\n\n /** The same question asked by the gesture that wants to activate. */\n const blockedFromActivating = (participant: Participant): boolean =>\n recorded.some(\n (other) =>\n other !== participant &&\n !canRunSimultaneously(participant, other) &&\n cancelledByActive(other),\n )\n\n const park = (participant: Participant): void => {\n const entry = entries.get(participant)\n if (entry === undefined || entry.awaiting) {\n return\n }\n entry.awaiting = true\n entry.activationIndex = activationIndex\n activationIndex += 1\n awaiting.push(participant)\n }\n\n const makeActive = (participant: Participant): void => {\n const entry = entries.get(participant)\n if (entry === undefined) {\n return\n }\n entry.activationIndex = activationIndex\n activationIndex += 1\n // The interaction is already React Native's once another gesture HOLDS\n // it, and `grant()` is a no-op for the GTK claim after the first one\n // anyway. So a second gesture does not ask for the responder — it would be\n // asking for a lock that is single-holder by design, and winning it would\n // take the interaction away from the gesture it was told to run alongside.\n //\n // Holding, not merely being active: an active `Gesture.Native()` holds\n // nothing on purpose, so a pan beside it still has a lock to take.\n const held = recorded.some(\n (other) =>\n other !== participant &&\n entries.get(other)?.active === true &&\n other.holdsResponder(),\n )\n participant.authorize(!held)\n }\n\n const tryActivate = (participant: Participant): void => {\n const entry = live(participant)\n if (entry === null || entry.active) {\n return\n }\n\n // Waiting for a gesture that already ENDED is not waiting, it is losing:\n // the thing this one was deferring to actually happened.\n if (\n recorded.some(\n (other) =>\n shouldWaitForOther(participant, other) &&\n entries.get(other)?.finishedAs === GESTURE_STATE.END,\n )\n ) {\n unpark(participant)\n participant.cancel()\n return\n }\n\n // Rule 1: park in BEGAN, holding nothing, until the other one finishes.\n // This is what `requireExternalGestureToFail` IS.\n if (\n recorded.some(\n (other) =>\n live(other) !== null && shouldWaitForOther(participant, other),\n )\n ) {\n park(participant)\n return\n }\n\n if (blockedFromActivating(participant)) {\n unpark(participant)\n participant.cancel()\n return\n }\n\n makeActive(participant)\n }\n\n const activated = (participant: Participant): void => {\n const entry = entries.get(participant)\n if (entry === undefined) {\n return\n }\n entry.active = true\n unpark(participant)\n\n // Upstream cancels the losers inside `makeActive`, because activation is\n // synchronous there. Here it is not always: a gesture that has to take the\n // responder first becomes ACTIVE only when the negotiation grants it, and\n // an ancestor can still win. Cancelling from the moment the gesture really\n // is ACTIVE is the same rule applied at the only moment it is true.\n for (let i = recorded.length - 1; i >= 0; i -= 1) {\n const other = recorded[i]!\n if (other !== participant && live(other) !== null) {\n if (shouldBeCancelledBy(other, participant)) {\n other.cancel()\n }\n }\n }\n }\n\n const finished = (\n participant: Participant,\n outcome: GestureStateValue,\n ): void => {\n const entry = entries.get(participant)\n if (entry === undefined) {\n return\n }\n entry.finishedAs = outcome\n entry.active = false\n unpark(participant)\n\n // Rule 4: FAILED or CANCELLED releases the waiters, END cancels them.\n for (const other of [...awaiting]) {\n if (!shouldWaitForOther(other, participant)) {\n continue\n }\n unpark(other)\n if (outcome === GESTURE_STATE.END) {\n other.cancel()\n continue\n }\n tryActivate(other)\n }\n }\n\n return {\n relations,\n\n record: (participant) => {\n // A press is the only thing that puts a gesture back into play, so it is\n // also where the previous interaction's leftovers go.\n sweepFinished()\n const existing = entries.get(participant)\n if (existing !== undefined) {\n existing.active = false\n existing.awaiting = false\n existing.finishedAs = null\n existing.activationIndex = Number.MAX_SAFE_INTEGER\n drop(awaiting, participant)\n return\n }\n entries.set(participant, {\n active: false,\n awaiting: false,\n activationIndex: Number.MAX_SAFE_INTEGER,\n finishedAs: null,\n })\n recorded.push(participant)\n },\n\n forget: (participant) => {\n entries.delete(participant)\n drop(recorded, participant)\n drop(awaiting, participant)\n relations.drop(participant.tag)\n },\n\n tryActivate,\n activated,\n finished,\n\n interactionLost: (participant) => {\n for (const other of [...recorded]) {\n if (other !== participant && entries.get(other)?.active === true) {\n other.cancel()\n }\n }\n },\n\n isAwaiting: (participant) => entries.get(participant)?.awaiting === true,\n\n activeTags: () =>\n recorded\n .filter((participant) => entries.get(participant)?.active === true)\n .map((participant) => participant.tag),\n\n reset: () => {\n entries.clear()\n recorded.length = 0\n awaiting.length = 0\n activationIndex = 0\n relations.reset()\n },\n }\n}\n\n/**\n * The process-wide loop, which is the right scope for it and needs saying\n * because the responder lock's scope is the question slice 3 had to answer.\n *\n * This registry has NO tree knowledge at all: it is keyed by handler tag and\n * a gesture enters it only when the interaction's pointer reaches it. That is\n * what makes the islands answer simple — see docs/api.md, \"Relations across\n * `Root`s\". A relation naming a gesture in another `Root` is expressible and\n * simply never has an occasion to apply, because a gesture in another `Root`\n * is never recorded during this interaction. In particular it does not\n * deadlock: parking only ever happens against a gesture that is live in the\n * interaction under way.\n */\nexport const gestureOrchestrator = createOrchestrator()\n"]}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { RecognizerDecider } from "./recognizer";
|
|
2
|
+
import type { OffsetBound, RecognizerConfig } from "./types";
|
|
3
|
+
/** Upstream's default minimum travel before an unconfigured pan activates. */
|
|
4
|
+
export declare const DEFAULT_MIN_DISTANCE = 10;
|
|
5
|
+
/**
|
|
6
|
+
* A bound in its two spellings, normalised to `[low, high]`.
|
|
7
|
+
*
|
|
8
|
+
* The single-number form is DIRECTIONAL and the direction is the sign:
|
|
9
|
+
* `activeOffsetX(20)` bounds the positive side and leaves the negative side
|
|
10
|
+
* open, `activeOffsetX(-20)` the reverse. Reading it as a symmetric ±20 turns
|
|
11
|
+
* a one-way drawer into a two-way one, and does it silently.
|
|
12
|
+
*/
|
|
13
|
+
export declare const asRange: (value: OffsetBound) => [number, number];
|
|
14
|
+
/**
|
|
15
|
+
* The distance that activates an otherwise unconfigured pan.
|
|
16
|
+
*
|
|
17
|
+
* An explicit `minDistance` always wins. Otherwise a config that names its
|
|
18
|
+
* own activation criteria opts OUT of the distance rule entirely — the
|
|
19
|
+
* offsets are the criteria, and a 10px fallback underneath them would
|
|
20
|
+
* activate diagonal drags neither offset asked for.
|
|
21
|
+
*/
|
|
22
|
+
export declare const effectiveMinDistance: (config: RecognizerConfig) => number;
|
|
23
|
+
export declare const panDecider: RecognizerDecider;
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/** Upstream's default minimum travel before an unconfigured pan activates. */
|
|
2
|
+
export const DEFAULT_MIN_DISTANCE = 10;
|
|
3
|
+
/**
|
|
4
|
+
* A bound in its two spellings, normalised to `[low, high]`.
|
|
5
|
+
*
|
|
6
|
+
* The single-number form is DIRECTIONAL and the direction is the sign:
|
|
7
|
+
* `activeOffsetX(20)` bounds the positive side and leaves the negative side
|
|
8
|
+
* open, `activeOffsetX(-20)` the reverse. Reading it as a symmetric ±20 turns
|
|
9
|
+
* a one-way drawer into a two-way one, and does it silently.
|
|
10
|
+
*/
|
|
11
|
+
export const asRange = (value) => {
|
|
12
|
+
if (typeof value !== "number") {
|
|
13
|
+
return [value[0], value[1]];
|
|
14
|
+
}
|
|
15
|
+
return value < 0
|
|
16
|
+
? [value, Number.POSITIVE_INFINITY]
|
|
17
|
+
: [Number.NEGATIVE_INFINITY, value];
|
|
18
|
+
};
|
|
19
|
+
const crosses = (value, bound) => {
|
|
20
|
+
if (bound === undefined) {
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
const [low, high] = asRange(bound);
|
|
24
|
+
return value < low || value > high;
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* Whether the config names its own activation criteria.
|
|
28
|
+
*
|
|
29
|
+
* `failOffset*` is deliberately NOT in this set, and that is a correction to
|
|
30
|
+
* the spike this module grew out of. A pan configured with only
|
|
31
|
+
* `failOffsetY([-5, 5])` still activates on ordinary distance upstream;
|
|
32
|
+
* treating a failure bound as an activation criterion would pin `minDistance`
|
|
33
|
+
* at infinity and the gesture could never start at all.
|
|
34
|
+
*/
|
|
35
|
+
const hasCustomActivationCriteria = (config) => config.activeOffsetX !== undefined ||
|
|
36
|
+
config.activeOffsetY !== undefined ||
|
|
37
|
+
config.minVelocity !== undefined ||
|
|
38
|
+
config.minVelocityX !== undefined ||
|
|
39
|
+
config.minVelocityY !== undefined;
|
|
40
|
+
/**
|
|
41
|
+
* The distance that activates an otherwise unconfigured pan.
|
|
42
|
+
*
|
|
43
|
+
* An explicit `minDistance` always wins. Otherwise a config that names its
|
|
44
|
+
* own activation criteria opts OUT of the distance rule entirely — the
|
|
45
|
+
* offsets are the criteria, and a 10px fallback underneath them would
|
|
46
|
+
* activate diagonal drags neither offset asked for.
|
|
47
|
+
*/
|
|
48
|
+
export const effectiveMinDistance = (config) => {
|
|
49
|
+
if (config.minDistance !== undefined) {
|
|
50
|
+
return config.minDistance;
|
|
51
|
+
}
|
|
52
|
+
return hasCustomActivationCriteria(config)
|
|
53
|
+
? Number.POSITIVE_INFINITY
|
|
54
|
+
: DEFAULT_MIN_DISTANCE;
|
|
55
|
+
};
|
|
56
|
+
const exceeds = (value, floor) => floor !== undefined && Math.abs(value) >= floor;
|
|
57
|
+
/**
|
|
58
|
+
* Whether this pan has to be HELD before it may activate.
|
|
59
|
+
*
|
|
60
|
+
* `> 0`, not "was set": upstream's default for the option is the number `0`,
|
|
61
|
+
* and both of its own implementations guard on `activateAfterLongPress > 0`
|
|
62
|
+
* (`src/web/handlers/PanGestureHandler.ts`, `PanGestureHandler.kt`). So
|
|
63
|
+
* passing `0` explicitly means "no hold at all" there, and reading it as
|
|
64
|
+
* "a hold of zero" is not the same thing: it arms a zero-delay timer and,
|
|
65
|
+
* until that timer has fired, the failure test below turns any movement past
|
|
66
|
+
* the default minimum into a DEAD gesture. `react-native-reanimated-dnd`'s
|
|
67
|
+
* `SortableGrid` takes an `activationDelay` prop and hands it straight
|
|
68
|
+
* through, so `activationDelay={0}` — the natural thing for a desktop app
|
|
69
|
+
* whose users drag with a mouse rather than long-pressing — used to produce a
|
|
70
|
+
* grid that could not be dragged at all.
|
|
71
|
+
*/
|
|
72
|
+
const holdsBeforeActivating = (config) => config.activateAfterLongPress !== undefined &&
|
|
73
|
+
config.activateAfterLongPress > 0;
|
|
74
|
+
export const panDecider = {
|
|
75
|
+
kind: "pan",
|
|
76
|
+
/** Only `activateAfterLongPress` arms one; an ordinary pan has no clock. */
|
|
77
|
+
timer: (config) => holdsBeforeActivating(config)
|
|
78
|
+
? { delay: config.activateAfterLongPress, elapsed: "activate" }
|
|
79
|
+
: null,
|
|
80
|
+
shouldFail: (view, config) => {
|
|
81
|
+
if (holdsBeforeActivating(config) && !view.timerElapsed) {
|
|
82
|
+
// Before the timer, travelling further than the default minimum says
|
|
83
|
+
// this was a drag rather than a hold, and the gesture is done. Past the
|
|
84
|
+
// timer the press has matured and the ordinary bounds take over.
|
|
85
|
+
return (Math.hypot(view.translationX, view.translationY) > DEFAULT_MIN_DISTANCE);
|
|
86
|
+
}
|
|
87
|
+
return (crosses(view.translationX, config.failOffsetX) ||
|
|
88
|
+
crosses(view.translationY, config.failOffsetY));
|
|
89
|
+
},
|
|
90
|
+
shouldActivate: (view, config) => {
|
|
91
|
+
// The timer is an activation criterion in its own right, and the only one
|
|
92
|
+
// that does not come from the pointer. Checked first because the pointer
|
|
93
|
+
// has not necessarily moved at all when it fires.
|
|
94
|
+
if (view.timerElapsed) {
|
|
95
|
+
return true;
|
|
96
|
+
}
|
|
97
|
+
if (crosses(view.translationX, config.activeOffsetX) ||
|
|
98
|
+
crosses(view.translationY, config.activeOffsetY)) {
|
|
99
|
+
return true;
|
|
100
|
+
}
|
|
101
|
+
if (exceeds(view.velocityX, config.minVelocityX) ||
|
|
102
|
+
exceeds(view.velocityY, config.minVelocityY) ||
|
|
103
|
+
exceeds(Math.hypot(view.velocityX, view.velocityY), config.minVelocity)) {
|
|
104
|
+
return true;
|
|
105
|
+
}
|
|
106
|
+
const minDistance = effectiveMinDistance(config);
|
|
107
|
+
if (!Number.isFinite(minDistance)) {
|
|
108
|
+
return false;
|
|
109
|
+
}
|
|
110
|
+
// Non-strict, where the failure test above is strict: a translation
|
|
111
|
+
// sitting exactly on a bound activates rather than fails. That asymmetry
|
|
112
|
+
// is upstream's.
|
|
113
|
+
return Math.hypot(view.translationX, view.translationY) >= minDistance;
|
|
114
|
+
},
|
|
115
|
+
};
|
|
116
|
+
//# sourceMappingURL=pan.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pan.js","sourceRoot":"","sources":["../../src/gesture-handler-compat/pan.ts"],"names":[],"mappings":"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","sourcesContent":["// Pan's two predicates, and the defaults that decide when they are consulted.\n//\n// The arithmetic is restated from upstream's BEHAVIOUR, not transcribed from\n// its source: activation when the translation crosses an `activeOffset*`\n// bound, or exceeds a velocity floor, or covers `minDistance`; failure when\n// it crosses a `failOffset*` bound; and, with `activateAfterLongPress` set,\n// movement past the default minimum before the timer fires is a failure,\n// because that press was a drag and not a hold.\nimport type {\n RecognizerDecider,\n RecognizerTimer,\n RecognizerView,\n} from \"./recognizer\"\nimport type { OffsetBound, RecognizerConfig } from \"./types\"\n\n/** Upstream's default minimum travel before an unconfigured pan activates. */\nexport const DEFAULT_MIN_DISTANCE = 10\n\n/**\n * A bound in its two spellings, normalised to `[low, high]`.\n *\n * The single-number form is DIRECTIONAL and the direction is the sign:\n * `activeOffsetX(20)` bounds the positive side and leaves the negative side\n * open, `activeOffsetX(-20)` the reverse. Reading it as a symmetric ±20 turns\n * a one-way drawer into a two-way one, and does it silently.\n */\nexport const asRange = (value: OffsetBound): [number, number] => {\n if (typeof value !== \"number\") {\n return [value[0], value[1]]\n }\n return value < 0\n ? [value, Number.POSITIVE_INFINITY]\n : [Number.NEGATIVE_INFINITY, value]\n}\n\nconst crosses = (value: number, bound: OffsetBound | undefined): boolean => {\n if (bound === undefined) {\n return false\n }\n const [low, high] = asRange(bound)\n return value < low || value > high\n}\n\n/**\n * Whether the config names its own activation criteria.\n *\n * `failOffset*` is deliberately NOT in this set, and that is a correction to\n * the spike this module grew out of. A pan configured with only\n * `failOffsetY([-5, 5])` still activates on ordinary distance upstream;\n * treating a failure bound as an activation criterion would pin `minDistance`\n * at infinity and the gesture could never start at all.\n */\nconst hasCustomActivationCriteria = (config: RecognizerConfig): boolean =>\n config.activeOffsetX !== undefined ||\n config.activeOffsetY !== undefined ||\n config.minVelocity !== undefined ||\n config.minVelocityX !== undefined ||\n config.minVelocityY !== undefined\n\n/**\n * The distance that activates an otherwise unconfigured pan.\n *\n * An explicit `minDistance` always wins. Otherwise a config that names its\n * own activation criteria opts OUT of the distance rule entirely — the\n * offsets are the criteria, and a 10px fallback underneath them would\n * activate diagonal drags neither offset asked for.\n */\nexport const effectiveMinDistance = (config: RecognizerConfig): number => {\n if (config.minDistance !== undefined) {\n return config.minDistance\n }\n return hasCustomActivationCriteria(config)\n ? Number.POSITIVE_INFINITY\n : DEFAULT_MIN_DISTANCE\n}\n\nconst exceeds = (value: number, floor: number | undefined): boolean =>\n floor !== undefined && Math.abs(value) >= floor\n\n/**\n * Whether this pan has to be HELD before it may activate.\n *\n * `> 0`, not \"was set\": upstream's default for the option is the number `0`,\n * and both of its own implementations guard on `activateAfterLongPress > 0`\n * (`src/web/handlers/PanGestureHandler.ts`, `PanGestureHandler.kt`). So\n * passing `0` explicitly means \"no hold at all\" there, and reading it as\n * \"a hold of zero\" is not the same thing: it arms a zero-delay timer and,\n * until that timer has fired, the failure test below turns any movement past\n * the default minimum into a DEAD gesture. `react-native-reanimated-dnd`'s\n * `SortableGrid` takes an `activationDelay` prop and hands it straight\n * through, so `activationDelay={0}` — the natural thing for a desktop app\n * whose users drag with a mouse rather than long-pressing — used to produce a\n * grid that could not be dragged at all.\n */\nconst holdsBeforeActivating = (config: RecognizerConfig): boolean =>\n config.activateAfterLongPress !== undefined &&\n config.activateAfterLongPress > 0\n\nexport const panDecider: RecognizerDecider = {\n kind: \"pan\",\n\n /** Only `activateAfterLongPress` arms one; an ordinary pan has no clock. */\n timer: (config: RecognizerConfig): RecognizerTimer | null =>\n holdsBeforeActivating(config)\n ? { delay: config.activateAfterLongPress!, elapsed: \"activate\" }\n : null,\n\n shouldFail: (view: RecognizerView, config: RecognizerConfig): boolean => {\n if (holdsBeforeActivating(config) && !view.timerElapsed) {\n // Before the timer, travelling further than the default minimum says\n // this was a drag rather than a hold, and the gesture is done. Past the\n // timer the press has matured and the ordinary bounds take over.\n return (\n Math.hypot(view.translationX, view.translationY) > DEFAULT_MIN_DISTANCE\n )\n }\n return (\n crosses(view.translationX, config.failOffsetX) ||\n crosses(view.translationY, config.failOffsetY)\n )\n },\n\n shouldActivate: (view: RecognizerView, config: RecognizerConfig): boolean => {\n // The timer is an activation criterion in its own right, and the only one\n // that does not come from the pointer. Checked first because the pointer\n // has not necessarily moved at all when it fires.\n if (view.timerElapsed) {\n return true\n }\n if (\n crosses(view.translationX, config.activeOffsetX) ||\n crosses(view.translationY, config.activeOffsetY)\n ) {\n return true\n }\n if (\n exceeds(view.velocityX, config.minVelocityX) ||\n exceeds(view.velocityY, config.minVelocityY) ||\n exceeds(Math.hypot(view.velocityX, view.velocityY), config.minVelocity)\n ) {\n return true\n }\n const minDistance = effectiveMinDistance(config)\n if (!Number.isFinite(minDistance)) {\n return false\n }\n // Non-strict, where the failure test above is strict: a translation\n // sitting exactly on a bound activates rather than fails. That asymmetry\n // is upstream's.\n return Math.hypot(view.translationX, view.translationY) >= minDistance\n },\n}\n"]}
|