@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
@@ -5,11 +5,12 @@ import type { ValuesStyle } from '../../types/layout/styles';
5
5
  import type { ValueLabelsOptions } from '../../types/phases/styles/phase';
6
6
  import type { Series } from '../../types/plots/data/common';
7
7
  import type { FontStyle, StrokeStyle } from '../../types/plots/styling';
8
- import type { TextAnchor, ValueAnchor } from '../../types/plots/constants';
8
+ import type { LabelAnchor, TextAnchor, ValueAnchor } from '../../types/plots/constants';
9
9
  import type { BoxAnchors, EdgeBox } from '../../types/layout/box';
10
10
  import type { MarginSide } from '../../types/plots/props';
11
11
  import type { AxisPlotCtx, AxisPlotState, ProcessedData, ProcessedRow } from '../../types/phases';
12
12
  import type { TextMarkerConfig } from '../../types/markers/common';
13
+ import type { TextLabelConfig } from '../../types/markers/basic/text';
13
14
  /**
14
15
  * A plot's value labels: the author's `styles.values` over the plot kind's
15
16
  * own defaults, how their text measures, where they are expected to sit
@@ -78,7 +79,37 @@ export declare class ValueLabels {
78
79
  * dims with its row's series, and with `hoverIsolate`, hovering it
79
80
  * isolates that series. A row without a series gets the margins only.
80
81
  */
