@fjader/svelte 0.2.4 → 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 (139) hide show
  1. package/README.md +1 -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 +4 -2
  9. package/dist/components/axes/AxisCenter.svelte +4 -2
  10. package/dist/components/axes/AxisLeft.svelte +4 -2
  11. package/dist/components/axes/BandAxisTicks.svelte +3 -2
  12. package/dist/components/charts/ChartCell.svelte +32 -5
  13. package/dist/components/charts/ChartCell.svelte.d.ts +9 -0
  14. package/dist/components/charts/ChartContainer.svelte +45 -3
  15. package/dist/components/charts/ChartContainer.svelte.d.ts +7 -1
  16. package/dist/components/charts/ChartHover.svelte.d.ts +14 -0
  17. package/dist/components/charts/ChartHover.svelte.js +22 -0
  18. package/dist/components/charts/ChartVisibility.svelte.d.ts +43 -0
  19. package/dist/components/charts/ChartVisibility.svelte.js +67 -0
  20. package/dist/components/guides/Grid.svelte +12 -1
  21. package/dist/components/marks/AreaMarkX.svelte +16 -4
  22. package/dist/components/marks/AreaMarkY.svelte +16 -4
  23. package/dist/components/marks/BarMarkX.svelte +7 -3
  24. package/dist/components/marks/BarMarkY.svelte +7 -3
  25. package/dist/components/marks/DotsMark.svelte +6 -3
  26. package/dist/components/marks/LineMark.svelte +11 -8
  27. package/dist/components/marks/ParallelCoordinatesMark.svelte +7 -2
  28. package/dist/components/marks/ParallelCoordinatesMark.svelte.d.ts +2 -0
  29. package/dist/components/marks/RectMark.svelte +6 -3
  30. package/dist/components/marks/RectXMark.svelte +7 -3
  31. package/dist/components/marks/RectYMark.svelte +7 -3
  32. package/dist/components/marks/RuleMarkX.svelte +30 -26
  33. package/dist/components/marks/RuleMarkY.svelte +30 -26
  34. package/dist/components/marks/TextMark.svelte +44 -5
  35. package/dist/components/marks/lifecycle/MarkLifecycleV2.svelte.d.ts +16 -0
  36. package/dist/components/marks/lifecycle/MarkLifecycleV2.svelte.js +398 -54
  37. package/dist/components/marks/lifecycle/domain-queries.d.ts +11 -2
  38. package/dist/components/marks/lifecycle/domain-queries.js +33 -6
  39. package/dist/components/marks/lifecycle/domain-registration.d.ts +6 -1
  40. package/dist/components/marks/lifecycle/domain-registration.js +15 -14
  41. package/dist/components/marks/lifecycle/other-rows.svelte.d.ts +9 -1
  42. package/dist/components/marks/lifecycle/other-rows.svelte.js +2 -2
  43. package/dist/components/marks/lifecycle/query-builder.d.ts +27 -2
  44. package/dist/components/marks/lifecycle/query-builder.js +23 -20
  45. package/dist/components/marks/utils/MarkColor.svelte.d.ts +3 -1
  46. package/dist/components/marks/utils/MarkColor.svelte.js +33 -3
  47. package/dist/components/marks/utils/MarkSeries.svelte.d.ts +14 -1
  48. package/dist/components/marks/utils/MarkSeries.svelte.js +25 -1
  49. package/dist/components/marks/utils/MarkSize.svelte.js +4 -0
  50. package/dist/components/marks/utils/reactive-config.d.ts +1 -1
  51. package/dist/components/marks/utils/reactive-config.js +3 -0
  52. package/dist/components/rendering/Areas.svelte +57 -19
  53. package/dist/components/rendering/Areas.svelte.d.ts +12 -0
  54. package/dist/components/rendering/BaseGPURenderer.svelte.d.ts +1 -1
  55. package/dist/components/rendering/Dots.svelte +23 -3
  56. package/dist/components/rendering/Dots.svelte.d.ts +6 -0
  57. package/dist/components/rendering/GridLine.svelte +13 -1
  58. package/dist/components/rendering/GridLine.svelte.d.ts +5 -0
  59. package/dist/components/rendering/Lines.svelte +48 -13
  60. package/dist/components/rendering/Lines.svelte.d.ts +12 -0
  61. package/dist/components/rendering/ParallelLines.svelte +5 -5
  62. package/dist/components/rendering/Rects.svelte +51 -11
  63. package/dist/components/rendering/Rects.svelte.d.ts +16 -2
  64. package/dist/components/rendering/RuleX.svelte +1 -1
  65. package/dist/components/rendering/RuleY.svelte +1 -1
  66. package/dist/components/rendering/WebGPUCanvas.svelte +4 -2
  67. package/dist/components/rendering/activity-reads.d.ts +41 -24
  68. package/dist/components/rendering/activity-reads.js +54 -31
  69. package/dist/components/rendering/uniform-sizes.d.ts +1 -1
  70. package/dist/components/rendering/uniform-sizes.js +1 -1
  71. package/dist/config/ChartConfig.svelte.d.ts +7 -0
  72. package/dist/config/ChartConfig.svelte.js +17 -0
  73. package/dist/gpu/GPUCoordinator.svelte.d.ts +31 -1
  74. package/dist/gpu/GPUCoordinator.svelte.js +164 -7
  75. package/dist/gpu/MorphedChannels.d.ts +11 -2
  76. package/dist/gpu/MorphedChannels.js +33 -11
  77. package/dist/gpu/ScaleTemplateSystem.d.ts +20 -2
  78. package/dist/gpu/ScaleTemplateSystem.js +58 -2
  79. package/dist/index.d.ts +17 -4
  80. package/dist/index.js +19 -4
  81. package/dist/interactors/TooltipManager.svelte.js +14 -5
  82. package/dist/query-engine/CrossfilterSelection.svelte.d.ts +4 -2
  83. package/dist/query-engine/CrossfilterSelection.svelte.js +2 -0
  84. package/dist/query-engine/FieldActivity.svelte.d.ts +2 -3
  85. package/dist/query-engine/FieldActivity.svelte.js +1 -2
  86. package/dist/query-engine/MarkHandle.svelte.d.ts +28 -1
  87. package/dist/query-engine/MarkHandle.svelte.js +33 -1
  88. package/dist/query-engine/QueryEngine.d.ts +225 -5
  89. package/dist/query-engine/QueryEngine.js +671 -41
  90. package/dist/query-engine/categorical-identity.d.ts +34 -8
  91. package/dist/query-engine/categorical-identity.js +138 -73
  92. package/dist/query-engine/debug/query-log.d.ts +39 -0
  93. package/dist/query-engine/debug/query-log.js +90 -0
  94. package/dist/query-engine/duckdb.d.ts +55 -8
  95. package/dist/query-engine/duckdb.js +82 -30
  96. package/dist/query-engine/index.d.ts +7 -4
  97. package/dist/query-engine/index.js +3 -3
  98. package/dist/query-engine/preagg/BuildQueue.d.ts +33 -13
  99. package/dist/query-engine/preagg/BuildQueue.js +51 -15
  100. package/dist/query-engine/preagg/Preagg.d.ts +46 -3
  101. package/dist/query-engine/preagg/Preagg.js +159 -31
  102. package/dist/query-engine/preagg/plan.d.ts +28 -1
  103. package/dist/query-engine/preagg/plan.js +128 -8
  104. package/dist/query-engine/sliced-lane.d.ts +108 -0
  105. package/dist/query-engine/sliced-lane.js +299 -0
  106. package/dist/query-engine/source-routing.d.ts +106 -0
  107. package/dist/query-engine/source-routing.js +189 -0
  108. package/dist/scales/BandScaleState.svelte.d.ts +9 -1
  109. package/dist/scales/BandScaleState.svelte.js +16 -5
  110. package/dist/scales/ChartScales.svelte.d.ts +3 -0
  111. package/dist/scales/ChartScales.svelte.js +26 -8
  112. package/dist/scales/ColorScaleState.svelte.d.ts +18 -2
  113. package/dist/scales/ColorScaleState.svelte.js +138 -26
  114. package/dist/scales/LinearScaleState.svelte.d.ts +7 -10
  115. package/dist/scales/LinearScaleState.svelte.js +21 -39
  116. package/dist/scales/ScaleGeneration.svelte.d.ts +48 -0
  117. package/dist/scales/ScaleGeneration.svelte.js +42 -0
  118. package/dist/scales/ScaleRegistry.svelte.js +3 -1
  119. package/dist/scales/SlotTransition.d.ts +15 -1
  120. package/dist/scales/SlotTransition.js +33 -4
  121. package/dist/scales/TimeScaleState.svelte.d.ts +7 -3
  122. package/dist/scales/TimeScaleState.svelte.js +7 -7
  123. package/dist/scales/color-utils.d.ts +0 -3
  124. package/dist/scales/color-utils.js +0 -3
  125. package/dist/scales/entered.d.ts +8 -0
  126. package/dist/scales/entered.js +10 -0
  127. package/dist/spec/LazyQueryEngine.js +2 -19
  128. package/dist/spec/MarkRegistry.d.ts +3 -1
  129. package/dist/spec/MarkRegistry.js +13 -1
  130. package/dist/spec/PlotFromSpec.svelte +1 -0
  131. package/dist/spec/export.js +11 -2
  132. package/dist/spec/types.d.ts +41 -13
  133. package/dist/sql/bin.d.ts +25 -0
  134. package/dist/sql/bin.js +108 -5
  135. package/dist/transforms/TransformCompiler.d.ts +10 -0
  136. package/dist/transforms/TransformCompiler.js +26 -0
  137. package/dist/transforms/index.js +144 -24
  138. package/dist/types.d.ts +136 -2
  139. package/package.json +2 -2
