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
|
@@ -1,35 +1,43 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import { useLayoutEffect, useRef } from "react";
|
|
2
|
+
import { useImperativeHandle, useLayoutEffect, useRef, useState, } from "react";
|
|
3
|
+
import { INSET_PROPERTIES, insetRefusalReason, insetTranslation, } from "../style/absolute-insets";
|
|
4
|
+
import { drivenSizeRefusal, SIZE_PROPERTIES, } from "../style/animated-size";
|
|
5
|
+
import { DRIVEABLE_COLOR_PROPERTIES, driveableColorsToCss, } from "../style/imperative-css";
|
|
6
|
+
import { StyleSheet } from "../style/index";
|
|
3
7
|
import { createAnimated } from "../animated/index";
|
|
4
|
-
import {
|
|
8
|
+
import { createWidgetCss, GtkBox, queueAllocate, setBoxPassthrough, setPointerTarget, setZIndex, } from "../gtkx/bridge/index";
|
|
5
9
|
import { useResponder } from "../responder/use-responder";
|
|
6
|
-
import {
|
|
10
|
+
import { applyDrivenSize, releaseDrivenSize } from "./driven-size";
|
|
11
|
+
import { glibScheduler } from "./frame-scheduler";
|
|
12
|
+
import { HostNodeContext, useHostNode } from "./host-node";
|
|
13
|
+
import { Image } from "./image";
|
|
14
|
+
import { createMeasureHandle, widgetForHandle, } from "./measure";
|
|
7
15
|
import { setStoredTransform } from "./rect-store";
|
|
8
|
-
import {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
const glibScheduler = {
|
|
12
|
-
schedule(callback) {
|
|
13
|
-
let cancelled = false;
|
|
14
|
-
const id = GLib.timeoutAdd(GLib.PRIORITY_DEFAULT, 16, () => {
|
|
15
|
-
if (!cancelled) {
|
|
16
|
-
callback(Number(GLib.getMonotonicTime()) / 1000);
|
|
17
|
-
}
|
|
18
|
-
return false;
|
|
19
|
-
});
|
|
20
|
-
return () => {
|
|
21
|
-
if (!cancelled) {
|
|
22
|
-
cancelled = true;
|
|
23
|
-
GLib.Source.remove(id);
|
|
24
|
-
}
|
|
25
|
-
};
|
|
26
|
-
},
|
|
27
|
-
};
|
|
16
|
+
import { ScrollView } from "./scroll-view";
|
|
17
|
+
import { Text } from "./text";
|
|
18
|
+
import { nodeForWidget, useLayoutChild, useRnContainer, } from "./use-layout-child";
|
|
28
19
|
const api = createAnimated(glibScheduler);
|
|
29
20
|
const isAnimatedNode = (value) => typeof value === "object" &&
|
|
30
21
|
value !== null &&
|
|
31
22
|
typeof value.addListener === "function" &&
|
|
32
23
|
typeof value.__getValue === "function";
|
|
24
|
+
const toNumber = (value) => typeof value === "number" ? value : Number.parseFloat(value);
|
|
25
|
+
// What a slot actually writes into the transform array. An ordinary slot
|
|
26
|
+
// writes its value; a derived inset slot writes its offset from the committed
|
|
27
|
+
// base, which is what makes an absolute `top` a translation.
|
|
28
|
+
const slotValue = (slot, value) => {
|
|
29
|
+
if (!slot.inset) {
|
|
30
|
+
return value;
|
|
31
|
+
}
|
|
32
|
+
const numeric = toNumber(value);
|
|
33
|
+
// A non-numeric inset (a percentage produced mid-animation) has no offset
|
|
34
|
+
// from a point base. Holding the last position beats jumping to NaN, and
|
|
35
|
+
// the leaf never becomes driveable in the first place unless it starts
|
|
36
|
+
// numeric — see style/absolute-insets.ts and reanimated-compat/style.ts.
|
|
37
|
+
return Number.isFinite(numeric)
|
|
38
|
+
? slot.inset.sign * (numeric - slot.inset.base)
|
|
39
|
+
: 0;
|
|
40
|
+
};
|
|
33
41
|
// Stable per-node identity for the effect's dependency key: Animated nodes
|
|
34
42
|
// are objects, and the bindings must be rebuilt when the node behind a slot
|
|
35
43
|
// changes — not on every render that rebuilds an equal-looking array.
|
|
@@ -43,6 +51,62 @@ const nodeId = (value) => {
|
|
|
43
51
|
}
|
|
44
52
|
return id;
|
|
45
53
|
};
|
|
54
|
+
// One warning per inset property per session, like every other channel here.
|
|
55
|
+
const warnedInsets = new Set();
|
|
56
|
+
const insetTranslationOf = (property) => property === "top" || property === "bottom" ? "translateY" : "translateX";
|
|
57
|
+
const warnInsetNotTranslatable = (property, reason) => {
|
|
58
|
+
if (warnedInsets.has(property)) {
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
warnedInsets.add(property);
|
|
62
|
+
const isProduction = typeof process !== "undefined" && process.env.NODE_ENV === "production";
|
|
63
|
+
if (isProduction) {
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
const spec = insetTranslationOf(property);
|
|
67
|
+
console.warn(`react-native-gtkx: an animated \`${property}\` cannot be driven here. ` +
|
|
68
|
+
(reason
|
|
69
|
+
? `The node IS absolutely positioned, but ${reason}, so there is no translation that reproduces it. `
|
|
70
|
+
: "`top`/`left`/`right`/`bottom` are driven at frame rate only on a node whose own `position` is " +
|
|
71
|
+
'"absolute", where moving it is exactly a translation and touches no sibling. Anything else needs a ' +
|
|
72
|
+
"Yoga pass over the container plus its commit walk, which costs what the CONTAINER costs (71 µs at " +
|
|
73
|
+
"five children, 509 µs at three hundred) against a transform's 0.12 µs. ") +
|
|
74
|
+
`Animate \`transform: [{ ${spec}: … }]\` instead. ` +
|
|
75
|
+
"The value is still applied on the next React render. See docs/api.md.");
|
|
76
|
+
};
|
|
77
|
+
/** @internal Test seam: the warning is once per property per session by design. */
|
|
78
|
+
export const resetAnimatedInsetWarnings = () => {
|
|
79
|
+
warnedInsets.clear();
|
|
80
|
+
};
|
|
81
|
+
// Same policy for sizes: once per property per session, and the reason is the
|
|
82
|
+
// actionable part — the rule refuses for one of eight named reasons and each
|
|
83
|
+
// of them has a different fix.
|
|
84
|
+
const warnedSizes = new Set();
|
|
85
|
+
const warnSizeNotDriveable = (property, reason) => {
|
|
86
|
+
if (warnedSizes.has(property)) {
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
warnedSizes.add(property);
|
|
90
|
+
const isProduction = typeof process !== "undefined" && process.env.NODE_ENV === "production";
|
|
91
|
+
if (isProduction) {
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
const scale = property === "width" ? "scaleX" : "scaleY";
|
|
95
|
+
console.warn(`react-native-gtkx: an animated \`${property}\` cannot be driven here, because ${reason}. ` +
|
|
96
|
+
`\`width\`/\`height\` run at frame rate only where the change stops at the node that owns it — the ` +
|
|
97
|
+
"node's own subtree is then re-laid-out pinned to the driven value (7.1 µs for a leaf, 21.7 µs with " +
|
|
98
|
+
"wrapped text, the same at five children and at three hundred) and one allocation puts it on screen. " +
|
|
99
|
+
"Anything else needs a Yoga pass over the container plus its commit walk, which costs what the " +
|
|
100
|
+
"CONTAINER costs: 71 µs at five children, 509 µs at three hundred, against a transform's 0.6 µs. " +
|
|
101
|
+
`The closest transform is \`transform: [{ ${scale}: … }]\`, but it is NOT the same thing — a scale ` +
|
|
102
|
+
"grows about the view's centre, so the box moves as it grows, and it scales the content with the box " +
|
|
103
|
+
"instead of re-laying it out, so text stretches rather than re-wrapping. " +
|
|
104
|
+
"The new value is applied on the next React render either way. See docs/api.md.");
|
|
105
|
+
};
|
|
106
|
+
/** @internal Test seam: the warning is once per property per session by design. */
|
|
107
|
+
export const resetAnimatedSizeWarnings = () => {
|
|
108
|
+
warnedSizes.clear();
|
|
109
|
+
};
|
|
46
110
|
const splitAnimated = (style) => {
|
|
47
111
|
const flat = {};
|
|
48
112
|
const collect = (entry) => {
|
|
@@ -57,11 +121,31 @@ const splitAnimated = (style) => {
|
|
|
57
121
|
};
|
|
58
122
|
collect(style);
|
|
59
123
|
let opacity = null;
|
|
124
|
+
let zIndex = null;
|
|
60
125
|
const slots = [];
|
|
126
|
+
const colors = [];
|
|
127
|
+
const sizes = [];
|
|
61
128
|
if (isAnimatedNode(flat.opacity)) {
|
|
62
129
|
opacity = flat.opacity;
|
|
63
130
|
delete flat.opacity;
|
|
64
131
|
}
|
|
132
|
+
// `zIndex` is in `useSortable`'s style object on every frame and changes
|
|
133
|
+
// value about twice per drag. It takes the same door opacity does — one
|
|
134
|
+
// widget-level write, no Yoga, no CSS — so there is nothing to refuse.
|
|
135
|
+
if (isAnimatedNode(flat.zIndex)) {
|
|
136
|
+
zIndex = flat.zIndex;
|
|
137
|
+
delete flat.zIndex;
|
|
138
|
+
}
|
|
139
|
+
for (const property of DRIVEABLE_COLOR_PROPERTIES) {
|
|
140
|
+
const value = flat[property];
|
|
141
|
+
if (isAnimatedNode(value)) {
|
|
142
|
+
colors.push({ property, node: value });
|
|
143
|
+
// Removed from the static style so the shared class registry never sees
|
|
144
|
+
// a per-frame value: its memoisation stays keyed on styles that change
|
|
145
|
+
// when React renders, which is the only thing it is good at.
|
|
146
|
+
delete flat[property];
|
|
147
|
+
}
|
|
148
|
+
}
|
|
65
149
|
if (Array.isArray(flat.transform)) {
|
|
66
150
|
for (const part of flat.transform) {
|
|
67
151
|
const key = Object.keys(part)[0];
|
|
@@ -79,39 +163,166 @@ const splitAnimated = (style) => {
|
|
|
79
163
|
// each other every render.
|
|
80
164
|
delete flat.transform;
|
|
81
165
|
}
|
|
82
|
-
|
|
166
|
+
// Slice 2b: an animated inset on an absolutely positioned node becomes a
|
|
167
|
+
// derived translate. It is the one layout property with an exact transform
|
|
168
|
+
// equivalent, because an out-of-flow node's position affects nothing but
|
|
169
|
+
// where it is drawn — see src/style/absolute-insets.ts for the measured
|
|
170
|
+
// rule and for the configurations where the equivalence does NOT hold.
|
|
171
|
+
//
|
|
172
|
+
// Three things happen here at once, and all three are load-bearing:
|
|
173
|
+
//
|
|
174
|
+
// - THE BASE. The inset's current value is written back into the static
|
|
175
|
+
// style as a plain number, so Yoga is given a real position and the
|
|
176
|
+
// committed rect the parent's allocate reads is the base the offset is
|
|
177
|
+
// measured from. Nothing about the shadow tree changes.
|
|
178
|
+
// - THE REBASE. This runs on every render, so when React commits a layout
|
|
179
|
+
// with a different inset, the base moves and the offset is recomputed
|
|
180
|
+
// against it in the SAME commit — the Yoga write and the offset write
|
|
181
|
+
// are both layout effects, and the engine's flush is a microtask, so GTK
|
|
182
|
+
// never gets a frame in between and there is no jump.
|
|
183
|
+
// - THE ORDER. Derived slots go FIRST. The array's leftmost entry is the
|
|
184
|
+
// outermost matrix (style/transform.ts), so a derived translate is
|
|
185
|
+
// applied to the point LAST — it moves the already-rotated, already-
|
|
186
|
+
// scaled box in the parent's coordinates, exactly as a layout position
|
|
187
|
+
// does. Appending instead would put the user's scale on the OUTSIDE and
|
|
188
|
+
// multiply the offset by it.
|
|
189
|
+
const insetSlots = [];
|
|
190
|
+
for (const property of INSET_PROPERTIES) {
|
|
191
|
+
const value = flat[property];
|
|
192
|
+
if (!isAnimatedNode(value)) {
|
|
193
|
+
continue;
|
|
194
|
+
}
|
|
195
|
+
const base = toNumber(value.__getValue());
|
|
196
|
+
const translation = Number.isFinite(base)
|
|
197
|
+
? insetTranslation(flat, property)
|
|
198
|
+
: null;
|
|
199
|
+
if (!translation) {
|
|
200
|
+
// Not an equivalence. Resolve to the current value so the static style
|
|
201
|
+
// is at least correct on this render, and keep slice 2's refusal loud.
|
|
202
|
+
flat[property] = Number.isFinite(base) ? base : undefined;
|
|
203
|
+
warnInsetNotTranslatable(property, insetRefusalReason(flat, property));
|
|
204
|
+
continue;
|
|
205
|
+
}
|
|
206
|
+
flat[property] = base;
|
|
207
|
+
insetSlots.push({
|
|
208
|
+
key: translation.transform,
|
|
209
|
+
node: value,
|
|
210
|
+
value: 0,
|
|
211
|
+
inset: { base, sign: translation.sign },
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
// Slice 3: an animated `width`/`height` where the change is confined to the
|
|
215
|
+
// node that owns it. Unlike an inset this is NOT re-expressed as a
|
|
216
|
+
// transform — there is no transform that reproduces a size change, which
|
|
217
|
+
// docs/research/animated-size.md §6 measured rather than assumed — so the
|
|
218
|
+
// slot carries the property itself and the view layer drives it through a
|
|
219
|
+
// pinned pass over the node's own subtree.
|
|
220
|
+
//
|
|
221
|
+
// THE REBASE, exactly as above and for the same reason: the driven value is
|
|
222
|
+
// written back into the static style as a plain number, so Yoga is given the
|
|
223
|
+
// size the animation is currently showing and the committed rect the
|
|
224
|
+
// override sits on catches up in the SAME commit. Both writes are layout
|
|
225
|
+
// effects and the engine's flush is a microtask, so GTK never gets a frame
|
|
226
|
+
// in between.
|
|
227
|
+
//
|
|
228
|
+
// WHETHER it can be driven is not decided here. The answer depends on the
|
|
229
|
+
// CONTAINER's `flexDirection`, its `alignItems` and where its own size comes
|
|
230
|
+
// from, none of which are in this style object or in this component's props
|
|
231
|
+
// — it is asked of the layout tree in useAnimatedBinding, which is the only
|
|
232
|
+
// place the tree exists.
|
|
233
|
+
for (const property of SIZE_PROPERTIES) {
|
|
234
|
+
const value = flat[property];
|
|
235
|
+
if (!isAnimatedNode(value)) {
|
|
236
|
+
continue;
|
|
237
|
+
}
|
|
238
|
+
const base = toNumber(value.__getValue());
|
|
239
|
+
if (!Number.isFinite(base)) {
|
|
240
|
+
// A percentage produced mid-animation has no point base to lay out at.
|
|
241
|
+
// Dropped rather than driven, and the leaf never becomes driveable in
|
|
242
|
+
// the first place unless it starts numeric — reanimated-compat/style.ts.
|
|
243
|
+
flat[property] = undefined;
|
|
244
|
+
continue;
|
|
245
|
+
}
|
|
246
|
+
flat[property] = base;
|
|
247
|
+
sizes.push({ property, node: value, base });
|
|
248
|
+
}
|
|
249
|
+
return {
|
|
250
|
+
staticStyle: flat,
|
|
251
|
+
opacity,
|
|
252
|
+
zIndex,
|
|
253
|
+
slots: insetSlots.length > 0 ? [...insetSlots, ...slots] : slots,
|
|
254
|
+
colors,
|
|
255
|
+
sizes,
|
|
256
|
+
};
|
|
83
257
|
};
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
258
|
+
/**
|
|
259
|
+
* The imperative write path, and the whole of it.
|
|
260
|
+
*
|
|
261
|
+
* Animated values bypass React entirely: listeners write straight to the
|
|
262
|
+
* widget (opacity), to the rect store (the transform applied by the parent's
|
|
263
|
+
* allocate) on top of the engine-committed base rect, and to a CSS provider
|
|
264
|
+
* private to the widget (colours). Animation frames never touch Yoga, and
|
|
265
|
+
* never touch the shared stylesheet.
|
|
266
|
+
*
|
|
267
|
+
* Nothing here is View-specific, which is why it is a hook rather than a
|
|
268
|
+
* paragraph inside `AnimatedView`: it needs the CHILD's widget (for
|
|
269
|
+
* `setOpacity` and the rect store) and its PARENT's (for `queueAllocate`),
|
|
270
|
+
* and any component that can produce those two can be animated. The child is
|
|
271
|
+
* reached through a getter rather than a ref because a wrapper does not own
|
|
272
|
+
* the widget — it reads it back out of whatever handle the wrapped component
|
|
273
|
+
* exposed, which may only exist after that component's own layout effects.
|
|
274
|
+
*/
|
|
275
|
+
const useAnimatedBinding = (getWidget, host, opacity, zIndex, slots, colors, sizes,
|
|
276
|
+
// Anything else that must force a rebind. `AnimatedView` passes its layout
|
|
277
|
+
// node; the generic wrapper has nothing to add.
|
|
278
|
+
rebindKey) => {
|
|
97
279
|
// The effect reads the slots of the render that (re)armed it; the values
|
|
98
280
|
// inside are then owned by the listeners.
|
|
99
281
|
const slotsRef = useRef(slots);
|
|
100
282
|
slotsRef.current = slots;
|
|
283
|
+
const colorsRef = useRef(colors);
|
|
284
|
+
colorsRef.current = colors;
|
|
285
|
+
const sizesRef = useRef(sizes);
|
|
286
|
+
sizesRef.current = sizes;
|
|
287
|
+
const getWidgetRef = useRef(getWidget);
|
|
288
|
+
getWidgetRef.current = getWidget;
|
|
289
|
+
const hostRef = useRef(host);
|
|
290
|
+
hostRef.current = host;
|
|
291
|
+
const parentWidgetRef = host.widgetRef;
|
|
101
292
|
// Rebind on a change of shape (which transforms, in which order), of the
|
|
102
293
|
// node behind an animated entry, or of a static entry's value — not on
|
|
103
294
|
// every render that rebuilds an equal-looking array.
|
|
295
|
+
//
|
|
296
|
+
// A derived inset slot adds its BASE to the key, and that is what makes the
|
|
297
|
+
// rebase happen: React committing a new `top` moves the base, which re-arms
|
|
298
|
+
// this effect in the same commit that gives Yoga the new position.
|
|
104
299
|
const bindingKey = (opacity ? `opacity#${nodeId(opacity)}` : "") +
|
|
300
|
+
(zIndex ? `zIndex#${nodeId(zIndex)}` : "") +
|
|
105
301
|
slots
|
|
106
|
-
.map((slot) =>
|
|
107
|
-
?
|
|
108
|
-
|
|
302
|
+
.map((slot) => {
|
|
303
|
+
const inset = slot.inset ? `~${slot.inset.sign}:${slot.inset.base}` : "";
|
|
304
|
+
return slot.node
|
|
305
|
+
? `|${slot.key}#${nodeId(slot.node)}${inset}`
|
|
306
|
+
: `|${slot.key}=${String(slot.value)}`;
|
|
307
|
+
})
|
|
308
|
+
.join("") +
|
|
309
|
+
colors.map((slot) => `|${slot.property}#${nodeId(slot.node)}`).join("") +
|
|
310
|
+
// The base is in the key for the same reason a derived inset's is: React
|
|
311
|
+
// committing a new size moves it, and re-arming here is what hands the
|
|
312
|
+
// override back to the engine in the same commit that gives Yoga the new
|
|
313
|
+
// value.
|
|
314
|
+
sizes
|
|
315
|
+
.map((slot) => `|${slot.property}#${nodeId(slot.node)}~${slot.base}`)
|
|
109
316
|
.join("");
|
|
110
317
|
useLayoutEffect(() => {
|
|
111
|
-
const widget =
|
|
318
|
+
const widget = getWidgetRef.current();
|
|
112
319
|
if (!widget) {
|
|
113
320
|
return;
|
|
114
321
|
}
|
|
322
|
+
// Captured for the CLEANUP only: by then the ref may already have been
|
|
323
|
+
// detached, and the container to re-allocate is the one this binding was
|
|
324
|
+
// made against. Every live write below re-reads the ref instead.
|
|
325
|
+
const boundParentWidget = parentWidgetRef.current;
|
|
115
326
|
// The live transform array, mutated in place: an Animated write must
|
|
116
327
|
// stay one numeric store plus one queued allocation — no React render,
|
|
117
328
|
// no Yoga pass, no allocation per frame beyond the composed matrix.
|
|
@@ -120,10 +331,18 @@ const AnimatedView = ({ style, children, onLayout, testID, ...responderProps })
|
|
|
120
331
|
// rect store unclamped — the parent's measure ignores children, so a
|
|
121
332
|
// transformed child draws past the boundary over its neighbors without
|
|
122
333
|
// moving a single ancestor.
|
|
123
|
-
const parts = slotsRef.current.map((slot) => ({
|
|
334
|
+
const parts = slotsRef.current.map((slot) => ({
|
|
335
|
+
[slot.key]: slot.node
|
|
336
|
+
? slotValue(slot, slot.node.__getValue())
|
|
337
|
+
: slot.value,
|
|
338
|
+
}));
|
|
124
339
|
const flush = () => {
|
|
340
|
+
const widget = getWidgetRef.current();
|
|
341
|
+
if (!widget) {
|
|
342
|
+
return;
|
|
343
|
+
}
|
|
125
344
|
setStoredTransform(widget, parts);
|
|
126
|
-
const parentWidget =
|
|
345
|
+
const parentWidget = parentWidgetRef.current;
|
|
127
346
|
if (parentWidget) {
|
|
128
347
|
queueAllocate(parentWidget);
|
|
129
348
|
}
|
|
@@ -137,7 +356,7 @@ const AnimatedView = ({ style, children, onLayout, testID, ...responderProps })
|
|
|
137
356
|
subscriptions.push({
|
|
138
357
|
node: slot.node,
|
|
139
358
|
id: slot.node.addListener(({ value }) => {
|
|
140
|
-
part[slot.key] = value;
|
|
359
|
+
part[slot.key] = slotValue(slot, value);
|
|
141
360
|
flush();
|
|
142
361
|
}),
|
|
143
362
|
});
|
|
@@ -145,7 +364,7 @@ const AnimatedView = ({ style, children, onLayout, testID, ...responderProps })
|
|
|
145
364
|
if (opacity) {
|
|
146
365
|
const applyOpacity = (value) => {
|
|
147
366
|
const numeric = typeof value === "number" ? value : parseFloat(value);
|
|
148
|
-
|
|
367
|
+
getWidgetRef.current()?.setOpacity(Math.min(1, Math.max(0, numeric)));
|
|
149
368
|
};
|
|
150
369
|
subscriptions.push({
|
|
151
370
|
node: opacity,
|
|
@@ -155,19 +374,313 @@ const AnimatedView = ({ style, children, onLayout, testID, ...responderProps })
|
|
|
155
374
|
});
|
|
156
375
|
applyOpacity(opacity.__getValue());
|
|
157
376
|
}
|
|
377
|
+
// The container's paint and pick order (gtkx/bridge/view-box.ts). The
|
|
378
|
+
// write drops the container's cached order and queues a redraw; nothing
|
|
379
|
+
// is re-measured and nothing is re-allocated, because zIndex changes
|
|
380
|
+
// neither.
|
|
381
|
+
if (zIndex) {
|
|
382
|
+
const applyZIndex = (value) => {
|
|
383
|
+
const widget = getWidgetRef.current();
|
|
384
|
+
if (!widget) {
|
|
385
|
+
return;
|
|
386
|
+
}
|
|
387
|
+
const numeric = typeof value === "number" ? value : parseFloat(value);
|
|
388
|
+
setZIndex(widget, Number.isFinite(numeric) ? Math.round(numeric) : 0);
|
|
389
|
+
};
|
|
390
|
+
subscriptions.push({
|
|
391
|
+
node: zIndex,
|
|
392
|
+
id: zIndex.addListener(({ value }) => {
|
|
393
|
+
applyZIndex(value);
|
|
394
|
+
}),
|
|
395
|
+
});
|
|
396
|
+
applyZIndex(zIndex.__getValue());
|
|
397
|
+
}
|
|
398
|
+
// Colours take the other imperative door: a CSS provider private to this
|
|
399
|
+
// widget, replaced in place. Deliberately NOT the shared class registry —
|
|
400
|
+
// that one memoises by CSS text, so a driven colour would mint a class per
|
|
401
|
+
// frame into a document GTK re-parses whole and that is never pruned
|
|
402
|
+
// (docs/research/animated-colors.md). All driven colours share one
|
|
403
|
+
// provider and one declaration block: two properties animating together
|
|
404
|
+
// are one write, not two.
|
|
405
|
+
const colorSlots = colorsRef.current;
|
|
406
|
+
let widgetCss = null;
|
|
407
|
+
if (colorSlots.length > 0) {
|
|
408
|
+
const css = createWidgetCss(widget);
|
|
409
|
+
widgetCss = css;
|
|
410
|
+
const values = {};
|
|
411
|
+
const writeColors = () => {
|
|
412
|
+
css.set(driveableColorsToCss(values));
|
|
413
|
+
};
|
|
414
|
+
for (const slot of colorSlots) {
|
|
415
|
+
values[slot.property] = slot.node.__getValue();
|
|
416
|
+
subscriptions.push({
|
|
417
|
+
node: slot.node,
|
|
418
|
+
id: slot.node.addListener(({ value }) => {
|
|
419
|
+
values[slot.property] = value;
|
|
420
|
+
writeColors();
|
|
421
|
+
}),
|
|
422
|
+
});
|
|
423
|
+
}
|
|
424
|
+
writeColors();
|
|
425
|
+
}
|
|
426
|
+
// Sizes take the third door, and it is the only one that runs Yoga.
|
|
427
|
+
//
|
|
428
|
+
// Nothing is written at BIND time on purpose: the render that armed this
|
|
429
|
+
// effect put the current value into the static style, so the engine is
|
|
430
|
+
// already committing exactly this size and an override would be a copy of
|
|
431
|
+
// it. The first write happens when a frame asks for something else.
|
|
432
|
+
const sizeSlots = sizesRef.current;
|
|
433
|
+
const drivenWidgets = [];
|
|
434
|
+
if (sizeSlots.length > 0) {
|
|
435
|
+
const layoutNode = nodeForWidget(widget);
|
|
436
|
+
const driven = {};
|
|
437
|
+
const driveable = new Set();
|
|
438
|
+
let decided = false;
|
|
439
|
+
// Deferred to the first frame that actually asks for a new size, and
|
|
440
|
+
// that is not laziness: the rule is asked of the CONTAINER's Yoga node,
|
|
441
|
+
// and on a first mount a container's own layout effects run AFTER its
|
|
442
|
+
// children's — so at bind time the container may still be carrying the
|
|
443
|
+
// defaults rather than its style. By the first frame the tree has been
|
|
444
|
+
// laid out at least once and the answer is the real one.
|
|
445
|
+
const decide = () => {
|
|
446
|
+
decided = true;
|
|
447
|
+
if (!layoutNode) {
|
|
448
|
+
return;
|
|
449
|
+
}
|
|
450
|
+
const context = {
|
|
451
|
+
node: layoutNode,
|
|
452
|
+
rootIsContentSized: hostRef.current.engine.contentSized,
|
|
453
|
+
};
|
|
454
|
+
for (const slot of sizeSlots) {
|
|
455
|
+
const reason = drivenSizeRefusal(context, slot.property);
|
|
456
|
+
if (reason === null) {
|
|
457
|
+
driveable.add(slot.property);
|
|
458
|
+
}
|
|
459
|
+
else {
|
|
460
|
+
warnSizeNotDriveable(slot.property, reason);
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
};
|
|
464
|
+
for (const slot of sizeSlots) {
|
|
465
|
+
subscriptions.push({
|
|
466
|
+
node: slot.node,
|
|
467
|
+
id: slot.node.addListener(({ value }) => {
|
|
468
|
+
const numeric = toNumber(value);
|
|
469
|
+
if (!Number.isFinite(numeric)) {
|
|
470
|
+
return;
|
|
471
|
+
}
|
|
472
|
+
if (!decided) {
|
|
473
|
+
decide();
|
|
474
|
+
}
|
|
475
|
+
if (!driveable.has(slot.property) || !layoutNode) {
|
|
476
|
+
return;
|
|
477
|
+
}
|
|
478
|
+
driven[slot.property] = numeric;
|
|
479
|
+
const target = getWidgetRef.current();
|
|
480
|
+
if (target) {
|
|
481
|
+
applyDrivenSize(layoutNode, target, parentWidgetRef.current, driven, drivenWidgets);
|
|
482
|
+
}
|
|
483
|
+
}),
|
|
484
|
+
});
|
|
485
|
+
}
|
|
486
|
+
}
|
|
158
487
|
flush();
|
|
159
488
|
return () => {
|
|
160
489
|
for (const { node: animated, id } of subscriptions) {
|
|
161
490
|
animated.removeListener(id);
|
|
162
491
|
}
|
|
492
|
+
widgetCss?.dispose();
|
|
493
|
+
// Every widget this binding was driving goes back to the rect the
|
|
494
|
+
// engine committed. On a REBASE that is the whole point: the render
|
|
495
|
+
// that re-armed this effect handed Yoga the size the animation is
|
|
496
|
+
// showing, so the committed rect is about to become the driven one and
|
|
497
|
+
// holding an override on top of it would be holding a stale copy.
|
|
498
|
+
if (drivenWidgets.length > 0) {
|
|
499
|
+
releaseDrivenSize(drivenWidgets);
|
|
500
|
+
if (boundParentWidget) {
|
|
501
|
+
queueAllocate(boundParentWidget);
|
|
502
|
+
}
|
|
503
|
+
}
|
|
163
504
|
};
|
|
164
505
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
165
|
-
}, [bindingKey,
|
|
506
|
+
}, [bindingKey, rebindKey]);
|
|
507
|
+
};
|
|
508
|
+
const AnimatedView = ({ style, children, onLayout, testID, ref, pointerEvents, animatedProps, ...responderProps }) => {
|
|
509
|
+
const widgetRef = useRef(null);
|
|
510
|
+
const { staticStyle, opacity, zIndex, slots, colors, sizes } = splitAnimated(style);
|
|
511
|
+
const { host, node, cssClass } = useLayoutChild(widgetRef, {
|
|
512
|
+
style: staticStyle,
|
|
513
|
+
onLayout,
|
|
514
|
+
});
|
|
515
|
+
useRnContainer(widgetRef, node);
|
|
516
|
+
useResponder(widgetRef, responderProps);
|
|
517
|
+
// `animatedProps` LAST, matching `createAnimatedComponent`'s
|
|
518
|
+
// `{ ...rest, ...animatedProps }` — a value produced by the updater is the
|
|
519
|
+
// live one and wins over the static prop. Only the picking mode is read
|
|
520
|
+
// here: a numeric animated prop has no meaning on a View, and the SVG
|
|
521
|
+
// shapes that do take them go through `createAnimatedComponent` already.
|
|
522
|
+
const mode = animatedProps?.pointerEvents ??
|
|
523
|
+
pointerEvents ??
|
|
524
|
+
StyleSheet.flatten(staticStyle)?.pointerEvents ??
|
|
525
|
+
"auto";
|
|
526
|
+
useLayoutEffect(() => {
|
|
527
|
+
const widget = widgetRef.current;
|
|
528
|
+
if (!widget) {
|
|
529
|
+
return;
|
|
530
|
+
}
|
|
531
|
+
setPointerTarget(widget, mode !== "none");
|
|
532
|
+
setBoxPassthrough(widget, mode === "box-none");
|
|
533
|
+
}, [mode]);
|
|
534
|
+
useImperativeHandle(ref, () => createMeasureHandle(widgetRef, node), [node]);
|
|
535
|
+
useAnimatedBinding(() => widgetRef.current, host, opacity, zIndex, slots, colors, sizes, node);
|
|
166
536
|
return (_jsx(GtkBox, { ref: widgetRef, name: testID, cssClasses: cssClass ? [cssClass] : [], children: _jsx(HostNodeContext.Provider, { value: { engine: host.engine, node, widgetRef }, children: children }) }));
|
|
167
537
|
};
|
|
538
|
+
const displayNameOf = (component) => {
|
|
539
|
+
if (typeof component === "string") {
|
|
540
|
+
return component;
|
|
541
|
+
}
|
|
542
|
+
const named = component;
|
|
543
|
+
return named?.displayName ?? named?.name ?? "Component";
|
|
544
|
+
};
|
|
545
|
+
// One warning per wrapped component per session — a mapper runs at frame
|
|
546
|
+
// rate, and the name is the actionable part.
|
|
547
|
+
const warnedWithoutWidget = new Set();
|
|
548
|
+
const warnNoWidget = (name) => {
|
|
549
|
+
if (warnedWithoutWidget.has(name)) {
|
|
550
|
+
return;
|
|
551
|
+
}
|
|
552
|
+
warnedWithoutWidget.add(name);
|
|
553
|
+
const isProduction = typeof process !== "undefined" && process.env.NODE_ENV === "production";
|
|
554
|
+
if (!isProduction) {
|
|
555
|
+
console.warn(`react-native-gtkx: createAnimatedComponent(${name}) was given an animated \`opacity\`, ` +
|
|
556
|
+
"`transform`, colour or size, but the component exposed no ref carrying a widget, so there is nothing to write " +
|
|
557
|
+
"to. Give it a `ref?: Ref<MeasureHandle>` built with the platform's own measure handle, or wrap " +
|
|
558
|
+
"it in an `Animated.View`. See docs/api.md.");
|
|
559
|
+
}
|
|
560
|
+
};
|
|
561
|
+
/** @internal Test seam: the warning is once per component per session by design. */
|
|
562
|
+
export const resetAnimatedComponentWarnings = () => {
|
|
563
|
+
warnedWithoutWidget.clear();
|
|
564
|
+
};
|
|
565
|
+
/**
|
|
566
|
+
* Wraps a component so its `style`'s `opacity`/`transform` and its
|
|
567
|
+
* `animatedProps` can be driven imperatively — RN's own
|
|
568
|
+
* `Animated.createAnimatedComponent`.
|
|
569
|
+
*
|
|
570
|
+
* IT ADDS NO WIDGET. Wrapping in an `Animated.View` would have been three
|
|
571
|
+
* lines and would have been wrong: an extra box changes flex layout, changes
|
|
572
|
+
* what `measureLayout` is relative to, and changes which widget a parent's
|
|
573
|
+
* allocate walks. That is not a shim, it is a different tree. Instead the
|
|
574
|
+
* wrapper renders the component itself and reaches the widget through the
|
|
575
|
+
* handle the component already exposes (see measure.ts's `widgetOf`), so the
|
|
576
|
+
* rendered output is byte-for-byte what the unwrapped component produces.
|
|
577
|
+
*
|
|
578
|
+
* `animatedProps` are passed straight through as animated NODES rather than
|
|
579
|
+
* resolved to numbers, because the components that take them already accept
|
|
580
|
+
* one: the SVG shapes duck-type an animated node on every numeric geometry
|
|
581
|
+
* and paint prop and subscribe to it themselves (svg/animated-support.ts),
|
|
582
|
+
* redrawing through `queueDraw`. Nothing is resolved here that the receiver
|
|
583
|
+
* can resolve better.
|
|
584
|
+
*/
|
|
585
|
+
export const createAnimatedComponent = (component) => {
|
|
586
|
+
const name = displayNameOf(component);
|
|
587
|
+
const AnimatedWrapper = ({ style, animatedProps, ref, ...rest }) => {
|
|
588
|
+
// The PARENT's widget, for `queueAllocate` — the wrapper is not a host
|
|
589
|
+
// node itself, so this is the container the wrapped component is laid out
|
|
590
|
+
// by, exactly as it would be without the wrapper.
|
|
591
|
+
const host = useHostNode();
|
|
592
|
+
const handleRef = useRef(null);
|
|
593
|
+
const { staticStyle, opacity, zIndex, slots, colors, sizes } = splitAnimated(style);
|
|
594
|
+
const driven = opacity !== null ||
|
|
595
|
+
zIndex !== null ||
|
|
596
|
+
slots.length > 0 ||
|
|
597
|
+
colors.length > 0 ||
|
|
598
|
+
sizes.length > 0;
|
|
599
|
+
// Stable identity: a fresh callback ref every render would detach and
|
|
600
|
+
// reattach the wrapped component's handle on every render.
|
|
601
|
+
const forwardedRef = useRef(ref);
|
|
602
|
+
forwardedRef.current = ref;
|
|
603
|
+
const [assignHandle] = useState(() => (instance) => {
|
|
604
|
+
handleRef.current = instance;
|
|
605
|
+
const target = forwardedRef.current;
|
|
606
|
+
if (typeof target === "function") {
|
|
607
|
+
target(instance);
|
|
608
|
+
}
|
|
609
|
+
else if (target) {
|
|
610
|
+
;
|
|
611
|
+
target.current = instance;
|
|
612
|
+
}
|
|
613
|
+
// No return value: React 19 reads one as a callback-ref cleanup.
|
|
614
|
+
});
|
|
615
|
+
useAnimatedBinding(() => widgetForHandle(handleRef.current), host, opacity, zIndex, slots, colors, sizes);
|
|
616
|
+
// A silent no-op is the failure this repo refuses. Checked after the
|
|
617
|
+
// wrapped component's own layout effects have run, which is where its
|
|
618
|
+
// `useImperativeHandle` publishes the handle.
|
|
619
|
+
useLayoutEffect(() => {
|
|
620
|
+
if (driven && widgetForHandle(handleRef.current) === null) {
|
|
621
|
+
warnNoWidget(name);
|
|
622
|
+
}
|
|
623
|
+
});
|
|
624
|
+
const targetProps = { ...rest, ...animatedProps };
|
|
625
|
+
// Only when there is something to attach: a component that takes no ref
|
|
626
|
+
// (the SVG shapes) would otherwise get one in its rest props.
|
|
627
|
+
if (driven || ref) {
|
|
628
|
+
targetProps.ref = assignHandle;
|
|
629
|
+
}
|
|
630
|
+
// Likewise `style`: passing `{}` to a component that has no style prop
|
|
631
|
+
// would push an empty object into whatever collects its rest props.
|
|
632
|
+
if (style !== undefined) {
|
|
633
|
+
targetProps.style = staticStyle;
|
|
634
|
+
}
|
|
635
|
+
const Target = component;
|
|
636
|
+
return _jsx(Target, { ...targetProps });
|
|
637
|
+
};
|
|
638
|
+
AnimatedWrapper.displayName = `Animated(${name})`;
|
|
639
|
+
return AnimatedWrapper;
|
|
640
|
+
};
|
|
641
|
+
/**
|
|
642
|
+
* `Animated.FlatList` is deliberately absent, and this is the refusal.
|
|
643
|
+
*
|
|
644
|
+
* FlatList is a COMPOSITE, not a host component: it renders the windowed
|
|
645
|
+
* VirtualizedList core, which renders a ScrollView, which is the only thing
|
|
646
|
+
* in that chain owning a widget. Its ref is a scroll API by contract
|
|
647
|
+
* (`scrollToIndex`/`scrollToItem`/`scrollToOffset`), so there is no handle to
|
|
648
|
+
* read a widget back out of — and giving it one would mean publishing the
|
|
649
|
+
* scrolled window through two layers that exist to hide it.
|
|
650
|
+
*
|
|
651
|
+
* Upstream's `Animated.FlatList` mostly exists so `onScroll` can be an
|
|
652
|
+
* `Animated.event`/`useAnimatedScrollHandler`, and neither of those is
|
|
653
|
+
* implemented here either. So this throws where it is used, naming itself and
|
|
654
|
+
* naming the two things that do work.
|
|
655
|
+
*/
|
|
656
|
+
const AnimatedFlatList = () => {
|
|
657
|
+
throw new Error("react-native-gtkx: `Animated.FlatList` is not implemented. FlatList is a composite — the windowed " +
|
|
658
|
+
"core over a ScrollView — and its ref is a scroll API, so there is no host widget for an animated " +
|
|
659
|
+
"style to write to. Put the animated style on an `Animated.View` wrapping the list, or use " +
|
|
660
|
+
"`Animated.ScrollView` if the list does not need virtualization. See docs/api.md.");
|
|
661
|
+
};
|
|
662
|
+
AnimatedFlatList.displayName = "Animated.FlatList";
|
|
663
|
+
/**
|
|
664
|
+
* `Text`, `Image` and `ScrollView` are the generic wrapper over the
|
|
665
|
+
* platform's own components — no subclass, no special case, and no widget
|
|
666
|
+
* added to the tree. They are animatable at all because those three now
|
|
667
|
+
* expose a ref carrying their widget, which is RN parity in its own right.
|
|
668
|
+
*/
|
|
669
|
+
const AnimatedText = createAnimatedComponent(Text);
|
|
670
|
+
const AnimatedImage = createAnimatedComponent(Image);
|
|
671
|
+
const AnimatedScrollView = createAnimatedComponent(ScrollView);
|
|
168
672
|
export const Animated = {
|
|
169
673
|
...api,
|
|
170
674
|
View: AnimatedView,
|
|
675
|
+
Text: AnimatedText,
|
|
676
|
+
Image: AnimatedImage,
|
|
677
|
+
ScrollView: AnimatedScrollView,
|
|
678
|
+
// Typed as taking the props it refuses, so the failure is at RUNTIME with
|
|
679
|
+
// the message above rather than at compile time with "no overload matches
|
|
680
|
+
// this call" — an app porting from mobile deserves the sentence, not a
|
|
681
|
+
// type puzzle.
|
|
682
|
+
FlatList: AnimatedFlatList,
|
|
683
|
+
createAnimatedComponent,
|
|
171
684
|
};
|
|
172
685
|
export { Easing } from "../animated/index";
|
|
173
686
|
//# sourceMappingURL=animated.js.map
|