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
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"split-style.js","sourceRoot":"","sources":["../../src/style/split-style.ts"],"names":[],"mappings":"AAAA,uEAAuE;AACvE,iEAAiE;AAQjE,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AAExC,sEAAsE;AACtE,mEAAmE;AACnE,MAAM,WAAW,GAAoC;IACnD,YAAY,EAAE,IAAI;IAClB,UAAU,EAAE,IAAI;IAChB,SAAS,EAAE,IAAI;IACf,WAAW,EAAE,IAAI;IACjB,MAAM,EAAE,IAAI;IACZ,SAAS,EAAE,IAAI;IACf,SAAS,EAAE,IAAI;IACf,OAAO,EAAE,IAAI;IACb,IAAI,EAAE,IAAI;IACV,SAAS,EAAE,IAAI;IACf,aAAa,EAAE,IAAI;IACnB,QAAQ,EAAE,IAAI;IACd,UAAU,EAAE,IAAI;IAChB,QAAQ,EAAE,IAAI;IACd,GAAG,EAAE,IAAI;IACT,MAAM,EAAE,IAAI;IACZ,cAAc,EAAE,IAAI;IACpB,IAAI,EAAE,IAAI;IACV,MAAM,EAAE,IAAI;IACZ,YAAY,EAAE,IAAI;IAClB,gBAAgB,EAAE,IAAI;IACtB,UAAU,EAAE,IAAI;IAChB,WAAW,EAAE,IAAI;IACjB,SAAS,EAAE,IAAI;IACf,cAAc,EAAE,IAAI;IACpB,SAAS,EAAE,IAAI;IACf,QAAQ,EAAE,IAAI;IACd,SAAS,EAAE,IAAI;IACf,QAAQ,EAAE,IAAI;IACd,QAAQ,EAAE,IAAI;IACd,OAAO,EAAE,IAAI;IACb,aAAa,EAAE,IAAI;IACnB,iBAAiB,EAAE,IAAI;IACvB,WAAW,EAAE,IAAI;IACjB,YAAY,EAAE,IAAI;IAClB,UAAU,EAAE,IAAI;IAChB,eAAe,EAAE,IAAI;IACrB,QAAQ,EAAE,IAAI;IACd,KAAK,EAAE,IAAI;IACX,MAAM,EAAE,IAAI;IACZ,GAAG,EAAE,IAAI;IACT,KAAK,EAAE,IAAI;CACZ,CAAA;AAED,
|
|
1
|
+
{"version":3,"file":"split-style.js","sourceRoot":"","sources":["../../src/style/split-style.ts"],"names":[],"mappings":"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","sourcesContent":["// Classifies a flattened style into layout props (consumed by the Yoga\n// engine) and visual props (compiled into GTK CSS). Pure module.\n\nimport type {\n FlatStyle,\n LayoutStyle,\n SplitStyle,\n VisualStyle,\n} from \"../contracts\"\nimport { warnOnce } from \"./dev-warning\"\n\n// Exhaustive over the frozen contract: adding a key to LayoutStyle in\n// contracts.ts fails compilation here until the key is classified.\nconst LAYOUT_KEYS: Record<keyof LayoutStyle, true> = {\n alignContent: true,\n alignItems: true,\n alignSelf: true,\n aspectRatio: true,\n bottom: true,\n columnGap: true,\n direction: true,\n display: true,\n flex: true,\n flexBasis: true,\n flexDirection: true,\n flexGrow: true,\n flexShrink: true,\n flexWrap: true,\n gap: true,\n height: true,\n justifyContent: true,\n left: true,\n margin: true,\n marginBottom: true,\n marginHorizontal: true,\n marginLeft: true,\n marginRight: true,\n marginTop: true,\n marginVertical: true,\n maxHeight: true,\n maxWidth: true,\n minHeight: true,\n minWidth: true,\n overflow: true,\n padding: true,\n paddingBottom: true,\n paddingHorizontal: true,\n paddingLeft: true,\n paddingRight: true,\n paddingTop: true,\n paddingVertical: true,\n position: true,\n right: true,\n rowGap: true,\n top: true,\n width: true,\n}\n\n// Note: `transform` is visual by contract — it is applied as the\n// GskTransform of the child's allocation, not through CSS, and is passed\n// through here untouched.\nconst VISUAL_KEYS: Record<keyof VisualStyle, true> = {\n backgroundColor: true,\n borderBottomColor: true,\n borderBottomLeftRadius: true,\n borderBottomRightRadius: true,\n borderBottomWidth: true,\n borderColor: true,\n borderLeftColor: true,\n borderLeftWidth: true,\n borderRadius: true,\n borderRightColor: true,\n borderRightWidth: true,\n borderStyle: true,\n borderTopColor: true,\n borderTopLeftRadius: true,\n borderTopRightRadius: true,\n borderTopWidth: true,\n borderWidth: true,\n color: true,\n fontFamily: true,\n fontSize: true,\n fontStyle: true,\n fontWeight: true,\n letterSpacing: true,\n lineHeight: true,\n opacity: true,\n textAlign: true,\n transform: true,\n}\n\n/**\n * Splits a flattened style into { layout, visual }. Unknown properties are\n * ignored with a one-time dev warning per key; undefined values are dropped.\n */\nexport const splitStyle = (flat: FlatStyle): SplitStyle => {\n const layout: LayoutStyle = {}\n const visual: VisualStyle = {}\n for (const [key, value] of Object.entries(flat)) {\n if (value === undefined) {\n continue\n }\n if (Object.hasOwn(LAYOUT_KEYS, key)) {\n ;(layout as Record<string, unknown>)[key] = value\n } else if (Object.hasOwn(VISUAL_KEYS, key)) {\n ;(visual as Record<string, unknown>)[key] = value\n } else {\n warnOnce(\n `unknown-style-prop:${key}`,\n `[react-native-gtkx] Unknown style property \"${key}\" is not supported and will be ignored`,\n )\n }\n }\n return { layout, visual }\n}\n"]}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { Transform2D, TransformPart } from "../contracts";
|
|
2
|
+
export declare const IDENTITY_TRANSFORM: Transform2D;
|
|
3
|
+
/**
|
|
4
|
+
* RN angle syntax: a number with an explicit `deg` or `rad` unit. Returns
|
|
5
|
+
* degrees, or null for anything unparseable (warned once, then dropped —
|
|
6
|
+
* a bad angle must not take the whole style down).
|
|
7
|
+
*/
|
|
8
|
+
export declare const parseAngle: (value: unknown) => number | null;
|
|
9
|
+
/**
|
|
10
|
+
* Folds an RN transform array into a single matrix. Order is RN's (and
|
|
11
|
+
* CSS's): the leftmost entry is the outermost matrix, so it is applied to a
|
|
12
|
+
* point LAST — `[{rotate}, {translateX}]` translates, then rotates the
|
|
13
|
+
* result about the origin.
|
|
14
|
+
*/
|
|
15
|
+
export declare const composeTransform: (parts: readonly TransformPart[]) => Transform2D;
|
|
16
|
+
export declare const isIdentityTransform: (matrix: Transform2D) => boolean;
|
|
17
|
+
/**
|
|
18
|
+
* True when the matrix is a pure translation. Those keep the existing
|
|
19
|
+
* positional fast path (the rect is simply offset), which costs no
|
|
20
|
+
* GskTransform at all — only rotate/scale need one.
|
|
21
|
+
*/
|
|
22
|
+
export declare const isTranslationOnly: (matrix: Transform2D) => boolean;
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
// Composes an RN `transform` array into one 2D affine matrix. Pure module —
|
|
2
|
+
// no bridge imports — so the whole ordering contract is unit-testable on any
|
|
3
|
+
// OS; the bridge only turns the finished matrix into a GskTransform.
|
|
4
|
+
//
|
|
5
|
+
// Why a matrix and not a chain of GskTransform calls: measured in the VM
|
|
6
|
+
// (docs/research/transforms.md), a five-op GskTransform chain costs 18.3 us
|
|
7
|
+
// per build against 5.7 us for one gsk_transform_matrix2d(), and both
|
|
8
|
+
// produce a bit-identical to2d(). Composition is six multiplies of plain
|
|
9
|
+
// numbers, so it stays on the Animated fast path.
|
|
10
|
+
import { warnOnce } from "./dev-warning";
|
|
11
|
+
export const IDENTITY_TRANSFORM = {
|
|
12
|
+
xx: 1,
|
|
13
|
+
yx: 0,
|
|
14
|
+
xy: 0,
|
|
15
|
+
yy: 1,
|
|
16
|
+
dx: 0,
|
|
17
|
+
dy: 0,
|
|
18
|
+
};
|
|
19
|
+
const DEG_PER_RAD = 180 / Math.PI;
|
|
20
|
+
/**
|
|
21
|
+
* RN angle syntax: a number with an explicit `deg` or `rad` unit. Returns
|
|
22
|
+
* degrees, or null for anything unparseable (warned once, then dropped —
|
|
23
|
+
* a bad angle must not take the whole style down).
|
|
24
|
+
*/
|
|
25
|
+
export const parseAngle = (value) => {
|
|
26
|
+
if (typeof value === "number") {
|
|
27
|
+
// RN rejects unitless angles in styles, but an Animated node driving a
|
|
28
|
+
// rotation with a plain numeric outputRange is a common shortcut and
|
|
29
|
+
// degrees are the only sane reading of it.
|
|
30
|
+
return Number.isFinite(value) ? value : null;
|
|
31
|
+
}
|
|
32
|
+
if (typeof value !== "string") {
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
const match = /^\s*([+-]?(?:\d+\.?\d*|\.\d+)(?:[eE][+-]?\d+)?)(deg|rad)\s*$/.exec(value);
|
|
36
|
+
if (!match) {
|
|
37
|
+
warnOnce(`invalid-angle:${value}`, `[react-native-gtkx] Invalid angle ${JSON.stringify(value)} in "transform" — expected "<number>deg" or "<number>rad"; the entry is ignored`);
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
const amount = parseFloat(match[1]);
|
|
41
|
+
return match[2] === "rad" ? amount * DEG_PER_RAD : amount;
|
|
42
|
+
};
|
|
43
|
+
// left * right, both in the Transform2D component order.
|
|
44
|
+
const multiply = (left, right) => ({
|
|
45
|
+
xx: left.xx * right.xx + left.xy * right.yx,
|
|
46
|
+
yx: left.yx * right.xx + left.yy * right.yx,
|
|
47
|
+
xy: left.xx * right.xy + left.xy * right.yy,
|
|
48
|
+
yy: left.yx * right.xy + left.yy * right.yy,
|
|
49
|
+
dx: left.xx * right.dx + left.xy * right.dy + left.dx,
|
|
50
|
+
dy: left.yx * right.dx + left.yy * right.dy + left.dy,
|
|
51
|
+
});
|
|
52
|
+
const scaleMatrix = (x, y) => Number.isFinite(x) && Number.isFinite(y)
|
|
53
|
+
? { xx: x, yx: 0, xy: 0, yy: y, dx: 0, dy: 0 }
|
|
54
|
+
: null;
|
|
55
|
+
const rotateMatrix = (angle) => {
|
|
56
|
+
const degrees = parseAngle(angle);
|
|
57
|
+
if (degrees === null) {
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
const radians = degrees / DEG_PER_RAD;
|
|
61
|
+
const cos = Math.cos(radians);
|
|
62
|
+
const sin = Math.sin(radians);
|
|
63
|
+
return { xx: cos, yx: sin, xy: -sin, yy: cos, dx: 0, dy: 0 };
|
|
64
|
+
};
|
|
65
|
+
// A TransformPart is a single-key object, and this runs on every animated
|
|
66
|
+
// frame — so the supported keys are probed directly (a monomorphic property
|
|
67
|
+
// load) instead of being discovered with Object.keys/for-in, and the matrices
|
|
68
|
+
// are plain literals rather than spreads of the identity. Key discovery only
|
|
69
|
+
// happens on the miss path, to name the unsupported entry in the warning.
|
|
70
|
+
const partToMatrix = (part) => {
|
|
71
|
+
const entry = part;
|
|
72
|
+
const translateX = entry.translateX;
|
|
73
|
+
if (typeof translateX === "number") {
|
|
74
|
+
return Number.isFinite(translateX)
|
|
75
|
+
? { xx: 1, yx: 0, xy: 0, yy: 1, dx: translateX, dy: 0 }
|
|
76
|
+
: null;
|
|
77
|
+
}
|
|
78
|
+
const translateY = entry.translateY;
|
|
79
|
+
if (typeof translateY === "number") {
|
|
80
|
+
return Number.isFinite(translateY)
|
|
81
|
+
? { xx: 1, yx: 0, xy: 0, yy: 1, dx: 0, dy: translateY }
|
|
82
|
+
: null;
|
|
83
|
+
}
|
|
84
|
+
const scale = entry.scale;
|
|
85
|
+
if (typeof scale === "number") {
|
|
86
|
+
return scaleMatrix(scale, scale);
|
|
87
|
+
}
|
|
88
|
+
const scaleX = entry.scaleX;
|
|
89
|
+
if (typeof scaleX === "number") {
|
|
90
|
+
return scaleMatrix(scaleX, 1);
|
|
91
|
+
}
|
|
92
|
+
const scaleY = entry.scaleY;
|
|
93
|
+
if (typeof scaleY === "number") {
|
|
94
|
+
return scaleMatrix(1, scaleY);
|
|
95
|
+
}
|
|
96
|
+
if (entry.rotate !== undefined) {
|
|
97
|
+
return rotateMatrix(entry.rotate);
|
|
98
|
+
}
|
|
99
|
+
if (entry.rotateZ !== undefined) {
|
|
100
|
+
return rotateMatrix(entry.rotateZ);
|
|
101
|
+
}
|
|
102
|
+
for (const key of Object.keys(entry)) {
|
|
103
|
+
warnOnce(`unsupported-transform:${key}`, `[react-native-gtkx] Unsupported transform "${key}" — supported: translateX, translateY, scale, scaleX, scaleY, rotate, rotateZ`);
|
|
104
|
+
}
|
|
105
|
+
return null;
|
|
106
|
+
};
|
|
107
|
+
/**
|
|
108
|
+
* Folds an RN transform array into a single matrix. Order is RN's (and
|
|
109
|
+
* CSS's): the leftmost entry is the outermost matrix, so it is applied to a
|
|
110
|
+
* point LAST — `[{rotate}, {translateX}]` translates, then rotates the
|
|
111
|
+
* result about the origin.
|
|
112
|
+
*/
|
|
113
|
+
export const composeTransform = (parts) => {
|
|
114
|
+
// `matrix === null` means "still the identity", which lets the common
|
|
115
|
+
// single-entry array skip the multiply entirely.
|
|
116
|
+
let matrix = null;
|
|
117
|
+
for (const part of parts) {
|
|
118
|
+
const next = partToMatrix(part);
|
|
119
|
+
if (next) {
|
|
120
|
+
matrix = matrix === null ? next : multiply(matrix, next);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
return matrix ?? IDENTITY_TRANSFORM;
|
|
124
|
+
};
|
|
125
|
+
export const isIdentityTransform = (matrix) => matrix.xx === 1 &&
|
|
126
|
+
matrix.yx === 0 &&
|
|
127
|
+
matrix.xy === 0 &&
|
|
128
|
+
matrix.yy === 1 &&
|
|
129
|
+
matrix.dx === 0 &&
|
|
130
|
+
matrix.dy === 0;
|
|
131
|
+
/**
|
|
132
|
+
* True when the matrix is a pure translation. Those keep the existing
|
|
133
|
+
* positional fast path (the rect is simply offset), which costs no
|
|
134
|
+
* GskTransform at all — only rotate/scale need one.
|
|
135
|
+
*/
|
|
136
|
+
export const isTranslationOnly = (matrix) => matrix.xx === 1 && matrix.yx === 0 && matrix.xy === 0 && matrix.yy === 1;
|
|
137
|
+
//# sourceMappingURL=transform.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transform.js","sourceRoot":"","sources":["../../src/style/transform.ts"],"names":[],"mappings":"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","sourcesContent":["// Composes an RN `transform` array into one 2D affine matrix. Pure module —\n// no bridge imports — so the whole ordering contract is unit-testable on any\n// OS; the bridge only turns the finished matrix into a GskTransform.\n//\n// Why a matrix and not a chain of GskTransform calls: measured in the VM\n// (docs/research/transforms.md), a five-op GskTransform chain costs 18.3 us\n// per build against 5.7 us for one gsk_transform_matrix2d(), and both\n// produce a bit-identical to2d(). Composition is six multiplies of plain\n// numbers, so it stays on the Animated fast path.\n\nimport type { Transform2D, TransformPart } from \"../contracts\"\nimport { warnOnce } from \"./dev-warning\"\n\nexport const IDENTITY_TRANSFORM: Transform2D = {\n xx: 1,\n yx: 0,\n xy: 0,\n yy: 1,\n dx: 0,\n dy: 0,\n}\n\nconst DEG_PER_RAD = 180 / Math.PI\n\n/**\n * RN angle syntax: a number with an explicit `deg` or `rad` unit. Returns\n * degrees, or null for anything unparseable (warned once, then dropped —\n * a bad angle must not take the whole style down).\n */\nexport const parseAngle = (value: unknown): number | null => {\n if (typeof value === \"number\") {\n // RN rejects unitless angles in styles, but an Animated node driving a\n // rotation with a plain numeric outputRange is a common shortcut and\n // degrees are the only sane reading of it.\n return Number.isFinite(value) ? value : null\n }\n if (typeof value !== \"string\") {\n return null\n }\n const match =\n /^\\s*([+-]?(?:\\d+\\.?\\d*|\\.\\d+)(?:[eE][+-]?\\d+)?)(deg|rad)\\s*$/.exec(value)\n if (!match) {\n warnOnce(\n `invalid-angle:${value}`,\n `[react-native-gtkx] Invalid angle ${JSON.stringify(value)} in \"transform\" — expected \"<number>deg\" or \"<number>rad\"; the entry is ignored`,\n )\n return null\n }\n const amount = parseFloat(match[1]!)\n return match[2] === \"rad\" ? amount * DEG_PER_RAD : amount\n}\n\n// left * right, both in the Transform2D component order.\nconst multiply = (left: Transform2D, right: Transform2D): Transform2D => ({\n xx: left.xx * right.xx + left.xy * right.yx,\n yx: left.yx * right.xx + left.yy * right.yx,\n xy: left.xx * right.xy + left.xy * right.yy,\n yy: left.yx * right.xy + left.yy * right.yy,\n dx: left.xx * right.dx + left.xy * right.dy + left.dx,\n dy: left.yx * right.dx + left.yy * right.dy + left.dy,\n})\n\nconst scaleMatrix = (x: number, y: number): Transform2D | null =>\n Number.isFinite(x) && Number.isFinite(y)\n ? { xx: x, yx: 0, xy: 0, yy: y, dx: 0, dy: 0 }\n : null\n\nconst rotateMatrix = (angle: unknown): Transform2D | null => {\n const degrees = parseAngle(angle)\n if (degrees === null) {\n return null\n }\n const radians = degrees / DEG_PER_RAD\n const cos = Math.cos(radians)\n const sin = Math.sin(radians)\n return { xx: cos, yx: sin, xy: -sin, yy: cos, dx: 0, dy: 0 }\n}\n\n// A TransformPart is a single-key object, and this runs on every animated\n// frame — so the supported keys are probed directly (a monomorphic property\n// load) instead of being discovered with Object.keys/for-in, and the matrices\n// are plain literals rather than spreads of the identity. Key discovery only\n// happens on the miss path, to name the unsupported entry in the warning.\nconst partToMatrix = (part: TransformPart): Transform2D | null => {\n const entry = part as Record<string, unknown>\n\n const translateX = entry.translateX\n if (typeof translateX === \"number\") {\n return Number.isFinite(translateX)\n ? { xx: 1, yx: 0, xy: 0, yy: 1, dx: translateX, dy: 0 }\n : null\n }\n const translateY = entry.translateY\n if (typeof translateY === \"number\") {\n return Number.isFinite(translateY)\n ? { xx: 1, yx: 0, xy: 0, yy: 1, dx: 0, dy: translateY }\n : null\n }\n const scale = entry.scale\n if (typeof scale === \"number\") {\n return scaleMatrix(scale, scale)\n }\n const scaleX = entry.scaleX\n if (typeof scaleX === \"number\") {\n return scaleMatrix(scaleX, 1)\n }\n const scaleY = entry.scaleY\n if (typeof scaleY === \"number\") {\n return scaleMatrix(1, scaleY)\n }\n if (entry.rotate !== undefined) {\n return rotateMatrix(entry.rotate)\n }\n if (entry.rotateZ !== undefined) {\n return rotateMatrix(entry.rotateZ)\n }\n\n for (const key of Object.keys(entry)) {\n warnOnce(\n `unsupported-transform:${key}`,\n `[react-native-gtkx] Unsupported transform \"${key}\" — supported: translateX, translateY, scale, scaleX, scaleY, rotate, rotateZ`,\n )\n }\n return null\n}\n\n/**\n * Folds an RN transform array into a single matrix. Order is RN's (and\n * CSS's): the leftmost entry is the outermost matrix, so it is applied to a\n * point LAST — `[{rotate}, {translateX}]` translates, then rotates the\n * result about the origin.\n */\nexport const composeTransform = (\n parts: readonly TransformPart[],\n): Transform2D => {\n // `matrix === null` means \"still the identity\", which lets the common\n // single-entry array skip the multiply entirely.\n let matrix: Transform2D | null = null\n for (const part of parts) {\n const next = partToMatrix(part)\n if (next) {\n matrix = matrix === null ? next : multiply(matrix, next)\n }\n }\n return matrix ?? IDENTITY_TRANSFORM\n}\n\nexport const isIdentityTransform = (matrix: Transform2D): boolean =>\n matrix.xx === 1 &&\n matrix.yx === 0 &&\n matrix.xy === 0 &&\n matrix.yy === 1 &&\n matrix.dx === 0 &&\n matrix.dy === 0\n\n/**\n * True when the matrix is a pure translation. Those keep the existing\n * positional fast path (the rect is simply offset), which costs no\n * GskTransform at all — only rotate/scale need one.\n */\nexport const isTranslationOnly = (matrix: Transform2D): boolean =>\n matrix.xx === 1 && matrix.yx === 0 && matrix.xy === 0 && matrix.yy === 1\n"]}
|
package/dist/style/visual-css.js
CHANGED
|
@@ -8,8 +8,9 @@
|
|
|
8
8
|
// - line-height: supported since GTK 4.6 (we target 4.20+);
|
|
9
9
|
// - text-align does NOT exist in GTK CSS — textAlign is applied by the Text
|
|
10
10
|
// component via widget properties (justify/xalign), so it is skipped here;
|
|
11
|
-
// - transform
|
|
12
|
-
//
|
|
11
|
+
// - transform does NOT exist as a widget property in GTK4 CSS either: it is
|
|
12
|
+
// composed into a matrix by ./transform.ts and applied as the GskTransform
|
|
13
|
+
// of the child's allocation, so it is skipped here as well.
|
|
13
14
|
import { parseColor } from "./colors";
|
|
14
15
|
import { warnOnce } from "./dev-warning";
|
|
15
16
|
const px = (value) => `${value}px`;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"visual-css.js","sourceRoot":"","sources":["../../src/style/visual-css.ts"],"names":[],"mappings":"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","sourcesContent":["// Compiles a VisualStyle into a GTK4 CSS declaration block (no selector).\n// Pure module — the resulting string is registered as a class through an\n// injected `css` function (see registry.ts / registry.gtkx.ts).\n//\n// GTK4 CSS is not web CSS. What this generator relies on:\n// - background-color, border-*, border-radius (incl. per-corner), opacity,\n// color, font-*, letter-spacing: supported by GTK4;\n// - line-height: supported since GTK 4.6 (we target 4.20+);\n// - text-align does NOT exist in GTK CSS — textAlign is applied by the Text\n// component via widget properties (justify/xalign), so it is skipped here;\n// - transform is applied by the layout engine via Fixed.Child matrices,\n// never through CSS, and is skipped here as well.\n\nimport type { VisualStyle } from \"../contracts\"\nimport { parseColor } from \"./colors\"\nimport { warnOnce } from \"./dev-warning\"\n\nconst px = (value: number): string => `${value}px`\n\nconst clamp01 = (value: number): number => Math.min(1, Math.max(0, value))\n\nconst pushColor = (\n declarations: string[],\n cssProperty: string,\n value: string,\n styleProperty: string,\n): void => {\n const parsed = parseColor(value)\n if (parsed === null) {\n warnOnce(\n `invalid-color:${styleProperty}:${value}`,\n `[react-native-gtkx] Invalid color ${JSON.stringify(value)} in \"${styleProperty}\" — declaration dropped`,\n )\n return\n }\n declarations.push(`${cssProperty}: ${parsed};`)\n}\n\nconst formatFontFamily = (family: string): string => {\n const trimmed = family.trim()\n if (/^(\".*\"|'.*')$/.test(trimmed)) {\n return trimmed\n }\n return /^[A-Za-z][\\w-]*$/.test(trimmed)\n ? trimmed\n : `\"${trimmed.replace(/\"/g, '\\\\\"')}\"`\n}\n\n/**\n * Renders a VisualStyle as GTK CSS declarations (one per line, stable\n * order). Returns \"\" when the style produces no CSS (e.g. transform-only).\n */\nexport const visualStyleToCss = (visual: VisualStyle): string => {\n const decls: string[] = []\n\n if (visual.backgroundColor !== undefined) {\n pushColor(\n decls,\n \"background-color\",\n visual.backgroundColor,\n \"backgroundColor\",\n )\n }\n if (visual.opacity !== undefined) {\n decls.push(`opacity: ${clamp01(visual.opacity)};`)\n }\n\n // GTK defaults border-style to none — width alone renders nothing. An\n // explicit borderStyle wins (GTK4 CSS supports solid/dotted/dashed);\n // otherwise any positive border width turns the border on with \"solid\".\n const hasVisibleBorderWidth = [\n visual.borderWidth,\n visual.borderTopWidth,\n visual.borderRightWidth,\n visual.borderBottomWidth,\n visual.borderLeftWidth,\n ].some((width) => width !== undefined && width > 0)\n if (visual.borderStyle !== undefined) {\n decls.push(`border-style: ${visual.borderStyle};`)\n } else if (hasVisibleBorderWidth) {\n decls.push(\"border-style: solid;\")\n }\n // Shorthands first, then per-side overrides so the sides win.\n if (visual.borderWidth !== undefined) {\n decls.push(`border-width: ${px(visual.borderWidth)};`)\n }\n if (visual.borderTopWidth !== undefined) {\n decls.push(`border-top-width: ${px(visual.borderTopWidth)};`)\n }\n if (visual.borderRightWidth !== undefined) {\n decls.push(`border-right-width: ${px(visual.borderRightWidth)};`)\n }\n if (visual.borderBottomWidth !== undefined) {\n decls.push(`border-bottom-width: ${px(visual.borderBottomWidth)};`)\n }\n if (visual.borderLeftWidth !== undefined) {\n decls.push(`border-left-width: ${px(visual.borderLeftWidth)};`)\n }\n if (visual.borderColor !== undefined) {\n pushColor(decls, \"border-color\", visual.borderColor, \"borderColor\")\n }\n if (visual.borderTopColor !== undefined) {\n pushColor(\n decls,\n \"border-top-color\",\n visual.borderTopColor,\n \"borderTopColor\",\n )\n }\n if (visual.borderRightColor !== undefined) {\n pushColor(\n decls,\n \"border-right-color\",\n visual.borderRightColor,\n \"borderRightColor\",\n )\n }\n if (visual.borderBottomColor !== undefined) {\n pushColor(\n decls,\n \"border-bottom-color\",\n visual.borderBottomColor,\n \"borderBottomColor\",\n )\n }\n if (visual.borderLeftColor !== undefined) {\n pushColor(\n decls,\n \"border-left-color\",\n visual.borderLeftColor,\n \"borderLeftColor\",\n )\n }\n if (visual.borderRadius !== undefined) {\n decls.push(`border-radius: ${px(visual.borderRadius)};`)\n }\n // Per-corner radii come after the shorthand so they override it.\n if (visual.borderTopLeftRadius !== undefined) {\n decls.push(`border-top-left-radius: ${px(visual.borderTopLeftRadius)};`)\n }\n if (visual.borderTopRightRadius !== undefined) {\n decls.push(`border-top-right-radius: ${px(visual.borderTopRightRadius)};`)\n }\n if (visual.borderBottomRightRadius !== undefined) {\n decls.push(\n `border-bottom-right-radius: ${px(visual.borderBottomRightRadius)};`,\n )\n }\n if (visual.borderBottomLeftRadius !== undefined) {\n decls.push(\n `border-bottom-left-radius: ${px(visual.borderBottomLeftRadius)};`,\n )\n }\n\n if (visual.color !== undefined) {\n pushColor(decls, \"color\", visual.color, \"color\")\n }\n if (visual.fontFamily !== undefined) {\n decls.push(`font-family: ${formatFontFamily(visual.fontFamily)};`)\n }\n if (visual.fontSize !== undefined) {\n decls.push(`font-size: ${px(visual.fontSize)};`)\n }\n if (visual.fontStyle !== undefined) {\n decls.push(`font-style: ${visual.fontStyle};`)\n }\n if (visual.fontWeight !== undefined) {\n decls.push(`font-weight: ${visual.fontWeight};`)\n }\n if (visual.letterSpacing !== undefined) {\n decls.push(`letter-spacing: ${px(visual.letterSpacing)};`)\n }\n if (visual.lineHeight !== undefined) {\n decls.push(`line-height: ${px(visual.lineHeight)};`)\n }\n\n // visual.textAlign, visual.transform: intentionally not CSS (see header).\n\n return decls.join(\"\\n\")\n}\n"]}
|
|
1
|
+
{"version":3,"file":"visual-css.js","sourceRoot":"","sources":["../../src/style/visual-css.ts"],"names":[],"mappings":"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","sourcesContent":["// Compiles a VisualStyle into a GTK4 CSS declaration block (no selector).\n// Pure module — the resulting string is registered as a class through an\n// injected `css` function (see registry.ts / registry.gtkx.ts).\n//\n// GTK4 CSS is not web CSS. What this generator relies on:\n// - background-color, border-*, border-radius (incl. per-corner), opacity,\n// color, font-*, letter-spacing: supported by GTK4;\n// - line-height: supported since GTK 4.6 (we target 4.20+);\n// - text-align does NOT exist in GTK CSS — textAlign is applied by the Text\n// component via widget properties (justify/xalign), so it is skipped here;\n// - transform does NOT exist as a widget property in GTK4 CSS either: it is\n// composed into a matrix by ./transform.ts and applied as the GskTransform\n// of the child's allocation, so it is skipped here as well.\n\nimport type { VisualStyle } from \"../contracts\"\nimport { parseColor } from \"./colors\"\nimport { warnOnce } from \"./dev-warning\"\n\nconst px = (value: number): string => `${value}px`\n\nconst clamp01 = (value: number): number => Math.min(1, Math.max(0, value))\n\nconst pushColor = (\n declarations: string[],\n cssProperty: string,\n value: string,\n styleProperty: string,\n): void => {\n const parsed = parseColor(value)\n if (parsed === null) {\n warnOnce(\n `invalid-color:${styleProperty}:${value}`,\n `[react-native-gtkx] Invalid color ${JSON.stringify(value)} in \"${styleProperty}\" — declaration dropped`,\n )\n return\n }\n declarations.push(`${cssProperty}: ${parsed};`)\n}\n\nconst formatFontFamily = (family: string): string => {\n const trimmed = family.trim()\n if (/^(\".*\"|'.*')$/.test(trimmed)) {\n return trimmed\n }\n return /^[A-Za-z][\\w-]*$/.test(trimmed)\n ? trimmed\n : `\"${trimmed.replace(/\"/g, '\\\\\"')}\"`\n}\n\n/**\n * Renders a VisualStyle as GTK CSS declarations (one per line, stable\n * order). Returns \"\" when the style produces no CSS (e.g. transform-only).\n */\nexport const visualStyleToCss = (visual: VisualStyle): string => {\n const decls: string[] = []\n\n if (visual.backgroundColor !== undefined) {\n pushColor(\n decls,\n \"background-color\",\n visual.backgroundColor,\n \"backgroundColor\",\n )\n }\n if (visual.opacity !== undefined) {\n decls.push(`opacity: ${clamp01(visual.opacity)};`)\n }\n\n // GTK defaults border-style to none — width alone renders nothing. An\n // explicit borderStyle wins (GTK4 CSS supports solid/dotted/dashed);\n // otherwise any positive border width turns the border on with \"solid\".\n const hasVisibleBorderWidth = [\n visual.borderWidth,\n visual.borderTopWidth,\n visual.borderRightWidth,\n visual.borderBottomWidth,\n visual.borderLeftWidth,\n ].some((width) => width !== undefined && width > 0)\n if (visual.borderStyle !== undefined) {\n decls.push(`border-style: ${visual.borderStyle};`)\n } else if (hasVisibleBorderWidth) {\n decls.push(\"border-style: solid;\")\n }\n // Shorthands first, then per-side overrides so the sides win.\n if (visual.borderWidth !== undefined) {\n decls.push(`border-width: ${px(visual.borderWidth)};`)\n }\n if (visual.borderTopWidth !== undefined) {\n decls.push(`border-top-width: ${px(visual.borderTopWidth)};`)\n }\n if (visual.borderRightWidth !== undefined) {\n decls.push(`border-right-width: ${px(visual.borderRightWidth)};`)\n }\n if (visual.borderBottomWidth !== undefined) {\n decls.push(`border-bottom-width: ${px(visual.borderBottomWidth)};`)\n }\n if (visual.borderLeftWidth !== undefined) {\n decls.push(`border-left-width: ${px(visual.borderLeftWidth)};`)\n }\n if (visual.borderColor !== undefined) {\n pushColor(decls, \"border-color\", visual.borderColor, \"borderColor\")\n }\n if (visual.borderTopColor !== undefined) {\n pushColor(\n decls,\n \"border-top-color\",\n visual.borderTopColor,\n \"borderTopColor\",\n )\n }\n if (visual.borderRightColor !== undefined) {\n pushColor(\n decls,\n \"border-right-color\",\n visual.borderRightColor,\n \"borderRightColor\",\n )\n }\n if (visual.borderBottomColor !== undefined) {\n pushColor(\n decls,\n \"border-bottom-color\",\n visual.borderBottomColor,\n \"borderBottomColor\",\n )\n }\n if (visual.borderLeftColor !== undefined) {\n pushColor(\n decls,\n \"border-left-color\",\n visual.borderLeftColor,\n \"borderLeftColor\",\n )\n }\n if (visual.borderRadius !== undefined) {\n decls.push(`border-radius: ${px(visual.borderRadius)};`)\n }\n // Per-corner radii come after the shorthand so they override it.\n if (visual.borderTopLeftRadius !== undefined) {\n decls.push(`border-top-left-radius: ${px(visual.borderTopLeftRadius)};`)\n }\n if (visual.borderTopRightRadius !== undefined) {\n decls.push(`border-top-right-radius: ${px(visual.borderTopRightRadius)};`)\n }\n if (visual.borderBottomRightRadius !== undefined) {\n decls.push(\n `border-bottom-right-radius: ${px(visual.borderBottomRightRadius)};`,\n )\n }\n if (visual.borderBottomLeftRadius !== undefined) {\n decls.push(\n `border-bottom-left-radius: ${px(visual.borderBottomLeftRadius)};`,\n )\n }\n\n if (visual.color !== undefined) {\n pushColor(decls, \"color\", visual.color, \"color\")\n }\n if (visual.fontFamily !== undefined) {\n decls.push(`font-family: ${formatFontFamily(visual.fontFamily)};`)\n }\n if (visual.fontSize !== undefined) {\n decls.push(`font-size: ${px(visual.fontSize)};`)\n }\n if (visual.fontStyle !== undefined) {\n decls.push(`font-style: ${visual.fontStyle};`)\n }\n if (visual.fontWeight !== undefined) {\n decls.push(`font-weight: ${visual.fontWeight};`)\n }\n if (visual.letterSpacing !== undefined) {\n decls.push(`letter-spacing: ${px(visual.letterSpacing)};`)\n }\n if (visual.lineHeight !== undefined) {\n decls.push(`line-height: ${px(visual.lineHeight)};`)\n }\n\n // visual.textAlign, visual.transform: intentionally not CSS (see header).\n\n return decls.join(\"\\n\")\n}\n"]}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export type Point = {
|
|
2
|
+
x: number;
|
|
3
|
+
y: number;
|
|
4
|
+
};
|
|
5
|
+
export declare const parsePoints: (points: string) => Point[];
|
|
6
|
+
export type RectShape = {
|
|
7
|
+
x: number;
|
|
8
|
+
y: number;
|
|
9
|
+
width: number;
|
|
10
|
+
height: number;
|
|
11
|
+
rx?: number;
|
|
12
|
+
ry?: number;
|
|
13
|
+
};
|
|
14
|
+
export declare const rectToPathData: ({ x, y, width, height, rx, ry, }: RectShape) => string;
|
|
15
|
+
export declare const ellipseToPathData: (cx: number, cy: number, rx: number, ry: number) => string;
|
|
16
|
+
export declare const circleToPathData: (cx: number, cy: number, r: number) => string;
|
|
17
|
+
export declare const lineToPathData: (x1: number, y1: number, x2: number, y2: number) => string;
|
|
18
|
+
export declare const polygonToPathData: (points: string | Point[]) => string;
|
|
19
|
+
export declare const polylineToPathData: (points: string | Point[]) => string;
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
// Pure shape → SVG path `d` string generators. No gtkx imports (unit-testable
|
|
2
|
+
// on any OS, like style/colors.ts) — the only consumer of the output is
|
|
3
|
+
// `Gsk.Path.parse()` in the bridge, so every generator here produces (a
|
|
4
|
+
// superset of) SVG path syntax and nothing else. This is the "no parser of
|
|
5
|
+
// our own" bet from the PRD: <Path> passes its `d` straight through, and
|
|
6
|
+
// every other shape is just a small amount of arithmetic away from the same
|
|
7
|
+
// syntax.
|
|
8
|
+
// Accepts both the comma-separated and whitespace-separated forms RN-svg
|
|
9
|
+
// tolerates ("0,0 10,5" and "0 0 10 5"), and odd trailing tokens are
|
|
10
|
+
// dropped rather than throwing — malformed `points` should render nothing
|
|
11
|
+
// useful, not crash the tree.
|
|
12
|
+
export const parsePoints = (points) => {
|
|
13
|
+
const numbers = points
|
|
14
|
+
.trim()
|
|
15
|
+
.split(/[\s,]+/)
|
|
16
|
+
.filter((token) => token.length > 0)
|
|
17
|
+
.map(Number)
|
|
18
|
+
.filter((value) => !Number.isNaN(value));
|
|
19
|
+
const result = [];
|
|
20
|
+
for (let i = 0; i + 1 < numbers.length; i += 2) {
|
|
21
|
+
result.push({ x: numbers[i], y: numbers[i + 1] });
|
|
22
|
+
}
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
25
|
+
// SVG's rx/ry resolution: either one fills in for a missing other, and both
|
|
26
|
+
// clamp to half the box so opposite corners never overlap.
|
|
27
|
+
const resolveCornerRadii = (width, height, rx, ry) => {
|
|
28
|
+
const rawRx = rx ?? ry ?? 0;
|
|
29
|
+
const rawRy = ry ?? rx ?? 0;
|
|
30
|
+
return [
|
|
31
|
+
Math.max(0, Math.min(rawRx, width / 2)),
|
|
32
|
+
Math.max(0, Math.min(rawRy, height / 2)),
|
|
33
|
+
];
|
|
34
|
+
};
|
|
35
|
+
export const rectToPathData = ({ x, y, width, height, rx, ry, }) => {
|
|
36
|
+
if (width <= 0 || height <= 0) {
|
|
37
|
+
return "";
|
|
38
|
+
}
|
|
39
|
+
const [radiusX, radiusY] = resolveCornerRadii(width, height, rx, ry);
|
|
40
|
+
if (radiusX <= 0 || radiusY <= 0) {
|
|
41
|
+
return `M ${x} ${y} H ${x + width} V ${y + height} H ${x} Z`;
|
|
42
|
+
}
|
|
43
|
+
const right = x + width;
|
|
44
|
+
const bottom = y + height;
|
|
45
|
+
return (`M ${x + radiusX} ${y} ` +
|
|
46
|
+
`H ${right - radiusX} ` +
|
|
47
|
+
`A ${radiusX} ${radiusY} 0 0 1 ${right} ${y + radiusY} ` +
|
|
48
|
+
`V ${bottom - radiusY} ` +
|
|
49
|
+
`A ${radiusX} ${radiusY} 0 0 1 ${right - radiusX} ${bottom} ` +
|
|
50
|
+
`H ${x + radiusX} ` +
|
|
51
|
+
`A ${radiusX} ${radiusY} 0 0 1 ${x} ${bottom - radiusY} ` +
|
|
52
|
+
`V ${y + radiusY} ` +
|
|
53
|
+
`A ${radiusX} ${radiusY} 0 0 1 ${x + radiusX} ${y} Z`);
|
|
54
|
+
};
|
|
55
|
+
// Two semicircle arcs — the standard trick for a full ellipse/circle in a
|
|
56
|
+
// path grammar that has no dedicated "ellipse" command.
|
|
57
|
+
export const ellipseToPathData = (cx, cy, rx, ry) => {
|
|
58
|
+
if (rx <= 0 || ry <= 0) {
|
|
59
|
+
return "";
|
|
60
|
+
}
|
|
61
|
+
return (`M ${cx - rx} ${cy} ` +
|
|
62
|
+
`A ${rx} ${ry} 0 1 0 ${cx + rx} ${cy} ` +
|
|
63
|
+
`A ${rx} ${ry} 0 1 0 ${cx - rx} ${cy} Z`);
|
|
64
|
+
};
|
|
65
|
+
export const circleToPathData = (cx, cy, r) => ellipseToPathData(cx, cy, r, r);
|
|
66
|
+
export const lineToPathData = (x1, y1, x2, y2) => `M ${x1} ${y1} L ${x2} ${y2}`;
|
|
67
|
+
const pointsToPathData = (points, close) => {
|
|
68
|
+
if (points.length === 0) {
|
|
69
|
+
return "";
|
|
70
|
+
}
|
|
71
|
+
const [first, ...rest] = points;
|
|
72
|
+
const segments = rest.map((p) => `L ${p.x} ${p.y}`).join(" ");
|
|
73
|
+
const body = segments.length > 0
|
|
74
|
+
? `M ${first.x} ${first.y} ${segments}`
|
|
75
|
+
: `M ${first.x} ${first.y}`;
|
|
76
|
+
return close ? `${body} Z` : body;
|
|
77
|
+
};
|
|
78
|
+
export const polygonToPathData = (points) => pointsToPathData(typeof points === "string" ? parsePoints(points) : points, true);
|
|
79
|
+
export const polylineToPathData = (points) => pointsToPathData(typeof points === "string" ? parsePoints(points) : points, false);
|
|
80
|
+
//# sourceMappingURL=geometry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"geometry.js","sourceRoot":"","sources":["../../src/svg/geometry.ts"],"names":[],"mappings":"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","sourcesContent":["// Pure shape → SVG path `d` string generators. No gtkx imports (unit-testable\n// on any OS, like style/colors.ts) — the only consumer of the output is\n// `Gsk.Path.parse()` in the bridge, so every generator here produces (a\n// superset of) SVG path syntax and nothing else. This is the \"no parser of\n// our own\" bet from the PRD: <Path> passes its `d` straight through, and\n// every other shape is just a small amount of arithmetic away from the same\n// syntax.\n\nexport type Point = { x: number; y: number }\n\n// Accepts both the comma-separated and whitespace-separated forms RN-svg\n// tolerates (\"0,0 10,5\" and \"0 0 10 5\"), and odd trailing tokens are\n// dropped rather than throwing — malformed `points` should render nothing\n// useful, not crash the tree.\nexport const parsePoints = (points: string): Point[] => {\n const numbers = points\n .trim()\n .split(/[\\s,]+/)\n .filter((token) => token.length > 0)\n .map(Number)\n .filter((value) => !Number.isNaN(value))\n const result: Point[] = []\n for (let i = 0; i + 1 < numbers.length; i += 2) {\n result.push({ x: numbers[i]!, y: numbers[i + 1]! })\n }\n return result\n}\n\nexport type RectShape = {\n x: number\n y: number\n width: number\n height: number\n rx?: number\n ry?: number\n}\n\n// SVG's rx/ry resolution: either one fills in for a missing other, and both\n// clamp to half the box so opposite corners never overlap.\nconst resolveCornerRadii = (\n width: number,\n height: number,\n rx: number | undefined,\n ry: number | undefined,\n): [number, number] => {\n const rawRx = rx ?? ry ?? 0\n const rawRy = ry ?? rx ?? 0\n return [\n Math.max(0, Math.min(rawRx, width / 2)),\n Math.max(0, Math.min(rawRy, height / 2)),\n ]\n}\n\nexport const rectToPathData = ({\n x,\n y,\n width,\n height,\n rx,\n ry,\n}: RectShape): string => {\n if (width <= 0 || height <= 0) {\n return \"\"\n }\n const [radiusX, radiusY] = resolveCornerRadii(width, height, rx, ry)\n if (radiusX <= 0 || radiusY <= 0) {\n return `M ${x} ${y} H ${x + width} V ${y + height} H ${x} Z`\n }\n const right = x + width\n const bottom = y + height\n return (\n `M ${x + radiusX} ${y} ` +\n `H ${right - radiusX} ` +\n `A ${radiusX} ${radiusY} 0 0 1 ${right} ${y + radiusY} ` +\n `V ${bottom - radiusY} ` +\n `A ${radiusX} ${radiusY} 0 0 1 ${right - radiusX} ${bottom} ` +\n `H ${x + radiusX} ` +\n `A ${radiusX} ${radiusY} 0 0 1 ${x} ${bottom - radiusY} ` +\n `V ${y + radiusY} ` +\n `A ${radiusX} ${radiusY} 0 0 1 ${x + radiusX} ${y} Z`\n )\n}\n\n// Two semicircle arcs — the standard trick for a full ellipse/circle in a\n// path grammar that has no dedicated \"ellipse\" command.\nexport const ellipseToPathData = (\n cx: number,\n cy: number,\n rx: number,\n ry: number,\n): string => {\n if (rx <= 0 || ry <= 0) {\n return \"\"\n }\n return (\n `M ${cx - rx} ${cy} ` +\n `A ${rx} ${ry} 0 1 0 ${cx + rx} ${cy} ` +\n `A ${rx} ${ry} 0 1 0 ${cx - rx} ${cy} Z`\n )\n}\n\nexport const circleToPathData = (cx: number, cy: number, r: number): string =>\n ellipseToPathData(cx, cy, r, r)\n\nexport const lineToPathData = (\n x1: number,\n y1: number,\n x2: number,\n y2: number,\n): string => `M ${x1} ${y1} L ${x2} ${y2}`\n\nconst pointsToPathData = (points: Point[], close: boolean): string => {\n if (points.length === 0) {\n return \"\"\n }\n const [first, ...rest] = points\n const segments = rest.map((p) => `L ${p.x} ${p.y}`).join(\" \")\n const body =\n segments.length > 0\n ? `M ${first!.x} ${first!.y} ${segments}`\n : `M ${first!.x} ${first!.y}`\n return close ? `${body} Z` : body\n}\n\nexport const polygonToPathData = (points: string | Point[]): string =>\n pointsToPathData(\n typeof points === \"string\" ? parsePoints(points) : points,\n true,\n )\n\nexport const polylineToPathData = (points: string | Point[]): string =>\n pointsToPathData(\n typeof points === \"string\" ? parsePoints(points) : points,\n false,\n )\n"]}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export type BoundingBox = {
|
|
2
|
+
x: number;
|
|
3
|
+
y: number;
|
|
4
|
+
width: number;
|
|
5
|
+
height: number;
|
|
6
|
+
};
|
|
7
|
+
export type GradientUnits = "objectBoundingBox" | "userSpaceOnUse";
|
|
8
|
+
export declare const resolveGradientPoint: (bounds: BoundingBox, fractionX: number, fractionY: number, units: GradientUnits) => {
|
|
9
|
+
x: number;
|
|
10
|
+
y: number;
|
|
11
|
+
};
|
|
12
|
+
export declare const resolveGradientRadius: (bounds: BoundingBox, r: number, units: GradientUnits) => {
|
|
13
|
+
hradius: number;
|
|
14
|
+
vradius: number;
|
|
15
|
+
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
// Pure objectBoundingBox / userSpaceOnUse coordinate math for gradients — no
|
|
2
|
+
// gtkx imports, so it stays testable even though the bridge's actual
|
|
3
|
+
// gradient PAINT is currently blocked by an upstream gtkx-rc2 native bug
|
|
4
|
+
// (see the WORKAROUND note by makeColorStop in gtkx/bridge/svg-node.ts).
|
|
5
|
+
// This is the part of gradient support that already works end to end today
|
|
6
|
+
// and will need no changes once that bug is fixed upstream.
|
|
7
|
+
export const resolveGradientPoint = (bounds, fractionX, fractionY, units) => units === "userSpaceOnUse"
|
|
8
|
+
? { x: fractionX, y: fractionY }
|
|
9
|
+
: {
|
|
10
|
+
x: bounds.x + fractionX * bounds.width,
|
|
11
|
+
y: bounds.y + fractionY * bounds.height,
|
|
12
|
+
};
|
|
13
|
+
// SVG only has one radial radius; objectBoundingBox stretches it into an
|
|
14
|
+
// ellipse matching the shape's own aspect ratio (what browsers do too).
|
|
15
|
+
export const resolveGradientRadius = (bounds, r, units) => units === "userSpaceOnUse"
|
|
16
|
+
? { hradius: r, vradius: r }
|
|
17
|
+
: { hradius: r * bounds.width, vradius: r * bounds.height };
|
|
18
|
+
//# sourceMappingURL=gradient-geometry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gradient-geometry.js","sourceRoot":"","sources":["../../src/svg/gradient-geometry.ts"],"names":[],"mappings":"AAAA,6EAA6E;AAC7E,qEAAqE;AACrE,yEAAyE;AACzE,yEAAyE;AACzE,2EAA2E;AAC3E,4DAA4D;AAU5D,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAClC,MAAmB,EACnB,SAAiB,EACjB,SAAiB,EACjB,KAAoB,EACM,EAAE,CAC5B,KAAK,KAAK,gBAAgB;IACxB,CAAC,CAAC,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,SAAS,EAAE;IAChC,CAAC,CAAC;QACE,CAAC,EAAE,MAAM,CAAC,CAAC,GAAG,SAAS,GAAG,MAAM,CAAC,KAAK;QACtC,CAAC,EAAE,MAAM,CAAC,CAAC,GAAG,SAAS,GAAG,MAAM,CAAC,MAAM;KACxC,CAAA;AAEP,yEAAyE;AACzE,wEAAwE;AACxE,MAAM,CAAC,MAAM,qBAAqB,GAAG,CACnC,MAAmB,EACnB,CAAS,EACT,KAAoB,EACkB,EAAE,CACxC,KAAK,KAAK,gBAAgB;IACxB,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE;IAC5B,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,GAAG,MAAM,CAAC,KAAK,EAAE,OAAO,EAAE,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,CAAA","sourcesContent":["// Pure objectBoundingBox / userSpaceOnUse coordinate math for gradients — no\n// gtkx imports, so it stays testable even though the bridge's actual\n// gradient PAINT is currently blocked by an upstream gtkx-rc2 native bug\n// (see the WORKAROUND note by makeColorStop in gtkx/bridge/svg-node.ts).\n// This is the part of gradient support that already works end to end today\n// and will need no changes once that bug is fixed upstream.\n\nexport type BoundingBox = {\n x: number\n y: number\n width: number\n height: number\n}\nexport type GradientUnits = \"objectBoundingBox\" | \"userSpaceOnUse\"\n\nexport const resolveGradientPoint = (\n bounds: BoundingBox,\n fractionX: number,\n fractionY: number,\n units: GradientUnits,\n): { x: number; y: number } =>\n units === \"userSpaceOnUse\"\n ? { x: fractionX, y: fractionY }\n : {\n x: bounds.x + fractionX * bounds.width,\n y: bounds.y + fractionY * bounds.height,\n }\n\n// SVG only has one radial radius; objectBoundingBox stretches it into an\n// ellipse matching the shape's own aspect ratio (what browsers do too).\nexport const resolveGradientRadius = (\n bounds: BoundingBox,\n r: number,\n units: GradientUnits,\n): { hradius: number; vradius: number } =>\n units === \"userSpaceOnUse\"\n ? { hradius: r, vradius: r }\n : { hradius: r * bounds.width, vradius: r * bounds.height }\n"]}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export type SvgTransformOp = {
|
|
2
|
+
type: "translate";
|
|
3
|
+
x: number;
|
|
4
|
+
y: number;
|
|
5
|
+
} | {
|
|
6
|
+
type: "scale";
|
|
7
|
+
x: number;
|
|
8
|
+
y: number;
|
|
9
|
+
} | {
|
|
10
|
+
type: "rotate";
|
|
11
|
+
angleDeg: number;
|
|
12
|
+
} | {
|
|
13
|
+
type: "matrix";
|
|
14
|
+
a: number;
|
|
15
|
+
b: number;
|
|
16
|
+
c: number;
|
|
17
|
+
d: number;
|
|
18
|
+
e: number;
|
|
19
|
+
f: number;
|
|
20
|
+
};
|
|
21
|
+
/** Parses an SVG transform-list string left to right into primitive ops. */
|
|
22
|
+
export declare const parseSvgTransform: (value: string | undefined) => SvgTransformOp[];
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
// Pure parser for the SVG `transform` attribute's function-list syntax
|
|
2
|
+
// (translate/scale/rotate/matrix — no gtkx imports, unit-testable anywhere).
|
|
3
|
+
// `skewX`/`skewY` are a deliberate cut (see epic.md): nothing in the shapes
|
|
4
|
+
// this API ships needs them, and Gsk's skew() takes degrees on both axes
|
|
5
|
+
// while SVG's skewX/skewY are single-axis, different enough to not be a
|
|
6
|
+
// one-line addition later if it turns out to matter.
|
|
7
|
+
//
|
|
8
|
+
// `rotate(angle, cx, cy)` is expanded here into translate/rotate/translate
|
|
9
|
+
// so the bridge only ever has to apply four primitive ops, never carry
|
|
10
|
+
// pivot-point bookkeeping of its own.
|
|
11
|
+
const CALL_PATTERN = /([a-zA-Z]+)\s*\(([^)]*)\)/g;
|
|
12
|
+
const parseArgs = (raw) => raw
|
|
13
|
+
.trim()
|
|
14
|
+
.split(/[\s,]+/)
|
|
15
|
+
.filter((token) => token.length > 0)
|
|
16
|
+
.map(Number);
|
|
17
|
+
const isAllFinite = (values) => values.every((value) => Number.isFinite(value));
|
|
18
|
+
const buildOps = (name, args) => {
|
|
19
|
+
switch (name) {
|
|
20
|
+
case "translate": {
|
|
21
|
+
if (args.length < 1 || args.length > 2 || !isAllFinite(args)) {
|
|
22
|
+
return [];
|
|
23
|
+
}
|
|
24
|
+
const [x, y = 0] = args;
|
|
25
|
+
return [{ type: "translate", x: x, y }];
|
|
26
|
+
}
|
|
27
|
+
case "scale": {
|
|
28
|
+
if (args.length < 1 || args.length > 2 || !isAllFinite(args)) {
|
|
29
|
+
return [];
|
|
30
|
+
}
|
|
31
|
+
const [x, y = x] = args;
|
|
32
|
+
return [{ type: "scale", x: x, y: y }];
|
|
33
|
+
}
|
|
34
|
+
case "rotate": {
|
|
35
|
+
if ((args.length !== 1 && args.length !== 3) || !isAllFinite(args)) {
|
|
36
|
+
return [];
|
|
37
|
+
}
|
|
38
|
+
const [angleDeg, cx, cy] = args;
|
|
39
|
+
if (cx === undefined || cy === undefined) {
|
|
40
|
+
return [{ type: "rotate", angleDeg: angleDeg }];
|
|
41
|
+
}
|
|
42
|
+
return [
|
|
43
|
+
{ type: "translate", x: cx, y: cy },
|
|
44
|
+
{ type: "rotate", angleDeg: angleDeg },
|
|
45
|
+
{ type: "translate", x: -cx, y: -cy },
|
|
46
|
+
];
|
|
47
|
+
}
|
|
48
|
+
case "matrix": {
|
|
49
|
+
if (args.length !== 6 || !isAllFinite(args)) {
|
|
50
|
+
return [];
|
|
51
|
+
}
|
|
52
|
+
const [a, b, c, d, e, f] = args;
|
|
53
|
+
return [{ type: "matrix", a: a, b: b, c: c, d: d, e: e, f: f }];
|
|
54
|
+
}
|
|
55
|
+
default:
|
|
56
|
+
// skewX/skewY (deliberate cut) and anything unrecognized: ignored,
|
|
57
|
+
// same leniency RN-svg shows toward transform-list garbage.
|
|
58
|
+
return [];
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
/** Parses an SVG transform-list string left to right into primitive ops. */
|
|
62
|
+
export const parseSvgTransform = (value) => {
|
|
63
|
+
if (!value) {
|
|
64
|
+
return [];
|
|
65
|
+
}
|
|
66
|
+
const ops = [];
|
|
67
|
+
for (const match of value.matchAll(CALL_PATTERN)) {
|
|
68
|
+
const name = match[1];
|
|
69
|
+
const args = parseArgs(match[2] ?? "");
|
|
70
|
+
ops.push(...buildOps(name, args));
|
|
71
|
+
}
|
|
72
|
+
return ops;
|
|
73
|
+
};
|
|
74
|
+
//# sourceMappingURL=transform.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transform.js","sourceRoot":"","sources":["../../src/svg/transform.ts"],"names":[],"mappings":"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","sourcesContent":["// Pure parser for the SVG `transform` attribute's function-list syntax\n// (translate/scale/rotate/matrix — no gtkx imports, unit-testable anywhere).\n// `skewX`/`skewY` are a deliberate cut (see epic.md): nothing in the shapes\n// this API ships needs them, and Gsk's skew() takes degrees on both axes\n// while SVG's skewX/skewY are single-axis, different enough to not be a\n// one-line addition later if it turns out to matter.\n//\n// `rotate(angle, cx, cy)` is expanded here into translate/rotate/translate\n// so the bridge only ever has to apply four primitive ops, never carry\n// pivot-point bookkeeping of its own.\n\nexport type SvgTransformOp =\n | { type: \"translate\"; x: number; y: number }\n | { type: \"scale\"; x: number; y: number }\n | { type: \"rotate\"; angleDeg: number }\n | {\n type: \"matrix\"\n a: number\n b: number\n c: number\n d: number\n e: number\n f: number\n }\n\nconst CALL_PATTERN = /([a-zA-Z]+)\\s*\\(([^)]*)\\)/g\n\nconst parseArgs = (raw: string): number[] =>\n raw\n .trim()\n .split(/[\\s,]+/)\n .filter((token) => token.length > 0)\n .map(Number)\n\nconst isAllFinite = (values: number[]): boolean =>\n values.every((value) => Number.isFinite(value))\n\nconst buildOps = (name: string, args: number[]): SvgTransformOp[] => {\n switch (name) {\n case \"translate\": {\n if (args.length < 1 || args.length > 2 || !isAllFinite(args)) {\n return []\n }\n const [x, y = 0] = args\n return [{ type: \"translate\", x: x!, y }]\n }\n case \"scale\": {\n if (args.length < 1 || args.length > 2 || !isAllFinite(args)) {\n return []\n }\n const [x, y = x] = args\n return [{ type: \"scale\", x: x!, y: y! }]\n }\n case \"rotate\": {\n if ((args.length !== 1 && args.length !== 3) || !isAllFinite(args)) {\n return []\n }\n const [angleDeg, cx, cy] = args\n if (cx === undefined || cy === undefined) {\n return [{ type: \"rotate\", angleDeg: angleDeg! }]\n }\n return [\n { type: \"translate\", x: cx, y: cy },\n { type: \"rotate\", angleDeg: angleDeg! },\n { type: \"translate\", x: -cx, y: -cy },\n ]\n }\n case \"matrix\": {\n if (args.length !== 6 || !isAllFinite(args)) {\n return []\n }\n const [a, b, c, d, e, f] = args\n return [{ type: \"matrix\", a: a!, b: b!, c: c!, d: d!, e: e!, f: f! }]\n }\n default:\n // skewX/skewY (deliberate cut) and anything unrecognized: ignored,\n // same leniency RN-svg shows toward transform-list garbage.\n return []\n }\n}\n\n/** Parses an SVG transform-list string left to right into primitive ops. */\nexport const parseSvgTransform = (\n value: string | undefined,\n): SvgTransformOp[] => {\n if (!value) {\n return []\n }\n const ops: SvgTransformOp[] = []\n for (const match of value.matchAll(CALL_PATTERN)) {\n const name = match[1]!\n const args = parseArgs(match[2] ?? \"\")\n ops.push(...buildOps(name, args))\n }\n return ops\n}\n"]}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export type ViewBox = {
|
|
2
|
+
minX: number;
|
|
3
|
+
minY: number;
|
|
4
|
+
width: number;
|
|
5
|
+
height: number;
|
|
6
|
+
};
|
|
7
|
+
export declare const parseViewBox: (value: string | undefined) => ViewBox | null;
|
|
8
|
+
export type AlignX = "min" | "mid" | "max";
|
|
9
|
+
export type AlignY = "min" | "mid" | "max";
|
|
10
|
+
export type MeetOrSlice = "meet" | "slice";
|
|
11
|
+
export type PreserveAspectRatio = {
|
|
12
|
+
align: "none" | {
|
|
13
|
+
x: AlignX;
|
|
14
|
+
y: AlignY;
|
|
15
|
+
};
|
|
16
|
+
meetOrSlice: MeetOrSlice;
|
|
17
|
+
};
|
|
18
|
+
export declare const DEFAULT_PRESERVE_ASPECT_RATIO: PreserveAspectRatio;
|
|
19
|
+
export declare const parsePreserveAspectRatio: (value: string | undefined) => PreserveAspectRatio;
|
|
20
|
+
export type ViewBoxTransform = {
|
|
21
|
+
scaleX: number;
|
|
22
|
+
scaleY: number;
|
|
23
|
+
translateX: number;
|
|
24
|
+
translateY: number;
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* Maps viewBox user-space coordinates onto a `viewportWidth` x
|
|
28
|
+
* `viewportHeight` box: `screen = user * scale + translate`. Mirrors the SVG
|
|
29
|
+
* algorithm exactly (see spec §7.11) — "none" stretches independently per
|
|
30
|
+
* axis, "meet" fits the whole viewBox inside the viewport (letterboxing),
|
|
31
|
+
* "slice" fills the viewport and overflows the viewBox (the bridge clips to
|
|
32
|
+
* the viewport bounds, same as a real SVG renderer).
|
|
33
|
+
*/
|
|
34
|
+
export declare const resolveViewBoxTransform: (viewBox: ViewBox | null, viewportWidth: number, viewportHeight: number, preserveAspectRatio?: PreserveAspectRatio) => ViewBoxTransform;
|