@fundar/data-chart-telling 1.16.0 → 1.17.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 (194) hide show
  1. package/CHANGELOG.md +32 -0
  2. package/dist/configuration/ConfigVars.svelte +14 -0
  3. package/dist/configuration/ConfigVars.svelte.d.ts +3 -0
  4. package/dist/configuration/config.svelte.d.ts +5 -0
  5. package/dist/configuration/config.svelte.js +17 -4
  6. package/dist/configuration/themes/dark.js +1 -1
  7. package/dist/configuration/themes/editorial.js +1 -1
  8. package/dist/index.d.ts +6 -3
  9. package/dist/index.js +2 -1
  10. package/dist/layout/base/BasePlotLayout.svelte +1 -1
  11. package/dist/layout/figure/Figure.svelte +5 -12
  12. package/dist/layout/figure/FigureChrome.svelte +2 -10
  13. package/dist/layout/geometry/GeometryLayout.svelte +42 -1
  14. package/dist/layout/hover/HoverSettings.d.ts +3 -1
  15. package/dist/layout/hover/HoverSettings.js +4 -2
  16. package/dist/layout/legend/DisabledStyle.d.ts +1 -1
  17. package/dist/layout/legend/DisabledStyle.js +1 -1
  18. package/dist/layout/legend/DiscreteBadgeItem.svelte +1 -1
  19. package/dist/layout/legend/DiscreteSection.svelte +1 -1
  20. package/dist/layout/legend/LegendState.svelte.d.ts +12 -2
  21. package/dist/layout/legend/LegendState.svelte.js +18 -4
  22. package/dist/layout/legend/LegendText.d.ts +4 -1
  23. package/dist/layout/legend/LegendText.js +7 -1
  24. package/dist/layout/margins/PlotMargins.svelte.d.ts +2 -2
  25. package/dist/layout/margins/PlotMargins.svelte.js +2 -2
  26. package/dist/layout/plot/Plot.svelte +4 -2
  27. package/dist/layout/plot/Plot.svelte.d.ts +2 -1
  28. package/dist/layout/scales/ScalesLayout.svelte +1 -1
  29. package/dist/layout/styles/ValueLabels.d.ts +33 -2
  30. package/dist/layout/styles/ValueLabels.js +67 -8
  31. package/dist/layout/timeline/Timeline.svelte +5 -1
  32. package/dist/markers/area/AreaMarker.svelte +3 -0
  33. package/dist/markers/arrow/ArrowMarker.svelte +9 -5
  34. package/dist/markers/bar/BarMarker.svelte +45 -48
  35. package/dist/markers/bollinger/BollingerMarker.svelte +3 -0
  36. package/dist/markers/box/BoxMarker.svelte +3 -1
  37. package/dist/markers/cell/CellMarker.svelte +11 -7
  38. package/dist/markers/contour/ContourMarker.svelte +3 -0
  39. package/dist/markers/delaunay/DelaunayLinkMarker.svelte +9 -5
  40. package/dist/markers/delaunay/DelaunayMeshMarker.svelte +3 -0
  41. package/dist/markers/density/DensityMarker.svelte +3 -0
  42. package/dist/markers/difference/DifferenceMarker.svelte +3 -0
  43. package/dist/markers/dot/DotMarker.svelte +11 -6
  44. package/dist/markers/geo/GraticuleMarker.svelte +3 -0
  45. package/dist/markers/geo/SphereMarker.svelte +3 -0
  46. package/dist/markers/hover/HoverMarker.svelte +22 -6
  47. package/dist/markers/hull/HullMarker.svelte +3 -0
  48. package/dist/markers/image/ImageMarker.svelte +3 -0
  49. package/dist/markers/line/LineMarker.svelte +3 -0
  50. package/dist/markers/link/LinkMarker.svelte +3 -0
  51. package/dist/markers/raster/RasterMarker.svelte +3 -0
  52. package/dist/markers/rect/RectMarker.svelte +25 -17
  53. package/dist/markers/regression/RegressionMarker.svelte +3 -0
  54. package/dist/markers/rule/RuleMarker.svelte +15 -5
  55. package/dist/markers/text/TextFit.d.ts +44 -0
  56. package/dist/markers/text/TextFit.js +86 -0
  57. package/dist/markers/text/TextMarker.svelte +186 -90
  58. package/dist/markers/text/TextMarker.svelte.d.ts +2 -0
  59. package/dist/markers/tick/TickMarker.svelte +9 -3
  60. package/dist/markers/trail/TrailMarker.svelte +3 -0
  61. package/dist/markers/utils/BoxLabel.d.ts +58 -0
  62. package/dist/markers/utils/BoxLabel.js +175 -0
  63. package/dist/markers/utils/MarkerEvents.d.ts +23 -0
  64. package/dist/markers/utils/MarkerEvents.js +44 -0
  65. package/dist/markers/utils/MarkerLabel.svelte +33 -0
  66. package/dist/markers/utils/MarkerLabel.svelte.d.ts +10 -0
  67. package/dist/markers/utils/RowStroke.d.ts +9 -21
  68. package/dist/markers/utils/RowStroke.js +10 -21
  69. package/dist/markers/utils/RowStyle.d.ts +26 -0
  70. package/dist/markers/utils/RowStyle.js +55 -0
  71. package/dist/markers/vector/VectorMarker.svelte +3 -0
  72. package/dist/markers/voronoi/VoronoiMarker.svelte +9 -5
  73. package/dist/markers/voronoi/VoronoiMeshMarker.svelte +3 -0
  74. package/dist/markers/waffle/WaffleMarker.svelte +9 -5
  75. package/dist/phases/Pipeline.d.ts +3 -1
  76. package/dist/phases/Pipeline.js +1 -0
  77. package/dist/phases/data/DataPhase.js +51 -10
  78. package/dist/phases/hover/HoverPhase.js +9 -1
  79. package/dist/phases/mapping/MappingPhase.js +2 -1
  80. package/dist/phases/markers/MarkerStyles.d.ts +18 -0
  81. package/dist/phases/markers/MarkerStyles.js +111 -0
  82. package/dist/phases/markers/MarkersPhase.d.ts +1 -1
  83. package/dist/phases/markers/MarkersPhase.js +7 -5
  84. package/dist/phases/statistics/StatisticsPhase.js +1 -1
  85. package/dist/phases/transforms/Transforms.js +3 -1
  86. package/dist/phases/transforms/TransformsPhase.js +58 -4
  87. package/dist/phases/transforms/methods/TreemapTransform.d.ts +28 -0
  88. package/dist/phases/transforms/methods/TreemapTransform.js +119 -0
  89. package/dist/plots/correlation/heatmap/defaults.js +2 -2
  90. package/dist/plots/correlation/heatmap/markers.d.ts +0 -8
  91. package/dist/plots/correlation/heatmap/markers.js +8 -35
  92. package/dist/plots/correlation/scatter/markers.d.ts +1 -1
  93. package/dist/plots/correlation/scatter/markers.js +2 -2
  94. package/dist/plots/distribution/histogram/defaults.js +2 -2
  95. package/dist/plots/distribution/histogram/markers.d.ts +1 -10
  96. package/dist/plots/distribution/histogram/markers.js +17 -59
  97. package/dist/plots/distribution/pyramid/defaults.js +3 -3
  98. package/dist/plots/distribution/pyramid/markers.d.ts +0 -10
  99. package/dist/plots/distribution/pyramid/markers.js +13 -64
  100. package/dist/plots/distribution/violin/markers.js +1 -1
  101. package/dist/plots/map/geo/phases/markers.js +3 -2
  102. package/dist/plots/part-of-a-whole/treemap/Plot.svelte +47 -0
  103. package/dist/plots/part-of-a-whole/treemap/Plot.svelte.d.ts +26 -0
  104. package/dist/plots/part-of-a-whole/treemap/defaults.d.ts +10 -0
  105. package/dist/plots/part-of-a-whole/treemap/defaults.js +167 -0
  106. package/dist/plots/ranking/bar/defaults.js +2 -2
  107. package/dist/plots/ranking/bar/markers.d.ts +1 -11
  108. package/dist/plots/ranking/bar/markers.js +38 -80
  109. package/dist/plots/ranking/bullet/markers.js +2 -3
  110. package/dist/plots/ranking/cleveland/ClevelandMarkers.js +17 -13
  111. package/dist/types/configuration/styling.d.ts +27 -2
  112. package/dist/types/layout/legend.d.ts +8 -1
  113. package/dist/types/layout/mapping.d.ts +12 -3
  114. package/dist/types/layout/scales.d.ts +6 -0
  115. package/dist/types/layout/tooltip.d.ts +16 -4
  116. package/dist/types/markers/axis/arrow.d.ts +2 -8
  117. package/dist/types/markers/axis/bar.d.ts +4 -8
  118. package/dist/types/markers/axis/cell.d.ts +5 -5
  119. package/dist/types/markers/axis/delaunayLink.d.ts +2 -2
  120. package/dist/types/markers/axis/rect.d.ts +4 -8
  121. package/dist/types/markers/axis/rule.d.ts +2 -16
  122. package/dist/types/markers/axis/tick.d.ts +2 -2
  123. package/dist/types/markers/axis/voronoi.d.ts +2 -2
  124. package/dist/types/markers/axis/waffle.d.ts +4 -3
  125. package/dist/types/markers/basic/dot.d.ts +2 -2
  126. package/dist/types/markers/basic/text.d.ts +56 -14
  127. package/dist/types/markers/channels/commonMarkerConfig.d.ts +4 -1
  128. package/dist/types/markers/channels/commonMarkerConfigWithArea.d.ts +1 -2
  129. package/dist/types/markers/channels/commonMarkerConfigWithLabel.d.ts +10 -0
  130. package/dist/types/markers/channels/commonMarkerConfigWithLabel.js +1 -0
  131. package/dist/types/markers/channels/commonMarkerEvents.d.ts +17 -0
  132. package/dist/types/markers/channels/commonMarkerEvents.js +13 -0
  133. package/dist/types/markers/geo/contour.d.ts +2 -1
  134. package/dist/types/markers/geo/graticule.d.ts +2 -1
  135. package/dist/types/markers/geo/link.d.ts +2 -1
  136. package/dist/types/markers/geo/sphere.d.ts +2 -1
  137. package/dist/types/markers/geo/vector.d.ts +2 -1
  138. package/dist/types/phases/data/phase.d.ts +5 -0
  139. package/dist/types/phases/hover/phase.d.ts +2 -0
  140. package/dist/types/phases/state.d.ts +7 -0
  141. package/dist/types/phases/statistics/phase.d.ts +2 -1
  142. package/dist/types/phases/transforms/phase.d.ts +11 -0
  143. package/dist/types/plots/constants.d.ts +14 -14
  144. package/dist/types/plots/correlation/heatmap/props.d.ts +3 -3
  145. package/dist/types/plots/correlation/scatter/props.d.ts +3 -3
  146. package/dist/types/plots/data/common.d.ts +19 -6
  147. package/dist/types/plots/data/geo.d.ts +1 -1
  148. package/dist/types/plots/distribution/box/props.d.ts +3 -3
  149. package/dist/types/plots/distribution/density/props.d.ts +3 -11
  150. package/dist/types/plots/distribution/histogram/props.d.ts +3 -11
  151. package/dist/types/plots/distribution/pyramid/props.d.ts +3 -3
  152. package/dist/types/plots/distribution/violin/props.d.ts +3 -3
  153. package/dist/types/plots/evolution/area/props.d.ts +3 -3
  154. package/dist/types/plots/evolution/line/props.d.ts +3 -3
  155. package/dist/types/plots/part-of-a-whole/treemap/props.d.ts +19 -0
  156. package/dist/types/plots/part-of-a-whole/treemap/props.js +1 -0
  157. package/dist/types/plots/part-of-a-whole/treemap/state.d.ts +6 -0
  158. package/dist/types/plots/part-of-a-whole/treemap/state.js +1 -0
  159. package/dist/types/plots/props.d.ts +7 -4
  160. package/dist/types/plots/ranking/bar/props.d.ts +3 -3
  161. package/dist/types/plots/ranking/cleveland/props.d.ts +3 -3
  162. package/dist/types/plots/ranking/dumbbell/props.d.ts +3 -3
  163. package/dist/types/plots/ranking/forest/props.d.ts +3 -3
  164. package/dist/types/plots/ranking/lollipop/props.d.ts +3 -3
  165. package/dist/types/plots/styling.d.ts +24 -0
  166. package/dist/types/transforms/index.d.ts +3 -1
  167. package/dist/types/transforms/treemap.d.ts +32 -0
  168. package/dist/types/transforms/treemap.js +5 -0
  169. package/dist/utils/color/ColorTones.d.ts +19 -0
  170. package/dist/utils/color/ColorTones.js +64 -0
  171. package/dist/utils/color/CssColor.d.ts +14 -5
  172. package/dist/utils/color/CssColor.js +69 -27
  173. package/dist/utils/color/index.d.ts +4 -0
  174. package/dist/utils/color/index.js +4 -0
  175. package/dist/utils/grouping.d.ts +18 -1
  176. package/dist/utils/grouping.js +31 -0
  177. package/docs/concepts.md +3 -2
  178. package/docs/legend.md +8 -5
  179. package/docs/markers.md +76 -26
  180. package/docs/plots/part-of-a-whole/treemap.md +69 -0
  181. package/docs/plots/ranking/bar.md +1 -1
  182. package/docs/plots.md +21 -20
  183. package/docs/scales.md +3 -0
  184. package/docs/state.md +1 -1
  185. package/docs/styles-and-segments.md +19 -18
  186. package/docs/theming.md +4 -11
  187. package/docs/tooltip-and-hover.md +1 -1
  188. package/docs/transforms-and-statistics.md +17 -16
  189. package/examples/Treemap.svelte +13 -0
  190. package/examples/datasets/economic-activity.ts +40 -0
  191. package/examples/datasets/index.ts +1 -0
  192. package/llms-full.txt +5 -5
  193. package/llms.txt +2 -0
  194. package/package.json +3 -1