81
- hooks(ctx: AxisPlotCtx): Pick<TextMarkerConfig, 'fillOpacity' | 'fitMargins' | 'onPointerEnter' | 'onPointerLeave'>;
82
+ hooks(ctx: AxisPlotCtx): Pick<TextMarkerConfig, 'fitMargins' | 'onpointerenter' | 'onpointerleave' | 'styles'>;
83
+ /**
84
+ * Where in a bar a label goes, from the `anchor` of `styles.values`: its `label` anchor, and whether it is beyond
85
+ * the bar. `negative` tells a bar that runs to the left (or down) from its base from one that runs right (or up),
86
+ * which `'tip'`, `'base'` and `'outside'` follow; the others are the edges of the page. A `stacked` bar's label is
87
+ * always in the middle. An anchor the shapes do not read (`'start'`, `'end'`) is taken for `'outside'`, the default.
88
+ */
89
+ static boxAnchor(anchor: ValueAnchor, shape: {
90
+ isHorizontal: boolean;
91
+ negative?: boolean;
92
+ stacked?: boolean;
93
+ }): {
94
+ anchor: LabelAnchor;
95
+ outside?: boolean;
96
+ };
97
+ /** Whether a label at `anchor` goes to one side or the other of a bar with the sign of its value. */
98
+ static followsSign(anchor: ValueAnchor): boolean;
99
+ /**
100
+ * The `label` the shapes of a plot kind write their value in: this plot's `styles.values` over the `defaults` the kind
101
+ * sets, with what every label shares ({@link hooks}) - its dimming and isolating of a series. A label inside its shape
102
+ * never reaches out of the plot, so only one `outside` has the margins grow to hold it; and it takes the default
103
+ * `fill` only `outside`, for inside it is written in the color that contrasts with its shape.
104
+ */
105
+ shapeLabel(ctx: AxisPlotCtx, text: (row: ProcessedRow) => string, defaults?: {
106
+ fill?: string;
107
+ fontSize?: number;
108
+ fontWeight?: FontStyle['fontWeight'];
109
+ anchor?: LabelAnchor;
110
+ outside?: boolean;
111
+ gap?: number;
112
+ }): TextLabelConfig;
82
113
  /**
83
114
  * A processed row's own value, read through its series' own value channel
84
115
  * (`y`, or `x` on a horizontal plot) - what a label shows, even where a
@@ -140,14 +140,74 @@ export class ValueLabels {
140
140
  const isolate = this.hoverIsolate && interaction;
141
141
  return {
142
142
  fitMargins: true,
143
- fillOpacity: (d) => {
144
- const series = seriesOf(d);
145
- return series === undefined ? undefined : interaction?.fillFor(series)?.fillOpacity;
143
+ styles: {
144
+ fillOpacity: (d) => {
145
+ const series = seriesOf(d);
146
+ return series === undefined ? 1 : (interaction?.fillFor(series)?.fillOpacity ?? 1);
147
+ }
146
148
  },
147
- onPointerEnter: isolate
148
- ? (d) => interaction.isolate(seriesOf(d) ?? null)
149
+ onpointerenter: isolate
150
+ ? (_event, d) => interaction.isolate(seriesOf(d) ?? null)
149
151
  : undefined,
150
- onPointerLeave: isolate ? () => interaction.isolate(null) : undefined
152
+ onpointerleave: isolate ? () => interaction.isolate(null) : undefined
153
+ };
154
+ }
155
+ /**
156
+ * Where in a bar a label goes, from the `anchor` of `styles.values`: its `label` anchor, and whether it is beyond
157
+ * the bar. `negative` tells a bar that runs to the left (or down) from its base from one that runs right (or up),
158
+ * which `'tip'`, `'base'` and `'outside'` follow; the others are the edges of the page. A `stacked` bar's label is
159
+ * always in the middle. An anchor the shapes do not read (`'start'`, `'end'`) is taken for `'outside'`, the default.
160
+ */
161
+ static boxAnchor(anchor, shape) {
162
+ if (shape.stacked || anchor === 'middle')
163
+ return { anchor: 'middle' };
164
+ // A place in the shape as it is drawn on the page, which no sign moves.
165
+ if (anchor !== 'outside' &&
166
+ anchor !== 'start' &&
167
+ anchor !== 'end' &&
168
+ anchor !== 'tip' &&
169
+ anchor !== 'base') {
170
+ return { anchor };
171
+ }
172
+ const [tip, base] = shape.isHorizontal
173
+ ? shape.negative
174
+ ? ['left', 'right']
175
+ : ['right', 'left']
176
+ : shape.negative
177
+ ? ['bottom', 'top']
178
+ : ['top', 'bottom'];
179
+ if (anchor === 'base')
180
+ return { anchor: base };
181
+ return anchor === 'tip' ? { anchor: tip } : { anchor: tip, outside: true };
182
+ }
183
+ /** Whether a label at `anchor` goes to one side or the other of a bar with the sign of its value. */
184
+ static followsSign(anchor) {
185
+ return anchor === 'outside' || anchor === 'tip' || anchor === 'base';
186
+ }
187
+ /**
188
+ * The `label` the shapes of a plot kind write their value in: this plot's `styles.values` over the `defaults` the kind
189
+ * sets, with what every label shares ({@link hooks}) - its dimming and isolating of a series. A label inside its shape
190
+ * never reaches out of the plot, so only one `outside` has the margins grow to hold it; and it takes the default
191
+ * `fill` only `outside`, for inside it is written in the color that contrasts with its shape.
192
+ */
193
+ shapeLabel(ctx, text, defaults = {}) {
194
+ const { fitMargins, styles: shared, ...events } = this.hooks(ctx);
195
+ const { font } = this;
196
+ return {
197
+ ...events,
198
+ text,
199
+ anchor: defaults.anchor,
200
+ outside: defaults.outside,
201
+ gap: defaults.gap,
202
+ fitMargins: defaults.outside ? fitMargins : undefined,
203
+ styles: {
204
+ ...shared,
205
+ ...font,
206
+ // Inside its shape, a label unset is written in the color that reads best on it.
207
+ fill: font?.fill ?? (defaults.outside ? defaults.fill : undefined),
208
+ fontSize: font?.fontSize ?? defaults.fontSize ?? 12,
209
+ fontWeight: font?.fontWeight ?? defaults.fontWeight
210
+ }
151
211
  };
152
212
  }
