@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/examples/data.ts CHANGED
@@ -1,12 +1,324 @@
1
- export type Row = { year: number; region: string; value: number };
1
+ // Small real datasets shared by the examples and the documentation figures.
2
+ // Each section names its source; values are rounded to one decimal.
2
3
 
3
- const REGIONS = ['North', 'Center', 'South'];
4
+ // ── Monthly inflation by region ─────────────────────────────────────────────
5
+ // INDEC, consumer price index (IPC, base December 2016), via the datos.gob.ar
6
+ // series API. Change from the previous month, in percent, January 2023 to
7
+ // August 2026.
4
8
 
5
- /** Tiny deterministic dataset shared by the examples: one value per region and year. */
6
- export const ROWS: Row[] = REGIONS.flatMap((region, r) =>
7
- Array.from({ length: 8 }, (_, i) => ({
8
- year: 2016 + i,
9
- region,
10
- value: 100 + (r + 1) * 4 * i + (i % 3) * 2
9
+ export type RegionRow = { date: Date; region: string; value: number };
10
+
11
+ export const MONTHS = Array.from(
12
+ { length: 44 },
13
+ (_, monthIndex) => new Date(Date.UTC(2023, monthIndex, 1))
14
+ );
15
+
16
+ const INFLATION_BY_REGION = [
17
+ {
18
+ region: 'GBA',
19
+ inflation: [
20
+ 6, 6.7, 7.8, 8.6, 8, 5.8, 6.2, 12.3, 12.2, 8.6, 12.9, 25.1, 19.6, 15, 11.5, 9.2, 4.3, 4.4, 4,
21
+ 4.1, 3.7, 2.8, 2.6, 2.9, 2, 2.2, 3.9, 2.8, 1.5, 2, 1.9, 1.9, 2.1, 2.4, 2.5, 2.8, 2.8, 2.6,
22
+ 3.4, 2.8, 2.3, 1.9, 2.3, 1.6
23
+ ]
24
+ },
25
+ {
26
+ region: 'Cuyo',
27
+ inflation: [
28
+ 6.4, 6.7, 7.9, 7.2, 7.5, 5.3, 6.3, 12.3, 13.7, 7.4, 12.8, 25.8, 22.3, 13.5, 9.3, 8.1, 4.3,
29
+ 5.1, 4.6, 4.5, 3, 2.2, 2.1, 2.3, 2, 2.7, 3.5, 2.9, 1.6, 1, 1.9, 2.1, 2.2, 2.3, 2.8, 3, 3, 3.4,
30
+ 3.2, 2.1, 2.1, 1.6, 1.9, 1.7
31
+ ]
32
+ },
33
+ {
34
+ region: 'Northwest',
35
+ inflation: [
36
+ 6.3, 7.3, 7.3, 7.7, 8, 6.6, 6.1, 13.7, 13.2, 7.7, 11.8, 25.2, 21.7, 11.7, 8.8, 9.1, 4.2, 4.8,
37
+ 3.6, 5, 3.4, 2.6, 1.9, 2.7, 2.6, 2.6, 4.3, 2.9, 1.2, 1.2, 1.7, 2, 2.2, 2.1, 2.3, 2.6, 2.8,
38
+ 3.5, 4, 2.5, 2.1, 1.7, 2, 1.8
39
+ ]
40
+ }
41
+ ];
42
+
43
+ export const REGIONS = INFLATION_BY_REGION.map(({ region }) => region);
44
+
45
+ export const REGION_VALUES: RegionRow[] = INFLATION_BY_REGION.flatMap(({ region, inflation }) =>
46
+ MONTHS.map((date, monthIndex) => ({ date, region, value: inflation[monthIndex] }))
47
+ );
48
+
49
+ // ── International commodity prices ──────────────────────────────────────────
50
+ // IMF, Primary Commodity Prices (PCPS), via DBnomics. Monthly, 2021-2024, as
51
+ // an index with January 2021 = 100 so every commodity shares one axis.
52
+
53
+ export type PriceRow = { date: Date; commodity: string; index: number };
54
+
55
+ const PRICE_INDEX_BY_COMMODITY = [
56
+ {
57
+ commodity: 'Brent oil',
58
+ index: [
59
+ 100, 113, 119, 119, 124, 133, 135, 128, 135, 152, 146, 135, 155, 171, 204, 192, 203, 213, 191,
60
+ 177, 164, 170, 165, 148, 152, 151, 144, 151, 137, 136, 145, 154, 168, 161, 149, 140, 143, 148,
61
+ 154, 161, 150, 150, 152, 143, 132, 136, 133, 133
62
+ ]
63
+ },
64
+ {
65
+ commodity: 'Natural gas',
66
+ index: [
67
+ 100, 110, 99, 101, 112, 124, 144, 152, 192, 210, 193, 146, 160, 169, 188, 255, 308, 286, 268,
68
+ 331, 296, 230, 245, 217, 131, 92, 91, 83, 87, 94, 100, 100, 102, 120, 115, 96, 103, 68, 66,
69
+ 67, 91, 106, 84, 79, 91, 97, 113, 130
70
+ ]
71
+ },
72
+ {
73
+ commodity: 'Coal',
74
+ index: [
75
+ 100, 102, 105, 108, 115, 145, 168, 191, 207, 265, 195, 199, 255, 299, 388, 368, 460, 470, 477,
76
+ 492, 514, 469, 405, 470, 445, 268, 215, 221, 197, 152, 155, 168, 185, 166, 145, 164, 155, 141,
77
+ 151, 151, 163, 158, 156, 167, 163, 168, 165, 154
78
+ ]
79
+ },
80
+ {
81
+ commodity: 'Wheat',
82
+ index: [
83
+ 100, 101, 97, 101, 117, 100, 102, 116, 113, 124, 133, 136, 137, 146, 163, 171, 187, 167, 135,
84
+ 136, 146, 149, 145, 136, 135, 140, 130, 131, 126, 119, 117, 101, 96, 91, 91, 97, 95, 92, 89,
85
+ 88, 96, 86, 77, 74, 79, 83, 78, 78
86
+ ]
87
+ }
88
+ ];
89
+
90
+ export const PRICES: PriceRow[] = PRICE_INDEX_BY_COMMODITY.flatMap(({ commodity, index }) =>
91
+ index.map((value, monthIndex) => ({
92
+ date: new Date(Date.UTC(2021, monthIndex, 1)),
93
+ commodity,
94
+ index: value
11
95
  }))
12
96
  );
97
+
98
+ // ── Life expectancy at birth in Argentina, by sex ───────────────────────────
99
+ // INDEC, life tables. Each year stands for the period around it (1947 is
100
+ // 1946-1948, 2001 is 2000-2001, ...).
101
+
102
+ export type LifeRow = { year: number; sex: string; value: number | null };
103
+
104
+ const LIFE_EXPECTANCY = [
105
+ { year: 1947, female: 61.4, male: 56.9 },
106
+ { year: 1965, female: 69.5, male: 63.3 },
107
+ { year: 1972, female: 70.8, male: 64.1 },
108
+ { year: 1980, female: 72.7, male: 65.5 },
109
+ { year: 1991, female: 75.6, male: 68.4 },
110
+ { year: 2001, female: 77.5, male: 70 },
111
+ { year: 2009, female: 78.8, male: 72.1 },
112
+ { year: 2019, female: 80.2, male: 74.4 }
113
+ ];
114
+
115
+ export const LIFE: LifeRow[] = LIFE_EXPECTANCY.flatMap(({ year, female, male }) => [
116
+ { year, sex: 'female', value: female },
117
+ { year, sex: 'male', value: male }
118
+ ]);
119
+
120
+ /** The female series with holes: the first year, one in the middle and the last. */
121
+ export const LIFE_GAPS: LifeRow[] = LIFE.filter((row) => row.sex === 'female').map((row) =>
122
+ [1947, 1980, 2019].includes(row.year) ? { ...row, value: null } : row
123
+ );
124
+
125
+ // ── Life expectancy at birth by province, 2019 ──────────────────────────────
126
+ // Same source as above.
127
+
128
+ export type ProvinceRow = { province: string; sex: string; value: number };
129
+
130
+ const LIFE_EXPECTANCY_BY_PROVINCE = [
131
+ { province: 'Buenos Aires', female: 79.9, male: 74.1 },
132
+ { province: 'Córdoba', female: 80.8, male: 75.2 },
133
+ { province: 'Mendoza', female: 81, male: 75.3 },
134
+ { province: 'Salta', female: 79.5, male: 74 },
135
+ { province: 'Chubut', female: 82.1, male: 74.9 }
136
+ ];
137
+
138
+ export const PROVINCES = LIFE_EXPECTANCY_BY_PROVINCE.map(({ province }) => province);
139
+
140
+ export const PROVINCE_VALUES: ProvinceRow[] = LIFE_EXPECTANCY_BY_PROVINCE.flatMap(
141
+ ({ province, female, male }) => [
142
+ { province, sex: 'female', value: female },
143
+ { province, sex: 'male', value: male }
144
+ ]
145
+ );
146
+
147
+ // ── Population of Buenos Aires City by age and sex ──────────────────────────
148
+ // National censuses 1991-2022. Share of the city's population, in percent.
149
+
150
+ /** The 2022 census: one row per age group and sex, for a pyramid. */
151
+ export type AgeRow = { ageGroup: string; sex: string; share: number };
152
+
153
+ const POPULATION_BY_AGE = [
154
+ { ageGroup: '0-4', female: 2.3, male: 2.4 },
155
+ { ageGroup: '5-9', female: 3.1, male: 3.2 },
156
+ { ageGroup: '10-14', female: 3.1, male: 3.2 },
157
+ { ageGroup: '15-19', female: 3, male: 3.1 },
158
+ { ageGroup: '20-24', female: 3.5, male: 3.3 },
159
+ { ageGroup: '25-29', female: 4, male: 3.7 },
160
+ { ageGroup: '30-34', female: 3.8, male: 3.6 },
161
+ { ageGroup: '35-39', female: 3.7, male: 3.5 },
162
+ { ageGroup: '40-44', female: 3.4, male: 3.1 },
163
+ { ageGroup: '45-49', female: 3.3, male: 2.8 },
164
+ { ageGroup: '50-54', female: 3.1, male: 2.5 },
165
+ { ageGroup: '55-59', female: 2.9, male: 2.4 },
166
+ { ageGroup: '60-64', female: 2.8, male: 2.2 },
167
+ { ageGroup: '65-69', female: 2.6, male: 2 },
168
+ { ageGroup: '70-74', female: 2.6, male: 1.8 },
169
+ { ageGroup: '75-79', female: 2.3, male: 1.4 },
170
+ { ageGroup: '80-84', female: 1.8, male: 0.9 },
171
+ { ageGroup: '85+', female: 2.5, male: 0.9 }
172
+ ];
173
+
174
+ export const AGE_GROUPS = POPULATION_BY_AGE.map(({ ageGroup }) => ageGroup);
175
+
176
+ export const AGES: AgeRow[] = POPULATION_BY_AGE.flatMap(({ ageGroup, female, male }) => [
177
+ { ageGroup, sex: 'Female', share: female },
178
+ { ageGroup, sex: 'Male', share: male }
179
+ ]);
180
+
181
+ /** The same census data over the years, grouped by sex: the share of each broad age group. */
182
+ export type AgeShareRow = { year: number; ageGroup: string; share: number };
183
+
184
+ export const AGE_SHARES: AgeShareRow[] = [
185
+ { year: 1991, ageGroup: '0-14', share: 19 },
186
+ { year: 1991, ageGroup: '15-29', share: 21.7 },
187
+ { year: 1991, ageGroup: '30-44', share: 20.1 },
188
+ { year: 1991, ageGroup: '45-64', share: 22.8 },
189
+ { year: 1991, ageGroup: '65+', share: 16.5 },
190
+ { year: 2001, ageGroup: '0-14', share: 16.9 },
191
+ { year: 2001, ageGroup: '15-29', share: 23.2 },
192
+ { year: 2001, ageGroup: '30-44', share: 19.9 },
193
+ { year: 2001, ageGroup: '45-64', share: 22.3 },
194
+ { year: 2001, ageGroup: '65+', share: 17.2 },
195
+ { year: 2010, ageGroup: '0-14', share: 16.4 },
196
+ { year: 2010, ageGroup: '15-29', share: 22.4 },
197
+ { year: 2010, ageGroup: '30-44', share: 22.2 },
198
+ { year: 2010, ageGroup: '45-64', share: 22.9 },
199
+ { year: 2010, ageGroup: '65+', share: 16.3 },
200
+ { year: 2022, ageGroup: '0-14', share: 17.4 },
201
+ { year: 2022, ageGroup: '15-29', share: 20.7 },
202
+ { year: 2022, ageGroup: '30-44', share: 21.1 },
203
+ { year: 2022, ageGroup: '45-64', share: 22 },
204
+ { year: 2022, ageGroup: '65+', share: 18.9 }
205
+ ];
206
+
207
+ /** The same census data over the years, grouped by age: the share of each sex. */
208
+ export type SexShareRow = { year: number; sex: string; share: number };
209
+
210
+ export const SEX_SHARES: SexShareRow[] = [
211
+ { year: 1991, sex: 'Female', share: 54.6 },
212
+ { year: 1991, sex: 'Male', share: 45.5 },
213
+ { year: 2001, sex: 'Female', share: 54.2 },
214
+ { year: 2001, sex: 'Male', share: 45.3 },
215
+ { year: 2010, sex: 'Female', share: 54.2 },
216
+ { year: 2010, sex: 'Male', share: 46 },
217
+ { year: 2022, sex: 'Female', share: 53.8 },
218
+ { year: 2022, sex: 'Male', share: 46.2 }
219
+ ];
220
+
221
+ // ── Countries, 2007 ─────────────────────────────────────────────────────────
222
+ // Gapminder, via plotly/datasets. GDP per capita in US dollars (PPP, 2005
223
+ // prices), life expectancy in years, population in millions. `income` groups
224
+ // the countries by GDP per capita.
225
+
226
+ export type CountryRow = {
227
+ country: string;
228
+ gdp: number;
229
+ life: number;
230
+ population: number;
231
+ continent: string;
232
+ income: string;
233
+ };
234
+
235
+ const COUNTRY_INDICATORS = [
236
+ { country: 'Nigeria', continent: 'Africa', gdp: 2014, life: 46.9, population: 135 },
237
+ { country: 'Egypt', continent: 'Africa', gdp: 5581, life: 71.3, population: 80.3 },
238
+ { country: 'South Africa', continent: 'Africa', gdp: 9270, life: 49.3, population: 44 },
239
+ { country: 'Ethiopia', continent: 'Africa', gdp: 691, life: 52.9, population: 76.5 },
240
+ { country: 'Kenya', continent: 'Africa', gdp: 1463, life: 54.1, population: 35.6 },
241
+ { country: 'Morocco', continent: 'Africa', gdp: 3820, life: 71.2, population: 33.8 },
242
+ { country: 'Ghana', continent: 'Africa', gdp: 1328, life: 60, population: 22.9 },
243
+ { country: 'Argentina', continent: 'Americas', gdp: 12779, life: 75.3, population: 40.3 },
244
+ { country: 'Brazil', continent: 'Americas', gdp: 9066, life: 72.4, population: 190 },
245
+ { country: 'Chile', continent: 'Americas', gdp: 13172, life: 78.6, population: 16.3 },
246
+ { country: 'Mexico', continent: 'Americas', gdp: 11978, life: 76.2, population: 108.7 },
247
+ { country: 'United States', continent: 'Americas', gdp: 42952, life: 78.2, population: 301.1 },
248
+ { country: 'Canada', continent: 'Americas', gdp: 36319, life: 80.7, population: 33.4 },
249
+ { country: 'Bolivia', continent: 'Americas', gdp: 3822, life: 65.6, population: 9.1 },
250
+ { country: 'China', continent: 'Asia', gdp: 4959, life: 73, population: 1318.7 },
251
+ { country: 'India', continent: 'Asia', gdp: 2452, life: 64.7, population: 1110.4 },
252
+ { country: 'Japan', continent: 'Asia', gdp: 31656, life: 82.6, population: 127.5 },
253
+ { country: 'Indonesia', continent: 'Asia', gdp: 3541, life: 70.7, population: 223.5 },
254
+ { country: 'Vietnam', continent: 'Asia', gdp: 2442, life: 74.2, population: 85.3 },
255
+ { country: 'Pakistan', continent: 'Asia', gdp: 2606, life: 65.5, population: 169.3 },
256
+ { country: 'South Korea', continent: 'Asia', gdp: 23348, life: 78.6, population: 49 },
257
+ { country: 'Germany', continent: 'Europe', gdp: 32170, life: 79.4, population: 82.4 },
258
+ { country: 'France', continent: 'Europe', gdp: 30470, life: 80.7, population: 61.1 },
259
+ { country: 'Spain', continent: 'Europe', gdp: 28821, life: 80.9, population: 40.4 },
260
+ { country: 'Italy', continent: 'Europe', gdp: 28570, life: 80.5, population: 58.1 },
261
+ { country: 'United Kingdom', continent: 'Europe', gdp: 33203, life: 79.4, population: 60.8 },
262
+ { country: 'Poland', continent: 'Europe', gdp: 15390, life: 75.6, population: 38.5 },
263
+ { country: 'Norway', continent: 'Europe', gdp: 49357, life: 80.2, population: 4.6 }
264
+ ];
265
+
266
+ export const COUNTRIES: CountryRow[] = COUNTRY_INDICATORS.map((country) => ({
267
+ ...country,
268
+ income: country.gdp >= 20000 ? 'High' : country.gdp >= 5000 ? 'Middle' : 'Low'
269
+ }));
270
+
271
+ // ── Households of Buenos Aires City by rooms and people, 2022 ───────────────
272
+ // 2022 national census. Share of all households, in percent.
273
+
274
+ export type HouseholdRow = { rooms: string; people: string; share: number };
275
+
276
+ export const ROOMS = ['1', '2', '3', '4', '5+'];
277
+
278
+ const HOUSEHOLD_SHARES = [
279
+ { people: '1', byRooms: [8.8, 14.8, 9.6, 3.7, 2.1] },
280
+ { people: '2', byRooms: [3, 8.4, 9.3, 4.5, 2.9] },
281
+ { people: '3', byRooms: [1.2, 3.4, 5.9, 3.2, 2] },
282
+ { people: '4', byRooms: [0.6, 1.8, 3.8, 3, 2] },
283
+ { people: '5+', byRooms: [0.3, 1, 1.7, 1.5, 1.6] }
284
+ ];
285
+
286
+ export const HOUSEHOLDS: HouseholdRow[] = HOUSEHOLD_SHARES.flatMap(({ people, byRooms }) =>
287
+ ROOMS.map((rooms, roomsIndex) => ({ rooms, people, share: byRooms[roomsIndex] }))
288
+ );
289
+
290
+ // ── Adult heights by sex ────────────────────────────────────────────────────
291
+ // Nancy Howell's census of the !Kung San (Howell1, from the rethinking R
292
+ // package), adults only. Heights in centimeters, sorted.
293
+
294
+ export type HeightRow = { height: number; group: string };
295
+
296
+ const FEMALE_HEIGHTS = [
297
+ 137, 137, 138, 139, 140, 140, 140, 141, 142, 142, 142, 142, 142, 142, 143, 143, 143, 143, 143,
298
+ 143, 143, 143, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 145, 145, 145, 145, 145, 145,
299
+ 145, 145, 145, 145, 145, 145, 145, 145, 146, 146, 146, 146, 146, 146, 146, 146, 147, 147, 147,
300
+ 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 148, 148, 148, 148, 148, 148, 148, 148,
301
+ 148, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 150, 150,
302
+ 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 151,
303
+ 151, 151, 151, 151, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152,
304
+ 152, 152, 152, 153, 153, 153, 153, 153, 153, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154,
305
+ 154, 154, 154, 154, 155, 155, 155, 155, 155, 155, 155, 156, 156, 156, 156, 156, 156, 156, 156,
306
+ 156, 156, 156, 156, 157, 157, 157, 157, 157, 158, 159, 159, 160, 162, 163, 163
307
+ ];
308
+
309
+ const MALE_HEIGHTS = [
310
+ 140, 142, 146, 147, 148, 149, 149, 149, 149, 151, 151, 152, 152, 152, 152, 152, 152, 152, 152,
311
+ 153, 154, 154, 154, 155, 155, 155, 155, 155, 155, 155, 156, 156, 156, 156, 157, 157, 157, 157,
312
+ 157, 157, 157, 157, 157, 157, 157, 157, 157, 158, 158, 158, 158, 158, 158, 158, 159, 159, 159,
313
+ 159, 159, 159, 159, 159, 159, 159, 159, 159, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160,
314
+ 160, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161,
315
+ 161, 161, 161, 161, 161, 162, 162, 162, 162, 162, 162, 162, 163, 163, 163, 163, 163, 163, 163,
316
+ 163, 163, 163, 163, 163, 163, 163, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 165,
317
+ 165, 166, 166, 166, 166, 166, 166, 166, 166, 167, 167, 167, 167, 167, 167, 168, 168, 168, 169,
318
+ 169, 169, 170, 170, 170, 170, 171, 171, 171, 171, 173, 173, 179
319
+ ];
320
+
321
+ export const HEIGHTS: HeightRow[] = [
322
+ ...FEMALE_HEIGHTS.map((height) => ({ height, group: 'female' })),
323
+ ...MALE_HEIGHTS.map((height) => ({ height, group: 'male' }))
324
+ ];
package/llms-full.txt CHANGED
@@ -12,7 +12,7 @@ Svelte 5 plot components on top of svelteplot. This file is checked against the
12
12
  ## The mental model
13
13
 
14
14
  - A **plot** is one graph (axes + marks). Titles, subtitle, caption, legend, facets and a timeline are props of the plot itself.
15
- - Plots: `LinePlot AreaPlot BarPlot HistogramPlot HeatmapPlot PyramidPlot ScatterPlot GeoPlot`.
15
+ - Plots: `LinePlot AreaPlot BarPlot HistogramPlot BoxPlot HeatmapPlot PyramidPlot ScatterPlot GeoPlot`.
16
16
  - `PlotComposer` lays several plots out in one figure with shared hover, legend and timeline.
17
17
  - `width` and `height` are pixel numbers on a standalone plot. To fill a container or the window, bind its size (`examples/ResponsiveLine.svelte`) and render the plot once both are > 0.
18
18
 
@@ -38,17 +38,17 @@ Two ways to give data, one per plot:
38
38
  | `title` `subtitle` `caption` | Text chrome. `header` / `footer` snippets replace title+subtitle / caption. |
39
39
  | `legend` | unset/`true`: automatic legend; `false`: none; `{ series: { interactive: { enabled: true, disabled: { mode: 'omit' } } } }` adjusts a section. |
40
40
  | `order` | Chrome blocks top to bottom, e.g. `['title','subtitle','legend','plot','caption']`; rows as `'(plot, timeline)'`; `'divider'` between blocks. |
41
- | `scales` | Per channel: `{ x: { label, tickFormat, domain, type, ticks, grid }, y: {...}, color: { scheme, domain } }`. |
41
+ | `scales` | Per channel: `{ x: { label, tickFormat, domain, type, ticks, grid, niceTolerance, edgeTicks }, y: {...}, color: { scheme, domain } }`. Stacked parts adding up to a hair past a tick (`100.2`) stay on it; `niceTolerance` (default `0.05` of a step) tunes that. A continuous x axis also ticks the data's first and last values (y too with `edgeTicks: true`); `edgeTicks: false` or `{ collision: 'drop-nice'\|'drop-edge'\|'extend' }` (global defaults: `axis.edgeTicks.x` / `.y`). |
42
42
  | `styles` | `{ values: {...}, frame: true, gaps: {...} }` (shape depends on the kind). |
43
43
  | `segments` | Style ranges of the data (rule 1 below). |
44
44
  | `markers` | Extra marks, an array of `{ type: '...', ... }`. |
45
45
  | `margins` | `{ top, right, bottom, left }`, each a number or `'auto'` (all `'auto'` by default). |
46
- | `tooltip` | `true` or `{ strategy, sync, format, content, series, anchorX, anchorY }`. |
47
- | `timeline` | `{ accessor: 'year' }` steps through the rows by a field, drawn as a player bar (default); `type: 'rule'` draws a rule beside the plot instead. |
46
+ | `tooltip` | `true` or `{ strategy, sync, format, content, series, anchorX, anchorY }`. The text lists matched rows highest value first; a `content` snippet gets them unsorted. |
47
+ | `timeline` | `{ accessor: 'year' }` steps through the rows by a field, drawn as a player bar (default); `type: 'rule'` draws a rule beside the plot instead. The field may hold numbers, `Date`s or date strings (ISO, or any other with `dateFormat: 'DD/MM/YYYY'`); date steps read as dates, and reach `onvalue`, `player.format` and `useFigure().timeline` (`steps`, `value`, `seekValue`) as `Date`s. |
48
48
  | `facet` | `{ by: 'field', columns, gap, scales: 'shared'\|'free', mode: 'grid'\|'carousel'\|'paginated' }` or `{ rows, cols }`. |
49
49
  | `transforms` `statistics` | svelteplot transforms / derived summaries (regression, movingAverage, ...). |
50
50
 
51
- Per kind: `BarPlot` x = category, y = value (swap for horizontal bars); `PyramidPlot` mirrors two series around zero; `HeatmapPlot` colors cells by `color`; `HistogramPlot` bins one numeric field; `ScatterPlot` takes `r` (size) and `symbol`; `GeoPlot` takes GeoJSON/TopoJSON in `data` and `coordinates` for the projection (`docs/geo.md`).
51
+ Per kind: `BarPlot` x = category, y = value (swap for horizontal bars); `PyramidPlot` mirrors two series around zero; `HeatmapPlot` colors cells by `color`; `HistogramPlot` bins one numeric field; `BoxPlot` x = category, y = value, one box per category (its tooltip row is the box summary: `min`, `p25`, `median`, `p75`, `max`, `count`, `outliers`); `ScatterPlot` takes `r` (size) and `symbol`; `GeoPlot` takes GeoJSON/TopoJSON in `data` and `coordinates` for the projection (`docs/geo.md`).
52
52
 
53
53
  ## Switches
54
54
 
@@ -104,11 +104,39 @@ GeoPlot: `graticule sphere dot text vector link contour inset geo-hover geo-comp
104
104
 
105
105
  ## Examples (in `examples/`, all runnable)
106
106
 
107
+ - `LineByRegion.svelte` - one line per region with title, legend, end labels and tooltip.
107
108
  - `ResponsiveLine.svelte` - fill the window, end-of-line labels, axis labels.
108
109
  - `Tooltip.svelte` - tooltip + hover marker.
109
110
  - `Segments.svelte` - styling a range of the data.
110
111
  - `LegendToggle.svelte` + `LegendReader.svelte` - interactive legend and reading its state.
111
112
  - `TimelinePlayer.svelte` - animated bar chart with a player.
113
+ - `TimelineRule.svelte` - a timeline rule moved by a slider through `progress`.
112
114
  - `Facets.svelte` - small multiples with a synced tooltip.
113
115
  - `BarValues.svelte` - value labels on bars.
114
116
  - `CustomTooltip.svelte` + `TooltipCard.svelte` - a tooltip drawn with your own card (`tooltip.content`).
117
+ - `FirstPlot.svelte` - the smallest plot: rows, fields and a pixel size.
118
+ - `LineDates.svelte` - line per series over a date axis, with end labels.
119
+ - `StackedArea.svelte` - stacked area plot.
120
+ - `GroupedBars.svelte` - vertical bars grouped by color.
121
+ - `HorizontalBars.svelte` - horizontal bars.
122
+ - `Histogram.svelte` - overlapping histograms.
123
+ - `Box.svelte` - box plots per group.
124
+ - `Heatmap.svelte` - heatmap.
125
+ - `Pyramid.svelte` - population pyramid.
126
+ - `Scatter.svelte` - scatter plot sized, colored and shaped by channels.
127
+ - `Scales.svelte` - axis labels, fixed domain, gridlines and series colors.
128
+ - `ValueLabels.svelte` - end-of-line value labels with a custom format.
129
+ - `Gaps.svelte` - filling gaps of missing values.
130
+ - `SegmentHighlight.svelte` - highlighting a range of one series.
131
+ - `SegmentsBySeries.svelte` - segments per series and shared rules.
132
+ - `HeatmapSegments.svelte` - segments on a heatmap.
133
+ - `Markers.svelte` - hover, rule and delta markers.
134
+ - `StackedBars.svelte` - bars stacked with stackY.
135
+ - `MovingAverage.svelte` - moving average with windowY.
136
+ - `Regression.svelte` - linear regression with a confidence band.
137
+ - `LegendSections.svelte` - explicit legend sections.
138
+ - `Chrome.svelte` - title, subtitle and caption.
139
+ - `ChromeOrder.svelte` - ordering chrome blocks with dividers.
140
+ - `FacetsCarousel.svelte` - facets as a carousel.
141
+ - `FacetsRowsCols.svelte` - facets by rows and columns.
142
+ - `Composer.svelte` - several plots in one figure.
package/llms.txt CHANGED
@@ -1,6 +1,6 @@
1
1
  # @fundar/data-chart-telling
2
2
 
3
- > Svelte 5 plot components (line, area, bar, histogram, heatmap, pyramid, scatter, geo) built on svelteplot, with titles, legends, tooltips, facets, timelines and value labels built in. Plots take pixel `width`/`height`; hover and tooltips are switched on with `tooltip` and a `hover` marker.
3
+ > Svelte 5 plot components (line, area, bar, histogram, box, heatmap, pyramid, scatter, geo) built on svelteplot, with titles, legends, tooltips, facets, timelines and value labels built in. Plots take pixel `width`/`height`; hover and tooltips are switched on with `tooltip` and a `hover` marker.
4
4
 
5
5
  Start with the full guide: it lists the mental model, the props every plot shares, the switches and the rules that matter. Runnable examples are type-checked, so copy them as a starting point.
6
6
 
@@ -12,7 +12,7 @@ Start with the full guide: it lists the mental model, the props every plot share
12
12
 
13
13
  - [Getting started](./docs/getting-started.md): install, first plot, sizing, data
14
14
  - [Concepts](./docs/concepts.md): plots, figures, rows and series, channels, shared props
15
- - [Plots](./docs/plots.md): the eight plot kinds
15
+ - [Plots](./docs/plots.md): the nine plot kinds
16
16
  - [Scales](./docs/scales.md): axes and channel scales
17
17
  - [Styles and segments](./docs/styles-and-segments.md): value labels, frame, gaps, ranges of data
18
18
  - [Markers](./docs/markers.md): annotations, regions, hover highlight, custom components
@@ -29,14 +29,42 @@ Start with the full guide: it lists the mental model, the props every plot share
29
29
 
30
30
  ## Examples
31
31
 
32
+ - [Line plot by region with title, legend, end labels and tooltip](./examples/LineByRegion.svelte)
32
33
  - [Responsive line chart with end labels](./examples/ResponsiveLine.svelte)
33
34
  - [Tooltip and hover marker](./examples/Tooltip.svelte)
34
35
  - [Segments (styling a range of data)](./examples/Segments.svelte)
35
36
  - [Interactive legend and reading its state](./examples/LegendToggle.svelte)
36
37
  - [Timeline player](./examples/TimelinePlayer.svelte)
38
+ - [Timeline rule driven by a slider](./examples/TimelineRule.svelte)
37
39
  - [Facets with a synced tooltip](./examples/Facets.svelte)
38
40
  - [Bars with value labels](./examples/BarValues.svelte)
39
41
  - [Custom tooltip card](./examples/CustomTooltip.svelte)
42
+ - [The smallest plot: rows, fields and a pixel size](./examples/FirstPlot.svelte)
43
+ - [Line per series over a date axis, with end labels](./examples/LineDates.svelte)
44
+ - [Stacked area plot](./examples/StackedArea.svelte)
45
+ - [Vertical bars grouped by color](./examples/GroupedBars.svelte)
46
+ - [Horizontal bars](./examples/HorizontalBars.svelte)
47
+ - [Overlapping histograms](./examples/Histogram.svelte)
48
+ - [Box plots per group](./examples/Box.svelte)
49
+ - [Heatmap](./examples/Heatmap.svelte)
50
+ - [Population pyramid](./examples/Pyramid.svelte)
51
+ - [Scatter plot sized, colored and shaped by channels](./examples/Scatter.svelte)
52
+ - [Axis labels, fixed domain, gridlines and series colors](./examples/Scales.svelte)
53
+ - [End-of-line value labels with a custom format](./examples/ValueLabels.svelte)
54
+ - [Filling gaps of missing values](./examples/Gaps.svelte)
55
+ - [Highlighting a range of one series](./examples/SegmentHighlight.svelte)
56
+ - [Segments per series and shared rules](./examples/SegmentsBySeries.svelte)
57
+ - [Segments on a heatmap](./examples/HeatmapSegments.svelte)
58
+ - [Hover, rule and delta markers](./examples/Markers.svelte)
59
+ - [Bars stacked with stackY](./examples/StackedBars.svelte)
60
+ - [Moving average with windowY](./examples/MovingAverage.svelte)
61
+ - [Linear regression with a confidence band](./examples/Regression.svelte)
62
+ - [Explicit legend sections](./examples/LegendSections.svelte)
63
+ - [Title, subtitle and caption](./examples/Chrome.svelte)
64
+ - [Ordering chrome blocks with dividers](./examples/ChromeOrder.svelte)
65
+ - [Facets as a carousel](./examples/FacetsCarousel.svelte)
66
+ - [Facets by rows and columns](./examples/FacetsRowsCols.svelte)
67
+ - [Several plots in one figure](./examples/Composer.svelte)
40
68
  - [Shared example data](./examples/data.ts)
41
69
 
42
70
  ## Types
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fundar/data-chart-telling",
3
- "version": "1.0.0",
3
+ "version": "1.1.0",
4
4
  "description": "Declarative charts, maps and small multiples for Svelte, built on svelteplot.",
5
5
  "license": "MIT",
6
6
  "author": "Ezequiel Puerta",
@@ -123,6 +123,7 @@
123
123
  "test:stories": "vitest run --project=storybook",
124
124
  "storybook": "storybook dev -p 6006",
125
125
  "build-storybook": "storybook build",
126
- "docs:images": "node scripts/docs-images.mjs"
126
+ "docs:images": "node scripts/docs-images.mjs",
127
+ "docs:examples": "node scripts/sync-examples.mjs"
127
128
  }
128
129
  }
@@ -1,8 +0,0 @@
1
- import type { AxisScale } from '../../types/layout/scales';
2
- import type { AxisValue } from '../../types/layout/mapping';
3
- /**
4
- * An {@link AxisScale} as svelteplot's `<Plot x>`/`<Plot y>` accepts it: without
5
- * `grid`, `axisDisplay` and a vertical `label`, which `ScalesLayout` draws, and
6
- * with `nice: true` on an unpinned numeric axis.
7
- */
8
- export declare function buildScale<TData>(scale: AxisScale<TData> | undefined, rows: TData[], getAxisValue: (d: TData) => AxisValue): AxisScale<TData> | undefined;
@@ -1,67 +0,0 @@
1
- import { SvelteSet } from 'svelte/reactivity';
2
- /** One representative row per unique axis value (first occurrence in `rows`). */
3
- function uniqueRows(rows, getAxisValue) {
4
- const seen = new SvelteSet();
5
- const out = [];
6
- for (const row of rows) {
7
- const v = getAxisValue(row);
8
- if (seen.has(v))
9
- continue;
10
- seen.add(v);
11
- out.push(row);
12
- }
13
- return out;
14
- }
15
- /** svelteplot's `sort` option only does a fixed ascending sort; a custom `scale.sort` comparator is resolved into an explicit `domain` instead, which svelteplot honors as-is. */
16
- function sortedScale(scale, rows, getAxisValue) {
17
- const sort = scale?.sort;
18
- if (!sort || scale?.domain)
19
- return scale;
20
- const reps = uniqueRows(rows, getAxisValue);
21
- if (reps.length === 0 || !reps.every((r) => typeof getAxisValue(r) === 'string'))
22
- return scale;
23
- const domain = [...reps].sort(sort).map(getAxisValue);
24
- return { ...scale, domain };
25
- }
26
- /** Scale types {@link buildScale}'s default `nice` only applies to - every other type has its own tick semantics (or isn't numeric at all). */
27
- const CONTINUOUS_NUMERIC_TYPES = new Set(['auto', 'linear', 'pow', 'sqrt', 'log', 'symlog']);
28
- /** Whether the axis' values are numeric; `nice` on a categorical axis would make svelteplot treat it as linear. */
29
- function isNumericAxis(rows, getAxisValue) {
30
- let numericSeen = 0;
31
- for (const row of rows) {
32
- const v = getAxisValue(row);
33
- if (v == null)
34
- continue;
35
- if (typeof v !== 'number')
36
- return false;
37
- if (++numericSeen >= 2)
38
- return true;
39
- }
40
- return numericSeen > 0;
41
- }
42
- /**
43
- * An {@link AxisScale} as svelteplot's `<Plot x>`/`<Plot y>` accepts it: without
44
- * `grid`, `axisDisplay` and a vertical `label`, which `ScalesLayout` draws, and
45
- * with `nice: true` on an unpinned numeric axis.
46
- */
47
- export function buildScale(scale, rows, getAxisValue) {
48
- const sorted = sortedScale(scale, rows, getAxisValue);
49
- const wantsNice = scale?.domain == null &&
50
- scale?.ticks == null &&
51
- scale?.nice == null &&
52
- (scale?.type == null || CONTINUOUS_NUMERIC_TYPES.has(scale.type)) &&
53
- isNumericAxis(rows, getAxisValue);
54
- const removesAxis = scale?.axisDisplay === 'none';
55
- if (!sorted && !wantsNice && !removesAxis)
56
- return sorted;
57
- const out = { ...sorted };
58
- delete out.grid;
59
- delete out.axisDisplay;
60
- if (removesAxis)
61
- out.axis = false;
62
- if (scale?.labelOrientation === 'vertical')
63
- out.label = false;
64
- if (wantsNice)
65
- out.nice = true;
66
- return out;
67
- }
@@ -1,23 +0,0 @@
1
- /** Step between "nice" (1/2/5×10ⁿ) tick values for an approximate `count` over `[start, stop]`. */
2
- export declare function tickStep(start: number, stop: number, count: number): number;
3
- /** Multiples of `step` covering `[start, stop]`, inclusive of either endpoint when it lands exactly on one. */
4
- export declare function ticksInRange(start: number, stop: number, step: number): number[];
5
- /** Ticks-per-axis heuristic - used by `ScalesLayout` to pick a target count from an axis's actual pixel span. */
6
- export declare function naturalTickCount(span: number, spacing: number, min: number): number;
7
- /**
8
- * Widens `[lo, hi]` outward to the nearest `count`-ish "nice" step -
9
- * equivalent to d3's own `scale.nice(count)`, except a value within
10
- * `epsilonFraction × step` of a step boundary is treated as already sitting
11
- * on it, rather than needing a full extra step to clear it.
12
- *
13
- * Exists because plain `Math.ceil`/`Math.floor` (what `nice()` uses
14
- * internally, both here and in d3) has zero tolerance: a value meant to be
15
- * exactly `100` that comes out `100.00000000000001` after summing several
16
- * independently-rounded parts (a common shape for "these categories add up
17
- * to a whole" data, e.g. percentages of a total) still fails `hi <= step*N`
18
- * and gets rounded up a full step further than the data actually warrants
19
- * (`100` → `110`, not just a hair past `100`). `epsilonFraction` defaults to
20
- * 1% of the step - small enough to only catch rounding/summation noise, not
21
- * data that's genuinely meaningfully past the boundary.
22
- */
23
- export declare function niceDomain(lo: number, hi: number, count?: number, epsilonFraction?: number): [number, number];
@@ -1,59 +0,0 @@
1
- // d3-array-compatible "nice" tick step/generation, reimplemented locally to
2
- // avoid depending on internals svelteplot itself pulls in (not one of our
3
- // declared dependencies) - see https://d3js.org/d3-array/ticks.
4
- /** Step between "nice" (1/2/5×10ⁿ) tick values for an approximate `count` over `[start, stop]`. */
5
- export function tickStep(start, stop, count) {
6
- const e10 = Math.sqrt(50);
7
- const e5 = Math.sqrt(10);
8
- const e2 = Math.sqrt(2);
9
- const step0 = Math.abs(stop - start) / Math.max(0, count);
10
- let step1 = 10 ** Math.floor(Math.log10(step0));
11
- const error = step0 / step1;
12
- if (error >= e10)
13
- step1 *= 10;
14
- else if (error >= e5)
15
- step1 *= 5;
16
- else if (error >= e2)
17
- step1 *= 2;
18
- return step1;
19
- }
20
- /** Multiples of `step` covering `[start, stop]`, inclusive of either endpoint when it lands exactly on one. */
21
- export function ticksInRange(start, stop, step) {
22
- if (!(step > 0) || !Number.isFinite(step))
23
- return [];
24
- const lo = Math.ceil(start / step);
25
- const hi = Math.floor(stop / step);
26
- const out = [];
27
- for (let i = lo; i <= hi; i++)
28
- out.push(i * step);
29
- return out;
30
- }
31
- /** Ticks-per-axis heuristic - used by `ScalesLayout` to pick a target count from an axis's actual pixel span. */
32
- export function naturalTickCount(span, spacing, min) {
33
- return Math.max(min, Math.round(span / spacing));
34
- }
35
- /**
36
- * Widens `[lo, hi]` outward to the nearest `count`-ish "nice" step -
37
- * equivalent to d3's own `scale.nice(count)`, except a value within
38
- * `epsilonFraction × step` of a step boundary is treated as already sitting
39
- * on it, rather than needing a full extra step to clear it.
40
- *
41
- * Exists because plain `Math.ceil`/`Math.floor` (what `nice()` uses
42
- * internally, both here and in d3) has zero tolerance: a value meant to be
43
- * exactly `100` that comes out `100.00000000000001` after summing several
44
- * independently-rounded parts (a common shape for "these categories add up
45
- * to a whole" data, e.g. percentages of a total) still fails `hi <= step*N`
46
- * and gets rounded up a full step further than the data actually warrants
47
- * (`100` → `110`, not just a hair past `100`). `epsilonFraction` defaults to
48
- * 1% of the step - small enough to only catch rounding/summation noise, not
49
- * data that's genuinely meaningfully past the boundary.
50
- */
51
- export function niceDomain(lo, hi, count = 5, epsilonFraction = 0.01) {
52
- if (!(hi > lo))
53
- return [lo, hi];
54
- const step = tickStep(lo, hi, count);
55
- const eps = step * epsilonFraction;
56
- const niceLo = Math.floor((lo + eps) / step) * step;
57
- const niceHi = Math.ceil((hi - eps) / step) * step;
58
- return [niceLo, niceHi];
59
- }