@fundar/data-chart-telling 1.0.0 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (155) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/README.md +57 -18
  3. package/dist/configuration/config.svelte.js +13 -1
  4. package/dist/configuration/themes/index.d.ts +124 -0
  5. package/dist/index.d.ts +2 -1
  6. package/dist/index.js +1 -0
  7. package/dist/layout/base/BasePlotLayout.svelte +18 -5
  8. package/dist/layout/hover/HoverSettings.d.ts +20 -6
  9. package/dist/layout/hover/HoverSettings.js +29 -4
  10. package/dist/layout/legend/LegendState.svelte.d.ts +5 -0
  11. package/dist/layout/legend/LegendState.svelte.js +4 -0
  12. package/dist/layout/plot/Plot.svelte +6 -3
  13. package/dist/layout/plot/PublicPlotContext.d.ts +5 -4
  14. package/dist/layout/plot/PublicPlotContext.js +7 -3
  15. package/dist/layout/plot/Slice.js +4 -2
  16. package/dist/layout/scales/EdgeTicks.d.ts +39 -0
  17. package/dist/layout/scales/EdgeTicks.js +82 -0
  18. package/dist/layout/scales/NiceTicks.d.ts +40 -0
  19. package/dist/layout/scales/NiceTicks.js +72 -0
  20. package/dist/layout/scales/ScalesLayout.svelte +64 -30
  21. package/dist/layout/scales/ScalesLayout.svelte.d.ts +5 -0
  22. package/dist/layout/scales/SveltePlotScale.d.ts +15 -0
  23. package/dist/layout/scales/SveltePlotScale.js +79 -0
  24. package/dist/layout/styles/ValueLabels.js +3 -2
  25. package/dist/layout/timeline/Timeline.svelte +5 -2
  26. package/dist/layout/timeline/Timeline.svelte.d.ts +2 -0
  27. package/dist/layout/timeline/TimelineLayout.svelte +38 -12
  28. package/dist/layout/timeline/TimelineLayout.svelte.d.ts +7 -5
  29. package/dist/layout/timeline/TimelineRule.svelte +1 -0
  30. package/dist/layout/timeline/TimelineState.svelte.d.ts +13 -5
  31. package/dist/layout/timeline/TimelineState.svelte.js +42 -8
  32. package/dist/layout/timeline/TimelineTime.d.ts +18 -0
  33. package/dist/layout/timeline/TimelineTime.js +117 -0
  34. package/dist/layout/tooltip/Tooltip.svelte +6 -3
  35. package/dist/layout/tooltip/Tooltip.svelte.d.ts +2 -0
  36. package/dist/layout/tooltip/TooltipState.svelte.d.ts +8 -2
  37. package/dist/layout/tooltip/TooltipState.svelte.js +22 -3
  38. package/dist/markers/delta/DeltaMarker.svelte +9 -7
  39. package/dist/phases/Pipeline.js +3 -2
  40. package/dist/phases/data/DataPhase.d.ts +5 -11
  41. package/dist/phases/data/DataPhase.js +11 -8
  42. package/dist/phases/facet/FacetPanels.d.ts +54 -0
  43. package/dist/phases/facet/FacetPanels.js +95 -0
  44. package/dist/phases/facet/FacetPhase.d.ts +1 -42
  45. package/dist/phases/facet/FacetPhase.js +1 -87
  46. package/dist/phases/hover/HoverPhase.d.ts +6 -43
  47. package/dist/phases/hover/HoverPhase.js +19 -54
  48. package/dist/phases/mapping/MappingPhase.d.ts +1 -1
  49. package/dist/phases/mapping/MappingPhase.js +2 -4
  50. package/dist/phases/scales/ScalesPhase.d.ts +1 -3
  51. package/dist/phases/scales/ScalesPhase.js +17 -21
  52. package/dist/phases/sharedScales/SharedScalesPhase.d.ts +4 -1
  53. package/dist/phases/sharedScales/SharedScalesPhase.js +10 -7
  54. package/dist/phases/statistics/StatisticsPhase.js +3 -4
  55. package/dist/phases/tooltip/TooltipPhase.d.ts +4 -2
  56. package/dist/phases/tooltip/TooltipPhase.js +10 -3
  57. package/dist/phases/transforms/ProcessedPositions.d.ts +30 -0
  58. package/dist/phases/transforms/ProcessedPositions.js +74 -0
  59. package/dist/phases/transforms/TransformsPhase.d.ts +5 -42
  60. package/dist/phases/transforms/TransformsPhase.js +20 -57
  61. package/dist/plots/area/markers.js +1 -2
  62. package/dist/plots/bar/defaults.js +6 -10
  63. package/dist/plots/bar/markers.js +5 -4
  64. package/dist/plots/box/BoxSummaries.d.ts +19 -0
  65. package/dist/plots/box/BoxSummaries.js +101 -0
  66. package/dist/plots/box/Plot.svelte +42 -0
  67. package/dist/plots/box/Plot.svelte.d.ts +26 -0
  68. package/dist/plots/box/defaults.d.ts +12 -0
  69. package/dist/plots/box/defaults.js +34 -0
  70. package/dist/plots/box/markers.d.ts +12 -0
  71. package/dist/plots/box/markers.js +34 -0
  72. package/dist/plots/geo/phases/hover.js +5 -5
  73. package/dist/plots/geo/phases/markers.js +3 -4
  74. package/dist/plots/geo/slices.js +6 -3
  75. package/dist/plots/heatmap/markers.js +2 -1
  76. package/dist/plots/histogram/defaults.js +1 -1
  77. package/dist/plots/histogram/markers.js +4 -4
  78. package/dist/plots/line/markers.js +1 -2
  79. package/dist/plots/pyramid/defaults.js +3 -2
  80. package/dist/plots/pyramid/markers.js +3 -6
  81. package/dist/plots/scatter/defaults.js +3 -2
  82. package/dist/plots/scatter/markers.js +4 -3
  83. package/dist/types/configuration/styling.d.ts +24 -2
  84. package/dist/types/layout/composer.d.ts +5 -2
  85. package/dist/types/layout/scales.d.ts +16 -0
  86. package/dist/types/layout/timeline.d.ts +14 -5
  87. package/dist/types/phases/hover/phase.d.ts +6 -0
  88. package/dist/types/phases/state.d.ts +5 -2
  89. package/dist/types/phases/statistics/phase.d.ts +3 -1
  90. package/dist/types/phases/transforms/phase.d.ts +10 -0
  91. package/dist/types/plots/box/props.d.ts +10 -0
  92. package/dist/types/plots/box/props.js +1 -0
  93. package/dist/types/plots/box/state.d.ts +23 -0
  94. package/dist/types/plots/box/state.js +1 -0
  95. package/dist/types/plots/props.d.ts +2 -0
  96. package/docs/chrome.md +39 -8
  97. package/docs/composer.md +19 -3
  98. package/docs/concepts.md +1 -1
  99. package/docs/development.md +14 -5
  100. package/docs/facets.md +62 -8
  101. package/docs/getting-started.md +8 -9
  102. package/docs/legend.md +62 -15
  103. package/docs/markers.md +17 -6
  104. package/docs/plots.md +168 -42
  105. package/docs/scales.md +36 -19
  106. package/docs/state.md +1 -1
  107. package/docs/styles-and-segments.md +85 -31
  108. package/docs/theming.md +30 -19
  109. package/docs/timeline.md +62 -28
  110. package/docs/tooltip-and-hover.md +72 -24
  111. package/docs/transforms-and-statistics.md +47 -17
  112. package/examples/BarValues.svelte +2 -2
  113. package/examples/Box.svelte +7 -0
  114. package/examples/Chrome.svelte +17 -0
  115. package/examples/ChromeOrder.svelte +18 -0
  116. package/examples/Composer.svelte +23 -0
  117. package/examples/CustomTooltip.svelte +21 -14
  118. package/examples/Facets.svelte +3 -3
  119. package/examples/FacetsCarousel.svelte +14 -0
  120. package/examples/FacetsRowsCols.svelte +23 -0
  121. package/examples/FirstPlot.svelte +7 -0
  122. package/examples/Gaps.svelte +21 -0
  123. package/examples/GroupedBars.svelte +15 -0
  124. package/examples/Heatmap.svelte +7 -0
  125. package/examples/HeatmapSegments.svelte +26 -0
  126. package/examples/Histogram.svelte +14 -0
  127. package/examples/HorizontalBars.svelte +15 -0
  128. package/examples/LegendSections.svelte +27 -0
  129. package/examples/LegendToggle.svelte +3 -3
  130. package/examples/LineByRegion.svelte +18 -0
  131. package/examples/LineDates.svelte +15 -0
  132. package/examples/Markers.svelte +27 -0
  133. package/examples/MovingAverage.svelte +15 -0
  134. package/examples/Pyramid.svelte +7 -0
  135. package/examples/Regression.svelte +14 -0
  136. package/examples/ResponsiveLine.svelte +7 -7
  137. package/examples/Scales.svelte +26 -0
  138. package/examples/Scatter.svelte +16 -0
  139. package/examples/SegmentHighlight.svelte +22 -0
  140. package/examples/Segments.svelte +11 -3
  141. package/examples/SegmentsBySeries.svelte +25 -0
  142. package/examples/StackedArea.svelte +16 -0
  143. package/examples/StackedBars.svelte +15 -0
  144. package/examples/TimelinePlayer.svelte +8 -7
  145. package/examples/TimelineRule.svelte +19 -0
  146. package/examples/Tooltip.svelte +3 -3
  147. package/examples/ValueLabels.svelte +21 -0
  148. package/examples/data.ts +320 -8
  149. package/llms-full.txt +33 -5
  150. package/llms.txt +30 -2
  151. package/package.json +3 -2
  152. package/dist/layout/scales/buildScale.d.ts +0 -8
  153. package/dist/layout/scales/buildScale.js +0 -67
  154. package/dist/layout/scales/niceDomain.d.ts +0 -23
  155. package/dist/layout/scales/niceDomain.js +0 -59
