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,311 @@
|
|
|
1
|
+
// Driving a built layout-animation config against a real GTK widget.
|
|
2
|
+
//
|
|
3
|
+
// The builders in layout-animation.ts produce upstream's own
|
|
4
|
+
// `{ initialValues, animations, callback }`, keyed by style property. This
|
|
5
|
+
// file is the half that knows where each of those properties can actually go
|
|
6
|
+
// on this platform, and it is the same three doors every other animated write
|
|
7
|
+
// here uses — `gtk_widget_set_opacity`, the rect store's transform, and (new)
|
|
8
|
+
// the rect store's layout offset. No React render, no Yoga pass, no second
|
|
9
|
+
// clock: every animation is built with the platform's own engine on the
|
|
10
|
+
// platform's own frame scheduler, exactly as `withTiming` on a shared value
|
|
11
|
+
// is.
|
|
12
|
+
//
|
|
13
|
+
// WHAT IS HONOURED, and the one thing that is not:
|
|
14
|
+
//
|
|
15
|
+
// `opacity` → the widget, per frame.
|
|
16
|
+
// `transform` → the rect store's transform slot, per frame.
|
|
17
|
+
// `originX` / `originY` → the rect store's LAYOUT offset, per frame, as the
|
|
18
|
+
// distance still to travel from where the child was
|
|
19
|
+
// to where the engine put it. This is what makes
|
|
20
|
+
// `LinearTransition` a paint-only animation.
|
|
21
|
+
// `width` / `height` → applied by the layout engine immediately, not
|
|
22
|
+
// animated. `LinearTransition` always emits them,
|
|
23
|
+
// and animating a size means a Yoga pass per frame
|
|
24
|
+
// whose cost is the tree's rather than the value's
|
|
25
|
+
// (63.9 µs at five children, 496.3 µs at three
|
|
26
|
+
// hundred — docs/research/animated-colors.md). This
|
|
27
|
+
// is the same refusal `useAnimatedStyle` already
|
|
28
|
+
// makes for layout properties, and it is the reason
|
|
29
|
+
// the position is animated with a translation.
|
|
30
|
+
//
|
|
31
|
+
// Anything else is named once, per property, per session — the platform's
|
|
32
|
+
// standing rule that an undriveable property must be visible rather than
|
|
33
|
+
// dropped.
|
|
34
|
+
import { clearStoredLayoutOffset, deferDuringAllocate, setStoredLayoutOffset, setStoredTransform, } from "../components/rect-store";
|
|
35
|
+
import { parseAngle } from "../style/transform";
|
|
36
|
+
import { queueAllocate } from "../gtkx/bridge/index";
|
|
37
|
+
import { buildAnimation, isAnimationSpec, } from "./animation";
|
|
38
|
+
// Emitted by LinearTransition on every run and honoured by the layout engine
|
|
39
|
+
// on the next commit rather than per frame — see the header.
|
|
40
|
+
const SIZE_PROPERTIES = new Set(["width", "height"]);
|
|
41
|
+
const warned = new Set();
|
|
42
|
+
const warnUndriveable = (property) => {
|
|
43
|
+
if (warned.has(property)) {
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
warned.add(property);
|
|
47
|
+
const isProduction = typeof process !== "undefined" && process.env.NODE_ENV === "production";
|
|
48
|
+
if (isProduction) {
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
console.warn(`react-native-reanimated: a layout animation asked for \`${property}\`, which react-native-gtkx cannot ` +
|
|
52
|
+
"write to a mounted widget without a React render. Layout animations drive `opacity`, `transform` and " +
|
|
53
|
+
"position here. See docs/api.md.");
|
|
54
|
+
};
|
|
55
|
+
/** @internal Test seam: the warning is once per property per session. */
|
|
56
|
+
export const resetLayoutAnimationWarnings = () => {
|
|
57
|
+
warned.clear();
|
|
58
|
+
};
|
|
59
|
+
const numberOrNull = (value) => typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
60
|
+
const clamp01 = (value) => Math.min(1, Math.max(0, value));
|
|
61
|
+
/**
|
|
62
|
+
* Runs one built config against one widget.
|
|
63
|
+
*
|
|
64
|
+
* `values` is what the builder was handed, and the runtime needs it back:
|
|
65
|
+
* `originX`/`originY` animate towards `target*`, so the offset to write is
|
|
66
|
+
* the driven value minus that target.
|
|
67
|
+
*/
|
|
68
|
+
export const runLayoutAnimation = (engine, target, config, values, onEnd) => {
|
|
69
|
+
const { widget, parentWidget } = target;
|
|
70
|
+
// --- the three doors ---------------------------------------------------
|
|
71
|
+
// A `layout` transition's first write happens INSIDE a GTK allocation pass
|
|
72
|
+
// — the engine flushes there, the commit hands the new rect to the rect
|
|
73
|
+
// store, and the observer that starts this animation runs on that write —
|
|
74
|
+
// and queueing an allocation from inside one is what `deferDuringAllocate`
|
|
75
|
+
// exists to stop. Keyed on a token private to this animation so it can
|
|
76
|
+
// never displace the commit's own deferred job for the same widget.
|
|
77
|
+
const allocateToken = {};
|
|
78
|
+
// The container to re-allocate, and it is NOT always the one the caller
|
|
79
|
+
// named. An `entering` animation starts from a layout effect, and React
|
|
80
|
+
// attaches refs from the leaves up — so a view mounting in the same commit
|
|
81
|
+
// as its container sees `parentWidget` null, because the container's own
|
|
82
|
+
// ref has not been assigned yet. The widget is already PARENTED by then
|
|
83
|
+
// (the reconciler's mutation phase runs before any layout effect), so GTK
|
|
84
|
+
// itself has the answer. Without this, every transform- or position-driven
|
|
85
|
+
// entering animation wrote its frames into the rect store and never asked
|
|
86
|
+
// anyone to draw them: the first allocation picked up the initial value and
|
|
87
|
+
// the view froze there — a `ZoomIn` that mounts at scale 0 and stays.
|
|
88
|
+
let container = parentWidget;
|
|
89
|
+
const requestAllocate = () => {
|
|
90
|
+
container ??= widget.getParent();
|
|
91
|
+
const containerWidget = container;
|
|
92
|
+
if (!containerWidget) {
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
const queue = () => queueAllocate(containerWidget);
|
|
96
|
+
if (!deferDuringAllocate(allocateToken, queue)) {
|
|
97
|
+
queue();
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
// The live transform array, mutated in place and re-published whole, the
|
|
101
|
+
// same shape src/components/animated.tsx keeps for a driven style.
|
|
102
|
+
let transformParts = [];
|
|
103
|
+
const flushTransform = () => {
|
|
104
|
+
setStoredTransform(widget, transformParts);
|
|
105
|
+
requestAllocate();
|
|
106
|
+
};
|
|
107
|
+
let offsetX = 0;
|
|
108
|
+
let offsetY = 0;
|
|
109
|
+
let hasOffset = false;
|
|
110
|
+
const flushOffset = () => {
|
|
111
|
+
hasOffset = true;
|
|
112
|
+
setStoredLayoutOffset(widget, offsetX, offsetY);
|
|
113
|
+
requestAllocate();
|
|
114
|
+
};
|
|
115
|
+
const opacityChannel = {
|
|
116
|
+
apply(value) {
|
|
117
|
+
const numeric = numberOrNull(value);
|
|
118
|
+
if (numeric !== null) {
|
|
119
|
+
widget.setOpacity(clamp01(numeric));
|
|
120
|
+
}
|
|
121
|
+
},
|
|
122
|
+
start(value) {
|
|
123
|
+
return numberOrNull(value);
|
|
124
|
+
},
|
|
125
|
+
push(value) {
|
|
126
|
+
widget.setOpacity(clamp01(value));
|
|
127
|
+
},
|
|
128
|
+
};
|
|
129
|
+
const originChannel = (axis) => {
|
|
130
|
+
const targetValue = axis === "x" ? values.targetOriginX : values.targetOriginY;
|
|
131
|
+
const write = (offset) => {
|
|
132
|
+
if (axis === "x") {
|
|
133
|
+
offsetX = offset;
|
|
134
|
+
}
|
|
135
|
+
else {
|
|
136
|
+
offsetY = offset;
|
|
137
|
+
}
|
|
138
|
+
flushOffset();
|
|
139
|
+
};
|
|
140
|
+
return {
|
|
141
|
+
apply(value) {
|
|
142
|
+
const numeric = numberOrNull(value);
|
|
143
|
+
if (numeric !== null) {
|
|
144
|
+
write(numeric - targetValue);
|
|
145
|
+
}
|
|
146
|
+
},
|
|
147
|
+
start(value) {
|
|
148
|
+
return numberOrNull(value);
|
|
149
|
+
},
|
|
150
|
+
push(value) {
|
|
151
|
+
write(value - targetValue);
|
|
152
|
+
},
|
|
153
|
+
};
|
|
154
|
+
};
|
|
155
|
+
// An angle slot takes upstream's own spelling as well as a number: the
|
|
156
|
+
// catalogue emits degrees (a numeric animation cannot carry a unit), but
|
|
157
|
+
// `.withInitialValues({ transform: [{ rotate: "45deg" }] })` is written by
|
|
158
|
+
// hand and would otherwise be dropped as "not a number". `parseAngle` is
|
|
159
|
+
// the same reader src/style/transform.ts uses to build the matrix, so both
|
|
160
|
+
// spellings land on the same rotation.
|
|
161
|
+
const ANGLE_KEYS = new Set(["rotate", "rotateZ"]);
|
|
162
|
+
const transformChannel = (index, key) => {
|
|
163
|
+
const read = ANGLE_KEYS.has(key)
|
|
164
|
+
? (value) => parseAngle(value)
|
|
165
|
+
: numberOrNull;
|
|
166
|
+
return {
|
|
167
|
+
apply(value) {
|
|
168
|
+
const numeric = read(value);
|
|
169
|
+
if (numeric !== null) {
|
|
170
|
+
transformParts[index] = { [key]: numeric };
|
|
171
|
+
flushTransform();
|
|
172
|
+
}
|
|
173
|
+
},
|
|
174
|
+
start(value) {
|
|
175
|
+
return read(value);
|
|
176
|
+
},
|
|
177
|
+
push(value) {
|
|
178
|
+
transformParts[index] = { [key]: value };
|
|
179
|
+
flushTransform();
|
|
180
|
+
},
|
|
181
|
+
};
|
|
182
|
+
};
|
|
183
|
+
// --- initial values ----------------------------------------------------
|
|
184
|
+
const channelFor = (property) => {
|
|
185
|
+
if (property === "opacity") {
|
|
186
|
+
return opacityChannel;
|
|
187
|
+
}
|
|
188
|
+
if (property === "originX") {
|
|
189
|
+
return originChannel("x");
|
|
190
|
+
}
|
|
191
|
+
if (property === "originY") {
|
|
192
|
+
return originChannel("y");
|
|
193
|
+
}
|
|
194
|
+
if (SIZE_PROPERTIES.has(property)) {
|
|
195
|
+
return null;
|
|
196
|
+
}
|
|
197
|
+
warnUndriveable(property);
|
|
198
|
+
return null;
|
|
199
|
+
};
|
|
200
|
+
const eachTransformEntry = (source, visit) => {
|
|
201
|
+
if (!Array.isArray(source)) {
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
source.forEach((entry, index) => {
|
|
205
|
+
if (typeof entry !== "object" || entry === null) {
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
for (const [key, value] of Object.entries(entry)) {
|
|
209
|
+
visit(index, key, value);
|
|
210
|
+
}
|
|
211
|
+
});
|
|
212
|
+
};
|
|
213
|
+
// The transform array is seeded whole from the initial values so a static
|
|
214
|
+
// entry beside an animated one keeps its place in the composition order.
|
|
215
|
+
const initialTransform = config.initialValues.transform;
|
|
216
|
+
if (Array.isArray(initialTransform)) {
|
|
217
|
+
transformParts = initialTransform.map((entry) => typeof entry === "object" && entry !== null
|
|
218
|
+
? { ...entry }
|
|
219
|
+
: {});
|
|
220
|
+
flushTransform();
|
|
221
|
+
}
|
|
222
|
+
for (const [property, value] of Object.entries(config.initialValues)) {
|
|
223
|
+
if (property === "transform") {
|
|
224
|
+
continue;
|
|
225
|
+
}
|
|
226
|
+
channelFor(property)?.apply(value);
|
|
227
|
+
}
|
|
228
|
+
const jobs = [];
|
|
229
|
+
const collect = (property, value, channel, initial) => {
|
|
230
|
+
if (channel === null) {
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
if (!isAnimationSpec(value)) {
|
|
234
|
+
channel.apply(value);
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
const from = channel.start(initial);
|
|
238
|
+
if (from === null) {
|
|
239
|
+
warnUndriveable(property);
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
242
|
+
jobs.push({ channel, spec: value, from });
|
|
243
|
+
};
|
|
244
|
+
for (const [property, value] of Object.entries(config.animations)) {
|
|
245
|
+
if (property === "transform") {
|
|
246
|
+
eachTransformEntry(value, (index, key, entry) => {
|
|
247
|
+
const initial = transformParts[index]?.[key];
|
|
248
|
+
collect(`transform.${key}`, entry, transformChannel(index, key), initial);
|
|
249
|
+
});
|
|
250
|
+
continue;
|
|
251
|
+
}
|
|
252
|
+
collect(property, value, channelFor(property), config.initialValues[property]);
|
|
253
|
+
}
|
|
254
|
+
const running = [];
|
|
255
|
+
let settled = false;
|
|
256
|
+
let pending = jobs.length;
|
|
257
|
+
let allFinished = true;
|
|
258
|
+
const finish = () => {
|
|
259
|
+
if (settled) {
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
settled = true;
|
|
263
|
+
if (hasOffset) {
|
|
264
|
+
clearStoredLayoutOffset(widget);
|
|
265
|
+
requestAllocate();
|
|
266
|
+
}
|
|
267
|
+
config.callback?.(allFinished);
|
|
268
|
+
onEnd?.(allFinished);
|
|
269
|
+
};
|
|
270
|
+
for (const job of jobs) {
|
|
271
|
+
const driver = new engine.api.Value(job.from);
|
|
272
|
+
const listener = driver.addListener(({ value }) => {
|
|
273
|
+
job.channel.push(value);
|
|
274
|
+
});
|
|
275
|
+
const animation = buildAnimation(engine, driver, job.spec);
|
|
276
|
+
running.push({
|
|
277
|
+
stop: () => {
|
|
278
|
+
animation.stop();
|
|
279
|
+
driver.removeListener(listener);
|
|
280
|
+
},
|
|
281
|
+
});
|
|
282
|
+
animation.start((result) => {
|
|
283
|
+
driver.removeListener(listener);
|
|
284
|
+
if (!result.finished) {
|
|
285
|
+
allFinished = false;
|
|
286
|
+
}
|
|
287
|
+
pending -= 1;
|
|
288
|
+
if (pending === 0) {
|
|
289
|
+
finish();
|
|
290
|
+
}
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
if (jobs.length === 0) {
|
|
294
|
+
// Nothing to drive is still a completed animation: whoever is holding a
|
|
295
|
+
// widget for this must be released, or it is held until the timer.
|
|
296
|
+
finish();
|
|
297
|
+
}
|
|
298
|
+
return {
|
|
299
|
+
stop() {
|
|
300
|
+
if (settled) {
|
|
301
|
+
return;
|
|
302
|
+
}
|
|
303
|
+
allFinished = false;
|
|
304
|
+
for (const animation of running) {
|
|
305
|
+
animation.stop();
|
|
306
|
+
}
|
|
307
|
+
finish();
|
|
308
|
+
},
|
|
309
|
+
};
|
|
310
|
+
};
|
|
311
|
+
//# sourceMappingURL=layout-animation-runtime.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"layout-animation-runtime.js","sourceRoot":"","sources":["../../src/reanimated-compat/layout-animation-runtime.ts"],"names":[],"mappings":"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","sourcesContent":["// Driving a built layout-animation config against a real GTK widget.\n//\n// The builders in layout-animation.ts produce upstream's own\n// `{ initialValues, animations, callback }`, keyed by style property. This\n// file is the half that knows where each of those properties can actually go\n// on this platform, and it is the same three doors every other animated write\n// here uses — `gtk_widget_set_opacity`, the rect store's transform, and (new)\n// the rect store's layout offset. No React render, no Yoga pass, no second\n// clock: every animation is built with the platform's own engine on the\n// platform's own frame scheduler, exactly as `withTiming` on a shared value\n// is.\n//\n// WHAT IS HONOURED, and the one thing that is not:\n//\n// `opacity` → the widget, per frame.\n// `transform` → the rect store's transform slot, per frame.\n// `originX` / `originY` → the rect store's LAYOUT offset, per frame, as the\n// distance still to travel from where the child was\n// to where the engine put it. This is what makes\n// `LinearTransition` a paint-only animation.\n// `width` / `height` → applied by the layout engine immediately, not\n// animated. `LinearTransition` always emits them,\n// and animating a size means a Yoga pass per frame\n// whose cost is the tree's rather than the value's\n// (63.9 µs at five children, 496.3 µs at three\n// hundred — docs/research/animated-colors.md). This\n// is the same refusal `useAnimatedStyle` already\n// makes for layout properties, and it is the reason\n// the position is animated with a translation.\n//\n// Anything else is named once, per property, per session — the platform's\n// standing rule that an undriveable property must be visible rather than\n// dropped.\nimport {\n clearStoredLayoutOffset,\n deferDuringAllocate,\n setStoredLayoutOffset,\n setStoredTransform,\n} from \"../components/rect-store\"\nimport { parseAngle } from \"../style/transform\"\nimport type { TransformPart } from \"../contracts\"\nimport type { Gtk } from \"../gtkx/bridge/index\"\nimport { queueAllocate } from \"../gtkx/bridge/index\"\nimport {\n buildAnimation,\n isAnimationSpec,\n type AnimationEngine,\n type AnimationSpec,\n} from \"./animation\"\nimport type {\n BuiltLayoutAnimation,\n LayoutAnimationValues,\n} from \"./layout-animation\"\n\nexport type LayoutAnimationTarget = {\n widget: Gtk.Widget\n parentWidget: Gtk.Widget | null\n}\n\nexport type RunningLayoutAnimation = {\n /** Cuts the animation short. The callback still fires, with `false`. */\n stop(): void\n}\n\n// Emitted by LinearTransition on every run and honoured by the layout engine\n// on the next commit rather than per frame — see the header.\nconst SIZE_PROPERTIES = new Set([\"width\", \"height\"])\n\nconst warned = new Set<string>()\n\nconst warnUndriveable = (property: string): 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 console.warn(\n `react-native-reanimated: a layout animation asked for \\`${property}\\`, which react-native-gtkx cannot ` +\n \"write to a mounted widget without a React render. Layout animations drive `opacity`, `transform` and \" +\n \"position here. See docs/api.md.\",\n )\n}\n\n/** @internal Test seam: the warning is once per property per session. */\nexport const resetLayoutAnimationWarnings = (): void => {\n warned.clear()\n}\n\nconst numberOrNull = (value: unknown): number | null =>\n typeof value === \"number\" && Number.isFinite(value) ? value : null\n\ntype Channel = {\n /** Applies an immediate value from `initialValues`. */\n apply(value: unknown): void\n /** The value an animation on this property should start from. */\n start(value: unknown): number | null\n /** Pushes a driven value. */\n push(value: number): void\n}\n\nconst clamp01 = (value: number): number => Math.min(1, Math.max(0, value))\n\n/**\n * Runs one built config against one widget.\n *\n * `values` is what the builder was handed, and the runtime needs it back:\n * `originX`/`originY` animate towards `target*`, so the offset to write is\n * the driven value minus that target.\n */\nexport const runLayoutAnimation = (\n engine: AnimationEngine,\n target: LayoutAnimationTarget,\n config: BuiltLayoutAnimation,\n values: LayoutAnimationValues,\n onEnd?: (finished: boolean) => void,\n): RunningLayoutAnimation => {\n const { widget, parentWidget } = target\n\n // --- the three doors ---------------------------------------------------\n\n // A `layout` transition's first write happens INSIDE a GTK allocation pass\n // — the engine flushes there, the commit hands the new rect to the rect\n // store, and the observer that starts this animation runs on that write —\n // and queueing an allocation from inside one is what `deferDuringAllocate`\n // exists to stop. Keyed on a token private to this animation so it can\n // never displace the commit's own deferred job for the same widget.\n const allocateToken = {}\n\n // The container to re-allocate, and it is NOT always the one the caller\n // named. An `entering` animation starts from a layout effect, and React\n // attaches refs from the leaves up — so a view mounting in the same commit\n // as its container sees `parentWidget` null, because the container's own\n // ref has not been assigned yet. The widget is already PARENTED by then\n // (the reconciler's mutation phase runs before any layout effect), so GTK\n // itself has the answer. Without this, every transform- or position-driven\n // entering animation wrote its frames into the rect store and never asked\n // anyone to draw them: the first allocation picked up the initial value and\n // the view froze there — a `ZoomIn` that mounts at scale 0 and stays.\n let container: Gtk.Widget | null = parentWidget\n const requestAllocate = (): void => {\n container ??= widget.getParent() as Gtk.Widget | null\n const containerWidget = container\n if (!containerWidget) {\n return\n }\n const queue = (): void => queueAllocate(containerWidget)\n if (!deferDuringAllocate(allocateToken, queue)) {\n queue()\n }\n }\n\n // The live transform array, mutated in place and re-published whole, the\n // same shape src/components/animated.tsx keeps for a driven style.\n let transformParts: Record<string, number | string>[] = []\n const flushTransform = (): void => {\n setStoredTransform(widget, transformParts as unknown as TransformPart[])\n requestAllocate()\n }\n\n let offsetX = 0\n let offsetY = 0\n let hasOffset = false\n const flushOffset = (): void => {\n hasOffset = true\n setStoredLayoutOffset(widget, offsetX, offsetY)\n requestAllocate()\n }\n\n const opacityChannel: Channel = {\n apply(value) {\n const numeric = numberOrNull(value)\n if (numeric !== null) {\n widget.setOpacity(clamp01(numeric))\n }\n },\n start(value) {\n return numberOrNull(value)\n },\n push(value) {\n widget.setOpacity(clamp01(value))\n },\n }\n\n const originChannel = (axis: \"x\" | \"y\"): Channel => {\n const targetValue =\n axis === \"x\" ? values.targetOriginX : values.targetOriginY\n const write = (offset: number): void => {\n if (axis === \"x\") {\n offsetX = offset\n } else {\n offsetY = offset\n }\n flushOffset()\n }\n return {\n apply(value) {\n const numeric = numberOrNull(value)\n if (numeric !== null) {\n write(numeric - targetValue)\n }\n },\n start(value) {\n return numberOrNull(value)\n },\n push(value) {\n write(value - targetValue)\n },\n }\n }\n\n // An angle slot takes upstream's own spelling as well as a number: the\n // catalogue emits degrees (a numeric animation cannot carry a unit), but\n // `.withInitialValues({ transform: [{ rotate: \"45deg\" }] })` is written by\n // hand and would otherwise be dropped as \"not a number\". `parseAngle` is\n // the same reader src/style/transform.ts uses to build the matrix, so both\n // spellings land on the same rotation.\n const ANGLE_KEYS = new Set([\"rotate\", \"rotateZ\"])\n\n const transformChannel = (index: number, key: string): Channel => {\n const read = ANGLE_KEYS.has(key)\n ? (value: unknown): number | null => parseAngle(value)\n : numberOrNull\n return {\n apply(value) {\n const numeric = read(value)\n if (numeric !== null) {\n transformParts[index] = { [key]: numeric }\n flushTransform()\n }\n },\n start(value) {\n return read(value)\n },\n push(value) {\n transformParts[index] = { [key]: value }\n flushTransform()\n },\n }\n }\n\n // --- initial values ----------------------------------------------------\n\n const channelFor = (property: string): Channel | null => {\n if (property === \"opacity\") {\n return opacityChannel\n }\n if (property === \"originX\") {\n return originChannel(\"x\")\n }\n if (property === \"originY\") {\n return originChannel(\"y\")\n }\n if (SIZE_PROPERTIES.has(property)) {\n return null\n }\n warnUndriveable(property)\n return null\n }\n\n const eachTransformEntry = (\n source: unknown,\n visit: (index: number, key: string, value: unknown) => void,\n ): void => {\n if (!Array.isArray(source)) {\n return\n }\n source.forEach((entry, index) => {\n if (typeof entry !== \"object\" || entry === null) {\n return\n }\n for (const [key, value] of Object.entries(entry)) {\n visit(index, key, value)\n }\n })\n }\n\n // The transform array is seeded whole from the initial values so a static\n // entry beside an animated one keeps its place in the composition order.\n const initialTransform = config.initialValues.transform\n if (Array.isArray(initialTransform)) {\n transformParts = initialTransform.map((entry) =>\n typeof entry === \"object\" && entry !== null\n ? ({ ...entry } as Record<string, number | string>)\n : {},\n )\n flushTransform()\n }\n\n for (const [property, value] of Object.entries(config.initialValues)) {\n if (property === \"transform\") {\n continue\n }\n channelFor(property)?.apply(value)\n }\n\n // --- the animations ----------------------------------------------------\n\n type Job = { channel: Channel; spec: AnimationSpec; from: number }\n const jobs: Job[] = []\n\n const collect = (\n property: string,\n value: unknown,\n channel: Channel | null,\n initial: unknown,\n ): void => {\n if (channel === null) {\n return\n }\n if (!isAnimationSpec(value)) {\n channel.apply(value)\n return\n }\n const from = channel.start(initial)\n if (from === null) {\n warnUndriveable(property)\n return\n }\n jobs.push({ channel, spec: value, from })\n }\n\n for (const [property, value] of Object.entries(config.animations)) {\n if (property === \"transform\") {\n eachTransformEntry(value, (index, key, entry) => {\n const initial = transformParts[index]?.[key]\n collect(\n `transform.${key}`,\n entry,\n transformChannel(index, key),\n initial,\n )\n })\n continue\n }\n collect(\n property,\n value,\n channelFor(property),\n config.initialValues[property],\n )\n }\n\n const running: { stop(): void }[] = []\n let settled = false\n let pending = jobs.length\n let allFinished = true\n\n const finish = (): void => {\n if (settled) {\n return\n }\n settled = true\n if (hasOffset) {\n clearStoredLayoutOffset(widget)\n requestAllocate()\n }\n config.callback?.(allFinished)\n onEnd?.(allFinished)\n }\n\n for (const job of jobs) {\n const driver = new engine.api.Value(job.from)\n const listener = driver.addListener(({ value }) => {\n job.channel.push(value)\n })\n const animation = buildAnimation(engine, driver, job.spec)\n running.push({\n stop: () => {\n animation.stop()\n driver.removeListener(listener)\n },\n })\n animation.start((result) => {\n driver.removeListener(listener)\n if (!result.finished) {\n allFinished = false\n }\n pending -= 1\n if (pending === 0) {\n finish()\n }\n })\n }\n\n if (jobs.length === 0) {\n // Nothing to drive is still a completed animation: whoever is holding a\n // widget for this must be released, or it is held until the timer.\n finish()\n }\n\n return {\n stop() {\n if (settled) {\n return\n }\n allFinished = false\n for (const animation of running) {\n animation.stop()\n }\n finish()\n },\n }\n}\n"]}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { type ComponentProps, type ElementType, type ReactNode } from "react";
|
|
2
|
+
import type { LayoutAnimationBuilderLike } from "./layout-animation";
|
|
3
|
+
/** What `entering`, `exiting` and `layout` accept: a builder, or its class. */
|
|
4
|
+
export type LayoutAnimationProps = {
|
|
5
|
+
/** Runs once, when the component mounts. */
|
|
6
|
+
entering?: LayoutAnimationBuilderLike | false | null;
|
|
7
|
+
/**
|
|
8
|
+
* Runs when the component unmounts, on a widget the platform keeps on
|
|
9
|
+
* screen for the duration. Ignored when the component's own container is
|
|
10
|
+
* unmounting in the same commit — there would be nothing left to animate
|
|
11
|
+
* inside.
|
|
12
|
+
*/
|
|
13
|
+
exiting?: LayoutAnimationBuilderLike | false | null;
|
|
14
|
+
/** Runs whenever the layout engine commits a new position for this child. */
|
|
15
|
+
layout?: LayoutAnimationBuilderLike | false | null;
|
|
16
|
+
};
|
|
17
|
+
/** @internal Test seam: the warning is once per component per session. */
|
|
18
|
+
export declare const resetLayoutAnimationComponentWarnings: () => void;
|
|
19
|
+
/** The props a layout-animated wrapper adds on top of the component's own. */
|
|
20
|
+
export type LayoutAnimatedComponent<C extends ElementType> = (props: ComponentProps<C> & LayoutAnimationProps) => ReactNode;
|
|
21
|
+
/**
|
|
22
|
+
* Adds `entering`, `exiting` and `layout` to an animated component.
|
|
23
|
+
*
|
|
24
|
+
* The rendered output is byte-for-byte what the wrapped component produces —
|
|
25
|
+
* the wrapper is a function component with no host element of its own.
|
|
26
|
+
*/
|
|
27
|
+
export declare const withLayoutAnimations: <C extends ElementType>(component: C) => LayoutAnimatedComponent<C>;
|
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
// `entering`, `exiting` and `layout` on an animated component.
|
|
3
|
+
//
|
|
4
|
+
// The three props are added by WRAPPING, not by changing the platform's
|
|
5
|
+
// `Animated.View`: the wrapper renders the component it was given, adds no
|
|
6
|
+
// widget to the tree, and reaches the real one through the handle that
|
|
7
|
+
// component already exposes — the same seam `createAnimatedComponent` uses.
|
|
8
|
+
// That is what lets `Animated.Text` and anything through
|
|
9
|
+
// `createAnimatedComponent` take a layout animation on exactly the same
|
|
10
|
+
// terms as `Animated.View`.
|
|
11
|
+
//
|
|
12
|
+
// WHY THE WRAPPER IS WHERE `exiting` LIVES, and this is the whole reason it
|
|
13
|
+
// exists as a component rather than a hook inside the platform: React runs a
|
|
14
|
+
// deleted subtree's layout-effect cleanups from the OUTSIDE IN, and unparents
|
|
15
|
+
// its topmost widget last. A cleanup registered here — one component above
|
|
16
|
+
// the one that owns the widget — therefore runs at the only moment when the
|
|
17
|
+
// widget is still parented, its Yoga node still alive and its layout manager
|
|
18
|
+
// still attached. It hands all three to the platform's retention primitive
|
|
19
|
+
// (src/components/widget-retention.ts), which puts the widget back after the
|
|
20
|
+
// reconciler takes it out and holds everything else until the fade ends or
|
|
21
|
+
// the fallback timer fires.
|
|
22
|
+
//
|
|
23
|
+
// The other two need nothing new. `entering` is "write the initial values on
|
|
24
|
+
// mount and animate to the real ones"; `layout` is "the engine committed a
|
|
25
|
+
// different rect for this child, so walk it there from where it was" — both
|
|
26
|
+
// are snapshot-measure-animate, which is exactly what upstream's own web
|
|
27
|
+
// implementation does on its single thread.
|
|
28
|
+
import { useInsertionEffect, useLayoutEffect, useRef, useState, } from "react";
|
|
29
|
+
import { Dimensions } from "../apis/index";
|
|
30
|
+
import { Animated as PlatformAnimated } from "../components/animated";
|
|
31
|
+
import { glibScheduler } from "../components/frame-scheduler";
|
|
32
|
+
import { useHostNode } from "../components/host-node";
|
|
33
|
+
import { widgetForHandle } from "../components/measure";
|
|
34
|
+
import { getStoredLayoutOffset, getStoredRect, observeStoredRect, } from "../components/rect-store";
|
|
35
|
+
import { retainWidget, } from "../components/widget-retention";
|
|
36
|
+
import { computePointInWindow } from "../gtkx/bridge/index";
|
|
37
|
+
import { useLayoutAnimationSkip } from "./layout-animation-config";
|
|
38
|
+
import { runLayoutAnimation, } from "./layout-animation-runtime";
|
|
39
|
+
/**
|
|
40
|
+
* How much longer than the animation itself a widget may be retained before
|
|
41
|
+
* the fallback drops it. The timer exists for the case where the animation
|
|
42
|
+
* never reports an end at all, so the margin only has to cover the ordinary
|
|
43
|
+
* jitter of a frame source — not a whole animation.
|
|
44
|
+
*/
|
|
45
|
+
const RETENTION_MARGIN_MS = 500;
|
|
46
|
+
const ZERO_RECT = { x: 0, y: 0, width: 0, height: 0 };
|
|
47
|
+
// The platform's own Animated api and the frame scheduler behind it: the same
|
|
48
|
+
// pair every other animation in this surface runs on, so a layout animation
|
|
49
|
+
// adds no second clock.
|
|
50
|
+
const ENGINE = {
|
|
51
|
+
api: PlatformAnimated,
|
|
52
|
+
scheduler: glibScheduler,
|
|
53
|
+
};
|
|
54
|
+
const retainForExit = (widget, parentWidget, builder, stop) => retainWidget(widget, parentWidget, {
|
|
55
|
+
// The fallback covers a frame source that never delivers an end, so it
|
|
56
|
+
// only has to outlast the animation the builder describes.
|
|
57
|
+
fallbackMs: builder.getMaxDuration() + RETENTION_MARGIN_MS,
|
|
58
|
+
onRelease: stop,
|
|
59
|
+
});
|
|
60
|
+
const warnedWithoutWidget = new Set();
|
|
61
|
+
const warnNoWidget = (name) => {
|
|
62
|
+
if (warnedWithoutWidget.has(name)) {
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
warnedWithoutWidget.add(name);
|
|
66
|
+
const isProduction = typeof process !== "undefined" && process.env.NODE_ENV === "production";
|
|
67
|
+
if (!isProduction) {
|
|
68
|
+
console.warn(`react-native-reanimated: ${name} was given an \`entering\`, \`exiting\` or \`layout\` animation, but ` +
|
|
69
|
+
"the component exposed no ref carrying a widget, so there is nothing to animate. Put the animation on " +
|
|
70
|
+
"an `Animated.View`, or give the component a `ref` built with the platform's own measure handle. " +
|
|
71
|
+
"See docs/api.md.");
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
/** @internal Test seam: the warning is once per component per session. */
|
|
75
|
+
export const resetLayoutAnimationComponentWarnings = () => {
|
|
76
|
+
warnedWithoutWidget.clear();
|
|
77
|
+
};
|
|
78
|
+
const valuesFor = (widget, current, target) => {
|
|
79
|
+
const window = Dimensions.get("window");
|
|
80
|
+
// One FFI call per animation START (never per frame), so a builder reading
|
|
81
|
+
// the global origin gets a real answer rather than a parent-relative one
|
|
82
|
+
// wearing its name.
|
|
83
|
+
const page = computePointInWindow(widget, 0, 0);
|
|
84
|
+
const dx = page ? page.x - target.x : 0;
|
|
85
|
+
const dy = page ? page.y - target.y : 0;
|
|
86
|
+
return {
|
|
87
|
+
targetOriginX: target.x,
|
|
88
|
+
targetOriginY: target.y,
|
|
89
|
+
targetWidth: target.width,
|
|
90
|
+
targetHeight: target.height,
|
|
91
|
+
targetGlobalOriginX: target.x + dx,
|
|
92
|
+
targetGlobalOriginY: target.y + dy,
|
|
93
|
+
currentOriginX: current.x,
|
|
94
|
+
currentOriginY: current.y,
|
|
95
|
+
currentWidth: current.width,
|
|
96
|
+
currentHeight: current.height,
|
|
97
|
+
currentGlobalOriginX: current.x + dx,
|
|
98
|
+
currentGlobalOriginY: current.y + dy,
|
|
99
|
+
windowWidth: window.width,
|
|
100
|
+
windowHeight: window.height,
|
|
101
|
+
};
|
|
102
|
+
};
|
|
103
|
+
const displayNameOf = (component) => {
|
|
104
|
+
if (typeof component === "string") {
|
|
105
|
+
return component;
|
|
106
|
+
}
|
|
107
|
+
const named = component;
|
|
108
|
+
return named?.displayName ?? named?.name ?? "Component";
|
|
109
|
+
};
|
|
110
|
+
/**
|
|
111
|
+
* Adds `entering`, `exiting` and `layout` to an animated component.
|
|
112
|
+
*
|
|
113
|
+
* The rendered output is byte-for-byte what the wrapped component produces —
|
|
114
|
+
* the wrapper is a function component with no host element of its own.
|
|
115
|
+
*/
|
|
116
|
+
export const withLayoutAnimations = (component) => {
|
|
117
|
+
const name = displayNameOf(component);
|
|
118
|
+
const LayoutAnimated = ({ entering, exiting, layout, ref, ...rest }) => {
|
|
119
|
+
const host = useHostNode();
|
|
120
|
+
const handleRef = useRef(null);
|
|
121
|
+
// Null unless a `<LayoutAnimationConfig>` is above. Captured once and read
|
|
122
|
+
// from inside the effects below: the object identity is stable for the
|
|
123
|
+
// life of that wrapper, and both of its flags are only meaningful at the
|
|
124
|
+
// moment the effect (or its cleanup) actually runs.
|
|
125
|
+
const skip = useLayoutAnimationSkip();
|
|
126
|
+
// Read by an unmount cleanup and by a rect observer, both of which
|
|
127
|
+
// outlive the render that produced them. Refreshed in an insertion effect
|
|
128
|
+
// — the project's standard escape from writing a ref during render, and
|
|
129
|
+
// early enough that a layout effect in the same commit sees it.
|
|
130
|
+
const exitingRef = useRef(exiting ?? null);
|
|
131
|
+
const layoutRef = useRef(layout ?? null);
|
|
132
|
+
const forwardedRef = useRef(ref);
|
|
133
|
+
useInsertionEffect(() => {
|
|
134
|
+
exitingRef.current = exiting ?? null;
|
|
135
|
+
layoutRef.current = layout ?? null;
|
|
136
|
+
forwardedRef.current = ref;
|
|
137
|
+
});
|
|
138
|
+
// Stable identity: a fresh callback ref every render would detach and
|
|
139
|
+
// reattach the wrapped component's handle on every render.
|
|
140
|
+
const [assignHandle] = useState(() => (instance) => {
|
|
141
|
+
handleRef.current = instance;
|
|
142
|
+
const target = forwardedRef.current;
|
|
143
|
+
if (typeof target === "function") {
|
|
144
|
+
target(instance);
|
|
145
|
+
}
|
|
146
|
+
else if (target) {
|
|
147
|
+
;
|
|
148
|
+
target.current = instance;
|
|
149
|
+
}
|
|
150
|
+
// No return value: React 19 reads one as a callback-ref cleanup.
|
|
151
|
+
});
|
|
152
|
+
const parentWidgetRef = host.widgetRef;
|
|
153
|
+
// --- entering --------------------------------------------------------
|
|
154
|
+
// Never refreshed, unlike the two above: an entering animation runs once,
|
|
155
|
+
// on mount, so the builder that counts is the one this component was
|
|
156
|
+
// mounted with.
|
|
157
|
+
const enteringRef = useRef(entering ?? null);
|
|
158
|
+
useLayoutEffect(() => {
|
|
159
|
+
const builder = enteringRef.current;
|
|
160
|
+
if (!builder || skip?.entering.current) {
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
const widget = widgetForHandle(handleRef.current);
|
|
164
|
+
if (!widget) {
|
|
165
|
+
warnNoWidget(name);
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
const rect = getStoredRect(widget) ?? ZERO_RECT;
|
|
169
|
+
const values = valuesFor(widget, rect, rect);
|
|
170
|
+
const running = runLayoutAnimation(ENGINE, { widget, parentWidget: parentWidgetRef.current }, builder.build()(values), values);
|
|
171
|
+
return () => running.stop();
|
|
172
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
173
|
+
}, []);
|
|
174
|
+
// --- layout ----------------------------------------------------------
|
|
175
|
+
// Keyed on whether there IS a layout animation, not on which one: a rect
|
|
176
|
+
// observer is what turns on the rect store's notify path, and a view that
|
|
177
|
+
// never asked for a layout transition must not pay for it.
|
|
178
|
+
const hasLayout = Boolean(layout);
|
|
179
|
+
useLayoutEffect(() => {
|
|
180
|
+
if (!hasLayout) {
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
const widget = widgetForHandle(handleRef.current);
|
|
184
|
+
if (!widget) {
|
|
185
|
+
warnNoWidget(name);
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
let running = null;
|
|
189
|
+
const unobserve = observeStoredRect(widget, (next, previous) => {
|
|
190
|
+
const builder = layoutRef.current;
|
|
191
|
+
// Nothing to walk from on the first commit, and a pure resize has no
|
|
192
|
+
// position to animate — the size lands immediately either way.
|
|
193
|
+
if (!builder || previous === undefined) {
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
if (previous.x === next.x && previous.y === next.y) {
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
// Where the child is being DRAWN right now, which is the rect it had
|
|
200
|
+
// plus whatever a transition still in flight has added to it.
|
|
201
|
+
const offset = getStoredLayoutOffset(widget);
|
|
202
|
+
const drawn = {
|
|
203
|
+
x: previous.x + offset.dx,
|
|
204
|
+
y: previous.y + offset.dy,
|
|
205
|
+
width: previous.width,
|
|
206
|
+
height: previous.height,
|
|
207
|
+
};
|
|
208
|
+
running?.stop();
|
|
209
|
+
const values = valuesFor(widget, drawn, next);
|
|
210
|
+
running = runLayoutAnimation(ENGINE, { widget, parentWidget: parentWidgetRef.current }, builder.build()(values), values, () => {
|
|
211
|
+
running = null;
|
|
212
|
+
});
|
|
213
|
+
});
|
|
214
|
+
return () => {
|
|
215
|
+
unobserve();
|
|
216
|
+
running?.stop();
|
|
217
|
+
};
|
|
218
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
219
|
+
}, [hasLayout]);
|
|
220
|
+
// --- exiting ---------------------------------------------------------
|
|
221
|
+
// Declared last so its cleanup runs after the other two have stopped
|
|
222
|
+
// whatever they were driving: React tears a component's layout effects
|
|
223
|
+
// down in the order they were created.
|
|
224
|
+
useLayoutEffect(() => {
|
|
225
|
+
return () => {
|
|
226
|
+
const builder = exitingRef.current;
|
|
227
|
+
// `skip.exiting` is set by a `<LayoutAnimationConfig skipExiting>`
|
|
228
|
+
// above us, whose own cleanup React already ran — deletions tear down
|
|
229
|
+
// outside in, which is the same ordering `exiting` itself relies on.
|
|
230
|
+
if (!builder || skip?.exiting.current) {
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
const widget = widgetForHandle(handleRef.current);
|
|
234
|
+
// Read HERE and not captured when the effect ran, which is the whole
|
|
235
|
+
// point: null means this component's own container is unmounting in
|
|
236
|
+
// the same commit (React detaches the container's ref before
|
|
237
|
+
// descending into it), and there is then no container left to hold
|
|
238
|
+
// the widget — an exit animation inside a disappearing parent is not
|
|
239
|
+
// one anybody sees.
|
|
240
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
241
|
+
const parentWidget = parentWidgetRef.current;
|
|
242
|
+
if (!widget || !parentWidget) {
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
245
|
+
const rect = getStoredRect(widget) ?? ZERO_RECT;
|
|
246
|
+
const values = valuesFor(widget, rect, rect);
|
|
247
|
+
let running = null;
|
|
248
|
+
const retention = retainForExit(widget, parentWidget, builder, () => running?.stop());
|
|
249
|
+
if (retention === null) {
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
running = runLayoutAnimation(ENGINE, { widget, parentWidget }, builder.build()(values), values, () => retention.release());
|
|
253
|
+
};
|
|
254
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
255
|
+
}, []);
|
|
256
|
+
const targetProps = { ...rest };
|
|
257
|
+
if (entering || exiting || layout || ref) {
|
|
258
|
+
targetProps.ref = assignHandle;
|
|
259
|
+
}
|
|
260
|
+
const Target = component;
|
|
261
|
+
return _jsx(Target, { ...targetProps });
|
|
262
|
+
};
|
|
263
|
+
LayoutAnimated.displayName = `LayoutAnimated(${name})`;
|
|
264
|
+
return LayoutAnimated;
|
|
265
|
+
};
|
|
266
|
+
//# sourceMappingURL=layout-animation-view.js.map
|