react-native-gtkx 0.1.0-alpha.3 → 0.2.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 +41 -3
- package/dist/adw/index.d.ts +3 -0
- package/dist/adw/index.js +41 -0
- package/dist/adw/index.js.map +1 -0
- package/dist/adw/widgets.generated.d.ts +63 -0
- package/dist/adw/widgets.generated.js +125 -0
- package/dist/adw/widgets.generated.js.map +1 -0
- package/dist/animated/create-animated.js +1 -1
- package/dist/animated/create-animated.js.map +1 -1
- package/dist/animated/index.js +3 -3
- package/dist/animated/index.js.map +1 -1
- package/dist/common/content.d.ts +69 -0
- package/dist/common/content.js +84 -0
- package/dist/common/content.js.map +1 -0
- package/dist/common/index.d.ts +3 -0
- package/dist/common/index.js +19 -0
- package/dist/common/index.js.map +1 -0
- package/dist/common/navigation-stack.d.ts +84 -0
- package/dist/common/navigation-stack.js +313 -0
- package/dist/common/navigation-stack.js.map +1 -0
- package/dist/common/widget.d.ts +83 -0
- package/dist/common/widget.js +142 -0
- package/dist/common/widget.js.map +1 -0
- package/dist/components/animated.d.ts +3 -2
- package/dist/components/animated.js +85 -66
- package/dist/components/animated.js.map +1 -1
- package/dist/components/app-registry.d.ts +10 -1
- package/dist/components/app-registry.js +8 -3
- package/dist/components/app-registry.js.map +1 -1
- package/dist/components/pressable.js +109 -16
- package/dist/components/pressable.js.map +1 -1
- package/dist/components/rect-store.d.ts +11 -1
- package/dist/components/rect-store.js +27 -3
- package/dist/components/rect-store.js.map +1 -1
- package/dist/components/svg/animated-support.d.ts +19 -0
- package/dist/components/svg/animated-support.js +64 -0
- package/dist/components/svg/animated-support.js.map +1 -0
- package/dist/components/svg/context.d.ts +8 -0
- package/dist/components/svg/context.js +15 -0
- package/dist/components/svg/context.js.map +1 -0
- package/dist/components/svg/gradients.d.ts +31 -0
- package/dist/components/svg/gradients.js +93 -0
- package/dist/components/svg/gradients.js.map +1 -0
- package/dist/components/svg/group.d.ts +8 -0
- package/dist/components/svg/group.js +30 -0
- package/dist/components/svg/group.js.map +1 -0
- package/dist/components/svg/index.d.ts +6 -0
- package/dist/components/svg/index.js +5 -0
- package/dist/components/svg/index.js.map +1 -0
- package/dist/components/svg/node.d.ts +7 -0
- package/dist/components/svg/node.js +7 -0
- package/dist/components/svg/node.js.map +1 -0
- package/dist/components/svg/paint.d.ts +24 -0
- package/dist/components/svg/paint.js +57 -0
- package/dist/components/svg/paint.js.map +1 -0
- package/dist/components/svg/shapes.d.ts +47 -0
- package/dist/components/svg/shapes.js +51 -0
- package/dist/components/svg/shapes.js.map +1 -0
- package/dist/components/svg/svg.d.ts +14 -0
- package/dist/components/svg/svg.js +48 -0
- package/dist/components/svg/svg.js.map +1 -0
- package/dist/components/svg/use-shape-node.d.ts +12 -0
- package/dist/components/svg/use-shape-node.js +52 -0
- package/dist/components/svg/use-shape-node.js.map +1 -0
- package/dist/components/text-input.js +3 -2
- package/dist/components/text-input.js.map +1 -1
- package/dist/components/text.js +11 -2
- package/dist/components/text.js.map +1 -1
- package/dist/components/use-layout-child.js +47 -4
- package/dist/components/use-layout-child.js.map +1 -1
- package/dist/contracts.d.ts +12 -1
- package/dist/contracts.js +2 -3
- package/dist/contracts.js.map +1 -1
- package/dist/gtk/index.d.ts +6 -0
- package/dist/gtk/index.js +39 -0
- package/dist/gtk/index.js.map +1 -0
- package/dist/gtk/widgets.generated.d.ts +103 -0
- package/dist/gtk/widgets.generated.js +206 -0
- package/dist/gtk/widgets.generated.js.map +1 -0
- package/dist/gtkx/bridge/index.d.ts +9 -4
- package/dist/gtkx/bridge/index.js +41 -5
- package/dist/gtkx/bridge/index.js.map +1 -1
- package/dist/gtkx/bridge/layout-manager.d.ts +2 -1
- package/dist/gtkx/bridge/layout-manager.js +31 -9
- package/dist/gtkx/bridge/layout-manager.js.map +1 -1
- package/dist/gtkx/bridge/svg-node.d.ts +79 -0
- package/dist/gtkx/bridge/svg-node.js +367 -0
- package/dist/gtkx/bridge/svg-node.js.map +1 -0
- package/dist/gtkx/bridge/theme.js +1 -1
- package/dist/gtkx/bridge/theme.js.map +1 -1
- package/dist/gtkx/bridge/use-signal.js +21 -10
- package/dist/gtkx/bridge/use-signal.js.map +1 -1
- package/dist/gtkx/bridge/widgets.generated.d.ts +2 -0
- package/dist/gtkx/bridge/widgets.generated.js +14 -0
- package/dist/gtkx/bridge/widgets.generated.js.map +1 -0
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/mcp/bin.d.ts +2 -0
- package/dist/mcp/bin.js +17 -0
- package/dist/mcp/bin.js.map +1 -0
- package/dist/mcp/data/generated.d.ts +1282 -0
- package/dist/mcp/data/generated.js +1148 -0
- package/dist/mcp/data/generated.js.map +1 -0
- package/dist/mcp/index.d.ts +5 -0
- package/dist/mcp/index.js +9 -0
- package/dist/mcp/index.js.map +1 -0
- package/dist/mcp/list.d.ts +4 -0
- package/dist/mcp/list.js +56 -0
- package/dist/mcp/list.js.map +1 -0
- package/dist/mcp/resolve.d.ts +55 -0
- package/dist/mcp/resolve.js +151 -0
- package/dist/mcp/resolve.js.map +1 -0
- package/dist/mcp/search.d.ts +13 -0
- package/dist/mcp/search.js +48 -0
- package/dist/mcp/search.js.map +1 -0
- package/dist/mcp/server.d.ts +7 -0
- package/dist/mcp/server.js +135 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/mcp/tool.d.ts +21 -0
- package/dist/mcp/tool.js +36 -0
- package/dist/mcp/tool.js.map +1 -0
- package/dist/metro/index.js +10 -0
- package/dist/metro/index.js.map +1 -1
- package/dist/navigation/index.d.ts +50 -13
- package/dist/navigation/index.js +183 -148
- package/dist/navigation/index.js.map +1 -1
- package/dist/navigation/option-warnings.js +9 -1
- package/dist/navigation/option-warnings.js.map +1 -1
- package/dist/navigation/sidebar.d.ts +177 -9
- package/dist/navigation/sidebar.js +284 -10
- package/dist/navigation/sidebar.js.map +1 -1
- package/dist/runner/index.d.ts +22 -2
- package/dist/runner/index.js +163 -12
- package/dist/runner/index.js.map +1 -1
- package/dist/sea/assemble.d.ts +29 -0
- package/dist/sea/assemble.js +129 -0
- package/dist/sea/assemble.js.map +1 -0
- package/dist/sea/bundle.d.ts +61 -0
- package/dist/sea/bundle.js +202 -0
- package/dist/sea/bundle.js.map +1 -0
- package/dist/sea/gtkx-config-module.d.ts +18 -0
- package/dist/sea/gtkx-config-module.js +67 -0
- package/dist/sea/gtkx-config-module.js.map +1 -0
- package/dist/sea/native-shim.d.ts +40 -0
- package/dist/sea/native-shim.js +121 -0
- package/dist/sea/native-shim.js.map +1 -0
- package/dist/sea/react-anchor-plugin.d.ts +2 -0
- package/dist/sea/react-anchor-plugin.js +30 -0
- package/dist/sea/react-anchor-plugin.js.map +1 -0
- package/dist/style/colors.js +3 -1
- package/dist/style/colors.js.map +1 -1
- package/dist/style/index.d.ts +1 -0
- package/dist/style/index.js +3 -2
- package/dist/style/index.js.map +1 -1
- package/dist/style/registry.gtkx.js +1 -1
- package/dist/style/registry.gtkx.js.map +1 -1
- package/dist/style/split-style.js +3 -3
- package/dist/style/split-style.js.map +1 -1
- package/dist/style/text-align.js +1 -1
- package/dist/style/text-align.js.map +1 -1
- package/dist/style/transform.d.ts +22 -0
- package/dist/style/transform.js +137 -0
- package/dist/style/transform.js.map +1 -0
- package/dist/style/visual-css.js +3 -2
- package/dist/style/visual-css.js.map +1 -1
- package/dist/svg/geometry.d.ts +19 -0
- package/dist/svg/geometry.js +80 -0
- package/dist/svg/geometry.js.map +1 -0
- package/dist/svg/gradient-geometry.d.ts +15 -0
- package/dist/svg/gradient-geometry.js +18 -0
- package/dist/svg/gradient-geometry.js.map +1 -0
- package/dist/svg/transform.d.ts +22 -0
- package/dist/svg/transform.js +74 -0
- package/dist/svg/transform.js.map +1 -0
- package/dist/svg/view-box.d.ts +34 -0
- package/dist/svg/view-box.js +108 -0
- package/dist/svg/view-box.js.map +1 -0
- package/dist/svg-compat/index.d.ts +1 -0
- package/dist/svg-compat/index.js +16 -0
- package/dist/svg-compat/index.js.map +1 -0
- package/dist/testing/index.d.ts +14 -0
- package/dist/testing/index.js +14 -0
- package/dist/testing/index.js.map +1 -0
- package/dist/vite/index.d.ts +7 -3
- package/dist/vite/index.js +16 -3
- package/dist/vite/index.js.map +1 -1
- package/dist/vitest/index.d.ts +52 -0
- package/dist/vitest/index.js +58 -0
- package/dist/vitest/index.js.map +1 -0
- package/dist/vitest/setup.d.ts +4 -0
- package/dist/vitest/setup.js +22 -0
- package/dist/vitest/setup.js.map +1 -0
- package/package.json +47 -8
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import { useLayoutEffect, useRef, useState } from "react";
|
|
2
|
+
import { useLayoutEffect, useReducer, useRef, useState, } from "react";
|
|
3
|
+
import { splitStyle, StyleSheet } from "../style/index";
|
|
4
|
+
import { defaultCssRegistry } from "../style/registry.gtkx";
|
|
3
5
|
import { Gtk, GtkBox } from "../gtkx/bridge/index";
|
|
6
|
+
import { perfAddTime, perfCount, perfEnabled, perfNow } from "../perf";
|
|
4
7
|
import { HostNodeContext } from "./host-node";
|
|
5
8
|
import { useLayoutChild, useRnContainer, } from "./use-layout-child";
|
|
6
9
|
const pressEvent = (x, y) => ({
|
|
@@ -11,14 +14,34 @@ const pressEvent = (x, y) => ({
|
|
|
11
14
|
export const Pressable = ({ style, children, onPress, onPressIn, onPressOut, onLongPress, onHoverIn, onHoverOut, disabled = false, delayLongPress = 500, onLayout, testID, }) => {
|
|
12
15
|
const widgetRef = useRef(null);
|
|
13
16
|
const [pressed, setPressed] = useState(false);
|
|
14
|
-
|
|
17
|
+
// Hover is not React state: a boundary crossing should cost what it costs
|
|
18
|
+
// a native GtkListBox row to swap a CSS class, not a setState + render +
|
|
19
|
+
// Yoga + allocate cycle (measured: roughly 500x the latency of a native
|
|
20
|
+
// row at rest, see docs/research/scroll-performance.md, task 007).
|
|
21
|
+
// `hoveredRef` is the single source of truth, read directly at render
|
|
22
|
+
// time — safe here because this component already mutates
|
|
23
|
+
// `handlersRef.current` in the render body below, so render was never
|
|
24
|
+
// React-DOM-pure to begin with. `forceRender` is used only for the two
|
|
25
|
+
// cases that genuinely need a real render: `children` reads `hovered`,
|
|
26
|
+
// or hovering would itself change layout (not just paint).
|
|
27
|
+
const hoveredRef = useRef(false);
|
|
28
|
+
const [, forceRender] = useReducer((n) => n + 1, 0);
|
|
15
29
|
const longPressTimer = useRef(null);
|
|
16
30
|
const longPressFired = useRef(false);
|
|
17
|
-
|
|
18
|
-
//
|
|
19
|
-
//
|
|
20
|
-
//
|
|
21
|
-
//
|
|
31
|
+
// TEMPORARY perf instrumentation (task 007, see
|
|
32
|
+
// docs/research/scroll-performance.md): timestamp the native enter/leave
|
|
33
|
+
// signal so hover's real signal-to-applied latency can be reported —
|
|
34
|
+
// synchronously for the fast path below, after the resulting commit for
|
|
35
|
+
// the slow one. perf.ts hooks are no-ops unless GTKX_PERF=1.
|
|
36
|
+
const hoverSignalAt = useRef(null);
|
|
37
|
+
const state = {
|
|
38
|
+
pressed,
|
|
39
|
+
hovered: hoveredRef.current,
|
|
40
|
+
};
|
|
41
|
+
// Gestures are attached imperatively even though rc.2 has a declarative
|
|
42
|
+
// `controllers` slot: the controllers are wired exactly once per widget and
|
|
43
|
+
// the handlers read the latest props through a ref, so a re-render never
|
|
44
|
+
// detaches and re-adds a gesture mid-press.
|
|
22
45
|
const handlersRef = useRef({
|
|
23
46
|
handlePressed: (n, x, y) => {
|
|
24
47
|
void n;
|
|
@@ -36,11 +59,54 @@ export const Pressable = ({ style, children, onPress, onPressIn, onPressOut, onL
|
|
|
36
59
|
});
|
|
37
60
|
const resolvedStyle = typeof style === "function" ? style(state) : style;
|
|
38
61
|
const resolvedChildren = typeof children === "function" ? children(state) : children;
|
|
39
|
-
const { host, node, cssClass } = useLayoutChild(widgetRef, {
|
|
62
|
+
const { host, node, cssClass, flat } = useLayoutChild(widgetRef, {
|
|
40
63
|
style: resolvedStyle,
|
|
41
64
|
onLayout,
|
|
42
65
|
});
|
|
43
66
|
useRnContainer(widgetRef, node);
|
|
67
|
+
// Hover fast path: when `children` does not read state, hovering can
|
|
68
|
+
// never change what is rendered — only `style` might. Precompute the CSS
|
|
69
|
+
// class for the OTHER hover value so the native enter/leave handlers
|
|
70
|
+
// below can swap classes directly on the widget, with no React involved
|
|
71
|
+
// at all. If hovering would also change LAYOUT (rare — every hover style
|
|
72
|
+
// in this codebase's own examples is background/border) that swap is
|
|
73
|
+
// unsafe, since a real reflow is required; fall back to the ordinary
|
|
74
|
+
// render in that case, exactly as before this change.
|
|
75
|
+
let hoverOnClass = null;
|
|
76
|
+
let hoverOffClass = null;
|
|
77
|
+
let canSwapHoverClass = typeof children !== "function";
|
|
78
|
+
if (canSwapHoverClass && typeof style === "function") {
|
|
79
|
+
const otherState = {
|
|
80
|
+
pressed,
|
|
81
|
+
hovered: !hoveredRef.current,
|
|
82
|
+
};
|
|
83
|
+
const otherFlat = StyleSheet.flatten(style(otherState));
|
|
84
|
+
const otherSplit = splitStyle(otherFlat);
|
|
85
|
+
const currentLayout = splitStyle(flat).layout;
|
|
86
|
+
if (JSON.stringify(currentLayout) !== JSON.stringify(otherSplit.layout)) {
|
|
87
|
+
// Hovering resizes or repositions this widget — a CSS class alone
|
|
88
|
+
// cannot do that, it needs Yoga. Fall back to a real render.
|
|
89
|
+
canSwapHoverClass = false;
|
|
90
|
+
}
|
|
91
|
+
else {
|
|
92
|
+
const otherClass = defaultCssRegistry.getClassName(otherSplit.visual);
|
|
93
|
+
hoverOnClass = hoveredRef.current ? cssClass : otherClass;
|
|
94
|
+
hoverOffClass = hoveredRef.current ? otherClass : cssClass;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
// TEMPORARY perf instrumentation (task 007): runs after every commit, so
|
|
98
|
+
// it always lands right after whatever render the SLOW hover path
|
|
99
|
+
// triggered. A no-op when no hover signal is pending (the common case:
|
|
100
|
+
// renders triggered by anything else, or the fast path above, which
|
|
101
|
+
// measures itself synchronously) or when GTKX_PERF is unset.
|
|
102
|
+
useLayoutEffect(() => {
|
|
103
|
+
if (!perfEnabled || hoverSignalAt.current === null) {
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
perfAddTime("pressable.hoverApply", perfNow() - hoverSignalAt.current);
|
|
107
|
+
perfCount("pressable.hoverFullCycle");
|
|
108
|
+
hoverSignalAt.current = null;
|
|
109
|
+
});
|
|
44
110
|
const clearLongPress = () => {
|
|
45
111
|
if (longPressTimer.current !== null) {
|
|
46
112
|
clearTimeout(longPressTimer.current);
|
|
@@ -77,18 +143,45 @@ export const Pressable = ({ style, children, onPress, onPressIn, onPressOut, onL
|
|
|
77
143
|
clearLongPress();
|
|
78
144
|
setPressed(false);
|
|
79
145
|
};
|
|
146
|
+
// Applies a hover transition: fires the RN callback, then either swaps
|
|
147
|
+
// the precomputed CSS class directly on the widget (fast path) or forces
|
|
148
|
+
// the ordinary render (slow path) — see the `canSwapHoverClass` block
|
|
149
|
+
// above for which one applies this render.
|
|
150
|
+
const applyHover = (next, fire) => {
|
|
151
|
+
const t0 = perfEnabled ? perfNow() : 0;
|
|
152
|
+
hoveredRef.current = next;
|
|
153
|
+
fire?.();
|
|
154
|
+
if (!canSwapHoverClass) {
|
|
155
|
+
if (perfEnabled) {
|
|
156
|
+
hoverSignalAt.current = t0;
|
|
157
|
+
perfCount("pressable.hoverEvent");
|
|
158
|
+
}
|
|
159
|
+
forceRender();
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
const widget = widgetRef.current;
|
|
163
|
+
const applyClass = next ? hoverOnClass : hoverOffClass;
|
|
164
|
+
const removeClass = next ? hoverOffClass : hoverOnClass;
|
|
165
|
+
if (widget && hoverOnClass !== hoverOffClass) {
|
|
166
|
+
if (removeClass) {
|
|
167
|
+
widget.removeCssClass(removeClass);
|
|
168
|
+
}
|
|
169
|
+
if (applyClass) {
|
|
170
|
+
widget.addCssClass(applyClass);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
if (perfEnabled) {
|
|
174
|
+
perfAddTime("pressable.hoverApply", perfNow() - t0);
|
|
175
|
+
perfCount("pressable.hoverEvent");
|
|
176
|
+
perfCount("pressable.hoverFast");
|
|
177
|
+
}
|
|
178
|
+
};
|
|
80
179
|
handlersRef.current = {
|
|
81
180
|
handlePressed,
|
|
82
181
|
handleReleased,
|
|
83
182
|
handleCancel,
|
|
84
|
-
handleEnter: () =>
|
|
85
|
-
|
|
86
|
-
onHoverIn?.();
|
|
87
|
-
},
|
|
88
|
-
handleLeave: () => {
|
|
89
|
-
setHovered(false);
|
|
90
|
-
onHoverOut?.();
|
|
91
|
-
},
|
|
183
|
+
handleEnter: () => applyHover(true, onHoverIn),
|
|
184
|
+
handleLeave: () => applyHover(false, onHoverOut),
|
|
92
185
|
};
|
|
93
186
|
useLayoutEffect(() => {
|
|
94
187
|
const widget = widgetRef.current;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pressable.js","sourceRoot":"","sources":["../../src/components/pressable.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useLayoutEffect, useRef, useState, type ReactNode } from \"react\"\nimport type { StyleProp } from \"../contracts\"\nimport { Gtk, GtkBox } from \"../gtkx/bridge/index\"\nimport { HostNodeContext } from \"./host-node\"\nimport {\n useLayoutChild,\n useRnContainer,\n type LayoutEvent,\n} from \"./use-layout-child\"\n\nexport type PressableStateCallbackType = {\n pressed: boolean\n hovered: boolean\n}\n\nexport type PressEvent = { nativeEvent: { x: number; y: number } }\n\nexport type PressableProps = {\n style?: StyleProp | ((state: PressableStateCallbackType) => StyleProp)\n children?: ReactNode | ((state: PressableStateCallbackType) => ReactNode)\n onPress?: (event: PressEvent) => void\n onPressIn?: (event: PressEvent) => void\n onPressOut?: (event: PressEvent) => void\n onLongPress?: (event: PressEvent) => void\n onHoverIn?: () => void\n onHoverOut?: () => void\n disabled?: boolean\n delayLongPress?: number\n onLayout?: (event: LayoutEvent) => void\n testID?: string\n}\n\nconst pressEvent = (x: number, y: number): PressEvent => ({\n nativeEvent: { x, y },\n})\n\n// A View with a click gesture and hover tracking. State-dependent style and\n// children follow the RN Pressable function-prop contract.\nexport const Pressable = ({\n style,\n children,\n onPress,\n onPressIn,\n onPressOut,\n onLongPress,\n onHoverIn,\n onHoverOut,\n disabled = false,\n delayLongPress = 500,\n onLayout,\n testID,\n}: PressableProps) => {\n const widgetRef = useRef<Gtk.Box | null>(null)\n const [pressed, setPressed] = useState(false)\n const [hovered, setHovered] = useState(false)\n const longPressTimer = useRef<ReturnType<typeof setTimeout> | null>(null)\n const longPressFired = useRef(false)\n\n const state: PressableStateCallbackType = { pressed, hovered }\n\n // RC1-WORKAROUND(controllers-as-children): see docs/gtkx-rc1-vs-main.md\n // rc.1 has no controller-as-JSX-children support (main-only feature):\n // gestures are attached imperatively; handlers read the latest props via a\n // ref so the controllers are wired exactly once.\n const handlersRef = useRef({\n handlePressed: (n: number, x: number, y: number): void => {\n void n\n void x\n void y\n },\n handleReleased: (n: number, x: number, y: number): void => {\n void n\n void x\n void y\n },\n handleCancel: (): void => {},\n handleEnter: (): void => {},\n handleLeave: (): void => {},\n })\n const resolvedStyle = typeof style === \"function\" ? style(state) : style\n const resolvedChildren =\n typeof children === \"function\" ? children(state) : children\n\n const { host, node, cssClass } = useLayoutChild(widgetRef, {\n style: resolvedStyle,\n onLayout,\n })\n useRnContainer(widgetRef, node)\n\n const clearLongPress = (): void => {\n if (longPressTimer.current !== null) {\n clearTimeout(longPressTimer.current)\n longPressTimer.current = null\n }\n }\n\n const handlePressed = (_n: number, x: number, y: number): void => {\n if (disabled) {\n return\n }\n longPressFired.current = false\n setPressed(true)\n onPressIn?.(pressEvent(x, y))\n if (onLongPress) {\n clearLongPress()\n longPressTimer.current = setTimeout(() => {\n longPressFired.current = true\n onLongPress(pressEvent(x, y))\n }, delayLongPress)\n }\n }\n\n const handleReleased = (_n: number, x: number, y: number): void => {\n if (disabled) {\n return\n }\n clearLongPress()\n setPressed(false)\n onPressOut?.(pressEvent(x, y))\n if (!longPressFired.current) {\n onPress?.(pressEvent(x, y))\n }\n }\n\n const handleCancel = (): void => {\n clearLongPress()\n setPressed(false)\n }\n\n handlersRef.current = {\n handlePressed,\n handleReleased,\n handleCancel,\n handleEnter: () => {\n setHovered(true)\n onHoverIn?.()\n },\n handleLeave: () => {\n setHovered(false)\n onHoverOut?.()\n },\n }\n\n useLayoutEffect(() => {\n const widget = widgetRef.current\n if (!widget) {\n return\n }\n const click = new Gtk.GestureClick()\n click.on(\"pressed\", (n: number, x: number, y: number) =>\n handlersRef.current.handlePressed(n, x, y),\n )\n click.on(\"released\", (n: number, x: number, y: number) =>\n handlersRef.current.handleReleased(n, x, y),\n )\n click.on(\"cancel\", () => handlersRef.current.handleCancel())\n widget.addController(click)\n\n const motion = new Gtk.EventControllerMotion()\n motion.on(\"enter\", () => handlersRef.current.handleEnter())\n motion.on(\"leave\", () => handlersRef.current.handleLeave())\n widget.addController(motion)\n\n return () => {\n widget.removeController(click)\n widget.removeController(motion)\n }\n }, [])\n\n return (\n <GtkBox\n ref={widgetRef}\n name={testID}\n cssClasses={cssClass ? [cssClass] : []}\n >\n <HostNodeContext.Provider\n value={{ engine: host.engine, node, widgetRef }}\n >\n {resolvedChildren}\n </HostNodeContext.Provider>\n </GtkBox>\n )\n}\n\nexport type TouchableOpacityProps = Omit<PressableProps, \"style\"> & {\n style?: StyleProp\n activeOpacity?: number\n}\n\n// Classic RN touchable: dims itself while pressed via widget opacity.\nexport const TouchableOpacity = ({\n style,\n activeOpacity = 0.5,\n ...rest\n}: TouchableOpacityProps) => (\n <Pressable\n {...rest}\n style={({ pressed }) => [style, pressed && { opacity: activeOpacity }]}\n />\n)\n"]}
|
|
1
|
+
{"version":3,"file":"pressable.js","sourceRoot":"","sources":["../../src/components/pressable.tsx"],"names":[],"mappings":"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","sourcesContent":["import {\n useLayoutEffect,\n useReducer,\n useRef,\n useState,\n type ReactNode,\n} from \"react\"\nimport { splitStyle, StyleSheet } from \"../style/index\"\nimport { defaultCssRegistry } from \"../style/registry.gtkx\"\nimport type { StyleProp } from \"../contracts\"\nimport { Gtk, GtkBox } from \"../gtkx/bridge/index\"\nimport { perfAddTime, perfCount, perfEnabled, perfNow } from \"../perf\"\nimport { HostNodeContext } from \"./host-node\"\nimport {\n useLayoutChild,\n useRnContainer,\n type LayoutEvent,\n} from \"./use-layout-child\"\n\nexport type PressableStateCallbackType = {\n pressed: boolean\n hovered: boolean\n}\n\nexport type PressEvent = { nativeEvent: { x: number; y: number } }\n\nexport type PressableProps = {\n style?: StyleProp | ((state: PressableStateCallbackType) => StyleProp)\n children?: ReactNode | ((state: PressableStateCallbackType) => ReactNode)\n onPress?: (event: PressEvent) => void\n onPressIn?: (event: PressEvent) => void\n onPressOut?: (event: PressEvent) => void\n onLongPress?: (event: PressEvent) => void\n onHoverIn?: () => void\n onHoverOut?: () => void\n disabled?: boolean\n delayLongPress?: number\n onLayout?: (event: LayoutEvent) => void\n testID?: string\n}\n\nconst pressEvent = (x: number, y: number): PressEvent => ({\n nativeEvent: { x, y },\n})\n\n// A View with a click gesture and hover tracking. State-dependent style and\n// children follow the RN Pressable function-prop contract.\nexport const Pressable = ({\n style,\n children,\n onPress,\n onPressIn,\n onPressOut,\n onLongPress,\n onHoverIn,\n onHoverOut,\n disabled = false,\n delayLongPress = 500,\n onLayout,\n testID,\n}: PressableProps) => {\n const widgetRef = useRef<Gtk.Box | null>(null)\n const [pressed, setPressed] = useState(false)\n // Hover is not React state: a boundary crossing should cost what it costs\n // a native GtkListBox row to swap a CSS class, not a setState + render +\n // Yoga + allocate cycle (measured: roughly 500x the latency of a native\n // row at rest, see docs/research/scroll-performance.md, task 007).\n // `hoveredRef` is the single source of truth, read directly at render\n // time — safe here because this component already mutates\n // `handlersRef.current` in the render body below, so render was never\n // React-DOM-pure to begin with. `forceRender` is used only for the two\n // cases that genuinely need a real render: `children` reads `hovered`,\n // or hovering would itself change layout (not just paint).\n const hoveredRef = useRef(false)\n const [, forceRender] = useReducer((n: number) => n + 1, 0)\n const longPressTimer = useRef<ReturnType<typeof setTimeout> | null>(null)\n const longPressFired = useRef(false)\n // TEMPORARY perf instrumentation (task 007, see\n // docs/research/scroll-performance.md): timestamp the native enter/leave\n // signal so hover's real signal-to-applied latency can be reported —\n // synchronously for the fast path below, after the resulting commit for\n // the slow one. perf.ts hooks are no-ops unless GTKX_PERF=1.\n const hoverSignalAt = useRef<number | null>(null)\n\n const state: PressableStateCallbackType = {\n pressed,\n hovered: hoveredRef.current,\n }\n\n // Gestures are attached imperatively even though rc.2 has a declarative\n // `controllers` slot: the controllers are wired exactly once per widget and\n // the handlers read the latest props through a ref, so a re-render never\n // detaches and re-adds a gesture mid-press.\n const handlersRef = useRef({\n handlePressed: (n: number, x: number, y: number): void => {\n void n\n void x\n void y\n },\n handleReleased: (n: number, x: number, y: number): void => {\n void n\n void x\n void y\n },\n handleCancel: (): void => {},\n handleEnter: (): void => {},\n handleLeave: (): void => {},\n })\n const resolvedStyle = typeof style === \"function\" ? style(state) : style\n const resolvedChildren =\n typeof children === \"function\" ? children(state) : children\n\n const { host, node, cssClass, flat } = useLayoutChild(widgetRef, {\n style: resolvedStyle,\n onLayout,\n })\n useRnContainer(widgetRef, node)\n\n // Hover fast path: when `children` does not read state, hovering can\n // never change what is rendered — only `style` might. Precompute the CSS\n // class for the OTHER hover value so the native enter/leave handlers\n // below can swap classes directly on the widget, with no React involved\n // at all. If hovering would also change LAYOUT (rare — every hover style\n // in this codebase's own examples is background/border) that swap is\n // unsafe, since a real reflow is required; fall back to the ordinary\n // render in that case, exactly as before this change.\n let hoverOnClass: string | null = null\n let hoverOffClass: string | null = null\n let canSwapHoverClass = typeof children !== \"function\"\n if (canSwapHoverClass && typeof style === \"function\") {\n const otherState: PressableStateCallbackType = {\n pressed,\n hovered: !hoveredRef.current,\n }\n const otherFlat = StyleSheet.flatten(style(otherState))\n const otherSplit = splitStyle(otherFlat)\n const currentLayout = splitStyle(flat).layout\n if (JSON.stringify(currentLayout) !== JSON.stringify(otherSplit.layout)) {\n // Hovering resizes or repositions this widget — a CSS class alone\n // cannot do that, it needs Yoga. Fall back to a real render.\n canSwapHoverClass = false\n } else {\n const otherClass = defaultCssRegistry.getClassName(otherSplit.visual)\n hoverOnClass = hoveredRef.current ? cssClass : otherClass\n hoverOffClass = hoveredRef.current ? otherClass : cssClass\n }\n }\n\n // TEMPORARY perf instrumentation (task 007): runs after every commit, so\n // it always lands right after whatever render the SLOW hover path\n // triggered. A no-op when no hover signal is pending (the common case:\n // renders triggered by anything else, or the fast path above, which\n // measures itself synchronously) or when GTKX_PERF is unset.\n useLayoutEffect(() => {\n if (!perfEnabled || hoverSignalAt.current === null) {\n return\n }\n perfAddTime(\"pressable.hoverApply\", perfNow() - hoverSignalAt.current)\n perfCount(\"pressable.hoverFullCycle\")\n hoverSignalAt.current = null\n })\n\n const clearLongPress = (): void => {\n if (longPressTimer.current !== null) {\n clearTimeout(longPressTimer.current)\n longPressTimer.current = null\n }\n }\n\n const handlePressed = (_n: number, x: number, y: number): void => {\n if (disabled) {\n return\n }\n longPressFired.current = false\n setPressed(true)\n onPressIn?.(pressEvent(x, y))\n if (onLongPress) {\n clearLongPress()\n longPressTimer.current = setTimeout(() => {\n longPressFired.current = true\n onLongPress(pressEvent(x, y))\n }, delayLongPress)\n }\n }\n\n const handleReleased = (_n: number, x: number, y: number): void => {\n if (disabled) {\n return\n }\n clearLongPress()\n setPressed(false)\n onPressOut?.(pressEvent(x, y))\n if (!longPressFired.current) {\n onPress?.(pressEvent(x, y))\n }\n }\n\n const handleCancel = (): void => {\n clearLongPress()\n setPressed(false)\n }\n\n // Applies a hover transition: fires the RN callback, then either swaps\n // the precomputed CSS class directly on the widget (fast path) or forces\n // the ordinary render (slow path) — see the `canSwapHoverClass` block\n // above for which one applies this render.\n const applyHover = (next: boolean, fire: (() => void) | undefined): void => {\n const t0 = perfEnabled ? perfNow() : 0\n hoveredRef.current = next\n fire?.()\n if (!canSwapHoverClass) {\n if (perfEnabled) {\n hoverSignalAt.current = t0\n perfCount(\"pressable.hoverEvent\")\n }\n forceRender()\n return\n }\n const widget = widgetRef.current\n const applyClass = next ? hoverOnClass : hoverOffClass\n const removeClass = next ? hoverOffClass : hoverOnClass\n if (widget && hoverOnClass !== hoverOffClass) {\n if (removeClass) {\n widget.removeCssClass(removeClass)\n }\n if (applyClass) {\n widget.addCssClass(applyClass)\n }\n }\n if (perfEnabled) {\n perfAddTime(\"pressable.hoverApply\", perfNow() - t0)\n perfCount(\"pressable.hoverEvent\")\n perfCount(\"pressable.hoverFast\")\n }\n }\n\n handlersRef.current = {\n handlePressed,\n handleReleased,\n handleCancel,\n handleEnter: () => applyHover(true, onHoverIn),\n handleLeave: () => applyHover(false, onHoverOut),\n }\n\n useLayoutEffect(() => {\n const widget = widgetRef.current\n if (!widget) {\n return\n }\n const click = new Gtk.GestureClick()\n click.on(\"pressed\", (n: number, x: number, y: number) =>\n handlersRef.current.handlePressed(n, x, y),\n )\n click.on(\"released\", (n: number, x: number, y: number) =>\n handlersRef.current.handleReleased(n, x, y),\n )\n click.on(\"cancel\", () => handlersRef.current.handleCancel())\n widget.addController(click)\n\n const motion = new Gtk.EventControllerMotion()\n motion.on(\"enter\", () => handlersRef.current.handleEnter())\n motion.on(\"leave\", () => handlersRef.current.handleLeave())\n widget.addController(motion)\n\n return () => {\n widget.removeController(click)\n widget.removeController(motion)\n }\n }, [])\n\n return (\n <GtkBox\n ref={widgetRef}\n name={testID}\n cssClasses={cssClass ? [cssClass] : []}\n >\n <HostNodeContext.Provider\n value={{ engine: host.engine, node, widgetRef }}\n >\n {resolvedChildren}\n </HostNodeContext.Provider>\n </GtkBox>\n )\n}\n\nexport type TouchableOpacityProps = Omit<PressableProps, \"style\"> & {\n style?: StyleProp\n activeOpacity?: number\n}\n\n// Classic RN touchable: dims itself while pressed via widget opacity.\nexport const TouchableOpacity = ({\n style,\n activeOpacity = 0.5,\n ...rest\n}: TouchableOpacityProps) => (\n <Pressable\n {...rest}\n style={({ pressed }) => [style, pressed && { opacity: activeOpacity }]}\n />\n)\n"]}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { Transform2D, TransformPart } from "../contracts";
|
|
1
2
|
export type StoredRect = {
|
|
2
3
|
x: number;
|
|
3
4
|
y: number;
|
|
@@ -7,11 +8,20 @@ export type StoredRect = {
|
|
|
7
8
|
export type StoredOffset = {
|
|
8
9
|
dx: number;
|
|
9
10
|
dy: number;
|
|
11
|
+
matrix: Transform2D | null;
|
|
10
12
|
};
|
|
11
13
|
export declare const setStoredRect: (widget: object, rect: StoredRect) => void;
|
|
12
14
|
export declare const getStoredRect: (widget: object) => StoredRect | undefined;
|
|
13
|
-
export declare const setStoredOffset: (widget: object, dx: number, dy: number) => void;
|
|
15
|
+
export declare const setStoredOffset: (widget: object, dx: number, dy: number, matrix?: Transform2D | null) => void;
|
|
14
16
|
export declare const getStoredOffset: (widget: object) => StoredOffset;
|
|
17
|
+
/**
|
|
18
|
+
* Stores a child's whole RN `transform` array, split the way the allocate
|
|
19
|
+
* hook wants it: a pure translation keeps the positional path (offset the
|
|
20
|
+
* rect, no GskTransform at all — the pre-rotate/scale behaviour), anything
|
|
21
|
+
* that rotates or scales carries the composed matrix instead, translations
|
|
22
|
+
* folded in so the array's order is preserved exactly.
|
|
23
|
+
*/
|
|
24
|
+
export declare const setStoredTransform: (widget: object, parts: readonly TransformPart[] | undefined) => void;
|
|
15
25
|
export declare const beginAllocatePass: () => void;
|
|
16
26
|
export declare const endAllocatePass: () => void;
|
|
17
27
|
export declare const deferDuringAllocate: (widget: object, job: () => void) => boolean;
|
|
@@ -1,14 +1,38 @@
|
|
|
1
|
-
|
|
1
|
+
// Committed engine rects and animated offsets, keyed by the CHILD widget.
|
|
2
|
+
// Written by commit callbacks (use-layout-child) and by Animated bindings;
|
|
3
|
+
// read by the parent container's RnGtkxLayout allocate() hook. Pure data —
|
|
4
|
+
// no FFI here, so both writers and the bridge-side reader stay testable.
|
|
5
|
+
import { composeTransform, isTranslationOnly } from "../style/transform";
|
|
6
|
+
const ZERO_OFFSET = { dx: 0, dy: 0, matrix: null };
|
|
2
7
|
const rects = new WeakMap();
|
|
3
8
|
const offsets = new WeakMap();
|
|
4
9
|
export const setStoredRect = (widget, rect) => {
|
|
5
10
|
rects.set(widget, rect);
|
|
6
11
|
};
|
|
7
12
|
export const getStoredRect = (widget) => rects.get(widget);
|
|
8
|
-
export const setStoredOffset = (widget, dx, dy) => {
|
|
9
|
-
offsets.set(widget, { dx, dy });
|
|
13
|
+
export const setStoredOffset = (widget, dx, dy, matrix = null) => {
|
|
14
|
+
offsets.set(widget, { dx, dy, matrix });
|
|
10
15
|
};
|
|
11
16
|
export const getStoredOffset = (widget) => offsets.get(widget) ?? ZERO_OFFSET;
|
|
17
|
+
/**
|
|
18
|
+
* Stores a child's whole RN `transform` array, split the way the allocate
|
|
19
|
+
* hook wants it: a pure translation keeps the positional path (offset the
|
|
20
|
+
* rect, no GskTransform at all — the pre-rotate/scale behaviour), anything
|
|
21
|
+
* that rotates or scales carries the composed matrix instead, translations
|
|
22
|
+
* folded in so the array's order is preserved exactly.
|
|
23
|
+
*/
|
|
24
|
+
export const setStoredTransform = (widget, parts) => {
|
|
25
|
+
if (!parts || parts.length === 0) {
|
|
26
|
+
setStoredOffset(widget, 0, 0, null);
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
const matrix = composeTransform(parts);
|
|
30
|
+
if (isTranslationOnly(matrix)) {
|
|
31
|
+
setStoredOffset(widget, matrix.dx, matrix.dy, null);
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
setStoredOffset(widget, 0, 0, matrix);
|
|
35
|
+
};
|
|
12
36
|
// While a window-root allocate() pass runs the engine synchronously, commit
|
|
13
37
|
// callbacks must not queue GTK resizes mid-pass — the pass itself is about to
|
|
14
38
|
// place everything. Queue jobs are deferred (deduped per widget) and run once
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rect-store.js","sourceRoot":"","sources":["../../src/components/rect-store.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"rect-store.js","sourceRoot":"","sources":["../../src/components/rect-store.ts"],"names":[],"mappings":"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","sourcesContent":["// Committed engine rects and animated offsets, keyed by the CHILD widget.\n// Written by commit callbacks (use-layout-child) and by Animated bindings;\n// read by the parent container's RnGtkxLayout allocate() hook. Pure data —\n// no FFI here, so both writers and the bridge-side reader stay testable.\nimport { composeTransform, isTranslationOnly } from \"../style/transform\"\nimport type { Transform2D, TransformPart } from \"../contracts\"\n\nexport type StoredRect = {\n x: number\n y: number\n width: number\n height: number\n}\n\n// The visual transform of one child, split by cost. A pure translation stays\n// `matrix: null` and is applied by simply offsetting the rect — the path that\n// existed before rotate/scale and that costs no GskTransform. Anything that\n// rotates or scales carries the whole composed matrix (translations\n// included, so the RN ordering is preserved) and pays one gsk call per\n// allocation. Both live in ONE record on purpose: the allocate hook already\n// reads it per child, so a transform costs no extra WeakMap lookup.\nexport type StoredOffset = {\n dx: number\n dy: number\n matrix: Transform2D | null\n}\n\nconst ZERO_OFFSET: StoredOffset = { dx: 0, dy: 0, matrix: null }\n\nconst rects = new WeakMap<object, StoredRect>()\nconst offsets = new WeakMap<object, StoredOffset>()\n\nexport const setStoredRect = (widget: object, rect: StoredRect): void => {\n rects.set(widget, rect)\n}\n\nexport const getStoredRect = (widget: object): StoredRect | undefined =>\n rects.get(widget)\n\nexport const setStoredOffset = (\n widget: object,\n dx: number,\n dy: number,\n matrix: Transform2D | null = null,\n): void => {\n offsets.set(widget, { dx, dy, matrix })\n}\n\nexport const getStoredOffset = (widget: object): StoredOffset =>\n offsets.get(widget) ?? ZERO_OFFSET\n\n/**\n * Stores a child's whole RN `transform` array, split the way the allocate\n * hook wants it: a pure translation keeps the positional path (offset the\n * rect, no GskTransform at all — the pre-rotate/scale behaviour), anything\n * that rotates or scales carries the composed matrix instead, translations\n * folded in so the array's order is preserved exactly.\n */\nexport const setStoredTransform = (\n widget: object,\n parts: readonly TransformPart[] | undefined,\n): void => {\n if (!parts || parts.length === 0) {\n setStoredOffset(widget, 0, 0, null)\n return\n }\n const matrix = composeTransform(parts)\n if (isTranslationOnly(matrix)) {\n setStoredOffset(widget, matrix.dx, matrix.dy, null)\n return\n }\n setStoredOffset(widget, 0, 0, matrix)\n}\n\n// While a window-root allocate() pass runs the engine synchronously, commit\n// callbacks must not queue GTK resizes mid-pass — the pass itself is about to\n// place everything. Queue jobs are deferred (deduped per widget) and run once\n// the pass ends; commits whose rect did not change never re-defer, so the\n// follow-up converges.\nlet allocatePassDepth = 0\nconst deferredJobs = new Map<object, () => void>()\n\nexport const beginAllocatePass = (): void => {\n allocatePassDepth += 1\n}\n\nexport const endAllocatePass = (): void => {\n allocatePassDepth -= 1\n if (allocatePassDepth === 0 && deferredJobs.size > 0) {\n const jobs = [...deferredJobs.values()]\n deferredJobs.clear()\n for (const job of jobs) {\n job()\n }\n }\n}\n\n// Returns true when the job was deferred (an allocate pass is running).\nexport const deferDuringAllocate = (\n widget: object,\n job: () => void,\n): boolean => {\n if (allocatePassDepth === 0) {\n return false\n }\n deferredJobs.set(widget, job)\n return true\n}\n"]}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export type AnimatedNodeLike = {
|
|
2
|
+
addListener(callback: (state: {
|
|
3
|
+
value: number;
|
|
4
|
+
}) => void): string;
|
|
5
|
+
removeListener(id: string): void;
|
|
6
|
+
__getValue(): number;
|
|
7
|
+
};
|
|
8
|
+
export type AnimatableNumber = number | AnimatedNodeLike;
|
|
9
|
+
export declare const isAnimatedNode: (value: unknown) => value is AnimatedNodeLike;
|
|
10
|
+
/**
|
|
11
|
+
* Resolves a set of possibly-animated numeric fields and calls `build` with
|
|
12
|
+
* the fully-resolved snapshot: once synchronously (mount / whenever a
|
|
13
|
+
* field's identity changes — a new plain number, a new Animated node, or a
|
|
14
|
+
* switch between the two) and again on every tick of any field that is
|
|
15
|
+
* currently animated. `build` itself is read from a ref on every call, so a
|
|
16
|
+
* re-render between ticks always reaches the latest closure without forcing
|
|
17
|
+
* a re-subscribe.
|
|
18
|
+
*/
|
|
19
|
+
export declare const useAnimatedShapeBuild: (fields: Record<string, AnimatableNumber>, build: (resolved: Record<string, number>) => void, extraDeps?: readonly unknown[]) => void;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
// Shared "own invalidation channel" for SVG: geometry/paint numeric props
|
|
2
|
+
// accept a plain number OR an Animated node (Animated.Value /
|
|
3
|
+
// AnimatedInterpolation). Same duck-typed detection as components/
|
|
4
|
+
// animated.tsx's isAnimatedNode — kept as a separate small copy rather than
|
|
5
|
+
// a shared export because the two call sites want different subscription
|
|
6
|
+
// targets (rect-store + queueAllocate there, an SVG descriptor + queueDraw
|
|
7
|
+
// here) and duplicating a five-line predicate is cheaper than coupling them.
|
|
8
|
+
//
|
|
9
|
+
// Every tick mutates the resolved-value snapshot and calls `build` directly
|
|
10
|
+
// — no React re-render, no setState, matching the epic's answer to "how does
|
|
11
|
+
// Animated redraw a path": queueDraw, not queueAllocate.
|
|
12
|
+
import { useLayoutEffect, useRef } from "react";
|
|
13
|
+
export const isAnimatedNode = (value) => typeof value === "object" &&
|
|
14
|
+
value !== null &&
|
|
15
|
+
typeof value.addListener === "function" &&
|
|
16
|
+
typeof value.__getValue === "function";
|
|
17
|
+
const resolve = (value) => isAnimatedNode(value) ? value.__getValue() : value;
|
|
18
|
+
/**
|
|
19
|
+
* Resolves a set of possibly-animated numeric fields and calls `build` with
|
|
20
|
+
* the fully-resolved snapshot: once synchronously (mount / whenever a
|
|
21
|
+
* field's identity changes — a new plain number, a new Animated node, or a
|
|
22
|
+
* switch between the two) and again on every tick of any field that is
|
|
23
|
+
* currently animated. `build` itself is read from a ref on every call, so a
|
|
24
|
+
* re-render between ticks always reaches the latest closure without forcing
|
|
25
|
+
* a re-subscribe.
|
|
26
|
+
*/
|
|
27
|
+
// `extraDeps` covers everything `build` closes over that is NOT one of the
|
|
28
|
+
// animatable `fields` — d/points strings, transform strings, fill/stroke and
|
|
29
|
+
// the rest of the static paint props. Without them here, a change to (say)
|
|
30
|
+
// `fill` alone would not re-run `build` until some unrelated field changed.
|
|
31
|
+
export const useAnimatedShapeBuild = (fields, build, extraDeps = []) => {
|
|
32
|
+
const buildRef = useRef(build);
|
|
33
|
+
buildRef.current = build;
|
|
34
|
+
const keys = Object.keys(fields);
|
|
35
|
+
const values = [...keys.map((key) => fields[key]), ...extraDeps];
|
|
36
|
+
useLayoutEffect(() => {
|
|
37
|
+
const resolved = {};
|
|
38
|
+
for (const key of keys) {
|
|
39
|
+
resolved[key] = resolve(fields[key]);
|
|
40
|
+
}
|
|
41
|
+
const run = () => buildRef.current(resolved);
|
|
42
|
+
run();
|
|
43
|
+
const subscriptions = [];
|
|
44
|
+
for (const key of keys) {
|
|
45
|
+
const source = fields[key];
|
|
46
|
+
if (isAnimatedNode(source)) {
|
|
47
|
+
const id = source.addListener(({ value }) => {
|
|
48
|
+
resolved[key] = value;
|
|
49
|
+
run();
|
|
50
|
+
});
|
|
51
|
+
subscriptions.push({ node: source, id });
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
return () => {
|
|
55
|
+
for (const { node, id } of subscriptions) {
|
|
56
|
+
node.removeListener(id);
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
// `values` is the real dependency list (flattened field values/node
|
|
60
|
+
// identities); `keys`/`fields`/`build` are read through refs/closures.
|
|
61
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
62
|
+
}, values);
|
|
63
|
+
};
|
|
64
|
+
//# sourceMappingURL=animated-support.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"animated-support.js","sourceRoot":"","sources":["../../../src/components/svg/animated-support.ts"],"names":[],"mappings":"AAAA,0EAA0E;AAC1E,8DAA8D;AAC9D,mEAAmE;AACnE,4EAA4E;AAC5E,yEAAyE;AACzE,2EAA2E;AAC3E,6EAA6E;AAC7E,EAAE;AACF,4EAA4E;AAC5E,6EAA6E;AAC7E,yDAAyD;AACzD,OAAO,EAAE,eAAe,EAAE,MAAM,EAAE,MAAM,OAAO,CAAA;AAU/C,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,KAAc,EAA6B,EAAE,CAC1E,OAAO,KAAK,KAAK,QAAQ;IACzB,KAAK,KAAK,IAAI;IACd,OAAQ,KAA0B,CAAC,WAAW,KAAK,UAAU;IAC7D,OAAQ,KAA0B,CAAC,UAAU,KAAK,UAAU,CAAA;AAE9D,MAAM,OAAO,GAAG,CAAC,KAAuB,EAAU,EAAE,CAClD,cAAc,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC,KAAK,CAAA;AAEpD;;;;;;;;GAQG;AACH,2EAA2E;AAC3E,6EAA6E;AAC7E,2EAA2E;AAC3E,4EAA4E;AAC5E,MAAM,CAAC,MAAM,qBAAqB,GAAG,CACnC,MAAwC,EACxC,KAAiD,EACjD,YAAgC,EAAE,EAC5B,EAAE;IACR,MAAM,QAAQ,GAAG,MAAM,CAAC,KAAK,CAAC,CAAA;IAC9B,QAAQ,CAAC,OAAO,GAAG,KAAK,CAAA;IAExB,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;IAChC,MAAM,MAAM,GAAG,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,EAAE,GAAG,SAAS,CAAC,CAAA;IAEhE,eAAe,CAAC,GAAG,EAAE;QACnB,MAAM,QAAQ,GAA2B,EAAE,CAAA;QAC3C,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;YACvB,QAAQ,CAAC,GAAG,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,GAAG,CAAE,CAAC,CAAA;QACvC,CAAC;QACD,MAAM,GAAG,GAAG,GAAS,EAAE,CAAC,QAAQ,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAA;QAClD,GAAG,EAAE,CAAA;QAEL,MAAM,aAAa,GAA6C,EAAE,CAAA;QAClE,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;YACvB,MAAM,MAAM,GAAG,MAAM,CAAC,GAAG,CAAE,CAAA;YAC3B,IAAI,cAAc,CAAC,MAAM,CAAC,EAAE,CAAC;gBAC3B,MAAM,EAAE,GAAG,MAAM,CAAC,WAAW,CAAC,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE;oBAC1C,QAAQ,CAAC,GAAG,CAAC,GAAG,KAAK,CAAA;oBACrB,GAAG,EAAE,CAAA;gBACP,CAAC,CAAC,CAAA;gBACF,aAAa,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,CAAA;YAC1C,CAAC;QACH,CAAC;QACD,OAAO,GAAG,EAAE;YACV,KAAK,MAAM,EAAE,IAAI,EAAE,EAAE,EAAE,IAAI,aAAa,EAAE,CAAC;gBACzC,IAAI,CAAC,cAAc,CAAC,EAAE,CAAC,CAAA;YACzB,CAAC;QACH,CAAC,CAAA;QACD,oEAAoE;QACpE,uEAAuE;QACvE,uDAAuD;IACzD,CAAC,EAAE,MAAM,CAAC,CAAA;AACZ,CAAC,CAAA","sourcesContent":["// Shared \"own invalidation channel\" for SVG: geometry/paint numeric props\n// accept a plain number OR an Animated node (Animated.Value /\n// AnimatedInterpolation). Same duck-typed detection as components/\n// animated.tsx's isAnimatedNode — kept as a separate small copy rather than\n// a shared export because the two call sites want different subscription\n// targets (rect-store + queueAllocate there, an SVG descriptor + queueDraw\n// here) and duplicating a five-line predicate is cheaper than coupling them.\n//\n// Every tick mutates the resolved-value snapshot and calls `build` directly\n// — no React re-render, no setState, matching the epic's answer to \"how does\n// Animated redraw a path\": queueDraw, not queueAllocate.\nimport { useLayoutEffect, useRef } from \"react\"\n\nexport type AnimatedNodeLike = {\n addListener(callback: (state: { value: number }) => void): string\n removeListener(id: string): void\n __getValue(): number\n}\n\nexport type AnimatableNumber = number | AnimatedNodeLike\n\nexport const isAnimatedNode = (value: unknown): value is AnimatedNodeLike =>\n typeof value === \"object\" &&\n value !== null &&\n typeof (value as AnimatedNodeLike).addListener === \"function\" &&\n typeof (value as AnimatedNodeLike).__getValue === \"function\"\n\nconst resolve = (value: AnimatableNumber): number =>\n isAnimatedNode(value) ? value.__getValue() : value\n\n/**\n * Resolves a set of possibly-animated numeric fields and calls `build` with\n * the fully-resolved snapshot: once synchronously (mount / whenever a\n * field's identity changes — a new plain number, a new Animated node, or a\n * switch between the two) and again on every tick of any field that is\n * currently animated. `build` itself is read from a ref on every call, so a\n * re-render between ticks always reaches the latest closure without forcing\n * a re-subscribe.\n */\n// `extraDeps` covers everything `build` closes over that is NOT one of the\n// animatable `fields` — d/points strings, transform strings, fill/stroke and\n// the rest of the static paint props. Without them here, a change to (say)\n// `fill` alone would not re-run `build` until some unrelated field changed.\nexport const useAnimatedShapeBuild = (\n fields: Record<string, AnimatableNumber>,\n build: (resolved: Record<string, number>) => void,\n extraDeps: readonly unknown[] = [],\n): void => {\n const buildRef = useRef(build)\n buildRef.current = build\n\n const keys = Object.keys(fields)\n const values = [...keys.map((key) => fields[key]), ...extraDeps]\n\n useLayoutEffect(() => {\n const resolved: Record<string, number> = {}\n for (const key of keys) {\n resolved[key] = resolve(fields[key]!)\n }\n const run = (): void => buildRef.current(resolved)\n run()\n\n const subscriptions: { node: AnimatedNodeLike; id: string }[] = []\n for (const key of keys) {\n const source = fields[key]!\n if (isAnimatedNode(source)) {\n const id = source.addListener(({ value }) => {\n resolved[key] = value\n run()\n })\n subscriptions.push({ node: source, id })\n }\n }\n return () => {\n for (const { node, id } of subscriptions) {\n node.removeListener(id)\n }\n }\n // `values` is the real dependency list (flattened field values/node\n // identities); `keys`/`fields`/`build` are read through refs/closures.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, values)\n}\n"]}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { type RefObject } from "react";
|
|
2
|
+
import type { Gtk } from "../../gtkx/bridge/index";
|
|
3
|
+
export type SvgRootContextValue = {
|
|
4
|
+
rootRef: RefObject<Gtk.Widget | null>;
|
|
5
|
+
requestRedraw: () => void;
|
|
6
|
+
};
|
|
7
|
+
export declare const SvgRootContext: React.Context<SvgRootContextValue | null>;
|
|
8
|
+
export declare const useSvgRoot: (componentName: string) => SvgRootContextValue;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// Context handed down from <Svg> to every descendant: a ref to the root
|
|
2
|
+
// widget (so a leaf can call queueDraw without walking the GTK tree upward)
|
|
3
|
+
// plus a bound convenience wrapper. Mirrors HostNodeContext (host-node.ts)
|
|
4
|
+
// for the Yoga tree — same idea, separate tree, because SVG nodes carry no
|
|
5
|
+
// Yoga node at all (see svg.tsx).
|
|
6
|
+
import { createContext, useContext } from "react";
|
|
7
|
+
export const SvgRootContext = createContext(null);
|
|
8
|
+
export const useSvgRoot = (componentName) => {
|
|
9
|
+
const context = useContext(SvgRootContext);
|
|
10
|
+
if (!context) {
|
|
11
|
+
throw new Error(`<${componentName}> must be rendered inside <Svg>`);
|
|
12
|
+
}
|
|
13
|
+
return context;
|
|
14
|
+
};
|
|
15
|
+
//# sourceMappingURL=context.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context.js","sourceRoot":"","sources":["../../../src/components/svg/context.ts"],"names":[],"mappings":"AAAA,wEAAwE;AACxE,4EAA4E;AAC5E,2EAA2E;AAC3E,2EAA2E;AAC3E,kCAAkC;AAClC,OAAO,EAAE,aAAa,EAAE,UAAU,EAAkB,MAAM,OAAO,CAAA;AAQjE,MAAM,CAAC,MAAM,cAAc,GAAG,aAAa,CAA6B,IAAI,CAAC,CAAA;AAE7E,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,aAAqB,EAAuB,EAAE;IACvE,MAAM,OAAO,GAAG,UAAU,CAAC,cAAc,CAAC,CAAA;IAC1C,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,MAAM,IAAI,KAAK,CAAC,IAAI,aAAa,iCAAiC,CAAC,CAAA;IACrE,CAAC;IACD,OAAO,OAAO,CAAA;AAChB,CAAC,CAAA","sourcesContent":["// Context handed down from <Svg> to every descendant: a ref to the root\n// widget (so a leaf can call queueDraw without walking the GTK tree upward)\n// plus a bound convenience wrapper. Mirrors HostNodeContext (host-node.ts)\n// for the Yoga tree — same idea, separate tree, because SVG nodes carry no\n// Yoga node at all (see svg.tsx).\nimport { createContext, useContext, type RefObject } from \"react\"\nimport type { Gtk } from \"../../gtkx/bridge/index\"\n\nexport type SvgRootContextValue = {\n rootRef: RefObject<Gtk.Widget | null>\n requestRedraw: () => void\n}\n\nexport const SvgRootContext = createContext<SvgRootContextValue | null>(null)\n\nexport const useSvgRoot = (componentName: string): SvgRootContextValue => {\n const context = useContext(SvgRootContext)\n if (!context) {\n throw new Error(`<${componentName}> must be rendered inside <Svg>`)\n }\n return context\n}\n"]}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { type ReactNode } from "react";
|
|
2
|
+
import { type SvgGradientUnits } from "../../gtkx/bridge/index";
|
|
3
|
+
export type DefsProps = {
|
|
4
|
+
children?: ReactNode;
|
|
5
|
+
};
|
|
6
|
+
export declare const Defs: ({ children }: DefsProps) => React.JSX.Element;
|
|
7
|
+
export type LinearGradientProps = {
|
|
8
|
+
id: string;
|
|
9
|
+
x1?: number;
|
|
10
|
+
y1?: number;
|
|
11
|
+
x2?: number;
|
|
12
|
+
y2?: number;
|
|
13
|
+
gradientUnits?: SvgGradientUnits;
|
|
14
|
+
children?: ReactNode;
|
|
15
|
+
};
|
|
16
|
+
export declare const LinearGradient: ({ id, x1, y1, x2, y2, gradientUnits, children, }: LinearGradientProps) => React.JSX.Element;
|
|
17
|
+
export type RadialGradientProps = {
|
|
18
|
+
id: string;
|
|
19
|
+
cx?: number;
|
|
20
|
+
cy?: number;
|
|
21
|
+
r?: number;
|
|
22
|
+
gradientUnits?: SvgGradientUnits;
|
|
23
|
+
children?: ReactNode;
|
|
24
|
+
};
|
|
25
|
+
export declare const RadialGradient: ({ id, cx, cy, r, gradientUnits, children, }: RadialGradientProps) => React.JSX.Element;
|
|
26
|
+
export type StopProps = {
|
|
27
|
+
offset: number | string;
|
|
28
|
+
stopColor?: string;
|
|
29
|
+
stopOpacity?: number;
|
|
30
|
+
};
|
|
31
|
+
export declare const Stop: ({ offset, stopColor, stopOpacity, }: StopProps) => React.JSX.Element;
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
// <Defs>/<LinearGradient>/<RadialGradient>/<Stop> — paint resources, not
|
|
3
|
+
// visible nodes. They ride the same RnGtkxSvgNode tree as everything else
|
|
4
|
+
// (so the bridge's snapshot() can walk <Defs> separately, before the main
|
|
5
|
+
// paint pass, to build an id → gradient map — see svg-node.ts), but are
|
|
6
|
+
// never part of the Animated numeric channel: gradients are definitions,
|
|
7
|
+
// not painted geometry, and nothing in scope needs an animated stop.
|
|
8
|
+
import { useLayoutEffect, useRef } from "react";
|
|
9
|
+
import { Gdk, resolveSvgColor, setSvgNodeDescriptor, } from "../../gtkx/bridge/index";
|
|
10
|
+
import { SvgNodeElement } from "./node";
|
|
11
|
+
export const Defs = ({ children }) => {
|
|
12
|
+
const widgetRef = useRef(null);
|
|
13
|
+
useLayoutEffect(() => {
|
|
14
|
+
const widget = widgetRef.current;
|
|
15
|
+
if (widget) {
|
|
16
|
+
setSvgNodeDescriptor(widget, { kind: "defs" });
|
|
17
|
+
}
|
|
18
|
+
}, []);
|
|
19
|
+
return _jsx(SvgNodeElement, { widgetRef: widgetRef, children: children });
|
|
20
|
+
};
|
|
21
|
+
export const LinearGradient = ({ id, x1 = 0, y1 = 0, x2 = 1, y2 = 0, gradientUnits = "objectBoundingBox", children, }) => {
|
|
22
|
+
const widgetRef = useRef(null);
|
|
23
|
+
useLayoutEffect(() => {
|
|
24
|
+
const widget = widgetRef.current;
|
|
25
|
+
if (widget) {
|
|
26
|
+
setSvgNodeDescriptor(widget, {
|
|
27
|
+
kind: "gradient",
|
|
28
|
+
type: "linear",
|
|
29
|
+
id,
|
|
30
|
+
units: gradientUnits,
|
|
31
|
+
x1,
|
|
32
|
+
y1,
|
|
33
|
+
x2,
|
|
34
|
+
y2,
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
}, [id, x1, y1, x2, y2, gradientUnits]);
|
|
38
|
+
return _jsx(SvgNodeElement, { widgetRef: widgetRef, children: children });
|
|
39
|
+
};
|
|
40
|
+
export const RadialGradient = ({ id, cx = 0.5, cy = 0.5, r = 0.5, gradientUnits = "objectBoundingBox", children, }) => {
|
|
41
|
+
const widgetRef = useRef(null);
|
|
42
|
+
useLayoutEffect(() => {
|
|
43
|
+
const widget = widgetRef.current;
|
|
44
|
+
if (widget) {
|
|
45
|
+
setSvgNodeDescriptor(widget, {
|
|
46
|
+
kind: "gradient",
|
|
47
|
+
type: "radial",
|
|
48
|
+
id,
|
|
49
|
+
units: gradientUnits,
|
|
50
|
+
cx,
|
|
51
|
+
cy,
|
|
52
|
+
r,
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
}, [id, cx, cy, r, gradientUnits]);
|
|
56
|
+
return _jsx(SvgNodeElement, { widgetRef: widgetRef, children: children });
|
|
57
|
+
};
|
|
58
|
+
// "50%" or 0.5 — both spellings are common in real SVGO output.
|
|
59
|
+
const parseOffset = (offset) => {
|
|
60
|
+
if (typeof offset === "number") {
|
|
61
|
+
return offset;
|
|
62
|
+
}
|
|
63
|
+
const trimmed = offset.trim();
|
|
64
|
+
return trimmed.endsWith("%")
|
|
65
|
+
? Number.parseFloat(trimmed.slice(0, -1)) / 100
|
|
66
|
+
: Number.parseFloat(trimmed);
|
|
67
|
+
};
|
|
68
|
+
export const Stop = ({ offset, stopColor = "black", stopOpacity = 1, }) => {
|
|
69
|
+
const widgetRef = useRef(null);
|
|
70
|
+
useLayoutEffect(() => {
|
|
71
|
+
const widget = widgetRef.current;
|
|
72
|
+
if (!widget) {
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
const color = resolveSvgColor(stopColor) ??
|
|
76
|
+
new Gdk.RGBA({ red: 0, green: 0, blue: 0, alpha: 1 });
|
|
77
|
+
const clampedOpacity = Math.max(0, Math.min(1, stopOpacity));
|
|
78
|
+
setSvgNodeDescriptor(widget, {
|
|
79
|
+
kind: "stop",
|
|
80
|
+
offset: Math.max(0, Math.min(1, parseOffset(offset))),
|
|
81
|
+
color: clampedOpacity >= 1
|
|
82
|
+
? color
|
|
83
|
+
: new Gdk.RGBA({
|
|
84
|
+
red: color.red,
|
|
85
|
+
green: color.green,
|
|
86
|
+
blue: color.blue,
|
|
87
|
+
alpha: color.alpha * clampedOpacity,
|
|
88
|
+
}),
|
|
89
|
+
});
|
|
90
|
+
}, [offset, stopColor, stopOpacity]);
|
|
91
|
+
return _jsx(SvgNodeElement, { widgetRef: widgetRef });
|
|
92
|
+
};
|
|
93
|
+
//# sourceMappingURL=gradients.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gradients.js","sourceRoot":"","sources":["../../../src/components/svg/gradients.tsx"],"names":[],"mappings":"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","sourcesContent":["// <Defs>/<LinearGradient>/<RadialGradient>/<Stop> — paint resources, not\n// visible nodes. They ride the same RnGtkxSvgNode tree as everything else\n// (so the bridge's snapshot() can walk <Defs> separately, before the main\n// paint pass, to build an id → gradient map — see svg-node.ts), but are\n// never part of the Animated numeric channel: gradients are definitions,\n// not painted geometry, and nothing in scope needs an animated stop.\nimport { useLayoutEffect, useRef, type ReactNode } from \"react\"\nimport {\n Gdk,\n resolveSvgColor,\n setSvgNodeDescriptor,\n type Gtk,\n type SvgGradientUnits,\n} from \"../../gtkx/bridge/index\"\nimport { SvgNodeElement } from \"./node\"\n\nexport type DefsProps = { children?: ReactNode }\n\nexport const Defs = ({ children }: DefsProps) => {\n const widgetRef = useRef<Gtk.Widget | null>(null)\n useLayoutEffect(() => {\n const widget = widgetRef.current\n if (widget) {\n setSvgNodeDescriptor(widget, { kind: \"defs\" })\n }\n }, [])\n return <SvgNodeElement widgetRef={widgetRef}>{children}</SvgNodeElement>\n}\n\nexport type LinearGradientProps = {\n id: string\n x1?: number\n y1?: number\n x2?: number\n y2?: number\n gradientUnits?: SvgGradientUnits\n children?: ReactNode\n}\n\nexport const LinearGradient = ({\n id,\n x1 = 0,\n y1 = 0,\n x2 = 1,\n y2 = 0,\n gradientUnits = \"objectBoundingBox\",\n children,\n}: LinearGradientProps) => {\n const widgetRef = useRef<Gtk.Widget | null>(null)\n useLayoutEffect(() => {\n const widget = widgetRef.current\n if (widget) {\n setSvgNodeDescriptor(widget, {\n kind: \"gradient\",\n type: \"linear\",\n id,\n units: gradientUnits,\n x1,\n y1,\n x2,\n y2,\n })\n }\n }, [id, x1, y1, x2, y2, gradientUnits])\n return <SvgNodeElement widgetRef={widgetRef}>{children}</SvgNodeElement>\n}\n\nexport type RadialGradientProps = {\n id: string\n cx?: number\n cy?: number\n r?: number\n gradientUnits?: SvgGradientUnits\n children?: ReactNode\n}\n\nexport const RadialGradient = ({\n id,\n cx = 0.5,\n cy = 0.5,\n r = 0.5,\n gradientUnits = \"objectBoundingBox\",\n children,\n}: RadialGradientProps) => {\n const widgetRef = useRef<Gtk.Widget | null>(null)\n useLayoutEffect(() => {\n const widget = widgetRef.current\n if (widget) {\n setSvgNodeDescriptor(widget, {\n kind: \"gradient\",\n type: \"radial\",\n id,\n units: gradientUnits,\n cx,\n cy,\n r,\n })\n }\n }, [id, cx, cy, r, gradientUnits])\n return <SvgNodeElement widgetRef={widgetRef}>{children}</SvgNodeElement>\n}\n\n// \"50%\" or 0.5 — both spellings are common in real SVGO output.\nconst parseOffset = (offset: number | string): number => {\n if (typeof offset === \"number\") {\n return offset\n }\n const trimmed = offset.trim()\n return trimmed.endsWith(\"%\")\n ? Number.parseFloat(trimmed.slice(0, -1)) / 100\n : Number.parseFloat(trimmed)\n}\n\nexport type StopProps = {\n offset: number | string\n stopColor?: string\n stopOpacity?: number\n}\n\nexport const Stop = ({\n offset,\n stopColor = \"black\",\n stopOpacity = 1,\n}: StopProps) => {\n const widgetRef = useRef<Gtk.Widget | null>(null)\n useLayoutEffect(() => {\n const widget = widgetRef.current\n if (!widget) {\n return\n }\n const color =\n resolveSvgColor(stopColor) ??\n new Gdk.RGBA({ red: 0, green: 0, blue: 0, alpha: 1 })\n const clampedOpacity = Math.max(0, Math.min(1, stopOpacity))\n setSvgNodeDescriptor(widget, {\n kind: \"stop\",\n offset: Math.max(0, Math.min(1, parseOffset(offset))),\n color:\n clampedOpacity >= 1\n ? color\n : new Gdk.RGBA({\n red: color.red,\n green: color.green,\n blue: color.blue,\n alpha: color.alpha * clampedOpacity,\n }),\n })\n }, [offset, stopColor, stopOpacity])\n return <SvgNodeElement widgetRef={widgetRef} />\n}\n"]}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { type ReactNode } from "react";
|
|
2
|
+
import { type AnimatableNumber } from "./animated-support";
|
|
3
|
+
export type GProps = {
|
|
4
|
+
transform?: string;
|
|
5
|
+
opacity?: AnimatableNumber;
|
|
6
|
+
children?: ReactNode;
|
|
7
|
+
};
|
|
8
|
+
export declare const G: ({ transform, opacity, children }: GProps) => React.JSX.Element;
|