@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
@@ -14,6 +14,12 @@ interface Props {
14
14
  share?: SplitProp | null;
15
15
  facetData?: Float32Array | null;
16
16
  dataVersion: number;
17
+ /**
18
+ * The data version whose first write lands without morphing, or 0: a
19
+ * catch-up result (MarkLifecycleV2.landsFor). Later writes of that version
20
+ * animate as usual.
21
+ */
22
+ landsFor?: number;
17
23
  zIndex?: number;
18
24
  colorMode?: number;
19
25
  colorScaleIndex?: number;
@@ -26,14 +32,22 @@ interface Props {
26
32
  markId?: string;
27
33
  /**
28
34
  * The band axis, when this mark's rows are one per category in category
29
- * order and may therefore morph. null to morph nothing.
35
+ * order and may therefore morph; or a histogram's binned axis, one row per
36
+ * bin in bin order. null to morph nothing.
30
37
  *
31
38
  * The OTHER axis's channels morph. The band's never do: they carry a
32
39
  * category id that the shader resolves through the slot table, and
33
40
  * interpolating an identity produces a fraction that resolves to some
34
- * other category. Position already animates, through the slot table.
41
+ * other category. Position already animates, through the slot table. A
42
+ * bin's edges do not change under a filter, and a re-bin snaps.
35
43
  */
36
44
  bandAxis?: 'x' | 'y' | null;
45
+ /**
46
+ * The axis this mark grows along when its chart enters: its measure axis,
47
+ * for bars and histograms anchored at 0. null fades it in instead, for
48
+ * rects with bounds of their own (a Gantt bar has no 0 to grow from).
49
+ */
50
+ grow?: 'x' | 'y' | null;
37
51
  }
38
52
  declare const Rects: import("svelte").Component<Props, {}, "">;
39
53
  type Rects = ReturnType<typeof Rects>;
@@ -181,7 +181,7 @@
181
181
  var output: VertexOutput;
182
182
  output.position = vec4<f32>(normalizedPos.x, -normalizedPos.y, 0.0, 1.0);
183
183
  output.colorValue = colorValue;
184
- output.alpha = alpha * selectionDim(
184
+ output.alpha = alpha * entranceAlpha(0u) * selectionDim(
185
185
  ${markActivityWGSL(ACTIVITY_READS, {
186
186
  color: 'markConstants.colorMode, markConstants.colorScaleIndex, colorValue',
187
187
  x: 'x',
@@ -180,7 +180,7 @@
180
180
  var output: VertexOutput;
181
181
  output.position = vec4<f32>(normalizedPos.x, -normalizedPos.y, 0.0, 1.0);
182
182
  output.colorValue = colorValue;
183
- output.alpha = alpha * selectionDim(
183
+ output.alpha = alpha * entranceAlpha(0u) * selectionDim(
184
184
  ${markActivityWGSL(ACTIVITY_READS, {
185
185
  color: 'markConstants.colorMode, markConstants.colorScaleIndex, colorValue',
186
186
  x: 'x0',
@@ -2,7 +2,7 @@
2
2
  import { onMount, onDestroy, setContext, getContext } from 'svelte';
3
3
  import { getGPUContext } from '../../gpu';
4
4
  import type { Snippet } from 'svelte';
5
- import type { ChartContext, WebGPUCanvasContext } from '../../types';
5
+ import type { ChartContext, EntranceMode, WebGPUCanvasContext } from '../../types';
6
6
  import type { GPUCoordinator, ChartRegistrationResult } from '../../gpu/GPUCoordinator.svelte';
7
7
  import type { FacetScaleRef } from '../../gpu/GPUCoordinator.svelte';
8
8
 
@@ -120,7 +120,9 @@
120
120
  chartControl = gpuCoordinator.registerChart(chartId, canvas, {
121
121
  zIndex: 0,
122
122
  facetIndex: facetIndex ?? 0,
123
- facetScale
123
+ facetScale,
124
+ entrance: c.config?.chartProps?.entrance as EntranceMode | undefined,
125
+ entranceGate: c.config?.entrance ?? null
124
126
  });
125
127
 
126
128
  // Notify coordinator that context is ready
@@ -1,6 +1,7 @@
1
1
  /**
2
2
  * Which selection activities a mark reads: its ordinal colour, its band x,
3
- * its band y. One answer per renderer, used twice:
3
+ * its band y, and a line's or area's series. One answer per renderer, used
4
+ * twice:
4
5
  *
5
6
  * - The renderer builds its shader's `markActivity` call from it, so a read
6
7
  * the table does not list is passed as 1.0 and cannot happen.
@@ -26,17 +27,14 @@ export interface ActivityReads {
26
27
  readonly color: boolean;
27
28
  readonly x: boolean;
28
29
  readonly y: boolean;
30
+ /** A line's or area's `z`: the series field when it is not the colour field. */
31
+ readonly series: boolean;
29
32
  }
30
33
  /**
31
34
  * What a renderer reads. `hasExtent` is a rule's: RuleX with y1/y2, RuleY with
32
- * x1/x2. Lines, areas and parallel coordinates dim by colour only: a line over
33
- * a band axis spans its categories.
34
- *
35
- * Not yet supported: a line or area grouped by `z` that is not its colour
36
- * field. Its series ids reach the shader, but only to break the line between
37
- * series; no activity is read through them, and the series field is not among
38
- * the mark's reads. So a selection on that field filters the mark in other
39
- * charts and does nothing in its own, instead of dimming the other series.
35
+ * x1/x2. Lines and areas dim by colour and by series (`z`), never by a band
36
+ * axis: a line over a band axis spans its categories. Parallel coordinates
37
+ * have no `z` and dim by colour only.
40
38
  */
41
39
  export declare function activityReads(renderer: ActivityRenderer, hasExtent?: boolean): ActivityReads;
42
40
  /**
@@ -53,12 +51,14 @@ export declare function rendererForMark(markType: string): ActivityRenderer | nu
53
51
  * sorted, from what its renderer reads and the identity key of each of its
54
52
  * categorical scales (null for a channel with no such scale, or a scale with
55
53
  * no identity: a field with no identity has no activity to dim by, so a
56
- * selection on it filters the mark).
54
+ * selection on it filters the mark). `series` is null unless the mark has a
55
+ * `z` of its own; a series that is the colour field is already in `color`.
57
56
  */
58
57
  export declare function readsFor(reads: ActivityReads, keys: {
59
58
  color: string | null;
60
59
  x: string | null;
61
60
  y: string | null;
61
+ series?: string | null;
62
62
  }): readonly string[];
63
63
  /** A mark that reads no activity. Shared, so its reads never look changed. */
64
64
  export declare const NO_READS: readonly string[];
@@ -68,29 +68,40 @@ export declare const NO_READS: readonly string[];
68
68
  * by (its band axes, its ordinal colour) dims it, and any other selection
69
69
  * filters it. 'filter': every selection filters it, even on a field it draws
70
70
  * by, so its aggregates are recomputed for the subset (top N within a region).
71
+ * 'ignore': no selection filters or dims it; context, such as the whole
72
+ * drawn behind a filtered part.
71
73
  */
72
- export type SelectionResponse = 'auto' | 'filter';
74
+ export type SelectionResponse = 'auto' | 'filter' | 'ignore';
73
75
  export { NO_GROUP };
74
- /** A mark's reads under its selection response: none for 'filter', which never dims. */
76
+ /**
77
+ * Whether a selection can dim the mark: only under 'auto'. A 'filter' mark is
78
+ * filtered instead, and an 'ignore' mark neither.
79
+ */
80
+ export declare function dimsBySelection(response: SelectionResponse | undefined): boolean;
81
+ /** A mark's reads under its selection response: none for a mark that never dims. */
75
82
  export declare function readsForResponse(response: SelectionResponse | undefined, reads: readonly string[]): readonly string[];
76
- /** The group index a mark's shader reads activity with: NO_GROUP for 'filter'. */
83
+ /** The group index a mark's shader reads activity with: NO_GROUP for a mark that never dims. */
77
84
  export declare function groupIndexForResponse(response: SelectionResponse | undefined, groupIndex: number): number;
78
85
  /**
79
- * The WGSL activity of a renderer that reads colour only (lines, areas,
80
- * parallel coordinates): `markColorActivity(...)`, or 1.0 if the table says
81
- * it reads nothing. Throws for a renderer that reads a band axis, whose call
82
- * is markActivityWGSL.
86
+ * The WGSL activity of a renderer that reads no band axis (lines, areas,
87
+ * parallel coordinates): its colour activity and, for lines and areas, its
88
+ * series activity, 1.0 for each the table says it does not read.
89
+ *
90
+ * `series` is markColorActivity's first three arguments for the series scale,
91
+ * the same lookup as colour on another scale and buffer. The mark passes mode
92
+ * 0 when it has no `z` of its own, so the read returns 1.0 before loading
93
+ * anything. The two combine by `min`, the least active wins, as in
94
+ * markActivity: a series on the colour field cannot dim twice.
95
+ *
96
+ * Throws for a renderer that reads a band axis, whose call is
97
+ * markActivityWGSL, and for one that reads a series it was given no
98
+ * arguments for.
83
99
  */
84
- export declare function colorActivityWGSL(reads: ActivityReads, args: {
100
+ export declare function connectedActivityWGSL(reads: ActivityReads, args: {
85
101
  color: string;
102
+ series?: string;
86
103
  group: string;
87
104
  }): string;
88
- /**
89
- * The WGSL `markActivity(...)` call for `reads`: each activity the renderer
90
- * reads, and 1.0 for each it does not. `color` is markColorActivity's first
91
- * three arguments; `x` and `y` are the band channel values; `group` the mark's
92
- * group index.
93
- */
94
105
  /**
95
106
  * The activity of an Other row: the same terms as markActivityWGSL without the
96
107
  * band axes', and without colour's when colour is the band's own field
@@ -103,6 +114,12 @@ export declare function otherRowActivityWGSL(reads: ActivityReads, args: {
103
114
  group: string;
104
115
  skipColor: string;
105
116
  }): string;
117
+ /**
118
+ * The WGSL `markActivity(...)` call for `reads`: each activity the renderer
119
+ * reads, and 1.0 for each it does not. `color` is markColorActivity's first
120
+ * three arguments; `x` and `y` are the band channel values; `group` the mark's
121
+ * group index.
122
+ */
106
123
  export declare function markActivityWGSL(reads: ActivityReads, args: {
107
124
  color: string;
108
125
  x: string;
@@ -1,6 +1,7 @@
1
1
  /**
2
2
  * Which selection activities a mark reads: its ordinal colour, its band x,
3
- * its band y. One answer per renderer, used twice:
3
+ * its band y, and a line's or area's series. One answer per renderer, used
4
+ * twice:
4
5
  *
5
6
  * - The renderer builds its shader's `markActivity` call from it, so a read
6
7
  * the table does not list is passed as 1.0 and cannot happen.
@@ -20,18 +21,14 @@
20
21
  * axis, belongs to no category on it, and must not dim by it.
21
22
  */
22
23
  import { NO_GROUP } from '../../gpu/identity-slot-packing';
23
- const ALL = { color: true, x: true, y: true };
24
- const COLOR_ONLY = { color: true, x: false, y: false };
24
+ const ALL = { color: true, x: true, y: true, series: false };
25
+ const CONNECTED = { color: true, x: false, y: false, series: true };
26
+ const COLOR_ONLY = { color: true, x: false, y: false, series: false };
25
27
  /**
26
28
  * What a renderer reads. `hasExtent` is a rule's: RuleX with y1/y2, RuleY with
27
- * x1/x2. Lines, areas and parallel coordinates dim by colour only: a line over
28
- * a band axis spans its categories.
29
- *
30
- * Not yet supported: a line or area grouped by `z` that is not its colour
31
- * field. Its series ids reach the shader, but only to break the line between
32
- * series; no activity is read through them, and the series field is not among
33
- * the mark's reads. So a selection on that field filters the mark in other
34
- * charts and does nothing in its own, instead of dimming the other series.
29
+ * x1/x2. Lines and areas dim by colour and by series (`z`), never by a band
30
+ * axis: a line over a band axis spans its categories. Parallel coordinates
31
+ * have no `z` and dim by colour only.
35
32
  */
36
33
  export function activityReads(renderer, hasExtent = false) {
37
34
  switch (renderer) {
@@ -40,11 +37,12 @@ export function activityReads(renderer, hasExtent = false) {
40
37
  case 'text':
41
38
  return ALL;
42
39
  case 'rulex':
43
- return { color: true, x: true, y: hasExtent };
40
+ return { color: true, x: true, y: hasExtent, series: false };
44
41
  case 'ruley':
45
- return { color: true, x: hasExtent, y: true };
42
+ return { color: true, x: hasExtent, y: true, series: false };
46
43
  case 'lines':
47
44
  case 'areas':
45
+ return CONNECTED;
48
46
  case 'parallel':
49
47
  return COLOR_ONLY;
50
48
  }
@@ -91,7 +89,8 @@ export function rendererForMark(markType) {
91
89
  * sorted, from what its renderer reads and the identity key of each of its
92
90
  * categorical scales (null for a channel with no such scale, or a scale with
93
91
  * no identity: a field with no identity has no activity to dim by, so a
94
- * selection on it filters the mark).
92
+ * selection on it filters the mark). `series` is null unless the mark has a
93
+ * `z` of its own; a series that is the colour field is already in `color`.
95
94
  */
96
95
  export function readsFor(reads, keys) {
97
96
  const out = [];
@@ -101,6 +100,8 @@ export function readsFor(reads, keys) {
101
100
  out.push(keys.x);
102
101
  if (reads.y && keys.y)
103
102
  out.push(keys.y);
103
+ if (reads.series && keys.series)
104
+ out.push(keys.series);
104
105
  if (out.length === 0)
105
106
  return NO_READS;
106
107
  return [...new Set(out)].sort();
@@ -108,31 +109,47 @@ export function readsFor(reads, keys) {
108
109
  /** A mark that reads no activity. Shared, so its reads never look changed. */
109
110
  export const NO_READS = [];
110
111
  export { NO_GROUP };
111
- /** A mark's reads under its selection response: none for 'filter', which never dims. */
112
+ /**
113
+ * Whether a selection can dim the mark: only under 'auto'. A 'filter' mark is
114
+ * filtered instead, and an 'ignore' mark neither.
115
+ */
116
+ export function dimsBySelection(response) {
117
+ return response !== 'filter' && response !== 'ignore';
118
+ }
119
+ /** A mark's reads under its selection response: none for a mark that never dims. */
112
120
  export function readsForResponse(response, reads) {
113
- return response === 'filter' ? NO_READS : reads;
121
+ return dimsBySelection(response) ? reads : NO_READS;
114
122
  }
115
- /** The group index a mark's shader reads activity with: NO_GROUP for 'filter'. */
123
+ /** The group index a mark's shader reads activity with: NO_GROUP for a mark that never dims. */
116
124
  export function groupIndexForResponse(response, groupIndex) {
117
- return response === 'filter' ? NO_GROUP : groupIndex;
125
+ return dimsBySelection(response) ? groupIndex : NO_GROUP;
118
126
  }
119
127
  /**
120
- * The WGSL activity of a renderer that reads colour only (lines, areas,
121
- * parallel coordinates): `markColorActivity(...)`, or 1.0 if the table says
122
- * it reads nothing. Throws for a renderer that reads a band axis, whose call
123
- * is markActivityWGSL.
128
+ * The WGSL activity of a renderer that reads no band axis (lines, areas,
129
+ * parallel coordinates): its colour activity and, for lines and areas, its
130
+ * series activity, 1.0 for each the table says it does not read.
131
+ *
132
+ * `series` is markColorActivity's first three arguments for the series scale,
133
+ * the same lookup as colour on another scale and buffer. The mark passes mode
134
+ * 0 when it has no `z` of its own, so the read returns 1.0 before loading
135
+ * anything. The two combine by `min`, the least active wins, as in
136
+ * markActivity: a series on the colour field cannot dim twice.
137
+ *
138
+ * Throws for a renderer that reads a band axis, whose call is
139
+ * markActivityWGSL, and for one that reads a series it was given no
140
+ * arguments for.
124
141
  */
125
- export function colorActivityWGSL(reads, args) {
142
+ export function connectedActivityWGSL(reads, args) {
126
143
  if (reads.x || reads.y)
127
- throw new Error('colorActivityWGSL: this renderer reads a band axis');
128
- return reads.color ? `markColorActivity(${args.color}, ${args.group})` : '1.0';
144
+ throw new Error('connectedActivityWGSL: this renderer reads a band axis');
145
+ if (reads.series && !args.series) {
146
+ throw new Error('connectedActivityWGSL: this renderer reads a series; pass its arguments');
147
+ }
148
+ const color = reads.color ? `markColorActivity(${args.color}, ${args.group})` : '1.0';
149
+ if (!reads.series)
150
+ return color;
151
+ return `min(${color}, markColorActivity(${args.series}, ${args.group}))`;
129
152
  }
130
- /**
131
- * The WGSL `markActivity(...)` call for `reads`: each activity the renderer
132
- * reads, and 1.0 for each it does not. `color` is markColorActivity's first
133
- * three arguments; `x` and `y` are the band channel values; `group` the mark's
134
- * group index.
135
- */
136
153
  /**
137
154
  * The activity of an Other row: the same terms as markActivityWGSL without the
138
155
  * band axes', and without colour's when colour is the band's own field
@@ -146,6 +163,12 @@ export function otherRowActivityWGSL(reads, args) {
146
163
  : '1.0';
147
164
  return `markActivity(${color}, 1.0, 1.0)`;
148
165
  }
166
+ /**
167
+ * The WGSL `markActivity(...)` call for `reads`: each activity the renderer
168
+ * reads, and 1.0 for each it does not. `color` is markColorActivity's first
169
+ * three arguments; `x` and `y` are the band channel values; `group` the mark's
170
+ * group index.
171
+ */
149
172
  export function markActivityWGSL(reads, args) {
150
173
  const color = reads.color ? `markColorActivity(${args.color}, ${args.group})` : '1.0';
151
174
  const x = reads.x ? `bandActivityX(${args.x}, ${args.group})` : '1.0';
@@ -13,7 +13,7 @@
13
13
  export declare const PARALLEL_MAX_AXES = 16;
14
14
  export declare const UNIFORM_BYTES: {
15
15
  readonly dotsMarkConstants: 48;
16
- readonly rectsMarkConstants: 48;
16
+ readonly rectsMarkConstants: 64;
17
17
  readonly ruleXMarkConstants: 48;
18
18
  readonly ruleYMarkConstants: 48;
19
19
  readonly linesMarkConstants: 64;
@@ -15,7 +15,7 @@ export const PARALLEL_MAX_AXES = 16;
15
15
  const SCALE_PARAM_BYTES = 16;
16
16
  export const UNIFORM_BYTES = {
17
17
  dotsMarkConstants: 48,
18
- rectsMarkConstants: 48,
18
+ rectsMarkConstants: 64,
19
19
  ruleXMarkConstants: 48,
20
20
  ruleYMarkConstants: 48,
21
21
  linesMarkConstants: 64,
@@ -1,3 +1,4 @@
1
+ import { EntranceGate } from '../animation/EntranceGate';
1
2
  import type { SelectionResponse } from '../components/rendering/activity-reads';
2
3
  import type { FieldRef, LODConfig, SortConfig, ColorScheme, CurveType, NullsResponse, Transform } from '../types';
3
4
  import type { MarkLODSurface } from '../lod/rung-types';
@@ -27,6 +28,7 @@ export interface MarkEntry {
27
28
  selectionResponse?: SelectionResponse;
28
29
  contributeDomain?: boolean;
29
30
  nulls?: NullsResponse;
31
+ holdsEntrance?: boolean;
30
32
  limit?: number;
31
33
  preferredXScaleType?: string | null;
32
34
  preferredYScaleType?: string | null;
@@ -61,6 +63,11 @@ export interface ChartProps {
61
63
  }
62
64
  export declare class ChartConfig {
63
65
  #private;
66
+ /**
67
+ * Holds the chart's entrance until its marks have their first data. One per
68
+ * container, shared by its facet cells, as this config is.
69
+ */
70
+ readonly entrance: EntranceGate;
64
71
  /**
65
72
  * Register a mark with prop getters for each configurable property.
66
73
  * Returns the reactive entry that the mark reads from.
@@ -12,10 +12,17 @@
12
12
  // The entry outlives MarkLifecycle — registerMark returns the existing entry
13
13
  // on re-registration, preventing infinite loops.
14
14
  // =============================================================================
15
+ import { untrack } from 'svelte';
15
16
  import { SvelteMap } from 'svelte/reactivity';
17
+ import { EntranceGate } from '../animation/EntranceGate';
16
18
  import { createOverridable } from './overridable.svelte';
17
19
  // ---- ChartConfig ----
18
20
  export class ChartConfig {
21
+ /**
22
+ * Holds the chart's entrance until its marks have their first data. One per
23
+ * container, shared by its facet cells, as this config is.
24
+ */
25
+ entrance = new EntranceGate();
19
26
  /** Overridable channels per mark, for reset support. */
20
27
  #channels = new Map();
21
28
  /** Reactive entries — SvelteMap so iteration/size reads are tracked. */
@@ -45,6 +52,14 @@ export class ChartConfig {
45
52
  const existing = this.#entries.get(id);
46
53
  if (existing)
47
54
  return existing;
55
+ // A data mark holds the chart's entrance from the moment it mounts, which
56
+ // is now: its lifecycle is built later, in an effect that waits for the
57
+ // chart's scales, and until something holds the gate the chart looks
58
+ // like one with no data marks and enters on its first drawable layer.
59
+ if (untrack(() => propGetters.table?.()) &&
60
+ untrack(() => propGetters.holdsEntrance?.()) !== false) {
61
+ this.entrance.hold(id);
62
+ }
48
63
  const channels = new Map();
49
64
  const entry = { id, markType };
50
65
  for (const [key, getter] of Object.entries(propGetters)) {
@@ -66,6 +81,8 @@ export class ChartConfig {
66
81
  return entry;
67
82
  }
68
83
  unregisterMark(id) {
84
+ // A mark that leaves before its first data must not hold the chart.
85
+ this.entrance.release(id);
69
86
  this.#entries.delete(id);
70
87
  this.#channels.delete(id);
71
88
  }
@@ -1,3 +1,6 @@
1
+ import { type TransitionRecord } from '../animation/transition-log';
2
+ import type { EntranceMode } from '../types';
3
+ import type { EntranceGate } from '../animation/EntranceGate';
1
4
  import type { RendererDefinition } from './ScaleTemplateSystem';
2
5
  import type { ScaleRegistry } from '../scales/ScaleRegistry.svelte';
3
6
  import type { FilterGroupRegistry } from '../query-engine/FilterGroupRegistry.svelte';
@@ -88,6 +91,18 @@ interface ChartRegistrationConfig {
88
91
  zIndex?: number;
89
92
  facetIndex?: number;
90
93
  facetScale?: FacetScaleRef | null;
94
+ /** How the chart's marks enter when its data first draws. Default 'auto'. */
95
+ entrance?: EntranceMode;
96
+ /** Holds the entrance until the chart's marks have their first data. */
97
+ entranceGate?: EntranceGate | null;
98
+ }
99
+ /** A chart's entrance starting, for observeEntrances. */
100
+ export interface ChartEntrance {
101
+ chartId: string;
102
+ /** The chart's canvas, to find it in the page (a label on an ancestor). */
103
+ canvas: HTMLCanvasElement;
104
+ /** performance.now() when the entrance started. */
105
+ time: number;
91
106
  }
92
107
  /**
93
108
  * One chart's pixels as the coordinator rendered them, for the scale-source
@@ -142,6 +157,12 @@ export declare class GPUCoordinator {
142
157
  * left alone.
143
158
  */
144
159
  writeScaleParams(scaleId: string, view: Float32Array, offset: number): boolean;
160
+ /**
161
+ * Listen to every chart's entrance as it starts: when its marks have their
162
+ * first data and it first draws. For measuring load (how soon charts enter);
163
+ * returns the unsubscribe. Costs nothing while no one listens.
164
+ */
165
+ observeEntrances(listener: (entrance: ChartEntrance) => void): () => void;
145
166
  /**
146
167
  * Configure canvases so the coordinator can copy from them. Test-only: set
147
168
  * it before the charts mount, since a canvas reads it when it configures
@@ -263,10 +284,19 @@ export declare class GPUCoordinator {
263
284
  removePhysicsCallback(fn: () => void): void;
264
285
  registerChart(chartId: string, canvas: HTMLCanvasElement, config: ChartRegistrationConfig): ChartRegistrationResult;
265
286
  unregisterChart(chartId: string): void;
287
+ /**
288
+ * Debug: every transition that starts, and whether it animates or lands at
289
+ * once (see transition-log). Returns the unsubscribe. Forwards to the
290
+ * page-wide log: records from every coordinator in the document arrive
291
+ * here, so filter by chart id.
292
+ */
293
+ observeTransitions(listener: (record: TransitionRecord) => void): () => void;
294
+ /** Whether a registered chart is visible: in view (with the 200 px margin) and the page shown. */
295
+ isChartVisible(chartId: string): boolean;
266
296
  /**
267
297
  * Register a callback invoked when a chart's effective visibility changes.
268
298
  * Effective visibility = IntersectionObserver visible AND page visible.
269
- * Used by ChartCell to toggle Mosaic client.enabled.
299
+ * Used by ChartCell to report into the chart's ChartVisibility.
270
300
  */
271
301
  setVisibilityCallback(chartId: string, callback: (visible: boolean) => void): () => void;
272
302
  invalidateChart(chartId: string): void;