bireactive 0.2.0
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/LICENSE +21 -0
- package/README.md +81 -0
- package/dist/animation/anim.d.ts +57 -0
- package/dist/animation/anim.js +318 -0
- package/dist/animation/combinators.d.ts +39 -0
- package/dist/animation/combinators.js +113 -0
- package/dist/animation/easings.d.ts +5 -0
- package/dist/animation/easings.js +5 -0
- package/dist/animation/index.d.ts +3 -0
- package/dist/animation/index.js +3 -0
- package/dist/assert/algebra.d.ts +20 -0
- package/dist/assert/algebra.js +79 -0
- package/dist/assert/claim.d.ts +40 -0
- package/dist/assert/claim.js +129 -0
- package/dist/assert/index.d.ts +7 -0
- package/dist/assert/index.js +19 -0
- package/dist/assert/predicates.d.ts +18 -0
- package/dist/assert/predicates.js +43 -0
- package/dist/assert/record.d.ts +20 -0
- package/dist/assert/record.js +78 -0
- package/dist/assert/scope.d.ts +42 -0
- package/dist/assert/scope.js +233 -0
- package/dist/assert/span.d.ts +37 -0
- package/dist/assert/span.js +68 -0
- package/dist/assert/tree.d.ts +22 -0
- package/dist/assert/tree.js +65 -0
- package/dist/code/code.d.ts +70 -0
- package/dist/code/code.js +361 -0
- package/dist/code/index.d.ts +2 -0
- package/dist/code/index.js +9 -0
- package/dist/code/morph.d.ts +5 -0
- package/dist/code/morph.js +194 -0
- package/dist/code/tokenize.d.ts +8 -0
- package/dist/code/tokenize.js +51 -0
- package/dist/constraints/cluster.d.ts +83 -0
- package/dist/constraints/cluster.js +213 -0
- package/dist/constraints/drivers.d.ts +15 -0
- package/dist/constraints/drivers.js +40 -0
- package/dist/constraints/factories.d.ts +73 -0
- package/dist/constraints/factories.js +248 -0
- package/dist/constraints/index.d.ts +11 -0
- package/dist/constraints/index.js +39 -0
- package/dist/constraints/interaction.d.ts +21 -0
- package/dist/constraints/interaction.js +148 -0
- package/dist/constraints/linalg.d.ts +18 -0
- package/dist/constraints/linalg.js +141 -0
- package/dist/constraints/phases.d.ts +21 -0
- package/dist/constraints/phases.js +60 -0
- package/dist/constraints/physics.d.ts +34 -0
- package/dist/constraints/physics.js +128 -0
- package/dist/constraints/rigid.d.ts +210 -0
- package/dist/constraints/rigid.js +835 -0
- package/dist/constraints/solver.d.ts +107 -0
- package/dist/constraints/solver.js +510 -0
- package/dist/constraints/term.d.ts +50 -0
- package/dist/constraints/term.js +80 -0
- package/dist/constraints/terms.d.ts +80 -0
- package/dist/constraints/terms.js +302 -0
- package/dist/constraints/world.d.ts +31 -0
- package/dist/constraints/world.js +245 -0
- package/dist/core/aggregates.d.ts +64 -0
- package/dist/core/aggregates.js +198 -0
- package/dist/core/anim.d.ts +84 -0
- package/dist/core/anim.js +301 -0
- package/dist/core/index.d.ts +38 -0
- package/dist/core/index.js +38 -0
- package/dist/core/introspect.d.ts +5 -0
- package/dist/core/introspect.js +31 -0
- package/dist/core/lenses/closed-form-policies.d.ts +64 -0
- package/dist/core/lenses/closed-form-policies.js +452 -0
- package/dist/core/lenses/domain-aggregates.d.ts +54 -0
- package/dist/core/lenses/domain-aggregates.js +259 -0
- package/dist/core/lenses/factor-lens.d.ts +42 -0
- package/dist/core/lenses/factor-lens.js +419 -0
- package/dist/core/lenses/index.d.ts +5 -0
- package/dist/core/lenses/index.js +16 -0
- package/dist/core/lenses/memory.d.ts +47 -0
- package/dist/core/lenses/memory.js +102 -0
- package/dist/core/lenses/typed-factor.d.ts +45 -0
- package/dist/core/lenses/typed-factor.js +376 -0
- package/dist/core/network-utils.d.ts +14 -0
- package/dist/core/network-utils.js +62 -0
- package/dist/core/new-primitives.d.ts +33 -0
- package/dist/core/new-primitives.js +113 -0
- package/dist/core/signal.d.ts +254 -0
- package/dist/core/signal.js +1349 -0
- package/dist/core/traits.d.ts +61 -0
- package/dist/core/traits.js +56 -0
- package/dist/core/tree.d.ts +23 -0
- package/dist/core/tree.js +62 -0
- package/dist/core/values/anchor.d.ts +23 -0
- package/dist/core/values/anchor.js +23 -0
- package/dist/core/values/audio.d.ts +33 -0
- package/dist/core/values/audio.js +107 -0
- package/dist/core/values/bool.d.ts +37 -0
- package/dist/core/values/bool.js +75 -0
- package/dist/core/values/box.d.ts +77 -0
- package/dist/core/values/box.js +211 -0
- package/dist/core/values/canvas.d.ts +71 -0
- package/dist/core/values/canvas.js +495 -0
- package/dist/core/values/color.d.ts +49 -0
- package/dist/core/values/color.js +106 -0
- package/dist/core/values/flags.d.ts +18 -0
- package/dist/core/values/flags.js +50 -0
- package/dist/core/values/gpu.d.ts +74 -0
- package/dist/core/values/gpu.js +426 -0
- package/dist/core/values/matrix.d.ts +53 -0
- package/dist/core/values/matrix.js +140 -0
- package/dist/core/values/num.d.ts +62 -0
- package/dist/core/values/num.js +166 -0
- package/dist/core/values/pose.d.ts +31 -0
- package/dist/core/values/pose.js +83 -0
- package/dist/core/values/range.d.ts +83 -0
- package/dist/core/values/range.js +167 -0
- package/dist/core/values/str.d.ts +76 -0
- package/dist/core/values/str.js +346 -0
- package/dist/core/values/template.d.ts +49 -0
- package/dist/core/values/template.js +148 -0
- package/dist/core/values/transform.d.ts +49 -0
- package/dist/core/values/transform.js +115 -0
- package/dist/core/values/tri.d.ts +31 -0
- package/dist/core/values/tri.js +95 -0
- package/dist/core/values/vec.d.ts +72 -0
- package/dist/core/values/vec.js +219 -0
- package/dist/core/writable.d.ts +15 -0
- package/dist/core/writable.js +29 -0
- package/dist/ext/events.d.ts +10 -0
- package/dist/ext/events.js +31 -0
- package/dist/ext/index.d.ts +4 -0
- package/dist/ext/index.js +4 -0
- package/dist/ext/snapshot.d.ts +8 -0
- package/dist/ext/snapshot.js +29 -0
- package/dist/ext/timeline.d.ts +56 -0
- package/dist/ext/timeline.js +94 -0
- package/dist/ext/waapi.d.ts +25 -0
- package/dist/ext/waapi.js +198 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +10 -0
- package/dist/propagators/index.d.ts +6 -0
- package/dist/propagators/index.js +6 -0
- package/dist/propagators/layout.d.ts +68 -0
- package/dist/propagators/layout.js +336 -0
- package/dist/propagators/network.d.ts +52 -0
- package/dist/propagators/network.js +185 -0
- package/dist/propagators/propagator.d.ts +12 -0
- package/dist/propagators/propagator.js +16 -0
- package/dist/propagators/range.d.ts +45 -0
- package/dist/propagators/range.js +147 -0
- package/dist/propagators/relations.d.ts +60 -0
- package/dist/propagators/relations.js +343 -0
- package/dist/shapes/annular-sector.d.ts +15 -0
- package/dist/shapes/annular-sector.js +64 -0
- package/dist/shapes/button.d.ts +14 -0
- package/dist/shapes/button.js +31 -0
- package/dist/shapes/choreographers.d.ts +22 -0
- package/dist/shapes/choreographers.js +69 -0
- package/dist/shapes/circle.d.ts +17 -0
- package/dist/shapes/circle.js +57 -0
- package/dist/shapes/clip.d.ts +5 -0
- package/dist/shapes/clip.js +31 -0
- package/dist/shapes/connect.d.ts +16 -0
- package/dist/shapes/connect.js +70 -0
- package/dist/shapes/curve.d.ts +60 -0
- package/dist/shapes/curve.js +285 -0
- package/dist/shapes/dashed.d.ts +16 -0
- package/dist/shapes/dashed.js +142 -0
- package/dist/shapes/debug.d.ts +43 -0
- package/dist/shapes/debug.js +97 -0
- package/dist/shapes/group.d.ts +5 -0
- package/dist/shapes/group.js +10 -0
- package/dist/shapes/handle.d.ts +32 -0
- package/dist/shapes/handle.js +88 -0
- package/dist/shapes/index.d.ts +23 -0
- package/dist/shapes/index.js +23 -0
- package/dist/shapes/interaction.d.ts +32 -0
- package/dist/shapes/interaction.js +187 -0
- package/dist/shapes/label.d.ts +20 -0
- package/dist/shapes/label.js +42 -0
- package/dist/shapes/layout.d.ts +29 -0
- package/dist/shapes/layout.js +74 -0
- package/dist/shapes/line.d.ts +21 -0
- package/dist/shapes/line.js +79 -0
- package/dist/shapes/list.d.ts +18 -0
- package/dist/shapes/list.js +51 -0
- package/dist/shapes/mount.d.ts +7 -0
- package/dist/shapes/mount.js +10 -0
- package/dist/shapes/path.d.ts +77 -0
- package/dist/shapes/path.js +227 -0
- package/dist/shapes/rect.d.ts +30 -0
- package/dist/shapes/rect.js +131 -0
- package/dist/shapes/shape.d.ts +132 -0
- package/dist/shapes/shape.js +306 -0
- package/dist/shapes/text.d.ts +24 -0
- package/dist/shapes/text.js +53 -0
- package/dist/shapes/tokens.d.ts +28 -0
- package/dist/shapes/tokens.js +27 -0
- package/dist/shapes/transitions.d.ts +23 -0
- package/dist/shapes/transitions.js +62 -0
- package/dist/tex/decorations.d.ts +26 -0
- package/dist/tex/decorations.js +116 -0
- package/dist/tex/index.d.ts +5 -0
- package/dist/tex/index.js +5 -0
- package/dist/tex/marker.d.ts +17 -0
- package/dist/tex/marker.js +63 -0
- package/dist/tex/motion.d.ts +43 -0
- package/dist/tex/motion.js +290 -0
- package/dist/tex/parts.d.ts +65 -0
- package/dist/tex/parts.js +149 -0
- package/dist/tex/tex.d.ts +45 -0
- package/dist/tex/tex.js +244 -0
- package/dist/web/attr.d.ts +16 -0
- package/dist/web/attr.js +98 -0
- package/dist/web/diagram.d.ts +49 -0
- package/dist/web/diagram.js +260 -0
- package/dist/web/index.d.ts +6 -0
- package/dist/web/index.js +6 -0
- package/dist/web/md-marker.d.ts +6 -0
- package/dist/web/md-marker.js +39 -0
- package/dist/web/md-tex.d.ts +6 -0
- package/dist/web/md-tex.js +61 -0
- package/dist/web/raf.d.ts +6 -0
- package/dist/web/raf.js +24 -0
- package/dist/web/viewport.d.ts +7 -0
- package/dist/web/viewport.js +13 -0
- package/package.json +87 -0
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
// debug.* — read-only diagnostic shapes that visualize layout state.
|
|
2
|
+
import { Box, derive, transformBox, transformPoint, Vec } from "../core/index.js";
|
|
3
|
+
import { circle } from "./circle.js";
|
|
4
|
+
import { group } from "./group.js";
|
|
5
|
+
import { label } from "./label.js";
|
|
6
|
+
import { line } from "./line.js";
|
|
7
|
+
import { rect } from "./rect.js";
|
|
8
|
+
import { Shape } from "./shape.js";
|
|
9
|
+
const COLOR = "var(--bireactive-debug, #c026d3)";
|
|
10
|
+
const baseOpts = { aside: true, opacity: 0.6 };
|
|
11
|
+
const outlineOpts = {
|
|
12
|
+
stroke: COLOR,
|
|
13
|
+
fill: "none",
|
|
14
|
+
thin: true,
|
|
15
|
+
dashed: true,
|
|
16
|
+
...baseOpts,
|
|
17
|
+
};
|
|
18
|
+
/** Parent-frame Box. Shapes have their transform applied (so the
|
|
19
|
+
* Box reflects the visual footprint); raw Boxes pass through. */
|
|
20
|
+
function parentBox(b) {
|
|
21
|
+
if (b instanceof Shape) {
|
|
22
|
+
return Box.derive(() => transformBox(b.localFrame.value, b.box.value));
|
|
23
|
+
}
|
|
24
|
+
return b;
|
|
25
|
+
}
|
|
26
|
+
/** Dashed rect over a Shape's parent-frame box (or a raw Box). */
|
|
27
|
+
const boxOutline = (b) => rect(parentBox(b), outlineOpts);
|
|
28
|
+
/** Small filled dot at a point or a Box's / Shape's center. */
|
|
29
|
+
const dot = (p, r = 2.5) => {
|
|
30
|
+
const at = p instanceof Vec ? p : p instanceof Shape ? p.center : p.center;
|
|
31
|
+
return circle(at, r, { fill: COLOR, stroke: "none", ...baseOpts });
|
|
32
|
+
};
|
|
33
|
+
/** Crosshair at a Shape's rotate/scale pivot, in parent frame. */
|
|
34
|
+
const origin = (s, size = 8) => {
|
|
35
|
+
const pivot = Vec.derive(() => transformPoint(s.localFrame.value, s.origin.value));
|
|
36
|
+
const half = size / 2;
|
|
37
|
+
return group({ aside: true, opacity: 0.75 }, line(pivot.left(half), pivot.right(half), { stroke: COLOR, thin: true }), line(pivot.up(half), pivot.down(half), { stroke: COLOR, thin: true }), circle(pivot, 1.5, { fill: COLOR, stroke: "none" }));
|
|
38
|
+
};
|
|
39
|
+
/** Dots at the 9 standard anchor positions: corners, edge midpoints,
|
|
40
|
+
* center. */
|
|
41
|
+
const anchors = (b, r = 2.5) => {
|
|
42
|
+
const g = group({ aside: true, opacity: 0.7 });
|
|
43
|
+
for (const u of [0, 0.5, 1]) {
|
|
44
|
+
for (const v of [0, 0.5, 1]) {
|
|
45
|
+
g.add(circle(b.at(u, v), r, {
|
|
46
|
+
fill: COLOR,
|
|
47
|
+
stroke: "none",
|
|
48
|
+
}));
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return g;
|
|
52
|
+
};
|
|
53
|
+
/** Faint dashed line between two shapes' (or points') centers. */
|
|
54
|
+
const connect = (a, b) => {
|
|
55
|
+
const aP = a instanceof Shape ? a.center : a;
|
|
56
|
+
const bP = b instanceof Shape ? b.center : b;
|
|
57
|
+
return line(aP, bP, {
|
|
58
|
+
stroke: COLOR,
|
|
59
|
+
thin: true,
|
|
60
|
+
dashed: true,
|
|
61
|
+
...baseOpts,
|
|
62
|
+
});
|
|
63
|
+
};
|
|
64
|
+
/** `connect(a, b)` + a live distance label at the midpoint. */
|
|
65
|
+
const distance = (a, b) => {
|
|
66
|
+
const aP = a instanceof Shape ? a.center : a;
|
|
67
|
+
const bP = b instanceof Shape ? b.center : b;
|
|
68
|
+
const mid = aP.lerp(bP, 0.5);
|
|
69
|
+
const d = aP.distance(bP);
|
|
70
|
+
return group({ aside: true }, connect(aP, bP), label(mid.up(6), derive(() => d.value.toFixed(0)), { size: 10, opacity: 0.85 }));
|
|
71
|
+
};
|
|
72
|
+
/** Markers + tiny tangent ticks at evenly-spaced t along a Path. */
|
|
73
|
+
const path = (p, ticks = 5) => {
|
|
74
|
+
const g = group({ aside: true, opacity: 0.75 });
|
|
75
|
+
for (let i = 0; i < ticks; i++) {
|
|
76
|
+
const t = ticks === 1 ? 0 : i / (ticks - 1);
|
|
77
|
+
const head = p.pointAt(t);
|
|
78
|
+
const tan = p.tangentAt(t);
|
|
79
|
+
const tip = Vec.derive(() => ({
|
|
80
|
+
x: head.value.x + tan.value.x * 6,
|
|
81
|
+
y: head.value.y + tan.value.y * 6,
|
|
82
|
+
}));
|
|
83
|
+
g.add(circle(head, 2.5, { fill: COLOR, stroke: "none" }), line(head, tip, { stroke: COLOR, thin: true }));
|
|
84
|
+
}
|
|
85
|
+
return g;
|
|
86
|
+
};
|
|
87
|
+
/** debug.* — diagnostic overlays. All are `aside: true` so they don't
|
|
88
|
+
* infect autofit. Drop in during development, remove when done. */
|
|
89
|
+
export const debug = {
|
|
90
|
+
box: boxOutline,
|
|
91
|
+
dot,
|
|
92
|
+
origin,
|
|
93
|
+
anchors,
|
|
94
|
+
connect,
|
|
95
|
+
distance,
|
|
96
|
+
path,
|
|
97
|
+
};
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { type AnyShape, Shape, type ShapeOpts } from "./shape.js";
|
|
2
|
+
/** Empty container — bundles children under one transform / opacity.
|
|
3
|
+
* Pass children after `opts` for JSX-ish nesting:
|
|
4
|
+
* `group({ translate }, rect(...), label(...))`. */
|
|
5
|
+
export declare function group<const O extends ShapeOpts>(opts?: O, ...children: AnyShape[]): Shape<O>;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Shape } from "./shape.js";
|
|
2
|
+
/** Empty container — bundles children under one transform / opacity.
|
|
3
|
+
* Pass children after `opts` for JSX-ish nesting:
|
|
4
|
+
* `group({ translate }, rect(...), label(...))`. */
|
|
5
|
+
export function group(opts, ...children) {
|
|
6
|
+
const g = new Shape(undefined, undefined, opts);
|
|
7
|
+
if (children.length > 0)
|
|
8
|
+
g.add(...children);
|
|
9
|
+
return g;
|
|
10
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { type Cell, type Val, Vec, type Writable } from "../core/index.js";
|
|
2
|
+
import { Circle } from "./circle.js";
|
|
3
|
+
import type { Path } from "./path.js";
|
|
4
|
+
import type { AnyShape, Has } from "./shape.js";
|
|
5
|
+
export interface HandleOpts {
|
|
6
|
+
/** Handle radius (px). Default 6. */
|
|
7
|
+
r?: number;
|
|
8
|
+
/** Fill color. Default `--bireactive-handle`. Accepts reactive values. */
|
|
9
|
+
fill?: Val<string>;
|
|
10
|
+
/** CSS cursor on hover. Default `grab`. */
|
|
11
|
+
cursor?: string;
|
|
12
|
+
}
|
|
13
|
+
/** Draggable circular handle. A `Circle` plus a `dragging` signal (true
|
|
14
|
+
* between pointerdown and pointerup/cancel) for coordinating animations. */
|
|
15
|
+
export declare class Handle extends Circle {
|
|
16
|
+
readonly dragging: Writable<Cell<boolean>>;
|
|
17
|
+
constructor(target: Writable<Vec>, opts?: HandleOpts);
|
|
18
|
+
}
|
|
19
|
+
declare function handleFn(target: Writable<Vec>, opts?: HandleOpts): Handle;
|
|
20
|
+
/** `handle(point)` is the atom; `.move`, `.centroid`, etc. are sugar. */
|
|
21
|
+
export declare const handle: typeof handleFn & {
|
|
22
|
+
move: (shape: AnyShape & Has<"translate">, opts?: HandleOpts) => Handle;
|
|
23
|
+
anchor: (shape: AnyShape & Has<"translate">, u: number, v: number, opts?: HandleOpts) => Handle;
|
|
24
|
+
centroid: (...shapes: (AnyShape & Has<"translate">)[]) => Handle;
|
|
25
|
+
midpoint: (a: Writable<Vec>, b: Writable<Vec>, opts?: HandleOpts) => Handle;
|
|
26
|
+
rotate: (shape: AnyShape & Has<"rotate">, radius?: number, opts?: HandleOpts) => Handle;
|
|
27
|
+
scale: (shape: AnyShape & Has<"scale">, radius?: number, opts?: HandleOpts) => Handle;
|
|
28
|
+
tOnPath: (p: Path, t: Cell<number>, opts?: HandleOpts & {
|
|
29
|
+
samples?: number;
|
|
30
|
+
}) => Handle;
|
|
31
|
+
};
|
|
32
|
+
export {};
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
// handle.* — writable derived shapes (draggable circles wired to a Vec).
|
|
2
|
+
import { cell, centroidLens, midpointLens, polar as polarLens, Vec, } from "../core/index.js";
|
|
3
|
+
import { Circle } from "./circle.js";
|
|
4
|
+
import { drag } from "./interaction.js";
|
|
5
|
+
const COLOR = "var(--bireactive-handle, #2563eb)";
|
|
6
|
+
/** Draggable circular handle. A `Circle` plus a `dragging` signal (true
|
|
7
|
+
* between pointerdown and pointerup/cancel) for coordinating animations. */
|
|
8
|
+
export class Handle extends Circle {
|
|
9
|
+
dragging;
|
|
10
|
+
constructor(target, opts = {}) {
|
|
11
|
+
const circleOpts = {
|
|
12
|
+
fill: opts.fill ?? COLOR,
|
|
13
|
+
// Background-colored halo so the handle pops on either theme.
|
|
14
|
+
stroke: "var(--bg-color, white)",
|
|
15
|
+
strokeWidth: 2,
|
|
16
|
+
aside: true,
|
|
17
|
+
};
|
|
18
|
+
super(target, opts.r ?? 6, circleOpts);
|
|
19
|
+
this.el.style.cursor = opts.cursor ?? "grab";
|
|
20
|
+
this.dragging = cell(false);
|
|
21
|
+
this.disposers.push(drag(this, target, this.dragging));
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
function handleFn(target, opts = {}) {
|
|
25
|
+
return new Handle(target, opts);
|
|
26
|
+
}
|
|
27
|
+
/** Drag handle at the shape's center — drags translate the shape. */
|
|
28
|
+
const move = (shape, opts) => handleFn(shape.center, opts);
|
|
29
|
+
/** Drag handle at a specific anchor `(u, v)` of the shape — drag
|
|
30
|
+
* translates the shape so that anchor lands at the pointer. */
|
|
31
|
+
const anchor = (shape, u, v, opts) => handleFn(shape.at(u, v), opts);
|
|
32
|
+
/** Drag handle at the centroid of N shapes' visual centers; drags translate
|
|
33
|
+
* all shapes rigidly. Reads the centroid of visible positions (cf. `centroid`
|
|
34
|
+
* in `shape.ts`, which works on translate deltas). */
|
|
35
|
+
const centroidHandle = (...shapes) => handleFn(centroidLens(shapes.map(s => s.center)));
|
|
36
|
+
/** Drag handle at the midpoint of two writable Points — drags both
|
|
37
|
+
* along with it. */
|
|
38
|
+
const midpoint = (a, b, opts) => handleFn(midpointLens(a, b), opts);
|
|
39
|
+
/** Rotation knob orbiting the shape's center at `radius`; drag to write
|
|
40
|
+
* `shape.rotate`. */
|
|
41
|
+
const rotate = (shape, radius = 40, opts) => {
|
|
42
|
+
// `polar` with `circular` policy: c and r fixed, writes only update θ.
|
|
43
|
+
return handleFn(polarLens(shape.center, radius, shape.rotate, "circular"), {
|
|
44
|
+
cursor: "grab",
|
|
45
|
+
...opts,
|
|
46
|
+
});
|
|
47
|
+
};
|
|
48
|
+
/** Uniform-scale knob along +x at `radius * scale.x`; drag x-distance writes
|
|
49
|
+
* both scale axes. */
|
|
50
|
+
const scaleHandle = (shape, radius = 40, opts) => {
|
|
51
|
+
// Reads center and scale; writes only scale.
|
|
52
|
+
const pos = Vec.lens([shape.center, shape.scale], vals => ({ x: vals[0].x + radius * vals[1].x, y: vals[0].y }), (target, vals) => {
|
|
53
|
+
const k = Math.max(0.05, Math.abs(target.x - vals[0].x) / radius);
|
|
54
|
+
return [undefined, { x: k, y: k }];
|
|
55
|
+
});
|
|
56
|
+
return handleFn(pos, { cursor: "ew-resize", ...opts });
|
|
57
|
+
};
|
|
58
|
+
/** Handle constrained to a Path: each drag projects the pointer onto the path
|
|
59
|
+
* and sets `t` to the nearest parameter (re-projects, so animated paths work). */
|
|
60
|
+
const tOnPath = (p, t, opts) => {
|
|
61
|
+
const N = opts?.samples ?? 64;
|
|
62
|
+
const project = (target) => {
|
|
63
|
+
let bestT = 0;
|
|
64
|
+
let bestD = Number.POSITIVE_INFINITY;
|
|
65
|
+
for (let i = 0; i <= N; i++) {
|
|
66
|
+
const tt = i / N;
|
|
67
|
+
const pp = p.pointAt(tt).value;
|
|
68
|
+
const d = (pp.x - target.x) ** 2 + (pp.y - target.y) ** 2;
|
|
69
|
+
if (d < bestD) {
|
|
70
|
+
bestD = d;
|
|
71
|
+
bestT = tt;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return bestT;
|
|
75
|
+
};
|
|
76
|
+
const pos = Vec.lens([t], ([tv]) => p.pointAt(tv).value, target => [project(target)]);
|
|
77
|
+
return handleFn(pos, opts);
|
|
78
|
+
};
|
|
79
|
+
/** `handle(point)` is the atom; `.move`, `.centroid`, etc. are sugar. */
|
|
80
|
+
export const handle = Object.assign(handleFn, {
|
|
81
|
+
move,
|
|
82
|
+
anchor,
|
|
83
|
+
centroid: centroidHandle,
|
|
84
|
+
midpoint,
|
|
85
|
+
rotate,
|
|
86
|
+
scale: scaleHandle,
|
|
87
|
+
tOnPath,
|
|
88
|
+
});
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export { AnnularSector, type AnnularSectorOpts, annularSector, } from "./annular-sector.js";
|
|
2
|
+
export { type ButtonOpts, button } from "./button.js";
|
|
3
|
+
export { assemble, orbit, splay, stagger, swap } from "./choreographers.js";
|
|
4
|
+
export { Circle, type CircleOpts, circle } from "./circle.js";
|
|
5
|
+
export { clipPath } from "./clip.js";
|
|
6
|
+
export { type ArrowOpts, arrow, connect, ensureArrowMarker } from "./connect.js";
|
|
7
|
+
export { Curve, type CurveOpts, type CurveSegment, curve, ellipse } from "./curve.js";
|
|
8
|
+
export { dashedPath } from "./dashed.js";
|
|
9
|
+
export { debug } from "./debug.js";
|
|
10
|
+
export { group } from "./group.js";
|
|
11
|
+
export { type HandleOpts, handle } from "./handle.js";
|
|
12
|
+
export { cursor, drag, draggable, dragRotate, dragWithState, hoverSignal } from "./interaction.js";
|
|
13
|
+
export { Label, type LabelOpts, label } from "./label.js";
|
|
14
|
+
export { type ArrangeOpts, arrange, expand, grid, split } from "./layout.js";
|
|
15
|
+
export { Line, type LineOpts, line } from "./line.js";
|
|
16
|
+
export { type ForEachOptions, forEach } from "./list.js";
|
|
17
|
+
export { type Mount, mount } from "./mount.js";
|
|
18
|
+
export { Path, type PathDOpts, type PathOpts, path, pathD } from "./path.js";
|
|
19
|
+
export { Rect, type RectOpts, rect } from "./rect.js";
|
|
20
|
+
export { type AnimatableKey, type AnyShape, type CommonOpts, centroid, type Has, meanRotation, meanScale, type Segment, Shape, type ShapeOpts, SVG_NS, } from "./shape.js";
|
|
21
|
+
export { type Content, Text, type TextPart, t } from "./text.js";
|
|
22
|
+
export { type Tokens, tokens } from "./tokens.js";
|
|
23
|
+
export { bounceIn, fadeIn, fadeOut, fadeUp, fadeUpOut, scaleIn, slideIn, slideOut, spinIn, zoomOut, } from "./transitions.js";
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export { AnnularSector, annularSector, } from "./annular-sector.js";
|
|
2
|
+
export { button } from "./button.js";
|
|
3
|
+
export { assemble, orbit, splay, stagger, swap } from "./choreographers.js";
|
|
4
|
+
export { Circle, circle } from "./circle.js";
|
|
5
|
+
export { clipPath } from "./clip.js";
|
|
6
|
+
export { arrow, connect, ensureArrowMarker } from "./connect.js";
|
|
7
|
+
export { Curve, curve, ellipse } from "./curve.js";
|
|
8
|
+
export { dashedPath } from "./dashed.js";
|
|
9
|
+
export { debug } from "./debug.js";
|
|
10
|
+
export { group } from "./group.js";
|
|
11
|
+
export { handle } from "./handle.js";
|
|
12
|
+
export { cursor, drag, draggable, dragRotate, dragWithState, hoverSignal } from "./interaction.js";
|
|
13
|
+
export { Label, label } from "./label.js";
|
|
14
|
+
export { arrange, expand, grid, split } from "./layout.js";
|
|
15
|
+
export { Line, line } from "./line.js";
|
|
16
|
+
export { forEach } from "./list.js";
|
|
17
|
+
export { mount } from "./mount.js";
|
|
18
|
+
export { Path, path, pathD } from "./path.js";
|
|
19
|
+
export { Rect, rect } from "./rect.js";
|
|
20
|
+
export { centroid, meanRotation, meanScale, Shape, SVG_NS, } from "./shape.js";
|
|
21
|
+
export { Text, t } from "./text.js";
|
|
22
|
+
export { tokens } from "./tokens.js";
|
|
23
|
+
export { bounceIn, fadeIn, fadeOut, fadeUp, fadeUpOut, scaleIn, slideIn, slideOut, spinIn, zoomOut, } from "./transitions.js";
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { type Cell, type Inner, type Num, Vec, type Writable } from "../core/index.js";
|
|
2
|
+
import type { AnyShape } from "./shape.js";
|
|
3
|
+
/** Set `sig` true/false from `mouseenter`/`mouseleave` on `shape`; returns a
|
|
4
|
+
* disposer. Lower-level than `hover(el, marker)` — writes the signal directly. */
|
|
5
|
+
export declare function hoverSignal(shape: AnyShape, sig: Writable<Cell<boolean>>): () => void;
|
|
6
|
+
/** Reactive `Vec` tracking the page pointer in `shape`'s SVG-root frame, via
|
|
7
|
+
* the shared window listener (N callers, one listener). `init` is returned
|
|
8
|
+
* before the first `pointermove` to avoid a first-frame jolt to (0, 0). */
|
|
9
|
+
export declare function cursor(shape: AnyShape, init?: Inner<Vec>): Vec;
|
|
10
|
+
/** Wire `handle` for pointer-drag. Each pointermove while pressed
|
|
11
|
+
* calls `onDrag(local)` with the pointer in `handle`'s local frame;
|
|
12
|
+
* pointer-captured so drags survive leaving the handle. The optional
|
|
13
|
+
* `onState(active)` callback fires `true` on pointerdown and `false`
|
|
14
|
+
* on pointerup/cancel — `Handle` uses it to drive `.dragging`. */
|
|
15
|
+
export declare function draggable(handle: AnyShape, onDrag: (local: Inner<Vec>) => void, onState?: (active: boolean) => void): () => void;
|
|
16
|
+
/** Bind pointer drag on `shape` directly to a writable `Vec` (no handle dot);
|
|
17
|
+
* returns a disposer. `target` is in the SVG-root frame and coords are read
|
|
18
|
+
* via `toWorld`, so the grab offset survives bwd writes back through
|
|
19
|
+
* `shape.translate`. Grab offset is captured on pointerdown; optional
|
|
20
|
+
* `dragging` reports active state. Defaults `cursor` to `"grab"`. */
|
|
21
|
+
export declare function drag(shape: AnyShape, target: Writable<Vec>, dragging?: Writable<Cell<boolean>>): () => void;
|
|
22
|
+
/** Wrap a `drag(shape, target)` call and return a local `dragging`
|
|
23
|
+
* Cell<boolean>. Sugar for "give me a drag handle that exposes its
|
|
24
|
+
* own state." */
|
|
25
|
+
export declare function dragWithState(shape: AnyShape, target: Writable<Vec>): {
|
|
26
|
+
dragging: Cell<boolean>;
|
|
27
|
+
dispose: () => void;
|
|
28
|
+
};
|
|
29
|
+
/** Drag-to-rotate about the shape's local origin: writes `angle` so the
|
|
30
|
+
* grabbed point tracks the cursor (Δ = current − grab angle, shortest arc).
|
|
31
|
+
* Returns a disposer. */
|
|
32
|
+
export declare function dragRotate(shape: AnyShape, angle: Writable<Num>, dragging?: Writable<Cell<boolean>>): () => void;
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
// DOM input → signal-world bridges that bind to scene-graph shapes.
|
|
2
|
+
import { cell, Vec } from "../core/index.js";
|
|
3
|
+
// Shared page-pointer state for `cursor()`: one lazy window listener feeding
|
|
4
|
+
// one signal. `null` until the first `pointermove`. Never disposed.
|
|
5
|
+
let _clientPointer = null;
|
|
6
|
+
function pageClientPointer() {
|
|
7
|
+
if (_clientPointer)
|
|
8
|
+
return _clientPointer;
|
|
9
|
+
const sig = cell(null);
|
|
10
|
+
window.addEventListener("pointermove", (e) => {
|
|
11
|
+
sig.value = { clientX: e.clientX, clientY: e.clientY };
|
|
12
|
+
});
|
|
13
|
+
_clientPointer = sig;
|
|
14
|
+
return sig;
|
|
15
|
+
}
|
|
16
|
+
const TAU = Math.PI * 2;
|
|
17
|
+
const wrapToPi = (x) => x - TAU * Math.round(x / TAU);
|
|
18
|
+
/** Stop a touch that lands on a draggable element from scrolling/zooming
|
|
19
|
+
* the page. Set on both the `<g>` and its intrinsic (the actual hit
|
|
20
|
+
* target on iOS Safari) so the gesture is owned by the drag, not the
|
|
21
|
+
* page. Non-draggable scenery keeps `touch-action: auto`, so swiping
|
|
22
|
+
* past a diagram still scrolls. */
|
|
23
|
+
function ownTouchGesture(shape) {
|
|
24
|
+
shape.el.style.touchAction = "none";
|
|
25
|
+
if (shape.intrinsic)
|
|
26
|
+
shape.intrinsic.style.touchAction = "none";
|
|
27
|
+
}
|
|
28
|
+
/** iOS Safari ignores `touch-action` on inner SVG nodes, so a drag that
|
|
29
|
+
* starts on a handle still pans the page. For the lifetime of an active
|
|
30
|
+
* drag we also `preventDefault` a non-passive document `touchmove`,
|
|
31
|
+
* which reliably suppresses scroll/zoom in every browser. Returns a
|
|
32
|
+
* disposer that re-enables scrolling; call it on pointerup/cancel. */
|
|
33
|
+
function blockPageScroll() {
|
|
34
|
+
const onMove = (e) => e.preventDefault();
|
|
35
|
+
document.addEventListener("touchmove", onMove, { passive: false });
|
|
36
|
+
return () => document.removeEventListener("touchmove", onMove);
|
|
37
|
+
}
|
|
38
|
+
/** Set `sig` true/false from `mouseenter`/`mouseleave` on `shape`; returns a
|
|
39
|
+
* disposer. Lower-level than `hover(el, marker)` — writes the signal directly. */
|
|
40
|
+
export function hoverSignal(shape, sig) {
|
|
41
|
+
const off1 = shape.on("mouseenter", () => {
|
|
42
|
+
sig.value = true;
|
|
43
|
+
});
|
|
44
|
+
const off2 = shape.on("mouseleave", () => {
|
|
45
|
+
sig.value = false;
|
|
46
|
+
});
|
|
47
|
+
return () => {
|
|
48
|
+
off1();
|
|
49
|
+
off2();
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
/** Reactive `Vec` tracking the page pointer in `shape`'s SVG-root frame, via
|
|
53
|
+
* the shared window listener (N callers, one listener). `init` is returned
|
|
54
|
+
* before the first `pointermove` to avoid a first-frame jolt to (0, 0). */
|
|
55
|
+
export function cursor(shape, init) {
|
|
56
|
+
const cp = pageClientPointer();
|
|
57
|
+
const fallback = init ?? { x: 0, y: 0 };
|
|
58
|
+
return Vec.derive(cp, p => (p ? shape.toWorld(p) : fallback));
|
|
59
|
+
}
|
|
60
|
+
/** Wire `handle` for pointer-drag. Each pointermove while pressed
|
|
61
|
+
* calls `onDrag(local)` with the pointer in `handle`'s local frame;
|
|
62
|
+
* pointer-captured so drags survive leaving the handle. The optional
|
|
63
|
+
* `onState(active)` callback fires `true` on pointerdown and `false`
|
|
64
|
+
* on pointerup/cancel — `Handle` uses it to drive `.dragging`. */
|
|
65
|
+
export function draggable(handle, onDrag, onState) {
|
|
66
|
+
let dragging = false;
|
|
67
|
+
let pointerId = -1;
|
|
68
|
+
let unblock = null;
|
|
69
|
+
ownTouchGesture(handle);
|
|
70
|
+
const offs = [];
|
|
71
|
+
offs.push(handle.on("pointerdown", e => {
|
|
72
|
+
const pe = e;
|
|
73
|
+
dragging = true;
|
|
74
|
+
pointerId = pe.pointerId;
|
|
75
|
+
handle.el.setPointerCapture(pointerId);
|
|
76
|
+
unblock = blockPageScroll();
|
|
77
|
+
onState?.(true);
|
|
78
|
+
onDrag(handle.toLocal(pe));
|
|
79
|
+
}));
|
|
80
|
+
offs.push(handle.on("pointermove", e => {
|
|
81
|
+
if (!dragging)
|
|
82
|
+
return;
|
|
83
|
+
onDrag(handle.toLocal(e));
|
|
84
|
+
}));
|
|
85
|
+
const stop = () => {
|
|
86
|
+
if (dragging && pointerId !== -1) {
|
|
87
|
+
try {
|
|
88
|
+
handle.el.releasePointerCapture(pointerId);
|
|
89
|
+
}
|
|
90
|
+
catch {
|
|
91
|
+
/* ok */
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
dragging = false;
|
|
95
|
+
pointerId = -1;
|
|
96
|
+
unblock?.();
|
|
97
|
+
unblock = null;
|
|
98
|
+
onState?.(false);
|
|
99
|
+
};
|
|
100
|
+
offs.push(handle.on("pointerup", stop));
|
|
101
|
+
offs.push(handle.on("pointercancel", stop));
|
|
102
|
+
return () => offs.forEach(d => d());
|
|
103
|
+
}
|
|
104
|
+
/** Bind pointer drag on `shape` directly to a writable `Vec` (no handle dot);
|
|
105
|
+
* returns a disposer. `target` is in the SVG-root frame and coords are read
|
|
106
|
+
* via `toWorld`, so the grab offset survives bwd writes back through
|
|
107
|
+
* `shape.translate`. Grab offset is captured on pointerdown; optional
|
|
108
|
+
* `dragging` reports active state. Defaults `cursor` to `"grab"`. */
|
|
109
|
+
export function drag(shape, target, dragging) {
|
|
110
|
+
if (!shape.el.style.cursor)
|
|
111
|
+
shape.el.style.cursor = "grab";
|
|
112
|
+
ownTouchGesture(shape);
|
|
113
|
+
let dx = 0;
|
|
114
|
+
let dy = 0;
|
|
115
|
+
let pointerId = -1;
|
|
116
|
+
let unblock = null;
|
|
117
|
+
const offs = [];
|
|
118
|
+
offs.push(shape.on("pointerdown", e => {
|
|
119
|
+
const pe = e;
|
|
120
|
+
pointerId = pe.pointerId;
|
|
121
|
+
shape.el.setPointerCapture(pointerId);
|
|
122
|
+
unblock = blockPageScroll();
|
|
123
|
+
const world = shape.toWorld(pe);
|
|
124
|
+
const v = target.value;
|
|
125
|
+
dx = world.x - v.x;
|
|
126
|
+
dy = world.y - v.y;
|
|
127
|
+
if (dragging)
|
|
128
|
+
dragging.value = true;
|
|
129
|
+
}));
|
|
130
|
+
offs.push(shape.on("pointermove", e => {
|
|
131
|
+
if (pointerId === -1)
|
|
132
|
+
return;
|
|
133
|
+
const world = shape.toWorld(e);
|
|
134
|
+
target.value = { x: world.x - dx, y: world.y - dy };
|
|
135
|
+
}));
|
|
136
|
+
const stop = () => {
|
|
137
|
+
if (pointerId !== -1) {
|
|
138
|
+
try {
|
|
139
|
+
shape.el.releasePointerCapture(pointerId);
|
|
140
|
+
}
|
|
141
|
+
catch {
|
|
142
|
+
/* ok */
|
|
143
|
+
}
|
|
144
|
+
pointerId = -1;
|
|
145
|
+
}
|
|
146
|
+
unblock?.();
|
|
147
|
+
unblock = null;
|
|
148
|
+
if (dragging)
|
|
149
|
+
dragging.value = false;
|
|
150
|
+
};
|
|
151
|
+
offs.push(shape.on("pointerup", stop));
|
|
152
|
+
offs.push(shape.on("pointercancel", stop));
|
|
153
|
+
return () => offs.forEach(d => d());
|
|
154
|
+
}
|
|
155
|
+
/** Wrap a `drag(shape, target)` call and return a local `dragging`
|
|
156
|
+
* Cell<boolean>. Sugar for "give me a drag handle that exposes its
|
|
157
|
+
* own state." */
|
|
158
|
+
export function dragWithState(shape, target) {
|
|
159
|
+
const dragging = cell(false);
|
|
160
|
+
const dispose = drag(shape, target, dragging);
|
|
161
|
+
return { dragging, dispose };
|
|
162
|
+
}
|
|
163
|
+
/** Drag-to-rotate about the shape's local origin: writes `angle` so the
|
|
164
|
+
* grabbed point tracks the cursor (Δ = current − grab angle, shortest arc).
|
|
165
|
+
* Returns a disposer. */
|
|
166
|
+
export function dragRotate(shape, angle, dragging) {
|
|
167
|
+
if (!shape.el.style.cursor)
|
|
168
|
+
shape.el.style.cursor = "grab";
|
|
169
|
+
let grabAngle = 0;
|
|
170
|
+
const offDown = shape.on("pointerdown", e => {
|
|
171
|
+
const local = shape.toLocal(e);
|
|
172
|
+
grabAngle = Math.atan2(local.y, local.x);
|
|
173
|
+
});
|
|
174
|
+
const stop = draggable(shape, local => {
|
|
175
|
+
const currentAngle = Math.atan2(local.y, local.x);
|
|
176
|
+
const current = angle.peek();
|
|
177
|
+
angle.value = current + wrapToPi(currentAngle - grabAngle);
|
|
178
|
+
}, dragging
|
|
179
|
+
? active => {
|
|
180
|
+
dragging.value = active;
|
|
181
|
+
}
|
|
182
|
+
: undefined);
|
|
183
|
+
return () => {
|
|
184
|
+
offDown();
|
|
185
|
+
stop();
|
|
186
|
+
};
|
|
187
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { type Inner, type Val, type Vec } from "../core/index.js";
|
|
2
|
+
import { Shape, type ShapeOpts } from "./shape.js";
|
|
3
|
+
import { type Content } from "./text.js";
|
|
4
|
+
export interface LabelOpts extends ShapeOpts {
|
|
5
|
+
size?: Val<number>;
|
|
6
|
+
/** Bbox point that sits at `at` — `{0, 0}` = top-left, `{0.5, 0.5}`
|
|
7
|
+
* (default) = center. See `Anchor` for named consts. */
|
|
8
|
+
align?: Inner<Vec>;
|
|
9
|
+
bold?: boolean;
|
|
10
|
+
/** Text color. Default `tokens.stroke` (i.e. `var(--text-color)`,
|
|
11
|
+
* flips with dark mode). Accepts a reactive `Val<string>`. */
|
|
12
|
+
fill?: Val<string>;
|
|
13
|
+
}
|
|
14
|
+
export declare class Label<O extends LabelOpts = LabelOpts> extends Shape<O> {
|
|
15
|
+
/** The position the label attaches to (subject to `align`); distinct from
|
|
16
|
+
* the inherited Box `center` / `at(u, v)`. */
|
|
17
|
+
readonly anchor: Vec;
|
|
18
|
+
constructor(anchor: Vec, content: Val<Content>, opts?: O);
|
|
19
|
+
}
|
|
20
|
+
export declare const label: <const O extends LabelOpts>(at: Vec, content: Val<Content>, opts?: O) => Label<O>;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { Cell, cell, derive, Num } from "../core/index.js";
|
|
2
|
+
import { Shape } from "./shape.js";
|
|
3
|
+
import { flattenText, renderContent } from "./text.js";
|
|
4
|
+
import { tokens } from "./tokens.js";
|
|
5
|
+
const xAttr = (x) => (x <= 0.25 ? "start" : x >= 0.75 ? "end" : "middle");
|
|
6
|
+
const yAttr = (y) => (y <= 0.25 ? "hanging" : y >= 0.75 ? "alphabetic" : "central");
|
|
7
|
+
export class Label extends Shape {
|
|
8
|
+
/** The position the label attaches to (subject to `align`); distinct from
|
|
9
|
+
* the inherited Box `center` / `at(u, v)`. */
|
|
10
|
+
anchor;
|
|
11
|
+
constructor(anchor, content, opts = {}) {
|
|
12
|
+
const contentSig = content instanceof Cell
|
|
13
|
+
? content
|
|
14
|
+
: typeof content === "function"
|
|
15
|
+
? derive(content)
|
|
16
|
+
: cell(content);
|
|
17
|
+
const sizeSig = Num.from(opts.size ?? tokens.fontSize);
|
|
18
|
+
const a = opts.align ?? { x: 0.5, y: 0.5 };
|
|
19
|
+
super("text", () => {
|
|
20
|
+
const text = flattenText(contentSig.value);
|
|
21
|
+
const fs = sizeSig.value;
|
|
22
|
+
const w = fs * Math.max(1, text.length) * tokens.charWidth;
|
|
23
|
+
return { x: anchor.x.value - a.x * w, y: anchor.y.value - a.y * fs, w, h: fs };
|
|
24
|
+
}, opts,
|
|
25
|
+
// Pivot rotations on the anchor, not the bbox center.
|
|
26
|
+
{ origin: anchor });
|
|
27
|
+
this.anchor = anchor;
|
|
28
|
+
this.attr("x", anchor.x);
|
|
29
|
+
this.attr("y", anchor.y);
|
|
30
|
+
this.attr("font-family", tokens.font);
|
|
31
|
+
this.attr("font-size", sizeSig);
|
|
32
|
+
this.attr("fill", opts.fill ?? tokens.stroke);
|
|
33
|
+
this.attr("text-anchor", xAttr(a.x));
|
|
34
|
+
this.attr("dominant-baseline", yAttr(a.y));
|
|
35
|
+
if (opts.bold)
|
|
36
|
+
this.attr("font-weight", 700);
|
|
37
|
+
this.effect(() => {
|
|
38
|
+
this.intrinsic.innerHTML = renderContent(contentSig.value);
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
export const label = (at, content, opts) => new Label(at, content, opts);
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { Box, type Val } from "../core/index.js";
|
|
2
|
+
import type { Shape } from "./shape.js";
|
|
3
|
+
export interface ArrangeOpts {
|
|
4
|
+
/** Spacing between adjacent bounding boxes. Default 0. */
|
|
5
|
+
gap?: number;
|
|
6
|
+
/** Cross-axis align vs the first shape: 0 top/left, 0.5 center,
|
|
7
|
+
* 1 bottom/right. Default 0. */
|
|
8
|
+
align?: number;
|
|
9
|
+
}
|
|
10
|
+
/** Lay out `shapes` in a row/column. First stays put; the rest bind
|
|
11
|
+
* their `translate` reactively to sit `gap` past the previous.
|
|
12
|
+
* Reflows on size or anchor change. */
|
|
13
|
+
export declare function arrange(shapes: readonly Shape[], axis: "row" | "column", opts?: ArrangeOpts): void;
|
|
14
|
+
/** Inflate a Box on each side by `by`. */
|
|
15
|
+
export declare function expand(b: Box, by: Val<number>): Box;
|
|
16
|
+
/** Split a Box along an axis into N reactive sub-Boxes.
|
|
17
|
+
*
|
|
18
|
+
* split(b, "x", 3) — 3 equal columns
|
|
19
|
+
* split(b, "x", [3, 2, 2]) — weighted 3:2:2
|
|
20
|
+
* split(b, "x", 3, { gap: 4 }) — 4px between
|
|
21
|
+
*/
|
|
22
|
+
export declare function split(source: Box, axis: "x" | "y", parts: number | number[], opts?: {
|
|
23
|
+
gap?: Val<number>;
|
|
24
|
+
}): Box[];
|
|
25
|
+
/** Two-axis split into a `rows × cols` grid (sugar over `split`).
|
|
26
|
+
* Returns `[row][col]`. */
|
|
27
|
+
export declare function grid(source: Box, rows: number, cols: number, opts?: {
|
|
28
|
+
gap?: Val<number>;
|
|
29
|
+
}): Box[][];
|