@@ -1,4 +1,7 @@
1
1
  <script lang="ts">
2
+ import { ScaleGeneration } from '../../scales/ScaleGeneration.svelte';
3
+ import { ChartVisibility } from './ChartVisibility.svelte';
4
+ import { ChartHover } from './ChartHover.svelte';
2
5
  import { setContext, getContext, onDestroy } from 'svelte';
3
6
  import type { Snippet } from 'svelte';
4
7
  import { ChartLayout } from '../../layout/ChartLayout.svelte';
@@ -6,8 +9,8 @@
6
9
  import { ChartScales } from '../../scales/ChartScales.svelte';
7
10
  import { ChartConfig } from '../../config';
8
11
  import { getGPUContext } from '../../gpu';
9
- import type { ScaleRegistry } from '../../scales/ScaleRegistry.svelte';
10
- import type { ChartContext, FieldRef } from '../../types';
12
+ import type { ScaleRegistry, AnyScaleState } from '../../scales/ScaleRegistry.svelte';
13
+ import type { ChartContext, EntranceMode, FieldRef } from '../../types';
11
14
  import type { BandScaleState } from '../../scales/BandScaleState.svelte';
12
15
  import type { DimValue } from '../../query-engine/categorical-identity';
13
16
  import { identityKeyFor } from '../../query-engine/identity-key';