153
213
  /**
@@ -232,14 +292,13 @@ export class ValueLabels {
232
292
  text: (d) => format(Number(valueOf(d)), nameOf(d), d),
233
293
  styles: {
234
294
  ...font,
235
- ...(this.fill ? { fill: this.fill } : {}),
295
+ fill: this.fill ?? ((d) => colorOf(d) ?? 'currentColor'),
236
296
  fontSize: font?.fontSize ?? 12,
237
297
  dx: font?.dx ?? (anchor === 'start' ? 6 : anchor === 'end' ? -6 : 0),
238
298
  dy: font?.dy ?? (anchor === 'outside' ? -8 : 0),
239
299
  textAnchor: this.textAnchor ?? (anchor === 'start' || anchor === 'end' ? anchor : 'middle'),
240
300
  lineAnchor: font?.lineAnchor ?? (anchor === 'outside' ? 'bottom' : 'middle')
241
301
  },
242
- fill: this.fill ? undefined : colorOf,
243
302
  declutter: {
244
303
  connector: this.strokeStyle,
245
304
  aligned: !!end.lane && (anchor === 'start' || anchor === 'end'),
@@ -2,6 +2,7 @@
2
2
  import { Plot, RuleX, RuleY, Text } from 'svelteplot';
3
3
  import { TimelineSteps } from './TimelineSteps';
4
4
  import { getConfiguration } from '../../configuration/config.svelte';
5
+ import ConfigVars from '../../configuration/ConfigVars.svelte';
5
6
  import type { Orientation, TimeValue } from '../../types/layout/timeline';
6
7
 
7
8
  /**
@@ -54,6 +55,9 @@
54
55
  );
55
56
  </script>
56
57
 
58
+ <!-- Its colors are the configuration's, with or without a figure around it. -->
59
+ <ConfigVars />
60
+
57
61
  <!-- Drawn with the configuration's font even outside a figure's chrome, which would hand it down. -->
58
62
  <div style="display: contents" style:font-family={config.font}>
59
63
  <Plot
@@ -89,7 +93,7 @@
89
93
  {...textOffset}
90
94
  fontSize={rule.activeFontSize}
91
95
  fontWeight={rule.activeFontWeight}
92
- fill={getConfiguration().timeline.activeColor}
96
+ fill="var(--dct-timeline-active, currentColor)"
93
97
  />
94
98
  </Plot>
95
99
  </div>
@@ -1,4 +1,5 @@
1
1
  <script lang="ts">
2
+ import { MarkerEvents } from '../utils/MarkerEvents';
2
3
  import { AreaX, AreaY } from 'svelteplot';
3
4
  import { usePlot } from '../../layout/plot/usePlot';
4
5
  import { MarkerChannels } from '../utils/MarkerChannels';
@@ -12,6 +13,7 @@
12
13
  * a band, the area runs from zero up to its own value.
13
14
  */
14
15
  let { marker }: { marker: AreaMarkerConfig } = $props();
16
+ const events = MarkerEvents.use(() => marker);
15
17
 
16
18
  const plot = usePlot();
17
19
  const isHorizontal = $derived(plot.axes?.isHorizontal ?? false);
@@ -36,6 +38,7 @@
36
38
 
37
39
  {#snippet area(rows: typeof data, fillOpacity: number | undefined)}
38
40
  <AreaMark
41
+ {...events.props}
39
42
  data={rows}
40
43
  x={marker.x}
41
44
  y={marker.y}
@@ -1,4 +1,6 @@
1
1
  <script lang="ts">
2
+ import { RowStyle } from '../utils/RowStyle';
3
+ import { MarkerEvents } from '../utils/MarkerEvents';
2
4
  import { Arrow } from 'svelteplot';
3
5
  import { usePlot } from '../../layout/plot/usePlot';
4
6
  import { RowStroke } from '../utils/RowStroke';
@@ -12,11 +14,12 @@
12
14
  * last, so such an arrow is placed in pixels instead.
13
15
  */
14
16
  let { marker }: { marker: ArrowMarkerConfig } = $props();
17
+ const events = MarkerEvents.use(() => marker);
15
18
 
16
19
  const plot = usePlot().svelteplot;
17
20
  const data = $derived(marker.data ?? []);
18
- /** Each row's own stroke, over the marker's `styles`. */
19
- const row = $derived(RowStroke.of(marker, marker.styles, { stroke: 'currentColor' }));
21
+ /** Each row's stroke: the marker's `styles`, which can be functions of the row. */
22
+ const row = $derived(RowStroke.of(marker.styles, data, { stroke: 'currentColor' }));
20
23
 
21
24
  type Row = Record<string, unknown>;
22
25
  type Channel = string | number | ((d: never) => unknown) | undefined;
@@ -51,6 +54,7 @@
51
54
  </script>
52
55
 
53
56
  <Arrow
57
+ {...events.props}
54
58
  data={placed}
55
59
  x1={onBand ? own('__x1') : marker.x1}
56
60
  y1={onBand ? own('__y1') : marker.y1}
@@ -59,9 +63,9 @@
59
63
  bend={marker.bend}
60
64
  headAngle={marker.headAngle ?? 60}
61
65
  headLength={marker.headLength ?? 8}
62
- inset={marker.inset ?? 0}
63
- insetStart={marker.insetStart}
64
- insetEnd={marker.insetEnd}
66
+ inset={RowStyle.field(marker.styles?.inset) ?? 0}
67
+ insetStart={RowStyle.field(marker.styles?.insetStart)}
68
+ insetEnd={RowStyle.field(marker.styles?.insetEnd)}
65
69
  sweep={marker.sweep}
66
70
  stroke={row.stroke}
67
71
  strokeWidth={row.strokeWidth}
@@ -1,7 +1,8 @@
1
1
  <script lang="ts">
2
+ import { RowStyle } from '../utils/RowStyle';
3
+ import { MarkerEvents } from '../utils/MarkerEvents';
2
4
  import { BarX, BarY } from 'svelteplot';
3
5
  import { usePlot } from '../../layout/plot/usePlot';
4
- import { PlotContext } from '../../layout/plot/PlotContext';
5
6
  import { MarkerChannels } from '../utils/MarkerChannels';
6
7
  import { getConfiguration } from '../../configuration/config.svelte';
7
8
  import type { BarMarkerConfig } from '../../types/markers/common';
@@ -12,19 +13,15 @@
12
13
  * one segment), or `role: 'highlight'` (visible area highlight).
