@weasel-js/d3 0.5.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 +23 -0
- package/dist/index.d.ts +84 -0
- package/dist/index.js +333 -0
- package/dist/index.js.map +1 -0
- package/package.json +57 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 orochi235
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# @weasel-js/d3
|
|
2
|
+
|
|
3
|
+
d3 bridge for weasel: data-join + transition chain over useScene and useAnimator. d3-force already integrates via useSimulation in the main kit.
|
|
4
|
+
|
|
5
|
+
Part of [weasel](https://github.com/orochi235/weasel), a domain-agnostic 2D
|
|
6
|
+
scene-graph canvas kit for React. See the
|
|
7
|
+
[API reference](https://orochi235.github.io/weasel/api/).
|
|
8
|
+
|
|
9
|
+
## Install
|
|
10
|
+
|
|
11
|
+
```sh
|
|
12
|
+
npm install @weasel-js/d3
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Usage
|
|
16
|
+
|
|
17
|
+
```ts
|
|
18
|
+
import { /* … */ } from '@weasel-js/d3';
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## License
|
|
22
|
+
|
|
23
|
+
MIT
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { Animator, PoseProjection, NodeId, Scene } from '@weasel-js/core';
|
|
2
|
+
|
|
3
|
+
interface BindOptions<TData, TPose> {
|
|
4
|
+
/** Stable id derivation per datum — the binding diffs old vs new state by this key. */
|
|
5
|
+
key: (d: TData, i: number) => string;
|
|
6
|
+
/** Which scene layer new leaves enter on. Defaults to the scene's first system layer. */
|
|
7
|
+
layer?: string;
|
|
8
|
+
/** Animator used by `.transition()` chains. Optional — only required if a
|
|
9
|
+
* transition is actually spawned. Calling `.transition()` without it throws. */
|
|
10
|
+
animator?: Animator;
|
|
11
|
+
/** Pose descriptor with `lerp(from, to, t)`. Default `RECT_POSE_DESCRIPTOR`
|
|
12
|
+
* (interpolates x/y/width/height linearly). Override for non-rect poses
|
|
13
|
+
* (e.g. `pathPoseDescriptor`). */
|
|
14
|
+
geometry?: PoseProjection<TPose>;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Builder produced by `d3Bind`. Configure with `.pose()` / `.data()` / `.enterFrom()`,
|
|
18
|
+
* then call `.join()` to emit the diff as one batched scene mutation and receive
|
|
19
|
+
* a chainable `D3Selection`.
|
|
20
|
+
*/
|
|
21
|
+
interface D3Binding<TData, TPose> {
|
|
22
|
+
/** Per-datum pose. Called for both enter (initial scene pose) and update (target). */
|
|
23
|
+
pose(fn: (d: TData, i: number) => TPose): this;
|
|
24
|
+
/** Per-datum data payload. Replaces the leaf's existing data on update. */
|
|
25
|
+
data(fn: (d: TData, i: number) => Record<string, unknown>): this;
|
|
26
|
+
/** Optional initial pose for ENTER nodes — `.transition()` will animate from this
|
|
27
|
+
* to the declared `.pose()`. Defaults to the same pose (entries snap in). */
|
|
28
|
+
enterFrom(fn: (d: TData, i: number) => TPose): this;
|
|
29
|
+
/** Emit the diff against the scene as one batched op group. Returns the merged
|
|
30
|
+
* selection (enter + update). Order of the selection matches the order of the
|
|
31
|
+
* bound `data` array. */
|
|
32
|
+
join(): D3Selection<TData, TPose>;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* The bound, post-join set of leaves. Carries per-node prior poses (used by
|
|
36
|
+
* `.transition()` to compute the tween `from`) and a stable id-per-datum mapping.
|
|
37
|
+
*/
|
|
38
|
+
interface D3Selection<TData, TPose> {
|
|
39
|
+
/** Node ids in the same order as the bound data. */
|
|
40
|
+
readonly ids: readonly NodeId[];
|
|
41
|
+
/** Bound data, in registration order. */
|
|
42
|
+
readonly data: readonly TData[];
|
|
43
|
+
/** Filter to a subset by predicate. The returned selection retains the prior
|
|
44
|
+
* poses for transition purposes. */
|
|
45
|
+
filter(pred: (d: TData, i: number) => boolean): D3Selection<TData, TPose>;
|
|
46
|
+
/** Iterate. Returns this for chaining. */
|
|
47
|
+
each(fn: (d: TData, id: NodeId, i: number) => void): this;
|
|
48
|
+
/** Spawn a transition. Throws if `BindOptions.animator` wasn't supplied. */
|
|
49
|
+
transition(name?: string): D3Transition<TData>;
|
|
50
|
+
/** Cancel any in-flight transitions matching `name` (or all if omitted) on these nodes. */
|
|
51
|
+
interrupt(name?: string): this;
|
|
52
|
+
}
|
|
53
|
+
/** Transition handle backed by the kit's `useAnimator`. */
|
|
54
|
+
interface D3Transition<TData> {
|
|
55
|
+
duration(ms: number): this;
|
|
56
|
+
ease(fn: (t: number) => number): this;
|
|
57
|
+
delay(ms: number | ((d: TData, i: number) => number)): this;
|
|
58
|
+
tween<TValue>(opts: {
|
|
59
|
+
name: string;
|
|
60
|
+
from: (d: TData, i: number) => TValue;
|
|
61
|
+
to: (d: TData, i: number) => TValue;
|
|
62
|
+
interpolate?: (from: TValue, to: TValue) => (t: number) => TValue;
|
|
63
|
+
apply: (d: TData, id: NodeId, value: TValue) => void;
|
|
64
|
+
}): this;
|
|
65
|
+
on(event: 'start' | 'end' | 'interrupt', fn: () => void): this;
|
|
66
|
+
end(): Promise<void>;
|
|
67
|
+
interrupt(): void;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Bind a data array to a scene. Returns a builder; configure with `.pose()` /
|
|
72
|
+
* `.data()` / `.enterFrom()`, then call `.join()` to emit the diff.
|
|
73
|
+
*
|
|
74
|
+
* Diff semantics (by `options.key`):
|
|
75
|
+
* - data has key not in scene → ENTER (scene.add)
|
|
76
|
+
* - data has key in scene → UPDATE (scene.setPose + scene.update)
|
|
77
|
+
* - scene has leaf on the target layer with key not in data → EXIT (scene.remove)
|
|
78
|
+
*
|
|
79
|
+
* All mutations dispatch through `scene.batch('d3Bind.join', ...)` so one join
|
|
80
|
+
* is one undo entry.
|
|
81
|
+
*/
|
|
82
|
+
declare function d3Bind<TData, TLayer extends string, TPose>(scene: Scene<unknown, TLayer, TPose>, data: readonly TData[], options: BindOptions<TData, TPose>): D3Binding<TData, TPose>;
|
|
83
|
+
|
|
84
|
+
export { type BindOptions, type D3Binding, type D3Selection, type D3Transition, d3Bind };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,333 @@
|
|
|
1
|
+
import { asNodeId, RECT_POSE_DESCRIPTOR } from '@weasel-js/core';
|
|
2
|
+
|
|
3
|
+
// src/bind.ts
|
|
4
|
+
|
|
5
|
+
// src/transition.ts
|
|
6
|
+
function createTransition(ctx) {
|
|
7
|
+
let duration = 250;
|
|
8
|
+
let easing;
|
|
9
|
+
let delayFn = null;
|
|
10
|
+
let started = false;
|
|
11
|
+
const customTweens = [];
|
|
12
|
+
const onStart = [];
|
|
13
|
+
const onEnd = [];
|
|
14
|
+
const onInterrupt = [];
|
|
15
|
+
let pendingCount = 0;
|
|
16
|
+
let cancelled = false;
|
|
17
|
+
let endResolved = false;
|
|
18
|
+
let resolveEnd = null;
|
|
19
|
+
const endPromise = new Promise((resolve) => {
|
|
20
|
+
resolveEnd = resolve;
|
|
21
|
+
});
|
|
22
|
+
const handles = [];
|
|
23
|
+
const pendingDelays = /* @__PURE__ */ new Set();
|
|
24
|
+
const settleIfDone = () => {
|
|
25
|
+
if (pendingCount === 0 && !endResolved) {
|
|
26
|
+
endResolved = true;
|
|
27
|
+
if (!cancelled) onEnd.forEach((fn) => fn());
|
|
28
|
+
resolveEnd?.();
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
const ensureStarted = () => {
|
|
32
|
+
if (started) return;
|
|
33
|
+
started = true;
|
|
34
|
+
const { scene, geometry, ids, data, priorPoses, name } = ctx;
|
|
35
|
+
const lerp = geometry.lerp;
|
|
36
|
+
if (!lerp) {
|
|
37
|
+
throw new Error(
|
|
38
|
+
"d3Bind.transition: provided geometry has no `lerp` \u2014 pass a PoseProjection with lerp in BindOptions"
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
const targetPoses = /* @__PURE__ */ new Map();
|
|
42
|
+
for (const id of ids) {
|
|
43
|
+
const node = scene.get(id);
|
|
44
|
+
if (node) targetPoses.set(id, node.pose);
|
|
45
|
+
}
|
|
46
|
+
for (const id of ids) {
|
|
47
|
+
const from = priorPoses.get(id);
|
|
48
|
+
if (from !== void 0) scene.setPose(id, from);
|
|
49
|
+
}
|
|
50
|
+
let anySpawned = false;
|
|
51
|
+
for (let i = 0; i < ids.length; i++) {
|
|
52
|
+
const id = ids[i];
|
|
53
|
+
const d = data[i];
|
|
54
|
+
const from = priorPoses.get(id);
|
|
55
|
+
const to = targetPoses.get(id);
|
|
56
|
+
if (from === void 0 || to === void 0) continue;
|
|
57
|
+
const delayMs = delayFn ? Math.max(0, delayFn(d, i)) : 0;
|
|
58
|
+
const spawnPoseTween = () => {
|
|
59
|
+
if (cancelled) return;
|
|
60
|
+
anySpawned = true;
|
|
61
|
+
const handle = ctx.animator.tween({
|
|
62
|
+
from,
|
|
63
|
+
to,
|
|
64
|
+
ms: duration,
|
|
65
|
+
easing,
|
|
66
|
+
cancelKey: `d3-transition:${name}:${id}`,
|
|
67
|
+
interpolate: (a, b, t2) => lerp(a, b, t2),
|
|
68
|
+
onTick: (pose) => {
|
|
69
|
+
scene.setPose(id, pose);
|
|
70
|
+
},
|
|
71
|
+
onDone: () => {
|
|
72
|
+
pendingCount--;
|
|
73
|
+
settleIfDone();
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
handles.push(handle);
|
|
77
|
+
};
|
|
78
|
+
pendingCount++;
|
|
79
|
+
if (delayMs > 0) {
|
|
80
|
+
const timer = setTimeout(() => {
|
|
81
|
+
pendingDelays.delete(timer);
|
|
82
|
+
spawnPoseTween();
|
|
83
|
+
}, delayMs);
|
|
84
|
+
pendingDelays.add(timer);
|
|
85
|
+
} else {
|
|
86
|
+
spawnPoseTween();
|
|
87
|
+
}
|
|
88
|
+
for (const ct of customTweens) {
|
|
89
|
+
const fromVal = ct.from(d, i);
|
|
90
|
+
const toVal = ct.to(d, i);
|
|
91
|
+
const spawnCustom = () => {
|
|
92
|
+
if (cancelled) return;
|
|
93
|
+
anySpawned = true;
|
|
94
|
+
const handle = ctx.animator.tween({
|
|
95
|
+
from: fromVal,
|
|
96
|
+
to: toVal,
|
|
97
|
+
ms: duration,
|
|
98
|
+
easing,
|
|
99
|
+
cancelKey: `d3-transition:${name}:${id}:${ct.name}`,
|
|
100
|
+
interpolator: ct.interpolate,
|
|
101
|
+
interpolate: ct.interpolate ? void 0 : ((a, b, t2) => {
|
|
102
|
+
if (typeof a === "number" && typeof b === "number") {
|
|
103
|
+
return a + (b - a) * t2;
|
|
104
|
+
}
|
|
105
|
+
throw new Error(
|
|
106
|
+
`d3Bind.transition.tween("${ct.name}"): non-numeric value without an interpolate factory`
|
|
107
|
+
);
|
|
108
|
+
}),
|
|
109
|
+
onTick: (value) => ct.apply(d, id, value),
|
|
110
|
+
onDone: () => {
|
|
111
|
+
pendingCount--;
|
|
112
|
+
settleIfDone();
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
handles.push(handle);
|
|
116
|
+
};
|
|
117
|
+
pendingCount++;
|
|
118
|
+
if (delayMs > 0) {
|
|
119
|
+
const timer = setTimeout(() => {
|
|
120
|
+
pendingDelays.delete(timer);
|
|
121
|
+
spawnCustom();
|
|
122
|
+
}, delayMs);
|
|
123
|
+
pendingDelays.add(timer);
|
|
124
|
+
} else {
|
|
125
|
+
spawnCustom();
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
if (anySpawned || pendingDelays.size > 0) {
|
|
130
|
+
onStart.forEach((fn) => fn());
|
|
131
|
+
} else {
|
|
132
|
+
settleIfDone();
|
|
133
|
+
}
|
|
134
|
+
};
|
|
135
|
+
const t = {
|
|
136
|
+
duration(ms) {
|
|
137
|
+
duration = ms;
|
|
138
|
+
return t;
|
|
139
|
+
},
|
|
140
|
+
ease(fn) {
|
|
141
|
+
easing = fn;
|
|
142
|
+
return t;
|
|
143
|
+
},
|
|
144
|
+
delay(arg) {
|
|
145
|
+
delayFn = typeof arg === "function" ? arg : () => arg;
|
|
146
|
+
return t;
|
|
147
|
+
},
|
|
148
|
+
tween(opts) {
|
|
149
|
+
customTweens.push({
|
|
150
|
+
name: opts.name,
|
|
151
|
+
from: opts.from,
|
|
152
|
+
to: opts.to,
|
|
153
|
+
interpolate: opts.interpolate,
|
|
154
|
+
apply: opts.apply
|
|
155
|
+
});
|
|
156
|
+
return t;
|
|
157
|
+
},
|
|
158
|
+
on(event, fn) {
|
|
159
|
+
if (event === "start") onStart.push(fn);
|
|
160
|
+
else if (event === "end") onEnd.push(fn);
|
|
161
|
+
else if (event === "interrupt") onInterrupt.push(fn);
|
|
162
|
+
return t;
|
|
163
|
+
},
|
|
164
|
+
interrupt() {
|
|
165
|
+
if (cancelled || endResolved) return;
|
|
166
|
+
cancelled = true;
|
|
167
|
+
for (const timer of pendingDelays) clearTimeout(timer);
|
|
168
|
+
pendingDelays.clear();
|
|
169
|
+
for (const h of handles) h.cancel();
|
|
170
|
+
handles.length = 0;
|
|
171
|
+
onInterrupt.forEach((fn) => fn());
|
|
172
|
+
pendingCount = 0;
|
|
173
|
+
if (!endResolved) {
|
|
174
|
+
endResolved = true;
|
|
175
|
+
resolveEnd?.();
|
|
176
|
+
}
|
|
177
|
+
},
|
|
178
|
+
end() {
|
|
179
|
+
ensureStarted();
|
|
180
|
+
return endPromise;
|
|
181
|
+
}
|
|
182
|
+
};
|
|
183
|
+
return t;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// src/bind.ts
|
|
187
|
+
function d3Bind(scene, data, options) {
|
|
188
|
+
let poseFn = null;
|
|
189
|
+
let dataFn = null;
|
|
190
|
+
let enterFromFn = null;
|
|
191
|
+
const binding = {
|
|
192
|
+
pose(fn) {
|
|
193
|
+
poseFn = fn;
|
|
194
|
+
return binding;
|
|
195
|
+
},
|
|
196
|
+
data(fn) {
|
|
197
|
+
dataFn = fn;
|
|
198
|
+
return binding;
|
|
199
|
+
},
|
|
200
|
+
enterFrom(fn) {
|
|
201
|
+
enterFromFn = fn;
|
|
202
|
+
return binding;
|
|
203
|
+
},
|
|
204
|
+
join() {
|
|
205
|
+
const layer = options.layer ?? scene.layers[0]?.id;
|
|
206
|
+
if (!layer) {
|
|
207
|
+
throw new Error("d3Bind: scene has no layers; pass `layer` in options");
|
|
208
|
+
}
|
|
209
|
+
const dataKeys = data.map((d, i) => asNodeId(options.key(d, i)));
|
|
210
|
+
const dataKeySet = new Set(dataKeys);
|
|
211
|
+
const enterIndices = [];
|
|
212
|
+
const updateIndices = [];
|
|
213
|
+
const exitIds = [];
|
|
214
|
+
const sceneLeafIdsOnLayer = /* @__PURE__ */ new Set();
|
|
215
|
+
for (const node of scene.nodes.values()) {
|
|
216
|
+
if (node.kind === "leaf" && node.layer === layer) {
|
|
217
|
+
sceneLeafIdsOnLayer.add(node.id);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
data.forEach((_, i) => {
|
|
221
|
+
if (sceneLeafIdsOnLayer.has(dataKeys[i])) updateIndices.push(i);
|
|
222
|
+
else enterIndices.push(i);
|
|
223
|
+
});
|
|
224
|
+
sceneLeafIdsOnLayer.forEach((id) => {
|
|
225
|
+
if (!dataKeySet.has(id)) exitIds.push(id);
|
|
226
|
+
});
|
|
227
|
+
const priorPoses = /* @__PURE__ */ new Map();
|
|
228
|
+
for (const i of updateIndices) {
|
|
229
|
+
const id = dataKeys[i];
|
|
230
|
+
const node = scene.get(id);
|
|
231
|
+
if (node) priorPoses.set(id, node.pose);
|
|
232
|
+
}
|
|
233
|
+
for (const i of enterIndices) {
|
|
234
|
+
const id = dataKeys[i];
|
|
235
|
+
if (enterFromFn) {
|
|
236
|
+
priorPoses.set(id, enterFromFn(data[i], i));
|
|
237
|
+
} else if (poseFn) {
|
|
238
|
+
priorPoses.set(id, poseFn(data[i], i));
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
scene.batch("d3Bind.join", () => {
|
|
242
|
+
for (const i of enterIndices) {
|
|
243
|
+
const id = dataKeys[i];
|
|
244
|
+
const pose = poseFn ? poseFn(data[i], i) : void 0;
|
|
245
|
+
const payload = dataFn ? dataFn(data[i], i) : {};
|
|
246
|
+
if (pose === void 0) {
|
|
247
|
+
throw new Error(
|
|
248
|
+
"d3Bind.join: enter node has no pose. Call `.pose(fn)` before `.join()`."
|
|
249
|
+
);
|
|
250
|
+
}
|
|
251
|
+
scene.add({
|
|
252
|
+
id,
|
|
253
|
+
kind: "leaf",
|
|
254
|
+
layer,
|
|
255
|
+
pose,
|
|
256
|
+
// The scene is typed `Scene<unknown, TLayer, TPose>` — payload is
|
|
257
|
+
// unknown to the kit and consumer's drawOne reads it as a typed
|
|
258
|
+
// record.
|
|
259
|
+
data: payload
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
for (const i of updateIndices) {
|
|
263
|
+
const id = dataKeys[i];
|
|
264
|
+
if (poseFn) scene.setPose(id, poseFn(data[i], i));
|
|
265
|
+
if (dataFn) scene.update(id, { data: dataFn(data[i], i) });
|
|
266
|
+
}
|
|
267
|
+
for (const id of exitIds) {
|
|
268
|
+
scene.remove(id);
|
|
269
|
+
}
|
|
270
|
+
});
|
|
271
|
+
return createSelection(
|
|
272
|
+
scene,
|
|
273
|
+
dataKeys,
|
|
274
|
+
[...data],
|
|
275
|
+
priorPoses,
|
|
276
|
+
options.animator,
|
|
277
|
+
options.geometry
|
|
278
|
+
);
|
|
279
|
+
}
|
|
280
|
+
};
|
|
281
|
+
return binding;
|
|
282
|
+
}
|
|
283
|
+
function createSelection(scene, ids, data, priorPoses, animator, geometry) {
|
|
284
|
+
const sel = {
|
|
285
|
+
ids,
|
|
286
|
+
data,
|
|
287
|
+
filter(pred) {
|
|
288
|
+
const subset = [];
|
|
289
|
+
const subData = [];
|
|
290
|
+
for (let i = 0; i < data.length; i++) {
|
|
291
|
+
if (pred(data[i], i)) {
|
|
292
|
+
subset.push(ids[i]);
|
|
293
|
+
subData.push(data[i]);
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
return createSelection(scene, subset, subData, priorPoses, animator, geometry);
|
|
297
|
+
},
|
|
298
|
+
each(fn) {
|
|
299
|
+
for (let i = 0; i < ids.length; i++) fn(data[i], ids[i], i);
|
|
300
|
+
return sel;
|
|
301
|
+
},
|
|
302
|
+
transition(name) {
|
|
303
|
+
if (!animator) {
|
|
304
|
+
throw new Error(
|
|
305
|
+
"d3Bind.transition: pass `animator` in BindOptions to enable transitions"
|
|
306
|
+
);
|
|
307
|
+
}
|
|
308
|
+
const resolvedGeometry = geometry ?? RECT_POSE_DESCRIPTOR;
|
|
309
|
+
return createTransition({
|
|
310
|
+
scene,
|
|
311
|
+
animator,
|
|
312
|
+
geometry: resolvedGeometry,
|
|
313
|
+
ids,
|
|
314
|
+
data,
|
|
315
|
+
priorPoses,
|
|
316
|
+
name: name ?? ""
|
|
317
|
+
});
|
|
318
|
+
},
|
|
319
|
+
interrupt(name) {
|
|
320
|
+
if (!animator) return sel;
|
|
321
|
+
const transitionName = name ?? "";
|
|
322
|
+
for (const id of ids) {
|
|
323
|
+
animator.cancelKey(`d3-transition:${transitionName}:${id}`);
|
|
324
|
+
}
|
|
325
|
+
return sel;
|
|
326
|
+
}
|
|
327
|
+
};
|
|
328
|
+
return sel;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
export { d3Bind };
|
|
332
|
+
//# sourceMappingURL=index.js.map
|
|
333
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/transition.ts","../src/bind.ts"],"names":["t"],"mappings":"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","file":"index.js","sourcesContent":["import type {\n Animator,\n AnimationHandle,\n EasingFn,\n NodeId,\n PoseProjection,\n Scene,\n} from '@weasel-js/core';\nimport type { D3Transition } from './types';\n\ninterface TransitionCtx<TData, TPose> {\n scene: Scene<unknown, string, TPose>;\n animator: Animator;\n geometry: PoseProjection<TPose>;\n ids: readonly NodeId[];\n data: readonly TData[];\n priorPoses: ReadonlyMap<NodeId, TPose>;\n name: string;\n}\n\ninterface CustomTween<TData> {\n name: string;\n from: (d: TData, i: number) => unknown;\n to: (d: TData, i: number) => unknown;\n interpolate?: (from: unknown, to: unknown) => (t: number) => unknown;\n apply: (d: TData, id: NodeId, value: unknown) => void;\n}\n\n/**\n * Build a transition handle. The transition is *lazy*: tweens don't spawn\n * until `.end()` is called (or `await`-ed). This lets the consumer chain\n * `.duration().ease().delay()...` after `.transition()` without having\n * to declare them in advance.\n *\n * For each selected node, one pose tween is spawned from `priorPoses[id]` →\n * current scene pose. Custom `.tween()` declarations spawn one extra tween\n * per node per declaration, using the kit's animator factory-interpolator\n * slot when an `interpolate` factory is provided.\n *\n * `interrupt()` cancels via the kit's `animator.cancelKey`. The cancelKey\n * format is `d3-transition:<name>:<nodeId>` for pose tweens and\n * `d3-transition:<name>:<nodeId>:<tweenName>` for custom tweens, so\n * `.interrupt(name)` on the selection cancels every tween in this\n * transition by namespace.\n */\nexport function createTransition<TData, TPose>(\n ctx: TransitionCtx<TData, TPose>,\n): D3Transition<TData> {\n let duration = 250;\n let easing: EasingFn | undefined;\n let delayFn: ((d: TData, i: number) => number) | null = null;\n let started = false;\n const customTweens: Array<CustomTween<TData>> = [];\n const onStart: Array<() => void> = [];\n const onEnd: Array<() => void> = [];\n const onInterrupt: Array<() => void> = [];\n\n let pendingCount = 0;\n let cancelled = false;\n let endResolved = false;\n let resolveEnd: (() => void) | null = null;\n const endPromise = new Promise<void>((resolve) => {\n resolveEnd = resolve;\n });\n const handles: AnimationHandle[] = [];\n const pendingDelays = new Set<ReturnType<typeof setTimeout>>();\n\n const settleIfDone = (): void => {\n if (pendingCount === 0 && !endResolved) {\n endResolved = true;\n if (!cancelled) onEnd.forEach((fn) => fn());\n resolveEnd?.();\n }\n };\n\n const ensureStarted = (): void => {\n if (started) return;\n started = true;\n\n const { scene, geometry, ids, data, priorPoses, name } = ctx;\n const lerp = geometry.lerp;\n if (!lerp) {\n throw new Error(\n 'd3Bind.transition: provided geometry has no `lerp` — pass a PoseProjection with lerp in BindOptions',\n );\n }\n\n // Snapshot target poses NOW (post-join scene state) for each selected id.\n const targetPoses = new Map<NodeId, TPose>();\n for (const id of ids) {\n const node = scene.get(id);\n if (node) targetPoses.set(id, node.pose);\n }\n\n // Eagerly write the from-pose for each selected node BEFORE any tween\n // spawns. Without this:\n // - Non-delayed tweens flash a single frame of the post-join pose\n // before the animator's first tick writes from-state.\n // - Delayed tweens show the post-join pose for the entire delay window.\n // The eager write produces one extra setPose per node in the undo log;\n // history-bypass for transition ticks is a v2 follow-up (see spec).\n for (const id of ids) {\n const from = priorPoses.get(id);\n if (from !== undefined) scene.setPose(id, from);\n }\n\n let anySpawned = false;\n\n for (let i = 0; i < ids.length; i++) {\n const id = ids[i];\n const d = data[i];\n const from = priorPoses.get(id);\n const to = targetPoses.get(id);\n if (from === undefined || to === undefined) continue;\n\n const delayMs = delayFn ? Math.max(0, delayFn(d, i)) : 0;\n\n const spawnPoseTween = (): void => {\n if (cancelled) return;\n anySpawned = true;\n const handle = ctx.animator.tween<TPose>({\n from,\n to,\n ms: duration,\n easing,\n cancelKey: `d3-transition:${name}:${id}`,\n interpolate: (a, b, t) => lerp(a, b, t),\n onTick: (pose) => {\n // setPose writes through to the scene; SceneCanvas redraws.\n scene.setPose(id, pose);\n },\n onDone: () => {\n pendingCount--;\n settleIfDone();\n },\n });\n handles.push(handle);\n };\n\n pendingCount++;\n if (delayMs > 0) {\n const timer = setTimeout(() => {\n pendingDelays.delete(timer);\n spawnPoseTween();\n }, delayMs);\n pendingDelays.add(timer);\n } else {\n spawnPoseTween();\n }\n\n // Custom tweens — spawn one per registered .tween() per node.\n for (const ct of customTweens) {\n const fromVal = ct.from(d, i);\n const toVal = ct.to(d, i);\n const spawnCustom = (): void => {\n if (cancelled) return;\n anySpawned = true;\n const handle = ctx.animator.tween({\n from: fromVal,\n to: toVal,\n ms: duration,\n easing,\n cancelKey: `d3-transition:${name}:${id}:${ct.name}`,\n interpolator: ct.interpolate,\n interpolate: ct.interpolate\n ? undefined\n : ((a, b, t) => {\n // Fallback per-tick interpolator for numeric values when no factory is given.\n if (typeof a === 'number' && typeof b === 'number') {\n return (a as number) + ((b as number) - (a as number)) * t;\n }\n throw new Error(\n `d3Bind.transition.tween(\"${ct.name}\"): non-numeric value without an interpolate factory`,\n );\n }),\n onTick: (value) => ct.apply(d, id, value),\n onDone: () => {\n pendingCount--;\n settleIfDone();\n },\n });\n handles.push(handle);\n };\n\n pendingCount++;\n if (delayMs > 0) {\n const timer = setTimeout(() => {\n pendingDelays.delete(timer);\n spawnCustom();\n }, delayMs);\n pendingDelays.add(timer);\n } else {\n spawnCustom();\n }\n }\n }\n\n if (anySpawned || pendingDelays.size > 0) {\n onStart.forEach((fn) => fn());\n } else {\n // Nothing to animate (empty selection, no pose deltas). Resolve immediately.\n settleIfDone();\n }\n };\n\n const t: D3Transition<TData> = {\n duration(ms) {\n duration = ms;\n return t;\n },\n ease(fn) {\n easing = fn;\n return t;\n },\n delay(arg) {\n delayFn = typeof arg === 'function' ? arg : () => arg;\n return t;\n },\n tween(opts) {\n customTweens.push({\n name: opts.name,\n from: opts.from as (d: TData, i: number) => unknown,\n to: opts.to as (d: TData, i: number) => unknown,\n interpolate: opts.interpolate as\n | ((from: unknown, to: unknown) => (t: number) => unknown)\n | undefined,\n apply: opts.apply as (d: TData, id: NodeId, value: unknown) => void,\n });\n return t;\n },\n on(event, fn) {\n if (event === 'start') onStart.push(fn);\n else if (event === 'end') onEnd.push(fn);\n else if (event === 'interrupt') onInterrupt.push(fn);\n return t;\n },\n interrupt() {\n if (cancelled || endResolved) return;\n cancelled = true;\n for (const timer of pendingDelays) clearTimeout(timer);\n pendingDelays.clear();\n for (const h of handles) h.cancel();\n handles.length = 0;\n onInterrupt.forEach((fn) => fn());\n // Drain pendingCount so resolveEnd fires; any in-flight onDone callbacks\n // will see endResolved=true and short-circuit.\n pendingCount = 0;\n if (!endResolved) {\n endResolved = true;\n resolveEnd?.();\n }\n },\n end() {\n ensureStarted();\n return endPromise;\n },\n };\n return t;\n}\n","import { asNodeId, RECT_POSE_DESCRIPTOR } from '@weasel-js/core';\nimport type { Animator, NodeId, PoseProjection, Scene } from '@weasel-js/core';\nimport { createTransition } from './transition';\nimport type {\n BindOptions,\n D3Binding,\n D3Selection,\n} from './types';\n\n/**\n * Bind a data array to a scene. Returns a builder; configure with `.pose()` /\n * `.data()` / `.enterFrom()`, then call `.join()` to emit the diff.\n *\n * Diff semantics (by `options.key`):\n * - data has key not in scene → ENTER (scene.add)\n * - data has key in scene → UPDATE (scene.setPose + scene.update)\n * - scene has leaf on the target layer with key not in data → EXIT (scene.remove)\n *\n * All mutations dispatch through `scene.batch('d3Bind.join', ...)` so one join\n * is one undo entry.\n */\nexport function d3Bind<TData, TLayer extends string, TPose>(\n scene: Scene<unknown, TLayer, TPose>,\n data: readonly TData[],\n options: BindOptions<TData, TPose>,\n): D3Binding<TData, TPose> {\n let poseFn: ((d: TData, i: number) => TPose) | null = null;\n let dataFn: ((d: TData, i: number) => Record<string, unknown>) | null = null;\n let enterFromFn: ((d: TData, i: number) => TPose) | null = null;\n\n const binding: D3Binding<TData, TPose> = {\n pose(fn) {\n poseFn = fn;\n return binding;\n },\n data(fn) {\n dataFn = fn;\n return binding;\n },\n enterFrom(fn) {\n enterFromFn = fn;\n return binding;\n },\n join() {\n const layer = (options.layer ?? scene.layers[0]?.id) as TLayer;\n if (!layer) {\n throw new Error('d3Bind: scene has no layers; pass `layer` in options');\n }\n\n // Compute the diff.\n const dataKeys = data.map((d, i) => asNodeId(options.key(d, i)));\n const dataKeySet = new Set(dataKeys);\n const enterIndices: number[] = [];\n const updateIndices: number[] = [];\n const exitIds: NodeId[] = [];\n\n const sceneLeafIdsOnLayer = new Set<NodeId>();\n for (const node of scene.nodes.values()) {\n if (node.kind === 'leaf' && node.layer === layer) {\n sceneLeafIdsOnLayer.add(node.id);\n }\n }\n\n data.forEach((_, i) => {\n if (sceneLeafIdsOnLayer.has(dataKeys[i])) updateIndices.push(i);\n else enterIndices.push(i);\n });\n sceneLeafIdsOnLayer.forEach((id) => {\n if (!dataKeySet.has(id)) exitIds.push(id);\n });\n\n // Snapshot prior poses for nodes that will be updated. Captured BEFORE the\n // batch mutates them so `.transition()` can interpolate from prior → new.\n const priorPoses = new Map<NodeId, TPose>();\n for (const i of updateIndices) {\n const id = dataKeys[i];\n const node = scene.get(id);\n if (node) priorPoses.set(id, node.pose);\n }\n // Entering nodes use enterFrom if set, otherwise their declared pose (snap-in).\n for (const i of enterIndices) {\n const id = dataKeys[i];\n if (enterFromFn) {\n priorPoses.set(id, enterFromFn(data[i], i));\n } else if (poseFn) {\n priorPoses.set(id, poseFn(data[i], i));\n }\n }\n\n // Apply mutations in one batched op group.\n scene.batch('d3Bind.join', () => {\n for (const i of enterIndices) {\n const id = dataKeys[i];\n const pose = poseFn ? poseFn(data[i], i) : (undefined as unknown as TPose);\n const payload = dataFn ? dataFn(data[i], i) : ({} as Record<string, unknown>);\n if (pose === undefined) {\n throw new Error(\n 'd3Bind.join: enter node has no pose. Call `.pose(fn)` before `.join()`.',\n );\n }\n scene.add({\n id,\n kind: 'leaf',\n layer,\n pose,\n // The scene is typed `Scene<unknown, TLayer, TPose>` — payload is\n // unknown to the kit and consumer's drawOne reads it as a typed\n // record.\n data: payload as never,\n });\n }\n for (const i of updateIndices) {\n const id = dataKeys[i];\n if (poseFn) scene.setPose(id, poseFn(data[i], i));\n if (dataFn) scene.update(id, { data: dataFn(data[i], i) as never });\n }\n for (const id of exitIds) {\n scene.remove(id);\n }\n });\n\n return createSelection<TData, TPose>(\n scene,\n dataKeys,\n [...data],\n priorPoses,\n options.animator,\n options.geometry,\n );\n },\n };\n\n return binding;\n}\n\n/** @internal — exported for `selection.filter` reuse. */\nfunction createSelection<TData, TPose>(\n scene: Scene<unknown, string, TPose>,\n ids: readonly NodeId[],\n data: readonly TData[],\n priorPoses: ReadonlyMap<NodeId, TPose>,\n animator: Animator | undefined,\n geometry: PoseProjection<TPose> | undefined,\n): D3Selection<TData, TPose> {\n const sel: D3Selection<TData, TPose> = {\n ids,\n data,\n filter(pred) {\n const subset: NodeId[] = [];\n const subData: TData[] = [];\n for (let i = 0; i < data.length; i++) {\n if (pred(data[i], i)) {\n subset.push(ids[i]);\n subData.push(data[i]);\n }\n }\n return createSelection(scene, subset, subData, priorPoses, animator, geometry);\n },\n each(fn) {\n for (let i = 0; i < ids.length; i++) fn(data[i], ids[i], i);\n return sel;\n },\n transition(name?: string) {\n if (!animator) {\n throw new Error(\n 'd3Bind.transition: pass `animator` in BindOptions to enable transitions',\n );\n }\n const resolvedGeometry = (geometry ??\n (RECT_POSE_DESCRIPTOR as unknown as PoseProjection<TPose>));\n return createTransition({\n scene,\n animator,\n geometry: resolvedGeometry,\n ids,\n data,\n priorPoses,\n name: name ?? '',\n });\n },\n interrupt(name?: string) {\n if (!animator) return sel;\n const transitionName = name ?? '';\n // Cancel all pose tweens registered for this transition name.\n for (const id of ids) {\n animator.cancelKey(`d3-transition:${transitionName}:${id}`);\n }\n return sel;\n },\n };\n return sel;\n}\n"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@weasel-js/d3",
|
|
3
|
+
"version": "0.5.0",
|
|
4
|
+
"description": "d3 bridge for weasel: data-join + transition chain over useScene and useAnimator. d3-force already integrates via useSimulation in the main kit.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"sideEffects": false,
|
|
8
|
+
"main": "./dist/index.js",
|
|
9
|
+
"module": "./dist/index.js",
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"exports": {
|
|
12
|
+
".": {
|
|
13
|
+
"import": "./dist/index.js",
|
|
14
|
+
"types": "./dist/index.d.ts"
|
|
15
|
+
},
|
|
16
|
+
"./package.json": "./package.json"
|
|
17
|
+
},
|
|
18
|
+
"peerDependencies": {
|
|
19
|
+
"react": ">=18",
|
|
20
|
+
"@weasel-js/core": ">=0.4.0"
|
|
21
|
+
},
|
|
22
|
+
"peerDependenciesMeta": {
|
|
23
|
+
"d3-force": {
|
|
24
|
+
"optional": true
|
|
25
|
+
},
|
|
26
|
+
"d3-ease": {
|
|
27
|
+
"optional": true
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
"dependencies": {
|
|
31
|
+
"d3-interpolate": "^3.0.1"
|
|
32
|
+
},
|
|
33
|
+
"author": "orochi235",
|
|
34
|
+
"homepage": "https://orochi235.github.io/weasel/",
|
|
35
|
+
"repository": {
|
|
36
|
+
"type": "git",
|
|
37
|
+
"url": "git+https://github.com/orochi235/weasel.git",
|
|
38
|
+
"directory": "packages/d3"
|
|
39
|
+
},
|
|
40
|
+
"bugs": {
|
|
41
|
+
"url": "https://github.com/orochi235/weasel/issues"
|
|
42
|
+
},
|
|
43
|
+
"engines": {
|
|
44
|
+
"node": ">=20"
|
|
45
|
+
},
|
|
46
|
+
"files": [
|
|
47
|
+
"dist",
|
|
48
|
+
"README.md",
|
|
49
|
+
"LICENSE"
|
|
50
|
+
],
|
|
51
|
+
"publishConfig": {
|
|
52
|
+
"access": "public"
|
|
53
|
+
},
|
|
54
|
+
"scripts": {
|
|
55
|
+
"build": "tsup"
|
|
56
|
+
}
|
|
57
|
+
}
|