@@ -85,6 +88,12 @@
85
88
  * contrast. `--fjader-tooltip-bg`/`-text` set by the app override both.
86
89
  */
87
90
  tooltipScheme?: 'contrast' | 'follow';
91
+ /**
92
+ * How the marks enter when data first draws: 'auto' grows bars and
93
+ * areas from 0 and fades the rest, 'fade' fades everything, 'none'
94
+ * shows them at once. Reduced motion turns 'auto' into 'fade'.
95
+ */
96
+ entrance?: EntranceMode;
88
97
  }
89
98
 
90
99
  let {
@@ -101,6 +110,7 @@
101
110
  interactors = undefined,
102
111
  legend = undefined,
103
112
  legendPosition = 'top',
113
+ entrance = 'auto',
104
114
  wheelInteraction = {
105
115
  dwellTimeMs: 250,
106
116
  momentumThreshold: 150,
@@ -212,6 +222,19 @@
212
222
 
213
223
  // --- ChartScales ---
214
224
  let scales = $state<ChartScales | null>(null);
225
+ // Survives `scales` being recreated: see ScaleGeneration.
226
+ const scaleGeneration = new ScaleGeneration(() => scales);
227
+ // Whether any cell is on screen; the cells report into it.
228
+ const chartVisibility = new ChartVisibility(
229
+ gpuCoordinator
230
+ ? {
231
+ chartsUsing: (scale) => scaleRegistry.chartsUsing(scale as AnyScaleState),
232
+ isChartVisible: (chartId) => gpuCoordinator.isChartVisible(chartId)
233
+ }
234
+ : null
235
+ );
236
+ // Whether the pointer is over any cell; the cells report into it.
237
+ const chartHover = new ChartHover();
215
238
 
216
239
  // Queue for scale registrations that arrive before ChartScales is created.
217
240
  // Marks mount synchronously during render, but ChartScales is created in an
@@ -247,7 +270,8 @@
247
270
  xDisableZoom,
248
271
  yDisableZoom,
249
272
  useXSelection,
250
- useYSelection
273
+ useYSelection,
274
+ entranceGate: chartConfig.entrance
251
275
  });
252
276
 
253
277
  // Replay any scale registrations that were queued before scales existed
@@ -274,9 +298,11 @@
274
298
 
275
299
  // --- ChartConfig (reactive configuration store) ---
276
300
  const chartConfig = new ChartConfig();
301
+ onDestroy(() => chartConfig.entrance.destroy());
277
302
 
278
303
  // Register chart-level props for palette overrides