13
14
  */
14
15
  let { marker }: { marker: BarMarkerConfig } = $props();
16
+ const events = MarkerEvents.use(() => marker);
17
+ /** The hover band stands for no row, so a style that is a function of one does not apply to it. */
18
+ const flatStyles = $derived(RowStyle.flat(marker.styles));
15
19
 
16
20
  const cfg = $derived(getConfiguration());
17
21
  const data = $derived(marker.data ?? []);
18
22
  const channelFill = $derived(MarkerChannels.buildFill(data, marker.color, marker.scales, cfg));
19
23
 
20
24
  const plot = usePlot().svelteplot;
21
- const hover = PlotContext.current()?.hover;
22
- const enter = $derived(
23
- marker.hover
24
- ? () => hover?.onPointer((marker.data ?? []) as Record<string, unknown>[])
25
- : undefined
26
- );
27
- const leave = $derived(marker.hover ? hover?.onLeave : undefined);
28
25
  function categoryScaleFor(isHorizontal: boolean) {
29
26
  return isHorizontal ? plot.scales.y : plot.scales.x;
30
27
  }
@@ -47,37 +44,41 @@
47
44
  marker.thickness != null ? Math.max(0, (bandwidth - marker.thickness) / 2) : undefined}
48
45
  {#if marker.isHorizontal}
49
46
  <BarX
47
+ {...events.props}
50
48
  {data}
51
49
  y={marker.y}
52
50
  x1={marker.x1}
53
51
  x2={marker.x2}
54
- insetTop={centered ?? (marker.insetTop != null ? marker.insetTop * bandwidth : undefined)}
52
+ insetTop={centered ??
53
+ (marker.styles?.insetTop != null ? marker.styles?.insetTop * bandwidth : undefined)}
55
54
  insetBottom={centered ??
56
- (marker.insetBottom != null ? marker.insetBottom * bandwidth : undefined)}
57
- fill={channelFill ?? marker.styles?.fill}
58
- fillOpacity={marker.styles?.fillOpacity}
59
- stroke={marker.styles?.stroke}
60
- strokeWidth={marker.styles?.strokeWidth}
61
- strokeOpacity={marker.styles?.strokeOpacity}
55
+ (marker.styles?.insetBottom != null ? marker.styles?.insetBottom * bandwidth : undefined)}
56
+ fill={channelFill ?? RowStyle.of(marker.styles?.fill, data)}
57
+ fillOpacity={RowStyle.opacity(marker.styles?.fillOpacity)}
58
+ stroke={RowStyle.of(marker.styles?.stroke, data)}
59
+ strokeWidth={RowStyle.field(marker.styles?.strokeWidth)}
60
+ strokeOpacity={RowStyle.opacity(marker.styles?.strokeOpacity)}
62
61
  strokeDasharray={marker.styles?.strokeDasharray}
