system-canvas 0.1.0 → 0.1.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/dist/actions.d.ts +50 -0
- package/dist/actions.d.ts.map +1 -0
- package/dist/actions.js +85 -0
- package/dist/actions.js.map +1 -0
- package/dist/canvas.d.ts +40 -1
- package/dist/canvas.d.ts.map +1 -1
- package/dist/canvas.js +180 -0
- package/dist/canvas.js.map +1 -1
- package/dist/index.d.ts +12 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +15 -3
- package/dist/index.js.map +1 -1
- package/dist/lanes.d.ts +50 -0
- package/dist/lanes.d.ts.map +1 -0
- package/dist/lanes.js +129 -0
- package/dist/lanes.js.map +1 -0
- package/dist/paths.d.ts +15 -0
- package/dist/paths.d.ts.map +1 -0
- package/dist/paths.js +41 -0
- package/dist/paths.js.map +1 -0
- package/dist/rendering/index.d.ts +2 -1
- package/dist/rendering/index.d.ts.map +1 -1
- package/dist/rendering/index.js +1 -1
- package/dist/rendering/index.js.map +1 -1
- package/dist/rendering/viewport-math.d.ts +28 -0
- package/dist/rendering/viewport-math.d.ts.map +1 -1
- package/dist/rendering/viewport-math.js +49 -0
- package/dist/rendering/viewport-math.js.map +1 -1
- package/dist/rollup.d.ts +23 -0
- package/dist/rollup.d.ts.map +1 -0
- package/dist/rollup.js +56 -0
- package/dist/rollup.js.map +1 -0
- package/dist/slots.d.ts +65 -0
- package/dist/slots.d.ts.map +1 -0
- package/dist/slots.js +367 -0
- package/dist/slots.js.map +1 -0
- package/dist/themes/blueprint.d.ts.map +1 -1
- package/dist/themes/blueprint.js +13 -1
- package/dist/themes/blueprint.js.map +1 -1
- package/dist/themes/dark.d.ts.map +1 -1
- package/dist/themes/dark.js +13 -1
- package/dist/themes/dark.js.map +1 -1
- package/dist/themes/index.d.ts +1 -0
- package/dist/themes/index.d.ts.map +1 -1
- package/dist/themes/index.js +1 -0
- package/dist/themes/index.js.map +1 -1
- package/dist/themes/light.d.ts.map +1 -1
- package/dist/themes/light.js +13 -1
- package/dist/themes/light.js.map +1 -1
- package/dist/themes/midnight.d.ts.map +1 -1
- package/dist/themes/midnight.js +13 -1
- package/dist/themes/midnight.js.map +1 -1
- package/dist/themes/resolve.d.ts.map +1 -1
- package/dist/themes/resolve.js +5 -0
- package/dist/themes/resolve.js.map +1 -1
- package/dist/themes/roadmap.d.ts +13 -0
- package/dist/themes/roadmap.d.ts.map +1 -0
- package/dist/themes/roadmap.js +264 -0
- package/dist/themes/roadmap.js.map +1 -0
- package/dist/themes/warm.d.ts.map +1 -1
- package/dist/themes/warm.js +13 -1
- package/dist/themes/warm.js.map +1 -1
- package/dist/types.d.ts +419 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +13 -3
package/dist/lanes.js
ADDED
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Find the lane (if any) that contains the given position along its axis.
|
|
3
|
+
*
|
|
4
|
+
* For `columns`, pass the node's x (or x + width/2). For `rows`, pass y.
|
|
5
|
+
* Returns the first matching lane, or null if the position sits outside
|
|
6
|
+
* every lane.
|
|
7
|
+
*/
|
|
8
|
+
export function findLaneAt(pos, lanes) {
|
|
9
|
+
if (!lanes || lanes.length === 0)
|
|
10
|
+
return null;
|
|
11
|
+
for (const lane of lanes) {
|
|
12
|
+
if (pos >= lane.start && pos < lane.start + lane.size)
|
|
13
|
+
return lane;
|
|
14
|
+
}
|
|
15
|
+
return null;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Snap a position to a lane boundary.
|
|
19
|
+
*
|
|
20
|
+
* `edge: 'start'` (default) snaps to the lane's start edge — useful for
|
|
21
|
+
* snapping a node's x/y when you want nodes to left/top-align in their lane.
|
|
22
|
+
* `edge: 'center'` snaps so an object of the given `size` is centered
|
|
23
|
+
* within the lane — useful when nodes sit visually centered in their row
|
|
24
|
+
* or column. Requires `size` to behave as expected; falls back to `start`
|
|
25
|
+
* behavior when `size` is 0.
|
|
26
|
+
* `edge: 'nearest'` snaps to whichever boundary (start, end, or any interior
|
|
27
|
+
* divider between two adjacent lanes) is closest — useful when the user
|
|
28
|
+
* drags across lane borders.
|
|
29
|
+
*
|
|
30
|
+
* If `size` is provided, the input is treated as the leading edge of an
|
|
31
|
+
* object of that size, and the snap target accounts for keeping the object
|
|
32
|
+
* fully inside a single lane when possible.
|
|
33
|
+
*
|
|
34
|
+
* Returns `pos` unchanged when `lanes` is empty or the position falls
|
|
35
|
+
* outside every lane (nothing meaningful to snap to).
|
|
36
|
+
*/
|
|
37
|
+
export function snapToLane(pos, lanes, options = {}) {
|
|
38
|
+
if (!lanes || lanes.length === 0)
|
|
39
|
+
return pos;
|
|
40
|
+
const edge = options.edge ?? 'start';
|
|
41
|
+
const size = options.size ?? 0;
|
|
42
|
+
if (edge === 'start' || edge === 'center') {
|
|
43
|
+
// Use the object's center for lane selection so a node doesn't flip
|
|
44
|
+
// into the neighbor lane just because its top/left edge crosses a
|
|
45
|
+
// divider. Prefer the lane whose body contains the center; fall back
|
|
46
|
+
// to nearest-start by center.
|
|
47
|
+
const center = pos + size / 2;
|
|
48
|
+
const containing = findLaneAt(center, lanes);
|
|
49
|
+
let target;
|
|
50
|
+
if (containing) {
|
|
51
|
+
target = containing;
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
let best = lanes[0];
|
|
55
|
+
let bestDist = Math.abs(center - (best.start + best.size / 2));
|
|
56
|
+
for (const lane of lanes) {
|
|
57
|
+
const d = Math.abs(center - (lane.start + lane.size / 2));
|
|
58
|
+
if (d < bestDist) {
|
|
59
|
+
best = lane;
|
|
60
|
+
bestDist = d;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
target = best;
|
|
64
|
+
}
|
|
65
|
+
if (edge === 'center') {
|
|
66
|
+
return target.start + (target.size - size) / 2;
|
|
67
|
+
}
|
|
68
|
+
return target.start;
|
|
69
|
+
}
|
|
70
|
+
// 'nearest': consider every lane boundary (start of each lane, plus end
|
|
71
|
+
// of the last lane) and pick the closest.
|
|
72
|
+
const boundaries = [];
|
|
73
|
+
for (const lane of lanes)
|
|
74
|
+
boundaries.push(lane.start);
|
|
75
|
+
const last = lanes[lanes.length - 1];
|
|
76
|
+
boundaries.push(last.start + last.size);
|
|
77
|
+
// When size is given, the object's right/bottom edge is `pos + size`.
|
|
78
|
+
// We want `pos` itself to snap to a boundary — simple nearest is fine.
|
|
79
|
+
let best = boundaries[0];
|
|
80
|
+
let bestDist = Math.abs(pos - best);
|
|
81
|
+
for (const b of boundaries) {
|
|
82
|
+
const d = Math.abs(pos - b);
|
|
83
|
+
if (d < bestDist) {
|
|
84
|
+
best = b;
|
|
85
|
+
bestDist = d;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
// If snapping would overflow the last lane given the object size, pull
|
|
89
|
+
// back to keep the object inside.
|
|
90
|
+
if (size > 0) {
|
|
91
|
+
const maxStart = last.start + last.size - size;
|
|
92
|
+
if (best > maxStart)
|
|
93
|
+
best = maxStart;
|
|
94
|
+
}
|
|
95
|
+
return best;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Convenience: produce a set of equally-sized lanes from an array of labels.
|
|
99
|
+
*
|
|
100
|
+
* Consumers that don't want to compute pixel positions can write
|
|
101
|
+
* `evenLanes(['Now', 'Next', 'Later'])` and get three 400-wide lanes
|
|
102
|
+
* starting at x = 0.
|
|
103
|
+
*/
|
|
104
|
+
export function evenLanes(labels, size = 400, start = 0) {
|
|
105
|
+
return labels.map((label, i) => ({
|
|
106
|
+
id: label,
|
|
107
|
+
label,
|
|
108
|
+
start: start + i * size,
|
|
109
|
+
size,
|
|
110
|
+
}));
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Total extent covered by a lane list (from first.start to last end).
|
|
114
|
+
* Returns 0 when the list is empty.
|
|
115
|
+
*/
|
|
116
|
+
export function lanesExtent(lanes) {
|
|
117
|
+
if (!lanes || lanes.length === 0)
|
|
118
|
+
return { start: 0, end: 0 };
|
|
119
|
+
let start = Infinity;
|
|
120
|
+
let end = -Infinity;
|
|
121
|
+
for (const lane of lanes) {
|
|
122
|
+
if (lane.start < start)
|
|
123
|
+
start = lane.start;
|
|
124
|
+
if (lane.start + lane.size > end)
|
|
125
|
+
end = lane.start + lane.size;
|
|
126
|
+
}
|
|
127
|
+
return { start, end };
|
|
128
|
+
}
|
|
129
|
+
//# sourceMappingURL=lanes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lanes.js","sourceRoot":"","sources":["../src/lanes.ts"],"names":[],"mappings":"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"}
|
package/dist/paths.d.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Minimal dot-path utilities used by the form editor to read/write nested
|
|
3
|
+
* fields like `customData.status`. Dependency-free; no array-index syntax.
|
|
4
|
+
*
|
|
5
|
+
* `getAtPath({customData: {status: 'done'}}, 'customData.status') === 'done'`
|
|
6
|
+
* `setAtPath({}, 'customData.status', 'done')` → `{customData: {status: 'done'}}`
|
|
7
|
+
*/
|
|
8
|
+
export declare function getAtPath(obj: unknown, path: string): unknown;
|
|
9
|
+
/**
|
|
10
|
+
* Set a value at the given dot-path, returning a new object with the value
|
|
11
|
+
* applied. Existing branches are shallow-cloned along the path; siblings
|
|
12
|
+
* are preserved by reference. Does not mutate `obj`.
|
|
13
|
+
*/
|
|
14
|
+
export declare function setAtPath<T extends Record<string, any>>(obj: T | undefined, path: string, value: unknown): T;
|
|
15
|
+
//# sourceMappingURL=paths.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"paths.d.ts","sourceRoot":"","sources":["../src/paths.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,wBAAgB,SAAS,CAAC,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAS7D;AAED;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EACrD,GAAG,EAAE,CAAC,GAAG,SAAS,EAClB,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,OAAO,GACb,CAAC,CAgBH"}
|
package/dist/paths.js
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Minimal dot-path utilities used by the form editor to read/write nested
|
|
3
|
+
* fields like `customData.status`. Dependency-free; no array-index syntax.
|
|
4
|
+
*
|
|
5
|
+
* `getAtPath({customData: {status: 'done'}}, 'customData.status') === 'done'`
|
|
6
|
+
* `setAtPath({}, 'customData.status', 'done')` → `{customData: {status: 'done'}}`
|
|
7
|
+
*/
|
|
8
|
+
export function getAtPath(obj, path) {
|
|
9
|
+
if (obj == null)
|
|
10
|
+
return undefined;
|
|
11
|
+
const parts = path.split('.');
|
|
12
|
+
let cur = obj;
|
|
13
|
+
for (const p of parts) {
|
|
14
|
+
if (cur == null || typeof cur !== 'object')
|
|
15
|
+
return undefined;
|
|
16
|
+
cur = cur[p];
|
|
17
|
+
}
|
|
18
|
+
return cur;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Set a value at the given dot-path, returning a new object with the value
|
|
22
|
+
* applied. Existing branches are shallow-cloned along the path; siblings
|
|
23
|
+
* are preserved by reference. Does not mutate `obj`.
|
|
24
|
+
*/
|
|
25
|
+
export function setAtPath(obj, path, value) {
|
|
26
|
+
const parts = path.split('.');
|
|
27
|
+
const root = obj ? { ...obj } : {};
|
|
28
|
+
let cur = root;
|
|
29
|
+
for (let i = 0; i < parts.length - 1; i++) {
|
|
30
|
+
const key = parts[i];
|
|
31
|
+
const next = cur[key];
|
|
32
|
+
const cloned = next != null && typeof next === 'object' && !Array.isArray(next)
|
|
33
|
+
? { ...next }
|
|
34
|
+
: {};
|
|
35
|
+
cur[key] = cloned;
|
|
36
|
+
cur = cloned;
|
|
37
|
+
}
|
|
38
|
+
cur[parts[parts.length - 1]] = value;
|
|
39
|
+
return root;
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=paths.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"paths.js","sourceRoot":"","sources":["../src/paths.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,MAAM,UAAU,SAAS,CAAC,GAAY,EAAE,IAAY;IAClD,IAAI,GAAG,IAAI,IAAI;QAAE,OAAO,SAAS,CAAA;IACjC,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;IAC7B,IAAI,GAAG,GAAQ,GAAG,CAAA;IAClB,KAAK,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC;QACtB,IAAI,GAAG,IAAI,IAAI,IAAI,OAAO,GAAG,KAAK,QAAQ;YAAE,OAAO,SAAS,CAAA;QAC5D,GAAG,GAAG,GAAG,CAAC,CAAC,CAAC,CAAA;IACd,CAAC;IACD,OAAO,GAAG,CAAA;AACZ,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,SAAS,CACvB,GAAkB,EAClB,IAAY,EACZ,KAAc;IAEd,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;IAC7B,MAAM,IAAI,GAAwB,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,CAAA;IACvD,IAAI,GAAG,GAAG,IAAI,CAAA;IACd,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;QAC1C,MAAM,GAAG,GAAG,KAAK,CAAC,CAAC,CAAC,CAAA;QACpB,MAAM,IAAI,GAAG,GAAG,CAAC,GAAG,CAAC,CAAA;QACrB,MAAM,MAAM,GACV,IAAI,IAAI,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC;YAC9D,CAAC,CAAC,EAAE,GAAG,IAAI,EAAE;YACb,CAAC,CAAC,EAAE,CAAA;QACR,GAAG,CAAC,GAAG,CAAC,GAAG,MAAM,CAAA;QACjB,GAAG,GAAG,MAAM,CAAA;IACd,CAAC;IACD,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,GAAG,KAAK,CAAA;IACpC,OAAO,IAAS,CAAA;AAClB,CAAC"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export { computeAnchorPoint, inferSide } from './anchor-points.js';
|
|
2
2
|
export { computeEdgePath, computeEdgeMidpoint } from './edge-routing.js';
|
|
3
|
-
export { computeBoundingBox, fitToBounds, screenToCanvas, canvasToScreen, } from './viewport-math.js';
|
|
3
|
+
export { computeBoundingBox, fitToBounds, fitBoundsIntoRect, canvasRectToScreenRect, screenToCanvas, canvasToScreen, } from './viewport-math.js';
|
|
4
|
+
export type { Rect } from './viewport-math.js';
|
|
4
5
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/rendering/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAA;AAClE,OAAO,EAAE,eAAe,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAA;AACxE,OAAO,EACL,kBAAkB,EAClB,WAAW,EACX,cAAc,EACd,cAAc,GACf,MAAM,oBAAoB,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/rendering/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAA;AAClE,OAAO,EAAE,eAAe,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAA;AACxE,OAAO,EACL,kBAAkB,EAClB,WAAW,EACX,iBAAiB,EACjB,sBAAsB,EACtB,cAAc,EACd,cAAc,GACf,MAAM,oBAAoB,CAAA;AAC3B,YAAY,EAAE,IAAI,EAAE,MAAM,oBAAoB,CAAA"}
|
package/dist/rendering/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
export { computeAnchorPoint, inferSide } from './anchor-points.js';
|
|
2
2
|
export { computeEdgePath, computeEdgeMidpoint } from './edge-routing.js';
|
|
3
|
-
export { computeBoundingBox, fitToBounds, screenToCanvas, canvasToScreen, } from './viewport-math.js';
|
|
3
|
+
export { computeBoundingBox, fitToBounds, fitBoundsIntoRect, canvasRectToScreenRect, screenToCanvas, canvasToScreen, } from './viewport-math.js';
|
|
4
4
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/rendering/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAA;AAClE,OAAO,EAAE,eAAe,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAA;AACxE,OAAO,EACL,kBAAkB,EAClB,WAAW,EACX,cAAc,EACd,cAAc,GACf,MAAM,oBAAoB,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/rendering/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAA;AAClE,OAAO,EAAE,eAAe,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAA;AACxE,OAAO,EACL,kBAAkB,EAClB,WAAW,EACX,iBAAiB,EACjB,sBAAsB,EACtB,cAAc,EACd,cAAc,GACf,MAAM,oBAAoB,CAAA"}
|
|
@@ -22,4 +22,32 @@ export declare function canvasToScreen(canvasX: number, canvasY: number, viewpor
|
|
|
22
22
|
x: number;
|
|
23
23
|
y: number;
|
|
24
24
|
};
|
|
25
|
+
/**
|
|
26
|
+
* Axis-aligned rectangle in either canvas-space or screen-space.
|
|
27
|
+
*/
|
|
28
|
+
export interface Rect {
|
|
29
|
+
x: number;
|
|
30
|
+
y: number;
|
|
31
|
+
width: number;
|
|
32
|
+
height: number;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Compute a viewport transform that places the given canvas-space bounds
|
|
36
|
+
* (`bounds`) so that they fit inside the given screen-space rect
|
|
37
|
+
* (`targetScreenRect`), preserving aspect ratio (contain). When the aspect
|
|
38
|
+
* ratios differ, the bounds are letterboxed (centered) on the shorter axis.
|
|
39
|
+
*
|
|
40
|
+
* `padding` (in screen pixels) insets the target rect on all sides before
|
|
41
|
+
* fitting, giving the content visual breathing room.
|
|
42
|
+
*
|
|
43
|
+
* This is the core "seamless handoff" math: given a parent node's on-screen
|
|
44
|
+
* rect and a child canvas's bounding box, it returns the new viewport
|
|
45
|
+
* transform that makes the child canvas appear exactly where the parent
|
|
46
|
+
* node was.
|
|
47
|
+
*/
|
|
48
|
+
export declare function fitBoundsIntoRect(bounds: BoundingBox, targetScreenRect: Rect, padding?: number): ViewportState;
|
|
49
|
+
/**
|
|
50
|
+
* Compute the on-screen rect of a canvas-space rect under the given viewport.
|
|
51
|
+
*/
|
|
52
|
+
export declare function canvasRectToScreenRect(canvasRect: Rect, viewport: ViewportState): Rect;
|
|
25
53
|
//# sourceMappingURL=viewport-math.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"viewport-math.d.ts","sourceRoot":"","sources":["../../src/rendering/viewport-math.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,YAAY,EACZ,WAAW,EACX,aAAa,EACd,MAAM,aAAa,CAAA;AAEpB;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,YAAY,EAAE,GAAG,WAAW,CAyBrE;AAED;;;GAGG;AACH,wBAAgB,WAAW,CACzB,KAAK,EAAE,YAAY,EAAE,EACrB,aAAa,EAAE,MAAM,EACrB,cAAc,EAAE,MAAM,EACtB,OAAO,GAAE,MAAW,GACnB,aAAa,CAsBf;AAED;;GAEG;AACH,wBAAgB,cAAc,CAC5B,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,aAAa,GACtB;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE,CAK1B;AAED;;GAEG;AACH,wBAAgB,cAAc,CAC5B,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,aAAa,GACtB;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE,CAK1B"}
|
|
1
|
+
{"version":3,"file":"viewport-math.d.ts","sourceRoot":"","sources":["../../src/rendering/viewport-math.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,YAAY,EACZ,WAAW,EACX,aAAa,EACd,MAAM,aAAa,CAAA;AAEpB;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,YAAY,EAAE,GAAG,WAAW,CAyBrE;AAED;;;GAGG;AACH,wBAAgB,WAAW,CACzB,KAAK,EAAE,YAAY,EAAE,EACrB,aAAa,EAAE,MAAM,EACrB,cAAc,EAAE,MAAM,EACtB,OAAO,GAAE,MAAW,GACnB,aAAa,CAsBf;AAED;;GAEG;AACH,wBAAgB,cAAc,CAC5B,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,aAAa,GACtB;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE,CAK1B;AAED;;GAEG;AACH,wBAAgB,cAAc,CAC5B,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,aAAa,GACtB;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE,CAK1B;AAED;;GAEG;AACH,MAAM,WAAW,IAAI;IACnB,CAAC,EAAE,MAAM,CAAA;IACT,CAAC,EAAE,MAAM,CAAA;IACT,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,MAAM,CAAA;CACf;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,WAAW,EACnB,gBAAgB,EAAE,IAAI,EACtB,OAAO,GAAE,MAAU,GAClB,aAAa,CA4Bf;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CACpC,UAAU,EAAE,IAAI,EAChB,QAAQ,EAAE,aAAa,GACtB,IAAI,CAON"}
|
|
@@ -63,4 +63,53 @@ export function canvasToScreen(canvasX, canvasY, viewport) {
|
|
|
63
63
|
y: canvasY * viewport.zoom + viewport.y,
|
|
64
64
|
};
|
|
65
65
|
}
|
|
66
|
+
/**
|
|
67
|
+
* Compute a viewport transform that places the given canvas-space bounds
|
|
68
|
+
* (`bounds`) so that they fit inside the given screen-space rect
|
|
69
|
+
* (`targetScreenRect`), preserving aspect ratio (contain). When the aspect
|
|
70
|
+
* ratios differ, the bounds are letterboxed (centered) on the shorter axis.
|
|
71
|
+
*
|
|
72
|
+
* `padding` (in screen pixels) insets the target rect on all sides before
|
|
73
|
+
* fitting, giving the content visual breathing room.
|
|
74
|
+
*
|
|
75
|
+
* This is the core "seamless handoff" math: given a parent node's on-screen
|
|
76
|
+
* rect and a child canvas's bounding box, it returns the new viewport
|
|
77
|
+
* transform that makes the child canvas appear exactly where the parent
|
|
78
|
+
* node was.
|
|
79
|
+
*/
|
|
80
|
+
export function fitBoundsIntoRect(bounds, targetScreenRect, padding = 0) {
|
|
81
|
+
if (bounds.width === 0 || bounds.height === 0) {
|
|
82
|
+
// Degenerate bounds — center the single point in the target rect at 1x.
|
|
83
|
+
return {
|
|
84
|
+
x: targetScreenRect.x + targetScreenRect.width / 2 - bounds.minX,
|
|
85
|
+
y: targetScreenRect.y + targetScreenRect.height / 2 - bounds.minY,
|
|
86
|
+
zoom: 1,
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
const availW = Math.max(1, targetScreenRect.width - padding * 2);
|
|
90
|
+
const availH = Math.max(1, targetScreenRect.height - padding * 2);
|
|
91
|
+
const scaleX = availW / bounds.width;
|
|
92
|
+
const scaleY = availH / bounds.height;
|
|
93
|
+
// Contain (fit-inside): use the smaller scale so both dimensions fit.
|
|
94
|
+
const zoom = Math.min(scaleX, scaleY);
|
|
95
|
+
// Center the scaled bounds inside the padded target rect.
|
|
96
|
+
const scaledW = bounds.width * zoom;
|
|
97
|
+
const scaledH = bounds.height * zoom;
|
|
98
|
+
const offsetX = padding + (availW - scaledW) / 2;
|
|
99
|
+
const offsetY = padding + (availH - scaledH) / 2;
|
|
100
|
+
const x = targetScreenRect.x + offsetX - bounds.minX * zoom;
|
|
101
|
+
const y = targetScreenRect.y + offsetY - bounds.minY * zoom;
|
|
102
|
+
return { x, y, zoom };
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Compute the on-screen rect of a canvas-space rect under the given viewport.
|
|
106
|
+
*/
|
|
107
|
+
export function canvasRectToScreenRect(canvasRect, viewport) {
|
|
108
|
+
return {
|
|
109
|
+
x: canvasRect.x * viewport.zoom + viewport.x,
|
|
110
|
+
y: canvasRect.y * viewport.zoom + viewport.y,
|
|
111
|
+
width: canvasRect.width * viewport.zoom,
|
|
112
|
+
height: canvasRect.height * viewport.zoom,
|
|
113
|
+
};
|
|
114
|
+
}
|
|
66
115
|
//# sourceMappingURL=viewport-math.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"viewport-math.js","sourceRoot":"","sources":["../../src/rendering/viewport-math.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"viewport-math.js","sourceRoot":"","sources":["../../src/rendering/viewport-math.ts"],"names":[],"mappings":"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"}
|
package/dist/rollup.d.ts
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { CanvasData, CanvasNode, RollupResult } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Count how many direct children of `canvas` match `predicate`, and return
|
|
4
|
+
* a `RollupResult` with `total`, `matched`, and `fraction`.
|
|
5
|
+
*
|
|
6
|
+
* Safe on `undefined` / empty canvases: returns `{ total: 0, matched: 0,
|
|
7
|
+
* fraction: 0 }`. This is the ergonomic fallback used by slot accessors
|
|
8
|
+
* when a node's sub-canvas hasn't resolved yet.
|
|
9
|
+
*
|
|
10
|
+
* Only counts direct children. For recursive counts through nested
|
|
11
|
+
* sub-canvases, use `rollupNodesDeep`.
|
|
12
|
+
*/
|
|
13
|
+
export declare function rollupNodes(canvas: CanvasData | undefined, predicate: (node: CanvasNode) => boolean): RollupResult;
|
|
14
|
+
/**
|
|
15
|
+
* Recursive version of `rollupNodes`. For each node that has a `ref`,
|
|
16
|
+
* looks up its sub-canvas via `getSubCanvas` and counts its descendants as
|
|
17
|
+
* well. Nodes with refs but unresolved sub-canvases still count as a single
|
|
18
|
+
* node (themselves) in `total`.
|
|
19
|
+
*
|
|
20
|
+
* Cycle-safe: each canvas ref is visited at most once per rollup.
|
|
21
|
+
*/
|
|
22
|
+
export declare function rollupNodesDeep(canvas: CanvasData | undefined, predicate: (node: CanvasNode) => boolean, getSubCanvas: (ref: string) => CanvasData | undefined): RollupResult;
|
|
23
|
+
//# sourceMappingURL=rollup.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rollup.d.ts","sourceRoot":"","sources":["../src/rollup.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAEtE;;;;;;;;;;GAUG;AACH,wBAAgB,WAAW,CACzB,MAAM,EAAE,UAAU,GAAG,SAAS,EAC9B,SAAS,EAAE,CAAC,IAAI,EAAE,UAAU,KAAK,OAAO,GACvC,YAAY,CASd;AAED;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAC7B,MAAM,EAAE,UAAU,GAAG,SAAS,EAC9B,SAAS,EAAE,CAAC,IAAI,EAAE,UAAU,KAAK,OAAO,EACxC,YAAY,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,UAAU,GAAG,SAAS,GACpD,YAAY,CAuBd"}
|
package/dist/rollup.js
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Count how many direct children of `canvas` match `predicate`, and return
|
|
3
|
+
* a `RollupResult` with `total`, `matched`, and `fraction`.
|
|
4
|
+
*
|
|
5
|
+
* Safe on `undefined` / empty canvases: returns `{ total: 0, matched: 0,
|
|
6
|
+
* fraction: 0 }`. This is the ergonomic fallback used by slot accessors
|
|
7
|
+
* when a node's sub-canvas hasn't resolved yet.
|
|
8
|
+
*
|
|
9
|
+
* Only counts direct children. For recursive counts through nested
|
|
10
|
+
* sub-canvases, use `rollupNodesDeep`.
|
|
11
|
+
*/
|
|
12
|
+
export function rollupNodes(canvas, predicate) {
|
|
13
|
+
const nodes = canvas?.nodes ?? [];
|
|
14
|
+
const total = nodes.length;
|
|
15
|
+
if (total === 0)
|
|
16
|
+
return { total: 0, matched: 0, fraction: 0 };
|
|
17
|
+
let matched = 0;
|
|
18
|
+
for (const n of nodes) {
|
|
19
|
+
if (predicate(n))
|
|
20
|
+
matched++;
|
|
21
|
+
}
|
|
22
|
+
return { total, matched, fraction: matched / total };
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Recursive version of `rollupNodes`. For each node that has a `ref`,
|
|
26
|
+
* looks up its sub-canvas via `getSubCanvas` and counts its descendants as
|
|
27
|
+
* well. Nodes with refs but unresolved sub-canvases still count as a single
|
|
28
|
+
* node (themselves) in `total`.
|
|
29
|
+
*
|
|
30
|
+
* Cycle-safe: each canvas ref is visited at most once per rollup.
|
|
31
|
+
*/
|
|
32
|
+
export function rollupNodesDeep(canvas, predicate, getSubCanvas) {
|
|
33
|
+
const visited = new Set();
|
|
34
|
+
let total = 0;
|
|
35
|
+
let matched = 0;
|
|
36
|
+
const walk = (c) => {
|
|
37
|
+
if (!c)
|
|
38
|
+
return;
|
|
39
|
+
for (const n of c.nodes ?? []) {
|
|
40
|
+
total++;
|
|
41
|
+
if (predicate(n))
|
|
42
|
+
matched++;
|
|
43
|
+
if (n.ref && !visited.has(n.ref)) {
|
|
44
|
+
visited.add(n.ref);
|
|
45
|
+
walk(getSubCanvas(n.ref));
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
walk(canvas);
|
|
50
|
+
return {
|
|
51
|
+
total,
|
|
52
|
+
matched,
|
|
53
|
+
fraction: total === 0 ? 0 : matched / total,
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=rollup.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rollup.js","sourceRoot":"","sources":["../src/rollup.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;GAUG;AACH,MAAM,UAAU,WAAW,CACzB,MAA8B,EAC9B,SAAwC;IAExC,MAAM,KAAK,GAAG,MAAM,EAAE,KAAK,IAAI,EAAE,CAAA;IACjC,MAAM,KAAK,GAAG,KAAK,CAAC,MAAM,CAAA;IAC1B,IAAI,KAAK,KAAK,CAAC;QAAE,OAAO,EAAE,KAAK,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,CAAA;IAC7D,IAAI,OAAO,GAAG,CAAC,CAAA;IACf,KAAK,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC;QACtB,IAAI,SAAS,CAAC,CAAC,CAAC;YAAE,OAAO,EAAE,CAAA;IAC7B,CAAC;IACD,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,OAAO,GAAG,KAAK,EAAE,CAAA;AACtD,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,eAAe,CAC7B,MAA8B,EAC9B,SAAwC,EACxC,YAAqD;IAErD,MAAM,OAAO,GAAG,IAAI,GAAG,EAAU,CAAA;IACjC,IAAI,KAAK,GAAG,CAAC,CAAA;IACb,IAAI,OAAO,GAAG,CAAC,CAAA;IAEf,MAAM,IAAI,GAAG,CAAC,CAAyB,EAAE,EAAE;QACzC,IAAI,CAAC,CAAC;YAAE,OAAM;QACd,KAAK,MAAM,CAAC,IAAI,CAAC,CAAC,KAAK,IAAI,EAAE,EAAE,CAAC;YAC9B,KAAK,EAAE,CAAA;YACP,IAAI,SAAS,CAAC,CAAC,CAAC;gBAAE,OAAO,EAAE,CAAA;YAC3B,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC;gBACjC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAA;gBAClB,IAAI,CAAC,YAAY,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAA;YAC3B,CAAC;QACH,CAAC;IACH,CAAC,CAAA;IAED,IAAI,CAAC,MAAM,CAAC,CAAA;IACZ,OAAO;QACL,KAAK;QACL,OAAO;QACP,QAAQ,EAAE,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,GAAG,KAAK;KAC5C,CAAA;AACH,CAAC"}
|
package/dist/slots.d.ts
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import type { CanvasTheme, CategorySlots, NodeAccessor, ResolvedNode, SlotContext, SlotPosition, SlotRect, SlotSpec } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Compute the region rect (in canvas-space) for every slot position on a
|
|
4
|
+
* resolved node. Pure geometry — no knowledge of which slots are actually
|
|
5
|
+
* in use. Callers index by `SlotPosition`.
|
|
6
|
+
*/
|
|
7
|
+
export declare function computeCategorySlotRegions(node: ResolvedNode, theme: CanvasTheme, slots?: CategorySlots): Record<SlotPosition, SlotRect>;
|
|
8
|
+
/**
|
|
9
|
+
* Resolve an accessor against a `SlotContext`. Function accessors are
|
|
10
|
+
* called; static values are returned as-is.
|
|
11
|
+
*/
|
|
12
|
+
export declare function resolveAccessor<T>(accessor: NodeAccessor<T>, ctx: SlotContext): T;
|
|
13
|
+
/**
|
|
14
|
+
* Resolve an accessor with a fallback for the function case. Useful when
|
|
15
|
+
* a slot renders a default from the theme but allows per-call overrides.
|
|
16
|
+
*/
|
|
17
|
+
export declare function resolveAccessorOr<T>(accessor: NodeAccessor<T> | undefined, fallback: T, ctx: SlotContext): T;
|
|
18
|
+
/**
|
|
19
|
+
* Look up the slots map for a node's category. Returns undefined when the
|
|
20
|
+
* node has no category, or the category has no slots declared.
|
|
21
|
+
*/
|
|
22
|
+
export declare function getCategorySlots(node: ResolvedNode, theme: CanvasTheme): CategorySlots | undefined;
|
|
23
|
+
type Corner = 'topLeft' | 'topRight' | 'bottomLeft' | 'bottomRight';
|
|
24
|
+
/**
|
|
25
|
+
* Pick a corner for the ref indicator given which slots are occupied.
|
|
26
|
+
*
|
|
27
|
+
* If the default corner is free, it wins. Otherwise we try the diagonally
|
|
28
|
+
* opposite corner, then the remaining two corners in clockwise order from
|
|
29
|
+
* the default. If all four corners are occupied we keep the default
|
|
30
|
+
* position — the caller can decide whether to log a dev warning.
|
|
31
|
+
*
|
|
32
|
+
* Corners occupied by `header` or `footer` count as occupied on *both*
|
|
33
|
+
* corners of that row: a header in the top strip blocks both `topLeft` and
|
|
34
|
+
* `topRight` (the indicator would overlap the header text strip anyway).
|
|
35
|
+
*/
|
|
36
|
+
export declare function pickRefIndicatorCorner(defaultCorner: Corner, slots: CategorySlots | undefined): Corner;
|
|
37
|
+
/**
|
|
38
|
+
* Iterate over a slots map in a deterministic order. Returns entries as
|
|
39
|
+
* `[position, spec]` pairs. Order matches the `SlotPosition` definition —
|
|
40
|
+
* stable across runs so SVG paint order is stable.
|
|
41
|
+
*/
|
|
42
|
+
export declare function slotEntries(slots: CategorySlots): Array<[SlotPosition, SlotSpec]>;
|
|
43
|
+
export interface ReflowReservations {
|
|
44
|
+
top: number;
|
|
45
|
+
bottom: number;
|
|
46
|
+
left: number;
|
|
47
|
+
right: number;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Compute how many pixels of the node's content box must be reserved on
|
|
51
|
+
* each side for library-owned slots so node text/icons can be reflowed
|
|
52
|
+
* without colliding with slots.
|
|
53
|
+
*
|
|
54
|
+
* - `header` → shift text down by header region height + inset
|
|
55
|
+
* - `footer` → shrink text vertical extent by footer region height + inset
|
|
56
|
+
* - `leftEdge` / `rightEdge` → inset text by the edge strip width
|
|
57
|
+
*
|
|
58
|
+
* Top/bottom edges and corner slots don't reflow text (they're thin strips
|
|
59
|
+
* or small badges that sit over the existing layout). If the node is so
|
|
60
|
+
* short that reservations would leave less than `fontSize` vertical space
|
|
61
|
+
* for text, the reflow is skipped (text stays centered).
|
|
62
|
+
*/
|
|
63
|
+
export declare function computeReflowReservations(node: ResolvedNode, theme: CanvasTheme, slots: CategorySlots | undefined): ReflowReservations;
|
|
64
|
+
export {};
|
|
65
|
+
//# sourceMappingURL=slots.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"slots.d.ts","sourceRoot":"","sources":["../src/slots.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,WAAW,EAEX,aAAa,EACb,YAAY,EACZ,YAAY,EACZ,WAAW,EACX,YAAY,EACZ,QAAQ,EACR,QAAQ,EACT,MAAM,YAAY,CAAA;AAmCnB;;;;GAIG;AACH,wBAAgB,0BAA0B,CACxC,IAAI,EAAE,YAAY,EAClB,KAAK,EAAE,WAAW,EAClB,KAAK,CAAC,EAAE,aAAa,GACpB,MAAM,CAAC,YAAY,EAAE,QAAQ,CAAC,CAmHhC;AAsCD;;;GAGG;AACH,wBAAgB,eAAe,CAAC,CAAC,EAAE,QAAQ,EAAE,YAAY,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,WAAW,GAAG,CAAC,CAEjF;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,EACjC,QAAQ,EAAE,YAAY,CAAC,CAAC,CAAC,GAAG,SAAS,EACrC,QAAQ,EAAE,CAAC,EACX,GAAG,EAAE,WAAW,GACf,CAAC,CAGH;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,YAAY,EAClB,KAAK,EAAE,WAAW,GACjB,aAAa,GAAG,SAAS,CAI3B;AAMD,KAAK,MAAM,GAAG,SAAS,GAAG,UAAU,GAAG,YAAY,GAAG,aAAa,CAAA;AAEnE;;;;;;;;;;;GAWG;AACH,wBAAgB,sBAAsB,CACpC,aAAa,EAAE,MAAM,EACrB,KAAK,EAAE,aAAa,GAAG,SAAS,GAC/B,MAAM,CAyBR;AAMD;;;;GAIG;AACH,wBAAgB,WAAW,CACzB,KAAK,EAAE,aAAa,GACnB,KAAK,CAAC,CAAC,YAAY,EAAE,QAAQ,CAAC,CAAC,CAwBjC;AAMD,MAAM,WAAW,kBAAkB;IACjC,GAAG,EAAE,MAAM,CAAA;IACX,MAAM,EAAE,MAAM,CAAA;IACd,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,MAAM,CAAA;CACd;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,yBAAyB,CACvC,IAAI,EAAE,YAAY,EAClB,KAAK,EAAE,WAAW,EAClB,KAAK,EAAE,aAAa,GAAG,SAAS,GAC/B,kBAAkB,CAEpB"}
|