@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.
- package/README.md +250 -0
- package/dist/charts/Chart.svelte +125 -0
- package/dist/charts/Chart.svelte.d.ts +42 -0
- package/dist/charts/bar/Chart.svelte +104 -0
- package/dist/charts/bar/Chart.svelte.d.ts +27 -0
- package/dist/charts/container/ChartContainer.svelte +127 -0
- package/dist/charts/container/ChartContainer.svelte.d.ts +14 -0
- package/dist/charts/heatmap/Chart.svelte +107 -0
- package/dist/charts/heatmap/Chart.svelte.d.ts +30 -0
- package/dist/charts/line/Chart.svelte +106 -0
- package/dist/charts/line/Chart.svelte.d.ts +27 -0
- package/dist/charts/pyramid/Chart.svelte +127 -0
- package/dist/charts/pyramid/Chart.svelte.d.ts +31 -0
- package/dist/configuration/config.svelte.d.ts +12 -0
- package/dist/configuration/config.svelte.js +159 -0
- package/dist/configuration/themes/compact.d.ts +2 -0
- package/dist/configuration/themes/compact.js +9 -0
- package/dist/configuration/themes/dark.d.ts +2 -0
- package/dist/configuration/themes/dark.js +17 -0
- package/dist/configuration/themes/default.d.ts +3 -0
- package/dist/configuration/themes/default.js +2 -0
- package/dist/configuration/themes/editorial.d.ts +2 -0
- package/dist/configuration/themes/editorial.js +14 -0
- package/dist/configuration/themes/index.d.ts +481 -0
- package/dist/configuration/themes/index.js +16 -0
- package/dist/index.d.ts +32 -0
- package/dist/index.js +27 -0
- package/dist/layout/facet/FacetLayout.svelte +97 -0
- package/dist/layout/facet/FacetLayout.svelte.d.ts +35 -0
- package/dist/layout/legend/ContinuousSection.svelte +95 -0
- package/dist/layout/legend/ContinuousSection.svelte.d.ts +7 -0
- package/dist/layout/legend/DiscreteSection.svelte +87 -0
- package/dist/layout/legend/DiscreteSection.svelte.d.ts +7 -0
- package/dist/layout/legend/LegendLayout.svelte +47 -0
- package/dist/layout/legend/LegendLayout.svelte.d.ts +7 -0
- package/dist/layout/plot/PlotLayout.svelte +236 -0
- package/dist/layout/plot/PlotLayout.svelte.d.ts +70 -0
- package/dist/layout/timeline/Timeline.svelte +86 -0
- package/dist/layout/timeline/Timeline.svelte.d.ts +11 -0
- package/dist/layout/timeline/TimelineLayout.svelte +181 -0
- package/dist/layout/timeline/TimelineLayout.svelte.d.ts +38 -0
- package/dist/layout/tooltip/Tooltip.svelte +55 -0
- package/dist/layout/tooltip/Tooltip.svelte.d.ts +12 -0
- package/dist/layout/tooltip/controller.svelte.d.ts +52 -0
- package/dist/layout/tooltip/controller.svelte.js +128 -0
- package/dist/layout/tooltip/hover.svelte.d.ts +7 -0
- package/dist/layout/tooltip/hover.svelte.js +28 -0
- package/dist/layout/tooltip/utils.d.ts +41 -0
- package/dist/layout/tooltip/utils.js +78 -0
- package/dist/plots/bar/Plot.svelte +176 -0
- package/dist/plots/bar/Plot.svelte.d.ts +26 -0
- package/dist/plots/heatmap/Plot.svelte +175 -0
- package/dist/plots/heatmap/Plot.svelte.d.ts +26 -0
- package/dist/plots/line/Plot.svelte +232 -0
- package/dist/plots/line/Plot.svelte.d.ts +26 -0
- package/dist/plots/markers/DeltaMarker.svelte +104 -0
- package/dist/plots/markers/DeltaMarker.svelte.d.ts +30 -0
- package/dist/plots/markers/DotMarker.svelte +41 -0
- package/dist/plots/markers/DotMarker.svelte.d.ts +31 -0
- package/dist/plots/markers/HoverMarker.svelte +78 -0
- package/dist/plots/markers/HoverMarker.svelte.d.ts +22 -0
- package/dist/plots/markers/PeakMarker.svelte +80 -0
- package/dist/plots/markers/PeakMarker.svelte.d.ts +13 -0
- package/dist/plots/markers/TextMarker.svelte +55 -0
- package/dist/plots/markers/TextMarker.svelte.d.ts +32 -0
- package/dist/plots/pyramid/Plot.svelte +282 -0
- package/dist/plots/pyramid/Plot.svelte.d.ts +26 -0
- package/dist/plots/utils/accessors.d.ts +6 -0
- package/dist/plots/utils/accessors.js +10 -0
- package/dist/plots/utils/delta.d.ts +12 -0
- package/dist/plots/utils/delta.js +69 -0
- package/dist/plots/utils/segments.d.ts +33 -0
- package/dist/plots/utils/segments.js +153 -0
- package/dist/stores/device.d.ts +7 -0
- package/dist/stores/device.js +39 -0
- package/dist/types/charts/common.d.ts +64 -0
- package/dist/types/charts/common.js +1 -0
- package/dist/types/charts/interpolate.d.ts +2 -0
- package/dist/types/charts/interpolate.js +1 -0
- package/dist/types/charts/legend.d.ts +29 -0
- package/dist/types/charts/legend.js +1 -0
- package/dist/types/charts/props.d.ts +35 -0
- package/dist/types/charts/props.js +1 -0
- package/dist/types/configuration/styling.d.ts +97 -0
- package/dist/types/configuration/styling.js +1 -0
- package/dist/types/layout/tooltip.d.ts +88 -0
- package/dist/types/layout/tooltip.js +1 -0
- package/dist/types/plots/common.d.ts +100 -0
- package/dist/types/plots/common.js +1 -0
- package/dist/types/plots/delta.d.ts +19 -0
- package/dist/types/plots/delta.js +1 -0
- package/dist/types/plots/markers.d.ts +56 -0
- package/dist/types/plots/markers.js +1 -0
- package/dist/types/plots/props.d.ts +23 -0
- package/dist/types/plots/props.js +1 -0
- package/dist/types/plots/styles.d.ts +176 -0
- package/dist/types/plots/styles.js +1 -0
- package/dist/types/viewport.d.ts +4 -0
- package/dist/types/viewport.js +1 -0
- package/dist/utils/color.d.ts +5 -0
- package/dist/utils/color.js +42 -0
- package/dist/utils/grouping.d.ts +14 -0
- package/dist/utils/grouping.js +36 -0
- package/dist/utils/interpolate.d.ts +30 -0
- package/dist/utils/interpolate.js +71 -0
- package/package.json +76 -0
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
<script lang="ts" generics="TRow extends Record<string, unknown>">
|
|
2
|
+
import Chart from '../Chart.svelte';
|
|
3
|
+
import HeatmapPlot from '../../plots/heatmap/Plot.svelte';
|
|
4
|
+
import {
|
|
5
|
+
resolveTooltip,
|
|
6
|
+
resolveHoverStrategy,
|
|
7
|
+
resolveHoverSync,
|
|
8
|
+
hasHoverMarker
|
|
9
|
+
} from '../../layout/tooltip/utils';
|
|
10
|
+
import { resolveAccessor } from '../../plots/utils/accessors';
|
|
11
|
+
import type { Accessor, ScalesConfig } from '../../types/plots/common';
|
|
12
|
+
import type { CellSegmentStyle } from '../../types/plots/styles';
|
|
13
|
+
import type { ChartProps } from '../../types/charts/props';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* High-level heatmap. Proves the chart chrome (container/legend/timeline/facet)
|
|
17
|
+
* is plot-agnostic: it reuses {@link Chart} exactly like {@link LineChart}, only
|
|
18
|
+
* swapping the leaf for {@link HeatmapPlot}. The colour domain is computed once
|
|
19
|
+
* over the full dataset so cells stay comparable across timeline steps/facets —
|
|
20
|
+
* it is passed via `scales.z.domain` and can be overridden by the caller.
|
|
21
|
+
*/
|
|
22
|
+
let {
|
|
23
|
+
width,
|
|
24
|
+
height,
|
|
25
|
+
title,
|
|
26
|
+
subtitle,
|
|
27
|
+
caption,
|
|
28
|
+
legend,
|
|
29
|
+
data,
|
|
30
|
+
x,
|
|
31
|
+
y,
|
|
32
|
+
z,
|
|
33
|
+
styles = {},
|
|
34
|
+
segments = [],
|
|
35
|
+
scales = {},
|
|
36
|
+
margins,
|
|
37
|
+
markers = [],
|
|
38
|
+
facet,
|
|
39
|
+
timeline,
|
|
40
|
+
tooltip,
|
|
41
|
+
}: ChartProps<TRow, CellSegmentStyle> & { z: Accessor<TRow, number> } = $props();
|
|
42
|
+
|
|
43
|
+
const getX = $derived(resolveAccessor<TRow>(x));
|
|
44
|
+
const getY = $derived(resolveAccessor<TRow>(y));
|
|
45
|
+
const getZ = $derived(resolveAccessor<TRow>(z));
|
|
46
|
+
const fmtVal = $derived(styles.values?.format ?? ((v: number) => `${v}`));
|
|
47
|
+
|
|
48
|
+
// Compute domain over the full dataset so colour is comparable across facets/timeline steps.
|
|
49
|
+
// Caller-provided `scales.z.domain` always takes precedence.
|
|
50
|
+
const fullDomain = $derived.by((): [number, number] => {
|
|
51
|
+
const vals = data.map((d) => Number(getZ(d)));
|
|
52
|
+
return vals.length ? [Math.min(...vals, 0), Math.max(...vals)] : [0, 1];
|
|
53
|
+
});
|
|
54
|
+
const mergedScales = $derived<ScalesConfig>({
|
|
55
|
+
...scales,
|
|
56
|
+
z: { domain: fullDomain, ...(scales.z ?? {}) },
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
const tip = $derived(resolveTooltip(tooltip, 'punctual'));
|
|
60
|
+
const hoverEnabled = $derived(tip.enabled || hasHoverMarker(markers));
|
|
61
|
+
const resolvedStrategy = $derived(resolveHoverStrategy(tip.enabled ? tip.strategy : undefined, markers, 'punctual'));
|
|
62
|
+
const resolvedSync = $derived(resolveHoverSync(tip.enabled ? tip.sync : undefined, markers));
|
|
63
|
+
const defaultFormat = $derived((row: TRow) => {
|
|
64
|
+
const xl = scales.x?.label || 'x';
|
|
65
|
+
const yl = scales.y?.label || 'y';
|
|
66
|
+
return `${xl} ${getX(row)} · ${yl} ${getY(row)}\n${fmtVal(Number(getZ(row)), '')}`;
|
|
67
|
+
});
|
|
68
|
+
</script>
|
|
69
|
+
|
|
70
|
+
<Chart
|
|
71
|
+
{width}
|
|
72
|
+
{height}
|
|
73
|
+
{title}
|
|
74
|
+
{subtitle}
|
|
75
|
+
{caption}
|
|
76
|
+
{legend}
|
|
77
|
+
{data}
|
|
78
|
+
{facet}
|
|
79
|
+
{timeline}
|
|
80
|
+
hover={hoverEnabled ? { strategy: resolvedStrategy, sync: resolvedSync } : undefined}
|
|
81
|
+
>
|
|
82
|
+
{#snippet plot({ data: rows, width: w, height: h, facetValue })}
|
|
83
|
+
<HeatmapPlot
|
|
84
|
+
width={w}
|
|
85
|
+
height={h}
|
|
86
|
+
data={rows}
|
|
87
|
+
{x}
|
|
88
|
+
{y}
|
|
89
|
+
{z}
|
|
90
|
+
{styles}
|
|
91
|
+
{segments}
|
|
92
|
+
scales={mergedScales}
|
|
93
|
+
{margins}
|
|
94
|
+
{markers}
|
|
95
|
+
tooltip={tip.enabled
|
|
96
|
+
? {
|
|
97
|
+
format: tip.format ?? defaultFormat,
|
|
98
|
+
facetId: facetValue ?? '',
|
|
99
|
+
strategy: tip.strategy,
|
|
100
|
+
sync: tip.sync,
|
|
101
|
+
series: tip.series,
|
|
102
|
+
content: tip.content,
|
|
103
|
+
}
|
|
104
|
+
: undefined}
|
|
105
|
+
/>
|
|
106
|
+
{/snippet}
|
|
107
|
+
</Chart>
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import Chart from '../Chart.svelte';
|
|
2
|
+
import type { Accessor } from '../../types/plots/common';
|
|
3
|
+
import type { CellSegmentStyle } from '../../types/plots/styles';
|
|
4
|
+
import type { ChartProps } from '../../types/charts/props';
|
|
5
|
+
declare function $$render<TRow extends Record<string, unknown>>(): {
|
|
6
|
+
props: ChartProps<TRow, CellSegmentStyle> & {
|
|
7
|
+
z: Accessor<TRow, number>;
|
|
8
|
+
};
|
|
9
|
+
exports: {};
|
|
10
|
+
bindings: "";
|
|
11
|
+
slots: {};
|
|
12
|
+
events: {};
|
|
13
|
+
};
|
|
14
|
+
declare class __sveltets_Render<TRow extends Record<string, unknown>> {
|
|
15
|
+
props(): ReturnType<typeof $$render<TRow>>['props'];
|
|
16
|
+
events(): ReturnType<typeof $$render<TRow>>['events'];
|
|
17
|
+
slots(): ReturnType<typeof $$render<TRow>>['slots'];
|
|
18
|
+
bindings(): "";
|
|
19
|
+
exports(): {};
|
|
20
|
+
}
|
|
21
|
+
interface $$IsomorphicComponent {
|
|
22
|
+
new <TRow extends Record<string, unknown>>(options: import('svelte').ComponentConstructorOptions<ReturnType<__sveltets_Render<TRow>['props']>>): import('svelte').SvelteComponent<ReturnType<__sveltets_Render<TRow>['props']>, ReturnType<__sveltets_Render<TRow>['events']>, ReturnType<__sveltets_Render<TRow>['slots']>> & {
|
|
23
|
+
$$bindings?: ReturnType<__sveltets_Render<TRow>['bindings']>;
|
|
24
|
+
} & ReturnType<__sveltets_Render<TRow>['exports']>;
|
|
25
|
+
<TRow extends Record<string, unknown>>(internal: unknown, props: ReturnType<__sveltets_Render<TRow>['props']> & {}): ReturnType<__sveltets_Render<TRow>['exports']>;
|
|
26
|
+
z_$$bindings?: ReturnType<__sveltets_Render<any>['bindings']>;
|
|
27
|
+
}
|
|
28
|
+
declare const Chart: $$IsomorphicComponent;
|
|
29
|
+
type Chart<TRow extends Record<string, unknown>> = InstanceType<typeof Chart<TRow>>;
|
|
30
|
+
export default Chart;
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
<script lang="ts" generics="TRow extends Record<string, unknown>">
|
|
2
|
+
import Chart from '../Chart.svelte';
|
|
3
|
+
import LinePlot from '../../plots/line/Plot.svelte';
|
|
4
|
+
import {
|
|
5
|
+
resolveTooltip,
|
|
6
|
+
resolveHoverStrategy,
|
|
7
|
+
resolveHoverSync,
|
|
8
|
+
hasHoverMarker
|
|
9
|
+
} from '../../layout/tooltip/utils';
|
|
10
|
+
import { resolveAccessor } from '../../plots/utils/accessors';
|
|
11
|
+
import { resolveTimelineInterpolation } from '../../utils/interpolate';
|
|
12
|
+
import type { LineSegmentStyle } from '../../types/plots/styles';
|
|
13
|
+
import type { ChartProps } from '../../types/charts/props';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* High-level line chart. It takes flat rows plus `x`/`y`/`z` encodings, groups
|
|
17
|
+
* by `z` into one line per group, and hands the result to {@link LinePlot}.
|
|
18
|
+
* Everything chrome-related (title, legend, timeline, facets) is delegated to
|
|
19
|
+
* {@link Chart}; this component only owns the flat-data → line-series adaption.
|
|
20
|
+
*/
|
|
21
|
+
let {
|
|
22
|
+
width,
|
|
23
|
+
height,
|
|
24
|
+
title,
|
|
25
|
+
subtitle,
|
|
26
|
+
caption,
|
|
27
|
+
legend,
|
|
28
|
+
data,
|
|
29
|
+
x,
|
|
30
|
+
y,
|
|
31
|
+
z,
|
|
32
|
+
styles = {},
|
|
33
|
+
segments,
|
|
34
|
+
markers = [],
|
|
35
|
+
scales = {},
|
|
36
|
+
margins,
|
|
37
|
+
facet,
|
|
38
|
+
timeline,
|
|
39
|
+
tooltip,
|
|
40
|
+
}: ChartProps<TRow, LineSegmentStyle> = $props();
|
|
41
|
+
|
|
42
|
+
// A line should grow into its full extent over time, not snap to a single
|
|
43
|
+
// year's point — so default to a cumulative reveal unless the caller wants
|
|
44
|
+
// the bar/heatmap-style exact-step snapshot instead.
|
|
45
|
+
const timelineConfig = $derived(
|
|
46
|
+
timeline
|
|
47
|
+
? {
|
|
48
|
+
...timeline,
|
|
49
|
+
filterMode: timeline.filterMode ?? 'upTo',
|
|
50
|
+
interpolate: resolveTimelineInterpolation(timeline.interpolate, { value: y, key: z }),
|
|
51
|
+
}
|
|
52
|
+
: undefined,
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
const tip = $derived(resolveTooltip(tooltip, 'x'));
|
|
56
|
+
const hoverEnabled = $derived(tip.enabled || hasHoverMarker(markers));
|
|
57
|
+
const resolvedStrategy = $derived(resolveHoverStrategy(tip.enabled ? tip.strategy : undefined, markers, 'x'));
|
|
58
|
+
const resolvedSync = $derived(resolveHoverSync(tip.enabled ? tip.sync : undefined, markers));
|
|
59
|
+
const getX = $derived(resolveAccessor<TRow>(x));
|
|
60
|
+
const getY = $derived(resolveAccessor<TRow>(y));
|
|
61
|
+
const getZ = $derived(z ? resolveAccessor<TRow>(z) : null);
|
|
62
|
+
const fmtX = (v: unknown) => (v instanceof Date ? v.toLocaleDateString() : String(v));
|
|
63
|
+
const defaultFormat = $derived((row: TRow) => {
|
|
64
|
+
const group = getZ ? `${getZ(row)}\n` : '';
|
|
65
|
+
return `${group}${fmtX(getX(row))}: ${getY(row)}`;
|
|
66
|
+
});
|
|
67
|
+
</script>
|
|
68
|
+
|
|
69
|
+
<Chart
|
|
70
|
+
{width}
|
|
71
|
+
{height}
|
|
72
|
+
{title}
|
|
73
|
+
{subtitle}
|
|
74
|
+
{caption}
|
|
75
|
+
{legend}
|
|
76
|
+
{data}
|
|
77
|
+
{facet}
|
|
78
|
+
timeline={timelineConfig}
|
|
79
|
+
hover={hoverEnabled ? { strategy: resolvedStrategy, sync: resolvedSync } : undefined}
|
|
80
|
+
>
|
|
81
|
+
{#snippet plot({ data: rows, width: w, height: h, facetValue })}
|
|
82
|
+
<LinePlot
|
|
83
|
+
width={w}
|
|
84
|
+
height={h}
|
|
85
|
+
data={rows}
|
|
86
|
+
{x}
|
|
87
|
+
{y}
|
|
88
|
+
{z}
|
|
89
|
+
{styles}
|
|
90
|
+
{segments}
|
|
91
|
+
{markers}
|
|
92
|
+
{scales}
|
|
93
|
+
{margins}
|
|
94
|
+
tooltip={tip.enabled
|
|
95
|
+
? {
|
|
96
|
+
format: tip.format ?? defaultFormat,
|
|
97
|
+
facetId: facetValue ?? '',
|
|
98
|
+
strategy: tip.strategy,
|
|
99
|
+
sync: tip.sync,
|
|
100
|
+
series: tip.series,
|
|
101
|
+
content: tip.content,
|
|
102
|
+
}
|
|
103
|
+
: undefined}
|
|
104
|
+
/>
|
|
105
|
+
{/snippet}
|
|
106
|
+
</Chart>
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import Chart from '../Chart.svelte';
|
|
2
|
+
import type { LineSegmentStyle } from '../../types/plots/styles';
|
|
3
|
+
import type { ChartProps } from '../../types/charts/props';
|
|
4
|
+
declare function $$render<TRow extends Record<string, unknown>>(): {
|
|
5
|
+
props: ChartProps<TRow, LineSegmentStyle>;
|
|
6
|
+
exports: {};
|
|
7
|
+
bindings: "";
|
|
8
|
+
slots: {};
|
|
9
|
+
events: {};
|
|
10
|
+
};
|
|
11
|
+
declare class __sveltets_Render<TRow extends Record<string, unknown>> {
|
|
12
|
+
props(): ReturnType<typeof $$render<TRow>>['props'];
|
|
13
|
+
events(): ReturnType<typeof $$render<TRow>>['events'];
|
|
14
|
+
slots(): ReturnType<typeof $$render<TRow>>['slots'];
|
|
15
|
+
bindings(): "";
|
|
16
|
+
exports(): {};
|
|
17
|
+
}
|
|
18
|
+
interface $$IsomorphicComponent {
|
|
19
|
+
new <TRow extends Record<string, unknown>>(options: import('svelte').ComponentConstructorOptions<ReturnType<__sveltets_Render<TRow>['props']>>): import('svelte').SvelteComponent<ReturnType<__sveltets_Render<TRow>['props']>, ReturnType<__sveltets_Render<TRow>['events']>, ReturnType<__sveltets_Render<TRow>['slots']>> & {
|
|
20
|
+
$$bindings?: ReturnType<__sveltets_Render<TRow>['bindings']>;
|
|
21
|
+
} & ReturnType<__sveltets_Render<TRow>['exports']>;
|
|
22
|
+
<TRow extends Record<string, unknown>>(internal: unknown, props: ReturnType<__sveltets_Render<TRow>['props']> & {}): ReturnType<__sveltets_Render<TRow>['exports']>;
|
|
23
|
+
z_$$bindings?: ReturnType<__sveltets_Render<any>['bindings']>;
|
|
24
|
+
}
|
|
25
|
+
declare const Chart: $$IsomorphicComponent;
|
|
26
|
+
type Chart<TRow extends Record<string, unknown>> = InstanceType<typeof Chart<TRow>>;
|
|
27
|
+
export default Chart;
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
<script lang="ts" generics="TRow extends Record<string, unknown>">
|
|
2
|
+
import Chart from '../Chart.svelte';
|
|
3
|
+
import PyramidPlot from '../../plots/pyramid/Plot.svelte';
|
|
4
|
+
import {
|
|
5
|
+
resolveTooltip,
|
|
6
|
+
resolveHoverStrategy,
|
|
7
|
+
resolveHoverSync,
|
|
8
|
+
hasHoverMarker
|
|
9
|
+
} from '../../layout/tooltip/utils';
|
|
10
|
+
import { resolveAccessor } from '../../plots/utils/accessors';
|
|
11
|
+
import { resolveTimelineInterpolation } from '../../utils/interpolate';
|
|
12
|
+
import type { Accessor, AxisValue } from '../../types/plots/common';
|
|
13
|
+
import type { BarSegmentStyle } from '../../types/plots/styles';
|
|
14
|
+
import type { ChartProps } from '../../types/charts/props';
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* High-level population-pyramid-style chart. Forwards flat rows and the
|
|
18
|
+
* `x`/`y`/`z` accessors straight to {@link PyramidPlot}, which groups them
|
|
19
|
+
* into series and assigns left/right sides automatically from the
|
|
20
|
+
* alphabetical order of `z` values. Pass `scales.z.reverse: true` to flip
|
|
21
|
+
* which side goes first. Delegates all chrome (title, legend, timeline,
|
|
22
|
+
* facets) to {@link Chart}.
|
|
23
|
+
*/
|
|
24
|
+
let {
|
|
25
|
+
width,
|
|
26
|
+
height,
|
|
27
|
+
title,
|
|
28
|
+
subtitle,
|
|
29
|
+
caption,
|
|
30
|
+
legend,
|
|
31
|
+
data,
|
|
32
|
+
x: magnitude,
|
|
33
|
+
y: category,
|
|
34
|
+
z: sides,
|
|
35
|
+
styles = {},
|
|
36
|
+
scales = {},
|
|
37
|
+
margins,
|
|
38
|
+
segments,
|
|
39
|
+
markers = [],
|
|
40
|
+
facet,
|
|
41
|
+
timeline,
|
|
42
|
+
tooltip,
|
|
43
|
+
}: ChartProps<TRow, BarSegmentStyle> & { x: Accessor<TRow, number>; z: Accessor<TRow, unknown> } = $props();
|
|
44
|
+
|
|
45
|
+
const getMagnitude = $derived(resolveAccessor<TRow>(magnitude));
|
|
46
|
+
const getCategory = $derived(resolveAccessor<TRow>(category));
|
|
47
|
+
const getSides = $derived(resolveAccessor<TRow>(sides));
|
|
48
|
+
const fmtVal = $derived(styles.values?.format ?? ((v: number) => `${v}`));
|
|
49
|
+
|
|
50
|
+
// Computed once over the full dataset (not the per-facet/timeline-filtered
|
|
51
|
+
// rows) so the diverging axis and category bands stay comparable across
|
|
52
|
+
// timeline steps/facets — the same reasoning HeatmapChart applies to its
|
|
53
|
+
// colour domain.
|
|
54
|
+
const categoryDomain = $derived([...new Set(data.map((d) => getCategory(d) as AxisValue))]);
|
|
55
|
+
const maxMagnitude = $derived.by(() => {
|
|
56
|
+
const vals = data.map((d) => Math.abs(Number(getMagnitude(d))));
|
|
57
|
+
return vals.length ? Math.max(...vals) : 1;
|
|
58
|
+
});
|
|
59
|
+
// Spread all caller scales first so scales.z (used by PyramidPlot for side
|
|
60
|
+
// assignment) is preserved, then override x/y with the computed domains.
|
|
61
|
+
const resolvedScales = $derived({
|
|
62
|
+
...scales,
|
|
63
|
+
x: { domain: [-maxMagnitude, maxMagnitude], ...(scales.x ?? {}) },
|
|
64
|
+
y: { domain: categoryDomain, ...(scales.y ?? {}) },
|
|
65
|
+
});
|
|
66
|
+
// A pyramid timeline snapshots one frame per step; interpolation needs to
|
|
67
|
+
// match each bar across steps by its category and z-group, not by the
|
|
68
|
+
// timeline's own time field.
|
|
69
|
+
const timelineConfig = $derived(
|
|
70
|
+
timeline
|
|
71
|
+
? {
|
|
72
|
+
...timeline,
|
|
73
|
+
interpolate: resolveTimelineInterpolation(timeline.interpolate, {
|
|
74
|
+
value: magnitude,
|
|
75
|
+
key: (d: TRow) => `${String(getCategory(d))}::${String(getSides(d))}`,
|
|
76
|
+
}),
|
|
77
|
+
}
|
|
78
|
+
: undefined,
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
const tip = $derived(resolveTooltip(tooltip, 'y'));
|
|
82
|
+
const hoverEnabled = $derived(tip.enabled || hasHoverMarker(markers));
|
|
83
|
+
const resolvedStrategy = $derived(resolveHoverStrategy(tip.enabled ? tip.strategy : undefined, markers, 'y'));
|
|
84
|
+
const resolvedSync = $derived(resolveHoverSync(tip.enabled ? tip.sync : undefined, markers));
|
|
85
|
+
const defaultFormat = $derived(
|
|
86
|
+
(row: TRow) => `${String(getSides(row))}\n${String(getCategory(row))}: ${fmtVal(Number(getMagnitude(row)), String(getSides(row)))}`,
|
|
87
|
+
);
|
|
88
|
+
</script>
|
|
89
|
+
|
|
90
|
+
<Chart
|
|
91
|
+
{width}
|
|
92
|
+
{height}
|
|
93
|
+
{title}
|
|
94
|
+
{subtitle}
|
|
95
|
+
{caption}
|
|
96
|
+
{legend}
|
|
97
|
+
{data}
|
|
98
|
+
{facet}
|
|
99
|
+
timeline={timelineConfig}
|
|
100
|
+
hover={hoverEnabled ? { strategy: resolvedStrategy, sync: resolvedSync } : undefined}
|
|
101
|
+
>
|
|
102
|
+
{#snippet plot({ data: rows, width: w, height: h, facetValue })}
|
|
103
|
+
<PyramidPlot
|
|
104
|
+
width={w}
|
|
105
|
+
height={h}
|
|
106
|
+
data={rows}
|
|
107
|
+
x={magnitude}
|
|
108
|
+
y={category}
|
|
109
|
+
z={sides}
|
|
110
|
+
{styles}
|
|
111
|
+
scales={resolvedScales}
|
|
112
|
+
{margins}
|
|
113
|
+
{segments}
|
|
114
|
+
{markers}
|
|
115
|
+
tooltip={tip.enabled
|
|
116
|
+
? {
|
|
117
|
+
format: tip.format ?? defaultFormat,
|
|
118
|
+
facetId: facetValue ?? '',
|
|
119
|
+
strategy: tip.strategy,
|
|
120
|
+
sync: tip.sync,
|
|
121
|
+
series: tip.series,
|
|
122
|
+
content: tip.content,
|
|
123
|
+
}
|
|
124
|
+
: undefined}
|
|
125
|
+
/>
|
|
126
|
+
{/snippet}
|
|
127
|
+
</Chart>
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import Chart from '../Chart.svelte';
|
|
2
|
+
import type { Accessor } from '../../types/plots/common';
|
|
3
|
+
import type { BarSegmentStyle } from '../../types/plots/styles';
|
|
4
|
+
import type { ChartProps } from '../../types/charts/props';
|
|
5
|
+
declare function $$render<TRow extends Record<string, unknown>>(): {
|
|
6
|
+
props: ChartProps<TRow, BarSegmentStyle> & {
|
|
7
|
+
x: Accessor<TRow, number>;
|
|
8
|
+
z: Accessor<TRow, unknown>;
|
|
9
|
+
};
|
|
10
|
+
exports: {};
|
|
11
|
+
bindings: "";
|
|
12
|
+
slots: {};
|
|
13
|
+
events: {};
|
|
14
|
+
};
|
|
15
|
+
declare class __sveltets_Render<TRow extends Record<string, unknown>> {
|
|
16
|
+
props(): ReturnType<typeof $$render<TRow>>['props'];
|
|
17
|
+
events(): ReturnType<typeof $$render<TRow>>['events'];
|
|
18
|
+
slots(): ReturnType<typeof $$render<TRow>>['slots'];
|
|
19
|
+
bindings(): "";
|
|
20
|
+
exports(): {};
|
|
21
|
+
}
|
|
22
|
+
interface $$IsomorphicComponent {
|
|
23
|
+
new <TRow extends Record<string, unknown>>(options: import('svelte').ComponentConstructorOptions<ReturnType<__sveltets_Render<TRow>['props']>>): import('svelte').SvelteComponent<ReturnType<__sveltets_Render<TRow>['props']>, ReturnType<__sveltets_Render<TRow>['events']>, ReturnType<__sveltets_Render<TRow>['slots']>> & {
|
|
24
|
+
$$bindings?: ReturnType<__sveltets_Render<TRow>['bindings']>;
|
|
25
|
+
} & ReturnType<__sveltets_Render<TRow>['exports']>;
|
|
26
|
+
<TRow extends Record<string, unknown>>(internal: unknown, props: ReturnType<__sveltets_Render<TRow>['props']> & {}): ReturnType<__sveltets_Render<TRow>['exports']>;
|
|
27
|
+
z_$$bindings?: ReturnType<__sveltets_Render<any>['bindings']>;
|
|
28
|
+
}
|
|
29
|
+
declare const Chart: $$IsomorphicComponent;
|
|
30
|
+
type Chart<TRow extends Record<string, unknown>> = InstanceType<typeof Chart<TRow>>;
|
|
31
|
+
export default Chart;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { ChartConfig, ChartConfigInput } from '../types/configuration/styling';
|
|
2
|
+
export declare const DEFAULT_CONFIG: ChartConfig;
|
|
3
|
+
/** Read the live, reactive configuration. */
|
|
4
|
+
export declare function getConfiguration(): ChartConfig;
|
|
5
|
+
/** Deep-merge a partial override into the global configuration. Reactive. */
|
|
6
|
+
export declare function setConfiguration(patch: ChartConfigInput): ChartConfig;
|
|
7
|
+
/** Restore the shipped defaults. */
|
|
8
|
+
export declare function resetConfiguration(): void;
|
|
9
|
+
/** Flatten a config into the `--dct-*` custom properties used by the styles. */
|
|
10
|
+
export declare function configToCssVars(c: ChartConfig): Record<string, string>;
|
|
11
|
+
/** Serialize a CSS-vars record into a `style` attribute string. */
|
|
12
|
+
export declare function cssVarsStyle(vars: Record<string, string>): string;
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Global, reactive styling configuration for the whole package.
|
|
3
|
+
*
|
|
4
|
+
* One source of truth feeds two consumers:
|
|
5
|
+
* 1. HTML chrome (container, legend, caption, tooltip) reads it as **CSS
|
|
6
|
+
* custom properties** — {@link configToCssVars} flattens the config and
|
|
7
|
+
* {@link ChartContainer} stamps the variables on its root so the scoped
|
|
8
|
+
* `<style>` blocks resolve them.
|
|
9
|
+
* 2. Plots read it as **plain JS values** for the props svelteplot needs as
|
|
10
|
+
* numbers/strings (margins, tick font sizes, palette, gradient endpoints).
|
|
11
|
+
*
|
|
12
|
+
* Because the store is a module-level Svelte `$state`, calling
|
|
13
|
+
* {@link setConfiguration} re-renders every chart on screen.
|
|
14
|
+
*/
|
|
15
|
+
// ── Defaults ────────────────────────────────────────────────────────────────
|
|
16
|
+
// Mirror the values the components shipped with, so adopting the config system
|
|
17
|
+
// is visually a no-op until something is overridden.
|
|
18
|
+
const text = (over) => ({
|
|
19
|
+
font: 'inherit',
|
|
20
|
+
size: '1rem',
|
|
21
|
+
weight: 400,
|
|
22
|
+
color: 'inherit',
|
|
23
|
+
leading: '1.3',
|
|
24
|
+
spacing: 'normal',
|
|
25
|
+
...over,
|
|
26
|
+
});
|
|
27
|
+
export const DEFAULT_CONFIG = {
|
|
28
|
+
font: 'inherit',
|
|
29
|
+
color: 'currentColor',
|
|
30
|
+
background: 'transparent',
|
|
31
|
+
border: 'color-mix(in srgb, currentColor 15%, transparent)',
|
|
32
|
+
spacing: {
|
|
33
|
+
headerPadding: '0.75rem 0 1rem',
|
|
34
|
+
legendPadding: '0.75rem 0',
|
|
35
|
+
captionPadding: '0.5rem 0',
|
|
36
|
+
},
|
|
37
|
+
title: text({ size: '1.5rem', weight: 500, leading: '1.2' }),
|
|
38
|
+
subtitle: text({ size: '1rem', weight: 400, color: 'color-mix(in srgb, currentColor 85%, transparent)' }),
|
|
39
|
+
caption: text({ size: '0.75rem', color: 'color-mix(in srgb, currentColor 75%, transparent)' }),
|
|
40
|
+
legend: {
|
|
41
|
+
font: 'inherit',
|
|
42
|
+
size: '0.875rem',
|
|
43
|
+
weight: 400,
|
|
44
|
+
color: 'inherit',
|
|
45
|
+
titleSize: '0.875rem',
|
|
46
|
+
titleWeight: 500,
|
|
47
|
+
gap: '0.5rem 2rem',
|
|
48
|
+
swatchSize: '1.25rem',
|
|
49
|
+
},
|
|
50
|
+
axis: {
|
|
51
|
+
color: 'currentColor',
|
|
52
|
+
fontSize: 14,
|
|
53
|
+
labelFontSize: 14,
|
|
54
|
+
titleFontSize: 14,
|
|
55
|
+
xTickSize: 5,
|
|
56
|
+
yTickSize: 5,
|
|
57
|
+
xTickPadding: 10,
|
|
58
|
+
yTickPadding: 10,
|
|
59
|
+
grid: false,
|
|
60
|
+
},
|
|
61
|
+
tooltip: {
|
|
62
|
+
background: '#111827',
|
|
63
|
+
color: '#ffffff',
|
|
64
|
+
fontSize: '0.8125rem',
|
|
65
|
+
padding: '0.5rem 0.75rem',
|
|
66
|
+
radius: '0.375rem',
|
|
67
|
+
border: 'none',
|
|
68
|
+
},
|
|
69
|
+
margins: { top: 30, right: 50, bottom: 30, left: 40 },
|
|
70
|
+
palette: ['#4f46e5', '#e6580d', '#16a34a', '#dc2626', '#0891b2', '#9333ea', '#ca8a04', '#db2777'],
|
|
71
|
+
continuous: { min: '#e0e7ff', max: '#4f46e5', labelColor: '#111827' },
|
|
72
|
+
line: { strokeWidth: 3, dotRadius: 4 },
|
|
73
|
+
timeline: { axisColor: 'currentColor', activeColor: '#e6580d', thickness: 60 },
|
|
74
|
+
hover: {
|
|
75
|
+
rule: { stroke: '#e6580d', strokeOpacity: 0.35, strokeWidth: 1.5, strokeDasharray: '4 4' },
|
|
76
|
+
dot: { dotRadius: 5, dotFill: 'currentColor' },
|
|
77
|
+
font: { fill: 'currentColor', fontSize: 12, dy: -18 },
|
|
78
|
+
},
|
|
79
|
+
};
|
|
80
|
+
// ── Merge ─────────────────────────────────────────────────────────────────────
|
|
81
|
+
function isPlainObject(v) {
|
|
82
|
+
return typeof v === 'object' && v !== null && !Array.isArray(v);
|
|
83
|
+
}
|
|
84
|
+
function merge(base, patch) {
|
|
85
|
+
const out = { ...base };
|
|
86
|
+
for (const key in patch) {
|
|
87
|
+
const pv = patch[key];
|
|
88
|
+
if (pv === undefined)
|
|
89
|
+
continue;
|
|
90
|
+
const bv = out[key];
|
|
91
|
+
out[key] = isPlainObject(bv) && isPlainObject(pv) ? merge(bv, pv) : pv;
|
|
92
|
+
}
|
|
93
|
+
return out;
|
|
94
|
+
}
|
|
95
|
+
// ── Reactive store ──────────────────────────────────────────────────────────
|
|
96
|
+
let current = $state(structuredClone(DEFAULT_CONFIG));
|
|
97
|
+
/** Read the live, reactive configuration. */
|
|
98
|
+
export function getConfiguration() {
|
|
99
|
+
return current;
|
|
100
|
+
}
|
|
101
|
+
/** Deep-merge a partial override into the global configuration. Reactive. */
|
|
102
|
+
export function setConfiguration(patch) {
|
|
103
|
+
current = merge(current, patch);
|
|
104
|
+
return current;
|
|
105
|
+
}
|
|
106
|
+
/** Restore the shipped defaults. */
|
|
107
|
+
export function resetConfiguration() {
|
|
108
|
+
current = structuredClone(DEFAULT_CONFIG);
|
|
109
|
+
}
|
|
110
|
+
// ── CSS variable bridge ───────────────────────────────────────────────────────
|
|
111
|
+
/** Flatten a config into the `--dct-*` custom properties used by the styles. */
|
|
112
|
+
export function configToCssVars(c) {
|
|
113
|
+
const t = (prefix, s) => ({
|
|
114
|
+
[`--dct-${prefix}-font`]: s.font,
|
|
115
|
+
[`--dct-${prefix}-size`]: s.size,
|
|
116
|
+
[`--dct-${prefix}-weight`]: String(s.weight),
|
|
117
|
+
[`--dct-${prefix}-color`]: s.color,
|
|
118
|
+
[`--dct-${prefix}-leading`]: s.leading,
|
|
119
|
+
[`--dct-${prefix}-spacing`]: s.spacing,
|
|
120
|
+
});
|
|
121
|
+
return {
|
|
122
|
+
'--dct-font': c.font,
|
|
123
|
+
'--dct-fg': c.color,
|
|
124
|
+
'--dct-bg': c.background,
|
|
125
|
+
'--dct-border': c.border,
|
|
126
|
+
'--dct-header-pad': c.spacing.headerPadding,
|
|
127
|
+
'--dct-legend-pad': c.spacing.legendPadding,
|
|
128
|
+
'--dct-caption-pad': c.spacing.captionPadding,
|
|
129
|
+
...t('title', c.title),
|
|
130
|
+
...t('subtitle', c.subtitle),
|
|
131
|
+
...t('caption', c.caption),
|
|
132
|
+
'--dct-legend-font': c.legend.font,
|
|
133
|
+
'--dct-legend-size': c.legend.size,
|
|
134
|
+
'--dct-legend-weight': String(c.legend.weight),
|
|
135
|
+
'--dct-legend-color': c.legend.color,
|
|
136
|
+
'--dct-legend-title-size': c.legend.titleSize,
|
|
137
|
+
'--dct-legend-title-weight': String(c.legend.titleWeight),
|
|
138
|
+
'--dct-legend-gap': c.legend.gap,
|
|
139
|
+
'--dct-legend-swatch': c.legend.swatchSize,
|
|
140
|
+
'--dct-axis-color': c.axis.color,
|
|
141
|
+
'--dct-tooltip-bg': c.tooltip.background,
|
|
142
|
+
'--dct-tooltip-color': c.tooltip.color,
|
|
143
|
+
'--dct-tooltip-size': c.tooltip.fontSize,
|
|
144
|
+
'--dct-tooltip-pad': c.tooltip.padding,
|
|
145
|
+
'--dct-tooltip-radius': c.tooltip.radius,
|
|
146
|
+
'--dct-tooltip-border': c.tooltip.border,
|
|
147
|
+
'--dct-timeline-axis': c.timeline.axisColor,
|
|
148
|
+
'--dct-timeline-active': c.timeline.activeColor,
|
|
149
|
+
'--dct-heat-min': c.continuous.min,
|
|
150
|
+
'--dct-heat-max': c.continuous.max,
|
|
151
|
+
'--dct-heat-text': c.continuous.labelColor,
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
/** Serialize a CSS-vars record into a `style` attribute string. */
|
|
155
|
+
export function cssVarsStyle(vars) {
|
|
156
|
+
return Object.entries(vars)
|
|
157
|
+
.map(([k, v]) => `${k}:${v}`)
|
|
158
|
+
.join(';');
|
|
159
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export const COMPACT_THEME = {
|
|
2
|
+
title: { size: '1.1rem', weight: 600 },
|
|
3
|
+
subtitle: { size: '0.85rem' },
|
|
4
|
+
legend: { size: '0.75rem', swatchSize: '0.9rem', gap: '0.25rem 1rem' },
|
|
5
|
+
axis: { fontSize: 11, xTickSize: 3, yTickSize: 3, xTickPadding: 6, yTickPadding: 6 },
|
|
6
|
+
margins: { top: 18, right: 30, bottom: 22, left: 30 },
|
|
7
|
+
line: { strokeWidth: 2, dotRadius: 3 },
|
|
8
|
+
hover: { rule: { strokeWidth: 1, strokeDasharray: '3 3' }, dot: { dotRadius: 3 } },
|
|
9
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export const DARK_THEME = {
|
|
2
|
+
font: 'ui-sans-serif, system-ui, sans-serif',
|
|
3
|
+
color: '#e5e7eb',
|
|
4
|
+
background: '#0f172a',
|
|
5
|
+
border: 'color-mix(in srgb, #e5e7eb 18%, transparent)',
|
|
6
|
+
title: { size: '1.6rem', weight: 600 },
|
|
7
|
+
subtitle: { color: '#94a3b8' },
|
|
8
|
+
caption: { color: '#94a3b8' },
|
|
9
|
+
legend: { gap: '0.5rem 1.5rem', swatchSize: '1rem' },
|
|
10
|
+
palette: ['#38bdf8', '#f472b6', '#a3e635', '#fbbf24', '#c084fc'],
|
|
11
|
+
continuous: { min: '#1e293b', max: '#38bdf8', labelColor: '#e5e7eb' },
|
|
12
|
+
axis: { color: '#94a3b8', fontSize: 13, xTickSize: 6, yTickSize: 6 },
|
|
13
|
+
tooltip: { background: '#e5e7eb', color: '#0f172a' },
|
|
14
|
+
line: { strokeWidth: 3, dotRadius: 4 },
|
|
15
|
+
timeline: { activeColor: '#38bdf8', axisColor: '#94a3b8', thickness: 60 },
|
|
16
|
+
hover: { rule: { stroke: '#38bdf8' } },
|
|
17
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export const EDITORIAL_THEME = {
|
|
2
|
+
font: 'Georgia, "Times New Roman", serif',
|
|
3
|
+
color: '#2b2118',
|
|
4
|
+
border: 'color-mix(in srgb, #2b2118 20%, transparent)',
|
|
5
|
+
title: { size: '1.9rem', weight: 700, leading: '1.1', spacing: '-0.01em' },
|
|
6
|
+
subtitle: { color: '#8a6d3b', size: '1.05rem' },
|
|
7
|
+
caption: { color: '#8a6d3b' },
|
|
8
|
+
palette: ['#9b2226', '#606c38', '#6a4c93', '#ee9b00', '#005f73'],
|
|
9
|
+
continuous: { min: '#fff3e0', max: '#bb3e03', labelColor: '#2b2118' },
|
|
10
|
+
axis: { color: '#8a6d3b', fontSize: 13, xTickSize: 6, yTickSize: 6 },
|
|
11
|
+
line: { strokeWidth: 5, dotRadius: 5 },
|
|
12
|
+
timeline: { activeColor: '#bb3e03', axisColor: '#8a6d3b', thickness: 60 },
|
|
13
|
+
hover: { rule: { stroke: '#bb3e03' } },
|
|
14
|
+
};
|