bireactive 0.3.0 → 0.3.2
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 +14 -7
- package/dist/automerge/doc-cell.d.ts +20 -0
- package/dist/automerge/doc-cell.js +80 -0
- package/dist/automerge/index.d.ts +3 -0
- package/dist/automerge/index.js +12 -0
- package/dist/automerge/reconcile.d.ts +5 -0
- package/dist/automerge/reconcile.js +63 -0
- package/dist/core/_counts.d.ts +48 -0
- package/dist/core/_counts.js +51 -0
- package/dist/core/cell.d.ts +148 -112
- package/dist/core/cell.js +945 -768
- package/dist/core/debug.d.ts +25 -0
- package/dist/core/debug.js +121 -0
- package/dist/core/derived-geometry.js +4 -7
- package/dist/core/index.d.ts +9 -2
- package/dist/core/index.js +8 -1
- package/dist/core/lenses/aggregates.d.ts +42 -52
- package/dist/core/lenses/aggregates.js +225 -116
- package/dist/core/lenses/geometry.d.ts +22 -4
- package/dist/core/lenses/geometry.js +59 -27
- package/dist/core/lenses/index.d.ts +6 -6
- package/dist/core/lenses/index.js +6 -6
- package/dist/core/lenses/memory.js +4 -17
- package/dist/core/lenses/numerical.d.ts +100 -0
- package/dist/core/lenses/{typed-factor.js → numerical.js} +136 -34
- package/dist/core/lenses/point-cloud.d.ts +67 -0
- package/dist/core/lenses/{closed-form-policies.js → point-cloud.js} +226 -84
- package/dist/core/lenses/snap.d.ts +18 -0
- package/dist/core/lenses/snap.js +138 -0
- package/dist/core/lenses/text.d.ts +40 -0
- package/dist/core/lenses/text.js +202 -0
- package/dist/core/lifecycle.js +3 -6
- package/dist/core/linalg.js +5 -11
- package/dist/core/optic.d.ts +13 -0
- package/dist/core/optic.js +39 -0
- package/dist/core/optics.d.ts +10 -0
- package/dist/core/optics.js +26 -0
- package/dist/core/store.d.ts +9 -0
- package/dist/core/store.js +77 -0
- package/dist/core/traits.d.ts +4 -7
- package/dist/core/traits.js +8 -12
- package/dist/core/values/anchor.js +0 -4
- package/dist/core/values/arr.d.ts +110 -0
- package/dist/core/values/arr.js +336 -0
- package/dist/core/values/audio.d.ts +8 -9
- package/dist/core/values/audio.js +11 -28
- package/dist/core/values/bool.d.ts +11 -11
- package/dist/core/values/bool.js +12 -22
- package/dist/core/values/box.d.ts +15 -20
- package/dist/core/values/box.js +20 -33
- package/dist/core/values/canvas.d.ts +18 -25
- package/dist/core/values/canvas.js +32 -66
- package/dist/core/values/color.d.ts +5 -7
- package/dist/core/values/color.js +5 -11
- package/dist/core/values/field.d.ts +6 -7
- package/dist/core/values/field.js +10 -35
- package/dist/core/values/flags.d.ts +1 -2
- package/dist/core/values/flags.js +1 -17
- package/dist/core/values/gpu.d.ts +6 -10
- package/dist/core/values/gpu.js +8 -22
- package/dist/core/values/matrix.d.ts +2 -4
- package/dist/core/values/matrix.js +2 -12
- package/dist/core/values/num.d.ts +19 -28
- package/dist/core/values/num.js +23 -41
- package/dist/core/values/pose.d.ts +2 -4
- package/dist/core/values/pose.js +3 -12
- package/dist/core/values/range.d.ts +18 -26
- package/dist/core/values/range.js +22 -39
- package/dist/core/values/reg/ambiguity.d.ts +8 -0
- package/dist/core/values/reg/ambiguity.js +131 -0
- package/dist/core/values/reg/engine.d.ts +91 -0
- package/dist/core/values/reg/engine.js +373 -0
- package/dist/core/values/reg/nfa.d.ts +42 -0
- package/dist/core/values/reg/nfa.js +391 -0
- package/dist/core/values/reg/regex.d.ts +7 -0
- package/dist/core/values/reg/regex.js +318 -0
- package/dist/core/values/reg/types.d.ts +60 -0
- package/dist/core/values/reg/types.js +3 -0
- package/dist/core/values/reg.d.ts +250 -0
- package/dist/core/values/reg.js +649 -0
- package/dist/core/values/str.d.ts +16 -60
- package/dist/core/values/str.js +133 -315
- package/dist/core/values/template.js +1 -24
- package/dist/core/values/transform.d.ts +3 -5
- package/dist/core/values/transform.js +3 -12
- package/dist/core/values/tri.d.ts +9 -10
- package/dist/core/values/tri.js +9 -15
- package/dist/core/values/vec.d.ts +9 -24
- package/dist/core/values/vec.js +9 -64
- package/dist/formats/lens.js +6 -9
- package/dist/index.d.ts +0 -11
- package/dist/index.js +1 -11
- package/dist/jsx-dev-runtime.d.ts +2 -0
- package/dist/jsx-dev-runtime.js +5 -0
- package/dist/jsx-runtime.d.ts +54 -0
- package/dist/jsx-runtime.js +219 -0
- package/dist/schema/lens.js +5 -5
- package/dist/shapes/drag-behaviors.d.ts +56 -0
- package/dist/shapes/drag-behaviors.js +102 -0
- package/dist/shapes/drag-spec.d.ts +52 -0
- package/dist/shapes/drag-spec.js +112 -0
- package/dist/shapes/index.d.ts +3 -1
- package/dist/shapes/index.js +3 -1
- package/dist/shapes/interaction.d.ts +2 -3
- package/dist/shapes/interaction.js +77 -56
- package/dist/shapes/label.js +6 -0
- package/dist/shapes/layout.d.ts +47 -1
- package/dist/shapes/layout.js +59 -1
- package/package.json +22 -1
- package/dist/coll.d.ts +0 -74
- package/dist/coll.js +0 -210
- package/dist/core/lenses/closed-form-policies.d.ts +0 -57
- package/dist/core/lenses/decompositions.d.ts +0 -14
- package/dist/core/lenses/decompositions.js +0 -224
- package/dist/core/lenses/domain-aggregates.d.ts +0 -42
- package/dist/core/lenses/domain-aggregates.js +0 -245
- package/dist/core/lenses/typed-factor.d.ts +0 -40
|
@@ -1,10 +1,3 @@
|
|
|
1
|
-
// transform.ts — reactive 2D transform.
|
|
2
|
-
//
|
|
3
|
-
// Invertibles (`add`, `sub`) return `: this` and ride on
|
|
4
|
-
// `Cell#lens(fwd, bwd)`; chained calls compose into a lens chain.
|
|
5
|
-
// Field-lens getters use `fieldLens()`, so writability propagates through
|
|
6
|
-
// nested chains
|
|
7
|
-
// (`Transform.translate.x.value = 5` works on writable receivers).
|
|
8
1
|
import { tween } from "../../animation/index.js";
|
|
9
2
|
import { Cell, fieldLens, reader, readNow } from "../cell.js";
|
|
10
3
|
import { Num } from "./num.js";
|
|
@@ -58,8 +51,6 @@ export const metric = (a, b) => vMetric(a.translate, b.translate) +
|
|
|
58
51
|
const linearImpl = { add, sub, scale };
|
|
59
52
|
export class Transform extends Cell {
|
|
60
53
|
static traits = { linear: linearImpl, lerp, metric, equals };
|
|
61
|
-
/** Scalar `scale` is the `.scale` Vec field lens, not an eager method;
|
|
62
|
-
* scalar-multiply via `Transform.lens(...)` or field writes. */
|
|
63
54
|
constructor(v = DEFAULT) {
|
|
64
55
|
super(v, { equals });
|
|
65
56
|
}
|
|
@@ -89,13 +80,13 @@ export class Transform extends Cell {
|
|
|
89
80
|
get opacity() {
|
|
90
81
|
return fieldLens(this, "opacity", Num);
|
|
91
82
|
}
|
|
92
|
-
/** Tween-builder
|
|
83
|
+
/** Tween-builder. */
|
|
93
84
|
to(target, dur, ease) {
|
|
94
85
|
return tween(this, target, dur, ease);
|
|
95
86
|
}
|
|
96
87
|
}
|
|
97
|
-
/**
|
|
98
|
-
*
|
|
88
|
+
/** Writable `Transform` from literal fields. For reactive sources use
|
|
89
|
+
* `Transform.lens` or field writes. */
|
|
99
90
|
export function transform(init) {
|
|
100
91
|
const tr = new Transform();
|
|
101
92
|
if (init) {
|
|
@@ -15,19 +15,18 @@ export declare class Tri extends Cell<V> {
|
|
|
15
15
|
};
|
|
16
16
|
readonly _t: typeof Tri.traits;
|
|
17
17
|
constructor(v?: V);
|
|
18
|
-
/** Kleene negation.
|
|
18
|
+
/** Kleene negation. */
|
|
19
19
|
not(): this;
|
|
20
|
-
/**
|
|
21
|
-
* `
|
|
22
|
-
*
|
|
23
|
-
* child, recursing through nested aggregates; `"mixed"` is a no-op. */
|
|
20
|
+
/** AND-aggregate over writable `Bool`/`Tri` children: `true` if all are true,
|
|
21
|
+
* `false` if all false, else `"mixed"`. A `true`/`false` write broadcasts to
|
|
22
|
+
* every child (recursing into nested aggregates); `"mixed"` is a no-op. */
|
|
24
23
|
static allOf(parents: readonly (Bool | Tri)[]): Writable<Tri>;
|
|
25
|
-
/**
|
|
26
|
-
* `
|
|
27
|
-
* `"mixed"
|
|
24
|
+
/** OR-aggregate over writable `Bool`/`Tri` children: `true` if any is true,
|
|
25
|
+
* `false` if all false, else `"mixed"`. A `true`/`false` write broadcasts to
|
|
26
|
+
* every child; `"mixed"` is a no-op. */
|
|
28
27
|
static anyOf(parents: readonly (Bool | Tri)[]): Writable<Tri>;
|
|
29
28
|
}
|
|
30
|
-
/** Writable `Tri
|
|
31
|
-
*
|
|
29
|
+
/** Writable `Tri` from a literal (new cell) or existing writable (passed
|
|
30
|
+
* through). Defaults to `"mixed"`. */
|
|
32
31
|
export declare function tri(v?: Init<Tri>): Writable<Tri>;
|
|
33
32
|
export {};
|
package/dist/core/values/tri.js
CHANGED
|
@@ -1,8 +1,3 @@
|
|
|
1
|
-
// tri.ts — three-valued logical type (Kleene logic).
|
|
2
|
-
//
|
|
3
|
-
// `Tri.value ∈ { true, false, "mixed" }` — Bool plus an unknown state
|
|
4
|
-
// fixed under negation. Strong-Kleene AND/OR follow the partial-info
|
|
5
|
-
// reading (`mixed AND false` → `false`, `mixed AND true` → `mixed`).
|
|
6
1
|
import { Cell, SKIP } from "../cell.js";
|
|
7
2
|
const equals = (a, b) => a === b;
|
|
8
3
|
/** Kleene negation: `true` / `false` swap, `"mixed"` is fixed. */
|
|
@@ -30,14 +25,13 @@ export class Tri extends Cell {
|
|
|
30
25
|
constructor(v = "mixed") {
|
|
31
26
|
super(v, { equals });
|
|
32
27
|
}
|
|
33
|
-
/** Kleene negation.
|
|
28
|
+
/** Kleene negation. */
|
|
34
29
|
not() {
|
|
35
30
|
return this.lens(not, not);
|
|
36
31
|
}
|
|
37
|
-
/**
|
|
38
|
-
* `
|
|
39
|
-
*
|
|
40
|
-
* child, recursing through nested aggregates; `"mixed"` is a no-op. */
|
|
32
|
+
/** AND-aggregate over writable `Bool`/`Tri` children: `true` if all are true,
|
|
33
|
+
* `false` if all false, else `"mixed"`. A `true`/`false` write broadcasts to
|
|
34
|
+
* every child (recursing into nested aggregates); `"mixed"` is a no-op. */
|
|
41
35
|
static allOf(parents) {
|
|
42
36
|
return Tri.lens(parents, (vs) => {
|
|
43
37
|
let anyT = false;
|
|
@@ -59,9 +53,9 @@ export class Tri extends Cell {
|
|
|
59
53
|
return parents.map(() => target);
|
|
60
54
|
});
|
|
61
55
|
}
|
|
62
|
-
/**
|
|
63
|
-
* `
|
|
64
|
-
* `"mixed"
|
|
56
|
+
/** OR-aggregate over writable `Bool`/`Tri` children: `true` if any is true,
|
|
57
|
+
* `false` if all false, else `"mixed"`. A `true`/`false` write broadcasts to
|
|
58
|
+
* every child; `"mixed"` is a no-op. */
|
|
65
59
|
static anyOf(parents) {
|
|
66
60
|
return Tri.lens(parents, (vs) => {
|
|
67
61
|
let anyT = false;
|
|
@@ -86,8 +80,8 @@ export class Tri extends Cell {
|
|
|
86
80
|
});
|
|
87
81
|
}
|
|
88
82
|
}
|
|
89
|
-
/** Writable `Tri
|
|
90
|
-
*
|
|
83
|
+
/** Writable `Tri` from a literal (new cell) or existing writable (passed
|
|
84
|
+
* through). Defaults to `"mixed"`. */
|
|
91
85
|
export function tri(v = "mixed") {
|
|
92
86
|
if (v instanceof Tri)
|
|
93
87
|
return v;
|
|
@@ -21,6 +21,9 @@ export declare const scaleAbout: (v: V, p: V, k: number) => V;
|
|
|
21
21
|
* (y-down screen coords flip the visual sense). Returns `c` if `p` is
|
|
22
22
|
* inside or on the circle. */
|
|
23
23
|
export declare function tangentPoint(p: V, c: V, r: number, side?: 1 | -1): V;
|
|
24
|
+
/** Representative of cyclic angle `target` closest to `current`
|
|
25
|
+
* (shortest-arc inverse). */
|
|
26
|
+
export declare const nearestAngle: (target: number, current: number) => number;
|
|
24
27
|
export declare class Vec extends Cell<V> {
|
|
25
28
|
static traits: {
|
|
26
29
|
linear: Linear<V>;
|
|
@@ -34,11 +37,9 @@ export declare class Vec extends Cell<V> {
|
|
|
34
37
|
constructor(v?: V);
|
|
35
38
|
add(b: Val<V>): this;
|
|
36
39
|
sub(b: Val<V>): this;
|
|
37
|
-
/** Uniform scale by `k` about `pivot` (default origin).
|
|
38
|
-
* by `1/k`; exact bijection for `k ≠ 0`. */
|
|
40
|
+
/** Uniform scale by `k` about `pivot` (default origin). Invertible when k ≠ 0. */
|
|
39
41
|
scale(k: Val<number>, pivot?: Val<V>): this;
|
|
40
|
-
/** Rotate by `angle` (radians) about `pivot` (default origin).
|
|
41
|
-
* rotates by `−angle`; exact bijection. */
|
|
42
|
+
/** Rotate by `angle` (radians) about `pivot` (default origin). */
|
|
42
43
|
rotate(angle: Val<number>, pivot?: Val<V>): this;
|
|
43
44
|
offset(dx: Val<number>, dy: Val<number>): this;
|
|
44
45
|
up(n: Val<number>): this;
|
|
@@ -52,27 +53,11 @@ export declare class Vec extends Cell<V> {
|
|
|
52
53
|
get x(): this extends import("../index.js").WritableBrand ? Writable<Num> : Num;
|
|
53
54
|
get y(): this extends import("../index.js").WritableBrand ? Writable<Num> : Num;
|
|
54
55
|
get magnitude(): Num;
|
|
55
|
-
/** Tween-builder
|
|
56
|
-
* receivers. */
|
|
56
|
+
/** Tween-builder. */
|
|
57
57
|
to(this: Writable<Vec>, target: V, dur: Val<number>, ease?: Easing): Tween<V>;
|
|
58
58
|
}
|
|
59
|
-
/** Writable `Vec` at `(x, y)`. Each axis is a literal
|
|
60
|
-
*
|
|
61
|
-
*
|
|
62
|
-
* reactive RO tracking, or `cell.value` to snapshot. Lock an axis with
|
|
63
|
-
* `Num.pin(c)`: `vec(slider, Num.pin(100))`. */
|
|
59
|
+
/** Writable `Vec` at `(x, y)`. Each axis is a literal (new cell) or existing
|
|
60
|
+
* writable (passed through); for read-only sources use `Vec.derive`. Lock an
|
|
61
|
+
* axis with `Num.pin`: `vec(slider, Num.pin(100))`. */
|
|
64
62
|
export declare function vec(x?: Init<Num>, y?: Init<Num>): Writable<Vec>;
|
|
65
|
-
/** Policy for `polar`'s inverse — which inputs absorb a write:
|
|
66
|
-
*
|
|
67
|
-
* - `rotate` — c fixed; write r and a to land on target.
|
|
68
|
-
* - `translate` — r and a fixed; shift c by Δ.
|
|
69
|
-
* - `radial` — c and a fixed; project the drag onto the ray.
|
|
70
|
-
* - `circular` — c and r fixed; project the drag onto the circle. */
|
|
71
|
-
export type PolarPolicy = "rotate" | "translate" | "radial" | "circular";
|
|
72
|
-
/** Vec at polar offset from `center`: `center + (r·cos a, r·sin a)`.
|
|
73
|
-
* Bidirectional; each input is a literal (lifted to a fresh seed) or an
|
|
74
|
-
* existing writable cell. RO inputs are rejected at the type level.
|
|
75
|
-
* `policy` selects which inputs absorb writes; lock one with
|
|
76
|
-
* `Num.pin(c)`: `polar(c, Num.pin(100), a)`. */
|
|
77
|
-
export declare function polar(center: Init<Vec>, r: Init<Num>, a: Init<Num>, policy?: PolarPolicy): Writable<Vec>;
|
|
78
63
|
export {};
|
package/dist/core/values/vec.js
CHANGED
|
@@ -1,10 +1,5 @@
|
|
|
1
|
-
// vec.ts — reactive 2D point.
|
|
2
|
-
//
|
|
3
|
-
// Invertibles return `: this` and ride on `Cell#lens(fwd, bwd)`;
|
|
4
|
-
// chained calls compose into a lens chain. Field-lens getters use
|
|
5
|
-
// `fieldLens()` (propagates writability); `cachedDerive()` wraps RO views.
|
|
6
1
|
import { tween } from "../../animation/index.js";
|
|
7
|
-
import { Cell, cachedDerive, fieldLens, reader, readNow,
|
|
2
|
+
import { Cell, cachedDerive, fieldLens, reader, readNow, } from "../cell.js";
|
|
8
3
|
import { Num, num } from "./num.js";
|
|
9
4
|
export const add = (a, b) => ({ x: a.x + b.x, y: a.y + b.y });
|
|
10
5
|
export const sub = (a, b) => ({ x: a.x - b.x, y: a.y - b.y });
|
|
@@ -51,7 +46,7 @@ export function tangentPoint(p, c, r, side = -1) {
|
|
|
51
46
|
const wrapToPi = (x) => x - 2 * Math.PI * Math.round(x / (2 * Math.PI));
|
|
52
47
|
/** Representative of cyclic angle `target` closest to `current`
|
|
53
48
|
* (shortest-arc inverse). */
|
|
54
|
-
const nearestAngle = (target, current) => current + wrapToPi(target - current);
|
|
49
|
+
export const nearestAngle = (target, current) => current + wrapToPi(target - current);
|
|
55
50
|
const linearImpl = { add, sub, scale };
|
|
56
51
|
const packImpl = {
|
|
57
52
|
dim: 2,
|
|
@@ -94,8 +89,7 @@ export class Vec extends Cell {
|
|
|
94
89
|
return { x: n.x + o.x, y: n.y + o.y };
|
|
95
90
|
});
|
|
96
91
|
}
|
|
97
|
-
/** Uniform scale by `k` about `pivot` (default origin).
|
|
98
|
-
* by `1/k`; exact bijection for `k ≠ 0`. */
|
|
92
|
+
/** Uniform scale by `k` about `pivot` (default origin). Invertible when k ≠ 0. */
|
|
99
93
|
scale(k, pivot) {
|
|
100
94
|
const kf = reader(k);
|
|
101
95
|
const pf = pivot === undefined ? undefined : reader(pivot);
|
|
@@ -107,8 +101,7 @@ export class Vec extends Cell {
|
|
|
107
101
|
return pf ? scaleAbout(n, pf(), 1 / k) : { x: n.x / k, y: n.y / k };
|
|
108
102
|
});
|
|
109
103
|
}
|
|
110
|
-
/** Rotate by `angle` (radians) about `pivot` (default origin).
|
|
111
|
-
* rotates by `−angle`; exact bijection. */
|
|
104
|
+
/** Rotate by `angle` (radians) about `pivot` (default origin). */
|
|
112
105
|
rotate(angle, pivot = ORIGIN) {
|
|
113
106
|
const af = reader(angle);
|
|
114
107
|
const pf = reader(pivot);
|
|
@@ -119,7 +112,6 @@ export class Vec extends Cell {
|
|
|
119
112
|
const yf = reader(dy);
|
|
120
113
|
return this.lens(v => ({ x: v.x + xf(), y: v.y + yf() }), n => ({ x: n.x - xf(), y: n.y - yf() }));
|
|
121
114
|
}
|
|
122
|
-
// Axis-aligned offset sugar — same fwd/bwd shape as offset.
|
|
123
115
|
up(n) {
|
|
124
116
|
const f = reader(n);
|
|
125
117
|
return this.lens(v => ({ x: v.x, y: v.y - f() }), o => ({ x: o.x, y: o.y + f() }));
|
|
@@ -157,68 +149,21 @@ export class Vec extends Cell {
|
|
|
157
149
|
get magnitude() {
|
|
158
150
|
return cachedDerive(this, "magnitude", Num, v => Math.hypot(v.x, v.y));
|
|
159
151
|
}
|
|
160
|
-
/** Tween-builder
|
|
161
|
-
* receivers. */
|
|
152
|
+
/** Tween-builder. */
|
|
162
153
|
to(target, dur, ease) {
|
|
163
154
|
return tween(this, target, dur, ease);
|
|
164
155
|
}
|
|
165
156
|
}
|
|
166
|
-
/**
|
|
167
|
-
* here after lifting literals. */
|
|
157
|
+
/** Lens combining two writable `Num`s into a `Vec`. */
|
|
168
158
|
function axes(x, y) {
|
|
169
|
-
// The view fully reconstructs both axes (1-arg bwd ⇒ no source read).
|
|
170
159
|
return Vec.lens([x, y], ([xv, yv]) => ({ x: xv, y: yv }), v => [v.x, v.y]);
|
|
171
160
|
}
|
|
172
|
-
/** Writable `Vec` at `(x, y)`. Each axis is a literal
|
|
173
|
-
*
|
|
174
|
-
*
|
|
175
|
-
* reactive RO tracking, or `cell.value` to snapshot. Lock an axis with
|
|
176
|
-
* `Num.pin(c)`: `vec(slider, Num.pin(100))`. */
|
|
161
|
+
/** Writable `Vec` at `(x, y)`. Each axis is a literal (new cell) or existing
|
|
162
|
+
* writable (passed through); for read-only sources use `Vec.derive`. Lock an
|
|
163
|
+
* axis with `Num.pin`: `vec(slider, Num.pin(100))`. */
|
|
177
164
|
export function vec(x = 0, y = 0) {
|
|
178
165
|
if (typeof x === "number" && typeof y === "number") {
|
|
179
166
|
return new Vec({ x, y });
|
|
180
167
|
}
|
|
181
168
|
return axes(num(x), num(y));
|
|
182
169
|
}
|
|
183
|
-
/** Vec at polar offset from `center`: `center + (r·cos a, r·sin a)`.
|
|
184
|
-
* Bidirectional; each input is a literal (lifted to a fresh seed) or an
|
|
185
|
-
* existing writable cell. RO inputs are rejected at the type level.
|
|
186
|
-
* `policy` selects which inputs absorb writes; lock one with
|
|
187
|
-
* `Num.pin(c)`: `polar(c, Num.pin(100), a)`. */
|
|
188
|
-
export function polar(center, r, a, policy = "rotate") {
|
|
189
|
-
// Lift literals; already-writable inputs pass through by identity.
|
|
190
|
-
const cSig = center instanceof Vec ? center : vec(center.x, center.y);
|
|
191
|
-
const rSig = num(r);
|
|
192
|
-
const aSig = num(a);
|
|
193
|
-
const project = (c, rv, av) => ({
|
|
194
|
-
x: c.x + rv * Math.cos(av),
|
|
195
|
-
y: c.y + rv * Math.sin(av),
|
|
196
|
-
});
|
|
197
|
-
let bwd;
|
|
198
|
-
switch (policy) {
|
|
199
|
-
case "rotate":
|
|
200
|
-
bwd = (p, [cv, , av]) => {
|
|
201
|
-
const dx = p.x - cv.x;
|
|
202
|
-
const dy = p.y - cv.y;
|
|
203
|
-
return [SKIP, Math.hypot(dx, dy), nearestAngle(Math.atan2(dy, dx), av)];
|
|
204
|
-
};
|
|
205
|
-
break;
|
|
206
|
-
case "translate":
|
|
207
|
-
bwd = (p, [cv, rv, av]) => {
|
|
208
|
-
const f = project(cv, rv, av);
|
|
209
|
-
return [{ x: cv.x + (p.x - f.x), y: cv.y + (p.y - f.y) }, SKIP, SKIP];
|
|
210
|
-
};
|
|
211
|
-
break;
|
|
212
|
-
case "radial":
|
|
213
|
-
bwd = (p, [cv, , av]) => {
|
|
214
|
-
const dx = p.x - cv.x;
|
|
215
|
-
const dy = p.y - cv.y;
|
|
216
|
-
return [SKIP, dx * Math.cos(av) + dy * Math.sin(av), SKIP];
|
|
217
|
-
};
|
|
218
|
-
break;
|
|
219
|
-
case "circular":
|
|
220
|
-
bwd = (p, [cv, , av]) => [SKIP, SKIP, nearestAngle(Math.atan2(p.y - cv.y, p.x - cv.x), av)];
|
|
221
|
-
break;
|
|
222
|
-
}
|
|
223
|
-
return Vec.lens([cSig, rSig, aSig], ([c, rv, av]) => project(c, rv, av), bwd);
|
|
224
|
-
}
|
package/dist/formats/lens.js
CHANGED
|
@@ -36,19 +36,16 @@ export function valueHub(initial) {
|
|
|
36
36
|
* the error regions. */
|
|
37
37
|
export function formatSpoke(hub, adapter) {
|
|
38
38
|
return lens(hub, {
|
|
39
|
-
init:
|
|
40
|
-
step: (
|
|
41
|
-
fwd: (
|
|
42
|
-
bwd: (target,
|
|
39
|
+
init: v => fromValue(adapter, v),
|
|
40
|
+
step: (v, c) => (v === c.synced ? c : absorb(adapter, c, v)),
|
|
41
|
+
fwd: (_v, c) => c.text,
|
|
42
|
+
bwd: (target, _v, c) => {
|
|
43
43
|
const { tree, errors } = adapter.parse(target);
|
|
44
44
|
if (errors.length === 0) {
|
|
45
45
|
const v = valueOf(tree);
|
|
46
|
-
return {
|
|
46
|
+
return { update: v, complement: { text: target, tree, errors, synced: v } };
|
|
47
47
|
}
|
|
48
|
-
return {
|
|
49
|
-
updates: [SKIP],
|
|
50
|
-
complement: { text: target, tree, errors, synced: c.synced },
|
|
51
|
-
};
|
|
48
|
+
return { update: SKIP, complement: { text: target, tree, errors, synced: c.synced } };
|
|
52
49
|
},
|
|
53
50
|
});
|
|
54
51
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,20 +1,9 @@
|
|
|
1
|
-
/** @group Reactivity */
|
|
2
|
-
/** @group Animation */
|
|
3
1
|
export * from "./animation/index.js";
|
|
4
|
-
/** @group Utilities */
|
|
5
2
|
export * from "./assert/index.js";
|
|
6
|
-
/** @group Rendering */
|
|
7
3
|
export { type CodeOpts, CodeShape, code, codeStyles, type Token, tokenize } from "./code/index.js";
|
|
8
|
-
/** @group Utilities */
|
|
9
|
-
export * from "./coll.js";
|
|
10
4
|
export * from "./core/index.js";
|
|
11
|
-
/** @group Utilities */
|
|
12
5
|
export * from "./ext/index.js";
|
|
13
|
-
/** @group Shapes */
|
|
14
6
|
export * from "./shapes/index.js";
|
|
15
|
-
/** @group Rendering */
|
|
16
7
|
export * from "./tex/index.js";
|
|
17
|
-
/** @group Utilities */
|
|
18
8
|
export { allNodes, atPath, isLeaf, leavesOf, node as treeNode, nodeCount, type TreeNode, walkTree, } from "./tree.js";
|
|
19
|
-
/** @group Web */
|
|
20
9
|
export * from "./web/index.js";
|
package/dist/index.js
CHANGED
|
@@ -1,22 +1,12 @@
|
|
|
1
|
-
|
|
2
|
-
/** @group Animation */
|
|
1
|
+
// API-reference groups are assigned by source folder in theme/group-by-source.mjs.
|
|
3
2
|
export * from "./animation/index.js";
|
|
4
|
-
/** @group Utilities */
|
|
5
3
|
export * from "./assert/index.js";
|
|
6
4
|
// `code` and `tex` both export `Part`; re-export `code`'s other symbols
|
|
7
5
|
// explicitly so the wildcard below lets `tex`'s `Part` win.
|
|
8
|
-
/** @group Rendering */
|
|
9
6
|
export { CodeShape, code, codeStyles, tokenize } from "./code/index.js";
|
|
10
|
-
/** @group Utilities */
|
|
11
|
-
export * from "./coll.js";
|
|
12
7
|
export * from "./core/index.js";
|
|
13
|
-
/** @group Utilities */
|
|
14
8
|
export * from "./ext/index.js";
|
|
15
|
-
/** @group Shapes */
|
|
16
9
|
export * from "./shapes/index.js";
|
|
17
|
-
/** @group Rendering */
|
|
18
10
|
export * from "./tex/index.js";
|
|
19
|
-
/** @group Utilities */
|
|
20
11
|
export { allNodes, atPath, isLeaf, leavesOf, node as treeNode, nodeCount, walkTree, } from "./tree.js";
|
|
21
|
-
/** @group Web */
|
|
22
12
|
export * from "./web/index.js";
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
// Development entry for the automatic JSX runtime. esbuild/tsc import this when
|
|
2
|
+
// `jsxDev` is enabled; the dev signature carries extra debug args we ignore, so
|
|
3
|
+
// `jsxDEV` just forwards to `jsx`.
|
|
4
|
+
export * from "./jsx-runtime.js";
|
|
5
|
+
export { jsx as jsxDEV } from "./jsx-runtime.js";
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { Cell } from "./core/cell.js";
|
|
2
|
+
/** Marker for `<>…</>`; lowered to `jsx(Fragment, …)`. */
|
|
3
|
+
export declare const Fragment: unique symbol;
|
|
4
|
+
type Disposer = () => void;
|
|
5
|
+
/** Register a teardown with the active scope (`mount` / `scope` / an `each`
|
|
6
|
+
* item) — for raw `effect`s or listeners created in a component body, which
|
|
7
|
+
* the JSX helpers otherwise track for you. No-op outside a scope. */
|
|
8
|
+
export declare function onCleanup(fn: Disposer): void;
|
|
9
|
+
type Props = Record<string, unknown> & {
|
|
10
|
+
children?: unknown;
|
|
11
|
+
};
|
|
12
|
+
type Component = (props: Props) => Node;
|
|
13
|
+
/** Build a DOM node for one JSX element. */
|
|
14
|
+
export declare function jsx(type: string | symbol | Component, props?: Props): Node;
|
|
15
|
+
/** Static-children variant; behaviourally identical in a runtime builder. */
|
|
16
|
+
export declare const jsxs: typeof jsx;
|
|
17
|
+
/** Render `component` into `host`, collecting reactive teardowns. The returned
|
|
18
|
+
* disposer releases them — call it on unmount (e.g. `disconnectedCallback`). */
|
|
19
|
+
export declare function mount(component: () => Node, host: Node): Disposer;
|
|
20
|
+
/** Run `fn` under a fresh reactive scope, returning its result and a disposer
|
|
21
|
+
* for every effect created during it — `mount` without a host. `each` gives
|
|
22
|
+
* each keyed item its own scope so its effects die when the item leaves. */
|
|
23
|
+
export declare function scope<T>(fn: () => T): [T, Disposer];
|
|
24
|
+
/** Keyed list rendering: keep `parent`'s children in sync with a reactive array,
|
|
25
|
+
* reusing and reordering nodes by key, disposing those that leave. Each item is
|
|
26
|
+
* rendered in its own `scope` (untracked from the list effect, so item-internal
|
|
27
|
+
* reads don't retrigger the whole list). Attach via `ref`:
|
|
28
|
+
* `<div ref={el => each(el, items, s => s.id, render)} />`. */
|
|
29
|
+
export declare function each<T>(parent: Element, items: Cell<T[]> | (() => readonly T[]), key: (item: T, index: number) => string, render: (item: T, index: number) => Node): void;
|
|
30
|
+
type Reactive<T> = T | Cell<T> | (() => T);
|
|
31
|
+
export declare namespace JSX {
|
|
32
|
+
export type Element = Node;
|
|
33
|
+
export interface ElementChildrenAttribute {
|
|
34
|
+
children: unknown;
|
|
35
|
+
}
|
|
36
|
+
export interface IntrinsicAttributes {
|
|
37
|
+
children?: unknown;
|
|
38
|
+
}
|
|
39
|
+
export interface CommonProps {
|
|
40
|
+
class?: Reactive<string>;
|
|
41
|
+
style?: Reactive<string | Partial<CSSStyleDeclaration>>;
|
|
42
|
+
id?: Reactive<string>;
|
|
43
|
+
lens?: Cell<any>;
|
|
44
|
+
ref?: (el: Element) => void;
|
|
45
|
+
children?: unknown;
|
|
46
|
+
[attr: string]: any;
|
|
47
|
+
}
|
|
48
|
+
type Tag = keyof HTMLElementTagNameMap | keyof SVGElementTagNameMap;
|
|
49
|
+
export type IntrinsicElements = {
|
|
50
|
+
[K in Tag]: CommonProps;
|
|
51
|
+
};
|
|
52
|
+
export {};
|
|
53
|
+
}
|
|
54
|
+
export {};
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
// jsx-runtime.ts — minimal runtime JSX for bireactive (no compiler step).
|
|
2
|
+
//
|
|
3
|
+
// esbuild's automatic runtime (`jsxImportSource: "@bireactive"`) lowers JSX to
|
|
4
|
+
// `jsx`/`jsxs`/`Fragment` calls; this module builds real DOM nodes and wires
|
|
5
|
+
// reactive props and children through `effect`. The one binding with no React
|
|
6
|
+
// analogue is the `lens` prop: a single bidirectional terminal — the dual of
|
|
7
|
+
// the value-plus-onInput pair — that reads a writable cell forward into the
|
|
8
|
+
// control and writes edits back, so a chain of lenses can be driven from the
|
|
9
|
+
// leaf. Components are plain `props → Node`; reactive expressions are passed as
|
|
10
|
+
// thunks (`{() => expr}`) or cells, since there is no compile step to wrap them.
|
|
11
|
+
import { Cell, effect, untracked } from "./core/cell.js";
|
|
12
|
+
/** Marker for `<>…</>`; lowered to `jsx(Fragment, …)`. */
|
|
13
|
+
export const Fragment = Symbol.for("bireactive.jsx.Fragment");
|
|
14
|
+
// The active mount scope: reactive teardowns created while building a tree are
|
|
15
|
+
// collected here so `mount` can release them all on unmount. Non-reentrant —
|
|
16
|
+
// `mount` saves/restores the previous owner around the render.
|
|
17
|
+
let currentOwner = null;
|
|
18
|
+
function track(d) {
|
|
19
|
+
currentOwner?.push(d);
|
|
20
|
+
}
|
|
21
|
+
/** Register a teardown with the active scope (`mount` / `scope` / an `each`
|
|
22
|
+
* item) — for raw `effect`s or listeners created in a component body, which
|
|
23
|
+
* the JSX helpers otherwise track for you. No-op outside a scope. */
|
|
24
|
+
export function onCleanup(fn) {
|
|
25
|
+
track(fn);
|
|
26
|
+
}
|
|
27
|
+
/** Build a DOM node for one JSX element. */
|
|
28
|
+
export function jsx(type, props) {
|
|
29
|
+
if (typeof type === "function")
|
|
30
|
+
return type(props ?? {});
|
|
31
|
+
if (type === Fragment) {
|
|
32
|
+
const frag = document.createDocumentFragment();
|
|
33
|
+
append(frag, props?.children);
|
|
34
|
+
return frag;
|
|
35
|
+
}
|
|
36
|
+
const el = document.createElement(type);
|
|
37
|
+
if (props)
|
|
38
|
+
for (const key in props)
|
|
39
|
+
applyProp(el, key, props[key]);
|
|
40
|
+
return el;
|
|
41
|
+
}
|
|
42
|
+
/** Static-children variant; behaviourally identical in a runtime builder. */
|
|
43
|
+
export const jsxs = jsx;
|
|
44
|
+
function applyProp(el, key, value) {
|
|
45
|
+
if (key === "children")
|
|
46
|
+
return append(el, value);
|
|
47
|
+
if (key === "ref") {
|
|
48
|
+
if (typeof value === "function")
|
|
49
|
+
value(el);
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
if (key === "lens")
|
|
53
|
+
return bindLens(el, value);
|
|
54
|
+
if (key.startsWith("on") && typeof value === "function") {
|
|
55
|
+
el.addEventListener(key.slice(2).toLowerCase(), value);
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
if (value instanceof Cell) {
|
|
59
|
+
track(effect(() => setProp(el, key, value.value)));
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
if (typeof value === "function") {
|
|
63
|
+
track(effect(() => setProp(el, key, value())));
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
setProp(el, key, value);
|
|
67
|
+
}
|
|
68
|
+
function setProp(el, key, value) {
|
|
69
|
+
if (key === "class" || key === "className") {
|
|
70
|
+
el.setAttribute("class", value == null ? "" : String(value));
|
|
71
|
+
}
|
|
72
|
+
else if (key === "style") {
|
|
73
|
+
if (value && typeof value === "object")
|
|
74
|
+
Object.assign(el.style, value);
|
|
75
|
+
else
|
|
76
|
+
el.setAttribute("style", value == null ? "" : String(value));
|
|
77
|
+
}
|
|
78
|
+
else if (key === "value") {
|
|
79
|
+
el.value = value == null ? "" : String(value);
|
|
80
|
+
}
|
|
81
|
+
else if (key === "checked" || key === "disabled" || key === "selected") {
|
|
82
|
+
// biome-ignore lint/suspicious/noExplicitAny: boolean DOM properties
|
|
83
|
+
el[key] = !!value;
|
|
84
|
+
}
|
|
85
|
+
else if (value == null || value === false) {
|
|
86
|
+
el.removeAttribute(key);
|
|
87
|
+
}
|
|
88
|
+
else {
|
|
89
|
+
el.setAttribute(key, value === true ? "" : String(value));
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
/** Append a child (array / Node / text / cell / thunk) to `parent`. */
|
|
93
|
+
function append(parent, child) {
|
|
94
|
+
if (Array.isArray(child)) {
|
|
95
|
+
for (const c of child)
|
|
96
|
+
append(parent, c);
|
|
97
|
+
}
|
|
98
|
+
else if (child instanceof Node) {
|
|
99
|
+
parent.appendChild(child);
|
|
100
|
+
}
|
|
101
|
+
else if (child instanceof Cell) {
|
|
102
|
+
parent.appendChild(reactiveText(() => child.value));
|
|
103
|
+
}
|
|
104
|
+
else if (typeof child === "function") {
|
|
105
|
+
parent.appendChild(reactiveText(child));
|
|
106
|
+
}
|
|
107
|
+
else if (child != null && child !== false && child !== true) {
|
|
108
|
+
parent.appendChild(document.createTextNode(String(child)));
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
/** A text node whose content tracks `get()`. Primitive children only — the
|
|
112
|
+
* minimal runtime does not reconcile dynamic element children. */
|
|
113
|
+
function reactiveText(get) {
|
|
114
|
+
const node = document.createTextNode("");
|
|
115
|
+
track(effect(() => {
|
|
116
|
+
const v = get();
|
|
117
|
+
node.data = v == null ? "" : String(v);
|
|
118
|
+
}));
|
|
119
|
+
return node;
|
|
120
|
+
}
|
|
121
|
+
/** Two-way bind a form control to a writable cell: read forward into the
|
|
122
|
+
* control, write back on input. The forward write is skipped while the
|
|
123
|
+
* control is focused, so a live edit is never clobbered mid-drag (the
|
|
124
|
+
* controlled-input focus guard, written once). */
|
|
125
|
+
function bindLens(el, lens) {
|
|
126
|
+
const checkbox = el.type === "checkbox";
|
|
127
|
+
track(effect(() => {
|
|
128
|
+
const v = lens.value;
|
|
129
|
+
if (checkbox) {
|
|
130
|
+
el.checked = !!v;
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
const next = v == null ? "" : String(v);
|
|
134
|
+
const root = el.getRootNode();
|
|
135
|
+
if (root.activeElement !== el && el.value !== next)
|
|
136
|
+
el.value = next;
|
|
137
|
+
}));
|
|
138
|
+
const evt = checkbox || el.tagName === "SELECT" ? "change" : "input";
|
|
139
|
+
el.addEventListener(evt, () => {
|
|
140
|
+
lens.value = checkbox
|
|
141
|
+
? el.checked
|
|
142
|
+
: el.type === "range" || el.type === "number"
|
|
143
|
+
? Number(el.value)
|
|
144
|
+
: el.value;
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
/** Render `component` into `host`, collecting reactive teardowns. The returned
|
|
148
|
+
* disposer releases them — call it on unmount (e.g. `disconnectedCallback`). */
|
|
149
|
+
export function mount(component, host) {
|
|
150
|
+
const [node, dispose] = scope(component);
|
|
151
|
+
host.appendChild(node);
|
|
152
|
+
return dispose;
|
|
153
|
+
}
|
|
154
|
+
/** Run `fn` under a fresh reactive scope, returning its result and a disposer
|
|
155
|
+
* for every effect created during it — `mount` without a host. `each` gives
|
|
156
|
+
* each keyed item its own scope so its effects die when the item leaves. */
|
|
157
|
+
export function scope(fn) {
|
|
158
|
+
const prev = currentOwner;
|
|
159
|
+
const owner = [];
|
|
160
|
+
currentOwner = owner;
|
|
161
|
+
try {
|
|
162
|
+
return [
|
|
163
|
+
fn(),
|
|
164
|
+
() => {
|
|
165
|
+
for (const d of owner)
|
|
166
|
+
d();
|
|
167
|
+
owner.length = 0;
|
|
168
|
+
},
|
|
169
|
+
];
|
|
170
|
+
}
|
|
171
|
+
finally {
|
|
172
|
+
currentOwner = prev;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
/** Keyed list rendering: keep `parent`'s children in sync with a reactive array,
|
|
176
|
+
* reusing and reordering nodes by key, disposing those that leave. Each item is
|
|
177
|
+
* rendered in its own `scope` (untracked from the list effect, so item-internal
|
|
178
|
+
* reads don't retrigger the whole list). Attach via `ref`:
|
|
179
|
+
* `<div ref={el => each(el, items, s => s.id, render)} />`. */
|
|
180
|
+
export function each(parent, items, key, render) {
|
|
181
|
+
const read = typeof items === "function" ? items : () => items.value;
|
|
182
|
+
const cache = new Map();
|
|
183
|
+
const stop = effect(() => {
|
|
184
|
+
const arr = read();
|
|
185
|
+
const seen = new Set();
|
|
186
|
+
const nodes = [];
|
|
187
|
+
arr.forEach((item, i) => {
|
|
188
|
+
const k = key(item, i);
|
|
189
|
+
seen.add(k);
|
|
190
|
+
let entry = cache.get(k);
|
|
191
|
+
if (entry === undefined) {
|
|
192
|
+
const [node, dispose] = untracked(() => scope(() => render(item, i)));
|
|
193
|
+
entry = { node, dispose };
|
|
194
|
+
cache.set(k, entry);
|
|
195
|
+
}
|
|
196
|
+
nodes.push(entry.node);
|
|
197
|
+
});
|
|
198
|
+
for (const [k, entry] of cache) {
|
|
199
|
+
if (!seen.has(k)) {
|
|
200
|
+
entry.dispose();
|
|
201
|
+
cache.delete(k);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
// Only touch the DOM when the ordered node set actually changed — re-inserting
|
|
205
|
+
// identical children mid-interaction would steal focus and reset clicks.
|
|
206
|
+
const cur = parent.childNodes;
|
|
207
|
+
let same = cur.length === nodes.length;
|
|
208
|
+
for (let i = 0; same && i < nodes.length; i++)
|
|
209
|
+
same = cur[i] === nodes[i];
|
|
210
|
+
if (!same)
|
|
211
|
+
parent.replaceChildren(...nodes);
|
|
212
|
+
});
|
|
213
|
+
track(() => {
|
|
214
|
+
stop();
|
|
215
|
+
for (const entry of cache.values())
|
|
216
|
+
entry.dispose();
|
|
217
|
+
cache.clear();
|
|
218
|
+
});
|
|
219
|
+
}
|