package/CHANGELOG.md CHANGED
@@ -2,6 +2,27 @@
2
2
 
3
3
  All notable changes to this project are documented here. The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and the project adheres to [Semantic Versioning](https://semver.org/).
4
4
 
5
+ ## [1.1.0] - 2026-10-20
6
+
7
+ ### Added
8
+
9
+ - `BoxPlot`, a new plot kind for the distribution of values per category. It draws one box per category of each series: the quartiles, the median, whiskers that reach the farthest values within 1.5 times the interquartile range, and the values past them as outlier dots.
10
+ - Boxes stand upright when the categories are on `x` and lie sideways when they are on `y`.
11
+ - `grouping` or `color` gives each group its own box and color, and `facet` compares groups in separate panels.
12
+ - The tooltip reads one box at a time instead of every value inside it. The hovered row carries `category`, `min`, `p25`, `median`, `p75`, `max`, `count` and `outliers` for a custom `format`.
13
+ - Categories keep the order the data gives them, and the value axis fits the values without reaching for zero.
14
+ - It takes the same props as every other plot: `segments` to restyle whole categories, the interactive legend, `markers`, `styles.frame` and the rest.
15
+ - New `box` block in the configuration for the box fill opacity and the spacing between boxes (`fillOpacity`, `paddingInner`, `paddingOuter`).
16
+
17
+ ### Fixed
18
+
19
+ - Nice axis domains no longer grow a whole extra step when the data ends just past a round value (parts that sum to `100.00000000000001` stay on `100`). `niceTolerance` on a scale sets how far past a tick, as a share of the step, the data may end and still stay on it.
20
+ - Ticks at the data's first and last values no longer crowd the nice tick next to them. `axis.edgeTicks` in the configuration (or `edgeTicks` on a scale) chooses what happens then: `collision` is `'drop-nice'`, `'drop-edge'` or `'extend'`.
21
+ - A tooltip that lists several rows shows them highest value first.
22
+ - The timeline accepts steps given as `Date` objects or date strings (ISO, or any pattern set with `dateFormat`), not only numbers.
23
+ - The `delta` marker's label offset and anchor have defaults per axis in the configuration (`delta.text`).
24
+ - Documentation and examples brought up to date with the current API.
25
+
5
26
  ## [1.0.0] - 2026-10-16
6
27
 
7
28
  First stable release.
package/README.md CHANGED
@@ -18,24 +18,21 @@ Svelte 5 plots for data storytelling, built on [svelteplot](https://svelteplot.d
18
18
 
19
19
  It aims for a sweet spot between the flexibility of `d3`, the declarative spirit of the _grammar of graphics_ (data mapped to channels, scales and marks) and the convenience of a classic plotting library, where each plot kind is a first-class component.
20
20
 
21
+ <!-- example: examples/LineByRegion.svelte -->
22
+
21
23
  ```svelte
22
24
  <script lang="ts">
23
25
  import { LinePlot } from '@fundar/data-chart-telling';
24
-
25
- const rows = [
26
- { year: 2020, region: 'North', value: 3.1 },
27
- { year: 2021, region: 'North', value: 3.8 },
28
- { year: 2020, region: 'South', value: 2.2 },
29
- { year: 2021, region: 'South', value: 2.9 }
30
- ];
26
+ import { REGION_VALUES } from './data';
31
27
  </script>
32
28
 
29
+ <!-- One line per region, with a title, the automatic legend, end-of-line values and a tooltip. -->
33
30
  <LinePlot
34
31
  width={720}
35
32
  height={400}
36
- title="Value by region"
37
- data={rows}
38
- x="year"
33
+ title="Monthly inflation by region (%)"
34
+ data={REGION_VALUES}
35
+ x="date"
39
36
  y="value"
40
37
  color="region"
41
38
  tooltip
@@ -44,9 +41,13 @@ It aims for a sweet spot between the flexibility of `d3`, the declarative spirit
44
41
  />
45
42
  ```
46
43
 
47
- ![A line plot with a title, a legend, end-of-line value labels and a tooltip](docs/images/readme-hero.png)
44
+ <!-- /example -->
45
+
46
+ ![A line plot with a title, a legend, end-of-line value labels and a tooltip](docs/images/line-by-region.png)
48
47
 
49
- Eight plot kinds - line, area, bar, histogram, heatmap, pyramid, scatter and geographic maps - share one vocabulary: the same channels (`x`, `y`, `color`, `grouping`, `opacity`, `symbol`, `r`), the same `scales`, `styles`, `segments`, `markers`, `transforms`, `tooltip`, `legend`, `timeline` and `facet` props, and one global theme.
48
+ Every Svelte snippet in this README is a runnable file in [examples/](examples/), and the data comes from [examples/data.ts](examples/data.ts).
49
+
50
+ Nine plot kinds - line, area, bar, histogram, box, heatmap, pyramid, scatter and geographic maps - share one vocabulary: the same channels (`x`, `y`, `color`, `grouping`, `opacity`, `symbol`, `r`), the same `scales`, `styles`, `segments`, `markers`, `transforms`, `tooltip`, `legend`, `timeline` and `facet` props, and one global theme.
50
51
 
51
52
  ## Install
52
53
 
@@ -62,26 +63,64 @@ The package ships Svelte components, so the app needs the Svelte compiler (`@sve
62
63
 
63
64
  `timeline` slices the rows by a field and steps through them. It draws as a **player** by default: play and pause, the current step and a slider, in the figure's chrome.
64
65
 
66
+ <!-- example: examples/TimelinePlayer.svelte -->
67
+
65
68
  ```svelte
66
- <BarPlot {width} {height} data={rows} x="region" y="value" timeline={{ accessor: 'year' }} />
69
+ <script lang="ts">
70
+ import { BarPlot } from '@fundar/data-chart-telling';
71
+ import { REGION_VALUES } from './data';
72
+ </script>
73
+
74
+ <!--
75
+ `timeline` slices the rows by `accessor`. A timeline is a player by default:
76
+ play/pause, the current step and a slider in the chrome. `progress` sets where
77
+ it starts. `type: 'rule'` draws a guide beside the plot with no controls
78
+ instead (see TimelineRule). To build your own widget, read
79
+ `useFigure().timeline` (seek, play, pause, next, ...).
80
+ -->
81
+ <BarPlot
82
+ width={720}
83
+ height={400}
84
+ data={REGION_VALUES}
85
+ x="region"
86
+ y="value"
87
+ scales={{ y: { domain: [0, 30] } }}
88
+ timeline={{ accessor: 'date', progress: 0.25, player: { autoplay: false, loop: true } }}
89
+ />
67
90
  ```
68
91
 
92
+ <!-- /example -->
93
+
69
94
  ![A bar plot with a player bar](docs/images/timeline-player.png)
70
95
 
71
96
  `type: 'rule'` draws a **rule** beside the plot instead, a line with the steps written along it. The page drives it through `progress` (`0` to `1`), from a slider, scroll position or anything else.
72
97
 
98
+ <!-- example: examples/TimelineRule.svelte -->
99
+
73
100
  ```svelte
101
+ <script lang="ts">
102
+ import { LinePlot } from '@fundar/data-chart-telling';
103
+ import { REGION_VALUES } from './data';
104
+
105
+ // A rule has no controls of its own: the page moves it through `progress` (0 to 1).
106
+ // One step per year; a line plot shows every row up to the current step.
107
+ let progress = $state(0.5);
108
+ </script>
109
+
110
+ <input type="range" min="0" max="1" step="0.001" bind:value={progress} />
74
111
  <LinePlot
75
- {width}
76
- {height}
77
- data={rows}
78
- x="year"
112
+ width={720}
113
+ height={400}
114
+ data={REGION_VALUES}
115
+ x="date"
79
116
  y="value"
80
117
  color="region"
81
- timeline={{ accessor: 'year', type: 'rule', progress }}
118
+ timeline={{ accessor: (row) => row.date.getUTCFullYear(), type: 'rule', progress }}
82
119
  />
83
120
  ```
84
121
 
122
+ <!-- /example -->
123
+
85
124
  ![A line plot revealed up to the current step, with a timeline rule](docs/images/timeline-rule.png)
86
125
 
87
126
  See [Timeline](docs/timeline.md) for both in detail.
@@ -116,7 +116,11 @@ export const DEFAULT_CONFIG = {
116
116
  yTickPadding: 10,
117
117
  grid: false,
118
118
  locale: 'es-AR',
119
- compactDisplay: 'short'
119
+ compactDisplay: 'short',
120
+ edgeTicks: {
121
+ x: { enabled: true, collision: 'drop-nice' },
122
+ y: { enabled: false, collision: 'drop-nice' }
123
+ }
120
124
  },
121
125
  tooltip: {
122
126
  background: '#111827',
@@ -157,6 +161,7 @@ export const DEFAULT_CONFIG = {
157
161
  pyramid: { gap: { fillOpacity: 0.5 } },
158
162
  area: { fillOpacity: 0.6 },
159
163
  histogram: { inset: 1, fillOpacity: 1 },
164
+ box: { fillOpacity: 0.9, paddingInner: 0.4, paddingOuter: 0.2 },
160
165
  scatter: { dotRadius: 5 },
161
166
  timeline: {
162
167
  axisColor: 'currentColor',
@@ -214,6 +219,13 @@ export const DEFAULT_CONFIG = {
214
219
  font: { fill: 'currentColor', fontSize: 12, dy: -18 },
215
220
  area: { fill: '#94a3b8', fillOpacity: 0.25 }
216
221
  },
222
+ // Beside a vertical delta (taken on x), above a horizontal one (on y).
223
+ delta: {
224
+ text: {
225
+ x: { dx: 12, dy: 0, lineAnchor: 'middle', textAnchor: 'start' },
226
+ y: { dx: 0, dy: -10, lineAnchor: 'middle', textAnchor: 'middle' }
227
+ }
228
+ },
217
229
  facet: {
218
230
  indicator: {
219
231
  background: '#111827',
@@ -116,6 +116,16 @@ export declare const THEMES: {
116
116
  grid?: boolean | undefined;
117
117
  locale?: string | undefined;
118
118
  compactDisplay?: "short" | "long" | undefined;
119
+ edgeTicks?: {
120
+ x?: {
121
+ enabled?: boolean | undefined;
122
+ collision?: import("../../types/layout/scales").EdgeTickCollision | undefined;
123
+ } | undefined;
124
+ y?: {
125
+ enabled?: boolean | undefined;
126
+ collision?: import("../../types/layout/scales").EdgeTickCollision | undefined;
127
+ } | undefined;
128
+ } | undefined;
119
129
  } | undefined;
120
130
  tooltip?: {
121
131
  background?: string | undefined;
@@ -213,6 +223,11 @@ export declare const THEMES: {
213
223
  inset?: number | undefined;
214
224
  fillOpacity?: number | undefined;
215
225
  } | undefined;
226
+ box?: {
227
+ fillOpacity?: number | undefined;
228
+ paddingInner?: number | undefined;
229
+ paddingOuter?: number | undefined;
230
+ } | undefined;
216
231
  scatter?: {
217
232
  dotRadius?: number | undefined;
218
233
  } | undefined;
@@ -315,6 +330,22 @@ export declare const THEMES: {
315
330
  borderRadius?: number | undefined;
316
331
  } | undefined;
317
332
  } | undefined;
333
+ delta?: {
334
+ text?: {
335
+ x?: {
336
+ dx?: number | undefined;
337
+ dy?: number | undefined;
338
+ lineAnchor?: import("../../types/plots/constants").LineAnchor | undefined;
339
+ textAnchor?: import("../../types/plots/constants").TextAnchor | undefined;
340
+ } | undefined;
341
+ y?: {
342
+ dx?: number | undefined;
343
+ dy?: number | undefined;
344
+ lineAnchor?: import("../../types/plots/constants").LineAnchor | undefined;
345
+ textAnchor?: import("../../types/plots/constants").TextAnchor | undefined;
346
+ } | undefined;
347
+ } | undefined;
348
+ } | undefined;
318
349
  facet?: {
319
350
  indicator?: {
320
351
  background?: string | undefined;
@@ -442,6 +473,16 @@ export declare const THEMES: {
442
473
  grid?: boolean | undefined;
443
474
  locale?: string | undefined;
444
475
  compactDisplay?: "short" | "long" | undefined;
476
+ edgeTicks?: {
477
+ x?: {
478
+ enabled?: boolean | undefined;
479
+ collision?: import("../../types/layout/scales").EdgeTickCollision | undefined;
480
+ } | undefined;
481
+ y?: {
482
+ enabled?: boolean | undefined;
483
+ collision?: import("../../types/layout/scales").EdgeTickCollision | undefined;
484
+ } | undefined;
485
+ } | undefined;
445
486
  } | undefined;
446
487
  tooltip?: {
447
488
  background?: string | undefined;
@@ -539,6 +580,11 @@ export declare const THEMES: {
539
580
  inset?: number | undefined;
540
581
  fillOpacity?: number | undefined;
541
582
  } | undefined;
583
+ box?: {
584
+ fillOpacity?: number | undefined;
585
+ paddingInner?: number | undefined;
586
+ paddingOuter?: number | undefined;
587
+ } | undefined;
542
588
  scatter?: {
543
589
  dotRadius?: number | undefined;
544
590
  } | undefined;
@@ -641,6 +687,22 @@ export declare const THEMES: {
641
687
  borderRadius?: number | undefined;
642
688
  } | undefined;
643
689
  } | undefined;
690
+ delta?: {
691
+ text?: {
692
+ x?: {
693
+ dx?: number | undefined;
694
+ dy?: number | undefined;
695
+ lineAnchor?: import("../../types/plots/constants").LineAnchor | undefined;
696
+ textAnchor?: import("../../types/plots/constants").TextAnchor | undefined;
697
+ } | undefined;
698
+ y?: {
699
+ dx?: number | undefined;
700
+ dy?: number | undefined;
701
+ lineAnchor?: import("../../types/plots/constants").LineAnchor | undefined;
702
+ textAnchor?: import("../../types/plots/constants").TextAnchor | undefined;
703
+ } | undefined;
704
+ } | undefined;
705
+ } | undefined;
644
706
  facet?: {
645
707
  indicator?: {
646
708
  background?: string | undefined;
@@ -768,6 +830,16 @@ export declare const THEMES: {
768
830
  grid?: boolean | undefined;
769
831
  locale?: string | undefined;
770
832
  compactDisplay?: "short" | "long" | undefined;
833
+ edgeTicks?: {
834
+ x?: {
835
+ enabled?: boolean | undefined;
836
+ collision?: import("../../types/layout/scales").EdgeTickCollision | undefined;
837
+ } | undefined;
838
+ y?: {
839
+ enabled?: boolean | undefined;
840
+ collision?: import("../../types/layout/scales").EdgeTickCollision | undefined;
841
+ } | undefined;
842
+ } | undefined;
771
843
  } | undefined;
772
844
  tooltip?: {
773
845
  background?: string | undefined;
@@ -865,6 +937,11 @@ export declare const THEMES: {
865
937
  inset?: number | undefined;
866
938
  fillOpacity?: number | undefined;
867
939
  } | undefined;
940
+ box?: {
941
+ fillOpacity?: number | undefined;
942
+ paddingInner?: number | undefined;
943
+ paddingOuter?: number | undefined;
944
+ } | undefined;
868
945
  scatter?: {
869
946
  dotRadius?: number | undefined;
870
947
  } | undefined;
@@ -967,6 +1044,22 @@ export declare const THEMES: {
967
1044
  borderRadius?: number | undefined;
968
1045
  } | undefined;
969
1046
  } | undefined;
1047
+ delta?: {
1048
+ text?: {
1049
+ x?: {
1050
+ dx?: number | undefined;
1051
+ dy?: number | undefined;
1052
+ lineAnchor?: import("../../types/plots/constants").LineAnchor | undefined;
1053
+ textAnchor?: import("../../types/plots/constants").TextAnchor | undefined;
1054
+ } | undefined;
1055
+ y?: {
1056
+ dx?: number | undefined;
1057
+ dy?: number | undefined;
1058
+ lineAnchor?: import("../../types/plots/constants").LineAnchor | undefined;
1059
+ textAnchor?: import("../../types/plots/constants").TextAnchor | undefined;
1060
+ } | undefined;
1061
+ } | undefined;
1062
+ } | undefined;
970
1063
  facet?: {
971
1064
  indicator?: {
972
1065
  background?: string | undefined;
@@ -1094,6 +1187,16 @@ export declare const THEMES: {
1094
1187
  grid?: boolean | undefined;
1095
1188
  locale?: string | undefined;
1096
1189
  compactDisplay?: "short" | "long" | undefined;
1190
+ edgeTicks?: {
1191
+ x?: {
1192
+ enabled?: boolean | undefined;
1193
+ collision?: import("../../types/layout/scales").EdgeTickCollision | undefined;
1194
+ } | undefined;
1195
+ y?: {
1196
+ enabled?: boolean | undefined;
1197
+ collision?: import("../../types/layout/scales").EdgeTickCollision | undefined;
1198
+ } | undefined;
1199
+ } | undefined;
1097
1200
  } | undefined;
1098
1201
  tooltip?: {
1099
1202
  background?: string | undefined;
@@ -1191,6 +1294,11 @@ export declare const THEMES: {
1191
1294
  inset?: number | undefined;
1192
1295
  fillOpacity?: number | undefined;
1193
1296
  } | undefined;
1297
+ box?: {
1298
+ fillOpacity?: number | undefined;
1299
+ paddingInner?: number | undefined;
1300
+ paddingOuter?: number | undefined;
1301
+ } | undefined;
1194
1302
  scatter?: {
1195
1303
  dotRadius?: number | undefined;
1196
1304
  } | undefined;
@@ -1293,6 +1401,22 @@ export declare const THEMES: {
1293
1401
  borderRadius?: number | undefined;
1294
1402
  } | undefined;
1295
1403
  } | undefined;
1404
+ delta?: {
1405
+ text?: {
1406
+ x?: {
1407
+ dx?: number | undefined;
1408
+ dy?: number | undefined;
1409
+ lineAnchor?: import("../../types/plots/constants").LineAnchor | undefined;
1410
+ textAnchor?: import("../../types/plots/constants").TextAnchor | undefined;
1411
+ } | undefined;
1412
+ y?: {
1413
+ dx?: number | undefined;
1414
+ dy?: number | undefined;
1415
+ lineAnchor?: import("../../types/plots/constants").LineAnchor | undefined;
1416
+ textAnchor?: import("../../types/plots/constants").TextAnchor | undefined;
1417
+ } | undefined;
1418
+ } | undefined;
1419
+ } | undefined;
1296
1420
  facet?: {
1297
1421
  indicator?: {
1298
1422
  background?: string | undefined;
package/dist/index.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  export { default as AreaPlot } from './plots/area/Plot.svelte';
2
2
  export { default as BarPlot } from './plots/bar/Plot.svelte';
3
+ export { default as BoxPlot } from './plots/box/Plot.svelte';
3
4
  export { default as HistogramPlot } from './plots/histogram/Plot.svelte';
4
5
  export { default as HeatmapPlot } from './plots/heatmap/Plot.svelte';
5
6
  export { default as LinePlot } from './plots/line/Plot.svelte';
@@ -41,7 +42,7 @@ export type { GeoCustomProjection, GeoCustomProjectionFactory, GeoScalesConfig,
41
42
  export type { GeoCameraConfig } from './types/plots/camera';
42
43
  export type { GeoMarkersConfig } from './types/markers/geo';
43
44
  export type { SharedAxisValues } from './types/phases';
44
- export type { TimeValue, Orientation, TimelineConfig, TimelineType, TimelineCallbacks, PlayerConfig, PlayerControl } from './types/layout/timeline';
45
+ export type { TimelineConfig, TimelineType, TimelineCallbacks, PlayerConfig, PlayerControl } from './types/layout/timeline';
45
46
  export type { ComposerLayout, ComposerTags, ComposerTimelineConfig, ComposerHoverConfig } from './types/layout/composer';
46
47
  export type { GridPlacement, GridInset, GridCellContext, GridCellSnippet } from './types/layout/grid';
47
48
  export type { Responsive, FacetConfig, FacetCellContext, FacetIndicatorContext, FacetIndicatorSnippet, FacetNavDirection, FacetNavContext, FacetNavSnippet } from './types/layout/facet';
package/dist/index.js CHANGED
@@ -3,6 +3,7 @@
3
3
  // timeline and facets around it.
4
4
  export { default as AreaPlot } from './plots/area/Plot.svelte';
5
5
  export { default as BarPlot } from './plots/bar/Plot.svelte';
6
+ export { default as BoxPlot } from './plots/box/Plot.svelte';
6
7
  export { default as HistogramPlot } from './plots/histogram/Plot.svelte';
7
8
  export { default as HeatmapPlot } from './plots/heatmap/Plot.svelte';
8
9
  export { default as LinePlot } from './plots/line/Plot.svelte';
@@ -46,10 +46,11 @@
46
46
  import { PlotMargins } from '../margins/PlotMargins.svelte';
47
47
  import { LegendState } from '../legend/LegendState.svelte';
48
48
  import { onDestroy, untrack } from 'svelte';
49
+ import { SvelteMap } from 'svelte/reactivity';
49
50
  import type { ComponentProps, Snippet } from 'svelte';
50
51
  import { Plot, Pointer, setPlotDefaults } from 'svelteplot';
51
52
  import { getConfiguration } from '../../configuration/config.svelte';
52
- import { buildScale } from '../scales/buildScale';
53
+ import { SveltePlotScale } from '../scales/SveltePlotScale';
53
54
  import ScalesLayout from '../scales/ScalesLayout.svelte';
54
55
  import GeometryLayout from '../geometry/GeometryLayout.svelte';
55
56
  import CoordinatesLayout from '../coordinates/CoordinatesLayout.svelte';
@@ -187,9 +188,20 @@
187
188
  size: () => ({ width, height })
188
189
  });
189
190
 
190
- // `buildScale` already left only svelteplot's own scale options.
191
- const resolvedX = $derived(buildScale(scales?.x, data, getX) as SveltePlotProps['x']);
192
- const resolvedY = $derived(buildScale(scales?.y, data, getY) as SveltePlotProps['y']);
191
+ /** The domains `ScalesLayout` widens an axis to, for an edge tick's `'extend'` (see `EdgeTicks`). */
192
+ const edgeDomains = new SvelteMap<'x' | 'y', [number, number]>();
193
+
194
+ // `SveltePlotScale.build` already left only svelteplot's own scale options.
195
+ const resolvedX = $derived.by(() => {
196
+ const built = SveltePlotScale.build(scales?.x, data, getX);
197
+ const domain = edgeDomains.get('x');
198
+ return (domain ? { ...built, domain, nice: false } : built) as SveltePlotProps['x'];
199
+ });
200
+ const resolvedY = $derived.by(() => {
201
+ const built = SveltePlotScale.build(scales?.y, data, getY);
202
+ const domain = edgeDomains.get('y');
203
+ return (domain ? { ...built, domain, nice: false } : built) as SveltePlotProps['y'];
204
+ });
193
205
  /** Threaded straight into svelteplot's own `r` scale option - see `RScaleConfig`'s own doc comment for why this isn't independently resolved like `color`. */
194
206
  const resolvedR = $derived(scales?.r as SveltePlotProps['r']);
195
207
  /** Threaded straight into svelteplot's own `length` scale option - see `LengthScaleConfig`'s own doc comment. `GeoPlot`-only in practice (see `scales`'s own doc comment), same mechanism as `r`. */
@@ -253,7 +265,7 @@
253
265
  <StylesLayout styles={state.styles} {width} {height} {numericMargins} />
254
266
  <!-- Axes for every kind but GeoPlot. -->
255
267
  {#if scales !== undefined && coordinates === undefined}
256
- <ScalesLayout {data} {getX} {getY} {width} {height} {numericMargins} {scales} />
268
+ <ScalesLayout {data} {getX} {getY} {width} {height} {numericMargins} {scales} {edgeDomains} />
257
269
  {/if}
258
270
  <GeometryLayout
259
271
  markers={resolvedMarkers}
@@ -297,6 +309,7 @@
297
309
  y={ctrl.tooltipPosition?.y ?? 0}
298
310
  rows={ctrl.matchedRows.map((p) => p.row)}
299
311
  format={hover.tooltip.format}
312
+ sort={hover.tooltip.sort}
300
313
  content={hover.tooltip.content}
301
314
  anchorX={hover.tooltip.anchorX}
302
315
  anchorY={hover.tooltip.anchorY}
@@ -1,11 +1,10 @@
1
1
  import type { TooltipProp } from '../../types/layout/tooltip';
2
- import type { HoverStrategy } from '../../types/layout/hover';
3
- import type { TooltipConfig } from '../../types/plots/props';
2
+ import type { HoverPoint, HoverStrategy } from '../../types/layout/hover';
3
+ import { PlotAxes } from '../../phases/data/PlotAxes';
4
+ import type { HoverConfig, PointMatchingStrategy, TooltipConfig } from '../../types/plots/props';
4
5
  /**
5
- * How the author set up a plot's hover, from its `tooltip` and its hover
6
- * marker (`'hover'`, or `'geo-hover'` on a map): whether it is on, the
7
- * strategy that matches points (the tooltip's, else the marker's, else the
8
- * plot kind's `defaultStrategy`) and whether it syncs across plots.
6
+ * A plot's hover as its `tooltip` and hover marker set it up: whether it is on,
7
+ * how it matches points and whether it syncs across plots.
9
8
  */
10
9
  export declare class HoverSettings<TRow extends Record<string, unknown>> {
11
10
  /** The tooltip, or `undefined` when off. */
@@ -17,4 +16,19 @@ export declare class HoverSettings<TRow extends Record<string, unknown>> {
17
16
  constructor(tooltip: TooltipProp<TRow> | TooltipConfig<TRow> | undefined, markers: {
18
17
  type: string;
19
18
  }[] | undefined, defaultStrategy: HoverStrategy);
19
+ /** The strategy a plot's axes ask for: along the unmeasured axis, or `'punctual'` when both hold categories. */
20
+ static strategyOf(axes: PlotAxes | undefined): HoverStrategy;
21
+ /** A plot's `hover` config; its tooltip reads as `format` unless it sets its own. */
22
+ static buildConfig<TRow extends Record<string, unknown>>(args: {
23
+ tooltip: TooltipConfig<TRow> | undefined;
24
+ format: (row: TRow) => string;
25
+ sort?: (a: TRow, b: TRow) => number;
26
+ markers: {
27
+ type: string;
28
+ }[];
29
+ points: HoverPoint<TRow>[];
30
+ label: (row: TRow) => string;
31
+ defaultStrategy: HoverStrategy;
32
+ pointMatching?: PointMatchingStrategy;
33
+ }): HoverConfig<TRow>;
20
34
  }
@@ -1,8 +1,7 @@
1
+ import { PlotAxes } from '../../phases/data/PlotAxes';
1
2
  /**
2
- * How the author set up a plot's hover, from its `tooltip` and its hover
3
- * marker (`'hover'`, or `'geo-hover'` on a map): whether it is on, the
4
- * strategy that matches points (the tooltip's, else the marker's, else the
5
- * plot kind's `defaultStrategy`) and whether it syncs across plots.
3
+ * A plot's hover as its `tooltip` and hover marker set it up: whether it is on,
4
+ * how it matches points and whether it syncs across plots.
6
5
  */
7
6
  export class HoverSettings {
8
7
  /** The tooltip, or `undefined` when off. */
@@ -24,4 +23,30 @@ export class HoverSettings {
24
23
  this.strategy = this.tooltip?.strategy ?? marker?.strategy ?? defaultStrategy;
25
24
  this.sync = this.tooltip?.sync ?? marker?.sync ?? false;
26
25
  }
26
+ /** The strategy a plot's axes ask for: along the unmeasured axis, or `'punctual'` when both hold categories. */
27
+ static strategyOf(axes) {
28
+ if (!axes)
29
+ return 'x';
30
+ if (axes.isDiscrete('x') && axes.isDiscrete('y'))
31
+ return 'punctual';
32
+ return PlotAxes.other(PlotAxes.valueOf({ axes }));
33
+ }
34
+ /** A plot's `hover` config; its tooltip reads as `format` unless it sets its own. */
35
+ static buildConfig(args) {
36
+ const { tooltip, format, sort, markers, points, label, defaultStrategy, pointMatching } = args;
37
+ const settings = new HoverSettings(tooltip, markers, defaultStrategy);
38
+ return {
39
+ active: settings.active,
40
+ points,
41
+ label,
42
+ strategy: settings.strategy,
43
+ sync: settings.sync,
44
+ tooltip: tooltip && {
45
+ ...tooltip,
46
+ format: tooltip.format ?? format,
47
+ ...(sort ? { sort } : {})
48
+ },
49
+ ...(pointMatching ? { pointMatching } : {})
50
+ };
51
+ }
27
52
  }
@@ -20,6 +20,11 @@ export declare class LegendState {
20
20
  static provide(): LegendState;
21
21
  /** The enclosing figure's legend, or a private one for a legend drawn outside any. Call during component init. */
22
22
  static ensure(): LegendState;
23
+ /** Whether `value` lies within `range`; a missing value always does. */
24
+ static inRange(value: number | null | undefined, range: {
25
+ min: number;
26
+ max: number;
27
+ }): boolean;
23
28
  /** Whether `series` is turned off. */
24
29
  isOff(series: string): boolean;
25
30
  /** What turning `series` off does, while it is off. */
@@ -33,6 +33,10 @@ export class LegendState {
33
33
  static ensure() {
34
34
  return LegendState.current() ?? new LegendState();
35
35
  }
36
+ /** Whether `value` lies within `range`; a missing value always does. */
37
+ static inRange(value, range) {
38
+ return value == null || (value >= range.min && value <= range.max);
39
+ }
36
40
  /** Whether `series` is turned off. */
37
41
  isOff(series) {
38
42
  return this.disabled.has(series);
@@ -14,7 +14,7 @@
14
14
  import { HoverState } from '../hover/HoverState.svelte';
15
15
  import { HoverSettings } from '../hover/HoverSettings';
16
16
  import { Interpolation } from '../../utils/Interpolation';
17
- import { FacetPhase } from '../../phases/facet/FacetPhase';
17
+ import { FacetPanels } from '../../phases/facet/FacetPanels';
18
18
  import { merge } from '../../utils/merge';
19
19
  import { Pipeline } from '../../phases/Pipeline';
20
20
  import { Slice } from './Slice';
@@ -191,7 +191,9 @@
191
191
  margins: place.margins,
192
192
  tooltip: tooltipConfig,
193
193
  scales: slicing.facetScales<TScales>(sharedScales, place),
194
- panel: panels ? FacetPhase.buildPanelState(panels, slicing.panelKey(place) ?? '') : undefined,
194
+ panel: panels
195
+ ? FacetPanels.buildPanelState(panels, slicing.panelKey(place) ?? '')
196
+ : undefined,
195
197
  keep: slicing.keep({ time, facet: place })
196
198
  })}
197
199
  </HoverScope>
@@ -246,9 +248,10 @@
246
248
  <TimelineLayout
247
249
  data={rows}
248
250
  accessor={timeline.accessor}
251
+ dateFormat={timeline.dateFormat}
249
252
  progress={timelineState?.progress ?? timeline.progress ?? 0}
250
253
  values={sharedTimeline ? sharedTimeline.steps : timeline.values}
251
- onsteps={ownTimeline ? (found) => ownTimeline.setSteps(found) : undefined}
254
+ onsteps={ownTimeline ? (found, temporal) => ownTimeline.setSteps(found, temporal) : undefined}
252
255
  orientation={timeline.orientation}
253
256
  show={false}
254
257
  filterMode={slicing.filterMode}
@@ -8,7 +8,7 @@ import type { Margins } from '../margins/Margins';
8
8
  import type { PlotAxes } from '../../phases/data/PlotAxes';
9
9
  import type { AxisValue } from '../../types/layout/mapping';
10
10
  import type { HoverPoint, HoverStrategy } from '../../types/layout/hover';
11
- import type { PlayerControl, TimeValue } from '../../types/layout/timeline';
11
+ import type { PlayerControl, TimeInput, TimelineStep } from '../../types/layout/timeline';
12
12
  import type { LegendDisabledAction, LegendEntry, LegendRange } from '../../types/layout/legend';
13
13
  /** What the pointer is over, shared by every plot in a figure. Read-only. */
14
14
  export declare class PublicHover {
@@ -69,11 +69,11 @@ export declare class PublicTimeline {
69
69
  /** Whether the figure has a timeline at all. */
70
70
  get enabled(): boolean;
71
71
  /** Every step it moves across, ascending. */
72
- get steps(): TimeValue[];
72
+ get steps(): TimelineStep[];
73
73
  /** The position across the steps, `0`-`1`. */
74
74
  get progress(): number;
75
75
  /** The step the position lands on, or `undefined` without steps. */
76
- get value(): TimeValue | undefined;
76
+ get value(): TimelineStep | undefined;
77
77
  get index(): number;
78
78
  get playing(): boolean;
79
79
  get loop(): boolean;
@@ -83,7 +83,8 @@ export declare class PublicTimeline {
83
83
  get controls(): PlayerControl[];
84
84
  seek(progress: number): void;
85
85
  seekIndex(index: number): void;
86
- seekValue(value: TimeValue): void;
86
+ /** Moves to the step closest to `value`. */
87
+ seekValue(value: TimeInput): void;
87
88
  next(): void;
88
89
  previous(): void;
89
90
  play(): void;