63
- borderRadius={marker.styles?.borderRadius}
62
+ borderRadius={RowStyle.field(marker.styles?.borderRadius)}
64
63
  />
65
64
  {:else}
66
65
  <BarY
66
+ {...events.props}
67
67
  {data}
68
68
  x={marker.x}
69
69
  y1={marker.y1}
70
70
  y2={marker.y2}
71
- insetLeft={centered ?? (marker.insetLeft != null ? marker.insetLeft * bandwidth : undefined)}
71
+ insetLeft={centered ??
72
+ (marker.styles?.insetLeft != null ? marker.styles?.insetLeft * bandwidth : undefined)}
72
73
  insetRight={centered ??
73
- (marker.insetRight != null ? marker.insetRight * bandwidth : undefined)}
74
- fill={channelFill ?? marker.styles?.fill}
75
- fillOpacity={marker.styles?.fillOpacity}
76
- stroke={marker.styles?.stroke}
77
- strokeWidth={marker.styles?.strokeWidth}
78
- strokeOpacity={marker.styles?.strokeOpacity}
74
+ (marker.styles?.insetRight != null ? marker.styles?.insetRight * bandwidth : undefined)}
75
+ fill={channelFill ?? RowStyle.of(marker.styles?.fill, data)}
76
+ fillOpacity={RowStyle.opacity(marker.styles?.fillOpacity)}
77
+ stroke={RowStyle.of(marker.styles?.stroke, data)}
78
+ strokeWidth={RowStyle.field(marker.styles?.strokeWidth)}
79
+ strokeOpacity={RowStyle.opacity(marker.styles?.strokeOpacity)}
79
80
  strokeDasharray={marker.styles?.strokeDasharray}
80
- borderRadius={marker.styles?.borderRadius}
81
+ borderRadius={RowStyle.field(marker.styles?.borderRadius)}
81
82
  />
82
83
  {/if}
83
84
  {:else}
