@fjader/svelte 0.2.3 → 0.2.5

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.
Files changed (167) hide show
  1. package/README.md +3 -1
  2. package/dist/animation/EntranceGate.d.ts +52 -0
  3. package/dist/animation/EntranceGate.js +93 -0
  4. package/dist/animation/TransitionClock.d.ts +61 -30
  5. package/dist/animation/TransitionClock.js +67 -29
  6. package/dist/animation/transition-log.d.ts +41 -0
  7. package/dist/animation/transition-log.js +57 -0
  8. package/dist/components/axes/AxisBottom.svelte +27 -8
  9. package/dist/components/axes/AxisBottom.svelte.d.ts +10 -0
  10. package/dist/components/axes/AxisCenter.svelte +4 -2
  11. package/dist/components/axes/AxisLeft.svelte +81 -10
  12. package/dist/components/axes/AxisLeft.svelte.d.ts +17 -0
  13. package/dist/components/axes/BandAxisTicks.svelte +4 -3
  14. package/dist/components/axes/axis-title.d.ts +30 -0
  15. package/dist/components/axes/axis-title.js +77 -0
  16. package/dist/components/charts/ChartCell.svelte +33 -6
  17. package/dist/components/charts/ChartCell.svelte.d.ts +9 -0
  18. package/dist/components/charts/ChartContainer.svelte +55 -5
  19. package/dist/components/charts/ChartContainer.svelte.d.ts +14 -1
  20. package/dist/components/charts/ChartHover.svelte.d.ts +14 -0
  21. package/dist/components/charts/ChartHover.svelte.js +22 -0
  22. package/dist/components/charts/ChartVisibility.svelte.d.ts +43 -0
  23. package/dist/components/charts/ChartVisibility.svelte.js +67 -0
  24. package/dist/components/guides/Grid.svelte +12 -1
  25. package/dist/components/guides/legend/ContinuousColorLegend.svelte +7 -1
  26. package/dist/components/guides/legend/OrdinalColorLegend.svelte +8 -5
  27. package/dist/components/marks/AreaMarkX.svelte +18 -4
  28. package/dist/components/marks/AreaMarkY.svelte +18 -4
  29. package/dist/components/marks/BarMarkX.svelte +20 -4
  30. package/dist/components/marks/BarMarkY.svelte +20 -4
  31. package/dist/components/marks/DotsMark.svelte +8 -3
  32. package/dist/components/marks/LineMark.svelte +13 -6
  33. package/dist/components/marks/ParallelCoordinatesMark.svelte +11 -2
  34. package/dist/components/marks/ParallelCoordinatesMark.svelte.d.ts +4 -0
  35. package/dist/components/marks/RectMark.svelte +8 -3
  36. package/dist/components/marks/RectXMark.svelte +9 -3
  37. package/dist/components/marks/RectYMark.svelte +9 -3
  38. package/dist/components/marks/RuleMarkX.svelte +31 -25
  39. package/dist/components/marks/RuleMarkY.svelte +31 -25
  40. package/dist/components/marks/TextMark.svelte +52 -14
  41. package/dist/components/marks/lifecycle/MarkLifecycleV2.svelte.d.ts +22 -1
  42. package/dist/components/marks/lifecycle/MarkLifecycleV2.svelte.js +579 -66
  43. package/dist/components/marks/lifecycle/domain-queries.d.ts +13 -2
  44. package/dist/components/marks/lifecycle/domain-queries.js +43 -11
  45. package/dist/components/marks/lifecycle/domain-registration.d.ts +6 -1
  46. package/dist/components/marks/lifecycle/domain-registration.js +17 -20
  47. package/dist/components/marks/lifecycle/other-rows.svelte.d.ts +112 -0
  48. package/dist/components/marks/lifecycle/other-rows.svelte.js +193 -0
  49. package/dist/components/marks/lifecycle/other-split.d.ts +80 -0
  50. package/dist/components/marks/lifecycle/other-split.js +112 -0
  51. package/dist/components/marks/lifecycle/query-builder.d.ts +53 -3
  52. package/dist/components/marks/lifecycle/query-builder.js +46 -23
  53. package/dist/components/marks/utils/MarkColor.svelte.d.ts +3 -1
  54. package/dist/components/marks/utils/MarkColor.svelte.js +33 -3
  55. package/dist/components/marks/utils/MarkSeries.svelte.d.ts +14 -1
  56. package/dist/components/marks/utils/MarkSeries.svelte.js +25 -1
  57. package/dist/components/marks/utils/MarkSize.svelte.js +4 -0
  58. package/dist/components/marks/utils/reactive-config.d.ts +1 -1
  59. package/dist/components/marks/utils/reactive-config.js +9 -0
  60. package/dist/components/marks/utils/tooltip-helpers.d.ts +4 -1
  61. package/dist/components/marks/utils/tooltip-helpers.js +2 -1
  62. package/dist/components/rendering/Areas.svelte +57 -19
  63. package/dist/components/rendering/Areas.svelte.d.ts +12 -0
  64. package/dist/components/rendering/BaseGPURenderer.svelte.d.ts +3 -1
  65. package/dist/components/rendering/BaseGPURenderer.svelte.js +4 -2
  66. package/dist/components/rendering/Dots.svelte +23 -3
  67. package/dist/components/rendering/Dots.svelte.d.ts +6 -0
  68. package/dist/components/rendering/GridLine.svelte +13 -1
  69. package/dist/components/rendering/GridLine.svelte.d.ts +5 -0
  70. package/dist/components/rendering/Lines.svelte +48 -13
  71. package/dist/components/rendering/Lines.svelte.d.ts +12 -0
  72. package/dist/components/rendering/ParallelLines.svelte +5 -5
  73. package/dist/components/rendering/Rects.svelte +164 -30
  74. package/dist/components/rendering/Rects.svelte.d.ts +23 -2
  75. package/dist/components/rendering/RuleX.svelte +1 -1
  76. package/dist/components/rendering/RuleY.svelte +1 -1
  77. package/dist/components/rendering/WebGPUCanvas.svelte +4 -2
  78. package/dist/components/rendering/activity-reads.d.ts +47 -12
  79. package/dist/components/rendering/activity-reads.js +61 -19
  80. package/dist/components/rendering/uniform-sizes.d.ts +1 -1
  81. package/dist/components/rendering/uniform-sizes.js +1 -1
  82. package/dist/config/ChartConfig.svelte.d.ts +10 -1
  83. package/dist/config/ChartConfig.svelte.js +17 -0
  84. package/dist/gpu/GPUCoordinator.svelte.d.ts +31 -1
  85. package/dist/gpu/GPUCoordinator.svelte.js +164 -7
  86. package/dist/gpu/MorphedChannels.d.ts +11 -28
  87. package/dist/gpu/MorphedChannels.js +72 -8
  88. package/dist/gpu/ScaleTemplateSystem.d.ts +20 -2
  89. package/dist/gpu/ScaleTemplateSystem.js +83 -6
  90. package/dist/index.d.ts +19 -4
  91. package/dist/index.js +22 -4
  92. package/dist/interactors/Interval1D.svelte +6 -1
  93. package/dist/interactors/Interval2D.svelte +6 -1
  94. package/dist/interactors/ParallelAxisSource.svelte +10 -1
  95. package/dist/interactors/Toggle.svelte +32 -3
  96. package/dist/interactors/Tooltip.svelte +138 -41
  97. package/dist/interactors/TooltipManager.svelte.d.ts +20 -5
  98. package/dist/interactors/TooltipManager.svelte.js +46 -12
  99. package/dist/interactors/other-tooltip.d.ts +16 -0
  100. package/dist/interactors/other-tooltip.js +22 -0
  101. package/dist/query-engine/CrossfilterSelection.svelte.d.ts +4 -2
  102. package/dist/query-engine/CrossfilterSelection.svelte.js +2 -0
  103. package/dist/query-engine/FieldActivity.svelte.d.ts +49 -1
  104. package/dist/query-engine/FieldActivity.svelte.js +175 -5
  105. package/dist/query-engine/FilterGroup.svelte.d.ts +23 -1
  106. package/dist/query-engine/FilterGroup.svelte.js +43 -3
  107. package/dist/query-engine/FilterGroupRegistry.svelte.d.ts +31 -3
  108. package/dist/query-engine/FilterGroupRegistry.svelte.js +62 -15
  109. package/dist/query-engine/MarkHandle.svelte.d.ts +28 -1
  110. package/dist/query-engine/MarkHandle.svelte.js +33 -1
  111. package/dist/query-engine/QueryEngine.d.ts +225 -5
  112. package/dist/query-engine/QueryEngine.js +697 -47
  113. package/dist/query-engine/categorical-identity.d.ts +41 -0
  114. package/dist/query-engine/categorical-identity.js +145 -65
  115. package/dist/query-engine/debug/query-log.d.ts +39 -0
  116. package/dist/query-engine/debug/query-log.js +90 -0
  117. package/dist/query-engine/duckdb.d.ts +55 -8
  118. package/dist/query-engine/duckdb.js +82 -30
  119. package/dist/query-engine/index.d.ts +7 -4
  120. package/dist/query-engine/index.js +3 -3
  121. package/dist/query-engine/preagg/BuildQueue.d.ts +33 -13
  122. package/dist/query-engine/preagg/BuildQueue.js +51 -15
  123. package/dist/query-engine/preagg/Preagg.d.ts +46 -3
  124. package/dist/query-engine/preagg/Preagg.js +159 -31
  125. package/dist/query-engine/preagg/plan.d.ts +33 -1
  126. package/dist/query-engine/preagg/plan.js +158 -14
  127. package/dist/query-engine/sliced-lane.d.ts +108 -0
  128. package/dist/query-engine/sliced-lane.js +299 -0
  129. package/dist/query-engine/source-routing.d.ts +106 -0
  130. package/dist/query-engine/source-routing.js +189 -0
  131. package/dist/query-engine/toggle-predicate.d.ts +21 -0
  132. package/dist/query-engine/toggle-predicate.js +29 -1
  133. package/dist/scales/ActivityTable.d.ts +5 -1
  134. package/dist/scales/ActivityTable.js +12 -3
  135. package/dist/scales/BandScaleState.svelte.d.ts +22 -9
  136. package/dist/scales/BandScaleState.svelte.js +39 -21
  137. package/dist/scales/ChartScales.svelte.d.ts +3 -0
  138. package/dist/scales/ChartScales.svelte.js +26 -8
  139. package/dist/scales/ColorScaleState.svelte.d.ts +18 -2
  140. package/dist/scales/ColorScaleState.svelte.js +138 -26
  141. package/dist/scales/LinearScaleState.svelte.d.ts +7 -10
  142. package/dist/scales/LinearScaleState.svelte.js +21 -39
  143. package/dist/scales/ScaleGeneration.svelte.d.ts +48 -0
  144. package/dist/scales/ScaleGeneration.svelte.js +42 -0
  145. package/dist/scales/ScaleRegistry.svelte.js +3 -1
  146. package/dist/scales/SlotTransition.d.ts +15 -1
  147. package/dist/scales/SlotTransition.js +33 -4
  148. package/dist/scales/TimeScaleState.svelte.d.ts +7 -3
  149. package/dist/scales/TimeScaleState.svelte.js +7 -7
  150. package/dist/scales/color-utils.d.ts +0 -3
  151. package/dist/scales/color-utils.js +0 -3
  152. package/dist/scales/entered.d.ts +8 -0
  153. package/dist/scales/entered.js +10 -0
  154. package/dist/spec/LazyQueryEngine.js +2 -19
  155. package/dist/spec/MarkRegistry.d.ts +3 -1
  156. package/dist/spec/MarkRegistry.js +13 -1
  157. package/dist/spec/PlotFromSpec.svelte +1 -0
  158. package/dist/spec/export.js +11 -2
  159. package/dist/spec/types.d.ts +43 -13
  160. package/dist/sql/bin.d.ts +25 -0
  161. package/dist/sql/bin.js +108 -5
  162. package/dist/theme.css +5 -4
  163. package/dist/transforms/TransformCompiler.d.ts +47 -2
  164. package/dist/transforms/TransformCompiler.js +70 -1
  165. package/dist/transforms/index.js +176 -42
  166. package/dist/types.d.ts +177 -2
  167. package/package.json +4 -3
