react-native-gtkx 0.1.0-alpha.4 → 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 +12 -8
- 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/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 +105 -12
- 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.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.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 +40 -4
- 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/use-signal.js +18 -8
- 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/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 -13
- 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/index.d.ts +1 -0
- package/dist/style/index.js +1 -0
- package/dist/style/index.js.map +1 -1
- package/dist/style/split-style.js +2 -2
- package/dist/style/split-style.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 +15 -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
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"group.js","sourceRoot":"","sources":["../../../src/components/svg/group.tsx"],"names":[],"mappings":";AAAA,6EAA6E;AAC7E,yEAAyE;AACzE,yEAAyE;AACzE,OAAO,EAAE,MAAM,EAAkB,MAAM,OAAO,CAAA;AAC9C,OAAO,EAAE,oBAAoB,EAAY,MAAM,yBAAyB,CAAA;AACxE,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAA;AACvD,OAAO,EACL,qBAAqB,GAEtB,MAAM,oBAAoB,CAAA;AAC3B,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAA;AACtC,OAAO,EAAE,cAAc,EAAE,MAAM,QAAQ,CAAA;AAQvC,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,SAAS,EAAE,OAAO,GAAG,CAAC,EAAE,QAAQ,EAAU,EAAE,EAAE;IAChE,MAAM,SAAS,GAAG,MAAM,CAAoB,IAAI,CAAC,CAAA;IACjD,MAAM,EAAE,aAAa,EAAE,GAAG,UAAU,CAAC,GAAG,CAAC,CAAA;IAEzC,uEAAuE;IACvE,uDAAuD;IACvD,qBAAqB,CACnB,EAAE,OAAO,EAAE,EACX,CAAC,QAAQ,EAAE,EAAE;QACX,MAAM,MAAM,GAAG,SAAS,CAAC,OAAO,CAAA;QAChC,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,OAAM;QACR,CAAC;QACD,oBAAoB,CAAC,MAAM,EAAE;YAC3B,IAAI,EAAE,GAAG;YACT,YAAY,EAAE,iBAAiB,CAAC,SAAS,CAAC;YAC1C,OAAO,EAAE,QAAQ,CAAC,OAAQ;SAC3B,CAAC,CAAA;QACF,aAAa,EAAE,CAAA;IACjB,CAAC,EACD,CAAC,SAAS,CAAC,CACZ,CAAA;IAED,OAAO,KAAC,cAAc,IAAC,SAAS,EAAE,SAAS,YAAG,QAAQ,GAAkB,CAAA;AAC1E,CAAC,CAAA","sourcesContent":["// <G> — a grouping node: applies an SVG transform-list string and/or opacity\n// to every descendant, then recurses. transform accepts translate/scale/\n// rotate/matrix (no skewX/skewY, no per-axis object form — see epic.md).\nimport { useRef, type ReactNode } from \"react\"\nimport { setSvgNodeDescriptor, type Gtk } from \"../../gtkx/bridge/index\"\nimport { parseSvgTransform } from \"../../svg/transform\"\nimport {\n useAnimatedShapeBuild,\n type AnimatableNumber,\n} from \"./animated-support\"\nimport { useSvgRoot } from \"./context\"\nimport { SvgNodeElement } from \"./node\"\n\nexport type GProps = {\n transform?: string\n opacity?: AnimatableNumber\n children?: ReactNode\n}\n\nexport const G = ({ transform, opacity = 1, children }: GProps) => {\n const widgetRef = useRef<Gtk.Widget | null>(null)\n const { requestRedraw } = useSvgRoot(\"G\")\n\n // transform is a static string (not part of the Animated channel — see\n // epic.md); only opacity can be an Animated node here.\n useAnimatedShapeBuild(\n { opacity },\n (resolved) => {\n const widget = widgetRef.current\n if (!widget) {\n return\n }\n setSvgNodeDescriptor(widget, {\n kind: \"g\",\n transformOps: parseSvgTransform(transform),\n opacity: resolved.opacity!,\n })\n requestRedraw()\n },\n [transform],\n )\n\n return <SvgNodeElement widgetRef={widgetRef}>{children}</SvgNodeElement>\n}\n"]}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { Defs, LinearGradient, RadialGradient, Stop, type DefsProps, type LinearGradientProps, type RadialGradientProps, type StopProps, } from "./gradients";
|
|
2
|
+
export { G, type GProps } from "./group";
|
|
3
|
+
export { Circle, Ellipse, Line, Path, Polygon, Polyline, Rect, type CircleProps, type EllipseProps, type LineProps, type PathProps, type PolygonProps, type PolylineProps, type RectProps, } from "./shapes";
|
|
4
|
+
export { Svg, type SvgProps } from "./svg";
|
|
5
|
+
export type { AnimatableNumber } from "./animated-support";
|
|
6
|
+
export type { SvgFillRule, SvgLineCap, SvgLineJoin } from "./paint";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/components/svg/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,IAAI,EACJ,cAAc,EACd,cAAc,EACd,IAAI,GAKL,MAAM,aAAa,CAAA;AACpB,OAAO,EAAE,CAAC,EAAe,MAAM,SAAS,CAAA;AACxC,OAAO,EACL,MAAM,EACN,OAAO,EACP,IAAI,EACJ,IAAI,EACJ,OAAO,EACP,QAAQ,EACR,IAAI,GAQL,MAAM,UAAU,CAAA;AACjB,OAAO,EAAE,GAAG,EAAiB,MAAM,OAAO,CAAA","sourcesContent":["export {\n Defs,\n LinearGradient,\n RadialGradient,\n Stop,\n type DefsProps,\n type LinearGradientProps,\n type RadialGradientProps,\n type StopProps,\n} from \"./gradients\"\nexport { G, type GProps } from \"./group\"\nexport {\n Circle,\n Ellipse,\n Line,\n Path,\n Polygon,\n Polyline,\n Rect,\n type CircleProps,\n type EllipseProps,\n type LineProps,\n type PathProps,\n type PolygonProps,\n type PolylineProps,\n type RectProps,\n} from \"./shapes\"\nexport { Svg, type SvgProps } from \"./svg\"\nexport type { AnimatableNumber } from \"./animated-support\"\nexport type { SvgFillRule, SvgLineCap, SvgLineJoin } from \"./paint\"\n"]}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { ReactNode, RefObject } from "react";
|
|
2
|
+
import { type Gtk } from "../../gtkx/bridge/index";
|
|
3
|
+
export type SvgNodeElementRefProps = {
|
|
4
|
+
widgetRef: RefObject<Gtk.Widget | null>;
|
|
5
|
+
children?: ReactNode;
|
|
6
|
+
};
|
|
7
|
+
export declare const SvgNodeElement: ({ widgetRef, children, }: SvgNodeElementRefProps) => React.JSX.Element;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { getSvgNodeComponent } from "../../gtkx/bridge/index";
|
|
3
|
+
export const SvgNodeElement = ({ widgetRef, children, }) => {
|
|
4
|
+
const Node = getSvgNodeComponent();
|
|
5
|
+
return _jsx(Node, { ref: widgetRef, children: children });
|
|
6
|
+
};
|
|
7
|
+
//# sourceMappingURL=node.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"node.js","sourceRoot":"","sources":["../../../src/components/svg/node.tsx"],"names":[],"mappings":";AASA,OAAO,EAAE,mBAAmB,EAAY,MAAM,yBAAyB,CAAA;AAcvE,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,EAC7B,SAAS,EACT,QAAQ,GACe,EAAE,EAAE;IAC3B,MAAM,IAAI,GAAG,mBAAmB,EAAwC,CAAA;IACxE,OAAO,KAAC,IAAI,IAAC,GAAG,EAAE,SAAS,YAAG,QAAQ,GAAQ,CAAA;AAChD,CAAC,CAAA","sourcesContent":["// Thin typed wrapper around the shared RnGtkxSvgNode intrinsic (bridge/\n// svg-node.ts) — every SVG element below <Svg> itself renders exactly this;\n// they differ only in what they write into the node's WeakMap descriptor.\n// JSX nesting is enough to build the GTK parent-child chain the bridge's\n// snapshot() walks (the reconciler's generic ref/set_parent attachment, the\n// same one View relies on for real Yoga children) — nothing here\n// participates in Yoga, so the props are deliberately minimal: no layout,\n// no name/cssClasses (unlike View, no <Path>/<G>/... needs a CSS identity).\nimport type { ReactNode, RefObject } from \"react\"\nimport { getSvgNodeComponent, type Gtk } from \"../../gtkx/bridge/index\"\n\ntype SvgNodeElementProps = {\n ref?: RefObject<Gtk.Widget | null> | null\n children?: ReactNode\n}\n\ntype SvgNodeElementComponent = (props: SvgNodeElementProps) => ReactNode\n\nexport type SvgNodeElementRefProps = {\n widgetRef: RefObject<Gtk.Widget | null>\n children?: ReactNode\n}\n\nexport const SvgNodeElement = ({\n widgetRef,\n children,\n}: SvgNodeElementRefProps) => {\n const Node = getSvgNodeComponent() as unknown as SvgNodeElementComponent\n return <Node ref={widgetRef}>{children}</Node>\n}\n"]}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { type SvgShapeDescriptor } from "../../gtkx/bridge/index";
|
|
2
|
+
export type SvgFillRule = "nonzero" | "evenodd";
|
|
3
|
+
export type SvgLineCap = "butt" | "round" | "square";
|
|
4
|
+
export type SvgLineJoin = "miter" | "round" | "bevel";
|
|
5
|
+
export type SvgPaintProps = {
|
|
6
|
+
fill?: string;
|
|
7
|
+
fillRule?: SvgFillRule;
|
|
8
|
+
stroke?: string;
|
|
9
|
+
strokeLinecap?: SvgLineCap;
|
|
10
|
+
strokeLinejoin?: SvgLineJoin;
|
|
11
|
+
strokeDasharray?: string | number[];
|
|
12
|
+
};
|
|
13
|
+
export type SvgPaintNumbers = {
|
|
14
|
+
fillOpacity: number;
|
|
15
|
+
strokeOpacity: number;
|
|
16
|
+
strokeWidth: number;
|
|
17
|
+
strokeDashoffset: number;
|
|
18
|
+
opacity: number;
|
|
19
|
+
};
|
|
20
|
+
export declare const DEFAULT_PAINT_NUMBERS: SvgPaintNumbers;
|
|
21
|
+
/** `d` may be empty (e.g. a Polygon with no points) — that shape simply
|
|
22
|
+
* paints nothing, it is not an error. Fill defaults to black, stroke to
|
|
23
|
+
* none, exactly like react-native-svg. */
|
|
24
|
+
export declare const buildShapeDescriptor: (d: string, paint: SvgPaintProps, numbers: SvgPaintNumbers) => SvgShapeDescriptor;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
// Shared fill/stroke prop shape and the descriptor builder every shape
|
|
2
|
+
// component (Path/Rect/Circle/Ellipse/Line/Polygon/Polyline) feeds its
|
|
3
|
+
// generated `d` string through. Defaults match react-native-svg: fill
|
|
4
|
+
// defaults to black, stroke to none.
|
|
5
|
+
import { Gsk, resolveSvgPaint, } from "../../gtkx/bridge/index";
|
|
6
|
+
export const DEFAULT_PAINT_NUMBERS = {
|
|
7
|
+
fillOpacity: 1,
|
|
8
|
+
strokeOpacity: 1,
|
|
9
|
+
strokeWidth: 1,
|
|
10
|
+
strokeDashoffset: 0,
|
|
11
|
+
opacity: 1,
|
|
12
|
+
};
|
|
13
|
+
const FILL_RULES = {
|
|
14
|
+
nonzero: Gsk.FillRule.WINDING,
|
|
15
|
+
evenodd: Gsk.FillRule.EVEN_ODD,
|
|
16
|
+
};
|
|
17
|
+
const LINE_CAPS = {
|
|
18
|
+
butt: Gsk.LineCap.BUTT,
|
|
19
|
+
round: Gsk.LineCap.ROUND,
|
|
20
|
+
square: Gsk.LineCap.SQUARE,
|
|
21
|
+
};
|
|
22
|
+
const LINE_JOINS = {
|
|
23
|
+
miter: Gsk.LineJoin.MITER,
|
|
24
|
+
round: Gsk.LineJoin.ROUND,
|
|
25
|
+
bevel: Gsk.LineJoin.BEVEL,
|
|
26
|
+
};
|
|
27
|
+
// RN-svg accepts both a comma/space-separated string and a number array.
|
|
28
|
+
const parseDasharray = (value) => {
|
|
29
|
+
if (value === undefined) {
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
const tokens = Array.isArray(value) ? value : value.split(/[\s,]+/);
|
|
33
|
+
const numbers = tokens
|
|
34
|
+
.map((token) => typeof token === "number" ? token : Number.parseFloat(String(token)))
|
|
35
|
+
.filter((n) => Number.isFinite(n) && n >= 0);
|
|
36
|
+
return numbers.length > 0 ? numbers : null;
|
|
37
|
+
};
|
|
38
|
+
/** `d` may be empty (e.g. a Polygon with no points) — that shape simply
|
|
39
|
+
* paints nothing, it is not an error. Fill defaults to black, stroke to
|
|
40
|
+
* none, exactly like react-native-svg. */
|
|
41
|
+
export const buildShapeDescriptor = (d, paint, numbers) => ({
|
|
42
|
+
kind: "shape",
|
|
43
|
+
path: d ? Gsk.Path.parse(d) : null,
|
|
44
|
+
fill: resolveSvgPaint(paint.fill ?? "black"),
|
|
45
|
+
fillOpacity: numbers.fillOpacity,
|
|
46
|
+
fillRule: FILL_RULES[paint.fillRule ?? "nonzero"],
|
|
47
|
+
stroke: resolveSvgPaint(paint.stroke ?? "none"),
|
|
48
|
+
strokeOpacity: numbers.strokeOpacity,
|
|
49
|
+
strokeWidth: numbers.strokeWidth,
|
|
50
|
+
strokeLinecap: LINE_CAPS[paint.strokeLinecap ?? "butt"],
|
|
51
|
+
strokeLinejoin: LINE_JOINS[paint.strokeLinejoin ?? "miter"],
|
|
52
|
+
strokeMiterlimit: 4,
|
|
53
|
+
strokeDasharray: parseDasharray(paint.strokeDasharray),
|
|
54
|
+
strokeDashoffset: numbers.strokeDashoffset,
|
|
55
|
+
opacity: numbers.opacity,
|
|
56
|
+
});
|
|
57
|
+
//# sourceMappingURL=paint.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"paint.js","sourceRoot":"","sources":["../../../src/components/svg/paint.ts"],"names":[],"mappings":"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","sourcesContent":["// Shared fill/stroke prop shape and the descriptor builder every shape\n// component (Path/Rect/Circle/Ellipse/Line/Polygon/Polyline) feeds its\n// generated `d` string through. Defaults match react-native-svg: fill\n// defaults to black, stroke to none.\nimport {\n Gsk,\n resolveSvgPaint,\n type SvgShapeDescriptor,\n} from \"../../gtkx/bridge/index\"\n\nexport type SvgFillRule = \"nonzero\" | \"evenodd\"\nexport type SvgLineCap = \"butt\" | \"round\" | \"square\"\nexport type SvgLineJoin = \"miter\" | \"round\" | \"bevel\"\n\nexport type SvgPaintProps = {\n fill?: string\n fillRule?: SvgFillRule\n stroke?: string\n strokeLinecap?: SvgLineCap\n strokeLinejoin?: SvgLineJoin\n strokeDasharray?: string | number[]\n}\n\n// The subset of paint that can be Animated (see animated-support.ts) — kept\n// separate from SvgPaintProps because those fields are always resolved to\n// plain numbers by the caller before this function ever sees them.\nexport type SvgPaintNumbers = {\n fillOpacity: number\n strokeOpacity: number\n strokeWidth: number\n strokeDashoffset: number\n opacity: number\n}\n\nexport const DEFAULT_PAINT_NUMBERS: SvgPaintNumbers = {\n fillOpacity: 1,\n strokeOpacity: 1,\n strokeWidth: 1,\n strokeDashoffset: 0,\n opacity: 1,\n}\n\nconst FILL_RULES: Record<SvgFillRule, Gsk.FillRule> = {\n nonzero: Gsk.FillRule.WINDING,\n evenodd: Gsk.FillRule.EVEN_ODD,\n}\n\nconst LINE_CAPS: Record<SvgLineCap, Gsk.LineCap> = {\n butt: Gsk.LineCap.BUTT,\n round: Gsk.LineCap.ROUND,\n square: Gsk.LineCap.SQUARE,\n}\n\nconst LINE_JOINS: Record<SvgLineJoin, Gsk.LineJoin> = {\n miter: Gsk.LineJoin.MITER,\n round: Gsk.LineJoin.ROUND,\n bevel: Gsk.LineJoin.BEVEL,\n}\n\n// RN-svg accepts both a comma/space-separated string and a number array.\nconst parseDasharray = (\n value: SvgPaintProps[\"strokeDasharray\"],\n): number[] | null => {\n if (value === undefined) {\n return null\n }\n const tokens = Array.isArray(value) ? value : value.split(/[\\s,]+/)\n const numbers = tokens\n .map((token) =>\n typeof token === \"number\" ? token : Number.parseFloat(String(token)),\n )\n .filter((n) => Number.isFinite(n) && n >= 0)\n return numbers.length > 0 ? numbers : null\n}\n\n/** `d` may be empty (e.g. a Polygon with no points) — that shape simply\n * paints nothing, it is not an error. Fill defaults to black, stroke to\n * none, exactly like react-native-svg. */\nexport const buildShapeDescriptor = (\n d: string,\n paint: SvgPaintProps,\n numbers: SvgPaintNumbers,\n): SvgShapeDescriptor => ({\n kind: \"shape\",\n path: d ? Gsk.Path.parse(d) : null,\n fill: resolveSvgPaint(paint.fill ?? \"black\"),\n fillOpacity: numbers.fillOpacity,\n fillRule: FILL_RULES[paint.fillRule ?? \"nonzero\"],\n stroke: resolveSvgPaint(paint.stroke ?? \"none\"),\n strokeOpacity: numbers.strokeOpacity,\n strokeWidth: numbers.strokeWidth,\n strokeLinecap: LINE_CAPS[paint.strokeLinecap ?? \"butt\"],\n strokeLinejoin: LINE_JOINS[paint.strokeLinejoin ?? \"miter\"],\n strokeMiterlimit: 4,\n strokeDasharray: parseDasharray(paint.strokeDasharray),\n strokeDashoffset: numbers.strokeDashoffset,\n opacity: numbers.opacity,\n})\n"]}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { type Point } from "../../svg/geometry";
|
|
2
|
+
import type { AnimatableNumber } from "./animated-support";
|
|
3
|
+
import type { SvgPaintProps } from "./paint";
|
|
4
|
+
import { type ShapeNumericPaintProps } from "./use-shape-node";
|
|
5
|
+
type ShapePaintProps = SvgPaintProps & ShapeNumericPaintProps;
|
|
6
|
+
export type PathProps = ShapePaintProps & {
|
|
7
|
+
d: string;
|
|
8
|
+
};
|
|
9
|
+
export declare const Path: ({ d, fillOpacity, strokeOpacity, strokeWidth, strokeDashoffset, opacity, ...paint }: PathProps) => React.JSX.Element;
|
|
10
|
+
export type RectProps = ShapePaintProps & {
|
|
11
|
+
x?: AnimatableNumber;
|
|
12
|
+
y?: AnimatableNumber;
|
|
13
|
+
width: AnimatableNumber;
|
|
14
|
+
height: AnimatableNumber;
|
|
15
|
+
rx?: AnimatableNumber;
|
|
16
|
+
ry?: AnimatableNumber;
|
|
17
|
+
};
|
|
18
|
+
export declare const Rect: ({ x, y, width, height, rx, ry, fillOpacity, strokeOpacity, strokeWidth, strokeDashoffset, opacity, ...paint }: RectProps) => React.JSX.Element;
|
|
19
|
+
export type CircleProps = ShapePaintProps & {
|
|
20
|
+
cx?: AnimatableNumber;
|
|
21
|
+
cy?: AnimatableNumber;
|
|
22
|
+
r: AnimatableNumber;
|
|
23
|
+
};
|
|
24
|
+
export declare const Circle: ({ cx, cy, r, fillOpacity, strokeOpacity, strokeWidth, strokeDashoffset, opacity, ...paint }: CircleProps) => React.JSX.Element;
|
|
25
|
+
export type EllipseProps = ShapePaintProps & {
|
|
26
|
+
cx?: AnimatableNumber;
|
|
27
|
+
cy?: AnimatableNumber;
|
|
28
|
+
rx: AnimatableNumber;
|
|
29
|
+
ry: AnimatableNumber;
|
|
30
|
+
};
|
|
31
|
+
export declare const Ellipse: ({ cx, cy, rx, ry, fillOpacity, strokeOpacity, strokeWidth, strokeDashoffset, opacity, ...paint }: EllipseProps) => React.JSX.Element;
|
|
32
|
+
export type LineProps = Omit<ShapePaintProps, "fill" | "fillRule"> & {
|
|
33
|
+
x1?: AnimatableNumber;
|
|
34
|
+
y1?: AnimatableNumber;
|
|
35
|
+
x2?: AnimatableNumber;
|
|
36
|
+
y2?: AnimatableNumber;
|
|
37
|
+
};
|
|
38
|
+
export declare const Line: ({ x1, y1, x2, y2, strokeOpacity, strokeWidth, strokeDashoffset, opacity, ...paint }: LineProps) => React.JSX.Element;
|
|
39
|
+
export type PolygonProps = ShapePaintProps & {
|
|
40
|
+
points: string | Point[];
|
|
41
|
+
};
|
|
42
|
+
export declare const Polygon: ({ points, fillOpacity, strokeOpacity, strokeWidth, strokeDashoffset, opacity, ...paint }: PolygonProps) => React.JSX.Element;
|
|
43
|
+
export type PolylineProps = ShapePaintProps & {
|
|
44
|
+
points: string | Point[];
|
|
45
|
+
};
|
|
46
|
+
export declare const Polyline: ({ points, fillOpacity, strokeOpacity, strokeWidth, strokeDashoffset, opacity, ...paint }: PolylineProps) => React.JSX.Element;
|
|
47
|
+
export {};
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
// Path/Rect/Circle/Ellipse/Line/Polygon/Polyline. Each is geometry (fed to
|
|
3
|
+
// the pure src/svg/geometry.ts generators, or passed straight through for
|
|
4
|
+
// Path's `d`) + the shared paint props (paint.ts). Geometry fields may be
|
|
5
|
+
// Animated (see use-shape-node.ts); `d`/`points`/the paint strings are not.
|
|
6
|
+
import { circleToPathData, ellipseToPathData, lineToPathData, polygonToPathData, polylineToPathData, rectToPathData, } from "../../svg/geometry";
|
|
7
|
+
import { SvgNodeElement } from "./node";
|
|
8
|
+
import { useShapeNode } from "./use-shape-node";
|
|
9
|
+
export const Path = ({ d, fillOpacity, strokeOpacity, strokeWidth, strokeDashoffset, opacity, ...paint }) => {
|
|
10
|
+
const widgetRef = useShapeNode("Path", {}, () => d, paint, { fillOpacity, strokeOpacity, strokeWidth, strokeDashoffset, opacity }, [d]);
|
|
11
|
+
return _jsx(SvgNodeElement, { widgetRef: widgetRef });
|
|
12
|
+
};
|
|
13
|
+
export const Rect = ({ x = 0, y = 0, width, height, rx, ry, fillOpacity, strokeOpacity, strokeWidth, strokeDashoffset, opacity, ...paint }) => {
|
|
14
|
+
const geometry = { x, y, width, height };
|
|
15
|
+
if (rx !== undefined) {
|
|
16
|
+
geometry.rx = rx;
|
|
17
|
+
}
|
|
18
|
+
if (ry !== undefined) {
|
|
19
|
+
geometry.ry = ry;
|
|
20
|
+
}
|
|
21
|
+
const widgetRef = useShapeNode("Rect", geometry, (v) => rectToPathData({
|
|
22
|
+
x: v.x,
|
|
23
|
+
y: v.y,
|
|
24
|
+
width: v.width,
|
|
25
|
+
height: v.height,
|
|
26
|
+
rx: v.rx,
|
|
27
|
+
ry: v.ry,
|
|
28
|
+
}), paint, { fillOpacity, strokeOpacity, strokeWidth, strokeDashoffset, opacity }, []);
|
|
29
|
+
return _jsx(SvgNodeElement, { widgetRef: widgetRef });
|
|
30
|
+
};
|
|
31
|
+
export const Circle = ({ cx = 0, cy = 0, r, fillOpacity, strokeOpacity, strokeWidth, strokeDashoffset, opacity, ...paint }) => {
|
|
32
|
+
const widgetRef = useShapeNode("Circle", { cx, cy, r }, (v) => circleToPathData(v.cx, v.cy, v.r), paint, { fillOpacity, strokeOpacity, strokeWidth, strokeDashoffset, opacity }, []);
|
|
33
|
+
return _jsx(SvgNodeElement, { widgetRef: widgetRef });
|
|
34
|
+
};
|
|
35
|
+
export const Ellipse = ({ cx = 0, cy = 0, rx, ry, fillOpacity, strokeOpacity, strokeWidth, strokeDashoffset, opacity, ...paint }) => {
|
|
36
|
+
const widgetRef = useShapeNode("Ellipse", { cx, cy, rx, ry }, (v) => ellipseToPathData(v.cx, v.cy, v.rx, v.ry), paint, { fillOpacity, strokeOpacity, strokeWidth, strokeDashoffset, opacity }, []);
|
|
37
|
+
return _jsx(SvgNodeElement, { widgetRef: widgetRef });
|
|
38
|
+
};
|
|
39
|
+
export const Line = ({ x1 = 0, y1 = 0, x2 = 0, y2 = 0, strokeOpacity, strokeWidth, strokeDashoffset, opacity, ...paint }) => {
|
|
40
|
+
const widgetRef = useShapeNode("Line", { x1, y1, x2, y2 }, (v) => lineToPathData(v.x1, v.y1, v.x2, v.y2), { ...paint, fill: "none" }, { strokeOpacity, strokeWidth, strokeDashoffset, opacity }, []);
|
|
41
|
+
return _jsx(SvgNodeElement, { widgetRef: widgetRef });
|
|
42
|
+
};
|
|
43
|
+
export const Polygon = ({ points, fillOpacity, strokeOpacity, strokeWidth, strokeDashoffset, opacity, ...paint }) => {
|
|
44
|
+
const widgetRef = useShapeNode("Polygon", {}, () => polygonToPathData(points), paint, { fillOpacity, strokeOpacity, strokeWidth, strokeDashoffset, opacity }, [points]);
|
|
45
|
+
return _jsx(SvgNodeElement, { widgetRef: widgetRef });
|
|
46
|
+
};
|
|
47
|
+
export const Polyline = ({ points, fillOpacity, strokeOpacity, strokeWidth, strokeDashoffset, opacity, ...paint }) => {
|
|
48
|
+
const widgetRef = useShapeNode("Polyline", {}, () => polylineToPathData(points), paint, { fillOpacity, strokeOpacity, strokeWidth, strokeDashoffset, opacity }, [points]);
|
|
49
|
+
return _jsx(SvgNodeElement, { widgetRef: widgetRef });
|
|
50
|
+
};
|
|
51
|
+
//# sourceMappingURL=shapes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shapes.js","sourceRoot":"","sources":["../../../src/components/svg/shapes.tsx"],"names":[],"mappings":"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","sourcesContent":["// Path/Rect/Circle/Ellipse/Line/Polygon/Polyline. Each is geometry (fed to\n// the pure src/svg/geometry.ts generators, or passed straight through for\n// Path's `d`) + the shared paint props (paint.ts). Geometry fields may be\n// Animated (see use-shape-node.ts); `d`/`points`/the paint strings are not.\nimport {\n circleToPathData,\n ellipseToPathData,\n lineToPathData,\n polygonToPathData,\n polylineToPathData,\n rectToPathData,\n type Point,\n} from \"../../svg/geometry\"\nimport type { AnimatableNumber } from \"./animated-support\"\nimport { SvgNodeElement } from \"./node\"\nimport type { SvgPaintProps } from \"./paint\"\nimport { useShapeNode, type ShapeNumericPaintProps } from \"./use-shape-node\"\n\ntype ShapePaintProps = SvgPaintProps & ShapeNumericPaintProps\n\nexport type PathProps = ShapePaintProps & { d: string }\n\nexport const Path = ({\n d,\n fillOpacity,\n strokeOpacity,\n strokeWidth,\n strokeDashoffset,\n opacity,\n ...paint\n}: PathProps) => {\n const widgetRef = useShapeNode(\n \"Path\",\n {},\n () => d,\n paint,\n { fillOpacity, strokeOpacity, strokeWidth, strokeDashoffset, opacity },\n [d],\n )\n return <SvgNodeElement widgetRef={widgetRef} />\n}\n\nexport type RectProps = ShapePaintProps & {\n x?: AnimatableNumber\n y?: AnimatableNumber\n width: AnimatableNumber\n height: AnimatableNumber\n rx?: AnimatableNumber\n ry?: AnimatableNumber\n}\n\nexport const Rect = ({\n x = 0,\n y = 0,\n width,\n height,\n rx,\n ry,\n fillOpacity,\n strokeOpacity,\n strokeWidth,\n strokeDashoffset,\n opacity,\n ...paint\n}: RectProps) => {\n const geometry: Record<string, AnimatableNumber> = { x, y, width, height }\n if (rx !== undefined) {\n geometry.rx = rx\n }\n if (ry !== undefined) {\n geometry.ry = ry\n }\n const widgetRef = useShapeNode(\n \"Rect\",\n geometry,\n (v) =>\n rectToPathData({\n x: v.x!,\n y: v.y!,\n width: v.width!,\n height: v.height!,\n rx: v.rx,\n ry: v.ry,\n }),\n paint,\n { fillOpacity, strokeOpacity, strokeWidth, strokeDashoffset, opacity },\n [],\n )\n return <SvgNodeElement widgetRef={widgetRef} />\n}\n\nexport type CircleProps = ShapePaintProps & {\n cx?: AnimatableNumber\n cy?: AnimatableNumber\n r: AnimatableNumber\n}\n\nexport const Circle = ({\n cx = 0,\n cy = 0,\n r,\n fillOpacity,\n strokeOpacity,\n strokeWidth,\n strokeDashoffset,\n opacity,\n ...paint\n}: CircleProps) => {\n const widgetRef = useShapeNode(\n \"Circle\",\n { cx, cy, r },\n (v) => circleToPathData(v.cx!, v.cy!, v.r!),\n paint,\n { fillOpacity, strokeOpacity, strokeWidth, strokeDashoffset, opacity },\n [],\n )\n return <SvgNodeElement widgetRef={widgetRef} />\n}\n\nexport type EllipseProps = ShapePaintProps & {\n cx?: AnimatableNumber\n cy?: AnimatableNumber\n rx: AnimatableNumber\n ry: AnimatableNumber\n}\n\nexport const Ellipse = ({\n cx = 0,\n cy = 0,\n rx,\n ry,\n fillOpacity,\n strokeOpacity,\n strokeWidth,\n strokeDashoffset,\n opacity,\n ...paint\n}: EllipseProps) => {\n const widgetRef = useShapeNode(\n \"Ellipse\",\n { cx, cy, rx, ry },\n (v) => ellipseToPathData(v.cx!, v.cy!, v.rx!, v.ry!),\n paint,\n { fillOpacity, strokeOpacity, strokeWidth, strokeDashoffset, opacity },\n [],\n )\n return <SvgNodeElement widgetRef={widgetRef} />\n}\n\n// Line never fills (a zero-area contour) — the `fill` prop is intentionally\n// not part of LineProps at all rather than silently ignored.\nexport type LineProps = Omit<ShapePaintProps, \"fill\" | \"fillRule\"> & {\n x1?: AnimatableNumber\n y1?: AnimatableNumber\n x2?: AnimatableNumber\n y2?: AnimatableNumber\n}\n\nexport const Line = ({\n x1 = 0,\n y1 = 0,\n x2 = 0,\n y2 = 0,\n strokeOpacity,\n strokeWidth,\n strokeDashoffset,\n opacity,\n ...paint\n}: LineProps) => {\n const widgetRef = useShapeNode(\n \"Line\",\n { x1, y1, x2, y2 },\n (v) => lineToPathData(v.x1!, v.y1!, v.x2!, v.y2!),\n { ...paint, fill: \"none\" },\n { strokeOpacity, strokeWidth, strokeDashoffset, opacity },\n [],\n )\n return <SvgNodeElement widgetRef={widgetRef} />\n}\n\n// points is a plain string/array (not part of the Animated field set — same\n// cut as Path's `d`, see epic.md).\nexport type PolygonProps = ShapePaintProps & { points: string | Point[] }\n\nexport const Polygon = ({\n points,\n fillOpacity,\n strokeOpacity,\n strokeWidth,\n strokeDashoffset,\n opacity,\n ...paint\n}: PolygonProps) => {\n const widgetRef = useShapeNode(\n \"Polygon\",\n {},\n () => polygonToPathData(points),\n paint,\n { fillOpacity, strokeOpacity, strokeWidth, strokeDashoffset, opacity },\n [points],\n )\n return <SvgNodeElement widgetRef={widgetRef} />\n}\n\nexport type PolylineProps = ShapePaintProps & { points: string | Point[] }\n\nexport const Polyline = ({\n points,\n fillOpacity,\n strokeOpacity,\n strokeWidth,\n strokeDashoffset,\n opacity,\n ...paint\n}: PolylineProps) => {\n const widgetRef = useShapeNode(\n \"Polyline\",\n {},\n () => polylineToPathData(points),\n paint,\n { fillOpacity, strokeOpacity, strokeWidth, strokeDashoffset, opacity },\n [points],\n )\n return <SvgNodeElement widgetRef={widgetRef} />\n}\n"]}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { type ReactNode } from "react";
|
|
2
|
+
import type { DimensionValue, StyleProp } from "../../contracts";
|
|
3
|
+
import { type LayoutEvent } from "../use-layout-child";
|
|
4
|
+
export type SvgProps = {
|
|
5
|
+
width?: DimensionValue;
|
|
6
|
+
height?: DimensionValue;
|
|
7
|
+
viewBox?: string;
|
|
8
|
+
preserveAspectRatio?: string;
|
|
9
|
+
style?: StyleProp;
|
|
10
|
+
onLayout?: (event: LayoutEvent) => void;
|
|
11
|
+
testID?: string;
|
|
12
|
+
children?: ReactNode;
|
|
13
|
+
};
|
|
14
|
+
export declare const Svg: ({ width, height, viewBox, preserveAspectRatio, style, onLayout, testID, children, }: SvgProps) => React.JSX.Element;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
// <Svg> — the one part of this API that touches Yoga at all. Sized purely
|
|
3
|
+
// by style (width/height/flex), no measureFromWidget: same precedent as
|
|
4
|
+
// Image (verified while researching the epic — Image does not introspect
|
|
5
|
+
// GtkPicture's natural size either, it is entirely style-driven). viewBox +
|
|
6
|
+
// preserveAspectRatio only reshape the coordinate space INSIDE the
|
|
7
|
+
// allocated rect; Yoga never sees them.
|
|
8
|
+
import { useLayoutEffect, useMemo, useRef, } from "react";
|
|
9
|
+
import { getSvgNodeComponent, queueSvgRedraw, setSvgNodeDescriptor, } from "../../gtkx/bridge/index";
|
|
10
|
+
import { parsePreserveAspectRatio, parseViewBox } from "../../svg/view-box";
|
|
11
|
+
import { useLayoutChild } from "../use-layout-child";
|
|
12
|
+
import { SvgRootContext } from "./context";
|
|
13
|
+
export const Svg = ({ width, height, viewBox, preserveAspectRatio, style, onLayout, testID, children, }) => {
|
|
14
|
+
const widgetRef = useRef(null);
|
|
15
|
+
// width/height are RN-svg convenience props layered onto style, the same
|
|
16
|
+
// relationship style has to every other layout prop in this codebase.
|
|
17
|
+
const sizeStyle = useMemo(() => width === undefined && height === undefined ? null : { width, height }, [width, height]);
|
|
18
|
+
const { cssClass } = useLayoutChild(widgetRef, {
|
|
19
|
+
style: [style, sizeStyle],
|
|
20
|
+
onLayout,
|
|
21
|
+
});
|
|
22
|
+
const parsedViewBox = useMemo(() => parseViewBox(viewBox), [viewBox]);
|
|
23
|
+
const parsedPreserveAspectRatio = useMemo(() => parsePreserveAspectRatio(preserveAspectRatio), [preserveAspectRatio]);
|
|
24
|
+
useLayoutEffect(() => {
|
|
25
|
+
const widget = widgetRef.current;
|
|
26
|
+
if (!widget) {
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
setSvgNodeDescriptor(widget, {
|
|
30
|
+
kind: "svg",
|
|
31
|
+
viewBox: parsedViewBox,
|
|
32
|
+
preserveAspectRatio: parsedPreserveAspectRatio,
|
|
33
|
+
});
|
|
34
|
+
queueSvgRedraw(widget);
|
|
35
|
+
}, [parsedViewBox, parsedPreserveAspectRatio]);
|
|
36
|
+
const contextValue = useMemo(() => ({
|
|
37
|
+
rootRef: widgetRef,
|
|
38
|
+
requestRedraw: () => {
|
|
39
|
+
const widget = widgetRef.current;
|
|
40
|
+
if (widget) {
|
|
41
|
+
queueSvgRedraw(widget);
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
}), []);
|
|
45
|
+
const SvgElement = getSvgNodeComponent();
|
|
46
|
+
return (_jsx(SvgElement, { ref: widgetRef, name: testID, cssClasses: cssClass ? [cssClass] : [], children: _jsx(SvgRootContext.Provider, { value: contextValue, children: children }) }));
|
|
47
|
+
};
|
|
48
|
+
//# sourceMappingURL=svg.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"svg.js","sourceRoot":"","sources":["../../../src/components/svg/svg.tsx"],"names":[],"mappings":";AAAA,0EAA0E;AAC1E,wEAAwE;AACxE,yEAAyE;AACzE,4EAA4E;AAC5E,mEAAmE;AACnE,wCAAwC;AACxC,OAAO,EACL,eAAe,EACf,OAAO,EACP,MAAM,GAGP,MAAM,OAAO,CAAA;AAEd,OAAO,EACL,mBAAmB,EACnB,cAAc,EACd,oBAAoB,GAErB,MAAM,yBAAyB,CAAA;AAChC,OAAO,EAAE,wBAAwB,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AAC3E,OAAO,EAAE,cAAc,EAAoB,MAAM,qBAAqB,CAAA;AACtE,OAAO,EAAE,cAAc,EAA4B,MAAM,WAAW,CAAA;AAsBpE,MAAM,CAAC,MAAM,GAAG,GAAG,CAAC,EAClB,KAAK,EACL,MAAM,EACN,OAAO,EACP,mBAAmB,EACnB,KAAK,EACL,QAAQ,EACR,MAAM,EACN,QAAQ,GACC,EAAE,EAAE;IACb,MAAM,SAAS,GAAG,MAAM,CAAoB,IAAI,CAAC,CAAA;IAEjD,yEAAyE;IACzE,sEAAsE;IACtE,MAAM,SAAS,GAAG,OAAO,CACvB,GAAG,EAAE,CACH,KAAK,KAAK,SAAS,IAAI,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,EACxE,CAAC,KAAK,EAAE,MAAM,CAAC,CAChB,CAAA;IACD,MAAM,EAAE,QAAQ,EAAE,GAAG,cAAc,CAAC,SAAS,EAAE;QAC7C,KAAK,EAAE,CAAC,KAAK,EAAE,SAAS,CAAC;QACzB,QAAQ;KACT,CAAC,CAAA;IAEF,MAAM,aAAa,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC,YAAY,CAAC,OAAO,CAAC,EAAE,CAAC,OAAO,CAAC,CAAC,CAAA;IACrE,MAAM,yBAAyB,GAAG,OAAO,CACvC,GAAG,EAAE,CAAC,wBAAwB,CAAC,mBAAmB,CAAC,EACnD,CAAC,mBAAmB,CAAC,CACtB,CAAA;IAED,eAAe,CAAC,GAAG,EAAE;QACnB,MAAM,MAAM,GAAG,SAAS,CAAC,OAAO,CAAA;QAChC,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,OAAM;QACR,CAAC;QACD,oBAAoB,CAAC,MAAM,EAAE;YAC3B,IAAI,EAAE,KAAK;YACX,OAAO,EAAE,aAAa;YACtB,mBAAmB,EAAE,yBAAyB;SAC/C,CAAC,CAAA;QACF,cAAc,CAAC,MAAM,CAAC,CAAA;IACxB,CAAC,EAAE,CAAC,aAAa,EAAE,yBAAyB,CAAC,CAAC,CAAA;IAE9C,MAAM,YAAY,GAAG,OAAO,CAC1B,GAAG,EAAE,CAAC,CAAC;QACL,OAAO,EAAE,SAAS;QAClB,aAAa,EAAE,GAAG,EAAE;YAClB,MAAM,MAAM,GAAG,SAAS,CAAC,OAAO,CAAA;YAChC,IAAI,MAAM,EAAE,CAAC;gBACX,cAAc,CAAC,MAAM,CAAC,CAAA;YACxB,CAAC;QACH,CAAC;KACF,CAAC,EACF,EAAE,CACH,CAAA;IAED,MAAM,UAAU,GAAG,mBAAmB,EAAoC,CAAA;IAC1E,OAAO,CACL,KAAC,UAAU,IACT,GAAG,EAAE,SAAS,EACd,IAAI,EAAE,MAAM,EACZ,UAAU,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,EAAE,YAEtC,KAAC,cAAc,CAAC,QAAQ,IAAC,KAAK,EAAE,YAAY,YACzC,QAAQ,GACe,GACf,CACd,CAAA;AACH,CAAC,CAAA","sourcesContent":["// <Svg> — the one part of this API that touches Yoga at all. Sized purely\n// by style (width/height/flex), no measureFromWidget: same precedent as\n// Image (verified while researching the epic — Image does not introspect\n// GtkPicture's natural size either, it is entirely style-driven). viewBox +\n// preserveAspectRatio only reshape the coordinate space INSIDE the\n// allocated rect; Yoga never sees them.\nimport {\n useLayoutEffect,\n useMemo,\n useRef,\n type ReactNode,\n type RefObject,\n} from \"react\"\nimport type { DimensionValue, StyleProp } from \"../../contracts\"\nimport {\n getSvgNodeComponent,\n queueSvgRedraw,\n setSvgNodeDescriptor,\n type Gtk,\n} from \"../../gtkx/bridge/index\"\nimport { parsePreserveAspectRatio, parseViewBox } from \"../../svg/view-box\"\nimport { useLayoutChild, type LayoutEvent } from \"../use-layout-child\"\nimport { SvgRootContext, type SvgRootContextValue } from \"./context\"\n\nexport type SvgProps = {\n width?: DimensionValue\n height?: DimensionValue\n viewBox?: string\n preserveAspectRatio?: string\n style?: StyleProp\n onLayout?: (event: LayoutEvent) => void\n testID?: string\n children?: ReactNode\n}\n\ntype SvgElementProps = {\n ref?: RefObject<Gtk.Widget | null> | null\n name?: string\n cssClasses?: string[]\n children?: ReactNode\n}\n\ntype SvgElementComponent = (props: SvgElementProps) => ReactNode\n\nexport const Svg = ({\n width,\n height,\n viewBox,\n preserveAspectRatio,\n style,\n onLayout,\n testID,\n children,\n}: SvgProps) => {\n const widgetRef = useRef<Gtk.Widget | null>(null)\n\n // width/height are RN-svg convenience props layered onto style, the same\n // relationship style has to every other layout prop in this codebase.\n const sizeStyle = useMemo(\n () =>\n width === undefined && height === undefined ? null : { width, height },\n [width, height],\n )\n const { cssClass } = useLayoutChild(widgetRef, {\n style: [style, sizeStyle],\n onLayout,\n })\n\n const parsedViewBox = useMemo(() => parseViewBox(viewBox), [viewBox])\n const parsedPreserveAspectRatio = useMemo(\n () => parsePreserveAspectRatio(preserveAspectRatio),\n [preserveAspectRatio],\n )\n\n useLayoutEffect(() => {\n const widget = widgetRef.current\n if (!widget) {\n return\n }\n setSvgNodeDescriptor(widget, {\n kind: \"svg\",\n viewBox: parsedViewBox,\n preserveAspectRatio: parsedPreserveAspectRatio,\n })\n queueSvgRedraw(widget)\n }, [parsedViewBox, parsedPreserveAspectRatio])\n\n const contextValue = useMemo<SvgRootContextValue>(\n () => ({\n rootRef: widgetRef,\n requestRedraw: () => {\n const widget = widgetRef.current\n if (widget) {\n queueSvgRedraw(widget)\n }\n },\n }),\n [],\n )\n\n const SvgElement = getSvgNodeComponent() as unknown as SvgElementComponent\n return (\n <SvgElement\n ref={widgetRef}\n name={testID}\n cssClasses={cssClass ? [cssClass] : []}\n >\n <SvgRootContext.Provider value={contextValue}>\n {children}\n </SvgRootContext.Provider>\n </SvgElement>\n )\n}\n"]}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { type RefObject } from "react";
|
|
2
|
+
import { type Gtk } from "../../gtkx/bridge/index";
|
|
3
|
+
import { type AnimatableNumber } from "./animated-support";
|
|
4
|
+
import { type SvgPaintProps } from "./paint";
|
|
5
|
+
export type ShapeNumericPaintProps = {
|
|
6
|
+
fillOpacity?: AnimatableNumber;
|
|
7
|
+
strokeOpacity?: AnimatableNumber;
|
|
8
|
+
strokeWidth?: AnimatableNumber;
|
|
9
|
+
strokeDashoffset?: AnimatableNumber;
|
|
10
|
+
opacity?: AnimatableNumber;
|
|
11
|
+
};
|
|
12
|
+
export declare const useShapeNode: (componentName: string, geometry: Record<string, AnimatableNumber>, computeD: (resolvedGeometry: Record<string, number>) => string, paint: SvgPaintProps, numeric: ShapeNumericPaintProps, extraDeps: readonly unknown[]) => RefObject<Gtk.Widget | null>;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
// Shared plumbing for every shape component (Path/Rect/Circle/Ellipse/Line/
|
|
2
|
+
// Polygon/Polyline): resolves geometry + numeric paint fields (any of which
|
|
3
|
+
// may be Animated), builds the `d` string and the descriptor, writes it, and
|
|
4
|
+
// requests a redraw. `extraDeps` must list every non-numeric prop `computeD`
|
|
5
|
+
// or `paint` close over (a plain string like `d`/`points`/`fill` changing is
|
|
6
|
+
// not itself part of the Animated field set, but must still re-run `build`).
|
|
7
|
+
import { useRef } from "react";
|
|
8
|
+
import { setSvgNodeDescriptor } from "../../gtkx/bridge/index";
|
|
9
|
+
import { useAnimatedShapeBuild, } from "./animated-support";
|
|
10
|
+
import { useSvgRoot } from "./context";
|
|
11
|
+
import { buildShapeDescriptor, DEFAULT_PAINT_NUMBERS, } from "./paint";
|
|
12
|
+
export const useShapeNode = (componentName, geometry, computeD, paint, numeric, extraDeps) => {
|
|
13
|
+
const widgetRef = useRef(null);
|
|
14
|
+
const { requestRedraw } = useSvgRoot(componentName);
|
|
15
|
+
const geometryKeys = Object.keys(geometry);
|
|
16
|
+
const fields = {
|
|
17
|
+
...geometry,
|
|
18
|
+
fillOpacity: numeric.fillOpacity ?? DEFAULT_PAINT_NUMBERS.fillOpacity,
|
|
19
|
+
strokeOpacity: numeric.strokeOpacity ?? DEFAULT_PAINT_NUMBERS.strokeOpacity,
|
|
20
|
+
strokeWidth: numeric.strokeWidth ?? DEFAULT_PAINT_NUMBERS.strokeWidth,
|
|
21
|
+
strokeDashoffset: numeric.strokeDashoffset ?? DEFAULT_PAINT_NUMBERS.strokeDashoffset,
|
|
22
|
+
opacity: numeric.opacity ?? DEFAULT_PAINT_NUMBERS.opacity,
|
|
23
|
+
};
|
|
24
|
+
useAnimatedShapeBuild(fields, (resolved) => {
|
|
25
|
+
const widget = widgetRef.current;
|
|
26
|
+
if (!widget) {
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
const geometryValues = {};
|
|
30
|
+
for (const key of geometryKeys) {
|
|
31
|
+
geometryValues[key] = resolved[key];
|
|
32
|
+
}
|
|
33
|
+
setSvgNodeDescriptor(widget, buildShapeDescriptor(computeD(geometryValues), paint, {
|
|
34
|
+
fillOpacity: resolved.fillOpacity,
|
|
35
|
+
strokeOpacity: resolved.strokeOpacity,
|
|
36
|
+
strokeWidth: resolved.strokeWidth,
|
|
37
|
+
strokeDashoffset: resolved.strokeDashoffset,
|
|
38
|
+
opacity: resolved.opacity,
|
|
39
|
+
}));
|
|
40
|
+
requestRedraw();
|
|
41
|
+
}, [
|
|
42
|
+
paint.fill,
|
|
43
|
+
paint.fillRule,
|
|
44
|
+
paint.stroke,
|
|
45
|
+
paint.strokeLinecap,
|
|
46
|
+
paint.strokeLinejoin,
|
|
47
|
+
paint.strokeDasharray,
|
|
48
|
+
...extraDeps,
|
|
49
|
+
]);
|
|
50
|
+
return widgetRef;
|
|
51
|
+
};
|
|
52
|
+
//# sourceMappingURL=use-shape-node.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-shape-node.js","sourceRoot":"","sources":["../../../src/components/svg/use-shape-node.ts"],"names":[],"mappings":"AAAA,4EAA4E;AAC5E,4EAA4E;AAC5E,6EAA6E;AAC7E,6EAA6E;AAC7E,6EAA6E;AAC7E,6EAA6E;AAC7E,OAAO,EAAE,MAAM,EAAkB,MAAM,OAAO,CAAA;AAC9C,OAAO,EAAE,oBAAoB,EAAY,MAAM,yBAAyB,CAAA;AACxE,OAAO,EACL,qBAAqB,GAEtB,MAAM,oBAAoB,CAAA;AAC3B,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAA;AACtC,OAAO,EACL,oBAAoB,EACpB,qBAAqB,GAEtB,MAAM,SAAS,CAAA;AAUhB,MAAM,CAAC,MAAM,YAAY,GAAG,CAC1B,aAAqB,EACrB,QAA0C,EAC1C,QAA8D,EAC9D,KAAoB,EACpB,OAA+B,EAC/B,SAA6B,EACC,EAAE;IAChC,MAAM,SAAS,GAAG,MAAM,CAAoB,IAAI,CAAC,CAAA;IACjD,MAAM,EAAE,aAAa,EAAE,GAAG,UAAU,CAAC,aAAa,CAAC,CAAA;IACnD,MAAM,YAAY,GAAG,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAA;IAE1C,MAAM,MAAM,GAAqC;QAC/C,GAAG,QAAQ;QACX,WAAW,EAAE,OAAO,CAAC,WAAW,IAAI,qBAAqB,CAAC,WAAW;QACrE,aAAa,EAAE,OAAO,CAAC,aAAa,IAAI,qBAAqB,CAAC,aAAa;QAC3E,WAAW,EAAE,OAAO,CAAC,WAAW,IAAI,qBAAqB,CAAC,WAAW;QACrE,gBAAgB,EACd,OAAO,CAAC,gBAAgB,IAAI,qBAAqB,CAAC,gBAAgB;QACpE,OAAO,EAAE,OAAO,CAAC,OAAO,IAAI,qBAAqB,CAAC,OAAO;KAC1D,CAAA;IAED,qBAAqB,CACnB,MAAM,EACN,CAAC,QAAQ,EAAE,EAAE;QACX,MAAM,MAAM,GAAG,SAAS,CAAC,OAAO,CAAA;QAChC,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,OAAM;QACR,CAAC;QACD,MAAM,cAAc,GAA2B,EAAE,CAAA;QACjD,KAAK,MAAM,GAAG,IAAI,YAAY,EAAE,CAAC;YAC/B,cAAc,CAAC,GAAG,CAAC,GAAG,QAAQ,CAAC,GAAG,CAAE,CAAA;QACtC,CAAC;QACD,oBAAoB,CAClB,MAAM,EACN,oBAAoB,CAAC,QAAQ,CAAC,cAAc,CAAC,EAAE,KAAK,EAAE;YACpD,WAAW,EAAE,QAAQ,CAAC,WAAY;YAClC,aAAa,EAAE,QAAQ,CAAC,aAAc;YACtC,WAAW,EAAE,QAAQ,CAAC,WAAY;YAClC,gBAAgB,EAAE,QAAQ,CAAC,gBAAiB;YAC5C,OAAO,EAAE,QAAQ,CAAC,OAAQ;SAC3B,CAAC,CACH,CAAA;QACD,aAAa,EAAE,CAAA;IACjB,CAAC,EACD;QACE,KAAK,CAAC,IAAI;QACV,KAAK,CAAC,QAAQ;QACd,KAAK,CAAC,MAAM;QACZ,KAAK,CAAC,aAAa;QACnB,KAAK,CAAC,cAAc;QACpB,KAAK,CAAC,eAAe;QACrB,GAAG,SAAS;KACb,CACF,CAAA;IAED,OAAO,SAAS,CAAA;AAClB,CAAC,CAAA","sourcesContent":["// Shared plumbing for every shape component (Path/Rect/Circle/Ellipse/Line/\n// Polygon/Polyline): resolves geometry + numeric paint fields (any of which\n// may be Animated), builds the `d` string and the descriptor, writes it, and\n// requests a redraw. `extraDeps` must list every non-numeric prop `computeD`\n// or `paint` close over (a plain string like `d`/`points`/`fill` changing is\n// not itself part of the Animated field set, but must still re-run `build`).\nimport { useRef, type RefObject } from \"react\"\nimport { setSvgNodeDescriptor, type Gtk } from \"../../gtkx/bridge/index\"\nimport {\n useAnimatedShapeBuild,\n type AnimatableNumber,\n} from \"./animated-support\"\nimport { useSvgRoot } from \"./context\"\nimport {\n buildShapeDescriptor,\n DEFAULT_PAINT_NUMBERS,\n type SvgPaintProps,\n} from \"./paint\"\n\nexport type ShapeNumericPaintProps = {\n fillOpacity?: AnimatableNumber\n strokeOpacity?: AnimatableNumber\n strokeWidth?: AnimatableNumber\n strokeDashoffset?: AnimatableNumber\n opacity?: AnimatableNumber\n}\n\nexport const useShapeNode = (\n componentName: string,\n geometry: Record<string, AnimatableNumber>,\n computeD: (resolvedGeometry: Record<string, number>) => string,\n paint: SvgPaintProps,\n numeric: ShapeNumericPaintProps,\n extraDeps: readonly unknown[],\n): RefObject<Gtk.Widget | null> => {\n const widgetRef = useRef<Gtk.Widget | null>(null)\n const { requestRedraw } = useSvgRoot(componentName)\n const geometryKeys = Object.keys(geometry)\n\n const fields: Record<string, AnimatableNumber> = {\n ...geometry,\n fillOpacity: numeric.fillOpacity ?? DEFAULT_PAINT_NUMBERS.fillOpacity,\n strokeOpacity: numeric.strokeOpacity ?? DEFAULT_PAINT_NUMBERS.strokeOpacity,\n strokeWidth: numeric.strokeWidth ?? DEFAULT_PAINT_NUMBERS.strokeWidth,\n strokeDashoffset:\n numeric.strokeDashoffset ?? DEFAULT_PAINT_NUMBERS.strokeDashoffset,\n opacity: numeric.opacity ?? DEFAULT_PAINT_NUMBERS.opacity,\n }\n\n useAnimatedShapeBuild(\n fields,\n (resolved) => {\n const widget = widgetRef.current\n if (!widget) {\n return\n }\n const geometryValues: Record<string, number> = {}\n for (const key of geometryKeys) {\n geometryValues[key] = resolved[key]!\n }\n setSvgNodeDescriptor(\n widget,\n buildShapeDescriptor(computeD(geometryValues), paint, {\n fillOpacity: resolved.fillOpacity!,\n strokeOpacity: resolved.strokeOpacity!,\n strokeWidth: resolved.strokeWidth!,\n strokeDashoffset: resolved.strokeDashoffset!,\n opacity: resolved.opacity!,\n }),\n )\n requestRedraw()\n },\n [\n paint.fill,\n paint.fillRule,\n paint.stroke,\n paint.strokeLinecap,\n paint.strokeLinejoin,\n paint.strokeDasharray,\n ...extraDeps,\n ],\n )\n\n return widgetRef\n}\n"]}
|
package/dist/components/text.js
CHANGED
|
@@ -33,10 +33,19 @@ export const Text = ({ style, onLayout, numberOfLines, children, testID, }) => {
|
|
|
33
33
|
const measuredLines = useRef(1);
|
|
34
34
|
const measure = useMemo(() => {
|
|
35
35
|
return (width, widthMode) => {
|
|
36
|
-
const natural = measureWidget(probe, "horizontal")
|
|
36
|
+
const { minimum, natural } = measureWidget(probe, "horizontal");
|
|
37
|
+
// Floor at the probe's own minimum width, not at 1: gtk_widget_measure()
|
|
38
|
+
// clamps a height-for-width query up to the widget's minimum before
|
|
39
|
+
// computing regardless of what we pass (logging "Trying to measure
|
|
40
|
+
// GtkLabel for width of N, but it needs at least M" while doing it), so
|
|
41
|
+
// asking for anything below `minimum` was never honored — the height
|
|
42
|
+
// below was already being computed at `minimum`, not at our `used`.
|
|
43
|
+
// Clamping here ourselves gets the same height GTK would give us
|
|
44
|
+
// anyway, keeps the returned width consistent with it, and drops the
|
|
45
|
+
// warning as a side effect rather than the goal.
|
|
37
46
|
const used = widthMode === "undefined"
|
|
38
47
|
? natural
|
|
39
|
-
: Math.min(natural, Math.max(
|
|
48
|
+
: Math.min(natural, Math.max(minimum, Math.floor(width)));
|
|
40
49
|
const height = measureWidget(probe, "vertical", used).natural;
|
|
41
50
|
const singleLine = measureWidget(probe, "vertical", natural).natural;
|
|
42
51
|
measuredLines.current =
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"text.js","sourceRoot":"","sources":["../../src/components/text.tsx"],"names":[],"mappings":";AAAA,OAAO,EACL,eAAe,EACf,OAAO,EACP,MAAM,EACN,QAAQ,GAET,MAAM,OAAO,CAAA;AACd,OAAO,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAA;AAEtD,OAAO,EACL,eAAe,EACf,GAAG,EACH,QAAQ,EACR,aAAa,EACb,KAAK,GACN,MAAM,sBAAsB,CAAA;AAC7B,OAAO,EAAE,cAAc,EAAoB,MAAM,oBAAoB,CAAA;AAUrE,MAAM,eAAe,GAAG,CAAC,QAAmB,EAAU,EAAE;IACtD,IAAI,QAAQ,KAAK,IAAI,IAAI,QAAQ,KAAK,SAAS,IAAI,QAAQ,KAAK,KAAK,EAAE,CAAC;QACtE,OAAO,EAAE,CAAA;IACX,CAAC;IACD,IAAI,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC5B,OAAO,QAAQ,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;IAC/C,CAAC;IACD,OAAO,MAAM,CAAC,QAAQ,CAAC,CAAA;AACzB,CAAC,CAAA;AAED,MAAM,aAAa,GAAG;IACpB,IAAI,EAAE,GAAG,CAAC,aAAa,CAAC,IAAI;IAC5B,KAAK,EAAE,GAAG,CAAC,aAAa,CAAC,KAAK;IAC9B,MAAM,EAAE,GAAG,CAAC,aAAa,CAAC,MAAM;IAChC,IAAI,EAAE,GAAG,CAAC,aAAa,CAAC,IAAI;CACpB,CAAA;AAEV,4EAA4E;AAC5E,yEAAyE;AACzE,2EAA2E;AAC3E,MAAM,CAAC,MAAM,IAAI,GAAG,CAAC,EACnB,KAAK,EACL,QAAQ,EACR,aAAa,EACb,QAAQ,EACR,MAAM,GACI,EAAE,EAAE;IACd,MAAM,SAAS,GAAG,MAAM,CAAmB,IAAI,CAAC,CAAA;IAChD,MAAM,IAAI,GAAG,eAAe,CAAC,QAAQ,CAAC,CAAA;IAEtC,6EAA6E;IAC7E,MAAM,CAAC,KAAK,CAAC,GAAG,QAAQ,CAAY,GAAG,EAAE,CAAC,eAAe,EAAE,CAAC,CAAA;IAE5D,MAAM,cAAc,GAAG,MAAM,CAAgB,IAAI,CAAC,CAAA;IAElD,yEAAyE;IACzE,uEAAuE;IACvE,oDAAoD;IACpD,MAAM,aAAa,GAAG,MAAM,CAAC,CAAC,CAAC,CAAA;IAE/B,MAAM,OAAO,GAAG,OAAO,CAAY,GAAG,EAAE;QACtC,OAAO,CAAC,KAAK,EAAE,SAAS,EAAE,EAAE;YAC1B,MAAM,OAAO,GAAG,aAAa,CAAC,KAAK,EAAE,YAAY,CAAC,
|
|
1
|
+
{"version":3,"file":"text.js","sourceRoot":"","sources":["../../src/components/text.tsx"],"names":[],"mappings":"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","sourcesContent":["import {\n useLayoutEffect,\n useMemo,\n useRef,\n useState,\n type ReactNode,\n} from \"react\"\nimport { textAlignToLabelProps } from \"../style/index\"\nimport type { MeasureFn, StyleProp } from \"../contracts\"\nimport {\n createTextProbe,\n Gtk,\n GtkLabel,\n measureWidget,\n Pango,\n} from \"../gtkx/bridge/index\"\nimport { useLayoutChild, type LayoutEvent } from \"./use-layout-child\"\n\nexport type TextProps = {\n style?: StyleProp\n onLayout?: (event: LayoutEvent) => void\n numberOfLines?: number\n children?: ReactNode\n testID?: string\n}\n\nconst flattenToString = (children: ReactNode): string => {\n if (children === null || children === undefined || children === false) {\n return \"\"\n }\n if (Array.isArray(children)) {\n return children.map(flattenToString).join(\"\")\n }\n return String(children)\n}\n\nconst JUSTIFICATION = {\n left: Gtk.Justification.LEFT,\n right: Gtk.Justification.RIGHT,\n center: Gtk.Justification.CENTER,\n fill: Gtk.Justification.FILL,\n} as const\n\n// Text is a measured Yoga leaf: an offscreen probe label (with the same CSS\n// class, so fonts match) feeds Pango metrics into the layout engine; the\n// visible GtkLabel is positioned by the commit hook like any other widget.\nexport const Text = ({\n style,\n onLayout,\n numberOfLines,\n children,\n testID,\n}: TextProps) => {\n const widgetRef = useRef<Gtk.Label | null>(null)\n const text = flattenToString(children)\n\n // useState lazy init — see the note in use-layout-child.ts (React Compiler).\n const [probe] = useState<Gtk.Label>(() => createTextProbe())\n\n const lastProbeClass = useRef<string | null>(null)\n\n // GTK collapses wrap+ellipsize labels to a single line unless `lines` is\n // set explicitly — remember how many lines the probe wrapped into, the\n // layout callback pushes it onto the visible label.\n const measuredLines = useRef(1)\n\n const measure = useMemo<MeasureFn>(() => {\n return (width, widthMode) => {\n const { minimum, natural } = measureWidget(probe, \"horizontal\")\n // Floor at the probe's own minimum width, not at 1: gtk_widget_measure()\n // clamps a height-for-width query up to the widget's minimum before\n // computing regardless of what we pass (logging \"Trying to measure\n // GtkLabel for width of N, but it needs at least M\" while doing it), so\n // asking for anything below `minimum` was never honored — the height\n // below was already being computed at `minimum`, not at our `used`.\n // Clamping here ourselves gets the same height GTK would give us\n // anyway, keeps the returned width consistent with it, and drops the\n // warning as a side effect rather than the goal.\n const used =\n widthMode === \"undefined\"\n ? natural\n : Math.min(natural, Math.max(minimum, Math.floor(width)))\n const height = measureWidget(probe, \"vertical\", used).natural\n const singleLine = measureWidget(probe, \"vertical\", natural).natural\n measuredLines.current =\n singleLine > 0 ? Math.max(1, Math.round(height / singleLine)) : 1\n return { width: used, height }\n }\n // The probe is stable; re-measure is triggered via markDirty below.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [])\n\n const layoutWithLines = useMemo(\n () => (event: LayoutEvent) => {\n const label = widgetRef.current\n if (label) {\n label.setLines(numberOfLines ?? measuredLines.current)\n }\n onLayout?.(event)\n },\n [numberOfLines, onLayout],\n )\n\n const { node, cssClass, flat } = useLayoutChild(widgetRef, {\n style,\n onLayout: layoutWithLines,\n measure,\n })\n\n // RN text clips to its box: an under-allocated GtkLabel would otherwise\n // paint its full text past the allocation (spike finding). Containers keep\n // overflow VISIBLE (paint-overflow); the text leaf clips.\n useLayoutEffect(() => {\n widgetRef.current?.setOverflow(Gtk.Overflow.HIDDEN)\n }, [])\n\n // Keep the probe in sync with everything that affects metrics, then\n // invalidate the Yoga leaf so the next pass re-measures.\n useLayoutEffect(() => {\n probe.setText(text)\n if (numberOfLines !== undefined) {\n probe.setLines(numberOfLines)\n probe.setEllipsize(Pango.EllipsizeMode.END)\n } else {\n probe.setLines(-1)\n probe.setEllipsize(Pango.EllipsizeMode.NONE)\n }\n if (lastProbeClass.current !== cssClass) {\n if (lastProbeClass.current) {\n probe.removeCssClass(lastProbeClass.current)\n }\n if (cssClass) {\n probe.addCssClass(cssClass)\n }\n lastProbeClass.current = cssClass\n }\n node.markDirty()\n }, [probe, node, text, numberOfLines, cssClass])\n\n const align = textAlignToLabelProps(flat.textAlign)\n\n return (\n <GtkLabel\n ref={widgetRef}\n name={testID}\n label={text}\n wrap\n xalign={align.xalign}\n yalign={0}\n justify={JUSTIFICATION[align.justification]}\n lines={numberOfLines}\n // RN semantics: the ellipsis is opt-in via numberOfLines. Plain text\n // wraps naturally; an unbreakable word wider than the rect just clips\n // (overflow HIDDEN above) — the layout manager allocates the rect\n // regardless of the label's own minimum, so nothing pushes siblings.\n ellipsize={\n numberOfLines !== undefined\n ? Pango.EllipsizeMode.END\n : Pango.EllipsizeMode.NONE\n }\n cssClasses={cssClass ? [cssClass] : []}\n />\n )\n}\n"]}
|
|
@@ -4,7 +4,7 @@ import { defaultCssRegistry } from "../style/registry.gtkx";
|
|
|
4
4
|
import { allocateChild, attachRnLayout, detachRnLayout, measureWidget, queueAllocate, queueResize, } from "../gtkx/bridge/index";
|
|
5
5
|
import { perfAddTime, perfBurst, perfCount, perfEnabled, perfNow, } from "../perf";
|
|
6
6
|
import { useHostNode } from "./host-node";
|
|
7
|
-
import { deferDuringAllocate, getStoredOffset, getStoredRect, setStoredRect, } from "./rect-store";
|
|
7
|
+
import { deferDuringAllocate, getStoredOffset, getStoredRect, setStoredRect, setStoredTransform, } from "./rect-store";
|
|
8
8
|
// Perf: sizeAllocate recurses synchronously into child containers, so a
|
|
9
9
|
// naive per-pass timer double-counts nested passes. Track depth and time
|
|
10
10
|
// only the outermost (top-level) allocate — that is the real wall time GTK
|
|
@@ -32,6 +32,25 @@ export const useLayoutChild = (widgetRef, options) => {
|
|
|
32
32
|
node.setStyle(JSON.parse(layoutKey));
|
|
33
33
|
}, [node, layoutKey]);
|
|
34
34
|
const cssClass = defaultCssRegistry.getClassName(visual);
|
|
35
|
+
// RN transforms are visual only: the widget keeps the box Yoga gave it, so
|
|
36
|
+
// a transform never touches the shadow tree — it only asks the parent
|
|
37
|
+
// container to run its allocate hook again. Keyed on the serialized array
|
|
38
|
+
// so a re-render with the same transform costs nothing.
|
|
39
|
+
// (Animated.View owns its own transform and strips it from the static
|
|
40
|
+
// style before it gets here, so the two writers never race.)
|
|
41
|
+
const transformKey = JSON.stringify(visual.transform ?? null);
|
|
42
|
+
useLayoutEffect(() => {
|
|
43
|
+
const widget = widgetRef.current;
|
|
44
|
+
if (!widget) {
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
setStoredTransform(widget, JSON.parse(transformKey));
|
|
48
|
+
const parentWidget = host.widgetRef.current;
|
|
49
|
+
if (parentWidget) {
|
|
50
|
+
queueAllocate(parentWidget);
|
|
51
|
+
}
|
|
52
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
53
|
+
}, [transformKey]);
|
|
35
54
|
useLayoutEffect(() => {
|
|
36
55
|
const parent = host.node;
|
|
37
56
|
// The reconciler has already attached our widget in JSX order, so its
|
|
@@ -59,6 +78,26 @@ export const useLayoutChild = (widgetRef, options) => {
|
|
|
59
78
|
return;
|
|
60
79
|
}
|
|
61
80
|
const previous = getStoredRect(widget);
|
|
81
|
+
// A commit does not imply a change. The engine filters unchanged nodes
|
|
82
|
+
// out of the walk, EXCEPT nodes carrying a measure function — every
|
|
83
|
+
// Text leaf, every intrinsic widget — which it must still visit. Those
|
|
84
|
+
// then arrive here with the rect they already have, and queueing an
|
|
85
|
+
// allocation for them makes GTK re-allocate and re-snapshot the whole
|
|
86
|
+
// container for nothing.
|
|
87
|
+
//
|
|
88
|
+
// The cost of that is not academic: it scales with children × painted
|
|
89
|
+
// area. Measured on a 500-row list, maximized, windowSize 11 — 801
|
|
90
|
+
// live nodes — the frame average was 40.4 ms against 15.7 ms windowed,
|
|
91
|
+
// while neither 777 nodes in a small window nor 205 nodes maximized
|
|
92
|
+
// cost anything. Only the product hurts, and this is where we inflate
|
|
93
|
+
// the first factor.
|
|
94
|
+
if (previous &&
|
|
95
|
+
previous.x === rect.x &&
|
|
96
|
+
previous.y === rect.y &&
|
|
97
|
+
previous.width === rect.width &&
|
|
98
|
+
previous.height === rect.height) {
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
62
101
|
setStoredRect(widget, rect);
|
|
63
102
|
const sizeChanged = !previous ||
|
|
64
103
|
previous.width !== rect.width ||
|
|
@@ -102,10 +141,14 @@ export const useLayoutChild = (widgetRef, options) => {
|
|
|
102
141
|
if (!widget) {
|
|
103
142
|
return { width: 0, height: 0 };
|
|
104
143
|
}
|
|
105
|
-
const natural = measureWidget(widget, "horizontal")
|
|
144
|
+
const { minimum, natural } = measureWidget(widget, "horizontal");
|
|
145
|
+
// Floor at the widget's own minimum width, not at 1 — see the same
|
|
146
|
+
// clamp in text.tsx's measure for why: gtk_widget_measure() enforces
|
|
147
|
+
// this floor internally regardless of what we request (warning while
|
|
148
|
+
// it does), so anything below `minimum` was never actually honored.
|
|
106
149
|
const used = widthMode === "undefined"
|
|
107
150
|
? natural
|
|
108
|
-
: Math.min(natural, Math.max(
|
|
151
|
+
: Math.min(natural, Math.max(minimum, Math.floor(width)));
|
|
109
152
|
return {
|
|
110
153
|
width: used,
|
|
111
154
|
height: measureWidget(widget, "vertical", used).natural,
|
|
@@ -171,7 +214,7 @@ export const useRnContainer = (widgetRef, node, options) => {
|
|
|
171
214
|
const rect = getStoredRect(child);
|
|
172
215
|
if (rect) {
|
|
173
216
|
const offset = getStoredOffset(child);
|
|
174
|
-
allocateChild(child, rect.x + offset.dx, rect.y + offset.dy, rect.width, rect.height);
|
|
217
|
+
allocateChild(child, rect.x + offset.dx, rect.y + offset.dy, rect.width, rect.height, offset.matrix);
|
|
175
218
|
children += 1;
|
|
176
219
|
}
|
|
177
220
|
child = child.getNextSibling();
|