@fundar/data-chart-telling 0.0.1

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 (106) hide show
  1. package/README.md +250 -0
  2. package/dist/charts/Chart.svelte +125 -0
  3. package/dist/charts/Chart.svelte.d.ts +42 -0
  4. package/dist/charts/bar/Chart.svelte +104 -0
  5. package/dist/charts/bar/Chart.svelte.d.ts +27 -0
  6. package/dist/charts/container/ChartContainer.svelte +127 -0
  7. package/dist/charts/container/ChartContainer.svelte.d.ts +14 -0
  8. package/dist/charts/heatmap/Chart.svelte +107 -0
  9. package/dist/charts/heatmap/Chart.svelte.d.ts +30 -0
  10. package/dist/charts/line/Chart.svelte +106 -0
  11. package/dist/charts/line/Chart.svelte.d.ts +27 -0
  12. package/dist/charts/pyramid/Chart.svelte +127 -0
  13. package/dist/charts/pyramid/Chart.svelte.d.ts +31 -0
  14. package/dist/configuration/config.svelte.d.ts +12 -0
  15. package/dist/configuration/config.svelte.js +159 -0
  16. package/dist/configuration/themes/compact.d.ts +2 -0
  17. package/dist/configuration/themes/compact.js +9 -0
  18. package/dist/configuration/themes/dark.d.ts +2 -0
  19. package/dist/configuration/themes/dark.js +17 -0
  20. package/dist/configuration/themes/default.d.ts +3 -0
  21. package/dist/configuration/themes/default.js +2 -0
  22. package/dist/configuration/themes/editorial.d.ts +2 -0
  23. package/dist/configuration/themes/editorial.js +14 -0
  24. package/dist/configuration/themes/index.d.ts +481 -0
  25. package/dist/configuration/themes/index.js +16 -0
  26. package/dist/index.d.ts +32 -0
  27. package/dist/index.js +27 -0
  28. package/dist/layout/facet/FacetLayout.svelte +97 -0
  29. package/dist/layout/facet/FacetLayout.svelte.d.ts +35 -0
  30. package/dist/layout/legend/ContinuousSection.svelte +95 -0
  31. package/dist/layout/legend/ContinuousSection.svelte.d.ts +7 -0
  32. package/dist/layout/legend/DiscreteSection.svelte +87 -0
  33. package/dist/layout/legend/DiscreteSection.svelte.d.ts +7 -0
  34. package/dist/layout/legend/LegendLayout.svelte +47 -0
  35. package/dist/layout/legend/LegendLayout.svelte.d.ts +7 -0
  36. package/dist/layout/plot/PlotLayout.svelte +236 -0
  37. package/dist/layout/plot/PlotLayout.svelte.d.ts +70 -0
  38. package/dist/layout/timeline/Timeline.svelte +86 -0
  39. package/dist/layout/timeline/Timeline.svelte.d.ts +11 -0
  40. package/dist/layout/timeline/TimelineLayout.svelte +181 -0
  41. package/dist/layout/timeline/TimelineLayout.svelte.d.ts +38 -0
  42. package/dist/layout/tooltip/Tooltip.svelte +55 -0
  43. package/dist/layout/tooltip/Tooltip.svelte.d.ts +12 -0
  44. package/dist/layout/tooltip/controller.svelte.d.ts +52 -0
  45. package/dist/layout/tooltip/controller.svelte.js +128 -0
  46. package/dist/layout/tooltip/hover.svelte.d.ts +7 -0
  47. package/dist/layout/tooltip/hover.svelte.js +28 -0
  48. package/dist/layout/tooltip/utils.d.ts +41 -0
  49. package/dist/layout/tooltip/utils.js +78 -0
  50. package/dist/plots/bar/Plot.svelte +176 -0
  51. package/dist/plots/bar/Plot.svelte.d.ts +26 -0
  52. package/dist/plots/heatmap/Plot.svelte +175 -0
  53. package/dist/plots/heatmap/Plot.svelte.d.ts +26 -0
  54. package/dist/plots/line/Plot.svelte +232 -0
  55. package/dist/plots/line/Plot.svelte.d.ts +26 -0
  56. package/dist/plots/markers/DeltaMarker.svelte +104 -0
  57. package/dist/plots/markers/DeltaMarker.svelte.d.ts +30 -0
  58. package/dist/plots/markers/DotMarker.svelte +41 -0
  59. package/dist/plots/markers/DotMarker.svelte.d.ts +31 -0
  60. package/dist/plots/markers/HoverMarker.svelte +78 -0
  61. package/dist/plots/markers/HoverMarker.svelte.d.ts +22 -0
  62. package/dist/plots/markers/PeakMarker.svelte +80 -0
  63. package/dist/plots/markers/PeakMarker.svelte.d.ts +13 -0
  64. package/dist/plots/markers/TextMarker.svelte +55 -0
  65. package/dist/plots/markers/TextMarker.svelte.d.ts +32 -0
  66. package/dist/plots/pyramid/Plot.svelte +282 -0
  67. package/dist/plots/pyramid/Plot.svelte.d.ts +26 -0
  68. package/dist/plots/utils/accessors.d.ts +6 -0
  69. package/dist/plots/utils/accessors.js +10 -0
  70. package/dist/plots/utils/delta.d.ts +12 -0
  71. package/dist/plots/utils/delta.js +69 -0
  72. package/dist/plots/utils/segments.d.ts +33 -0
  73. package/dist/plots/utils/segments.js +153 -0
  74. package/dist/stores/device.d.ts +7 -0
  75. package/dist/stores/device.js +39 -0
  76. package/dist/types/charts/common.d.ts +64 -0
  77. package/dist/types/charts/common.js +1 -0
  78. package/dist/types/charts/interpolate.d.ts +2 -0
  79. package/dist/types/charts/interpolate.js +1 -0
  80. package/dist/types/charts/legend.d.ts +29 -0
  81. package/dist/types/charts/legend.js +1 -0
  82. package/dist/types/charts/props.d.ts +35 -0
  83. package/dist/types/charts/props.js +1 -0
  84. package/dist/types/configuration/styling.d.ts +97 -0
  85. package/dist/types/configuration/styling.js +1 -0
  86. package/dist/types/layout/tooltip.d.ts +88 -0
  87. package/dist/types/layout/tooltip.js +1 -0
  88. package/dist/types/plots/common.d.ts +100 -0
  89. package/dist/types/plots/common.js +1 -0
  90. package/dist/types/plots/delta.d.ts +19 -0
  91. package/dist/types/plots/delta.js +1 -0
  92. package/dist/types/plots/markers.d.ts +56 -0
  93. package/dist/types/plots/markers.js +1 -0
  94. package/dist/types/plots/props.d.ts +23 -0
  95. package/dist/types/plots/props.js +1 -0
  96. package/dist/types/plots/styles.d.ts +176 -0
  97. package/dist/types/plots/styles.js +1 -0
  98. package/dist/types/viewport.d.ts +4 -0
  99. package/dist/types/viewport.js +1 -0
  100. package/dist/utils/color.d.ts +5 -0
  101. package/dist/utils/color.js +42 -0
  102. package/dist/utils/grouping.d.ts +14 -0
  103. package/dist/utils/grouping.js +36 -0
  104. package/dist/utils/interpolate.d.ts +30 -0
  105. package/dist/utils/interpolate.js +71 -0
  106. package/package.json +76 -0
