@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/docs/plots.md CHANGED
@@ -1,17 +1,18 @@
1
1
  # Plots
2
2
 
3
- Eight plot kinds share one set of props (see [Concepts](concepts.md)). They differ in what they draw, which channels they need and how they hover.
4
-
5
- | Plot | Draws | Needs |
6
- | --------------------------------- | ------------------------------------ | ----------------------------------------------- |
7
- | [`LinePlot`](#lineplot) | A line per series | `x`, `y` |
8
- | [`AreaPlot`](#areaplot) | A filled area per series | `x`, `y` |
9
- | [`BarPlot`](#barplot) | Bars, grouped, stacked or horizontal | a category channel and a value channel |
10
- | [`HistogramPlot`](#histogramplot) | Counts of a numeric field in bins | `x` or `y` |
11
- | [`HeatmapPlot`](#heatmapplot) | A grid of colored cells | `x`, `y`, `color` |
12
- | [`PyramidPlot`](#pyramidplot) | Two series mirrored around zero | a category channel, a value channel, two series |
13
- | [`ScatterPlot`](#scatterplot) | A point per row | `x`, `y` |
14
- | [`GeoPlot`](#geoplot) | A map from GeoJSON or TopoJSON | `data` (see [Maps](geo.md)) |
3
+ Nine plot kinds share one set of props (see [Concepts](concepts.md)). They differ in what they draw, which channels they need and how they hover.
4
+
5
+ | Plot | Draws | Needs |
6
+ | --------------------------------- | --------------------------------------- | ----------------------------------------------- |
7
+ | [`LinePlot`](#lineplot) | A line per series | `x`, `y` |
8
+ | [`AreaPlot`](#areaplot) | A filled area per series | `x`, `y` |
9
+ | [`BarPlot`](#barplot) | Bars, grouped, stacked or horizontal | a category channel and a value channel |
10
+ | [`HistogramPlot`](#histogramplot) | Counts of a numeric field in bins | `x` or `y` |
11
+ | [`BoxPlot`](#boxplot) | The distribution of values per category | a category channel and a value channel |
12
+ | [`HeatmapPlot`](#heatmapplot) | A grid of colored cells | `x`, `y`, `color` |
13
+ | [`PyramidPlot`](#pyramidplot) | Two series mirrored around zero | a category channel, a value channel, two series |
14
+ | [`ScatterPlot`](#scatterplot) | A point per row | `x`, `y` |
15
+ | [`GeoPlot`](#geoplot) | A map from GeoJSON or TopoJSON | `data` (see [Maps](geo.md)) |
15
16
 
16
17
  All of them accept `data` with `grouping`/`color`, or pre-built `series`.
17
18
 
@@ -19,19 +20,29 @@ All of them accept `data` with `grouping`/`color`, or pre-built `series`.
19
20
 
20
21
  One line per series. Rows with a missing value (`null`, `undefined`, `NaN`) break the line; `styles.gaps` can [bridge the break](styles-and-segments.md#gaps).
21
22
 
23
+ <!-- example: examples/LineDates.svelte -->
24
+
22
25
  ```svelte
26
+ <script lang="ts">
27
+ import { LinePlot } from '@fundar/data-chart-telling';
28
+ import { PRICES } from './data';
29
+ </script>
30
+
31
+ <!-- A line per commodity over a date axis, with end-of-line labels. -->
23
32
  <LinePlot
24
- {width}
25
- {height}
26
- data={rows}
33
+ width={720}
34
+ height={400}
35
+ data={PRICES}
27
36
  x="date"
28
37
  y="index"
29
- color="division"
38
+ color="commodity"
30
39
  styles={{ values: { show: true, format: (v, name) => `${name}: ${v}` } }}
31
40
  />
32
41
  ```
33
42
 
34
- ![A line plot with end-of-line labels](images/plots-line.png)
43
+ <!-- /example -->
44
+
45
+ ![A line plot with end-of-line labels](images/line-dates.png)
35
46
 
36
47
  - `styles.values.show` writes a label at the end of each series, with the series name and value. Overlapping labels spread apart automatically and the margin grows to fit them.
37
48
  - Hover finds the points sharing the hovered `x`.
@@ -42,19 +53,30 @@ One line per series. Rows with a missing value (`null`, `undefined`, `NaN`) brea
42
53
 
43
54
  One filled area per series, under the line from `y` to the baseline. Default fill opacity comes from `area.fillOpacity` in the configuration.
44
55
 
56
+ <!-- example: examples/StackedArea.svelte -->
57
+
45
58
  ```svelte
59
+ <script lang="ts">
60
+ import { AreaPlot } from '@fundar/data-chart-telling';
61
+ import { AGE_SHARES } from './data';
62
+ </script>
63
+
64
+ <!-- Each age group's share of the population, stacked with `stackY` so the bands add up to 100%. -->
46
65
  <AreaPlot
47
- {width}
48
- {height}
49
- data={rows}
66
+ width={720}
67
+ height={400}
68
+ data={AGE_SHARES}
50
69
  x="year"
51
- y="value"
52
- color="region"
70
+ y="share"
71
+ color="ageGroup"
72
+ scales={{ x: { ticks: [1991, 2001, 2010, 2022] } }}
53
73
  transforms={[{ type: 'stackY' }]}
54
74
  />
55
75
  ```
56
76
 
57
- ![A stacked area plot](images/plots-area.png)
77
+ <!-- /example -->
78
+
79
+ ![A stacked area plot](images/stacked-area.png)
58
80
 
59
81
  `transforms={[{ type: 'stackY' }]}` stacks the series.
60
82
 
@@ -62,16 +84,53 @@ One filled area per series, under the line from `y` to the baseline. Default fil
62
84
 
63
85
  Bars where one axis holds the categories and the other the values. Categories on `x` give vertical bars; categories on `y` give horizontal bars.
64
86
 
87
+ <!-- example: examples/GroupedBars.svelte -->
88
+
89
+ ```svelte
90
+ <script lang="ts">
91
+ import { BarPlot } from '@fundar/data-chart-telling';
92
+ import { PROVINCE_VALUES } from './data';
93
+ </script>
94
+
95
+ <!-- Vertical bars, grouped by `color`. -->
96
+ <BarPlot
97
+ width={720}
98
+ height={400}
99
+ data={PROVINCE_VALUES}
100
+ x="province"
101
+ y="value"
102
+ color="sex"
103
+ scales={{ y: { domain: [70, 85] } }}
104
+ />
105
+ ```
106
+
107
+ <!-- /example -->
108
+
109
+ ![Vertical grouped bars](images/grouped-bars.png)
110
+
111
+ <!-- example: examples/HorizontalBars.svelte -->
112
+
65
113
  ```svelte
66
- <!-- vertical -->
67
- <BarPlot {width} {height} data={rows} x="province" y="value" color="sex" />
68
- <!-- horizontal -->
69
- <BarPlot {width} {height} data={rows} x="value" y="province" color="sex" />
114
+ <script lang="ts">
115
+ import { BarPlot } from '@fundar/data-chart-telling';
116
+ import { PROVINCE_VALUES } from './data';
117
+ </script>
118
+
119
+ <!-- Horizontal bars: the categories go on `y`, the values on `x`. -->
120
+ <BarPlot
121
+ width={720}
122
+ height={400}
123
+ data={PROVINCE_VALUES}
124
+ x="value"
125
+ y="province"
126
+ color="sex"
127
+ scales={{ x: { domain: [70, 85] } }}
128
+ />
70
129
  ```
71
130
 
72
- ![Vertical grouped bars](images/plots-bar.png)
131
+ <!-- /example -->
73
132
 
74
- ![Horizontal grouped bars](images/plots-bar-horizontal.png)
133
+ ![Horizontal grouped bars](images/horizontal-bars.png)
75
134
 
76
135
  - Several series sharing a category sit side by side, each in its slice of the category band. The gap between them is `bar.seriesPadding` in the configuration.
77
136
  - `transforms={[{ type: 'stackY' }]}` stacks the series instead. Stacking is always `stackY`, also for horizontal bars, because the value channel is the one stacked.
@@ -83,33 +142,80 @@ Bars where one axis holds the categories and the other the values. Categories on
83
142
 
84
143
  Bins the rows of each series along one axis and draws the count per bin. Give `x` alone for vertical bars or `y` alone for horizontal bars.
85
144
 
145
+ <!-- example: examples/Histogram.svelte -->
146
+
86
147
  ```svelte
148
+ <script lang="ts">
149
+ import { HistogramPlot } from '@fundar/data-chart-telling';
150
+ import { HEIGHTS } from './data';
151
+ </script>
152
+
153
+ <!-- Two overlapping distributions, one per `grouping` value. -->
87
154
  <HistogramPlot
88
- {width}
89
- {height}
90
- data={rows}
91
- x="weight"
155
+ width={720}
156
+ height={400}
157
+ data={HEIGHTS}
158
+ x="height"
92
159
  grouping="group"
93
160
  segments={{ default: [{ styles: { fillOpacity: 0.7 } }] }}
94
161
  />
95
162
  ```
96
163
 
97
- ![Two overlapping histograms](images/plots-histogram.png)
164
+ <!-- /example -->
165
+
166
+ ![Two overlapping histograms](images/histogram.png)
98
167
 
99
168
  - `grouping` (or `color`) draws one histogram per group.
100
169
  - Override the default binning with a `binX` (or `binY`) transform: `transforms={[{ type: 'binX', thresholds: 20 }]}`. A transform given here is merged over the default one.
101
170
  - `transforms={[{ type: 'stackY' }]}` stacks groups in each bin.
102
171
  - `segments={{ default: [{ styles: { fillOpacity: 0.7 } }] }}` makes overlapping groups translucent.
103
172
 
173
+ ## BoxPlot
174
+
175
+ Summarizes the values of each series per category as a box: it spans the middle half of the values (25th to 75th percentile), the line across it is the median, the whiskers reach the farthest values within 1.5 times the box's height, and dots mark the values past them. String values on `x` stand the boxes upright; on `y` they lie sideways.
176
+
177
+ <!-- example: examples/Box.svelte -->
178
+
179
+ ```svelte
180
+ <script lang="ts">
181
+ import { BoxPlot } from '@fundar/data-chart-telling';
182
+ import { HEIGHTS } from './data';
183
+ </script>
184
+
185
+ <!-- One box per group: quartiles, median, whiskers and outliers of each height distribution. -->
186
+ <BoxPlot width={720} height={400} data={HEIGHTS} x="group" y="height" color="group" />
187
+ ```
188
+
189
+ <!-- /example -->
190
+
191
+ ![Box plots of two height distributions](images/box.png)
192
+
193
+ - `grouping` (or `color`) draws one box per group and category. Groups that share categories draw their boxes on top of each other, so split them with `facet` instead, or color by the category field itself.
194
+ - Categories keep the order the data gives them; `scales.x.domain` (or `y`) orders them.
195
+ - The value axis fits the values without reaching for zero; pin `scales.y.domain` to start it at zero.
196
+ - `scales.x.paddingInner` / `paddingOuter` set how wide the boxes are; the defaults come from `box` in the [configuration](theming.md).
197
+ - The tooltip reads one box at a time, not every value inside it: the hovered row carries `category`, `min`, `p25`, `median`, `p75`, `max`, `count` and `outliers`.
198
+ - `segments` restyle whole categories (`areas: [{ x: { from: 'Bus', to: 'Bus' } }]`). A segment that splits one category's values draws two boxes.
199
+
104
200
  ## HeatmapPlot
105
201
 
106
202
  A grid of cells, `x` and `y` being the categories of the columns and rows and `color` the value that colors each cell.
107
203
 
204
+ <!-- example: examples/Heatmap.svelte -->
205
+
108
206
  ```svelte
109
- <HeatmapPlot {width} {height} data={cells} x="quarter" y="product" color="sales" />
207
+ <script lang="ts">
208
+ import { HeatmapPlot } from '@fundar/data-chart-telling';
209
+ import { HOUSEHOLDS } from './data';
210
+ </script>
211
+
212
+ <!-- One cell per number of rooms and of people, colored by the share of households. -->
213
+ <HeatmapPlot width={560} height={360} data={HOUSEHOLDS} x="rooms" y="people" color="share" />
110
214
  ```
111
215
 
112
- ![A heatmap with value labels](images/plots-heatmap.png)
216
+ <!-- /example -->
217
+
218
+ ![A heatmap with value labels](images/heatmap.png)
113
219
 
114
220
  - The color domain spans the whole dataset, so it stays comparable across [facets](facets.md).
115
221
  - Cell value labels are on by default (`styles.values.show` turns them off).
@@ -120,11 +226,21 @@ A grid of cells, `x` and `y` being the categories of the columns and rows and `c
120
226
 
121
227
  Two series mirrored around a shared zero baseline, such as a population pyramid. Categories on `y` give horizontal bars; categories on `x` give vertical ones.
122
228
 
229
+ <!-- example: examples/Pyramid.svelte -->
230
+
123
231
  ```svelte
124
- <PyramidPlot {width} {height} data={ages} x="share" y="ageGroup" color="sex" />
232
+ <script lang="ts">
233
+ import { PyramidPlot } from '@fundar/data-chart-telling';
234
+ import { AGES } from './data';
235
+ </script>
236
+
237
+ <!-- A population pyramid: one side per `color` value. -->
238
+ <PyramidPlot width={720} height={400} data={AGES} x="share" y="ageGroup" color="sex" />
125
239
  ```
126
240
 
127
- ![A population pyramid](images/plots-pyramid.png)
241
+ <!-- /example -->
242
+
243
+ ![A population pyramid](images/pyramid.png)
128
244
 
129
245
  - `scales.grouping.reverse` swaps which series sits on which side.
130
246
  - Hover finds the points sharing the hovered category.
@@ -133,11 +249,19 @@ Two series mirrored around a shared zero baseline, such as a population pyramid.
133
249
 
134
250
  A dot per row.
135
251
 
252
+ <!-- example: examples/Scatter.svelte -->
253
+
136
254
  ```svelte
255
+ <script lang="ts">
256
+ import { ScatterPlot } from '@fundar/data-chart-telling';
257
+ import { COUNTRIES } from './data';
258
+ </script>
259
+
260
+ <!-- Points sized by `r`, colored by `color` and shaped by `symbol`. -->
137
261
  <ScatterPlot
138
- {width}
139
- {height}
140
- data={countries}
262
+ width={720}
263
+ height={420}
264
+ data={COUNTRIES}
141
265
  x="gdp"
142
266
  y="life"
143
267
  r="population"
@@ -146,7 +270,9 @@ A dot per row.
146
270
  />
147
271
  ```
148
272
 
149
- ![A scatter plot sized, colored and shaped by channels](images/plots-scatter.png)
273
+ <!-- /example -->
274
+
275
+ ![A scatter plot sized, colored and shaped by channels](images/scatter.png)
150
276
 
151
277
  - `r` sizes the dots (area follows the value; configure with `scales.r`). The legend gets a size section.
152
278
  - `symbol` varies the dot shape by category (`scales.symbol`), `opacity` varies transparency, `color` colors by category or by a continuous ramp.
package/docs/scales.md CHANGED
@@ -2,43 +2,60 @@
2
2
 
3
3
  `scales` configures how data values become positions and visual properties. It has one entry per channel.
4
4
 
5
+ <!-- example: examples/Scales.svelte -->
6
+
5
7
  ```svelte
8
+ <script lang="ts">
9
+ import { LinePlot } from '@fundar/data-chart-telling';
10
+ import { PRICES } from './data';
11
+ </script>
12
+
13
+ <!-- Axis labels, a fixed domain with gridlines and a color per series. -->
6
14
  <LinePlot
7
- {width}
8
- {height}
9
- data={rows}
15
+ width={720}
16
+ height={400}
17
+ data={PRICES}
10
18
  x="date"
11
19
  y="index"
12
- color="division"
20
+ color="commodity"
13
21
  scales={{
14
22
  x: { label: 'Month' },
15
- y: { label: 'Index (Jan 2021 = 100)', domain: [90, 280], grid: true },
23
+ y: { label: 'Index (Jan 2021 = 100)', domain: [0, 600], grid: true },
16
24
  color: {
17
- scheme: { Food: '#1b9e77', Transport: '#d95f02', Housing: '#7570b3', Health: '#e7298a' }
25
+ scheme: {
26
+ 'Brent oil': '#1b9e77',
27
+ 'Natural gas': '#d95f02',
28
+ Coal: '#7570b3',
29
+ Wheat: '#e7298a'
30
+ }
18
31
  }
19
32
  }}
20
33
  />
21
34
  ```
22
35
 
36
+ <!-- /example -->
37
+
23
38
  ![A line plot with a labelled axis, gridlines and custom colors](images/scales.png)
24
39
 
25
40
  ## Axes: `x` and `y`
26
41
 
27
42
  Each axis takes an `AxisScale`.
28
43
 
29
- | Field | Meaning |
30
- | ---------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
31
- | `type` | `'auto'` (default), `'linear'`, `'pow'`, `'sqrt'`, `'log'`, `'symlog'`, `'time'`, `'utc'`, `'band'`, `'point'`, `'ordinal'`, `'categorical'` and the color-oriented types (`'sequential'`, `'diverging'`, `'threshold'`, ...) |
32
- | `domain`, `range` | Explicit input and output extent |
33
- | `reverse`, `nice`, `zero`, `clamp`, `round`, `percent` | Direction, rounding and baseline behaviour |
34
- | `label`, `labelAnchor`, `labelOrientation`, `labelPosition`, `titleFontSize` | The axis title (`label: false` hides it) |
35
- | `ticks`, `tickSpacing`, `tickSize`, `tickPadding`, `tickRotate` | Tick placement and size |
36
- | `tickFormat` | `false`, an `Intl.NumberFormatOptions` / `Intl.DateTimeFormatOptions` object, or `(d, index, ticks) => string` |
37
- | `grid` | `true` or a stroke style for gridlines |
38
- | `axisDisplay` | `'visible'` (default), `'hidden'` (keeps the space, so aligned panels stay aligned) or `'none'` |
39
- | `padding`, `paddingInner`, `paddingOuter`, `align` | Band and point spacing |
40
- | `insetLeft`, `insetRight`, `insetTop`, `insetBottom` | Pixels kept free at each end |
41
- | `interval`, `base`, `constant`, `sort` | Time/discrete intervals, log base, symlog constant, category order |
44
+ | Field | Meaning |
45
+ | ---------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
46
+ | `type` | `'auto'` (default), `'linear'`, `'pow'`, `'sqrt'`, `'log'`, `'symlog'`, `'time'`, `'utc'`, `'band'`, `'point'`, `'ordinal'`, `'categorical'` and the color-oriented types (`'sequential'`, `'diverging'`, `'threshold'`, ...) |
47
+ | `domain`, `range` | Explicit input and output extent |
48
+ | `reverse`, `nice`, `zero`, `clamp`, `round`, `percent` | Direction, rounding and baseline behaviour |
49
+ | `niceTolerance` | How far past a tick, as a share of the step, a domain the plot nices itself still ends on it (default `0.05`), so parts adding up to `100.2` stay at `100`. Applies to stacked and ranged axes, axes grown to cover markers or a transform's output, a bar plot's value axis and shared facet/timeline domains; `0` rounds like d3 |
50
+ | `edgeTicks` | Ticks at the data's first and last values beside the nice ones. `false` turns them off, `true` on, `{ collision: 'drop-nice' \| 'drop-edge' \| 'extend' }` picks what happens when one crowds a nice tick. Defaults to `axis.edgeTicks.x` / `.y` in the [configuration](theming.md#edge-ticks) (on for x, off for y); ignored when the axis sets `ticks`, `tickSpacing`, `interval` or `domain` |
51
+ | `label`, `labelAnchor`, `labelOrientation`, `labelPosition`, `titleFontSize` | The axis title (`label: false` hides it) |
52
+ | `ticks`, `tickSpacing`, `tickSize`, `tickPadding`, `tickRotate` | Tick placement and size |
53
+ | `tickFormat` | `false`, an `Intl.NumberFormatOptions` / `Intl.DateTimeFormatOptions` object, or `(d, index, ticks) => string` |
54
+ | `grid` | `true` or a stroke style for gridlines |
55
+ | `axisDisplay` | `'visible'` (default), `'hidden'` (keeps the space, so aligned panels stay aligned) or `'none'` |
56
+ | `padding`, `paddingInner`, `paddingOuter`, `align` | Band and point spacing |
57
+ | `insetLeft`, `insetRight`, `insetTop`, `insetBottom` | Pixels kept free at each end |
58
+ | `interval`, `base`, `constant`, `sort` | Time/discrete intervals, log base, symlog constant, category order |
42
59
 
43
60
  Number and date text defaults follow `axis.locale` and `axis.compactDisplay` in the [configuration](theming.md#locale).
44
61
 
package/docs/state.md CHANGED
@@ -8,7 +8,7 @@ Works anywhere inside a figure, including the `header`, `footer` and `content` s
8
8
 
9
9
  - `hover` - `activeX`, `activeY`, `activeSeries`, `strategy`, `sync`, `source` (the id of the plot owning the hover).
10
10
  - `legend` (or `null`) - `entries`, `range`, `off` (names of hidden series), `isOff(name)`, `turnOff(name, action)`, `turnOn(name)`, `toggle(name, action)`. `action` is `{ mode: 'dim' | 'hide' | 'omit' }`; `'omit'` removes the series and refits the axes.
11
- - `timeline` (or `null`) - `enabled`, `steps`, `value`, `index`, `progress`, `playing`, `loop`, `speed`, `speeds`, `controls`, and `seek`, `seekIndex`, `seekValue`, `next`, `previous`, `play`, `pause`, `toggle`, `setLoop`, `setSpeed`, `cycleSpeed`.
11
+ - `timeline` (or `null`) - `enabled`, `steps`, `value`, `index`, `progress`, `playing`, `loop`, `speed`, `speeds`, `controls`, and `seek`, `seekIndex`, `seekValue`, `next`, `previous`, `play`, `pause`, `toggle`, `setLoop`, `setSpeed`, `cycleSpeed`. On a timeline of dates, `steps` and `value` are `Date`s and `seekValue` takes a `Date` or a date string.
12
12
 
13
13
  A footer that toggles series through the legend:
14
14
 
@@ -16,14 +16,22 @@
16
16
 
17
17
  ### Value labels
18
18
 
19
+ <!-- example: examples/ValueLabels.svelte -->
20
+
19
21
  ```svelte
22
+ <script lang="ts">
23
+ import { LinePlot } from '@fundar/data-chart-telling';
24
+ import { PRICES } from './data';
25
+ </script>
26
+
27
+ <!-- End-of-line value labels with a custom format and font size. -->
20
28
  <LinePlot
21
- {width}
22
- {height}
23
- data={rows}
29
+ width={720}
30
+ height={400}
31
+ data={PRICES}
24
32
  x="date"
25
33
  y="index"
26
- color="division"
34
+ color="commodity"
27
35
  styles={{
28
36
  values: {
29
37
  show: true,
@@ -34,7 +42,9 @@
34
42
  />
35
43
  ```
36
44
 
37
- ![End-of-line value labels](images/styles-values.png)
45
+ <!-- /example -->
46
+
47
+ ![End-of-line value labels](images/value-labels.png)
38
48
 
39
49
  | Field | Meaning |
40
50
  | -------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
@@ -55,24 +65,35 @@ Where the labels go depends on the plot: the end of each series on a line or are
55
65
 
56
66
  A row whose value is `null`, `undefined` or `NaN` breaks a line. `styles.gaps` bridges the break with a distinct, interpolated stretch. It is keyed by series name, or `'default'` for the series with no entry of their own, and has three zones: `start` (before the first real point), `middle` and `end` (after the last).
57
67
 
68
+ <!-- example: examples/Gaps.svelte -->
69
+
58
70
  ```svelte
71
+ <script lang="ts">
72
+ import { LinePlot } from '@fundar/data-chart-telling';
73
+ import { LIFE_GAPS } from './data';
74
+ </script>
75
+
76
+ <!-- Missing values bridged: dashed in the middle, faded at the start, extended to the 2025 projection at the end. -->
59
77
  <LinePlot
60
- {width}
61
- {height}
62
- series={[{ name: 'female', x: 'year', y: 'value', data: rowsWithHoles }]}
78
+ width={720}
79
+ height={400}
80
+ series={[{ name: 'female', x: 'year', y: 'value', data: LIFE_GAPS }]}
81
+ scales={{ x: { edgeTicks: true }, y: { domain: [55, 85] } }}
63
82
  styles={{
64
83
  gaps: {
65
84
  default: {
66
85
  middle: { interpolate: 'linear', style: { stroke: { strokeDasharray: '4 4' } } },
67
- start: { x: 2007, style: { stroke: { strokeOpacity: 0.4 } } },
68
- end: { x: 2023, y: 85 }
86
+ start: { x: 1940, style: { stroke: { strokeOpacity: 0.4 } } },
87
+ end: { x: 2025, y: 81.1 }
69
88
  }
70
89
  }
71
90
  }}
72
91
  />
73
92
  ```
74
93
 
75
- ![A line bridged across missing values with dashed segments](images/styles-gaps.png)
94
+ <!-- /example -->
95
+
96
+ ![A line bridged across missing values with dashed segments](images/gaps.png)
76
97
 
77
98
  - `middle` bridges holes between real points, `interpolate` is an easing name (default `'linear'`).
78
99
  - `start` and `end` reach out to the given `x` (required to fill the zone), landing on `y` there (default: the nearest real value).
@@ -111,16 +132,26 @@ The style shape depends on the plot:
111
132
 
112
133
  A baseline color with a highlighted range:
113
134
 
135
+ <!-- example: examples/SegmentHighlight.svelte -->
136
+
114
137
  ```svelte
138
+ <script lang="ts">
139
+ import { LinePlot } from '@fundar/data-chart-telling';
140
+ import { REGION_VALUES } from './data';
141
+
142
+ const gba = REGION_VALUES.filter((row) => row.region === 'GBA');
143
+ </script>
144
+
145
+ <!-- A grey line with the 2023-2024 surge drawn in red. -->
115
146
  <LinePlot
116
- {width}
117
- {height}
118
- series={[{ name: 'gdp', x: 'year', y: 'value', data: gdp }]}
147
+ width={720}
148
+ height={380}
149
+ series={[{ name: 'GBA', x: 'date', y: 'value', data: gba }]}
119
150
  segments={{
120
- gdp: [
151
+ GBA: [
121
152
  { styles: { stroke: { stroke: '#94a3b8', strokeWidth: 1 } } },
122
153
  {
123
- areas: [{ x: { from: 2008, to: 2010 } }],
154
+ areas: [{ x: { from: new Date('2023-07-01'), to: new Date('2024-03-01') } }],
124
155
  styles: { stroke: { stroke: '#ef4444', strokeWidth: 3 } }
125
156
  }
126
157
  ]
@@ -128,24 +159,35 @@ A baseline color with a highlighted range:
128
159
  />
129
160
  ```
130
161
 
131
- ![A grey line with a highlighted red range](images/segments-highlight.png)
162
+ <!-- /example -->
163
+
164
+ ![A grey line with a highlighted red range](images/segment-highlight.png)
132
165
 
133
166
  Per-series colors, plus dots over a range shared by every series:
134
167
 
168
+ <!-- example: examples/SegmentsBySeries.svelte -->
169
+
135
170
  ```svelte
171
+ <script lang="ts">
172
+ import { LinePlot } from '@fundar/data-chart-telling';
173
+ import { LIFE } from './data';
174
+ </script>
175
+
176
+ <!-- Segments per series name, plus `default` for rules shared by every series. -->
136
177
  <LinePlot
137
- {width}
138
- {height}
139
- data={lifeExpectancy}
178
+ width={720}
179
+ height={380}
180
+ data={LIFE}
140
181
  x="year"
141
182
  y="value"
142
183
  color="sex"
184
+ scales={{ x: { edgeTicks: true } }}
143
185
  segments={{
144
186
  female: [{ styles: { stroke: { stroke: 'steelblue' } } }],
145
187
  male: [{ styles: { stroke: { stroke: 'tomato' } } }],
146
188
  default: [
147
189
  {
148
- areas: [{ x: { from: 2010, to: 2012 } }],
190
+ areas: [{ x: { from: 1965, to: 1980 } }],
149
191
  styles: { dots: { dotRadius: 5, dotSymbol: 'circle' } }
150
192
  }
151
193
  ]
@@ -153,7 +195,9 @@ Per-series colors, plus dots over a range shared by every series:
153
195
  />
154
196
  ```
155
197
 
156
- ![Two series colored individually, with dots on a shared range](images/segments-series.png)
198
+ <!-- /example -->
199
+
200
+ ![Two series colored individually, with dots on a shared range](images/segments-by-series.png)
157
201
 
158
202
  Two areas in one segment, the second with a `y` limit:
159
203
 
@@ -169,22 +213,30 @@ Two areas in one segment, the second with a `y` limit:
169
213
 
170
214
  Heatmap columns and cells:
171
215
 
216
+ <!-- example: examples/HeatmapSegments.svelte -->
217
+
172
218
  ```svelte
219
+ <script lang="ts">
220
+ import { HeatmapPlot } from '@fundar/data-chart-telling';
221
+ import { HOUSEHOLDS } from './data';
222
+ </script>
223
+
224
+ <!-- Segments on a heatmap: an outlined column and a filled block of overcrowded households. -->
173
225
  <HeatmapPlot
174
- {width}
175
- {height}
176
- {data}
177
- x="quarter"
178
- y="product"
179
- color="sales"
226
+ width={560}
227
+ height={360}
228
+ data={HOUSEHOLDS}
229
+ x="rooms"
230
+ y="people"
231
+ color="share"
180
232
  segments={{
181
233
  default: [
182
234
  {
183
- areas: [{ x: { from: 'Q1', to: 'Q1' } }, { x: { from: 'Q3', to: 'Q3' } }],
235
+ areas: [{ x: { from: '1', to: '1' } }],
184
236
  styles: { stroke: '#f59e0b', strokeWidth: 2 }
185
237
  },
186
238
  {
187
- areas: [{ x: { from: 'Q2', to: 'Q2' }, y: { from: 'Alpha', to: 'Beta' } }],
239
+ areas: [{ x: { from: '1', to: '2' }, y: { from: '4', to: '5+' } }],
188
240
  styles: { fill: '#ef4444', fillOpacity: 0.8 }
189
241
  }
190
242
  ]
@@ -192,7 +244,9 @@ Heatmap columns and cells:
192
244
  />
193
245
  ```
194
246
 
195
- ![A heatmap with highlighted columns and cells](images/segments-heatmap.png)
247
+ <!-- /example -->
248
+
249
+ ![A heatmap with highlighted columns and cells](images/heatmap-segments.png)
196
250
 
197
251
  Map features, keyed by feature id:
198
252