@@ -48,6 +48,8 @@
48
48
  table,
49
49
  selection = undefined,
50
50
  selectionResponse = undefined,
51
+ nulls = undefined,
52
+ holdsEntrance = undefined,
51
53
  x = null,
52
54
  x1: x1Prop = null,
53
55
  x2: x2Prop = null,
@@ -97,6 +99,8 @@
97
99
  transforms: () => transforms,
98
100
  selection: () => selection,
99
101
  selectionResponse: () => selectionResponse,
102
+ nulls: () => nulls,
103
+ holdsEntrance: () => holdsEntrance,
100
104
  limit: () => limit,
101
105
  preferredXScaleType: () => preferredXScaleType,
102
106
  preferredYScaleType: () => preferredYScaleType,
@@ -211,8 +215,8 @@
211
215
  resolveFitDomain: () => resolvedFitAxis
212
216
  });
213
217
 
214
- let xScaleId = $derived(chartContext.xScaleState?.id);
215
- let yScaleId = $derived(chartContext.yScaleState?.id);
218
+ // Rebuild only when a scale is replaced, not on its first resolution.
219
+ let scaleGeneration = $derived(chartContext.scaleGeneration);
216
220
 
217
221
  let instanceCount = $derived(computeInstanceCount(setup?.data));
218
222
 