@@ -85,15 +86,14 @@
85
86
  {#if categoryScale?.type === 'band'}
86
87
  {@const bandwidth = categoryScale.fn.bandwidth()}
87
88
  {@const bandStart = Number(categoryScale.fn(marker.category as never) ?? 0)}
88
- {@const insetLeft = (marker.insetLeft ?? 0) * bandwidth}
89
- {@const insetRight = (marker.insetRight ?? 0) * bandwidth}
90
- {@const insetTop = (marker.insetTop ?? 0) * bandwidth}
91
- {@const insetBottom = (marker.insetBottom ?? 0) * bandwidth}
89
+ {@const insetLeft = (marker.styles?.insetLeft ?? 0) * bandwidth}
90
+ {@const insetRight = (marker.styles?.insetRight ?? 0) * bandwidth}
91
+ {@const insetTop = (marker.styles?.insetTop ?? 0) * bandwidth}
92
+ {@const insetBottom = (marker.styles?.insetBottom ?? 0) * bandwidth}
92
93
  {#if marker.role === 'hitArea'}
93
94
  <!-- The band runs the plot's whole value axis, so the pointer needn't be over the bar. -->
94
95
  {@const [rangeMin, rangeMax] = valueExtentFor(marker.isHorizontal)}
95
96
  {#if marker.isHorizontal}
96
- <!-- svelte-ignore a11y_no_static_element_interactions -->
97
97
  <rect
98
98
  class="dct-hit-area"
99
99
  x={rangeMin + insetLeft}
@@ -101,11 +101,9 @@
101
101
  width={Math.max(0, rangeMax - rangeMin - insetLeft - insetRight)}
102
102
  height={Math.max(0, bandwidth - insetTop - insetBottom)}
103
103
  fill="transparent"
104
- onpointerenter={enter}
105
- onpointerleave={leave}
104
+ {...events.forRow(data[0], 0)}
106
105
  />
107
106
  {:else}
108
- <!-- svelte-ignore a11y_no_static_element_interactions -->
109
107
  <rect
110
108
  class="dct-hit-area"
111
109
  x={bandStart + insetLeft}
@@ -113,8 +111,7 @@
113
111
  width={Math.max(0, bandwidth - insetLeft - insetRight)}
114
112
  height={Math.max(0, rangeMax - rangeMin - insetTop - insetBottom)}
115
113
  fill="transparent"
116
- onpointerenter={enter}
117
- onpointerleave={leave}
114
+ {...events.forRow(data[0], 0)}
118
115
  />
119
116
  {/if}
120
117
  {:else}
@@ -128,13 +125,13 @@
128
125
  y={center - thickness / 2}
129
126
  width={Math.max(0, valueMax - valueMin)}
130
127
  height={thickness}
131
- fill={marker.styles?.fill}
132
- fill-opacity={marker.styles?.fillOpacity}
133
- stroke={marker.styles?.stroke}
134
- stroke-width={marker.styles?.strokeWidth}
135
- stroke-opacity={marker.styles?.strokeOpacity}
136
- stroke-dasharray={marker.styles?.strokeDasharray}
137
- rx={marker.styles?.borderRadius}
128
+ fill={flatStyles.fill}
129
+ fill-opacity={flatStyles.fillOpacity}
130
+ stroke={flatStyles.stroke}
131
+ stroke-width={flatStyles.strokeWidth}
132
+ stroke-opacity={flatStyles.strokeOpacity}
133
+ stroke-dasharray={flatStyles.strokeDasharray}
134
+ rx={flatStyles.borderRadius}
138
135
  pointer-events="none"
139
136
  />
140
137
  {:else}
@@ -146,13 +143,13 @@
146
143
  y={valueMin}
147
144
  width={thickness}
148
145
  height={Math.max(0, valueMax - valueMin)}
149
- fill={marker.styles?.fill}
150
- fill-opacity={marker.styles?.fillOpacity}
151
- stroke={marker.styles?.stroke}
152
- stroke-width={marker.styles?.strokeWidth}
153
- stroke-opacity={marker.styles?.strokeOpacity}
154
- stroke-dasharray={marker.styles?.strokeDasharray}
155
- ry={marker.styles?.borderRadius}
146
+ fill={flatStyles.fill}
147
+ fill-opacity={flatStyles.fillOpacity}
148
+ stroke={flatStyles.stroke}
149
+ stroke-width={flatStyles.strokeWidth}
150
+ stroke-opacity={flatStyles.strokeOpacity}
151
+ stroke-dasharray={flatStyles.strokeDasharray}
152
+ ry={flatStyles.borderRadius}
156
153
  pointer-events="none"
157
154
  />
158
155
  {/if}
@@ -1,4 +1,5 @@
1
1
  <script lang="ts">
2
+ import { MarkerEvents } from '../utils/MarkerEvents';
2
3
  import { SvelteMap } from 'svelte/reactivity';
3
4
  import { BollingerX, BollingerY } from 'svelteplot';
4
5
  import { usePlot } from '../../layout/plot/usePlot';
@@ -18,6 +19,7 @@
18
19
  * the plot gives that group unless `styles` sets one.
19
20
  */
20
21
  let { marker }: { marker: BollingerMarkerConfig } = $props();
22
+ const events = MarkerEvents.use(() => marker);
21
23
 
22
24
  const plot = usePlot();
23
25
  const isHorizontal = $derived(plot.axes?.isHorizontal ?? false);
@@ -47,6 +49,7 @@
47
49
  {#each groups as group (group.key)}
48
50
  {@const color = marker.grouping ? groupFill?.(group.rows[0]) : undefined}
49
51
  <BollingerMark
52
+ {...events.props}
50
53
  data={group.rows}
51
54
  x={marker.x}
52
55
  y={marker.y}
@@ -1,4 +1,5 @@
1
1
  <script lang="ts">
2
+ import { MarkerEvents } from '../utils/MarkerEvents';
2
3
  import { BoxX, BoxY } from 'svelteplot';
3
4
  import { usePlot } from '../../layout/plot/usePlot';
4
5
  import { CssColor } from '../../utils/color';
@@ -15,6 +16,7 @@
15
16
  * from `bar`.
16
17
  */
17
18
  let { marker }: { marker: BoxMarkerConfig } = $props();
19
+ const events = MarkerEvents.use(() => marker);
18
20
 
19
21
  const plot = usePlot();
20
22
  const isHorizontal = $derived(plot.axes?.isHorizontal ?? false);
@@ -171,4 +173,4 @@
171
173
  });
172
174
  </script>
173
175
 
174
- <BoxMark {data} x={marker.x} y={marker.y} sort={marker.sort} {...passthrough} />
176
+ <BoxMark {...events.props} {data} x={marker.x} y={marker.y} sort={marker.sort} {...passthrough} />
@@ -1,4 +1,6 @@
1
1
  <script lang="ts">
2
+ import { RowStyle } from '../utils/RowStyle';
3
+ import { MarkerEvents } from '../utils/MarkerEvents';
2
4
  import { Cell } from 'svelteplot';
3
5
  import { MarkerChannels } from '../utils/MarkerChannels';
4
6
  import { getConfiguration } from '../../configuration/config.svelte';
@@ -6,6 +8,7 @@
6
8
 
7
9
  /** Renders one `'cell'` marker as a `<Cell>` call. */
8
10
  let { marker }: { marker: CellMarkerConfig } = $props();
11
+ const events = MarkerEvents.use(() => marker);
9
12
 
10
13
  const cfg = $derived(getConfiguration());
11
14
  const data = $derived(marker.data ?? []);
@@ -13,15 +16,16 @@
13
16
  </script>
14
17
 
15
18
  <Cell
19
+ {...events.props}
16
20
  {data}
17
21
  x={marker.x}
18
22
  y={marker.y}
19
- fill={channelFill ?? marker.fill}
20
- fillOpacity={marker.styles?.fillOpacity ?? 1}
21
- stroke={marker.styles?.stroke}
22
- strokeWidth={marker.styles?.strokeWidth ?? 0}
23
- strokeOpacity={marker.styles?.strokeOpacity}
23
+ fill={channelFill ?? RowStyle.of(marker.styles?.fill, data)}
24
+ fillOpacity={RowStyle.opacity(marker.styles?.fillOpacity) ?? 1}
25
+ stroke={RowStyle.of(marker.styles?.stroke, data)}
26
+ strokeWidth={RowStyle.field(marker.styles?.strokeWidth) ?? 0}
27
+ strokeOpacity={RowStyle.opacity(marker.styles?.strokeOpacity)}
24
28
  strokeDasharray={marker.styles?.strokeDasharray}
25
- borderRadius={marker.styles?.borderRadius}
26
- inset={marker.inset ?? 0.5}
29
+ borderRadius={RowStyle.field(marker.styles?.borderRadius)}
30
+ inset={RowStyle.field(marker.styles?.inset) ?? 0.5}
27
31
  />
@@ -1,4 +1,5 @@
1
1
  <script lang="ts">
2
+ import { MarkerEvents } from '../utils/MarkerEvents';
2
3
  import { Contour } from 'svelteplot';
3
4
  import type { GeoContourMarkerConfig } from '../../types/markers/geo';
4
5
 
@@ -8,10 +9,12 @@
8
9
  * - this component only draws the contour itself.
9
10
  */
10
11
  let { marker }: { marker: GeoContourMarkerConfig } = $props();
12
+ const events = MarkerEvents.use(() => marker);
11
13
  </script>
12
14
 
13
15
  <!-- No `?? 'none'` fallback: an unset `fill` should leave svelteplot's own default (`fill="none"`) in place, not run the literal string `'none'` through the color scale. -->
14
16
  <Contour
17
+ {...events.props}
15
18
  class="contour"
16
19
  data={marker.data}
17
20
  x={marker.x}
@@ -1,4 +1,6 @@
1
1
  <script lang="ts">
2
+ import { RowStyle } from '../utils/RowStyle';
3
+ import { MarkerEvents } from '../utils/MarkerEvents';
2
4
  import { DelaunayLink } from 'svelteplot';
3
5
  import { MarkerChannels } from '../utils/MarkerChannels';
4
6
  import { getConfiguration } from '../../configuration/config.svelte';
@@ -6,6 +8,7 @@
6
8
 
7
9
  /** Renders one `'delaunayLink'` marker as `<DelaunayLink>` - one line per Delaunay triangulation edge. */
8
10
  let { marker }: { marker: DelaunayLinkMarkerConfig } = $props();
11
+ const events = MarkerEvents.use(() => marker);
9
12
 
10
13
  const cfg = $derived(getConfiguration());
11
14
  const data = $derived(marker.data ?? []);
@@ -13,14 +16,15 @@
13
16
  </script>
14
17
 
15
18
  <DelaunayLink
19
+ {...events.props}
16
20
  {data}
17
21
  x={marker.x}
18
22
  y={marker.y}
19
23
  z={marker.grouping}
20
- fill={marker.styles?.fill}
21
- fillOpacity={marker.styles?.fillOpacity}
22
- stroke={marker.styles?.stroke ?? channelColor}
23
- strokeWidth={marker.styles?.strokeWidth}
24
- strokeOpacity={marker.styles?.strokeOpacity}
24
+ fill={RowStyle.of(marker.styles?.fill, data)}
25
+ fillOpacity={RowStyle.opacity(marker.styles?.fillOpacity)}
26
+ stroke={RowStyle.of(marker.styles?.stroke, data) ?? channelColor}
27
+ strokeWidth={RowStyle.field(marker.styles?.strokeWidth)}
28
+ strokeOpacity={RowStyle.opacity(marker.styles?.strokeOpacity)}
25
29
  strokeDasharray={marker.styles?.strokeDasharray}
26
30
  />
@@ -1,4 +1,5 @@
1
1
  <script lang="ts">
2
+ import { MarkerEvents } from '../utils/MarkerEvents';
2
3
  import { DelaunayMesh } from 'svelteplot';
3
4
  import { MarkerChannels } from '../utils/MarkerChannels';
4
5
  import { getConfiguration } from '../../configuration/config.svelte';
@@ -6,6 +7,7 @@
6
7
 
7
8
  /** Renders one `'delaunayMesh'` marker as `<DelaunayMesh>` - the Delaunay triangulation as a single mesh. */
8
9
  let { marker }: { marker: DelaunayMeshMarkerConfig } = $props();
10
+ const events = MarkerEvents.use(() => marker);
9
11
 
10
12
  const cfg = $derived(getConfiguration());
11
13
  const data = $derived(marker.data ?? []);
@@ -13,6 +15,7 @@
13
15
  </script>
14
16
 
15
17
  <DelaunayMesh
18
+ {...events.props}
16
19
  {data}
17
20
  x={marker.x}
18
21
  y={marker.y}
@@ -1,4 +1,5 @@
1
1
  <script lang="ts">
2
+ import { MarkerEvents } from '../utils/MarkerEvents';
2
3
  import { Density } from 'svelteplot';
3
4
  import { SvelteMap } from 'svelte/reactivity';
4
5
  import { MarkerChannels } from '../utils/MarkerChannels';
@@ -17,6 +18,7 @@
17
18
  * class-based scoping every other marker kind gets from svelteplot itself.
18
19
  */
19
20
  let { marker }: { marker: DensityMarkerConfig } = $props();
21
+ const events = MarkerEvents.use(() => marker);
20
22
 
21
23
  const data = $derived(marker.data ?? []);
22
24
  const cfg = $derived(getConfiguration());
@@ -47,6 +49,7 @@
47
49
 
48
50
  {#each groups as group, i (i)}
49
51
  <Density
52
+ {...events.props}
50
53
  data={group.rows}
51
54
  class="density"
52
55
  x={marker.x}
@@ -1,4 +1,5 @@
1
1
  <script lang="ts">
2
+ import { MarkerEvents } from '../utils/MarkerEvents';
2
3
  import { DifferenceY } from 'svelteplot';
3
4
  import type { DifferenceMarkerConfig } from '../../types/markers/common';
4
5
 
@@ -9,11 +10,13 @@
9
10
  * would erase them - each default is restated here instead.
10
11
  */
11
12
  let { marker }: { marker: DifferenceMarkerConfig } = $props();
13
+ const events = MarkerEvents.use(() => marker);
12
14
 
13
15
  const data = $derived(marker.data ?? []);
14
16
  </script>
15
17
 
16
18
  <DifferenceY
19
+ {...events.props}
17
20
  {data}
18
21
  x={marker.x}
19
22
  x1={marker.x1}