@@ -1,66 +1,8 @@
1
1
  import { ProcessedPositions } from '../../../phases/transforms/ProcessedPositions';
2
- import { SERIES_KEY } from '../../../phases/transforms/TransformsPhase';
2
+ import { ValueLabels } from '../../../layout/styles/ValueLabels';
3
3
  import { Transforms } from '../../../phases/transforms/Transforms';
4
4
  import { resolveAccessor } from '../../../utils/accessors';
5
5
  import { PlotAxes } from '../../../phases/data/PlotAxes';
6
- /**
7
- * HistogramPlot's `styles.values` as one `'text'` marker over its non-empty
8
- * bins (a `filter` on the count), each labelled with its count by `format`.
9
- * `anchor` places each label along its bar: past its top (`'outside'`), just
10
- * inside it (`'start'`), at its base (`'end'`), or centred (`'middle'`) -
11
- * upward, or rightward on a horizontal histogram.
12
- */
13
- export function buildValueLabels(labels, state, ctx) {
14
- const { processedData: processed } = state;
15
- if (!processed?.data.length)
16
- return [];
17
- const { font, anchor, format } = labels;
18
- const nameOf = (d) => String(d[SERIES_KEY]);
19
- const value = PlotAxes.valueOf(state);
20
- const binned = PlotAxes.other(value);
21
- const horizontal = value === 'x';
22
- const position = ProcessedPositions.of(processed, value);
23
- const read = (channel) => channel === undefined
24
- ? undefined
25
- : resolveAccessor(channel);
26
- const topOf = (d) => Number(position[value](d));
27
- const bottomOf = read(processed.channels[`${value}1`]) ?? (() => 0);
28
- const countRead = read(processed.channels[value]);
29
- const countOf = (d) => countRead ? Number(countRead(d)) : topOf(d) - Number(bottomOf(d));
30
- const along = anchor === 'end'
31
- ? bottomOf
32
- : anchor === 'middle'
33
- ? (d) => (topOf(d) + Number(bottomOf(d))) / 2
34
- : topOf;
35
- // Outward from the bar's top: past it, just inside it, or out from its base.
36
- const outward = anchor === 'outside' ? 6 : anchor === 'start' ? -4 : anchor === 'end' ? 4 : 0;
37
- const outwardAnchor = anchor === 'start' ? 'end' : anchor === 'middle' ? 'middle' : 'start';
38
- const marker = Transforms.resolve({
39
- type: 'text',
40
- data: processed.data,
41
- [binned]: position[binned],
42
- [value]: along,
43
- transforms: [{ type: 'filter', filter: (d) => countOf(d) > 0 }],
44
- text: (d) => format(countOf(d), nameOf(d), d),
45
- styles: {
46
- ...font,
47
- fill: font?.fill ?? ctx.cfg.axis.color,
48
- fontSize: font?.fontSize ?? 12,
49
- dx: font?.dx ?? (horizontal ? outward : 0),
50
- dy: font?.dy ?? (horizontal ? 0 : -outward),
51
- textAnchor: labels.textAnchor ?? (horizontal ? outwardAnchor : 'middle'),
52
- lineAnchor: font?.lineAnchor ??
53
- (horizontal
54
- ? 'middle'
55
- : anchor === 'start'
56
- ? 'top'
57
- : anchor === 'middle'
58
- ? 'middle'
59
- : 'bottom')
60
- }
61
- }, processed.data);
62
- return marker.data?.length ? [marker] : [];
63
- }
64
6
  /**
65
7
  * HistogramPlot's "markers" phase: one `rect` per visual segment of each
66
8
  * series, drawing that segment's bins, dimmed with its series. The markers
@@ -70,6 +12,21 @@ export function buildValueLabels(labels, state, ctx) {
70
12
  export function buildMarks(state, ctx) {
71
13
  const { channels, options, transforms } = state.processedData;
72
14
  const fillOpacity = ctx.cfg.histogram.fillOpacity;
15
+ // Each bin writes its own count, along its bar: past its top (`'outside'`), against an edge, at its tip or its
16
+ // base - upward, or rightward on a horizontal histogram. A bin with nothing in it writes nothing.
17
+ const labels = state.valueLabels?.show ? state.valueLabels : undefined;
18
+ const value = PlotAxes.valueOf(state);
19
+ const position = ProcessedPositions.of(state.processedData, value);
20
+ const read = (channel) => channel === undefined
21
+ ? undefined
22
+ : resolveAccessor(channel);
23
+ const bottomOf = read(channels[`${value}1`]) ?? (() => 0);
24
+ const countRead = read(channels[value]);
25
+ const countOf = (d) => countRead ? Number(countRead(d)) : Number(position[value](d)) - Number(bottomOf(d));
26
+ const labelFor = (name) => labels?.shapeLabel(ctx, (d) => (countOf(d) > 0 ? labels.format(countOf(d), name, d) : ''), {
27
+ fill: 'currentColor',
28
+ ...ValueLabels.boxAnchor(labels.anchor, { isHorizontal: value === 'x' })
29
+ });
73
30
  const rects = (state.processedGroups ?? []).flatMap((group) => {
74
31
  const disabled = ctx.interaction?.fillFor(group.series.name);
75
32
  // A stroke the segment sets is not a reason to lose the series' fill, as the marker would take it for the color.
@@ -78,6 +35,7 @@ export function buildMarks(state, ctx) {
78
35
  type: 'rect',
79
36
  series: group.series.name,
80
37
  data: seg.data,
38
+ ...(labels ? { label: labelFor(group.series.name) } : {}),
81
39
  ...channels,
82
40
  ...options,
83
41
  styles: {
@@ -1,6 +1,6 @@
1
1
  import { ProcessedPositions } from '../../../phases/transforms/ProcessedPositions';
2
2
  import { PlotAxes } from '../../../phases/data/PlotAxes';
3
- import { buildMarks, buildValueLabels } from './markers';
3
+ import { buildMarks } from './markers';
4
4
  import { ValueLabels } from '../../../layout/styles/ValueLabels';
5
5
  import { seriesIdOf } from '../../../utils/grouping';
6
6
  import { isGapValue } from '../../utils/gaps';
@@ -48,7 +48,7 @@ export function buildPyramidDefaults() {
48
48
  values: {
49
49
  // A label past its bar's end reaches out on its own side, the longest bar's to the edge.
50
50
  reach: (labels, state) => {
51
- if (labels.anchor === 'middle' || labels.anchor === 'end')
51
+ if (labels.anchor !== 'outside')
52
52
  return [];
53
53
  const axis = PlotAxes.valueOf(state);
54
54
  const isHorizontal = state.axes?.isHorizontal ?? true;
@@ -63,7 +63,7 @@ export function buildPyramidDefaults() {
63
63
  }
64
64
  }
65
65
  },
66
- markers: { marks: buildMarks, valueLabels: buildValueLabels },
66
+ markers: { marks: buildMarks },
67
67
  hover: {
68
68
  // A bar sits at its signed magnitude - below or left of the baseline on the first
69
69
  // side - and its category, and reads as its magnitude.
@@ -1,15 +1,5 @@
1
- import type { TextMarkerConfig } from '../../../types/markers/common';
2
1
  import type { PyramidPlotState, PyramidPlotCtx } from '../../../types/plots/distribution/pyramid/state';
3
- import type { ValueLabels } from '../../../layout/styles/ValueLabels';
4
2
  import type { AxisBasedMarkersConfig } from '../../../types/markers/common';
5
- /**
6
- * PyramidPlot's `styles.values` as one `'text'` marker per series, over the
7
- * same processed rows its bars draw (gap rows never get a label), mirrored to
8
- * its own side of the shared zero baseline. `anchor` places each label along
9
- * its bar: past its end (`'outside'`/`'start'`), at the baseline (`'end'`),
10
- * or centred (`'middle'`). On a vertical pyramid the sides run down and up.
11
- */
12
- export declare function buildValueLabels<TData extends Record<string, unknown>>(labels: ValueLabels, state: PyramidPlotState<TData>, ctx: PyramidPlotCtx): TextMarkerConfig[];
13
3
  /**
14
4
  * PyramidPlot's "markers" phase: one `role: 'segment'` bar per visual
15
5
  * segment, along the category axis the "data" phase read (horizontal bars for
@@ -1,5 +1,5 @@
1
+ import { ValueLabels } from '../../../layout/styles/ValueLabels';
1
2
  import { resolveAccessor } from '../../../utils/accessors';
2
- import { SERIES_ID_KEY } from '../../../phases/transforms/TransformsPhase';
3
3
  import { seriesIdOf } from '../../../utils/grouping';
4
4
  import { PlotAxes } from '../../../phases/data/PlotAxes';
5
5
  import { getConfiguration } from '../../../configuration/config.svelte';
@@ -8,69 +8,6 @@ const GAP_KINDS = ['start', 'middle', 'end'];
8
8
  function signFor(s, magnitude) {
9
9
  return s.side === 'left' ? -magnitude : magnitude;
10
10
  }
11
- /**
12
- * PyramidPlot's `styles.values` as one `'text'` marker per series, over the
13
- * same processed rows its bars draw (gap rows never get a label), mirrored to
14
- * its own side of the shared zero baseline. `anchor` places each label along
15
- * its bar: past its end (`'outside'`/`'start'`), at the baseline (`'end'`),
16
- * or centred (`'middle'`). On a vertical pyramid the sides run down and up.
17
- */
18
- export function buildValueLabels(labels, state, ctx) {
19
- const { processedData } = state;
20
- if (!processedData)
21
- return [];
22
- const { font, anchor, format } = labels;
23
- const categoryAxis = PlotAxes.categoryOf(state);
24
- const valueAxis = PlotAxes.valueOf(state);
25
- const isHorizontal = state.axes?.isHorizontal ?? true;
26
- return (state.processedGroups ?? []).flatMap(({ series }) => {
27
- const id = seriesIdOf(series);
28
- const side = state.resolvedSeries.find((s) => seriesIdOf(s) === id).side;
29
- const isLeft = side === 'left';
30
- const getMagnitude = resolveAccessor(series[valueAxis]);
31
- const magnitude = (d) => Math.abs(Number(getMagnitude(d)));
32
- const signed = (d) => (isLeft ? -magnitude(d) : magnitude(d));
33
- const data = processedData.data.filter((d) => d[SERIES_ID_KEY] === id && !isGapValue(getMagnitude(d)));
34
- if (data.length === 0)
35
- return [];
36
- const outward = isLeft ? -1 : 1;
37
- const at = anchor === 'middle' ? (d) => signed(d) / 2 : anchor === 'end' ? 0 : signed;
38
- const shift = anchor === 'middle' ? 0 : outward * (anchor === 'end' ? 4 : 6);
39
- // Away from the baseline: left/right on a horizontal pyramid, down/up on a vertical one.
40
- const awayAnchor = isHorizontal
41
- ? anchor === 'middle'
42
- ? 'middle'
43
- : isLeft
44
- ? 'end'
45
- : 'start'
46
- : 'middle';
47
- const awayLine = isHorizontal
48
- ? 'middle'
49
- : anchor === 'middle'
50
- ? 'middle'
51
- : isLeft
52
- ? 'top'
53
- : 'bottom';
54
- return [
55
- {
56
- type: 'text',
57
- data,
58
- [valueAxis]: at,
59
- [categoryAxis]: series[categoryAxis],
60
- text: (d) => format(magnitude(d), series.name, d),
61
- styles: {
62
- ...font,
63
- fill: font?.fill ?? ctx.cfg.axis.color,
64
- fontSize: font?.fontSize ?? 12,
65
- dx: font?.dx ?? (isHorizontal ? shift : 0),
66
- dy: font?.dy ?? (isHorizontal ? 0 : -shift),
67
- textAnchor: labels.textAnchor ?? awayAnchor,
68
- lineAnchor: font?.lineAnchor ?? awayLine
69
- }
70
- }
71
- ];
72
- });
73
- }
74
11
  /**
75
12
  * PyramidPlot's "markers" phase: one `role: 'segment'` bar per visual
76
13
  * segment, along the category axis the "data" phase read (horizontal bars for
@@ -85,6 +22,12 @@ export function buildMarks(state, ctx) {
85
22
  const categoryAxis = PlotAxes.categoryOf(state);
86
23
  const valueAxis = PlotAxes.valueOf(state);
87
24
  const isHorizontal = state.axes?.isHorizontal ?? true;
25
+ const labels = state.valueLabels?.show ? state.valueLabels : undefined;
26
+ /**
27
+ * Where a series' labels sit on its bars, which run out from the shared baseline to its own side: left and right
28
+ * of it on a horizontal pyramid, down and up on a vertical one.
29
+ */
30
+ const anchorOf = (side) => ValueLabels.boxAnchor(labels?.anchor ?? 'outside', { isHorizontal, negative: side === 'left' });
88
31
  /** One bar's own channels: its category, and its span from the baseline to its signed value. */