@@ -297,7 +301,7 @@
297
301
  // --- SETUP LIFECYCLE ---
298
302
 
299
303
  $effect(() => {
300
- (void config, xScaleId, yScaleId);
304
+ (void config, scaleGeneration);
301
305
  untrack(() => {
302
306
  if (setup) {
303
307
  gpuCoordinator?.markForRetain(markId);
@@ -366,6 +370,7 @@
366
370
  constantColor={markColor.constantColor}
367
371
  facetData={setup?.facetData}
368
372
  dataVersion={setup?.dataVersion || 0}
373
+ landsFor={setup?.landsFor ?? 0}
369
374
  bandAxis={null}
370
375
  inset={entry.inset ?? 0.5}
371
376
  dx={entry.dx ?? 0}
@@ -48,6 +48,8 @@
48
48
  table,
49
49
  selection = undefined,
50
50
  selectionResponse = undefined,
51
+ nulls = undefined,
52
+ holdsEntrance = undefined,
51
53
  x,
52
54
  x1: x1Prop,
53
55
  x2: x2Prop,
@@ -92,6 +94,8 @@
92
94
  transforms: () => transforms,
93
95
  selection: () => selection,
94
96
  selectionResponse: () => selectionResponse,
97
+ nulls: () => nulls,
98
+ holdsEntrance: () => holdsEntrance,
95
99
  limit: () => limit,
96
100
  preferredYScaleType: () => preferredYScaleType,
97
101
  fitDomain: () => fitDomain,
@@ -203,8 +207,8 @@
203
207
  resolveFitDomain: () => resolvedFitAxis
204
208
  });
205
209
 
206
- let xScaleId = $derived(chartContext.xScaleState?.id);
207
- let yScaleId = $derived(chartContext.yScaleState?.id);
210
+ // Rebuild only when a scale is replaced, not on its first resolution.
211
+ let scaleGeneration = $derived(chartContext.scaleGeneration);
208
212
 
209
213
  let instanceCount = $derived(computeInstanceCount(setup?.data, x2));
210
214
 
@@ -281,7 +285,7 @@
281
285
  // --- SETUP LIFECYCLE ---
282
286
 
283
287
  $effect(() => {
284
- (void config, xScaleId, yScaleId);
288
+ (void config, scaleGeneration);
285
289
  untrack(() => {
286
290
  if (setup) {
287
291
  gpuCoordinator?.markForRetain(markId);
@@ -357,7 +361,9 @@
357
361
  constantColor={markColor.constantColor}
358
362
  facetData={setup?.facetData}
359
363
  dataVersion={setup?.dataVersion || 0}
364
+ landsFor={setup?.landsFor ?? 0}
360
365
  bandAxis={setup?.alignedBand ?? null}
366
+ grow={hasExplicitBounds ? null : 'x'}
361
367
  inset={entry.inset ?? 0.5}
362
368
  dx={entry.dx ?? 0}
363
369
  dy={entry.dy ?? 0}
@@ -49,6 +49,8 @@
49
49
  table,
50
50
  selection = undefined,
51
51
  selectionResponse = undefined,
52
+ nulls = undefined,
53
+ holdsEntrance = undefined,
52
54
  x = null,
53
55
  x1: x1Prop = null,
54
56
  x2: x2Prop = null,
@@ -94,6 +96,8 @@
94
96
  transforms: () => transforms,
95
97
  selection: () => selection,
96
98
  selectionResponse: () => selectionResponse,
99
+ nulls: () => nulls,
100
+ holdsEntrance: () => holdsEntrance,
97
101
  limit: () => limit,
98
102
  preferredXScaleType: () => preferredXScaleType,
99
103
  fitDomain: () => fitDomain,
@@ -209,8 +213,8 @@
209
213
  resolveFitDomain: () => resolvedFitAxis
210
214
  });
211
215
 
212
- let xScaleId = $derived(chartContext.xScaleState?.id);
213
- let yScaleId = $derived(chartContext.yScaleState?.id);
216
+ // Rebuild only when a scale is replaced, not on its first resolution.
217
+ let scaleGeneration = $derived(chartContext.scaleGeneration);
214
218
 
215
219
  let instanceCount = $derived(computeInstanceCount(setup?.data, y2));
216
220
 
@@ -287,7 +291,7 @@
287
291
  // --- SETUP LIFECYCLE ---
288
292
 
289
293
  $effect(() => {
290
- (void config, xScaleId, yScaleId);
294
+ (void config, scaleGeneration);
291
295
  untrack(() => {
292
296
  if (setup) {
293
297
  gpuCoordinator?.markForRetain(markId);
@@ -364,7 +368,9 @@
364
368
  constantColor={markColor.constantColor}
365
369
  facetData={setup?.facetData}
366
370
  dataVersion={setup?.dataVersion || 0}
371
+ landsFor={setup?.landsFor ?? 0}
367
372
  bandAxis={setup?.alignedBand ?? null}
373
+ grow={hasExplicitBounds ? null : 'y'}
368
374
  inset={entry.inset ?? 0.5}
369
375
  dx={entry.dx ?? 0}
370
376
  dy={entry.dy ?? 0}
@@ -36,6 +36,8 @@
36
36
  table = undefined,
37
37
  selection = undefined,
38
38
  selectionResponse = undefined,
39
+ nulls = undefined,
40
+ holdsEntrance = undefined,
39
41
  x,
40
42
  y: yShorthand = undefined,
41
43
  y1: y1Prop = undefined,
@@ -111,27 +113,31 @@
111
113
  );
112
114
 
113
115
  // --- ChartConfig registration (data-driven only) ---
114
- const entry = $derived.by(() => {
115
- if (!table) return null;
116
- return chartContext.config.registerMark(markId, 'rulex', {
117
- table: () => table,
118
- x: () => x,
119
- y1: () => y1,
120
- y2: () => y2,
121
- stroke: () => stroke,
122
- scheme: () => scheme,
123
- opacity: () => opacity,
124
- transforms: () => transforms,
125
- selection: () => selection,
126
- selectionResponse: () => selectionResponse,
127
- limit: () => limit,
128
- sort: () => sort,
129
- domain: () => domain,
130
- tooltip: () => tooltip,
131
- tip: () => tip,
132
- fitDomain: () => fitDomain
133
- });
134
- });
116
+ // Registered once, at mount, like every other mark. Not in a $derived:
117
+ // registering writes the config's map, and a derived must not write state.
118
+ // A rule without a table draws constants and has no config entry.
119
+ const entry = untrack(() => table)
120
+ ? chartContext.config.registerMark(markId, 'rulex', {
121
+ table: () => table,
122
+ x: () => x,
123
+ y1: () => y1,
124
+ y2: () => y2,
125
+ stroke: () => stroke,
126
+ scheme: () => scheme,
127
+ opacity: () => opacity,
128
+ transforms: () => transforms,
129
+ selection: () => selection,
130
+ selectionResponse: () => selectionResponse,
131
+ nulls: () => nulls,
132
+ holdsEntrance: () => holdsEntrance,
133
+ limit: () => limit,
134
+ sort: () => sort,
135
+ domain: () => domain,
136
+ tooltip: () => tooltip,
137
+ tip: () => tip,
138
+ fitDomain: () => fitDomain
139
+ })
140
+ : null;
135
141
 
136
142
  // Config for MarkLifecycle
137
143
  const channelSelect = $derived.by(() => {
@@ -189,8 +195,8 @@
189
195
  resolveFitDomain: () => resolvedFitAxis
190
196
  });
191
197
 
192
- let xScaleId = $derived(chartContext.xScaleState?.id);
193
- let yScaleId = $derived(chartContext.yScaleState?.id);
198
+ // Rebuild only when a scale is replaced, not on its first resolution.
199
+ let scaleGeneration = $derived(chartContext.scaleGeneration);
194
200
 
195
201
  let instanceCount = $derived(computeInstanceCount(setup?.data, x));
196
202
 
@@ -240,7 +246,7 @@
240
246
 
241
247
  $effect(() => {
242
248
  if (!config) return;
243
- (void config, xScaleId, yScaleId);
249
+ (void config, scaleGeneration);
244
250
  untrack(() => {
245
251
  if (setup) {
246
252
  gpuCoordinator?.markForRetain(markId);
@@ -289,7 +295,7 @@
289
295
 
290
296
  onDestroy(() => {
291
297
  setup?.destroy();
292
- if (table) chartContext.config.unregisterMark(markId);
298
+ if (entry) chartContext.config.unregisterMark(markId);
293
299
  gpuCoordinator?.releaseRetained(markId);
294
300
  });
295
301
  </script>
@@ -36,6 +36,8 @@
36
36
  table = undefined,
37
37
  selection = undefined,
38
38
  selectionResponse = undefined,
39
+ nulls = undefined,
40
+ holdsEntrance = undefined,
39
41
  y,
40
42
  x: xShorthand = undefined,
41
43
  x1: x1Prop = undefined,
@@ -109,27 +111,31 @@
109
111
  );
110
112
 
111
113
  // --- ChartConfig registration (data-driven only) ---
112
- const entry = $derived.by(() => {
113
- if (!table) return null;
114
- return chartContext.config.registerMark(markId, 'ruley', {
115
- table: () => table,
116
- y: () => y,
117
- x1: () => x1,
118
- x2: () => x2,
119
- stroke: () => stroke,
120
- scheme: () => scheme,
121
- opacity: () => opacity,
122
- transforms: () => transforms,
123
- selection: () => selection,
124
- selectionResponse: () => selectionResponse,
125
- limit: () => limit,
126
- sort: () => sort,
127
- domain: () => domain,
128
- tooltip: () => tooltip,
129
- tip: () => tip,
130
- fitDomain: () => fitDomain
131
- });
132
- });
114
+ // Registered once, at mount, like every other mark. Not in a $derived:
115
+ // registering writes the config's map, and a derived must not write state.
116
+ // A rule without a table draws constants and has no config entry.
117
+ const entry = untrack(() => table)
118
+ ? chartContext.config.registerMark(markId, 'ruley', {
119
+ table: () => table,
120
+ y: () => y,
121
+ x1: () => x1,
122
+ x2: () => x2,
123
+ stroke: () => stroke,
124
+ scheme: () => scheme,
125
+ opacity: () => opacity,
126
+ transforms: () => transforms,
127
+ selection: () => selection,
128
+ selectionResponse: () => selectionResponse,
129
+ nulls: () => nulls,
130
+ holdsEntrance: () => holdsEntrance,
131
+ limit: () => limit,
132
+ sort: () => sort,
133
+ domain: () => domain,
134
+ tooltip: () => tooltip,
135
+ tip: () => tip,
136
+ fitDomain: () => fitDomain
137
+ })
138
+ : null;
133
139
 
134
140
  const channelSelect = $derived.by(() => {
135
141
  if (!entry) return {};
@@ -185,8 +191,8 @@
185
191
  resolveFitDomain: () => resolvedFitAxis
186
192
  });
187
193
 
188
- let xScaleId = $derived(chartContext.xScaleState?.id);
189
- let yScaleId = $derived(chartContext.yScaleState?.id);
194
+ // Rebuild only when a scale is replaced, not on its first resolution.
195
+ let scaleGeneration = $derived(chartContext.scaleGeneration);
190
196
 
191
197
  let instanceCount = $derived(computeInstanceCount(setup?.data, y));
192
198
 
@@ -236,7 +242,7 @@
236
242
 
237
243
  $effect(() => {
238
244
  if (!config) return;
239
- (void config, xScaleId, yScaleId);
245
+ (void config, scaleGeneration);
240
246
  untrack(() => {
241
247
  if (setup) {
242
248
  gpuCoordinator?.markForRetain(markId);
@@ -285,7 +291,7 @@
285
291
 
286
292
  onDestroy(() => {
287
293
  setup?.destroy();
288
- if (table) chartContext.config.unregisterMark(markId);
294
+ if (entry) chartContext.config.unregisterMark(markId);
289
295
  gpuCoordinator?.releaseRetained(markId);
290
296
  });
291
297
  </script>
@@ -15,7 +15,13 @@
15
15
  resolveTip
16
16
  } from './utils';
17
17
  import { buildReactiveConfig } from './utils/reactive-config';
18
- import type { FieldRef, BaseMarkProps, ContinuousScaleTypeProps, ChartContext } from '../../types';
18
+ import type {
19
+ FieldRef,
20
+ BaseMarkProps,
21
+ ContinuousScaleTypeProps,
22
+ ChartContext,
23
+ EntranceMode
24
+ } from '../../types';
19
25
  import type { ScaleRegistry } from '../../scales/ScaleRegistry.svelte';
20
26
  import type { QueryEngine } from '../../query-engine/QueryEngine';
21
27
  import type { FilterGroupRegistry } from '../../query-engine/FilterGroupRegistry.svelte';
@@ -43,6 +49,8 @@
43
49
  table,
44
50
  selection = undefined,
45
51
  selectionResponse = undefined,
52
+ nulls = undefined,
53
+ holdsEntrance = undefined,
46
54
  x,
47
55
  y,
48
56
  text,
@@ -69,8 +77,15 @@
69
77
  }: Props = $props();
70
78
 
71
79
  const markId = generateMarkId('text');
80
+ // Held from mount, as other marks do in ChartConfig.registerMark: TextMark
81
+ // has no config entry, and its lifecycle is built later.
82
+ if (untrack(() => table) && untrack(() => holdsEntrance) !== false) {
83
+ getContext<ChartContext>('chart').config?.entrance.hold(markId);
84
+ }
72
85
  const queryEngine = getContext<QueryEngine>('query-engine');
73
86
  const chartContext = getContext<ChartContext>('chart');
87
+ // Read once: the entrance happens when the labels first mount.
88
+ const entrance = chartContext.config?.chartProps?.entrance as EntranceMode | undefined;
74
89
  const filterGroups = getContext<FilterGroupRegistry>('filter-groups');
75
90
  const scaleRegistry = getContext<ScaleRegistry>('scale-registry');
76
91
 
@@ -131,6 +146,12 @@
131
146
  get selectionResponse() {
132
147
  return selectionResponse;
133
148
  },
149
+ get nulls() {
150
+ return nulls;
151
+ },
152
+ get holdsEntrance() {
153
+ return holdsEntrance;
154
+ },
134
155
  get limit() {
135
156
  return limit;
136
157
  },
@@ -145,8 +166,8 @@
145
166
  }
146
167
  });
147
168
 
148
- let xScaleId = $derived(chartContext.xScaleState?.id);
149
- let yScaleId = $derived(chartContext.yScaleState?.id);
169
+ // Rebuild only when a scale is replaced, not on its first resolution.
170
+ let scaleGeneration = $derived(chartContext.scaleGeneration);
150
171
 
151
172
  let instanceCount = $derived(computeInstanceCount(setup?.data, x, y));
152
173
 
@@ -387,17 +408,12 @@
387
408
  // range: a label off the left edge, or below the axis, where the shader
388
409
  // discards the bar it belongs to. The GPU states this discard explicitly
389
410
  // rather than leaving it to the scissor rect, and so does this.
390
- const slotted = (
391
- band: BandScaleState,
392
- raw: string | number,
393
- range: number,
394
- axis: 'x' | 'y'
395
- ) => {
411
+ const slotted = (band: BandScaleState, raw: string | number, range: number) => {
396
412
  const key = bandSlotKey(band, raw);
397
413
  if (key == null) return null;
398
414
  // Asked once and handed on: tickPosition would look it up again.
399
415
  const slot = band.tickSlot(key);
400
- return slot < 0 ? null : band.slotPosition(slot, range, axis);
416
+ return slot < 0 ? null : band.slotPosition(slot, range);
401
417
  };
402
418
 
403
419
  const newPos = new Array(items.length);
@@ -406,13 +422,13 @@
406
422
  let px, py;
407
423
 
408
424
  if (xBand) {
409
- px = slotted(xBand, xRaw[dataIdx], rangeX, 'x');
425
+ px = slotted(xBand, xRaw[dataIdx], rangeX);
410
426
  } else {
411
427
  px = xScale(xRaw[dataIdx]);
412
428
  }
413
429
 
414
430
  if (yBand) {
415
- py = slotted(yBand, yRaw[dataIdx], rangeY, 'y');
431
+ py = slotted(yBand, yRaw[dataIdx], rangeY);
416
432
  } else {
417
433
  py = yScale(yRaw[dataIdx]);
418
434
  }
@@ -430,7 +446,7 @@
430
446
  // --- Setup Lifecycle ---
431
447
 
432
448
  $effect(() => {
433
- (void config, xScaleId, yScaleId);
449
+ (void config, scaleGeneration);
434
450
  untrack(() => {
435
451
  setup?.destroy();
436
452
  setup = null;
@@ -474,11 +490,17 @@
474
490
 
475
491
  onDestroy(() => {
476
492
  setup?.destroy();
493
+ // No config entry to unregister, which is where other marks release it.
494
+ chartContext.config?.entrance.release(markId);
477
495
  });
478
496
  </script>
479
497
 
480
498
  {#if (setup?.gpuReady || !table) && markColor.ready && labelItems.length > 0}
481
- <g class="text-mark" class:text-mark-hidden={useFadeMode && scalesAnimating}>
499
+ <g
500
+ class="text-mark"
501
+ class:text-mark-hidden={useFadeMode && scalesAnimating}
502
+ class:text-mark-enter={entrance !== 'none'}
503
+ >
482
504
  {#each labelItems as item, i (item.idx)}
483
505
  {@const pos = positions[i]}
484
506
  <!--
@@ -514,4 +536,20 @@
514
536
  .text-mark-hidden {
515
537
  opacity: 0;
516
538
  }
539
+
540
+ /*
541
+ * The chart's entrance, for SVG labels: a fade, never motion, so it is the
542
+ * same under reduced motion. Labels sit at their final positions, so on
543
+ * growing bars they arrive as the bars reach them rather than ahead.
544
+ */
545
+ .text-mark-enter {
546
+ /* The chart's fade: a soft S-curve over about 0.45s (ENTRANCE_FADE_STIFFNESS). */
547
+ animation: text-mark-enter 450ms ease-in-out;
548
+ }
549
+
550
+ @keyframes text-mark-enter {
551
+ from {
552
+ opacity: 0;
553
+ }
554
+ }
517
555
  </style>
@@ -19,7 +19,7 @@
19
19
  */
20
20
  import type { ScaleRequirement } from '../../../scales/ScalePriorities';
21
21
  import type { DataAccessor } from '../../../data/DataAccessor';
22
- import type { FieldRef, NormalizedSortConfig, Transform, ChartContext } from '../../../types';
22
+ import type { FieldRef, NormalizedSortConfig, Transform, ChartContext, NullsResponse } from '../../../types';
23
23
  import type { ColorScaleState } from '../../../scales/ColorScaleState.svelte';
24
24
  import type { SizeScaleState } from '../../../scales/SizeScaleState.svelte';
25
25
  import type { QueryEngine } from '../../../query-engine/QueryEngine';
@@ -27,6 +27,7 @@ import type { FilterGroup } from '../../../query-engine/FilterGroup.svelte';
27
27
  import { type SelectionResponse } from '../../rendering/activity-reads';
28
28
  import type { MarkLODSurface } from '../../../lod/rung-types';
29
29
  import type { GPUCoordinator } from '../../../gpu/GPUCoordinator.svelte';
30
+ import { OtherRows } from './other-rows.svelte';
30
31
  export interface MarkLifecycleV2Config {
31
32
  markType: string;
32
33
  supportedScaleTypes: {
@@ -93,6 +94,9 @@ export interface ReactiveConfig {
93
94
  readonly fitDomain: 'x' | 'y' | boolean | undefined;
94
95
  readonly tip: (unknown | '*')[] | false | undefined;
95
96
  readonly selectionResponse: SelectionResponse | undefined;
97
+ readonly nulls: NullsResponse | undefined;
98
+ readonly contributeDomain: boolean | undefined;
99
+ readonly holdsEntrance: boolean | undefined;
96
100
  }
97
101
  export declare class MarkLifecycleV2 {
98
102
  #private;
@@ -101,6 +105,8 @@ export declare class MarkLifecycleV2 {
101
105
  gpuCoordinator?: GPUCoordinator | null;
102
106
  onMarkLOD?: ((surface: MarkLODSurface | null) => void) | null;
103
107
  });
108
+ /** What this mark's Other rows hold, and what is done with it. */
109
+ readonly otherRows: OtherRows;
104
110
  /**
105
111
  * The axis whose rows are one per category, in category order — or null.
106
112
  *
@@ -115,6 +121,11 @@ export declare class MarkLifecycleV2 {
115
121
  * never mixed, the other axis's channels morph, and the band scale owns the
116
122
  * clock both run on.
117
123
  *
124
+ * A histogram's binned axis aligns too: one row per bin, in bin order. Its
125
+ * channels are bin edges rather than ids, still never mixed, and with no
126
+ * slot clock on a linear scale the mark's channels run on a clock of their
127
+ * own.
128
+ *
118
129
  * The first channel of the transforms' alignment — `dense()`'s plan, over the
119
130
  * same select and facts its `apply()` sees — so the conditions are written
120
131
  * once. Derived rather than recorded at apply time: apply runs for every
@@ -139,6 +150,16 @@ export declare class MarkLifecycleV2 {
139
150
  * MarkConstants to read its group's run of each field's activity block.
140
151
  */
141
152
  get groupIndex(): number;
153
+ /**
154
+ * The data version that lands without animating, or 0: a catch-up result
155
+ * (the chart came back into view stale), a change nobody just made. A
156
+ * version, not a flag: the cause stays set until the next result, and a
157
+ * renderer lands only its FIRST write of this version. Later writes of the
158
+ * same data (an Other split on a click, a JS transform's parameter) are
159
+ * changes someone just made, and animate. Reactive through `dataVersion`,
160
+ * which the cause is set with.
161
+ */
162
+ get landsFor(): number;
142
163
  get dataVersion(): number;
143
164
  get hasCategoryReducingTransforms(): boolean;
144
165
  data: DataAccessor | null;