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 @@
|
|
|
1
|
+
{"version":3,"file":"style.js","sourceRoot":"","sources":["../../src/reanimated-compat/style.ts"],"names":[],"mappings":"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","sourcesContent":["// Turning a `useAnimatedStyle` updater's plain object into a style the view\n// layer can drive without React.\n//\n// The mapper returns ordinary values every frame. On the first run each\n// animatable leaf is replaced by a small animated NODE and the mapping is\n// remembered; later runs only push new values into those nodes, so a running\n// animation costs zero React renders. `src/components/animated.tsx`\n// recognises those nodes structurally and writes them straight to GTK —\n// `widget.setOpacity` for opacity, the rect store plus `queueAllocate` for\n// transforms, and a CSS provider private to the widget for colours.\n//\n// WHERE THE BOUNDARY IS NOW. Opacity, transforms and colours are driven\n// unconditionally. `flex`, every `margin*`/`padding*`, `gap`, `flexBasis` and\n// the `min*`/`max*` family are refused, and that is a refusal on measured\n// grounds rather than a gap waiting to be filled: a layout write has to go\n// through Yoga, and a pass plus its commit walk costs what the CONTAINER\n// costs, not what the animated value costs — 52 µs for a five-child\n// container, 133 µs at sixty, 496 µs at three hundred, per frame. The same\n// frame's transform write is 1.5 µs and does not grow at all.\n//\n// It is a cost argument and ONLY a cost argument, which is narrower than this\n// comment used to claim. docs/research/animated-size.md re-measured the two\n// other things that were said here: GTK re-measuring the ancestors after the\n// resize adds nothing at any tree size, and a size write cannot move the\n// window — the RN root reports a zero size request, so the toplevel never\n// re-negotiates. (An `IntrinsicRoot` mounted directly in GTK chrome is the one\n// exception, and it does change the window's request.)\n//\n// TWO CARVE-OUTS, and neither is a softening of that.\n//\n// - `top`/`left`/`right`/`bottom` on a node whose OWN `position` is\n// `\"absolute\"`, because an out-of-flow node's inset has an exact transform\n// equivalent: moving it changes nothing but where it is drawn, which is\n// what the transform path already does, so no Yoga pass is needed at all —\n// src/style/absolute-insets.ts, docs/research/absolute-insets.md.\n// - `width`/`height` where the change is CONFINED to the node that owns it.\n// There is no transform that reproduces a size change (a scale grows about\n// the centre and stretches the content instead of re-laying it out —\n// measured), so this one really does run Yoga; it just runs it rooted at\n// the animated node rather than at the tree's root, which is 6.6 µs for a\n// leaf and 23 µs with wrapped text, IDENTICAL at 5, 60 and 300 siblings —\n// src/style/animated-size.ts, docs/research/animated-size.md.\n//\n// Both rules are measured, neither is universal, and the configurations where\n// they fail are refused as loudly as `flex` is.\n//\n// The rule for everything undriveable stays what it was: it must be VISIBLE.\n// A property that changes between mapper runs but cannot be driven says so\n// once, by name — and layout properties say it in their own words, because\n// \"use translateX instead of left\" is advice and \"cannot be written\" is not.\n\nimport {\n INSET_PROPERTIES,\n insetRefusalReason,\n insetTranslation,\n} from \"../style/absolute-insets\"\nimport { SIZE_PROPERTIES } from \"../style/animated-size\"\nimport { DRIVEABLE_COLOR_PROPERTIES } from \"../style/imperative-css\"\n\nexport type StyleValue = number | string\n\nexport type StyleObject = Record<string, unknown>\n\n/** The animated node a style leaf is replaced with. */\nexport type StyleNode = {\n addListener(callback: (state: { value: StyleValue }) => void): string\n removeListener(id: string): void\n __getValue(): StyleValue\n /** @internal Publishes a new value to the view layer. */\n __push(value: StyleValue): void\n}\n\n/**\n * @internal Shared with `props.ts`: `useAnimatedProps` publishes through the\n * exact same node the style path does, because the receivers duck-type it the\n * same way (`addListener` + `__getValue`).\n */\nexport const createStyleNode = (initial: StyleValue): StyleNode => {\n let current = initial\n const listeners = new Map<string, (state: { value: StyleValue }) => void>()\n let nextListenerId = 1\n return {\n addListener(callback) {\n const id = String(nextListenerId++)\n listeners.set(id, callback)\n return id\n },\n removeListener(id) {\n listeners.delete(id)\n },\n __getValue() {\n return current\n },\n __push(value) {\n if (Object.is(value, current)) {\n return\n }\n current = value\n for (const listener of [...listeners.values()]) {\n listener({ value: current })\n }\n },\n }\n}\n\nconst isStyleValue = (value: unknown): value is StyleValue =>\n typeof value === \"number\" || typeof value === \"string\"\n\nconst OPACITY = \"opacity\"\nconst Z_INDEX = \"zIndex\"\n\nconst transformLeafKey = (index: number, part: string): string =>\n `transform.${index}.${part}`\n\ntype InsetProperty = (typeof INSET_PROPERTIES)[number]\n\nconst isInsetProperty = (property: string): property is InsetProperty =>\n (INSET_PROPERTIES as readonly string[]).includes(property)\n\nconst isSizeProperty = (property: string): boolean =>\n (SIZE_PROPERTIES as readonly string[]).includes(property)\n\n/**\n * Whether this style's `property` is one of the insets that becomes a\n * transform. A number is required rather than merely a `DimensionValue`: a\n * percentage has no fixed offset from a point base, and switching to one\n * mid-animation changes the leaf signature, which rebuilds the style and puts\n * the property back on the refusal path where it belongs.\n *\n * The updater's object is not the whole style. `style={[styles.row, animated]}`\n * is how most people write this, so `position` and the opposite edge are very\n * often in a SIBLING entry that this module never sees. When the updater says\n * nothing about `position`, the question is simply not answerable here and the\n * leaf is made optimistically — the view layer re-asks it against the\n * FLATTENED style, which is the only place the true answer exists, and warns\n * there if the answer is no (components/animated.tsx). Nothing becomes silent\n * either way; only which channel says it moves.\n */\nconst isDriveableInset = (source: StyleObject, property: string): boolean => {\n if (!isInsetProperty(property) || typeof source[property] !== \"number\") {\n return false\n }\n return source.position === undefined\n ? true\n : insetTranslation(source, property) !== null\n}\n\n// RN's layout properties, as `STYLE_PROPERTIES_CONFIG` enumerates them.\n// Enumerated by hand rather than derived by subtracting the driveable set,\n// because the point of the list is to produce a DIFFERENT message: these are\n// refused for a measured reason and have a real alternative, which\n// `borderStyle` does not.\nconst LAYOUT_PROPERTIES = new Set([\n \"aspectRatio\",\n \"borderBottomWidth\",\n \"borderEndWidth\",\n \"borderLeftWidth\",\n \"borderRightWidth\",\n \"borderStartWidth\",\n \"borderTopWidth\",\n \"borderWidth\",\n \"bottom\",\n \"columnGap\",\n \"end\",\n \"flex\",\n \"flexBasis\",\n \"flexGrow\",\n \"flexShrink\",\n \"gap\",\n \"height\",\n \"left\",\n \"margin\",\n \"marginBottom\",\n \"marginEnd\",\n \"marginHorizontal\",\n \"marginLeft\",\n \"marginRight\",\n \"marginStart\",\n \"marginTop\",\n \"marginVertical\",\n \"maxHeight\",\n \"maxWidth\",\n \"minHeight\",\n \"minWidth\",\n \"padding\",\n \"paddingBottom\",\n \"paddingEnd\",\n \"paddingHorizontal\",\n \"paddingLeft\",\n \"paddingRight\",\n \"paddingStart\",\n \"paddingTop\",\n \"paddingVertical\",\n \"right\",\n \"rowGap\",\n \"start\",\n \"top\",\n \"width\",\n])\n\n// Which transform to reach for instead, in the two kinds it comes in. The\n// distinction is not pedantry — `scaleX` was named as the replacement for\n// `width` here and it is not one, which docs/research/animated-size.md §6\n// measured on a 100×60 box widened to 260:\n//\n// - a scale grows about the view's CENTRE, so the box MOVES: x went 500 →\n// 420, where the width change kept it at 500;\n// - a scale scales the CONTENT with the box instead of re-laying it out: the\n// label inside kept its three-line, 45 px-tall layout and was drawn 2.6×\n// wide — stretched glyphs — where the width change re-wrapped it to one\n// line, 15 px tall.\n//\n// So an inset really does have a transform that reproduces it, and a size does\n// not. Offering `scaleX` as if it did sends people to a different behaviour\n// with the same name on it — which is also why a numeric `width`/`height` is\n// driven for real now and only reaches this table as a PERCENTAGE, where\n// there is no point base to lay out at.\nconst EXACT_ALTERNATIVE: Record<string, string> = {\n bottom: \"translateY\",\n end: \"translateX\",\n left: \"translateX\",\n right: \"translateX\",\n start: \"translateX\",\n top: \"translateY\",\n}\n\nconst APPROXIMATE_ALTERNATIVE: Record<string, string> = {\n height: \"scaleY\",\n width: \"scaleX\",\n}\n\n// One warning per property name per session, matching the platform's\n// warnNativeDriverIgnored policy: a 60 Hz mapper must not produce 60 lines a\n// second, and the name is the actionable part.\nconst warned = new Set<string>()\n\nconst warnUndriveable = (property: string, source: StyleObject): void => {\n if (warned.has(property)) {\n return\n }\n warned.add(property)\n const isProduction =\n typeof process !== \"undefined\" && process.env.NODE_ENV === \"production\"\n if (isProduction) {\n return\n }\n const insetReason = isInsetProperty(property)\n ? insetRefusalReason(source, property)\n : null\n if (insetReason) {\n console.warn(\n `react-native-reanimated: useAnimatedStyle changed \\`${property}\\`. This node IS absolutely ` +\n `positioned — which is normally driven at frame rate here — but ${insetReason}, so no ` +\n \"translation reproduces it and it would need a Yoga pass. Give the axis a single edge (or a \" +\n `definite size) and \\`${property}\\` animates; otherwise animate ` +\n `\\`transform: [{ ${EXACT_ALTERNATIVE[property]}: … }]\\`. The new value is applied on the next ` +\n \"React render. See docs/api.md.\",\n )\n return\n }\n if (LAYOUT_PROPERTIES.has(property)) {\n const exact = EXACT_ALTERNATIVE[property]\n const approximate = APPROXIMATE_ALTERNATIVE[property]\n console.warn(\n `react-native-reanimated: useAnimatedStyle changed \\`${property}\\`, a LAYOUT property. ` +\n \"react-native-gtkx does not drive layout at frame rate on purpose: a layout write costs a Yoga \" +\n \"pass over the container plus the commit walk that follows it, and that cost grows with the \" +\n \"CONTAINER rather than with the number of animated values — measured at 52 µs for a five-child \" +\n \"container and 496 µs at three hundred, against a transform's 1.5 µs. \" +\n (exact\n ? `Animate \\`transform: [{ ${exact}: … }]\\` instead — it reproduces the move exactly, it is ` +\n \"paint-only, it costs the same at any tree size, and it is what RN's own native driver \" +\n \"restricts you to. \"\n : approximate\n ? `The closest transform is \\`transform: [{ ${approximate}: … }]\\`, but it is NOT the same ` +\n \"thing and it is worth knowing how: a scale grows about the view's CENTRE, so the box \" +\n \"moves as it grows, and it scales the CONTENT with the box instead of re-laying it out — \" +\n \"text stretches rather than re-wrapping. Reach for it when the content can take being \" +\n \"stretched (a plain box, an image), not as a replacement. \"\n : \"Transforms are paint-only and cost the same at any tree size. \") +\n (isInsetProperty(property)\n ? `(\\`${property}\\` IS driven at frame rate on a node whose own \\`position\\` is \"absolute\", ` +\n \"where moving it is exactly a translation and touches no sibling.) \"\n : isSizeProperty(property)\n ? `(A NUMERIC \\`${property}\\` IS driven at frame rate where the change is confined to the node ` +\n \"that owns it. This one is a percentage, which has no point base to re-lay the subtree out \" +\n \"at.) \"\n : \"\") +\n \"The new value is applied on the next React render. See docs/api.md.\",\n )\n return\n }\n console.warn(\n `react-native-reanimated: useAnimatedStyle changed \\`${property}\\`, which react-native-gtkx cannot ` +\n \"write to a mounted widget without a React render. `opacity`, `transform` and colours animate at \" +\n \"frame rate here; the new value is applied on the next render instead. See docs/api.md.\",\n )\n}\n\n/** @internal Test seam: the warning is once per session by design. */\nexport const resetUndriveableWarnings = (): void => {\n warned.clear()\n}\n\n/**\n * Whether a property that finishes an animation has to be published through a\n * React render to land at all.\n *\n * Every warning on the layout path ends with \"the new value is applied on the\n * next React render\", and until now that was true only by luck: a value that\n * moves solely inside a `useAnimatedStyle` produces no render, so the last\n * frame of the animation was the last place it existed. `@gorhom/bottom-sheet`\n * is exactly that case — it bounds its scrollable with an animated `height`\n * and nothing else re-renders — and `spike/core-exports` is what found it.\n *\n * Layout properties only. `opacity`, `transform` and colours are written to\n * the widget on the frame they change, so a render at the end would be a\n * render for nothing; a `width`/`height` the driven-size rule ACCEPTS is on\n * screen at the right size already, and the render is what puts the same value\n * into Yoga so the committed layout and the override agree.\n *\n * At the end of the animation AND on a cadence while it runs, which is the\n * correction §10 made to §9: the settle alone kept the promise only when the\n * animation was short and never when its target moved every frame, and\n * gorhom's sheet was blank for 1.38 s because of it. Ten renders a second at\n * the very most, against sixty frames — still the whole difference between\n * this and the naive layout write the refusal is about\n * (docs/research/animated-size.md §3, and LANDING_INTERVAL_MS in\n * ./updater-animations.ts for the numbers).\n */\nexport const settlesThroughReact = (property: string): boolean =>\n LAYOUT_PROPERTIES.has(property)\n\n/**\n * Collects the animatable leaves of a style, in the order the view layer\n * composes them (the transform array's order IS the composition order in RN).\n */\nconst leavesOf = (\n source: StyleObject,\n): { key: string; value: StyleValue }[] => {\n const leaves: { key: string; value: StyleValue }[] = []\n if (typeof source[OPACITY] === \"number\") {\n leaves.push({ key: OPACITY, value: source[OPACITY] })\n }\n // Driven for real, and by the same kind of write opacity uses: the paint\n // and pick order of the container (gtkx/bridge/view-box.ts). It has to be\n // driveable rather than merely accepted — `useSortable` puts it in the\n // style object on every frame, so a refusal here is a refusal of the whole\n // sortable-list shape.\n if (typeof source[Z_INDEX] === \"number\") {\n leaves.push({ key: Z_INDEX, value: source[Z_INDEX] })\n }\n for (const property of DRIVEABLE_COLOR_PROPERTIES) {\n const value = source[property]\n // Strings only. A colour is a string on this platform (RN's packed\n // integers never reach a GTK stylesheet), so a number here is not a\n // colour we can drive and belongs on the warn path with everything else.\n if (typeof value === \"string\") {\n leaves.push({ key: property, value })\n }\n }\n for (const property of INSET_PROPERTIES) {\n if (isDriveableInset(source, property)) {\n leaves.push({ key: property, value: source[property] as number })\n }\n }\n // Sizes: a number is the whole of what can be decided HERE. Whether the\n // change is confined to the node depends on the container's\n // `flexDirection`, its `alignItems` and where its own size comes from —\n // none of which is in the updater's object, and none of which is even in\n // the animated component's props. So the leaf is made optimistically and\n // the view layer, which has the layout tree, answers and warns\n // (src/style/animated-size.ts, components/animated.tsx). A percentage stays\n // on the refusal path: it has no point base to lay out at, and switching to\n // one mid-animation changes the leaf signature, which rebuilds the style.\n for (const property of SIZE_PROPERTIES) {\n if (typeof source[property] === \"number\") {\n leaves.push({ key: property, value: source[property] })\n }\n }\n if (Array.isArray(source.transform)) {\n source.transform.forEach((entry, index) => {\n if (typeof entry !== \"object\" || entry === null) {\n return\n }\n const part = Object.keys(entry)[0]\n if (part === undefined) {\n return\n }\n const value = (entry as StyleObject)[part]\n if (isStyleValue(value)) {\n leaves.push({ key: transformLeafKey(index, part), value })\n }\n })\n }\n return leaves\n}\n\nconst signatureOf = (leaves: { key: string }[]): string =>\n leaves.map((leaf) => leaf.key).join(\"|\")\n\nexport type AnimatedStyle = {\n /**\n * The object handed to `Animated.View`. Stable while an animation runs — a\n * driven frame is a write into a node, not a new style — and replaced by a\n * shallow copy exactly once per `renew()`, which is how a value that cannot\n * be driven reaches the view layer at all.\n */\n readonly style: StyleObject\n /** The nodes behind it, reusable when the style has to be rebuilt. */\n readonly nodes: ReadonlyMap<string, StyleNode>\n /**\n * Publishes a fresh updater result. Returns false when the SHAPE changed\n * (a transform entry appeared, vanished or swapped kind) and the caller\n * must rebuild — the node set no longer matches, and the bindings in the\n * view layer were made against the old one.\n */\n apply(next: StyleObject): boolean\n /**\n * Gives the style a new identity, so a render that follows actually reaches\n * the view.\n *\n * A stable object was the right default and it is still what a running\n * animation gets, but it is not enough on its own: `@gorhom/bottom-sheet`\n * wraps the view this style lands on in `memo`, and a re-render of the\n * component that OWNS the `useAnimatedStyle` stops at that boundary with\n * every prop identical. The value then exists in the style and never reaches\n * Yoga — measured, `spike/core-exports`: the sheet's content mask stayed at\n * the height of the animation's first frame while the animation itself ran\n * to 543 px.\n *\n * Called for a property the platform will not drive at frame rate when its\n * animation settles, and at most ten times a second while it runs — never\n * per frame. See LANDING_INTERVAL_MS in ./updater-animations.ts for why the\n * settle on its own was not enough.\n */\n renew(): void\n}\n\n/**\n * Builds the node-backed style. `reuse` carries nodes over from a previous\n * build so a shape change keeps the identity of the leaves that survived it,\n * which is what stops the view layer re-binding the parts that did not move.\n */\nexport const createAnimatedStyle = (\n source: StyleObject,\n reuse?: ReadonlyMap<string, StyleNode>,\n): AnimatedStyle => {\n const leaves = leavesOf(source)\n const signature = signatureOf(leaves)\n const nodes = new Map<string, StyleNode>()\n for (const leaf of leaves) {\n const existing = reuse?.get(leaf.key)\n if (existing) {\n existing.__push(leaf.value)\n nodes.set(leaf.key, existing)\n } else {\n nodes.set(leaf.key, createStyleNode(leaf.value))\n }\n }\n\n // Reassigned by `renew()` only. Every other write below — the node\n // substitutions here, and `apply`'s update of a value it cannot drive — goes\n // INTO the object currently published, so a renewed copy carries the latest\n // of everything rather than a snapshot taken when it was made.\n let style: StyleObject = { ...source }\n const opacityNode = nodes.get(OPACITY)\n if (opacityNode) {\n style[OPACITY] = opacityNode\n }\n const zIndexNode = nodes.get(Z_INDEX)\n if (zIndexNode) {\n style[Z_INDEX] = zIndexNode\n }\n for (const property of DRIVEABLE_COLOR_PROPERTIES) {\n const colorNode = nodes.get(property)\n if (colorNode) {\n style[property] = colorNode\n }\n }\n // The view layer duck-types these exactly as it does opacity and colours,\n // and turns each into a translate measured from the position Yoga committed\n // (components/animated.tsx, splitAnimated).\n for (const property of INSET_PROPERTIES) {\n const insetNode = nodes.get(property)\n if (insetNode) {\n style[property] = insetNode\n }\n }\n for (const property of SIZE_PROPERTIES) {\n const sizeNode = nodes.get(property)\n if (sizeNode) {\n style[property] = sizeNode\n }\n }\n if (Array.isArray(source.transform)) {\n style.transform = source.transform.map((entry, index) => {\n if (typeof entry !== \"object\" || entry === null) {\n return entry as unknown\n }\n const part = Object.keys(entry)[0]\n if (part === undefined) {\n return entry as unknown\n }\n const node = nodes.get(transformLeafKey(index, part))\n return node ? { [part]: node } : (entry as unknown)\n })\n }\n\n // The last values seen for everything that is NOT node-backed, so a change\n // can be reported rather than silently dropped.\n let staticSnapshot = source\n\n return {\n get style() {\n return style\n },\n nodes,\n renew() {\n style = { ...style }\n },\n apply(next) {\n const nextLeaves = leavesOf(next)\n if (signatureOf(nextLeaves) !== signature) {\n return false\n }\n for (const leaf of nextLeaves) {\n nodes.get(leaf.key)?.__push(leaf.value)\n }\n for (const key of Object.keys(next)) {\n if (key === OPACITY || key === \"transform\" || nodes.has(key)) {\n continue\n }\n if (!Object.is(next[key], staticSnapshot[key])) {\n warnUndriveable(key, next)\n // Kept up to date anyway: the next React render — whenever it\n // happens, for whatever reason — then applies the current value\n // rather than the one from mount.\n style[key] = next[key]\n }\n }\n staticSnapshot = next\n return true\n },\n }\n}\n"]}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runs `body` with the platform's frame driver replaced by one the test
|
|
3
|
+
* steps, then puts the real one back.
|
|
4
|
+
*
|
|
5
|
+
* ```tsx
|
|
6
|
+
* await withReanimatedTimer(async () => {
|
|
7
|
+
* await render(<Fading />)
|
|
8
|
+
* advanceAnimationByTime(150)
|
|
9
|
+
* expect(widget.getOpacity()).toBeCloseTo(0.5, 1)
|
|
10
|
+
* })
|
|
11
|
+
* ```
|
|
12
|
+
*
|
|
13
|
+
* Async bodies are supported, unlike upstream's — a test here has to await a
|
|
14
|
+
* render before there is anything to animate. Note that no frame is delivered
|
|
15
|
+
* while the body is awaiting: the clock only moves when the test moves it, so
|
|
16
|
+
* an `await` in the middle cannot let an animation slip forward.
|
|
17
|
+
*/
|
|
18
|
+
export declare const withReanimatedTimer: <T>(body: () => T) => T extends Promise<unknown> ? Promise<void> : void;
|
|
19
|
+
/**
|
|
20
|
+
* Moves the test clock forward, delivering one animation frame per 1/60 s of
|
|
21
|
+
* it. Everything the frame writes has already happened when this returns —
|
|
22
|
+
* see the header for the one write that has not.
|
|
23
|
+
*/
|
|
24
|
+
export declare const advanceAnimationByTime: (timeMs?: number) => void;
|
|
25
|
+
/** {@link advanceAnimationByTime} in whole frames. */
|
|
26
|
+
export declare const advanceAnimationByFrame: (count?: number) => void;
|
|
27
|
+
/** @internal Test seam: is a manual clock installed right now? */
|
|
28
|
+
export declare const isTestClockInstalled: () => boolean;
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
// Reanimated's test helpers: `withReanimatedTimer`, `advanceAnimationByTime`,
|
|
2
|
+
// `advanceAnimationByFrame`.
|
|
3
|
+
//
|
|
4
|
+
// WHY THESE EXIST HERE AT ALL. An app ported to this platform brings its own
|
|
5
|
+
// test suite, and a suite that tests animations cannot assert on one without
|
|
6
|
+
// controlling time — otherwise every case is `sleep(400)` and a prayer, which
|
|
7
|
+
// is the shape that produces the flaky test rather than the passing one.
|
|
8
|
+
// Upstream solves it by faking the timers Jest owns and by emulating frames
|
|
9
|
+
// on top of them. Nothing here is emulated: the frame driver every animation
|
|
10
|
+
// on this platform runs on is this repo's own (src/components/
|
|
11
|
+
// frame-scheduler.ts), so the honest version is to TAKE that driver for the
|
|
12
|
+
// duration of a test and step it by hand. Real animations, real widget
|
|
13
|
+
// writes, a clock the test owns.
|
|
14
|
+
//
|
|
15
|
+
// THE VIRTUAL CLOCK CONTINUES THE REAL ONE rather than starting at zero, so
|
|
16
|
+
// an animation still in flight when the helper hands the driver back sees
|
|
17
|
+
// time move forward, not backwards by half a century of monotonic
|
|
18
|
+
// microseconds. Handing back is therefore "the rest of it runs at wall
|
|
19
|
+
// speed", not "it jumps to the end".
|
|
20
|
+
//
|
|
21
|
+
// THE ONE THING THAT IS NOT SYNCHRONOUS, and it is the difference an app's
|
|
22
|
+
// tests will notice: `opacity` is a direct widget call and lands inside
|
|
23
|
+
// `advanceAnimationByTime()`, but a transform or a position goes through the
|
|
24
|
+
// rect store plus `queueAllocate()`, and GTK does that allocation on its own
|
|
25
|
+
// main loop. Assert those after yielding to the loop once (`await
|
|
26
|
+
// act(async () => {})` in a @gtkx/testing test); assert opacity straight
|
|
27
|
+
// away. `getAnimatedStyle` and `setUpTests` are refused for a related reason
|
|
28
|
+
// — see the refusals in index.tsx.
|
|
29
|
+
import { installFrameDriver, realFrameClock, } from "../components/frame-scheduler";
|
|
30
|
+
/** 60 fps, which is upstream's default and this platform's frame budget. */
|
|
31
|
+
const FRAME_MS = 1000 / 60;
|
|
32
|
+
const createManualDriver = () => {
|
|
33
|
+
let clock = realFrameClock();
|
|
34
|
+
let pending = [];
|
|
35
|
+
const deliver = () => {
|
|
36
|
+
// Taken whole: a callback that re-books (which every running animation
|
|
37
|
+
// does) must land on the NEXT frame, not on this one, or a single
|
|
38
|
+
// advance() would run an animation to completion in one tick.
|
|
39
|
+
const batch = pending;
|
|
40
|
+
pending = [];
|
|
41
|
+
for (const entry of batch) {
|
|
42
|
+
if (!entry.cancelled) {
|
|
43
|
+
entry.callback(clock);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
return {
|
|
48
|
+
now: () => clock,
|
|
49
|
+
schedule(callback) {
|
|
50
|
+
const entry = { callback, cancelled: false };
|
|
51
|
+
pending.push(entry);
|
|
52
|
+
return () => {
|
|
53
|
+
entry.cancelled = true;
|
|
54
|
+
};
|
|
55
|
+
},
|
|
56
|
+
advance(ms) {
|
|
57
|
+
let remaining = ms;
|
|
58
|
+
while (remaining > 0) {
|
|
59
|
+
const step = Math.min(FRAME_MS, remaining);
|
|
60
|
+
clock += step;
|
|
61
|
+
remaining -= step;
|
|
62
|
+
deliver();
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
};
|
|
66
|
+
};
|
|
67
|
+
let active = null;
|
|
68
|
+
const requireActive = (api) => {
|
|
69
|
+
if (active === null) {
|
|
70
|
+
throw new Error(`react-native-reanimated: ${api}() only works inside withReanimatedTimer(), which is what installs the ` +
|
|
71
|
+
"test clock. Outside it there is nothing to advance — animations are running on the GLib main loop at " +
|
|
72
|
+
"wall speed. See docs/api.md.");
|
|
73
|
+
}
|
|
74
|
+
return active;
|
|
75
|
+
};
|
|
76
|
+
/**
|
|
77
|
+
* Runs `body` with the platform's frame driver replaced by one the test
|
|
78
|
+
* steps, then puts the real one back.
|
|
79
|
+
*
|
|
80
|
+
* ```tsx
|
|
81
|
+
* await withReanimatedTimer(async () => {
|
|
82
|
+
* await render(<Fading />)
|
|
83
|
+
* advanceAnimationByTime(150)
|
|
84
|
+
* expect(widget.getOpacity()).toBeCloseTo(0.5, 1)
|
|
85
|
+
* })
|
|
86
|
+
* ```
|
|
87
|
+
*
|
|
88
|
+
* Async bodies are supported, unlike upstream's — a test here has to await a
|
|
89
|
+
* render before there is anything to animate. Note that no frame is delivered
|
|
90
|
+
* while the body is awaiting: the clock only moves when the test moves it, so
|
|
91
|
+
* an `await` in the middle cannot let an animation slip forward.
|
|
92
|
+
*/
|
|
93
|
+
export const withReanimatedTimer = (body) => {
|
|
94
|
+
if (active !== null) {
|
|
95
|
+
throw new Error("react-native-reanimated: withReanimatedTimer() is already active — nesting it would leave the second " +
|
|
96
|
+
"clock installed when the inner call returns.");
|
|
97
|
+
}
|
|
98
|
+
const driver = createManualDriver();
|
|
99
|
+
active = driver;
|
|
100
|
+
installFrameDriver(driver);
|
|
101
|
+
const restore = () => {
|
|
102
|
+
active = null;
|
|
103
|
+
installFrameDriver(null);
|
|
104
|
+
};
|
|
105
|
+
let result;
|
|
106
|
+
try {
|
|
107
|
+
result = body();
|
|
108
|
+
}
|
|
109
|
+
catch (error) {
|
|
110
|
+
restore();
|
|
111
|
+
throw error;
|
|
112
|
+
}
|
|
113
|
+
if (result instanceof Promise) {
|
|
114
|
+
return result.then(restore, (error) => {
|
|
115
|
+
restore();
|
|
116
|
+
throw error;
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
restore();
|
|
120
|
+
return undefined;
|
|
121
|
+
};
|
|
122
|
+
/**
|
|
123
|
+
* Moves the test clock forward, delivering one animation frame per 1/60 s of
|
|
124
|
+
* it. Everything the frame writes has already happened when this returns —
|
|
125
|
+
* see the header for the one write that has not.
|
|
126
|
+
*/
|
|
127
|
+
export const advanceAnimationByTime = (timeMs = FRAME_MS) => {
|
|
128
|
+
requireActive("advanceAnimationByTime").advance(timeMs);
|
|
129
|
+
};
|
|
130
|
+
/** {@link advanceAnimationByTime} in whole frames. */
|
|
131
|
+
export const advanceAnimationByFrame = (count = 1) => {
|
|
132
|
+
requireActive("advanceAnimationByFrame").advance(count * FRAME_MS);
|
|
133
|
+
};
|
|
134
|
+
/** @internal Test seam: is a manual clock installed right now? */
|
|
135
|
+
export const isTestClockInstalled = () => active !== null;
|
|
136
|
+
//# sourceMappingURL=test-timers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"test-timers.js","sourceRoot":"","sources":["../../src/reanimated-compat/test-timers.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,6BAA6B;AAC7B,EAAE;AACF,6EAA6E;AAC7E,6EAA6E;AAC7E,8EAA8E;AAC9E,yEAAyE;AACzE,4EAA4E;AAC5E,6EAA6E;AAC7E,+DAA+D;AAC/D,4EAA4E;AAC5E,uEAAuE;AACvE,iCAAiC;AACjC,EAAE;AACF,4EAA4E;AAC5E,0EAA0E;AAC1E,kEAAkE;AAClE,uEAAuE;AACvE,qCAAqC;AACrC,EAAE;AACF,2EAA2E;AAC3E,wEAAwE;AACxE,6EAA6E;AAC7E,6EAA6E;AAC7E,kEAAkE;AAClE,yEAAyE;AACzE,6EAA6E;AAC7E,mCAAmC;AACnC,OAAO,EACL,kBAAkB,EAClB,cAAc,GAEf,MAAM,+BAA+B,CAAA;AAEtC,4EAA4E;AAC5E,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAE,CAAA;AAY1B,MAAM,kBAAkB,GAAG,GAAiB,EAAE;IAC5C,IAAI,KAAK,GAAG,cAAc,EAAE,CAAA;IAC5B,IAAI,OAAO,GAAc,EAAE,CAAA;IAE3B,MAAM,OAAO,GAAG,GAAS,EAAE;QACzB,uEAAuE;QACvE,kEAAkE;QAClE,8DAA8D;QAC9D,MAAM,KAAK,GAAG,OAAO,CAAA;QACrB,OAAO,GAAG,EAAE,CAAA;QACZ,KAAK,MAAM,KAAK,IAAI,KAAK,EAAE,CAAC;YAC1B,IAAI,CAAC,KAAK,CAAC,SAAS,EAAE,CAAC;gBACrB,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAA;YACvB,CAAC;QACH,CAAC;IACH,CAAC,CAAA;IAED,OAAO;QACL,GAAG,EAAE,GAAG,EAAE,CAAC,KAAK;QAChB,QAAQ,CAAC,QAAQ;YACf,MAAM,KAAK,GAAY,EAAE,QAAQ,EAAE,SAAS,EAAE,KAAK,EAAE,CAAA;YACrD,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;YACnB,OAAO,GAAG,EAAE;gBACV,KAAK,CAAC,SAAS,GAAG,IAAI,CAAA;YACxB,CAAC,CAAA;QACH,CAAC;QACD,OAAO,CAAC,EAAE;YACR,IAAI,SAAS,GAAG,EAAE,CAAA;YAClB,OAAO,SAAS,GAAG,CAAC,EAAE,CAAC;gBACrB,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAA;gBAC1C,KAAK,IAAI,IAAI,CAAA;gBACb,SAAS,IAAI,IAAI,CAAA;gBACjB,OAAO,EAAE,CAAA;YACX,CAAC;QACH,CAAC;KACF,CAAA;AACH,CAAC,CAAA;AAED,IAAI,MAAM,GAAwB,IAAI,CAAA;AAEtC,MAAM,aAAa,GAAG,CAAC,GAAW,EAAgB,EAAE;IAClD,IAAI,MAAM,KAAK,IAAI,EAAE,CAAC;QACpB,MAAM,IAAI,KAAK,CACb,4BAA4B,GAAG,yEAAyE;YACtG,uGAAuG;YACvG,8BAA8B,CACjC,CAAA;IACH,CAAC;IACD,OAAO,MAAM,CAAA;AACf,CAAC,CAAA;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,CACjC,IAAa,EACsC,EAAE;IACrD,IAAI,MAAM,KAAK,IAAI,EAAE,CAAC;QACpB,MAAM,IAAI,KAAK,CACb,uGAAuG;YACrG,8CAA8C,CACjD,CAAA;IACH,CAAC;IACD,MAAM,MAAM,GAAG,kBAAkB,EAAE,CAAA;IACnC,MAAM,GAAG,MAAM,CAAA;IACf,kBAAkB,CAAC,MAAM,CAAC,CAAA;IAE1B,MAAM,OAAO,GAAG,GAAS,EAAE;QACzB,MAAM,GAAG,IAAI,CAAA;QACb,kBAAkB,CAAC,IAAI,CAAC,CAAA;IAC1B,CAAC,CAAA;IAED,IAAI,MAAS,CAAA;IACb,IAAI,CAAC;QACH,MAAM,GAAG,IAAI,EAAE,CAAA;IACjB,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,EAAE,CAAA;QACT,MAAM,KAAK,CAAA;IACb,CAAC;IACD,IAAI,MAAM,YAAY,OAAO,EAAE,CAAC;QAC9B,OAAO,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,KAAc,EAAE,EAAE;YAC7C,OAAO,EAAE,CAAA;YACT,MAAM,KAAK,CAAA;QACb,CAAC,CAAsD,CAAA;IACzD,CAAC;IACD,OAAO,EAAE,CAAA;IACT,OAAO,SAA8D,CAAA;AACvE,CAAC,CAAA;AAED;;;;GAIG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAAG,CAAC,MAAM,GAAG,QAAQ,EAAQ,EAAE;IAChE,aAAa,CAAC,wBAAwB,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,CAAA;AACzD,CAAC,CAAA;AAED,sDAAsD;AACtD,MAAM,CAAC,MAAM,uBAAuB,GAAG,CAAC,KAAK,GAAG,CAAC,EAAQ,EAAE;IACzD,aAAa,CAAC,yBAAyB,CAAC,CAAC,OAAO,CAAC,KAAK,GAAG,QAAQ,CAAC,CAAA;AACpE,CAAC,CAAA;AAED,kEAAkE;AAClE,MAAM,CAAC,MAAM,oBAAoB,GAAG,GAAY,EAAE,CAAC,MAAM,KAAK,IAAI,CAAA","sourcesContent":["// Reanimated's test helpers: `withReanimatedTimer`, `advanceAnimationByTime`,\n// `advanceAnimationByFrame`.\n//\n// WHY THESE EXIST HERE AT ALL. An app ported to this platform brings its own\n// test suite, and a suite that tests animations cannot assert on one without\n// controlling time — otherwise every case is `sleep(400)` and a prayer, which\n// is the shape that produces the flaky test rather than the passing one.\n// Upstream solves it by faking the timers Jest owns and by emulating frames\n// on top of them. Nothing here is emulated: the frame driver every animation\n// on this platform runs on is this repo's own (src/components/\n// frame-scheduler.ts), so the honest version is to TAKE that driver for the\n// duration of a test and step it by hand. Real animations, real widget\n// writes, a clock the test owns.\n//\n// THE VIRTUAL CLOCK CONTINUES THE REAL ONE rather than starting at zero, so\n// an animation still in flight when the helper hands the driver back sees\n// time move forward, not backwards by half a century of monotonic\n// microseconds. Handing back is therefore \"the rest of it runs at wall\n// speed\", not \"it jumps to the end\".\n//\n// THE ONE THING THAT IS NOT SYNCHRONOUS, and it is the difference an app's\n// tests will notice: `opacity` is a direct widget call and lands inside\n// `advanceAnimationByTime()`, but a transform or a position goes through the\n// rect store plus `queueAllocate()`, and GTK does that allocation on its own\n// main loop. Assert those after yielding to the loop once (`await\n// act(async () => {})` in a @gtkx/testing test); assert opacity straight\n// away. `getAnimatedStyle` and `setUpTests` are refused for a related reason\n// — see the refusals in index.tsx.\nimport {\n installFrameDriver,\n realFrameClock,\n type FrameDriver,\n} from \"../components/frame-scheduler\"\n\n/** 60 fps, which is upstream's default and this platform's frame budget. */\nconst FRAME_MS = 1000 / 60\n\ntype Pending = {\n callback: (timeMs: number) => void\n cancelled: boolean\n}\n\ntype ManualDriver = FrameDriver & {\n /** Steps the clock, delivering one frame per elapsed frame interval. */\n advance(ms: number): void\n}\n\nconst createManualDriver = (): ManualDriver => {\n let clock = realFrameClock()\n let pending: Pending[] = []\n\n const deliver = (): void => {\n // Taken whole: a callback that re-books (which every running animation\n // does) must land on the NEXT frame, not on this one, or a single\n // advance() would run an animation to completion in one tick.\n const batch = pending\n pending = []\n for (const entry of batch) {\n if (!entry.cancelled) {\n entry.callback(clock)\n }\n }\n }\n\n return {\n now: () => clock,\n schedule(callback) {\n const entry: Pending = { callback, cancelled: false }\n pending.push(entry)\n return () => {\n entry.cancelled = true\n }\n },\n advance(ms) {\n let remaining = ms\n while (remaining > 0) {\n const step = Math.min(FRAME_MS, remaining)\n clock += step\n remaining -= step\n deliver()\n }\n },\n }\n}\n\nlet active: ManualDriver | null = null\n\nconst requireActive = (api: string): ManualDriver => {\n if (active === null) {\n throw new Error(\n `react-native-reanimated: ${api}() only works inside withReanimatedTimer(), which is what installs the ` +\n \"test clock. Outside it there is nothing to advance — animations are running on the GLib main loop at \" +\n \"wall speed. See docs/api.md.\",\n )\n }\n return active\n}\n\n/**\n * Runs `body` with the platform's frame driver replaced by one the test\n * steps, then puts the real one back.\n *\n * ```tsx\n * await withReanimatedTimer(async () => {\n * await render(<Fading />)\n * advanceAnimationByTime(150)\n * expect(widget.getOpacity()).toBeCloseTo(0.5, 1)\n * })\n * ```\n *\n * Async bodies are supported, unlike upstream's — a test here has to await a\n * render before there is anything to animate. Note that no frame is delivered\n * while the body is awaiting: the clock only moves when the test moves it, so\n * an `await` in the middle cannot let an animation slip forward.\n */\nexport const withReanimatedTimer = <T>(\n body: () => T,\n): T extends Promise<unknown> ? Promise<void> : void => {\n if (active !== null) {\n throw new Error(\n \"react-native-reanimated: withReanimatedTimer() is already active — nesting it would leave the second \" +\n \"clock installed when the inner call returns.\",\n )\n }\n const driver = createManualDriver()\n active = driver\n installFrameDriver(driver)\n\n const restore = (): void => {\n active = null\n installFrameDriver(null)\n }\n\n let result: T\n try {\n result = body()\n } catch (error) {\n restore()\n throw error\n }\n if (result instanceof Promise) {\n return result.then(restore, (error: unknown) => {\n restore()\n throw error\n }) as T extends Promise<unknown> ? Promise<void> : void\n }\n restore()\n return undefined as T extends Promise<unknown> ? Promise<void> : void\n}\n\n/**\n * Moves the test clock forward, delivering one animation frame per 1/60 s of\n * it. Everything the frame writes has already happened when this returns —\n * see the header for the one write that has not.\n */\nexport const advanceAnimationByTime = (timeMs = FRAME_MS): void => {\n requireActive(\"advanceAnimationByTime\").advance(timeMs)\n}\n\n/** {@link advanceAnimationByTime} in whole frames. */\nexport const advanceAnimationByFrame = (count = 1): void => {\n requireActive(\"advanceAnimationByFrame\").advance(count * FRAME_MS)\n}\n\n/** @internal Test seam: is a manual clock installed right now? */\nexport const isTestClockInstalled = (): boolean => active !== null\n"]}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export declare const createThreads: () => {
|
|
2
|
+
runOnUI: <A extends unknown[]>(worklet: (...args: A) => unknown) => (...args: A) => void;
|
|
3
|
+
scheduleOnUI: <A extends unknown[]>(worklet: (...args: A) => unknown, ...args: A) => void;
|
|
4
|
+
runOnJS: <A extends unknown[]>(fn: (...args: A) => unknown) => (...args: A) => void;
|
|
5
|
+
scheduleOnRN: <A extends unknown[]>(fn: (...args: A) => unknown, ...args: A) => void;
|
|
6
|
+
};
|
|
7
|
+
/**
|
|
8
|
+
* True only for a function the Babel plugin actually processed, matching
|
|
9
|
+
* upstream's `__workletHash` check. Without the plugin — which is this
|
|
10
|
+
* platform's normal case, since it never runs Babel — nothing is a worklet,
|
|
11
|
+
* and nothing here needs one: a `"worklet"` directive is an inert string.
|
|
12
|
+
*/
|
|
13
|
+
export declare const isWorkletFunction: (value: unknown) => boolean;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
export const createThreads = () => {
|
|
2
|
+
// Everything queued in one tick runs in one batch, in order — the queue is
|
|
3
|
+
// read when the timer fires, so a job pushed later in the same tick still
|
|
4
|
+
// joins it.
|
|
5
|
+
let queue = [];
|
|
6
|
+
const scheduleOnUI = (worklet, ...args) => {
|
|
7
|
+
queue.push(() => {
|
|
8
|
+
worklet(...args);
|
|
9
|
+
});
|
|
10
|
+
if (queue.length === 1) {
|
|
11
|
+
setTimeout(() => {
|
|
12
|
+
const batch = queue;
|
|
13
|
+
queue = [];
|
|
14
|
+
for (const job of batch) {
|
|
15
|
+
job();
|
|
16
|
+
}
|
|
17
|
+
}, 0);
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
const runOnUI = (worklet) => (...args) => {
|
|
21
|
+
scheduleOnUI(worklet, ...args);
|
|
22
|
+
};
|
|
23
|
+
const scheduleOnRN = (fn, ...args) => {
|
|
24
|
+
queueMicrotask(() => {
|
|
25
|
+
fn(...args);
|
|
26
|
+
});
|
|
27
|
+
};
|
|
28
|
+
const runOnJS = (fn) => (...args) => {
|
|
29
|
+
scheduleOnRN(fn, ...args);
|
|
30
|
+
};
|
|
31
|
+
return { runOnUI, scheduleOnUI, runOnJS, scheduleOnRN };
|
|
32
|
+
};
|
|
33
|
+
/**
|
|
34
|
+
* True only for a function the Babel plugin actually processed, matching
|
|
35
|
+
* upstream's `__workletHash` check. Without the plugin — which is this
|
|
36
|
+
* platform's normal case, since it never runs Babel — nothing is a worklet,
|
|
37
|
+
* and nothing here needs one: a `"worklet"` directive is an inert string.
|
|
38
|
+
*/
|
|
39
|
+
export const isWorkletFunction = (value) => typeof value === "function" &&
|
|
40
|
+
!!value.__workletHash;
|
|
41
|
+
//# sourceMappingURL=threads.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"threads.js","sourceRoot":"","sources":["../../src/reanimated-compat/threads.ts"],"names":[],"mappings":"AA+CA,MAAM,CAAC,MAAM,aAAa,GAAG,GAAG,EAAE;IAChC,2EAA2E;IAC3E,0EAA0E;IAC1E,YAAY;IACZ,IAAI,KAAK,GAAU,EAAE,CAAA;IAErB,MAAM,YAAY,GAAG,CACnB,OAAgC,EAChC,GAAG,IAAO,EACJ,EAAE;QACR,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE;YACd,OAAO,CAAC,GAAG,IAAI,CAAC,CAAA;QAClB,CAAC,CAAC,CAAA;QACF,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACvB,UAAU,CAAC,GAAG,EAAE;gBACd,MAAM,KAAK,GAAG,KAAK,CAAA;gBACnB,KAAK,GAAG,EAAE,CAAA;gBACV,KAAK,MAAM,GAAG,IAAI,KAAK,EAAE,CAAC;oBACxB,GAAG,EAAE,CAAA;gBACP,CAAC;YACH,CAAC,EAAE,CAAC,CAAC,CAAA;QACP,CAAC;IACH,CAAC,CAAA;IAED,MAAM,OAAO,GACX,CAAsB,OAAgC,EAAE,EAAE,CAC1D,CAAC,GAAG,IAAO,EAAQ,EAAE;QACnB,YAAY,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC,CAAA;IAChC,CAAC,CAAA;IAEH,MAAM,YAAY,GAAG,CACnB,EAA2B,EAC3B,GAAG,IAAO,EACJ,EAAE;QACR,cAAc,CAAC,GAAG,EAAE;YAClB,EAAE,CAAC,GAAG,IAAI,CAAC,CAAA;QACb,CAAC,CAAC,CAAA;IACJ,CAAC,CAAA;IAED,MAAM,OAAO,GACX,CAAsB,EAA2B,EAAE,EAAE,CACrD,CAAC,GAAG,IAAO,EAAQ,EAAE;QACnB,YAAY,CAAC,EAAE,EAAE,GAAG,IAAI,CAAC,CAAA;IAC3B,CAAC,CAAA;IAEH,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,OAAO,EAAE,YAAY,EAAE,CAAA;AACzD,CAAC,CAAA;AAED;;;;;GAKG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,KAAc,EAAW,EAAE,CAC3D,OAAO,KAAK,KAAK,UAAU;IAC3B,CAAC,CAAE,KAA4C,CAAC,aAAa,CAAA","sourcesContent":["// `runOnUI` / `runOnJS` — the thread hop, on a platform with one thread.\n//\n// The spike called these direct synchronous calls, on the reasoning that\n// GTK's main loop IS the JS thread so there is nowhere to schedule to. That\n// reasoning is right about the DESTINATION and wrong about the CONTRACT, and\n// the implementation corrects it: upstream's thread functions are\n// asynchronous, return void rather than the function's result, and code\n// written for Reanimated assumes they do NOT run inline —\n// `runOnJS(setState)()` inside a gesture callback is written expecting to\n// finish the callback first. Making them synchronous would re-enter in places\n// the author never allowed for.\n//\n// HOW MUCH LATER is the part that had to be corrected a second time, and it\n// is measured rather than reasoned (docs/research/dnd-hover-flicker.md).\n//\n// `scheduleOnUI` used to defer by a microtask AND a frame, copying\n// `react-native-worklets/src/threads.ts` — the file the web and\n// react-native-windows run, which posts through `requestAnimationFrame`. That\n// is the web's way of standing in for a UI runtime it has not got: rAF is the\n// only \"later\" a browser offers that lands in the frame it will paint. React\n// Native itself does no such thing. `scheduleOnUI` there posts to a real UI\n// thread, which picks the job up as soon as it is scheduled and does not wait\n// for a frame; nothing about the RN contract makes a UI hop cost a frame.\n//\n// The frame gate is not free here, because it is ASYMMETRIC: `scheduleOnRN`\n// is a microtask, so the round trip every Reanimated-shaped measurement makes\n// — `scheduleOnUI(measure)` then `scheduleOnRN(use it)` — cost one whole\n// frame. And a gesture callback on this platform runs per GdkEvent, not per\n// frame: GTK delivers motion as it arrives, so the next pan update reliably\n// beat the round trip it had just started. `react-native-reanimated-dnd`\n// re-registers its drop zones through exactly that round trip and re-reads\n// them from exactly that pan update, and lost the race on every hover change\n// — the drop zone strobed for as long as the pointer kept moving.\n//\n// So a UI hop is a TASK, not a frame. That keeps everything the contract\n// asks for and drops only the wait nothing asked for:\n//\n// - still asynchronous, so nothing re-enters its caller;\n// - still batched, so everything queued in one tick runs in one go, in\n// order — two `runOnUI` calls cannot be split;\n// - still LATER than `scheduleOnRN`'s microtask, so the relative order of a\n// UI-ward and an RN-ward hop issued in the same tick is upstream's;\n// - and it cannot starve the main loop, which a microtask could: a worklet\n// that re-arms itself is a legitimate shape, and on a single-threaded\n// platform a microtask loop would take GTK's paint and input with it.\ntype Job = () => void\n\nexport const createThreads = () => {\n // Everything queued in one tick runs in one batch, in order — the queue is\n // read when the timer fires, so a job pushed later in the same tick still\n // joins it.\n let queue: Job[] = []\n\n const scheduleOnUI = <A extends unknown[]>(\n worklet: (...args: A) => unknown,\n ...args: A\n ): void => {\n queue.push(() => {\n worklet(...args)\n })\n if (queue.length === 1) {\n setTimeout(() => {\n const batch = queue\n queue = []\n for (const job of batch) {\n job()\n }\n }, 0)\n }\n }\n\n const runOnUI =\n <A extends unknown[]>(worklet: (...args: A) => unknown) =>\n (...args: A): void => {\n scheduleOnUI(worklet, ...args)\n }\n\n const scheduleOnRN = <A extends unknown[]>(\n fn: (...args: A) => unknown,\n ...args: A\n ): void => {\n queueMicrotask(() => {\n fn(...args)\n })\n }\n\n const runOnJS =\n <A extends unknown[]>(fn: (...args: A) => unknown) =>\n (...args: A): void => {\n scheduleOnRN(fn, ...args)\n }\n\n return { runOnUI, scheduleOnUI, runOnJS, scheduleOnRN }\n}\n\n/**\n * True only for a function the Babel plugin actually processed, matching\n * upstream's `__workletHash` check. Without the plugin — which is this\n * platform's normal case, since it never runs Babel — nothing is a worklet,\n * and nothing here needs one: a `\"worklet\"` directive is an inert string.\n */\nexport const isWorkletFunction = (value: unknown): boolean =>\n typeof value === \"function\" &&\n !!(value as unknown as Record<string, unknown>).__workletHash\n"]}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Something a mapper can depend on. Implemented by shared values; kept as an
|
|
3
|
+
* interface so this module never imports the mutable, which imports back.
|
|
4
|
+
*/
|
|
5
|
+
export type Trackable = {
|
|
6
|
+
__addMapper(mapper: Mapper): void;
|
|
7
|
+
__removeMapper(mapper: Mapper): void;
|
|
8
|
+
};
|
|
9
|
+
export type Mapper = {
|
|
10
|
+
/** Re-runs the body, re-collecting dependencies. */
|
|
11
|
+
run(): void;
|
|
12
|
+
/** Detaches from every dependency. Idempotent. */
|
|
13
|
+
dispose(): void;
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* Called by a shared value's getter. Outside a mapper run this is a no-op,
|
|
17
|
+
* which is what makes `sharedValue.value` in an event handler or a React
|
|
18
|
+
* render subscribe to nothing.
|
|
19
|
+
*/
|
|
20
|
+
export declare const trackRead: (source: Trackable) => void;
|
|
21
|
+
/**
|
|
22
|
+
* Runs `body` with tracking suspended, so reads inside it register nothing.
|
|
23
|
+
*
|
|
24
|
+
* `useAnimatedReaction` needs this: upstream derives a mapper's inputs from
|
|
25
|
+
* the `prepare` worklet's closure ALONE, so a shared value that only the
|
|
26
|
+
* `react` side reads must not become a trigger. Dynamic tracking would
|
|
27
|
+
* otherwise be more eager than upstream rather than merely more precise.
|
|
28
|
+
*/
|
|
29
|
+
export declare const untracked: <T>(body: () => T) => T;
|
|
30
|
+
/**
|
|
31
|
+
* Runs `body` under tracking and keeps it subscribed to exactly the shared
|
|
32
|
+
* values that run read — no more (a value dropped between runs is
|
|
33
|
+
* unsubscribed) and no less.
|
|
34
|
+
*
|
|
35
|
+
* Re-entrancy: a mapper that writes a value it also reads would recurse
|
|
36
|
+
* forever. The guard below drops the nested re-run rather than throwing,
|
|
37
|
+
* because the outer run is still in progress and is about to publish the
|
|
38
|
+
* newer value anyway — the observable result is the same as the last write
|
|
39
|
+
* winning, which is what a synchronous observer graph has to converge on.
|
|
40
|
+
*/
|
|
41
|
+
export declare const createMapper: (body: () => void) => Mapper;
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
// Dependency tracking for mappers — the one part of Reanimated that does NOT
|
|
2
|
+
// collapse on a single-runtime platform. Something still has to re-run a
|
|
3
|
+
// mapper when a shared value it reads is written.
|
|
4
|
+
//
|
|
5
|
+
// Upstream solves it STATICALLY: its Babel plugin emits a `__closure` object
|
|
6
|
+
// listing everything the worklet captured, and `extractInputs` filters that
|
|
7
|
+
// candidate list down to the shared values. With one runtime we can do it
|
|
8
|
+
// DYNAMICALLY instead — record which shared values a mapper actually reads
|
|
9
|
+
// while it runs. That is strictly more precise (a value read only on one
|
|
10
|
+
// branch is tracked on the runs that take that branch, and dropped on the
|
|
11
|
+
// runs that do not) and it needs no build step, which matters because this
|
|
12
|
+
// platform never runs Babel: the vite path is rolldown, and the Metro path
|
|
13
|
+
// uses the app's own stock preset.
|
|
14
|
+
//
|
|
15
|
+
// The whole mechanism is one module-level variable. There is no runtime and
|
|
16
|
+
// no thread to schedule onto; a write is a synchronous function call that
|
|
17
|
+
// walks its subscribers.
|
|
18
|
+
// The mapper currently running, so a read of `.value` knows who to register.
|
|
19
|
+
let activeMapper = null;
|
|
20
|
+
/**
|
|
21
|
+
* Called by a shared value's getter. Outside a mapper run this is a no-op,
|
|
22
|
+
* which is what makes `sharedValue.value` in an event handler or a React
|
|
23
|
+
* render subscribe to nothing.
|
|
24
|
+
*/
|
|
25
|
+
export const trackRead = (source) => {
|
|
26
|
+
activeMapper?.dependencies.add(source);
|
|
27
|
+
};
|
|
28
|
+
/**
|
|
29
|
+
* Runs `body` with tracking suspended, so reads inside it register nothing.
|
|
30
|
+
*
|
|
31
|
+
* `useAnimatedReaction` needs this: upstream derives a mapper's inputs from
|
|
32
|
+
* the `prepare` worklet's closure ALONE, so a shared value that only the
|
|
33
|
+
* `react` side reads must not become a trigger. Dynamic tracking would
|
|
34
|
+
* otherwise be more eager than upstream rather than merely more precise.
|
|
35
|
+
*/
|
|
36
|
+
export const untracked = (body) => {
|
|
37
|
+
const previousMapper = activeMapper;
|
|
38
|
+
activeMapper = null;
|
|
39
|
+
try {
|
|
40
|
+
return body();
|
|
41
|
+
}
|
|
42
|
+
finally {
|
|
43
|
+
activeMapper = previousMapper;
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
/**
|
|
47
|
+
* Runs `body` under tracking and keeps it subscribed to exactly the shared
|
|
48
|
+
* values that run read — no more (a value dropped between runs is
|
|
49
|
+
* unsubscribed) and no less.
|
|
50
|
+
*
|
|
51
|
+
* Re-entrancy: a mapper that writes a value it also reads would recurse
|
|
52
|
+
* forever. The guard below drops the nested re-run rather than throwing,
|
|
53
|
+
* because the outer run is still in progress and is about to publish the
|
|
54
|
+
* newer value anyway — the observable result is the same as the last write
|
|
55
|
+
* winning, which is what a synchronous observer graph has to converge on.
|
|
56
|
+
*/
|
|
57
|
+
export const createMapper = (body) => {
|
|
58
|
+
let disposed = false;
|
|
59
|
+
let running = false;
|
|
60
|
+
const dependencies = new Set();
|
|
61
|
+
const mapper = {
|
|
62
|
+
dependencies,
|
|
63
|
+
run() {
|
|
64
|
+
if (disposed || running) {
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
running = true;
|
|
68
|
+
const previousMapper = activeMapper;
|
|
69
|
+
const previousDependencies = new Set(dependencies);
|
|
70
|
+
activeMapper = mapper;
|
|
71
|
+
dependencies.clear();
|
|
72
|
+
try {
|
|
73
|
+
body();
|
|
74
|
+
}
|
|
75
|
+
finally {
|
|
76
|
+
activeMapper = previousMapper;
|
|
77
|
+
running = false;
|
|
78
|
+
// Diffed rather than resubscribed wholesale: a mapper that reads the
|
|
79
|
+
// same values every frame must not churn its subscriptions 60 times
|
|
80
|
+
// a second.
|
|
81
|
+
for (const dependency of dependencies) {
|
|
82
|
+
if (!previousDependencies.has(dependency)) {
|
|
83
|
+
dependency.__addMapper(mapper);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
for (const dependency of previousDependencies) {
|
|
87
|
+
if (!dependencies.has(dependency)) {
|
|
88
|
+
dependency.__removeMapper(mapper);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
},
|
|
93
|
+
dispose() {
|
|
94
|
+
if (disposed) {
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
disposed = true;
|
|
98
|
+
for (const dependency of dependencies) {
|
|
99
|
+
dependency.__removeMapper(mapper);
|
|
100
|
+
}
|
|
101
|
+
dependencies.clear();
|
|
102
|
+
},
|
|
103
|
+
};
|
|
104
|
+
return mapper;
|
|
105
|
+
};
|
|
106
|
+
//# sourceMappingURL=tracking.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tracking.js","sourceRoot":"","sources":["../../src/reanimated-compat/tracking.ts"],"names":[],"mappings":"AAAA,6EAA6E;AAC7E,yEAAyE;AACzE,kDAAkD;AAClD,EAAE;AACF,6EAA6E;AAC7E,4EAA4E;AAC5E,0EAA0E;AAC1E,2EAA2E;AAC3E,yEAAyE;AACzE,0EAA0E;AAC1E,2EAA2E;AAC3E,2EAA2E;AAC3E,mCAAmC;AACnC,EAAE;AACF,4EAA4E;AAC5E,0EAA0E;AAC1E,yBAAyB;AAsBzB,6EAA6E;AAC7E,IAAI,YAAY,GAA0B,IAAI,CAAA;AAE9C;;;;GAIG;AACH,MAAM,CAAC,MAAM,SAAS,GAAG,CAAC,MAAiB,EAAQ,EAAE;IACnD,YAAY,EAAE,YAAY,CAAC,GAAG,CAAC,MAAM,CAAC,CAAA;AACxC,CAAC,CAAA;AAED;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,SAAS,GAAG,CAAI,IAAa,EAAK,EAAE;IAC/C,MAAM,cAAc,GAAG,YAAY,CAAA;IACnC,YAAY,GAAG,IAAI,CAAA;IACnB,IAAI,CAAC;QACH,OAAO,IAAI,EAAE,CAAA;IACf,CAAC;YAAS,CAAC;QACT,YAAY,GAAG,cAAc,CAAA;IAC/B,CAAC;AACH,CAAC,CAAA;AAED;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,IAAgB,EAAU,EAAE;IACvD,IAAI,QAAQ,GAAG,KAAK,CAAA;IACpB,IAAI,OAAO,GAAG,KAAK,CAAA;IACnB,MAAM,YAAY,GAAG,IAAI,GAAG,EAAa,CAAA;IAEzC,MAAM,MAAM,GAAmB;QAC7B,YAAY;QACZ,GAAG;YACD,IAAI,QAAQ,IAAI,OAAO,EAAE,CAAC;gBACxB,OAAM;YACR,CAAC;YACD,OAAO,GAAG,IAAI,CAAA;YACd,MAAM,cAAc,GAAG,YAAY,CAAA;YACnC,MAAM,oBAAoB,GAAG,IAAI,GAAG,CAAC,YAAY,CAAC,CAAA;YAClD,YAAY,GAAG,MAAM,CAAA;YACrB,YAAY,CAAC,KAAK,EAAE,CAAA;YACpB,IAAI,CAAC;gBACH,IAAI,EAAE,CAAA;YACR,CAAC;oBAAS,CAAC;gBACT,YAAY,GAAG,cAAc,CAAA;gBAC7B,OAAO,GAAG,KAAK,CAAA;gBACf,qEAAqE;gBACrE,oEAAoE;gBACpE,YAAY;gBACZ,KAAK,MAAM,UAAU,IAAI,YAAY,EAAE,CAAC;oBACtC,IAAI,CAAC,oBAAoB,CAAC,GAAG,CAAC,UAAU,CAAC,EAAE,CAAC;wBAC1C,UAAU,CAAC,WAAW,CAAC,MAAM,CAAC,CAAA;oBAChC,CAAC;gBACH,CAAC;gBACD,KAAK,MAAM,UAAU,IAAI,oBAAoB,EAAE,CAAC;oBAC9C,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,UAAU,CAAC,EAAE,CAAC;wBAClC,UAAU,CAAC,cAAc,CAAC,MAAM,CAAC,CAAA;oBACnC,CAAC;gBACH,CAAC;YACH,CAAC;QACH,CAAC;QACD,OAAO;YACL,IAAI,QAAQ,EAAE,CAAC;gBACb,OAAM;YACR,CAAC;YACD,QAAQ,GAAG,IAAI,CAAA;YACf,KAAK,MAAM,UAAU,IAAI,YAAY,EAAE,CAAC;gBACtC,UAAU,CAAC,cAAc,CAAC,MAAM,CAAC,CAAA;YACnC,CAAC;YACD,YAAY,CAAC,KAAK,EAAE,CAAA;QACtB,CAAC;KACF,CAAA;IAED,OAAO,MAAM,CAAA;AACf,CAAC,CAAA","sourcesContent":["// Dependency tracking for mappers — the one part of Reanimated that does NOT\n// collapse on a single-runtime platform. Something still has to re-run a\n// mapper when a shared value it reads is written.\n//\n// Upstream solves it STATICALLY: its Babel plugin emits a `__closure` object\n// listing everything the worklet captured, and `extractInputs` filters that\n// candidate list down to the shared values. With one runtime we can do it\n// DYNAMICALLY instead — record which shared values a mapper actually reads\n// while it runs. That is strictly more precise (a value read only on one\n// branch is tracked on the runs that take that branch, and dropped on the\n// runs that do not) and it needs no build step, which matters because this\n// platform never runs Babel: the vite path is rolldown, and the Metro path\n// uses the app's own stock preset.\n//\n// The whole mechanism is one module-level variable. There is no runtime and\n// no thread to schedule onto; a write is a synchronous function call that\n// walks its subscribers.\n\n/**\n * Something a mapper can depend on. Implemented by shared values; kept as an\n * interface so this module never imports the mutable, which imports back.\n */\nexport type Trackable = {\n __addMapper(mapper: Mapper): void\n __removeMapper(mapper: Mapper): void\n}\n\nexport type Mapper = {\n /** Re-runs the body, re-collecting dependencies. */\n run(): void\n /** Detaches from every dependency. Idempotent. */\n dispose(): void\n}\n\ntype MapperInternal = Mapper & {\n readonly dependencies: Set<Trackable>\n}\n\n// The mapper currently running, so a read of `.value` knows who to register.\nlet activeMapper: MapperInternal | null = null\n\n/**\n * Called by a shared value's getter. Outside a mapper run this is a no-op,\n * which is what makes `sharedValue.value` in an event handler or a React\n * render subscribe to nothing.\n */\nexport const trackRead = (source: Trackable): void => {\n activeMapper?.dependencies.add(source)\n}\n\n/**\n * Runs `body` with tracking suspended, so reads inside it register nothing.\n *\n * `useAnimatedReaction` needs this: upstream derives a mapper's inputs from\n * the `prepare` worklet's closure ALONE, so a shared value that only the\n * `react` side reads must not become a trigger. Dynamic tracking would\n * otherwise be more eager than upstream rather than merely more precise.\n */\nexport const untracked = <T>(body: () => T): T => {\n const previousMapper = activeMapper\n activeMapper = null\n try {\n return body()\n } finally {\n activeMapper = previousMapper\n }\n}\n\n/**\n * Runs `body` under tracking and keeps it subscribed to exactly the shared\n * values that run read — no more (a value dropped between runs is\n * unsubscribed) and no less.\n *\n * Re-entrancy: a mapper that writes a value it also reads would recurse\n * forever. The guard below drops the nested re-run rather than throwing,\n * because the outer run is still in progress and is about to publish the\n * newer value anyway — the observable result is the same as the last write\n * winning, which is what a synchronous observer graph has to converge on.\n */\nexport const createMapper = (body: () => void): Mapper => {\n let disposed = false\n let running = false\n const dependencies = new Set<Trackable>()\n\n const mapper: MapperInternal = {\n dependencies,\n run() {\n if (disposed || running) {\n return\n }\n running = true\n const previousMapper = activeMapper\n const previousDependencies = new Set(dependencies)\n activeMapper = mapper\n dependencies.clear()\n try {\n body()\n } finally {\n activeMapper = previousMapper\n running = false\n // Diffed rather than resubscribed wholesale: a mapper that reads the\n // same values every frame must not churn its subscriptions 60 times\n // a second.\n for (const dependency of dependencies) {\n if (!previousDependencies.has(dependency)) {\n dependency.__addMapper(mapper)\n }\n }\n for (const dependency of previousDependencies) {\n if (!dependencies.has(dependency)) {\n dependency.__removeMapper(mapper)\n }\n }\n }\n },\n dispose() {\n if (disposed) {\n return\n }\n disposed = true\n for (const dependency of dependencies) {\n dependency.__removeMapper(mapper)\n }\n dependencies.clear()\n },\n }\n\n return mapper\n}\n"]}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { type AnimationEngine } from "./animation";
|
|
2
|
+
/** What an updater returns: a style object, or a props object. */
|
|
3
|
+
export type UpdaterObject = Record<string, unknown>;
|
|
4
|
+
export type UpdaterAnimations = {
|
|
5
|
+
/**
|
|
6
|
+
* Resolves `source`'s animations and publishes the result. Called from the
|
|
7
|
+
* mapper, so `source` is a fresh object every time.
|
|
8
|
+
*/
|
|
9
|
+
run(source: UpdaterObject): void;
|
|
10
|
+
dispose(): void;
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* @param publish Receives the updater's object with every animated key
|
|
14
|
+
* replaced by a number. Called once per mapper run and once per animation
|
|
15
|
+
* frame.
|
|
16
|
+
* @param onSettled Called when an animation on `key` reaches its target on its
|
|
17
|
+
* own (never when it is cancelled or replaced). The style layer uses it for
|
|
18
|
+
* the properties it refuses to drive at frame rate, whose contract is that
|
|
19
|
+
* the value lands on the next React render — without it, that promise is
|
|
20
|
+
* only kept when something else happens to re-render.
|
|
21
|
+
* @param onLanding Called when `key`'s current value has to reach React
|
|
22
|
+
* without its animation having reached a target. Two occasions: while an
|
|
23
|
+
* animation runs, at most once per `LANDING_INTERVAL_MS` and only when the
|
|
24
|
+
* value has moved far enough to change a committed layout (the cadence); and
|
|
25
|
+
* when a plain number REPLACES a running animation, where upstream cancels
|
|
26
|
+
* and snaps and a refused property cannot snap without a render. The snap is
|
|
27
|
+
* deliberately not rate-limited — it is a state change rather than a step of
|
|
28
|
+
* one, it happens once per mapper run that flips the key rather than once per
|
|
29
|
+
* frame, and upstream pushes exactly the same number of `updateProps` for it.
|
|
30
|
+
* Same job as `onSettled` and the same handling at the call site; it is a
|
|
31
|
+
* separate callback because a settle and a value that has merely got to be
|
|
32
|
+
* published are different claims, and only the first one is a promise kept.
|
|
33
|
+
* Omit it and both cost nothing at all — a caller with no refused properties
|
|
34
|
+
* (`useAnimatedProps`) does.
|
|
35
|
+
*/
|
|
36
|
+
export declare const createUpdaterAnimations: (engine: AnimationEngine, publish: (resolved: UpdaterObject) => void, onSettled?: (key: string) => void, onLanding?: (key: string) => void) => UpdaterAnimations;
|