@weasel-js/d3 1.6.1 → 1.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -20,16 +20,25 @@ interface BindOptions<TData, TPose> {
20
20
  /**
21
21
  * Builder produced by `d3Bind`. Configure with `.pose()` / `.data()` / `.enterFrom()`,
22
22
  * then call `.join()` to emit the diff as one batched scene mutation and receive
23
- * a chainable `D3Selection`.
23
+ * a chainable `D3Selection`. `TPayload` is the scene's node data type, which
24
+ * `d3Bind` infers from the scene it is given.
24
25
  */
25
- interface D3Binding<TData, TPose> {
26
+ interface D3Binding<TData, TPose, TPayload = Record<string, unknown>> {
26
27
  /** Per-datum pose. Called for both enter (initial scene pose) and update (target). */
27
28
  pose(fn: (d: TData, i: number) => TPose): this;
28
29
  /** Per-datum data payload. Replaces the leaf's existing data on update. */
29
- data(fn: (d: TData, i: number) => Record<string, unknown>): this;
30
+ data(fn: (d: TData, i: number) => TPayload): this;
30
31
  /** Optional initial pose for ENTER nodes — `.transition()` will animate from this
31
32
  * to the declared `.pose()`. Defaults to the same pose (entries snap in). */
32
33
  enterFrom(fn: (d: TData, i: number) => TPose): this;
34
+ /** Take over the EXIT set (d3's `join(enter, update, exit)` exit callback).
35
+ * Without it, `.join()` removes exiting leaves in its own batch. With it,
36
+ * they stay in the scene and `fn` receives them as a selection whose data
37
+ * is each node's scene payload — typically to run
38
+ * `exit.transition()…remove().end()`. A key that comes back in a later
39
+ * join while its node is still exiting cancels that node's pending
40
+ * removal and rebinds it as an UPDATE. */
41
+ exit(fn: (exit: D3Selection<TPayload, TPose>) => void): this;
33
42
  /** Emit the diff against the scene as one batched op group. Returns the merged
34
43
  * selection (enter + update). Order of the selection matches the order of the
35
44
  * bound `data` array. */
@@ -50,15 +59,26 @@ interface D3Selection<TData, TPose> {
50
59
  /** Iterate. Returns this for chaining. */
51
60
  each(fn: (d: TData, id: NodeId, i: number) => void): this;
52
61
  /** Spawn a transition. Throws if `BindOptions.animator` wasn't supplied. */
53
- transition(name?: string): D3Transition<TData>;
62
+ transition(name?: string): D3Transition<TData, TPose>;
54
63
  /** Cancel any in-flight transitions matching `name` (or all if omitted) on these nodes. */
55
64
  interrupt(name?: string): this;
56
65
  }
57
- /** Transition handle backed by the kit's `useAnimator`. */
58
- interface D3Transition<TData> {
66
+ /**
67
+ * Transition handle backed by the kit's `useAnimator`. Nothing runs until
68
+ * `.end()` is called on this transition or on any transition chained to it;
69
+ * starting one starts the whole chain.
70
+ */
71
+ interface D3Transition<TData, TPose = unknown> {
59
72
  duration(ms: number): this;
60
73
  ease(fn: (t: number) => number): this;
74
+ /** Per-item delay. On a chained transition it counts from the moment that
75
+ * item's previous transition ended. */
61
76
  delay(ms: number | ((d: TData, i: number) => number)): this;
77
+ /** Per-item target pose. Defaults to the pose the node holds when this
78
+ * transition starts — for the first transition, the pose `.join()` set, so
79
+ * it animates prior → joined; a chained transition without `.pose()` leaves
80
+ * the pose where the previous one put it. */
81
+ pose(fn: (d: TData, i: number) => TPose): this;
62
82
  tween<TValue>(opts: {
63
83
  name: string;
64
84
  from: (d: TData, i: number) => TValue;
@@ -67,6 +87,15 @@ interface D3Transition<TData> {
67
87
  apply: (d: TData, id: NodeId, value: TValue) => void;
68
88
  }): this;
69
89
  on(event: 'start' | 'end' | 'interrupt', fn: () => void): this;
90
+ /** A transition that starts on each item once this one finishes that item,
91
+ * with the same name and this one's duration and ease (d3's
92
+ * `transition.transition()`). Interrupting this one interrupts it too. */
93
+ transition(): D3Transition<TData, TPose>;
94
+ /** Remove each node from the scene when this transition ends on it (d3's
95
+ * `transition.remove()`), as an undoable delete. An interrupted node is
96
+ * not removed, nor one that left the scene mid-transition — even if
97
+ * another node has since taken its id. */
98
+ remove(): this;
70
99
  end(): Promise<void>;
71
100
  interrupt(): void;
72
101
  }
@@ -78,11 +107,12 @@ interface D3Transition<TData> {
78
107
  * Diff semantics (by `options.key`):
79
108
  * - data has key not in scene → ENTER (scene.add)
80
109
  * - data has key in scene → UPDATE (scene.setPose + scene.update)
81
- * - scene has leaf on the target layer with key not in data → EXIT (scene.remove)
110
+ * - scene has leaf on the target layer with key not in data → EXIT (scene.remove,
111
+ * or handed to the `.exit()` callback, which decides when they go)
82
112
  *
83
113
  * All mutations dispatch through `scene.batch('d3Bind.join', ...)` so one join
84
114
  * is one undo entry.
85
115
  */
86
- declare function d3Bind<TData, TLayer extends string, TPose>(scene: Scene<unknown, TLayer, TPose>, data: readonly TData[], options: BindOptions<TData, TPose>): D3Binding<TData, TPose>;
116
+ declare function d3Bind<TData, TLayer extends string, TPose, TPayload = Record<string, unknown>>(scene: Scene<TPayload, TLayer, TPose>, data: readonly TData[], options: BindOptions<TData, TPose>): D3Binding<TData, TPose, TPayload>;
87
117
 
88
118
  export { type BindOptions, type D3Binding, type D3Selection, type D3Transition, d3Bind };
package/dist/index.js CHANGED
@@ -21,100 +21,149 @@ function takeCustomKeys(ns) {
21
21
  LIVE_CUSTOM_KEYS.delete(ns);
22
22
  return [...set];
23
23
  }
24
+ var PENDING_REMOVALS = /* @__PURE__ */ new WeakMap();
25
+ function pendingFor(scene) {
26
+ let map = PENDING_REMOVALS.get(scene);
27
+ if (!map) {
28
+ map = /* @__PURE__ */ new Map();
29
+ PENDING_REMOVALS.set(scene, map);
30
+ }
31
+ return map;
32
+ }
33
+ function unregisterRemoval(scene, id, cancel) {
34
+ const map = PENDING_REMOVALS.get(scene);
35
+ const set = map?.get(id);
36
+ if (!map || !set) return;
37
+ set.delete(cancel);
38
+ if (set.size === 0) map.delete(id);
39
+ }
40
+ function cancelPendingRemoval(scene, id) {
41
+ const set = PENDING_REMOVALS.get(scene)?.get(id);
42
+ if (!set) return;
43
+ for (const cancel of [...set]) cancel();
44
+ }
24
45
  function createTransition(ctx) {
25
- let duration = 250;
26
- let easing;
46
+ return makeStage(ctx, null, { duration: 250, easing: void 0 }).handle;
47
+ }
48
+ function makeStage(ctx, parent, inherited) {
49
+ const { scene, ids, data, priorPoses, name } = ctx;
50
+ let duration = inherited.duration;
51
+ let easing = inherited.easing;
27
52
  let delayFn = null;
28
- let started = false;
53
+ let poseFn = null;
29
54
  const customTweens = [];
30
55
  const onStart = [];
31
56
  const onEnd = [];
32
57
  const onInterrupt = [];
33
- let pendingCount = 0;
34
- let cancelled = false;
58
+ const children = [];
59
+ let removeOnEnd = false;
60
+ let removalArmed = false;
61
+ const removalCancels = [];
62
+ let scheduled = false;
63
+ let startFired = false;
64
+ let canceled = false;
35
65
  let endResolved = false;
36
- const spawnedCustom = [];
66
+ const itemDone = ids.map(() => false);
67
+ const itemWaiters = /* @__PURE__ */ new Map();
68
+ let itemsLeft = ids.length;
69
+ const joinedPoses = /* @__PURE__ */ new Map();
70
+ const spawnedCustom = /* @__PURE__ */ new Map();
37
71
  let resolveEnd = null;
38
72
  const endPromise = new Promise((resolve) => {
39
73
  resolveEnd = resolve;
40
74
  });
41
- const handles = [];
42
- const pendingDelays = /* @__PURE__ */ new Set();
75
+ const handles = /* @__PURE__ */ new Map();
76
+ const pendingDelays = /* @__PURE__ */ new Map();
77
+ const stopItem = (i) => {
78
+ const timer = pendingDelays.get(i);
79
+ if (timer !== void 0) clearTimeout(timer);
80
+ pendingDelays.delete(i);
81
+ for (const h of handles.get(i) ?? []) h.cancel();
82
+ handles.delete(i);
83
+ for (const [ns, key] of spawnedCustom.get(i) ?? []) untrackCustomKey(ns, key);
84
+ spawnedCustom.delete(i);
85
+ };
86
+ const pushTo = (map, i, v) => {
87
+ const list = map.get(i) ?? [];
88
+ list.push(v);
89
+ map.set(i, list);
90
+ };
43
91
  const settleIfDone = () => {
44
- if (pendingCount === 0 && !endResolved) {
92
+ if (itemsLeft === 0 && !endResolved) {
45
93
  endResolved = true;
46
- if (!cancelled) onEnd.forEach((fn) => fn());
94
+ if (!canceled) onEnd.forEach((fn) => fn());
47
95
  resolveEnd?.();
48
96
  }
49
97
  };
50
- const ensureStarted = () => {
51
- if (started) return;
52
- started = true;
53
- const { scene, geometry, ids, data, priorPoses, name } = ctx;
54
- const lerp = geometry.lerp;
55
- if (!lerp) {
56
- throw new Error(
57
- "d3Bind.transition: provided geometry has no `lerp` \u2014 pass a PoseDescriptor with lerp in BindOptions"
58
- );
59
- }
60
- const targetPoses = /* @__PURE__ */ new Map();
61
- for (const id of ids) {
62
- const node = scene.get(id);
63
- if (node) targetPoses.set(id, node.pose);
98
+ const finishItem = (i) => {
99
+ if (canceled || itemDone[i]) return;
100
+ itemDone[i] = true;
101
+ itemsLeft--;
102
+ const waiters = itemWaiters.get(i);
103
+ itemWaiters.delete(i);
104
+ waiters?.forEach((fn) => fn());
105
+ settleIfDone();
106
+ };
107
+ const present = (i) => {
108
+ const token = scene.incarnation(ids[i]);
109
+ return token !== void 0 && token === ctx.incarnations.get(ids[i]);
110
+ };
111
+ const lost = (i) => {
112
+ if (present(i)) return false;
113
+ stage.root.cancelItem(i);
114
+ return true;
115
+ };
116
+ const runItem = (i) => {
117
+ if (canceled || lost(i)) return;
118
+ const id = ids[i];
119
+ const d = data[i];
120
+ let from;
121
+ let to;
122
+ if (parent) {
123
+ from = scene.get(id).pose;
124
+ to = poseFn ? poseFn(d, i) : void 0;
125
+ } else {
126
+ from = priorPoses.get(id);
127
+ const joined = joinedPoses.get(id);
128
+ if (from === void 0 || joined === void 0) return finishItem(i);
129
+ to = poseFn ? poseFn(d, i) : joined;
64
130
  }
65
- for (const id of ids) {
66
- const from = priorPoses.get(id);
67
- if (from !== void 0) scene.setPose(id, from);
131
+ const jobs = [];
132
+ const ns = `d3-transition:${name}:${id}`;
133
+ const lerp = ctx.geometry.lerp;
134
+ if (to !== void 0) {
135
+ const poseFrom = from;
136
+ const poseTo = to;
137
+ jobs.push((done) => {
138
+ pushTo(
139
+ handles,
140
+ i,
141
+ ctx.animator.tween({
142
+ from: poseFrom,
143
+ to: poseTo,
144
+ ms: duration,
145
+ easing,
146
+ cancelKey: ns,
147
+ interpolate: (a, b, t2) => lerp(a, b, t2),
148
+ onTick: (pose) => {
149
+ if (!lost(i)) scene.setPose(id, pose);
150
+ },
151
+ onDone: done
152
+ })
153
+ );
154
+ });
68
155
  }
69
- let anySpawned = false;
70
- for (let i = 0; i < ids.length; i++) {
71
- const id = ids[i];
72
- const d = data[i];
73
- const from = priorPoses.get(id);
74
- const to = targetPoses.get(id);
75
- if (from === void 0 || to === void 0) continue;
76
- const delayMs = delayFn ? Math.max(0, delayFn(d, i)) : 0;
77
- const spawnPoseTween = () => {
78
- if (cancelled) return;
79
- anySpawned = true;
80
- const handle = ctx.animator.tween({
81
- from,
82
- to,
83
- ms: duration,
84
- easing,
85
- cancelKey: `d3-transition:${name}:${id}`,
86
- interpolate: (a, b, t2) => lerp(a, b, t2),
87
- onTick: (pose) => {
88
- scene.setPose(id, pose);
89
- },
90
- onDone: () => {
91
- pendingCount--;
92
- settleIfDone();
93
- }
94
- });
95
- handles.push(handle);
96
- };
97
- pendingCount++;
98
- if (delayMs > 0) {
99
- const timer = setTimeout(() => {
100
- pendingDelays.delete(timer);
101
- spawnPoseTween();
102
- }, delayMs);
103
- pendingDelays.add(timer);
104
- } else {
105
- spawnPoseTween();
106
- }
107
- for (const ct of customTweens) {
108
- const fromVal = ct.from(d, i);
109
- const toVal = ct.to(d, i);
110
- const ns = `d3-transition:${name}:${id}`;
111
- const customKey = `${ns}:${ct.name}`;
112
- const spawnCustom = () => {
113
- if (cancelled) return;
114
- anySpawned = true;
115
- trackCustomKey(ns, customKey);
116
- spawnedCustom.push([ns, customKey]);
117
- const handle = ctx.animator.tween({
156
+ for (const ct of customTweens) {
157
+ const fromVal = ct.from(d, i);
158
+ const toVal = ct.to(d, i);
159
+ const customKey = `${ns}:${ct.name}`;
160
+ jobs.push((done) => {
161
+ trackCustomKey(ns, customKey);
162
+ pushTo(spawnedCustom, i, [ns, customKey]);
163
+ pushTo(
164
+ handles,
165
+ i,
166
+ ctx.animator.tween({
118
167
  from: fromVal,
119
168
  to: toVal,
120
169
  ms: duration,
@@ -129,33 +178,121 @@ function createTransition(ctx) {
129
178
  `d3Bind.transition.tween("${ct.name}"): non-numeric value without an interpolate factory`
130
179
  );
131
180
  }),
132
- onTick: (value) => ct.apply(d, id, value),
181
+ onTick: (value) => {
182
+ if (!lost(i)) ct.apply(d, id, value);
183
+ },
133
184
  onDone: () => {
134
185
  untrackCustomKey(ns, customKey);
135
- pendingCount--;
136
- settleIfDone();
186
+ done();
137
187
  }
138
- });
139
- handles.push(handle);
140
- };
141
- pendingCount++;
142
- if (delayMs > 0) {
143
- const timer = setTimeout(() => {
144
- pendingDelays.delete(timer);
145
- spawnCustom();
146
- }, delayMs);
147
- pendingDelays.add(timer);
148
- } else {
149
- spawnCustom();
150
- }
151
- }
188
+ })
189
+ );
190
+ });
152
191
  }
153
- if (anySpawned || pendingDelays.size > 0) {
192
+ if (jobs.length === 0) return finishItem(i);
193
+ if (!startFired) {
194
+ startFired = true;
154
195
  onStart.forEach((fn) => fn());
196
+ }
197
+ let jobsLeft = jobs.length;
198
+ const jobDone = () => {
199
+ if (--jobsLeft === 0) finishItem(i);
200
+ };
201
+ const spawn = () => {
202
+ if (canceled) return;
203
+ for (const job of jobs) job(jobDone);
204
+ };
205
+ const delayMs = delayFn ? Math.max(0, delayFn(d, i)) : 0;
206
+ if (delayMs > 0) {
207
+ const timer = setTimeout(() => {
208
+ pendingDelays.delete(i);
209
+ spawn();
210
+ }, delayMs);
211
+ pendingDelays.set(i, timer);
155
212
  } else {
213
+ spawn();
214
+ }
215
+ };
216
+ const armRemoval = () => {
217
+ if (removalArmed || canceled) return;
218
+ removalArmed = true;
219
+ ids.forEach((id, i) => {
220
+ const cancel = () => stage.root.cancelItem(i);
221
+ removalCancels.push([id, cancel]);
222
+ const set = pendingFor(scene).get(id) ?? /* @__PURE__ */ new Set();
223
+ set.add(cancel);
224
+ pendingFor(scene).set(id, set);
225
+ stage.whenItemDone(i, () => {
226
+ unregisterRemoval(scene, id, cancel);
227
+ if (present(i)) scene.remove(id);
228
+ });
229
+ });
230
+ };
231
+ const stage = {
232
+ root: void 0,
233
+ schedule(deferReady = false) {
234
+ if (scheduled || canceled) return;
235
+ scheduled = true;
236
+ if (removeOnEnd) armRemoval();
237
+ if (!ctx.geometry.lerp) {
238
+ throw new Error(
239
+ "d3Bind.transition: provided geometry has no `lerp` \u2014 pass a PoseDescriptor with lerp in BindOptions"
240
+ );
241
+ }
242
+ if (parent) {
243
+ parent.schedule();
244
+ ids.forEach((_, i) => parent.whenItemDone(i, () => runItem(i), deferReady));
245
+ } else {
246
+ ids.forEach((id, i) => {
247
+ if (present(i)) joinedPoses.set(id, scene.get(id).pose);
248
+ });
249
+ ids.forEach((id, i) => {
250
+ const from = priorPoses.get(id);
251
+ if (from !== void 0 && present(i)) scene.setPose(id, from);
252
+ });
253
+ ids.forEach((_, i) => runItem(i));
254
+ }
255
+ settleIfDone();
256
+ for (const child of children) child.schedule();
257
+ },
258
+ whenItemDone(i, fn, defer = false) {
259
+ if (!itemDone[i]) {
260
+ const list = itemWaiters.get(i) ?? [];
261
+ list.push(fn);
262
+ itemWaiters.set(i, list);
263
+ } else if (defer) {
264
+ queueMicrotask(fn);
265
+ } else {
266
+ fn();
267
+ }
268
+ },
269
+ interrupt() {
270
+ if (!canceled && !endResolved) {
271
+ canceled = true;
272
+ ids.forEach((_, i) => stopItem(i));
273
+ itemWaiters.clear();
274
+ for (const [id, cancel] of removalCancels) unregisterRemoval(scene, id, cancel);
275
+ removalCancels.length = 0;
276
+ onInterrupt.forEach((fn) => fn());
277
+ endResolved = true;
278
+ resolveEnd?.();
279
+ }
280
+ for (const child of children) child.interrupt();
281
+ },
282
+ cancelItem(i) {
283
+ if (!canceled && !itemDone[i]) {
284
+ itemDone[i] = true;
285
+ itemsLeft--;
286
+ itemWaiters.delete(i);
287
+ stopItem(i);
288
+ }
289
+ const cancel = removalCancels.find(([id]) => id === ids[i])?.[1];
290
+ if (cancel) unregisterRemoval(scene, ids[i], cancel);
291
+ for (const child of children) child.cancelItem(i);
156
292
  settleIfDone();
157
293
  }
158
294
  };
295
+ stage.root = parent ? parent.root : stage;
159
296
  const t = {
160
297
  duration(ms) {
161
298
  duration = ms;
@@ -169,6 +306,10 @@ function createTransition(ctx) {
169
306
  delayFn = typeof arg === "function" ? arg : () => arg;
170
307
  return t;
171
308
  },
309
+ pose(fn) {
310
+ poseFn = fn;
311
+ return t;
312
+ },
172
313
  tween(opts) {
173
314
  customTweens.push({
174
315
  name: opts.name,
@@ -185,28 +326,27 @@ function createTransition(ctx) {
185
326
  else if (event === "interrupt") onInterrupt.push(fn);
186
327
  return t;
187
328
  },
329
+ transition() {
330
+ const next = makeStage(ctx, stage, { duration, easing });
331
+ children.push(next.stage);
332
+ if (canceled) next.stage.interrupt();
333
+ else if (scheduled && !endResolved) next.stage.schedule(true);
334
+ return next.handle;
335
+ },
188
336
  interrupt() {
189
- if (cancelled || endResolved) return;
190
- cancelled = true;
191
- for (const timer of pendingDelays) clearTimeout(timer);
192
- pendingDelays.clear();
193
- for (const h of handles) h.cancel();
194
- handles.length = 0;
195
- for (const [ns, key] of spawnedCustom) untrackCustomKey(ns, key);
196
- spawnedCustom.length = 0;
197
- onInterrupt.forEach((fn) => fn());
198
- pendingCount = 0;
199
- if (!endResolved) {
200
- endResolved = true;
201
- resolveEnd?.();
202
- }
337
+ stage.interrupt();
338
+ },
339
+ remove() {
340
+ removeOnEnd = true;
341
+ if (scheduled) armRemoval();
342
+ return t;
203
343
  },
204
344
  end() {
205
- ensureStarted();
345
+ stage.schedule();
206
346
  return endPromise;
207
347
  }
208
348
  };
209
- return t;
349
+ return { handle: t, stage };
210
350
  }
211
351
 
212
352
  // src/bind.ts
@@ -214,6 +354,7 @@ function d3Bind(scene, data, options) {
214
354
  let poseFn = null;
215
355
  let dataFn = null;
216
356
  let enterFromFn = null;
357
+ let exitFn = null;
217
358
  const binding = {
218
359
  pose(fn) {
219
360
  poseFn = fn;
@@ -227,6 +368,10 @@ function d3Bind(scene, data, options) {
227
368
  enterFromFn = fn;
228
369
  return binding;
229
370
  },
371
+ exit(fn) {
372
+ exitFn = fn;
373
+ return binding;
374
+ },
230
375
  join() {
231
376
  const layer = options.layer ?? scene.layers[0]?.id;
232
377
  if (!layer) {
@@ -248,6 +393,7 @@ function d3Bind(scene, data, options) {
248
393
  for (const node of scene.nodesOnLayer(layer)) {
249
394
  if (node.kind === "leaf" && !dataKeySet.has(node.id)) exitIds.push(node.id);
250
395
  }
396
+ for (const id of [...dataKeys, ...exitIds]) cancelPendingRemoval(scene, id);
251
397
  const priorPoses = /* @__PURE__ */ new Map();
252
398
  for (const i of updateIndices) {
253
399
  const id = dataKeys[i];
@@ -277,9 +423,6 @@ function d3Bind(scene, data, options) {
277
423
  kind: "leaf",
278
424
  layer,
279
425
  pose,
280
- // The scene is typed `Scene<unknown, TLayer, TPose>` — payload is
281
- // unknown to the kit and consumer's drawOne reads it as a typed
282
- // record.
283
426
  data: payload
284
427
  });
285
428
  }
@@ -288,10 +431,29 @@ function d3Bind(scene, data, options) {
288
431
  if (poseFn) scene.setPose(id, poseFn(data[i], i));
289
432
  if (dataFn) scene.update(id, { data: dataFn(data[i], i) });
290
433
  }
291
- for (const id of exitIds) {
292
- scene.remove(id);
434
+ if (!exitFn) {
435
+ for (const id of exitIds) scene.remove(id);
293
436
  }
294
437
  });
438
+ if (exitFn) {
439
+ const exitPoses = /* @__PURE__ */ new Map();
440
+ const exitData = [];
441
+ for (const id of exitIds) {
442
+ const node = scene.get(id);
443
+ exitPoses.set(id, node.pose);
444
+ exitData.push(node.data);
445
+ }
446
+ exitFn(
447
+ createSelection(
448
+ scene,
449
+ exitIds,
450
+ exitData,
451
+ exitPoses,
452
+ options.animator,
453
+ options.geometry
454
+ )
455
+ );
456
+ }
295
457
  return createSelection(
296
458
  scene,
297
459
  dataKeys,
@@ -304,7 +466,9 @@ function d3Bind(scene, data, options) {
304
466
  };
305
467
  return binding;
306
468
  }
307
- function createSelection(scene, ids, data, priorPoses, animator, geometry) {
469
+ function createSelection(scene, ids, data, priorPoses, animator, geometry, incarnations = new Map(
470
+ ids.map((id) => [id, scene.incarnation(id)])
471
+ )) {
308
472
  const sel = {
309
473
  ids,
310
474
  data,
@@ -317,7 +481,15 @@ function createSelection(scene, ids, data, priorPoses, animator, geometry) {
317
481
  subData.push(data[i]);
318
482
  }
319
483
  }
320
- return createSelection(scene, subset, subData, priorPoses, animator, geometry);
484
+ return createSelection(
485
+ scene,
486
+ subset,
487
+ subData,
488
+ priorPoses,
489
+ animator,
490
+ geometry,
491
+ incarnations
492
+ );
321
493
  },
322
494
  each(fn) {
323
495
  for (let i = 0; i < ids.length; i++) fn(data[i], ids[i], i);
@@ -337,6 +509,7 @@ function createSelection(scene, ids, data, priorPoses, animator, geometry) {
337
509
  ids,
338
510
  data,
339
511
  priorPoses,
512
+ incarnations,
340
513
  name: name ?? ""
341
514
  });
342
515
  },
package/dist/index.js.map CHANGED
@@ -1 +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 PoseDescriptor,\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: PoseDescriptor<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.\n * `animator.cancelKey` matches a key exactly, so a selection-level\n * `.interrupt(name)` cannot reach the custom keys by prefix — live custom\n * keys are tracked in `LIVE_CUSTOM_KEYS` and read back by `customKeysFor`.\n */\n\n/** Live custom-tween cancelKeys, grouped by their pose-tween namespace\n * (`d3-transition:<name>:<nodeId>`). Populated on spawn and drained on\n * completion or cancel, so a selection-level interrupt can find the keys a\n * prefix match would otherwise have given it. */\nconst LIVE_CUSTOM_KEYS = new Map<string, Set<string>>();\n\nfunction trackCustomKey(ns: string, key: string): void {\n const set = LIVE_CUSTOM_KEYS.get(ns) ?? new Set<string>();\n set.add(key);\n LIVE_CUSTOM_KEYS.set(ns, set);\n}\n\nfunction untrackCustomKey(ns: string, key: string): void {\n const set = LIVE_CUSTOM_KEYS.get(ns);\n if (!set) return;\n set.delete(key);\n if (set.size === 0) LIVE_CUSTOM_KEYS.delete(ns);\n}\n\n/**\n * @internal Take the live custom-tween cancelKeys under `ns`, clearing them.\n *\n * Draining is what keeps the table bounded: the caller's next move is\n * `animator.cancelKey`, and a cancelled tween never runs its `onDone`, so\n * leaving the keys behind would strand one entry per interrupted tween.\n */\nexport function takeCustomKeys(ns: string): string[] {\n const set = LIVE_CUSTOM_KEYS.get(ns);\n if (!set) return [];\n LIVE_CUSTOM_KEYS.delete(ns);\n return [...set];\n}\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 /** (namespace, key) pairs this transition put in `LIVE_CUSTOM_KEYS`.\n * `handle.cancel()` does not run a tween's `onDone`, so `interrupt()`\n * drains these itself or the keys outlive their tweens. */\n const spawnedCustom: Array<[string, string]> = [];\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 PoseDescriptor 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 ns = `d3-transition:${name}:${id}`;\n const customKey = `${ns}:${ct.name}`;\n const spawnCustom = (): void => {\n if (cancelled) return;\n anySpawned = true;\n trackCustomKey(ns, customKey);\n spawnedCustom.push([ns, customKey]);\n const handle = ctx.animator.tween({\n from: fromVal,\n to: toVal,\n ms: duration,\n easing,\n cancelKey: customKey,\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 untrackCustomKey(ns, customKey);\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 for (const [ns, key] of spawnedCustom) untrackCustomKey(ns, key);\n spawnedCustom.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, PoseDescriptor, Scene } from '@weasel-js/core';\nimport { createTransition, takeCustomKeys } 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 data.forEach((_, i) => {\n const existing = scene.get(dataKeys[i]);\n if (existing && existing.kind === 'leaf' && existing.layer === layer) {\n updateIndices.push(i);\n } else {\n enterIndices.push(i);\n }\n });\n for (const node of scene.nodesOnLayer(layer)) {\n if (node.kind === 'leaf' && !dataKeySet.has(node.id)) exitIds.push(node.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: PoseDescriptor<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 PoseDescriptor<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 // `animator.cancelKey` matches exactly, so the custom tweens — whose\n // keys carry a `:<tweenName>` suffix — need naming individually.\n for (const id of ids) {\n const ns = `d3-transition:${transitionName}:${id}`;\n for (const key of takeCustomKeys(ns)) animator.cancelKey(key);\n animator.cancelKey(ns);\n }\n return sel;\n },\n };\n return sel;\n}\n"]}
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 PoseDescriptor,\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: PoseDescriptor<TPose>;\n ids: readonly NodeId[];\n data: readonly TData[];\n priorPoses: ReadonlyMap<NodeId, TPose>;\n /** `scene.incarnation` of each id when the selection was made: the nodes\n * this transition belongs to, as opposed to later ones reusing an id. */\n incarnations: ReadonlyMap<NodeId, number | undefined>;\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 on it or on a transition chained to it. This lets\n * the consumer chain `.duration().ease().delay()...` after `.transition()`\n * without having to declare them in advance.\n *\n * For each selected node, the first transition in a chain spawns one pose tween\n * from `priorPoses[id]` → the joined pose (or its `.pose()` target); a chained\n * transition tweens from wherever the previous one left the node, and only\n * when it has a `.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.\n * `animator.cancelKey` matches a key exactly, so a selection-level\n * `.interrupt(name)` cannot reach the custom keys by prefix — live custom\n * keys are tracked in `LIVE_CUSTOM_KEYS` and read back by `takeCustomKeys`.\n */\n\n/** Live custom-tween cancelKeys, grouped by their pose-tween namespace\n * (`d3-transition:<name>:<nodeId>`). Populated on spawn and drained on\n * completion or cancel, so a selection-level interrupt can find the keys a\n * prefix match would otherwise have given it. */\nconst LIVE_CUSTOM_KEYS = new Map<string, Set<string>>();\n\nfunction trackCustomKey(ns: string, key: string): void {\n const set = LIVE_CUSTOM_KEYS.get(ns) ?? new Set<string>();\n set.add(key);\n LIVE_CUSTOM_KEYS.set(ns, set);\n}\n\nfunction untrackCustomKey(ns: string, key: string): void {\n const set = LIVE_CUSTOM_KEYS.get(ns);\n if (!set) return;\n set.delete(key);\n if (set.size === 0) LIVE_CUSTOM_KEYS.delete(ns);\n}\n\n/**\n * @internal Take the live custom-tween cancelKeys under `ns`, clearing them.\n *\n * Draining is what keeps the table bounded: the caller's next move is\n * `animator.cancelKey`, and a canceled tween never runs its `onDone`, so\n * leaving the keys behind would strand one entry per interrupted tween.\n */\nexport function takeCustomKeys(ns: string): string[] {\n const set = LIVE_CUSTOM_KEYS.get(ns);\n if (!set) return [];\n LIVE_CUSTOM_KEYS.delete(ns);\n return [...set];\n}\n\n/** Per scene, the nodes a `.remove()` transition will delete when it ends,\n * each with the cancel that keeps it. Keyed weakly so a dropped scene takes\n * its entries with it. */\nconst PENDING_REMOVALS = new WeakMap<object, Map<NodeId, Set<() => void>>>();\n\nfunction pendingFor(scene: object): Map<NodeId, Set<() => void>> {\n let map = PENDING_REMOVALS.get(scene);\n if (!map) {\n map = new Map();\n PENDING_REMOVALS.set(scene, map);\n }\n return map;\n}\n\nfunction unregisterRemoval(scene: object, id: NodeId, cancel: () => void): void {\n const map = PENDING_REMOVALS.get(scene);\n const set = map?.get(id);\n if (!map || !set) return;\n set.delete(cancel);\n if (set.size === 0) map.delete(id);\n}\n\n/**\n * @internal Stop every transition chain that would remove `id` when it ends,\n * for that node only, so the node stays in the scene. The join calls this for\n * a key that re-enters while its node is still exiting (d3's rebinding of an\n * exiting element).\n */\nexport function cancelPendingRemoval(scene: object, id: NodeId): void {\n const set = PENDING_REMOVALS.get(scene)?.get(id);\n if (!set) return;\n for (const cancel of [...set]) cancel();\n}\n\n/** One transition in a chain, as the transition chained after it sees it. */\ninterface Stage {\n /** The first transition in this one's chain. */\n root: Stage;\n schedule(deferReady?: boolean): void;\n /** Run `fn` once item `i` has finished this transition. Never runs if the\n * transition is interrupted first. */\n whenItemDone(i: number, fn: () => void, defer?: boolean): void;\n interrupt(): void;\n /** Stop item `i` in this transition and every one chained after it, as if\n * it had finished without running what waits on it. */\n cancelItem(i: number): void;\n}\n\nexport function createTransition<TData, TPose>(\n ctx: TransitionCtx<TData, TPose>,\n): D3Transition<TData, TPose> {\n return makeStage(ctx, null, { duration: 250, easing: undefined }).handle;\n}\n\nfunction makeStage<TData, TPose>(\n ctx: TransitionCtx<TData, TPose>,\n parent: Stage | null,\n inherited: { duration: number; easing: EasingFn | undefined },\n): { handle: D3Transition<TData, TPose>; stage: Stage } {\n const { scene, ids, data, priorPoses, name } = ctx;\n let duration = inherited.duration;\n let easing = inherited.easing;\n let delayFn: ((d: TData, i: number) => number) | null = null;\n let poseFn: ((d: TData, i: number) => TPose) | null = null;\n const customTweens: Array<CustomTween<TData>> = [];\n const onStart: Array<() => void> = [];\n const onEnd: Array<() => void> = [];\n const onInterrupt: Array<() => void> = [];\n const children: Stage[] = [];\n let removeOnEnd = false;\n let removalArmed = false;\n const removalCancels: Array<[NodeId, () => void]> = [];\n\n let scheduled = false;\n let startFired = false;\n let canceled = false;\n let endResolved = false;\n const itemDone = ids.map(() => false);\n const itemWaiters = new Map<number, Array<() => void>>();\n let itemsLeft = ids.length;\n /** Where the first transition in a chain heads by default: the post-join pose. */\n const joinedPoses = new Map<NodeId, TPose>();\n /** (namespace, key) pairs this transition put in `LIVE_CUSTOM_KEYS`, per\n * item. `handle.cancel()` does not run a tween's `onDone`, so a cancel\n * drains these itself or the keys outlive their tweens. */\n const spawnedCustom = new Map<number, Array<[string, string]>>();\n let resolveEnd: (() => void) | null = null;\n const endPromise = new Promise<void>((resolve) => {\n resolveEnd = resolve;\n });\n const handles = new Map<number, AnimationHandle[]>();\n const pendingDelays = new Map<number, ReturnType<typeof setTimeout>>();\n\n const stopItem = (i: number): void => {\n const timer = pendingDelays.get(i);\n if (timer !== undefined) clearTimeout(timer);\n pendingDelays.delete(i);\n for (const h of handles.get(i) ?? []) h.cancel();\n handles.delete(i);\n for (const [ns, key] of spawnedCustom.get(i) ?? []) untrackCustomKey(ns, key);\n spawnedCustom.delete(i);\n };\n\n const pushTo = <V>(map: Map<number, V[]>, i: number, v: V): void => {\n const list = map.get(i) ?? [];\n list.push(v);\n map.set(i, list);\n };\n\n const settleIfDone = (): void => {\n if (itemsLeft === 0 && !endResolved) {\n endResolved = true;\n if (!canceled) onEnd.forEach((fn) => fn());\n resolveEnd?.();\n }\n };\n\n const finishItem = (i: number): void => {\n if (canceled || itemDone[i]) return;\n itemDone[i] = true;\n itemsLeft--;\n const waiters = itemWaiters.get(i);\n itemWaiters.delete(i);\n waiters?.forEach((fn) => fn());\n settleIfDone();\n };\n\n const present = (i: number): boolean => {\n const token = scene.incarnation(ids[i]);\n return token !== undefined && token === ctx.incarnations.get(ids[i]);\n };\n\n /** The scene can lose a node mid-tween (an undo, any outside `remove`), or\n * swap in another under the same id. Stop the item through the whole chain,\n * so neither a tween nor a pending `.remove()` touches the id again. */\n const lost = (i: number): boolean => {\n if (present(i)) return false;\n stage.root.cancelItem(i);\n return true;\n };\n\n const runItem = (i: number): void => {\n if (canceled || lost(i)) return;\n const id = ids[i];\n const d = data[i];\n let from: TPose | undefined;\n let to: TPose | undefined;\n if (parent) {\n from = scene.get(id)!.pose;\n to = poseFn ? poseFn(d, i) : undefined;\n } else {\n from = priorPoses.get(id);\n const joined = joinedPoses.get(id);\n if (from === undefined || joined === undefined) return finishItem(i);\n to = poseFn ? poseFn(d, i) : joined;\n }\n\n const jobs: Array<(done: () => void) => void> = [];\n const ns = `d3-transition:${name}:${id}`;\n const lerp = ctx.geometry.lerp!;\n if (to !== undefined) {\n const poseFrom = from;\n const poseTo = to;\n jobs.push((done) => {\n pushTo(\n handles,\n i,\n ctx.animator.tween<TPose>({\n from: poseFrom,\n to: poseTo,\n ms: duration,\n easing,\n cancelKey: ns,\n interpolate: (a, b, t) => lerp(a, b, t),\n onTick: (pose) => {\n if (!lost(i)) scene.setPose(id, pose);\n },\n onDone: done,\n }),\n );\n });\n }\n for (const ct of customTweens) {\n const fromVal = ct.from(d, i);\n const toVal = ct.to(d, i);\n const customKey = `${ns}:${ct.name}`;\n jobs.push((done) => {\n trackCustomKey(ns, customKey);\n pushTo(spawnedCustom, i, [ns, customKey] as [string, string]);\n pushTo(\n handles,\n i,\n ctx.animator.tween({\n from: fromVal,\n to: toVal,\n ms: duration,\n easing,\n cancelKey: customKey,\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) => {\n if (!lost(i)) ct.apply(d, id, value);\n },\n onDone: () => {\n untrackCustomKey(ns, customKey);\n done();\n },\n }),\n );\n });\n }\n\n if (jobs.length === 0) return finishItem(i);\n if (!startFired) {\n startFired = true;\n onStart.forEach((fn) => fn());\n }\n let jobsLeft = jobs.length;\n const jobDone = (): void => {\n if (--jobsLeft === 0) finishItem(i);\n };\n const spawn = (): void => {\n if (canceled) return;\n for (const job of jobs) job(jobDone);\n };\n const delayMs = delayFn ? Math.max(0, delayFn(d, i)) : 0;\n if (delayMs > 0) {\n const timer = setTimeout(() => {\n pendingDelays.delete(i);\n spawn();\n }, delayMs);\n pendingDelays.set(i, timer);\n } else {\n spawn();\n }\n };\n\n const armRemoval = (): void => {\n if (removalArmed || canceled) return;\n removalArmed = true;\n ids.forEach((id, i) => {\n const cancel = (): void => stage.root.cancelItem(i);\n removalCancels.push([id, cancel]);\n const set = pendingFor(scene).get(id) ?? new Set<() => void>();\n set.add(cancel);\n pendingFor(scene).set(id, set);\n stage.whenItemDone(i, () => {\n unregisterRemoval(scene, id, cancel);\n if (present(i)) scene.remove(id);\n });\n });\n };\n\n const stage: Stage = {\n root: undefined as unknown as Stage,\n schedule(deferReady = false) {\n if (scheduled || canceled) return;\n scheduled = true;\n if (removeOnEnd) armRemoval();\n if (!ctx.geometry.lerp) {\n throw new Error(\n 'd3Bind.transition: provided geometry has no `lerp` — pass a PoseDescriptor with lerp in BindOptions',\n );\n }\n if (parent) {\n parent.schedule();\n ids.forEach((_, i) => parent.whenItemDone(i, () => runItem(i), deferReady));\n } else {\n ids.forEach((id, i) => {\n if (present(i)) joinedPoses.set(id, scene.get(id)!.pose);\n });\n // Write every from-pose before any tween spawns, or non-delayed tweens\n // flash one frame of the joined pose and delayed ones hold it for the\n // whole delay. Costs one extra setPose per node in the undo log.\n ids.forEach((id, i) => {\n const from = priorPoses.get(id);\n if (from !== undefined && present(i)) scene.setPose(id, from);\n });\n ids.forEach((_, i) => runItem(i));\n }\n settleIfDone();\n for (const child of children) child.schedule();\n },\n whenItemDone(i, fn, defer = false) {\n if (!itemDone[i]) {\n const list = itemWaiters.get(i) ?? [];\n list.push(fn);\n itemWaiters.set(i, list);\n } else if (defer) {\n queueMicrotask(fn);\n } else {\n fn();\n }\n },\n interrupt() {\n if (!canceled && !endResolved) {\n canceled = true;\n ids.forEach((_, i) => stopItem(i));\n itemWaiters.clear();\n for (const [id, cancel] of removalCancels) unregisterRemoval(scene, id, cancel);\n removalCancels.length = 0;\n onInterrupt.forEach((fn) => fn());\n endResolved = true;\n resolveEnd?.();\n }\n for (const child of children) child.interrupt();\n },\n cancelItem(i) {\n if (!canceled && !itemDone[i]) {\n itemDone[i] = true;\n itemsLeft--;\n itemWaiters.delete(i);\n stopItem(i);\n }\n const cancel = removalCancels.find(([id]) => id === ids[i])?.[1];\n if (cancel) unregisterRemoval(scene, ids[i], cancel);\n for (const child of children) child.cancelItem(i);\n settleIfDone();\n },\n };\n stage.root = parent ? parent.root : stage;\n\n const t: D3Transition<TData, TPose> = {\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 pose(fn) {\n poseFn = fn;\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 transition() {\n const next = makeStage(ctx, stage, { duration, easing });\n children.push(next.stage);\n if (canceled) next.stage.interrupt();\n // Already in flight: queue behind it now, but let the caller finish\n // configuring before an item whose turn has already come runs.\n else if (scheduled && !endResolved) next.stage.schedule(true);\n return next.handle;\n },\n interrupt() {\n stage.interrupt();\n },\n remove() {\n removeOnEnd = true;\n if (scheduled) armRemoval();\n return t;\n },\n end() {\n stage.schedule();\n return endPromise;\n },\n };\n return { handle: t, stage };\n}\n","import { asNodeId, RECT_POSE_DESCRIPTOR } from '@weasel-js/core';\nimport type { Animator, NodeId, PoseDescriptor, Scene } from '@weasel-js/core';\nimport { cancelPendingRemoval, createTransition, takeCustomKeys } 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 * or handed to the `.exit()` callback, which decides when they go)\n *\n * All mutations dispatch through `scene.batch('d3Bind.join', ...)` so one join\n * is one undo entry.\n */\nexport function d3Bind<\n TData,\n TLayer extends string,\n TPose,\n TPayload = Record<string, unknown>,\n>(\n scene: Scene<TPayload, TLayer, TPose>,\n data: readonly TData[],\n options: BindOptions<TData, TPose>,\n): D3Binding<TData, TPose, TPayload> {\n let poseFn: ((d: TData, i: number) => TPose) | null = null;\n let dataFn: ((d: TData, i: number) => TPayload) | null = null;\n let enterFromFn: ((d: TData, i: number) => TPose) | null = null;\n let exitFn: ((exit: D3Selection<TPayload, TPose>) => void) | null = null;\n\n const binding: D3Binding<TData, TPose, TPayload> = {\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 exit(fn) {\n exitFn = 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 data.forEach((_, i) => {\n const existing = scene.get(dataKeys[i]);\n if (existing && existing.kind === 'leaf' && existing.layer === layer) {\n updateIndices.push(i);\n } else {\n enterIndices.push(i);\n }\n });\n for (const node of scene.nodesOnLayer(layer)) {\n if (node.kind === 'leaf' && !dataKeySet.has(node.id)) exitIds.push(node.id);\n }\n // A node already on its way out stops that exit here: re-entering keys\n // rebind as UPDATE, and still-absent ones exit afresh.\n for (const id of [...dataKeys, ...exitIds]) cancelPendingRemoval(scene, id);\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 TPayload);\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 data: payload,\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) });\n }\n if (!exitFn) {\n for (const id of exitIds) scene.remove(id);\n }\n });\n\n if (exitFn) {\n const exitPoses = new Map<NodeId, TPose>();\n const exitData: TPayload[] = [];\n for (const id of exitIds) {\n const node = scene.get(id)!;\n exitPoses.set(id, node.pose);\n exitData.push(node.data as TPayload);\n }\n exitFn(\n createSelection<TPayload, TPose>(\n scene,\n exitIds,\n exitData,\n exitPoses,\n options.animator,\n options.geometry,\n ),\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: PoseDescriptor<TPose> | undefined,\n incarnations: ReadonlyMap<NodeId, number | undefined> = new Map(\n ids.map((id) => [id, scene.incarnation(id)]),\n ),\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(\n scene, subset, subData, priorPoses, animator, geometry, incarnations,\n );\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 PoseDescriptor<TPose>));\n return createTransition({\n scene,\n animator,\n geometry: resolvedGeometry,\n ids,\n data,\n priorPoses,\n incarnations,\n name: name ?? '',\n });\n },\n interrupt(name?: string) {\n if (!animator) return sel;\n const transitionName = name ?? '';\n // `animator.cancelKey` matches exactly, so the custom tweens — whose\n // keys carry a `:<tweenName>` suffix — need naming individually.\n for (const id of ids) {\n const ns = `d3-transition:${transitionName}:${id}`;\n for (const key of takeCustomKeys(ns)) animator.cancelKey(key);\n animator.cancelKey(ns);\n }\n return sel;\n },\n };\n return sel;\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@weasel-js/d3",
3
- "version": "1.6.1",
3
+ "version": "1.7.1",
4
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
5
  "license": "MIT",
6
6
  "type": "module",
@@ -17,7 +17,7 @@
17
17
  },
18
18
  "peerDependencies": {
19
19
  "react": ">=18",
20
- "@weasel-js/core": "1.6.1"
20
+ "@weasel-js/core": "1.7.1"
21
21
  },
22
22
  "peerDependenciesMeta": {
23
23
  "d3-force": {