279
304
  chartConfig.registerChart({
305
+ entrance: () => entrance,
280
306
  marginTop: () => cellMargin.top,
281
307
  marginRight: () => cellMargin.right,
282
308
  marginBottom: () => cellMargin.bottom,
@@ -373,6 +399,16 @@
373
399
  get xScaleState() {
374
400
  return scales?.xScaleState;
375
401
  },
402
+ get scaleGeneration() {
403
+ return scaleGeneration.value;
404
+ },
405
+ get visible() {
406
+ return chartVisibility.visible;
407
+ },
408
+ shownElsewhere: (scale) => chartVisibility.shownElsewhere(scale),
409
+ get hovered() {
410
+ return chartHover.hovered;
411
+ },
376
412
  get yScaleState() {
377
413
  return scales?.yScaleState;
378
414
  },
@@ -493,6 +529,9 @@
493
529
  facetLabel={String(categoryLabel(cat))}
494
530
  margin={{ top: 20, right: 10, bottom: 35, left: 55 }}
495
531
  {scales}
532
+ {scaleGeneration}
533
+ {chartVisibility}
534
+ {chartHover}
496
535
  {chartConfig}
497
536
  {facetingProps}
498
537
  {gpuCoordinator}
@@ -515,6 +554,9 @@
515
554
  margin={cellMargin}
516
555
  {clipPadding}
517
556
  {scales}
557
+ {scaleGeneration}
558
+ {chartVisibility}
559
+ {chartHover}
518
560
  {chartConfig}
519
561
  {facetingProps}
520
562
  {gpuCoordinator}
@@ -1,6 +1,6 @@
1
1
  import type { Snippet } from 'svelte';
2
2
  import type { Margin } from '../../types';
3
- import type { FieldRef } from '../../types';
3
+ import type { EntranceMode, FieldRef } from '../../types';
4
4
  import type { DimValue } from '../../query-engine/categorical-identity';
5
5
  interface Props {
6
6
  xLabel?: string | null;
@@ -76,6 +76,12 @@ interface Props {
76
76
  * contrast. `--fjader-tooltip-bg`/`-text` set by the app override both.
77
77
  */
78
78
  tooltipScheme?: 'contrast' | 'follow';
79
+ /**
80
+ * How the marks enter when data first draws: 'auto' grows bars and
81
+ * areas from 0 and fades the rest, 'fade' fades everything, 'none'
82
+ * shows them at once. Reduced motion turns 'auto' into 'fade'.
83
+ */
84
+ entrance?: EntranceMode;
79
85
  }
80
86
  declare const ChartContainer: import("svelte").Component<Props, {}, "">;
81
87
  type ChartContainer = ReturnType<typeof ChartContainer>;
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Whether the pointer is over any of a chart's cells: hover intent, for work
3
+ * worth doing only when someone is about to hover a mark, such as writing its
4
+ * tooltip table.
5
+ *
6
+ * Owned by the chart container, which in facet mode holds the marks for many
7
+ * cells, as ChartVisibility is. Cells report pointer enter and leave.
8
+ */
9
+ export declare class ChartHover {
10
+ #private;
11
+ set(cellId: string, hovered: boolean): void;
12
+ /** True while the pointer is over any cell. Reactive. */
13
+ get hovered(): boolean;
14
+ }
@@ -0,0 +1,22 @@
1
+ import { SvelteSet } from 'svelte/reactivity';
2
+ /**
3
+ * Whether the pointer is over any of a chart's cells: hover intent, for work
4
+ * worth doing only when someone is about to hover a mark, such as writing its
5
+ * tooltip table.
6
+ *
7
+ * Owned by the chart container, which in facet mode holds the marks for many
8
+ * cells, as ChartVisibility is. Cells report pointer enter and leave.
9
+ */
10
+ export class ChartHover {
11
+ #cells = new SvelteSet();
12
+ set(cellId, hovered) {
13
+ if (hovered)
14
+ this.#cells.add(cellId);
15
+ else
16
+ this.#cells.delete(cellId);
17
+ }
18
+ /** True while the pointer is over any cell. Reactive. */
19
+ get hovered() {
20
+ return this.#cells.size > 0;
21
+ }
22
+ }
@@ -0,0 +1,43 @@
1
+ /** What `shownElsewhere` asks: which charts draw a scale, and which are visible. */
2
+ export interface ChartVisibilityPeers {
3
+ chartsUsing(scale: object): readonly string[];
4
+ isChartVisible(chartId: string): boolean;
5
+ }
6
+ /**
7
+ * Whether any of a chart's cells is on screen, for its marks to stop
8
+ * re-querying while it is not (see MarkHandle.active).
9
+ *
10
+ * Owned by the chart container, which in facet mode holds the marks for many
11
+ * cells: the marks stay active while any cell is visible. Each cell reports
12
+ * what the GPU coordinator's IntersectionObserver and page visibility say
13
+ * about it; the observer has a 200 px margin, so a chart counts as visible
14
+ * just before it scrolls in. A cell that has not reported counts as visible,
15
+ * as it does for the coordinator, so nothing is held back before the first
16
+ * report.
17
+ */
18
+ export declare class ChartVisibility {
19
+ #private;
20
+ constructor(peers?: ChartVisibilityPeers | null);
21
+ /** A cell's visibility changed. */
22
+ set(cellId: string, visible: boolean): void;
23
+ /** A cell unmounted. */
24
+ remove(cellId: string): void;
25
+ /**
26
+ * True while any cell is visible, or none has reported. Reactive: it reads
27
+ * the SvelteMap. A getter, not a derived, since a chart has few cells.
28
+ */
29
+ get visible(): boolean;
30
+ /**
31
+ * Whether a chart other than this one is visible and draws `scale`, so
32
+ * that changing the scale moves something in view. A chart catching up on
33
+ * its return snaps its own parts but springs a scale like this: the chart
34
+ * being watched moves through the scale, as it would for any change.
35
+ *
36
+ * Asked when a result lands, not when it was sent: visibility changes
37
+ * during a scroll. "Visible" is the coordinator's: it includes the 200 px
38
+ * margin, so a chart in the margin, which nobody sees yet, still counts and
39
+ * keeps the spring. Untracked: a point-in-time answer, asked from inside
40
+ * effects that must not re-run when a cell scrolls.
41
+ */
42
+ shownElsewhere(scale: object): boolean;
43
+ }
@@ -0,0 +1,67 @@
1
+ import { untrack } from 'svelte';
2
+ import { SvelteMap } from 'svelte/reactivity';
3
+ /**
4
+ * Whether any of a chart's cells is on screen, for its marks to stop
5
+ * re-querying while it is not (see MarkHandle.active).
6
+ *
7
+ * Owned by the chart container, which in facet mode holds the marks for many
8
+ * cells: the marks stay active while any cell is visible. Each cell reports
9
+ * what the GPU coordinator's IntersectionObserver and page visibility say
10
+ * about it; the observer has a 200 px margin, so a chart counts as visible
11
+ * just before it scrolls in. A cell that has not reported counts as visible,
12
+ * as it does for the coordinator, so nothing is held back before the first
13
+ * report.
14
+ */
15
+ export class ChartVisibility {
16
+ #cells = new SvelteMap();
17
+ #peers;
18
+ constructor(peers = null) {
19
+ this.#peers = peers;
20
+ }
21
+ /** A cell's visibility changed. */
22
+ set(cellId, visible) {
23
+ this.#cells.set(cellId, visible);
24
+ }
25
+ /** A cell unmounted. */
26
+ remove(cellId) {
27
+ this.#cells.delete(cellId);
28
+ }
29
+ /**
30
+ * True while any cell is visible, or none has reported. Reactive: it reads
31
+ * the SvelteMap. A getter, not a derived, since a chart has few cells.
32
+ */
33
+ get visible() {
34
+ if (this.#cells.size === 0)
35
+ return true;
36
+ for (const v of this.#cells.values())
37
+ if (v)
38
+ return true;
39
+ return false;
40
+ }
41
+ /**
42
+ * Whether a chart other than this one is visible and draws `scale`, so
43
+ * that changing the scale moves something in view. A chart catching up on
44
+ * its return snaps its own parts but springs a scale like this: the chart
45
+ * being watched moves through the scale, as it would for any change.
46
+ *
47
+ * Asked when a result lands, not when it was sent: visibility changes
48
+ * during a scroll. "Visible" is the coordinator's: it includes the 200 px
49
+ * margin, so a chart in the margin, which nobody sees yet, still counts and
50
+ * keeps the spring. Untracked: a point-in-time answer, asked from inside
51
+ * effects that must not re-run when a cell scrolls.
52
+ */
53
+ shownElsewhere(scale) {
54
+ const peers = this.#peers;
55
+ if (!peers)
56
+ return false;
57
+ return untrack(() => {
58
+ for (const chartId of peers.chartsUsing(scale)) {
59
+ if (this.#cells.has(chartId))
60
+ continue;
61
+ if (peers.isChartVisible(chartId))
62
+ return true;
63
+ }
64
+ return false;
65
+ });
66
+ }
67
+ }
@@ -1,4 +1,5 @@
1
1
  <script lang="ts">
2
+ import { hasEntered } from '../../scales/entered';
2
3
  import { getContext } from 'svelte';
3
4
  import { GridLine } from '../rendering';
4
5
  import type { ChartContext, ScaleStateInstance } from '../../types';
@@ -38,7 +39,9 @@
38
39
  let yBuffer = new Float32Array(64);
39
40
 
40
41
  function ticksOf(scale: ScaleStateInstance | null, which: 'x' | 'y'): Float32Array | null {
41
- const ticks = scale?.getTicks ? (scale.getTicks() as ArrayLike<number>) : null;
42
+ // None until the scale has entered: its domain is a placeholder before that.
43
+ const ticks =
44
+ scale?.getTicks && hasEntered(scale) ? (scale.getTicks() as ArrayLike<number>) : null;
42
45
  if (!ticks || ticks.length === 0) return null;
43
46
  let buffer = which === 'x' ? xBuffer : yBuffer;
44
47
  if (buffer.length < ticks.length) {
@@ -58,6 +61,12 @@
58
61
  const yTickValues = $derived(ticksOf(yScale, 'y'));
59
62
  const xAlpha = $derived(visibilityOf(xScale, xBand));
60
63
  const yAlpha = $derived(visibilityOf(yScale, yBand));
64
+ // A band axis's line opacity follows its layout: when that landed at once
65
+ // (a catch-up result), the opacity lands with it.
66
+ const landedOf = (scale: ScaleStateInstance | null, band: boolean) =>
67
+ band && scale ? (scale as BandScaleState).slotsLandedLayout : 0;
68
+ const xLands = $derived(landedOf(xScale, xBand));
69
+ const yLands = $derived(landedOf(yScale, yBand));
61
70
  </script>
62
71
 
63
72
  {#if xTickValues}
@@ -65,6 +74,7 @@
65
74
  axis="x"
66
75
  positions={xTickValues}
67
76
  alpha={xAlpha}
77
+ landsFor={xLands}
68
78
  bandAxis={xBand ? 'x' : null}
69
79
  {color}
70
80
  {lineWidth}
@@ -76,6 +86,7 @@
76
86
  axis="y"
77
87
  positions={yTickValues}
78
88
  alpha={yAlpha}
89
+ landsFor={yLands}
79
90
  bandAxis={yBand ? 'y' : null}
80
91
  {color}
81
92
  {lineWidth}
@@ -49,6 +49,7 @@
49
49
  selection = undefined,
50
50
  selectionResponse = undefined,
51
51
  nulls = undefined,
52
+ holdsEntrance = undefined,
52
53
  y,
53
54
  x,
54
55
  x1: x1Prop,
@@ -103,6 +104,7 @@
103
104
  selection: () => selection,
104
105
  selectionResponse: () => selectionResponse,
105
106
  nulls: () => nulls,
107
+ holdsEntrance: () => holdsEntrance,
106
108
  limit: () => limit,
107
109
  preferredXScaleType: () => preferredXScaleType,
108
110
  preferredYScaleType: () => preferredYScaleType,
@@ -148,7 +150,8 @@
148
150
  seriesChannelKey,
149
151
  () => setup,
150
152
  scaleRegistry,
151
- chartContext
153
+ chartContext,
154
+ () => markColor.scaleState
152
155
  );
153
156
 
154
157
  // Color: fill determines area color — reads from entry
@@ -239,8 +242,8 @@
239
242
  resolveFitDomain: () => resolvedFitAxis
240
243
  });
241
244
 
242
- let xScaleId = $derived(chartContext.xScaleState?.id);
243
- let yScaleId = $derived(chartContext.yScaleState?.id);
245
+ // Rebuild only when a scale is replaced, not on its first resolution.
246
+ let scaleGeneration = $derived(chartContext.scaleGeneration);
244
247
 
245
248
  let instanceCount = $derived(computeInstanceCount(setup?.data, entry.y, x2));
246
249
 
@@ -307,7 +310,7 @@
307
310
  // --- SETUP LIFECYCLE ---
308
311
 
309
312
  $effect(() => {
310
- (void config, xScaleId, yScaleId);
313
+ (void config, scaleGeneration);
311
314
  untrack(() => {
312
315
  if (setup) {
313
316
  gpuCoordinator?.markForRetain(markId);
@@ -375,6 +378,8 @@
375
378
  colorMode={markColor.colorMode}
376
379
  colorScaleIndex={markColor.colorScaleIndex}
377
380
  groupIndex={setup?.groupIndex ?? 0}
381
+ activitySeriesMode={markSeries.activityMode}
382
+ activitySeriesScaleIndex={markSeries.activityScaleIndex}
378
383
  constantColor={markColor.constantColor}
379
384
  fillOpacity={entry.fillOpacity ?? 0.5}
380
385
  dotRadius={effectiveDotRadius}
@@ -383,6 +388,7 @@
383
388
  orientation="horizontal"
384
389
  facetData={setup?.facetData}
385
390
  dataVersion={setup?.dataVersion || 0}
391
+ grow={!hasExplicitBounds}
386
392
  />
387
393
  {#if (entry.strokeWidth ?? 0) > 0}
388
394
  <Lines
@@ -396,11 +402,14 @@
396
402
  activityColorMode={outline.activityColorMode}
397
403
  activityColorScaleIndex={outline.activityColorScaleIndex}
398
404
  groupIndex={setup?.groupIndex ?? 0}
405
+ activitySeriesMode={markSeries.activityMode}
406
+ activitySeriesScaleIndex={markSeries.activityScaleIndex}
399
407
  constantColor={strokeConstantColor}
400
408
  strokeWidth={entry.strokeWidth ?? 0}
401
409
  zIndex={1}
402
410
  facetData={setup?.facetData}
403
411
  dataVersion={setup?.dataVersion || 0}
412
+ grow={hasExplicitBounds ? null : 'x'}
404
413
  />
405
414
  {#if showBothBoundaries}
406
415
  <Lines
@@ -414,11 +423,14 @@
414
423
  activityColorMode={outline.activityColorMode}
415
424
  activityColorScaleIndex={outline.activityColorScaleIndex}
416
425
  groupIndex={setup?.groupIndex ?? 0}
426
+ activitySeriesMode={markSeries.activityMode}
427
+ activitySeriesScaleIndex={markSeries.activityScaleIndex}
417
428
  constantColor={strokeConstantColor}
418
429
  strokeWidth={entry.strokeWidth ?? 0}
419
430
  zIndex={1}
420
431
  facetData={setup?.facetData}
421
432
  dataVersion={setup?.dataVersion || 0}
433
+ grow={hasExplicitBounds ? null : 'x'}
422
434
  />
423
435
  {/if}
424
436
  {/if}
@@ -49,6 +49,7 @@
49
49
  selection = undefined,
50
50
  selectionResponse = undefined,
51
51
  nulls = undefined,
52
+ holdsEntrance = undefined,
52
53
  x,
53
54
  y,
54
55
  y1: y1Prop,
@@ -103,6 +104,7 @@
103
104
  selection: () => selection,
104
105
  selectionResponse: () => selectionResponse,
105
106
  nulls: () => nulls,
107
+ holdsEntrance: () => holdsEntrance,
106
108
  limit: () => limit,
107
109
  preferredXScaleType: () => preferredXScaleType,
108
110
  preferredYScaleType: () => preferredYScaleType,
@@ -148,7 +150,8 @@
148
150
  seriesChannelKey,
149
151
  () => setup,
150
152
  scaleRegistry,
151
- chartContext
153
+ chartContext,
154
+ () => markColor.scaleState
152
155
  );
153
156
 
154
157
  // Color: fill determines area color — reads from entry
@@ -239,8 +242,8 @@
239
242
  resolveFitDomain: () => resolvedFitAxis
240
243
  });
241
244
 
242
- let xScaleId = $derived(chartContext.xScaleState?.id);
243
- let yScaleId = $derived(chartContext.yScaleState?.id);
245
+ // Rebuild only when a scale is replaced, not on its first resolution.
246
+ let scaleGeneration = $derived(chartContext.scaleGeneration);
244
247
 
245
248
  let instanceCount = $derived(computeInstanceCount(setup?.data, entry.x, y2));
246
249
 
@@ -306,7 +309,7 @@
306
309
  // --- SETUP LIFECYCLE ---
307
310
 
308
311
  $effect(() => {
309
- (void config, xScaleId, yScaleId);
312
+ (void config, scaleGeneration);
310
313
  untrack(() => {
311
314
  if (setup) {
312
315
  gpuCoordinator?.markForRetain(markId);
@@ -375,6 +378,8 @@
375
378
  colorMode={markColor.colorMode}
376
379
  colorScaleIndex={markColor.colorScaleIndex}
377
380
  groupIndex={setup?.groupIndex ?? 0}
381
+ activitySeriesMode={markSeries.activityMode}
382
+ activitySeriesScaleIndex={markSeries.activityScaleIndex}
378
383
  constantColor={markColor.constantColor}
379
384
  fillOpacity={entry.fillOpacity ?? 0.5}
380
385
  dotRadius={effectiveDotRadius}
@@ -383,6 +388,7 @@
383
388
  orientation="vertical"
384
389
  facetData={setup?.facetData}
385
390
  dataVersion={setup?.dataVersion || 0}
391
+ grow={!hasExplicitBounds}
386
392
  />
387
393
  {#if (entry.strokeWidth ?? 0) > 0}
388
394
  <Lines
@@ -396,11 +402,14 @@
396
402
  activityColorMode={outline.activityColorMode}
397
403
  activityColorScaleIndex={outline.activityColorScaleIndex}
398
404
  groupIndex={setup?.groupIndex ?? 0}
405
+ activitySeriesMode={markSeries.activityMode}
406
+ activitySeriesScaleIndex={markSeries.activityScaleIndex}
399
407
  constantColor={strokeConstantColor}
400
408
  strokeWidth={entry.strokeWidth ?? 0}
401
409
  zIndex={1}
402
410
  facetData={setup?.facetData}
403
411
  dataVersion={setup?.dataVersion || 0}
412
+ grow={hasExplicitBounds ? null : 'y'}
404
413
  />
405
414
  {#if showBothBoundaries}
406
415
  <Lines
@@ -414,11 +423,14 @@
414
423
  activityColorMode={outline.activityColorMode}
415
424
  activityColorScaleIndex={outline.activityColorScaleIndex}
416
425
  groupIndex={setup?.groupIndex ?? 0}
426
+ activitySeriesMode={markSeries.activityMode}
427
+ activitySeriesScaleIndex={markSeries.activityScaleIndex}
417
428
  constantColor={strokeConstantColor}
418
429
  strokeWidth={entry.strokeWidth ?? 0}
419
430
  zIndex={1}
420
431
  facetData={setup?.facetData}
421
432
  dataVersion={setup?.dataVersion || 0}
433
+ grow={hasExplicitBounds ? null : 'y'}
422
434
  />
423
435
  {/if}
424
436
  {/if}
@@ -42,6 +42,7 @@
42
42
  selection = undefined,
43
43
  selectionResponse = undefined,
44
44
  nulls = undefined,
45
+ holdsEntrance = undefined,
45
46
  contributeDomain = undefined,
46
47
  x,
47
48
  x1: x1Prop,
@@ -86,6 +87,7 @@
86
87
  selection: () => selection,
87
88
  selectionResponse: () => selectionResponse,
88
89
  nulls: () => nulls,
90
+ holdsEntrance: () => holdsEntrance,
89
91
  contributeDomain: () => contributeDomain,
90
92
  limit: () => limit,
91
93
  padding: () => padding,
@@ -178,8 +180,8 @@
178
180
 
179
181
  const reactiveConfig = buildReactiveConfig(entry);
180
182
 
181
- let xScaleId = $derived(chartContext.xScaleState?.id);
182
- let yScaleId = $derived(chartContext.yScaleState?.id);
183
+ // Rebuild only when a scale is replaced, not on its first resolution.
184
+ let scaleGeneration = $derived(chartContext.scaleGeneration);
183
185
 
184
186
  let instanceCount = $derived(computeInstanceCount(setup?.data, x2, entry.y));
185
187
 
@@ -242,7 +244,7 @@
242
244
  // --- SETUP LIFECYCLE ---
243
245
 
244
246
  $effect(() => {
245
- (void config, xScaleId, yScaleId);
247
+ (void config, scaleGeneration);
246
248
  untrack(() => {
247
249
  if (setup) {
248
250
  gpuCoordinator?.markForRetain(markId);
@@ -321,7 +323,9 @@
321
323
  constantColor={markColor.constantColor}
322
324
  facetData={setup?.facetData}
323
325
  dataVersion={setup?.dataVersion || 0}
326
+ landsFor={setup?.landsFor ?? 0}
324
327
  bandAxis={setup?.alignedBand ?? null}
328
+ grow={hasExplicitBounds ? null : 'x'}
325
329
  inset={entry.inset ?? 0}
326
330
  dx={entry.dx ?? 0}
327
331
  dy={entry.dy ?? 0}
@@ -41,6 +41,7 @@
41
41
  selection = undefined,
42
42
  selectionResponse = undefined,
43
43
  nulls = undefined,
44
+ holdsEntrance = undefined,
44
45
  contributeDomain = undefined,
45
46
  x,
46
47
  y,
@@ -85,6 +86,7 @@
85
86
  selection: () => selection,
86
87
  selectionResponse: () => selectionResponse,
87
88
  nulls: () => nulls,
89
+ holdsEntrance: () => holdsEntrance,
88
90
  contributeDomain: () => contributeDomain,
89
91
  limit: () => limit,
90
92
  padding: () => padding,
@@ -179,8 +181,8 @@
179
181
 
180
182
  const reactiveConfig = buildReactiveConfig(entry);
181
183
 
182
- let xScaleId = $derived(chartContext.xScaleState?.id);
183
- let yScaleId = $derived(chartContext.yScaleState?.id);
184
+ // Rebuild only when a scale is replaced, not on its first resolution.
185
+ let scaleGeneration = $derived(chartContext.scaleGeneration);
184
186
 
185
187
  let instanceCount = $derived(computeInstanceCount(setup?.data, y2, entry.x));
186
188
 
@@ -243,7 +245,7 @@
243
245
  // --- SETUP LIFECYCLE ---
244
246
 
245
247
  $effect(() => {
246
- (void config, xScaleId, yScaleId);
248
+ (void config, scaleGeneration);
247
249
  untrack(() => {
248
250
  if (setup) {
249
251
  gpuCoordinator?.markForRetain(markId);
@@ -322,7 +324,9 @@
322
324
  constantColor={markColor.constantColor}
323
325
  facetData={setup?.facetData}
324
326
  dataVersion={setup?.dataVersion || 0}
327
+ landsFor={setup?.landsFor ?? 0}
325
328
  bandAxis={setup?.alignedBand ?? null}
329
+ grow={hasExplicitBounds ? null : 'y'}
326
330
  inset={entry.inset ?? 0}
327
331
  dx={entry.dx ?? 0}
328
332
  dy={entry.dy ?? 0}
@@ -44,6 +44,7 @@
44
44
  selection = undefined,
45
45
  selectionResponse = undefined,
46
46
  nulls = undefined,
47
+ holdsEntrance = undefined,
47
48
  x,
48
49
  y,
49
50
  fill = null,
@@ -84,6 +85,7 @@
84
85
  selection: () => selection,
85
86
  selectionResponse: () => selectionResponse,
86
87
  nulls: () => nulls,
88
+ holdsEntrance: () => holdsEntrance,
87
89
  limit: () => limit,
88
90
  preferredXScaleType: () => preferredXScaleType,
89
91
  preferredYScaleType: () => preferredYScaleType,
@@ -177,8 +179,8 @@
177
179
  resolveFitDomain: () => resolvedFitAxis
178
180
  });
179
181
 
180
- let xScaleId = $derived(chartContext.xScaleState?.id);
181
- let yScaleId = $derived(chartContext.yScaleState?.id);
182
+ // Rebuild only when a scale is replaced, not on its first resolution.
183
+ let scaleGeneration = $derived(chartContext.scaleGeneration);
182
184
 
183
185
  let instanceCount = $derived(computeInstanceCount(setup?.data, entry.x, entry.y));
184
186
 
@@ -246,7 +248,7 @@
246
248
  // --- SETUP LIFECYCLE ---
247
249
 
248
250
  $effect(() => {
249
- (void config, xScaleId, yScaleId);
251
+ (void config, scaleGeneration);
250
252
  untrack(() => {
251
253
  if (setup) {
252
254
  gpuCoordinator?.markForRetain(markId);
@@ -316,6 +318,7 @@
316
318
  sizeScaleIndex={markSize.sizeScaleIndex}
317
319
  facetData={setup?.facetData}
318
320
  dataVersion={setup?.dataVersion || 0}
321
+ landsFor={setup?.landsFor ?? 0}
319
322
  bandAxis={setup?.alignedBand ?? null}
320
323
  />
321
324
  {/if}