89
32
  const span = (category, to) => ({
90
33
  isHorizontal,
@@ -118,6 +61,12 @@ export function buildMarks(state, ctx) {
118
61
  role: 'segment',
119
62
  data: realData,
120
63
  ...span(processedCategory, signed),
64
+ // Each bar writes its own magnitude, on its side of the baseline.
65
+ ...(labels
66
+ ? {
67
+ label: labels.shapeLabel(ctx, (d) => labels.format(Math.abs(Number(getMagnitude(d))), group.series.name, d), { fill: 'currentColor', ...anchorOf(group.series.side) })
68
+ }
69
+ : {}),
121
70
  styles: {
122
71
  // An outline asked for without a color is the fill's.
123
72
  ...(seg.styles.strokeWidth !== undefined
@@ -29,7 +29,7 @@ export function buildMarks(state, ctx) {
29
29
  : typeof styles?.box === 'number'
30
30
  ? styles.box
31
31
  : cfg.violin.boxWidth;
32
- const ink = cfg.axis.color;
32
+ const ink = 'currentColor';
33
33
  const violins = groups.flatMap((group, index) => {
34
34
  const name = group.series.name;
35
35
  const disabled = ctx.interaction?.fillFor(name);
@@ -1,3 +1,4 @@
1
+ import { MarkerStyles } from '../../../../phases/markers/MarkerStyles';
1
2
  import { LegendState } from '../../../../layout/legend/LegendState.svelte';
2
3
  import { GeoSegments } from '../GeoSegments';
3
4
  import { MarkerChannels } from '../../../../markers/utils/MarkerChannels';
@@ -58,7 +59,7 @@ export function buildMarkers(state, ctx) {
58
59
  data: group.data,
59
60
  fill: (d) => group.style.fill ?? resolveBaseFill(d),
60
61
  fillOpacity: group.style.fillOpacity ?? group.opacity ?? fillOpacity ?? 1,
61
- stroke: group.style.stroke ?? stroke ?? cfg.axis.color,
62
+ stroke: group.style.stroke ?? stroke ?? 'currentColor',
62
63
  strokeWidth: group.style.strokeWidth ?? strokeWidth ?? 0.5,
63
64
  hover: hoverable,
64
65
  onclick: onFeatureClick ? (e, d) => onFeatureClick(d, e) : undefined
@@ -66,6 +67,6 @@ export function buildMarkers(state, ctx) {
66
67
  const behindMarkers = authoredMarkers.filter(isBehindMarker);
67
68
  const frontMarkers = authoredMarkers.filter((m) => !isBehindMarker(m) && m.type !== 'geo-hover');
68
69
  const hoverMarker = authoredMarkers.find((m) => m.type === 'geo-hover');
69
- const markers = MarkerChannels.inheritAll([...behindMarkers, ...featureMarkers, ...frontMarkers, ...(hoverMarker ? [hoverMarker] : [])], state.scales);
70
+ const markers = MarkerStyles.resolveAll(MarkerChannels.inheritAll([...behindMarkers, ...featureMarkers, ...frontMarkers, ...(hoverMarker ? [hoverMarker] : [])], state.scales), { cfg: ctx.cfg });
70
71
  return { ...state, markers, styleGroups };
71
72
  }
@@ -0,0 +1,47 @@
1
+ <script lang="ts" generics="TData extends Record<string, unknown>">
2
+ import Plot from '../../../layout/plot/Plot.svelte';
3
+ import { buildTreemapDefaults } from './defaults';
4
+ import type {
5
+ TreemapPlotProps,
6
+ TreemapPlotCoreProps
7
+ } from '../../../types/plots/part-of-a-whole/treemap/props';
8
+
9
+ /**
10
+ * A part-to-whole of rectangles: each row is a tile whose area is its `value`, nested by `grouping` - a field,
11
+ * or the fields of several levels joined by dots (`'sector.branch'`), outermost first, whose last names the
12
+ * tile. The outermost level colors the tiles and is what the legend lists; each tile is labelled with its name
13
+ * and its share of the whole, cut short or left out where it does not fit (`styles.values`). Optional chrome
14
+ * (`title`, `subtitle`, `caption`, `header`, `footer`, `legend`) frames it; `timeline` and `facet` slice its
15
+ * `data` rows.
16
+ */
17
+ let {
18
+ title,
19
+ subtitle,
20
+ caption,
21
+ header,
22
+ footer,
23
+ legend,
24
+ order,
25
+ timeline,
26
+ facet,
27
+ tooltip,
28
+ margins = $bindable(),
29
+ ...core
30
+ }: TreemapPlotProps<TData> = $props();
31
+ </script>
32
+
33
+ <Plot
34
+ core={core as TreemapPlotCoreProps<TData>}
35
+ defaults={buildTreemapDefaults}
36
+ bind:margins
37
+ {title}
38
+ {subtitle}
39
+ {caption}
40
+ {header}
41
+ {footer}
42
+ {legend}
43
+ {order}
44
+ {timeline}
45
+ {facet}
46
+ {tooltip}
47
+ />
@@ -0,0 +1,26 @@
1
+ import Plot from '../../../layout/plot/Plot.svelte';
2
+ import type { TreemapPlotProps } from '../../../types/plots/part-of-a-whole/treemap/props';
3
+ declare function $$render<TData extends Record<string, unknown>>(): {
4
+ props: TreemapPlotProps<TData>;
5
+ exports: {};
6
+ bindings: "margins";
7
+ slots: {};
8
+ events: {};
9
+ };
10
+ declare class __sveltets_Render<TData extends Record<string, unknown>> {
11
+ props(): ReturnType<typeof $$render<TData>>['props'];
12
+ events(): ReturnType<typeof $$render<TData>>['events'];
13
+ slots(): ReturnType<typeof $$render<TData>>['slots'];
14
+ bindings(): "margins";
15
+ exports(): {};
16
+ }
17
+ interface $$IsomorphicComponent {
18
+ new <TData extends Record<string, unknown>>(options: import('svelte').ComponentConstructorOptions<ReturnType<__sveltets_Render<TData>['props']>>): import('svelte').SvelteComponent<ReturnType<__sveltets_Render<TData>['props']>, ReturnType<__sveltets_Render<TData>['events']>, ReturnType<__sveltets_Render<TData>['slots']>> & {
19
+ $$bindings?: ReturnType<__sveltets_Render<TData>['bindings']>;
20
+ } & ReturnType<__sveltets_Render<TData>['exports']>;
21
+ <TData extends Record<string, unknown>>(internal: unknown, props: ReturnType<__sveltets_Render<TData>['props']> & {}): ReturnType<__sveltets_Render<TData>['exports']>;
22
+ z_$$bindings?: ReturnType<__sveltets_Render<any>['bindings']>;
23
+ }
24
+ declare const Plot: $$IsomorphicComponent;
25
+ type Plot<TData extends Record<string, unknown>> = InstanceType<typeof Plot<TData>>;
26
+ export default Plot;
@@ -0,0 +1,10 @@
1
+ import type { AxisPlotDefaults } from '../../../types/phases';
2
+ import type { TreemapPlotState, TreemapPlotCtx } from '../../../types/plots/part-of-a-whole/treemap/state';
3
+ /**
4
+ * TreemapPlot on the shared pipeline: its rows are the tiles of the `treemap` transform, laid out in a unit square
5
+ * the plot's two continuous axes span (hidden). Its series are the outermost level of its `grouping`, which colors
6
+ * the tiles and fills the legend. The tiles are `rect` markers, labelled by their own `label`. Hovering a tile is
7
+ * hovering its row: an invisible `rect` over each one drives the plot's hover, and its tooltip names the tile and
8
+ * gives its share.
9
+ */
10
+ export declare function buildTreemapDefaults<TData extends Record<string, unknown>>(): AxisPlotDefaults<TreemapPlotState<TData>, TreemapPlotCtx>;
@@ -0,0 +1,167 @@
1
+ import { TREEMAP_ROW } from '../../../phases/transforms/methods/TreemapTransform';
2
+ import { NumberFormat } from '../../../utils/NumberFormat';
3
+ import { Grouping } from '../../../utils/grouping';
4
+ import { getConfiguration } from '../../../configuration/config.svelte';
5
+ /** A share is written with one decimal unless the author or the configuration's `numbers.decimals` says otherwise. */
6
+ const SHARE_DECIMALS = 1;
7
+ /** The width over the height of the box the tiles are shaped for, before the plot's own is known. */
8
+ const DEFAULT_ASPECT = 1.6;
9
+ /**
10
+ * What a treemap writes for a tile's share: `suffix` is a percent sign unless the author asks for something
11
+ * else, since a tile is a part of a whole.
12
+ */
13
+ function shareDisplay(values) {
14
+ return { ...NumberFormat.displayOf(values), suffix: values?.suffix ?? '%' };
15
+ }
16
+ /** A tile's name, path and share (from `0` to `100`), as the transform left them on its row. */
17
+ function tileOf(row) {
18
+ return {
19
+ name: String(row[TREEMAP_ROW.name] ?? ''),
20
+ path: row[TREEMAP_ROW.path] ?? [],
21
+ share: Number(row[TREEMAP_ROW.share]) * 100
22
+ };
23
+ }
24
+ /**
25
+ * The plot's tiles: a `treemap` of its `value`, nested by the levels of its `grouping` (its `color` when unset,
26
+ * as every plot groups by it then) and shaped for its box,
27
+ * with the options of the author's own `treemap` transforms over it (`tile`...), and their other transforms
28
+ * after it in order.
29
+ */
30
+ function buildTransforms(state) {
31
+ const own = state.transforms ?? [];
32
+ const base = {
33
+ type: 'treemap',
34
+ value: state.value,
35
+ levels: Grouping.levels(state.grouping ?? state.color, state.data),
36
+ aspect: state.width && state.height ? state.width / state.height : DEFAULT_ASPECT
37
+ };
38
+ const treemap = own
39
+ .filter((t) => t.type === 'treemap')
40
+ .reduce((merged, t) => ({ ...merged, ...t }), base);
41
+ return [treemap, ...own.filter((t) => t.type !== 'treemap')];
42
+ }
43
+ /** The edges of the tiles, as the processed rows carry them. */
44
+ function edgesOf(state) {
45
+ const { x1, x2, y1, y2 } = state.processedData.channels;
46
+ return { x1, x2, y1, y2 };
47
+ }
48
+ /**
49
+ * The keys of the segments a tile has below its series: its path down to each deeper level
50
+ * (`'Tertiary.Finance'`), outermost first, so the more specific wins. The series' own are in its style already.
51
+ */
52
+ function pathKeysOf(state, row) {
53
+ const { path } = tileOf(row);
54
+ return path
55
+ .slice(1)
56
+ .map((_, i) => path.slice(0, i + 2).join(Grouping.SEPARATOR))
57
+ .filter((key) => state.segments?.[key]);
58
+ }
59
+ /**
60
+ * The tiles as `rect` markers, each with a `label`: the name of the tile over its share, fitted to it. A series'
61
+ * tiles take its style - its color, under `styles.area` and the segments keyed by it (and `default`) - and the
62
+ * segments keyed by a tile's path restyle it, so the tiles split into one marker per style. The legend's dimming
63
+ * goes over all of them.
64
+ */
65
+ function buildMarks(state, ctx) {
66
+ if (!state.processedData?.data.length)
67
+ return [];
68
+ const look = ctx.cfg.treemap;
69
+ const edges = edgesOf(state);
70
+ const labels = state.valueLabels?.show ? state.valueLabels : undefined;
71
+ const number = NumberFormat.of(shareDisplay(state.values), ctx.cfg, SHARE_DECIMALS);
72
+ const shareText = (row, series) => {
73
+ const { share } = tileOf(row);
74
+ return state.values?.format ? state.values.format(share, series, row, number) : number(share);
75
+ };
76
+ /** The label of a series' tiles: the name over the share, fitted - or the share alone where the name does not fit. */
77
+ const labelOf = (labels, series) => {
78
+ const shape = labels.shapeLabel(ctx, (row) => `${tileOf(row).name}
79
+ ${shareText(row, series)}`, { fontSize: look.fontSize });
80
+ return {
81
+ ...shape,
82
+ styles: { lineHeight: look.lineHeight, ...shape.styles },
83
+ fit: { padding: look.padding, fallback: (row) => shareText(row, series) }
84
+ };
85
+ };
86
+ return (state.processedGroups ?? []).flatMap((group) => {
87
+ const series = group.series.name;
88
+ const dimmed = ctx.interaction?.fillFor(series);
89
+ const seriesColor = state.colorForSeries(series);
90
+ /** A tile's color: its series', or a tone of it by its path while `scales.color.depth` asks for one. */
91
+ const colorOf = (row) => state.colorForPath ? state.colorForPath(tileOf(row).path) : seriesColor;
92
+ return group.visualSegments.flatMap((segment) => {
93
+ const styled = new Map();
94
+ for (const row of segment.data) {
95
+ const keys = pathKeysOf(state, row);
96
+ const color = colorOf(row);
97
+ const id = JSON.stringify([keys, color]);
98
+ const bucket = styled.get(id) ?? styled.set(id, { keys, color, rows: [] }).get(id);
99
+ bucket.rows.push(row);
100
+ }
101
+ return [...styled.values()].map(({ keys, color, rows }) => {
102
+ const style = Object.assign({}, segment.styles, ...keys.flatMap((key) => state.segments[key].map((own) => own.styles ?? {})));
103
+ return {
104
+ type: 'rect',
105
+ data: rows,
106
+ ...edges,
107
+ styles: {
108
+ ...style,
109
+ fill: dimmed?.fill ?? style.fill ?? color,
110
+ fillOpacity: dimmed?.fillOpacity ?? style.fillOpacity,
111
+ stroke: style.stroke ?? look.stroke,
112
+ strokeWidth: style.strokeWidth ?? look.strokeWidth
113
+ },
114
+ label: labels && labelOf(labels, series)
115
+ };
116
+ });
117
+ });
118
+ });
119
+ }
120
+ /**
121
+ * TreemapPlot on the shared pipeline: its rows are the tiles of the `treemap` transform, laid out in a unit square
122
+ * the plot's two continuous axes span (hidden). Its series are the outermost level of its `grouping`, which colors
123
+ * the tiles and fills the legend. The tiles are `rect` markers, labelled by their own `label`. Hovering a tile is
124
+ * hovering its row: an invisible `rect` over each one drives the plot's hover, and its tooltip names the tile and
125
+ * gives its share.
126
+ */
127
+ export function buildTreemapDefaults() {
128
+ return {
129
+ data: {
130
+ discreteAxes: 0,
131
+ nested: true,
132
+ segments: { color: (style) => style?.fill, connect: false, base: { flat: 'area' } }
133
+ },
134
+ // An omitted sector keeps its tiles' room, and every other tile its share; a rescaled one does not.
135
+ transforms: { transforms: buildTransforms, keepsOmitted: true },
136
+ scales: {
137
+ defaults: () => ({
138
+ x: { axisDisplay: 'none', grid: false },
139
+ y: { axisDisplay: 'none', grid: false, reverse: true }
140
+ }),
141
+ fit: () => ({ x: { domain: [0, 1], fitted: true }, y: { domain: [0, 1], fitted: true } })
142
+ },
143
+ styles: { values: { show: true } },
144
+ markers: { marks: buildMarks },
145
+ hover: {
146
+ strategy: 'punctual',
147
+ // A tile is hovered through its own hit area, not through whichever point the pointer is nearest.
148
+ pointMatching: 'contains',
149
+ hitAreas: (state, hover) => hover.points.map(({ row }) => ({
150
+ type: 'rect',
151
+ data: [row],
152
+ ...edgesOf(state),
153
+ styles: { fill: 'transparent' },
154
+ hover: hover.active
155
+ })),
156
+ format: (state) => {
157
+ const number = NumberFormat.of(shareDisplay(state.values), getConfiguration(), SHARE_DECIMALS);
158
+ return ((row) => {
159
+ const tile = tileOf(row);
160
+ return `${tile.name}: ${number(tile.share)}`;
161
+ });
162
+ },
163
+ label: () => ((row) => tileOf(row).name),
164
+ context: () => ((row) => ({ share: tileOf(row).share }))
165
+ }
166
+ };
167
+ }
@@ -1,5 +1,5 @@
1
1
  import { ProcessedPositions } from '../../../phases/transforms/ProcessedPositions';
2
- import { buildMarks, buildValueLabels, buildHitAreas, buildBarGeometry } from './markers';
2
+ import { buildMarks, buildHitAreas, buildBarGeometry } from './markers';
3
3
  import { NiceTicks } from '../../../layout/scales/NiceTicks';
4
4
  import { SERIES_KEY } from '../../../phases/transforms/TransformsPhase';
5
5
  import { PlotAxes } from '../../../phases/data/PlotAxes';
@@ -90,7 +90,7 @@ export function buildBarDefaults() {
90
90
  }
91
91
  }
92
92
  },
93
- markers: { marks: buildMarks, valueLabels: buildValueLabels },
93
+ markers: { marks: buildMarks },
94
94
  hover: {
95
95
  // Each bar matched by its own band (see `buildHitAreas`); a grouped bar's dot is
96
96
  // drawn in its series' slice of the band.
@@ -1,10 +1,9 @@
1
- import type { BarMarkerConfig, TextMarkerConfig } from '../../../types/markers/common';
1
+ import type { BarMarkerConfig } from '../../../types/markers/common';
2
2
  import type { BarPlotState, BarPlotCtx } from '../../../types/plots/ranking/bar/state';
3
3
  import type { ProcessedRow } from '../../../types/phases';
4
4
  import type { AxisValue } from '../../../types/layout/mapping';
5
5
  import type { BandSlice } from '../../../types/layout/hover';
6
6
  import type { HoverConfig } from '../../../types/plots/props';
7
- import { ValueLabels } from '../../../layout/styles/ValueLabels';
8
7
  import type { AxisBasedMarkersConfig } from '../../../types/markers/common';
9
8
  /**
10
9
  * Where a bar plot's bars sit, read off the state its phases leave: each
@@ -27,15 +26,6 @@ export declare function buildBarGeometry<TData extends Record<string, unknown>>(
27
26
  * plot's hover when it is active.
28
27
  */
29
28
  export declare function buildHitAreas<TData extends Record<string, unknown>>(state: BarPlotState<TData>, hover: HoverConfig<any>, ctx: BarPlotCtx): BarMarkerConfig[];
30
- /**
31
- * BarPlot's `styles.values` as one `'text'` marker per series, over the same
32
- * processed rows its bars draw (gap rows never get a label). `anchor` places
33
- * each label along its bar: past its top (`'outside'`), just inside it
34
- * (`'start'`), at its base (`'end'`), or centred (`'middle'`); a stacked bar's
35
- * label always sits at the middle of its own span. A grouped bar's label
36
- * shifts with its series' slice of the band.
37
- */
38
- export declare function buildValueLabels<TData extends Record<string, unknown>>(labels: ValueLabels, state: BarPlotState<TData>, ctx: BarPlotCtx): TextMarkerConfig[];
39
29
  /**
40
30
  * BarPlot's "markers" phase: visible `role: 'segment'` bars - real data plus
41
31
  * gap fills - drawing the rows the "transforms" phase left, prepended onto