@@ -0,0 +1,176 @@
1
+ <script
2
+ lang="ts"
3
+ generics="TData extends Record<string, unknown>"
4
+ >
5
+ import { SvelteMap } from 'svelte/reactivity';
6
+ import { paletteColor } from '../../utils/color';
7
+ import { buildSeries } from '../../utils/grouping';
8
+ import { BarY, Text } from 'svelteplot';
9
+ import { resolveAccessor } from '../utils/accessors';
10
+ import { getConfiguration } from '../../configuration/config.svelte';
11
+ import { buildGroupedSeries, validateSegments, resolveSegmentsForSeries, matchesSegmentX } from '../utils/segments';
12
+ import PlotLayout from '../../layout/plot/PlotLayout.svelte';
13
+ import HoverMarker from '../markers/HoverMarker.svelte';
14
+ import type { Marker } from '../../types/plots/markers';
15
+ import type { PlotProps } from '../../types/plots/props';
16
+ import type { Series, AxisValue } from '../../types/plots/common';
17
+ import type { BarSegmentStyle, ValueAnchor } from '../../types/plots/styles';
18
+
19
+ /**
20
+ * A vertical bar chart. Accepts either a pre-built `series` array or flat
21
+ * `data/x/y/z` props — mirrors the line/heatmap plot contract so the
22
+ * chart-level container/timeline/facet/legend wrap it unchanged.
23
+ */
24
+ let {
25
+ width,
26
+ height,
27
+ series,
28
+ data,
29
+ x,
30
+ y,
31
+ z,
32
+ styles = {},
33
+ segments = {},
34
+ markers = [],
35
+ scales = {},
36
+ margins,
37
+ tooltip = undefined,
38
+ }: PlotProps<TData, BarSegmentStyle> = $props();
39
+
40
+ $effect(() => { validateSegments(segments); });
41
+
42
+ const cfg = $derived(getConfiguration());
43
+ const resolvedSeries = $derived(series ?? buildSeries(data!, x!, y!, z));
44
+ const groupedSeries = $derived(
45
+ buildGroupedSeries<Series<TData>, BarSegmentStyle>(resolvedSeries, segments),
46
+ );
47
+ const showVals = $derived(styles.values?.show ?? false);
48
+ const fmtVal = $derived(styles.values?.format ?? ((v: number) => `${v}`));
49
+ const valAnchor = $derived<ValueAnchor>(styles.values?.anchor ?? 'outside');
50
+
51
+ const seriesMarkers = $derived.by(() => {
52
+ const map = new SvelteMap<string, Marker[]>();
53
+ for (const marker of markers) {
54
+ if (marker.type === 'hover' && marker.series) {
55
+ if (!map.has(marker.series)) map.set(marker.series, []);
56
+ map.get(marker.series)!.push(marker);
57
+ }
58
+ }
59
+ return map;
60
+ });
61
+
62
+ const xAcc = $derived(resolveAccessor(resolvedSeries[0]?.x ?? ((d: TData) => d)));
63
+ const yAcc = $derived(resolveAccessor(resolvedSeries[0]?.y ?? ((d: TData) => d)));
64
+ const flat = $derived(resolvedSeries.flatMap((s) => s.data));
65
+ const hoverPoints = $derived(
66
+ resolvedSeries.flatMap((s) => {
67
+ const sx = resolveAccessor(s.x);
68
+ const sy = resolveAccessor(s.y);
69
+ return s.data.map((d) => ({
70
+ x: sx(d) as AxisValue,
71
+ y: Number(sy(d)),
72
+ row: d,
73
+ label: String(sy(d)),
74
+ series: s.name,
75
+ }));
76
+ }),
77
+ );
78
+ </script>
79
+
80
+ <PlotLayout
81
+ {width}
82
+ {height}
83
+ x={scales.x}
84
+ y={scales.y}
85
+ data={flat}
86
+ getX={xAcc}
87
+ getY={(d) => Number(yAcc(d))}
88
+ {hoverPoints}
89
+ label={(r) => String(yAcc(r))}
90
+ {tooltip}
91
+ {markers}
92
+ seriesData={resolvedSeries}
93
+ {scales}
94
+ {margins}
95
+ defaultStrategy="x"
96
+ >
97
+ {#snippet children({ matchedPoints, ruleX, ruleY, impliesRuleX, impliesRuleY })}
98
+ {#each groupedSeries as group, seriesIndex (group.series.name)}
99
+ {@const defaultColor = paletteColor(seriesIndex, cfg.palette)}
100
+ {@const xFn = resolveAccessor(group.series.x)}
101
+ {@const yFn = resolveAccessor(group.series.y)}
102
+ {#each group.visualSegments as seg, i (`${group.series.name}-${i}`)}
103
+ <BarY
104
+ data={seg.data}
105
+ x={xFn}
106
+ y={yFn}
107
+ fill={seg.style.fill ?? defaultColor}
108
+ fillOpacity={seg.style.fillOpacity ?? 1}
109
+ />
110
+ {/each}
111
+
112
+ {#if showVals}
113
+ {@const yTextFn = valAnchor === 'middle'
114
+ ? (d: TData) => Number(yFn(d)) / 2
115
+ : valAnchor === 'end'
116
+ ? () => 0
117
+ : (d: TData) => Number(yFn(d))}
118
+ {@const defaultDy = valAnchor === 'outside' ? -6 : valAnchor === 'start' ? 4 : valAnchor === 'end' ? -4 : 0}
119
+ {@const defaultLA = valAnchor === 'outside' || valAnchor === 'end'
120
+ ? ('bottom' as const)
121
+ : valAnchor === 'start'
122
+ ? ('top' as const)
123
+ : ('middle' as const)}
124
+ <Text
125
+ data={group.series.data}
126
+ x={xFn}
127
+ y={yTextFn}
128
+ text={(d: TData) => fmtVal(Number(yFn(d)), group.series.name)}
129
+ dx={styles.values?.dx ?? 0}
130
+ dy={styles.values?.dy ?? defaultDy}
131
+ textAnchor={styles.values?.textAnchor ?? 'middle'}
132
+ lineAnchor={styles.values?.lineAnchor ?? defaultLA}
133
+ lineHeight={styles.values?.lineHeight}
134
+ rotate={styles.values?.rotate}
135
+ class={styles.values?.class}
136
+ textClass={styles.values?.textClass}
137
+ fill={cfg.axis.color}
138
+ fontSize={12}
139
+ />
140
+ {/if}
141
+
142
+ {#each seriesMarkers.get(group.series.name) ?? [] as marker, i (`series-${group.series.name}-hover-${i}`)}
143
+ {#if marker.type === 'hover'}
144
+ {@const seriesPoints = matchedPoints.filter((p) => p.series === group.series.name)}
145
+ {#if marker.scope === 'segment'}
146
+ {@const segs = resolveSegmentsForSeries(segments, group.series.name)}
147
+ {#each segs as seg, segIdx (`${group.series.name}-hover-segment-${segIdx}`)}
148
+ {@const segPoints = seriesPoints.filter((p) => matchesSegmentX(p.x, seg))}
149
+ {#if segPoints.length > 0}
150
+ <HoverMarker
151
+ data={segPoints}
152
+ ruleX={marker.ruleX !== false && (impliesRuleX || marker.ruleX === true) ? ruleX : null}
153
+ ruleY={marker.ruleY !== false && (impliesRuleY || marker.ruleY === true) ? ruleY : null}
154
+ showLabels={marker.showLabels}
155
+ ruleStyle={marker.ruleStyle}
156
+ dotStyle={marker.dotStyle}
157
+ fontStyle={marker.fontStyle}
158
+ />
159
+ {/if}
160
+ {/each}
161
+ {:else}
162
+ <HoverMarker
163
+ data={seriesPoints}
164
+ ruleX={marker.ruleX !== false && (impliesRuleX || marker.ruleX === true) ? ruleX : null}
165
+ ruleY={marker.ruleY !== false && (impliesRuleY || marker.ruleY === true) ? ruleY : null}
166
+ showLabels={marker.showLabels}
167
+ ruleStyle={marker.ruleStyle}
168
+ dotStyle={marker.dotStyle}
169
+ fontStyle={marker.fontStyle}
170
+ />
171
+ {/if}
172
+ {/if}
173
+ {/each}
174
+ {/each}
175
+ {/snippet}
176
+ </PlotLayout>
@@ -0,0 +1,26 @@
1
+ import type { PlotProps } from '../../types/plots/props';
2
+ import type { BarSegmentStyle } from '../../types/plots/styles';
3
+ declare function $$render<TData extends Record<string, unknown>>(): {
4
+ props: PlotProps<TData, BarSegmentStyle>;
5
+ exports: {};
6
+ bindings: "";
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(): "";
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,175 @@
1
+ <script lang="ts" generics="TData extends Record<string, unknown>">
2
+ import { Cell, Text } from 'svelteplot';
3
+ import { resolveAccessor } from '../utils/accessors';
4
+ import { buildSeries } from '../../utils/grouping';
5
+ import { makeColorScale } from '../../utils/color';
6
+ import { getConfiguration } from '../../configuration/config.svelte';
7
+ import PlotLayout from '../../layout/plot/PlotLayout.svelte';
8
+ import type { PlotProps } from '../../types/plots/props';
9
+ import type { AxisValue } from '../../types/plots/common';
10
+ import type { CellSegmentStyle } from '../../types/plots/styles';
11
+
12
+ /**
13
+ * A categorical grid of value-coloured cells. Accepts either a pre-built
14
+ * `series` array (each {@link Series} may carry `z` as the colour accessor) or
15
+ * flat `data/x/y/z` props — mirrors the line/bar plot contract so the
16
+ * chart-level container/timeline/facet/legend wrap it unchanged.
17
+ *
18
+ * The colour domain is read from `scales.z.domain`; when absent it is derived
19
+ * from the visible slice of data.
20
+ */
21
+ let {
22
+ width,
23
+ height,
24
+ series,
25
+ data,
26
+ x,
27
+ y,
28
+ z,
29
+ styles = {},
30
+ segments = [],
31
+ scales = {},
32
+ margins,
33
+ markers = [],
34
+ tooltip = undefined,
35
+ }: PlotProps<TData, CellSegmentStyle> = $props();
36
+
37
+ $effect(() => {
38
+ const scoped = markers.filter(
39
+ (m) => (m.type === 'hover' || m.type === 'component') && m.series,
40
+ );
41
+ if (scoped.length > 0) {
42
+ console.warn(
43
+ 'HeatmapPlot: markers scoped to a `series` are ignored — heatmaps have no series concept.',
44
+ );
45
+ }
46
+ if (!Array.isArray(segments) && Object.keys(segments).length > 0) {
47
+ console.warn(
48
+ 'HeatmapPlot: `segments` was passed as a record (SeriesAwareSegmentsMap) but heatmaps have no named series — use the array form (Segment[]) instead.',
49
+ );
50
+ }
51
+ });
52
+
53
+ const cfg = $derived(getConfiguration());
54
+ const minColor = $derived(styles.colors?.min ?? cfg.continuous.min);
55
+ const maxColor = $derived(styles.colors?.max ?? cfg.continuous.max);
56
+ const showVals = $derived(styles.values?.show ?? true);
57
+ const fmtVal = $derived(styles.values?.format ?? ((v: number) => `${v}`));
58
+
59
+ const resolvedSeries = $derived(series ?? buildSeries(data!, x!, y!, undefined));
60
+ const flat = $derived(resolvedSeries.flatMap((s) => s.data));
61
+
62
+ const getX = $derived(resolveAccessor<TData>(resolvedSeries[0]?.x ?? x!));
63
+ const getY = $derived(resolveAccessor<TData>(resolvedSeries[0]?.y ?? y!));
64
+ const getZ = $derived(resolveAccessor<TData>(resolvedSeries[0]?.z ?? z!));
65
+
66
+ const hoverPoints = $derived(
67
+ flat.map((d) => ({
68
+ x: getX(d) as AxisValue,
69
+ y: getY(d) as AxisValue,
70
+ row: d,
71
+ label: fmtVal(Number(getZ(d)), ''),
72
+ })),
73
+ );
74
+
75
+ const xDomain = $derived([...new Set(flat.map((d) => getX(d) as AxisValue))]);
76
+ const yDomain = $derived([...new Set(flat.map((d) => getY(d) as AxisValue))]);
77
+
78
+ const valueDomain = $derived.by((): [number, number] => {
79
+ if (scales.z?.domain && scales.z.domain.length >= 2) {
80
+ return [Number(scales.z.domain[0]), Number(scales.z.domain[1])];
81
+ }
82
+ const vals = flat.map((d) => Number(getZ(d)));
83
+ return vals.length ? [Math.min(...vals, 0), Math.max(...vals)] : [0, 1];
84
+ });
85
+
86
+ const colorScale = $derived(
87
+ makeColorScale(valueDomain[0], valueDomain[1], minColor, maxColor),
88
+ );
89
+
90
+ const segList = $derived(Array.isArray(segments) ? segments : []);
91
+
92
+ const segStyleMap = $derived.by(() => {
93
+ const map = new Map<string, CellSegmentStyle>();
94
+ if (!segList.length) return map;
95
+ for (const d of flat) {
96
+ const cx = getX(d) as AxisValue;
97
+ const cy = getY(d) as AxisValue;
98
+ const key = `${String(cx)}::${String(cy)}`;
99
+ if (!map.has(key)) {
100
+ let style: CellSegmentStyle = {};
101
+ for (const seg of segList) {
102
+ const areas = seg.areas;
103
+ const matches =
104
+ !areas || areas.length === 0
105
+ ? true
106
+ : areas.some((area) => {
107
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
108
+ const xOk = !area.x || ((area.x.from === undefined || (cx as any) >= area.x.from) && (area.x.to === undefined || (cx as any) <= area.x.to));
109
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
110
+ const yOk = !area.y || ((area.y.from === undefined || (cy as any) >= area.y.from) && (area.y.to === undefined || (cy as any) <= area.y.to));
111
+ return xOk && yOk;
112
+ });
113
+ if (matches) style = { ...style, ...seg.style };
114
+ }
115
+ map.set(key, style);
116
+ }
117
+ }
118
+ return map;
119
+ });
120
+
121
+ const getCellStyle = $derived(
122
+ (d: TData) => segStyleMap.get(`${String(getX(d))}::${String(getY(d))}`) ?? {},
123
+ );
124
+ </script>
125
+
126
+ <PlotLayout
127
+ {width}
128
+ {height}
129
+ x={{ type: 'band', labelAnchor: 'right', domain: xDomain, ...(scales.x ?? {}) }}
130
+ y={{ type: 'band', domain: yDomain, ...(scales.y ?? {}) }}
131
+ data={flat}
132
+ {getX}
133
+ {getY}
134
+ matchY={getY}
135
+ {hoverPoints}
136
+ label={(d) => fmtVal(Number(getZ(d)), '')}
137
+ {tooltip}
138
+ {markers}
139
+ seriesData={resolvedSeries}
140
+ scales={scales}
141
+ {margins}
142
+ showHoverLabels={false}
143
+ defaultStrategy="punctual"
144
+ >
145
+ <Cell
146
+ data={flat as unknown as Record<string | symbol, never>[]}
147
+ x={getX}
148
+ y={getY}
149
+ fill={(d) => getCellStyle(d as TData).fill ?? colorScale(Number(getZ(d as TData)))}
150
+ fillOpacity={(d) => getCellStyle(d as TData).fillOpacity ?? 1}
151
+ stroke={(d) => getCellStyle(d as TData).stroke}
152
+ strokeWidth={(d) => getCellStyle(d as TData).strokeWidth ?? 0}
153
+ inset={0.5}
154
+ />
155
+
156
+ {#if showVals}
157
+ <Text
158
+ data={flat}
159
+ x={getX}
160
+ y={getY}
161
+ text={(d) => fmtVal(Number(getZ(d)), '')}
162
+ fill={cfg.continuous.labelColor}
163
+ fontSize={12}
164
+ fontWeight={600}
165
+ textAnchor={styles.values?.textAnchor ?? 'middle'}
166
+ lineAnchor={styles.values?.lineAnchor ?? 'middle'}
167
+ dx={styles.values?.dx ?? 0}
168
+ dy={styles.values?.dy ?? 0}
169
+ lineHeight={styles.values?.lineHeight}
170
+ rotate={styles.values?.rotate}
171
+ class={styles.values?.class}
172
+ textClass={styles.values?.textClass}
173
+ />
174
+ {/if}
175
+ </PlotLayout>
@@ -0,0 +1,26 @@
1
+ import type { PlotProps } from '../../types/plots/props';
2
+ import type { CellSegmentStyle } from '../../types/plots/styles';
3
+ declare function $$render<TData extends Record<string, unknown>>(): {
4
+ props: PlotProps<TData, CellSegmentStyle>;
5
+ exports: {};
6
+ bindings: "";
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(): "";
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,232 @@
1
+ <script
2
+ lang="ts"
3
+ generics="TData extends Record<string, unknown>"
4
+ >
5
+ import { SvelteMap } from 'svelte/reactivity';
6
+ import { Line, Dot, Text } from 'svelteplot';
7
+ import { resolveAccessor } from '../utils/accessors';
8
+ import { getConfiguration } from '../../configuration/config.svelte';
9
+ import { paletteColor } from '../../utils/color';
10
+ import { buildSeries } from '../../utils/grouping';
11
+ import PlotLayout from '../../layout/plot/PlotLayout.svelte';
12
+ import HoverMarker from '../markers/HoverMarker.svelte';
13
+ import { buildGroupedSeries, validateSegments, resolveSegmentsForSeries, matchesSegmentX, getDataForSegments } from '../utils/segments';
14
+ import type { PlotProps } from '../../types/plots/props';
15
+ import type { Series, AxisValue } from '../../types/plots/common';
16
+ import type { LineSegmentStyle, ValueAnchor } from '../../types/plots/styles';
17
+ import type { Marker } from '../../types/plots/markers';
18
+
19
+ let {
20
+ width,
21
+ height,
22
+ series,
23
+ data,
24
+ x,
25
+ y,
26
+ z,
27
+ styles = {},
28
+ segments = {},
29
+ markers = [],
30
+ scales = {},
31
+ margins,
32
+ tooltip = undefined,
33
+ }: PlotProps<TData, LineSegmentStyle> = $props();
34
+
35
+ const resolvedSeries = $derived(series ?? buildSeries(data!, x!, y!, z));
36
+
37
+ $effect(() => { validateSegments(segments); });
38
+
39
+ const groupedSeries = $derived(
40
+ buildGroupedSeries<Series<TData>, LineSegmentStyle>(resolvedSeries, segments),
41
+ );
42
+
43
+ const seriesMarkers = $derived.by(() => {
44
+ const map = new SvelteMap<string, Marker[]>();
45
+ for (const marker of markers) {
46
+ if ((marker.type === 'component' || marker.type === 'hover') && marker.series) {
47
+ if (!map.has(marker.series)) map.set(marker.series, []);
48
+ map.get(marker.series)!.push(marker);
49
+ }
50
+ }
51
+ return map;
52
+ });
53
+
54
+ const cfg = $derived(getConfiguration());
55
+ const showVals = $derived(styles.values?.show ?? false);
56
+ const fmtVal = $derived(
57
+ styles.values?.format ?? ((v: number, name: string) => `${name} - ${v}`),
58
+ );
59
+ const valAnchor = $derived<ValueAnchor>(styles.values?.anchor ?? 'start');
60
+
61
+ const xAcc = $derived(resolveAccessor(resolvedSeries[0]?.x ?? ((d: TData) => d)));
62
+ const yAcc = $derived(resolveAccessor(resolvedSeries[0]?.y ?? ((d: TData) => d)));
63
+ const flat = $derived(resolvedSeries.flatMap((s) => s.data));
64
+ const hoverPoints = $derived(
65
+ resolvedSeries.flatMap((s) => {
66
+ const sx = resolveAccessor(s.x);
67
+ const sy = resolveAccessor(s.y);
68
+ return s.data.map((d) => ({
69
+ x: sx(d) as AxisValue,
70
+ y: Number(sy(d)),
71
+ row: d,
72
+ label: String(sy(d)),
73
+ series: s.name,
74
+ }));
75
+ }),
76
+ );
77
+
78
+ // Last data point per series, with its palette index for colour.
79
+ const lastPoints = $derived(
80
+ resolvedSeries
81
+ .map((s, idx) => ({ series: s, lastRow: s.data[s.data.length - 1], seriesIndex: idx }))
82
+ .filter(({ lastRow }) => lastRow !== undefined),
83
+ );
84
+ </script>
85
+
86
+ <PlotLayout
87
+ {width}
88
+ {height}
89
+ x={scales.x}
90
+ y={scales.y}
91
+ data={flat}
92
+ getX={xAcc}
93
+ getY={(d) => Number(yAcc(d))}
94
+ {hoverPoints}
95
+ label={(r) => String(yAcc(r))}
96
+ {tooltip}
97
+ {markers}
98
+ seriesData={resolvedSeries}
99
+ {scales}
100
+ {margins}
101
+ defaultStrategy="x"
102
+ >
103
+ {#snippet children({ matchedPoints, ruleX, ruleY, impliesRuleX, impliesRuleY })}
104
+ {#each groupedSeries as group, seriesIndex (group.series.name)}
105
+ {@const defaultColor = paletteColor(seriesIndex, cfg.palette)}
106
+ {#each group.visualSegments as seg, i (`${group.series.name}-${i}`)}
107
+ {@const strokeStyle = seg.style.stroke}
108
+ {@const lineData = seg.data as unknown as Record<string | symbol, never>[]}
109
+ <Line
110
+ data={lineData}
111
+ x={resolveAccessor(group.series.x)}
112
+ y={resolveAccessor(group.series.y)}
113
+ stroke={strokeStyle?.stroke ?? defaultColor}
114
+ strokeWidth={strokeStyle?.strokeWidth ?? cfg.line.strokeWidth}
115
+ strokeOpacity={strokeStyle?.strokeOpacity ?? 1}
116
+ strokeDasharray={strokeStyle?.strokeDasharray}
117
+ strokeLinejoin={strokeStyle?.strokeLinejoin}
118
+ strokeLinecap={strokeStyle?.strokeLinecap}
119
+ />
120
+ {#if seg.style.dots}
121
+ {@const dotStyle = seg.style.dots}
122
+ <Dot
123
+ data={seg.data}
124
+ x={resolveAccessor(group.series.x)}
125
+ y={resolveAccessor(group.series.y)}
126
+ r={dotStyle.dotRadius ?? cfg.line.dotRadius}
127
+ fill={dotStyle.dotFill ?? strokeStyle?.stroke ?? defaultColor}
128
+ symbol={dotStyle.dotSymbol ?? 'circle'}
129
+ stroke={dotStyle.dotStroke ?? strokeStyle?.stroke ?? defaultColor}
130
+ strokeWidth={dotStyle.dotStrokeWidth ?? 1}
131
+ fillOpacity={dotStyle.dotFillOpacity ?? strokeStyle?.strokeOpacity ?? 1}
132
+ strokeOpacity={dotStyle.dotStrokeOpacity ?? strokeStyle?.strokeOpacity ?? 1}
133
+ strokeDasharray={dotStyle.dotStrokeDasharray}
134
+ />
135
+ {/if}
136
+ {/each}
137
+
138
+ {#each seriesMarkers.get(group.series.name) ?? [] as marker, i (`series-${group.series.name}-${marker.type}-${i}`)}
139
+ {#if marker.type === 'component'}
140
+ {#if marker.scope === 'segment'}
141
+ {@const segs = resolveSegmentsForSeries(segments, group.series.name)}
142
+ {@const xAccessor = resolveAccessor(group.series.x)}
143
+ {@const segmentDataArrays = getDataForSegments(group.series.data, xAccessor, segs)}
144
+ {#each segmentDataArrays as segmentData, segIdx (`${group.series.name}-marker-segment-${segIdx}`)}
145
+ {#if segmentData.length > 0}
146
+ {@const segmentColor = segs[segIdx]?.style?.stroke?.stroke ?? defaultColor}
147
+ <marker.component
148
+ {...marker.props}
149
+ series={group.series.name}
150
+ data={segmentData}
151
+ seriesColor={segmentColor}
152
+ />
153
+ {/if}
154
+ {/each}
155
+ {:else}
156
+ {@const segs = resolveSegmentsForSeries(segments, group.series.name)}
157
+ {@const seriesColor = segs[0]?.style?.stroke?.stroke ?? defaultColor}
158
+ <marker.component
159
+ {...marker.props}
160
+ series={group.series.name}
161
+ data={group.series.data}
162
+ seriesColor={seriesColor}
163
+ />
164
+ {/if}
165
+ {:else if marker.type === 'hover'}
166
+ {@const seriesPoints = matchedPoints.filter((p) => p.series === group.series.name)}
167
+ {#if marker.scope === 'segment'}
168
+ {@const segs = resolveSegmentsForSeries(segments, group.series.name)}
169
+ {#each segs as seg, segIdx (`${group.series.name}-hover-segment-${segIdx}`)}
170
+ {@const segPoints = seriesPoints.filter((p) => matchesSegmentX(p.x, seg))}
171
+ {#if segPoints.length > 0}
172
+ <HoverMarker
173
+ data={segPoints}
174
+ ruleX={marker.ruleX !== false && (impliesRuleX || marker.ruleX === true) ? ruleX : null}
175
+ ruleY={marker.ruleY !== false && (impliesRuleY || marker.ruleY === true) ? ruleY : null}
176
+ showLabels={marker.showLabels}
177
+ ruleStyle={marker.ruleStyle}
178
+ dotStyle={marker.dotStyle}
179
+ fontStyle={marker.fontStyle}
180
+ />
181
+ {/if}
182
+ {/each}
183
+ {:else}
184
+ <HoverMarker
185
+ data={seriesPoints}
186
+ ruleX={marker.ruleX !== false && (impliesRuleX || marker.ruleX === true) ? ruleX : null}
187
+ ruleY={marker.ruleY !== false && (impliesRuleY || marker.ruleY === true) ? ruleY : null}
188
+ showLabels={marker.showLabels}
189
+ ruleStyle={marker.ruleStyle}
190
+ dotStyle={marker.dotStyle}
191
+ fontStyle={marker.fontStyle}
192
+ />
193
+ {/if}
194
+ {/if}
195
+ {/each}
196
+ {/each}
197
+
198
+ {#if showVals}
199
+ {@const defaultDx = valAnchor === 'start' ? 6 : valAnchor === 'end' ? -6 : 0}
200
+ {@const defaultDy = valAnchor === 'outside' ? -8 : 0}
201
+ {@const defaultTA = valAnchor === 'start'
202
+ ? ('start' as const)
203
+ : valAnchor === 'end'
204
+ ? ('end' as const)
205
+ : ('middle' as const)}
206
+ {@const defaultLA = valAnchor === 'outside'
207
+ ? ('bottom' as const)
208
+ : ('middle' as const)}
209
+ {#each lastPoints as { series: s, lastRow, seriesIndex } (s.name)}
210
+ {@const xFn = resolveAccessor(s.x)}
211
+ {@const yFn = resolveAccessor(s.y)}
212
+ {@const seriesColor = paletteColor(seriesIndex, cfg.palette)}
213
+ <Text
214
+ data={[lastRow]}
215
+ x={xFn}
216
+ y={yFn}
217
+ text={() => fmtVal(Number(yFn(lastRow)), s.name)}
218
+ dx={styles.values?.dx ?? defaultDx}
219
+ dy={styles.values?.dy ?? defaultDy}
220
+ textAnchor={styles.values?.textAnchor ?? defaultTA}
221
+ lineAnchor={styles.values?.lineAnchor ?? defaultLA}
222
+ lineHeight={styles.values?.lineHeight}
223
+ rotate={styles.values?.rotate}
224
+ class={styles.values?.class}
225
+ textClass={styles.values?.textClass}
226
+ fill={seriesColor}
227
+ fontSize={12}
228
+ />
229
+ {/each}
230
+ {/if}
231
+ {/snippet}
232
+ </PlotLayout>
@@ -0,0 +1,26 @@
1
+ import type { PlotProps } from '../../types/plots/props';
2
+ import type { LineSegmentStyle } from '../../types/plots/styles';
3
+ declare function $$render<TData extends Record<string, unknown>>(): {
4
+ props: PlotProps<TData, LineSegmentStyle>;
5
+ exports: {};
6
+ bindings: "";
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(): "";
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;