@fundar/data-chart-telling 1.0.0 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +21 -0
- package/README.md +57 -18
- package/dist/configuration/config.svelte.js +13 -1
- package/dist/configuration/themes/index.d.ts +124 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.js +1 -0
- package/dist/layout/base/BasePlotLayout.svelte +18 -5
- package/dist/layout/hover/HoverSettings.d.ts +20 -6
- package/dist/layout/hover/HoverSettings.js +29 -4
- package/dist/layout/legend/LegendState.svelte.d.ts +5 -0
- package/dist/layout/legend/LegendState.svelte.js +4 -0
- package/dist/layout/plot/Plot.svelte +6 -3
- package/dist/layout/plot/PublicPlotContext.d.ts +5 -4
- package/dist/layout/plot/PublicPlotContext.js +7 -3
- package/dist/layout/plot/Slice.js +4 -2
- package/dist/layout/scales/EdgeTicks.d.ts +39 -0
- package/dist/layout/scales/EdgeTicks.js +82 -0
- package/dist/layout/scales/NiceTicks.d.ts +40 -0
- package/dist/layout/scales/NiceTicks.js +72 -0
- package/dist/layout/scales/ScalesLayout.svelte +64 -30
- package/dist/layout/scales/ScalesLayout.svelte.d.ts +5 -0
- package/dist/layout/scales/SveltePlotScale.d.ts +15 -0
- package/dist/layout/scales/SveltePlotScale.js +79 -0
- package/dist/layout/styles/ValueLabels.js +3 -2
- package/dist/layout/timeline/Timeline.svelte +5 -2
- package/dist/layout/timeline/Timeline.svelte.d.ts +2 -0
- package/dist/layout/timeline/TimelineLayout.svelte +38 -12
- package/dist/layout/timeline/TimelineLayout.svelte.d.ts +7 -5
- package/dist/layout/timeline/TimelineRule.svelte +1 -0
- package/dist/layout/timeline/TimelineState.svelte.d.ts +13 -5
- package/dist/layout/timeline/TimelineState.svelte.js +42 -8
- package/dist/layout/timeline/TimelineTime.d.ts +18 -0
- package/dist/layout/timeline/TimelineTime.js +117 -0
- package/dist/layout/tooltip/Tooltip.svelte +6 -3
- package/dist/layout/tooltip/Tooltip.svelte.d.ts +2 -0
- package/dist/layout/tooltip/TooltipState.svelte.d.ts +8 -2
- package/dist/layout/tooltip/TooltipState.svelte.js +22 -3
- package/dist/markers/delta/DeltaMarker.svelte +9 -7
- package/dist/phases/Pipeline.js +3 -2
- package/dist/phases/data/DataPhase.d.ts +5 -11
- package/dist/phases/data/DataPhase.js +11 -8
- package/dist/phases/facet/FacetPanels.d.ts +54 -0
- package/dist/phases/facet/FacetPanels.js +95 -0
- package/dist/phases/facet/FacetPhase.d.ts +1 -42
- package/dist/phases/facet/FacetPhase.js +1 -87
- package/dist/phases/hover/HoverPhase.d.ts +6 -43
- package/dist/phases/hover/HoverPhase.js +19 -54
- package/dist/phases/mapping/MappingPhase.d.ts +1 -1
- package/dist/phases/mapping/MappingPhase.js +2 -4
- package/dist/phases/scales/ScalesPhase.d.ts +1 -3
- package/dist/phases/scales/ScalesPhase.js +17 -21
- package/dist/phases/sharedScales/SharedScalesPhase.d.ts +4 -1
- package/dist/phases/sharedScales/SharedScalesPhase.js +10 -7
- package/dist/phases/statistics/StatisticsPhase.js +3 -4
- package/dist/phases/tooltip/TooltipPhase.d.ts +4 -2
- package/dist/phases/tooltip/TooltipPhase.js +10 -3
- package/dist/phases/transforms/ProcessedPositions.d.ts +30 -0
- package/dist/phases/transforms/ProcessedPositions.js +74 -0
- package/dist/phases/transforms/TransformsPhase.d.ts +5 -42
- package/dist/phases/transforms/TransformsPhase.js +20 -57
- package/dist/plots/area/markers.js +1 -2
- package/dist/plots/bar/defaults.js +6 -10
- package/dist/plots/bar/markers.js +5 -4
- package/dist/plots/box/BoxSummaries.d.ts +19 -0
- package/dist/plots/box/BoxSummaries.js +101 -0
- package/dist/plots/box/Plot.svelte +42 -0
- package/dist/plots/box/Plot.svelte.d.ts +26 -0
- package/dist/plots/box/defaults.d.ts +12 -0
- package/dist/plots/box/defaults.js +34 -0
- package/dist/plots/box/markers.d.ts +12 -0
- package/dist/plots/box/markers.js +34 -0
- package/dist/plots/geo/phases/hover.js +5 -5
- package/dist/plots/geo/phases/markers.js +3 -4
- package/dist/plots/geo/slices.js +6 -3
- package/dist/plots/heatmap/markers.js +2 -1
- package/dist/plots/histogram/defaults.js +1 -1
- package/dist/plots/histogram/markers.js +4 -4
- package/dist/plots/line/markers.js +1 -2
- package/dist/plots/pyramid/defaults.js +3 -2
- package/dist/plots/pyramid/markers.js +3 -6
- package/dist/plots/scatter/defaults.js +3 -2
- package/dist/plots/scatter/markers.js +4 -3
- package/dist/types/configuration/styling.d.ts +24 -2
- package/dist/types/layout/composer.d.ts +5 -2
- package/dist/types/layout/scales.d.ts +16 -0
- package/dist/types/layout/timeline.d.ts +14 -5
- package/dist/types/phases/hover/phase.d.ts +6 -0
- package/dist/types/phases/state.d.ts +5 -2
- package/dist/types/phases/statistics/phase.d.ts +3 -1
- package/dist/types/phases/transforms/phase.d.ts +10 -0
- package/dist/types/plots/box/props.d.ts +10 -0
- package/dist/types/plots/box/props.js +1 -0
- package/dist/types/plots/box/state.d.ts +23 -0
- package/dist/types/plots/box/state.js +1 -0
- package/dist/types/plots/props.d.ts +2 -0
- package/docs/chrome.md +39 -8
- package/docs/composer.md +19 -3
- package/docs/concepts.md +1 -1
- package/docs/development.md +14 -5
- package/docs/facets.md +62 -8
- package/docs/getting-started.md +8 -9
- package/docs/legend.md +62 -15
- package/docs/markers.md +17 -6
- package/docs/plots.md +168 -42
- package/docs/scales.md +36 -19
- package/docs/state.md +1 -1
- package/docs/styles-and-segments.md +85 -31
- package/docs/theming.md +30 -19
- package/docs/timeline.md +62 -28
- package/docs/tooltip-and-hover.md +72 -24
- package/docs/transforms-and-statistics.md +47 -17
- package/examples/BarValues.svelte +2 -2
- package/examples/Box.svelte +7 -0
- package/examples/Chrome.svelte +17 -0
- package/examples/ChromeOrder.svelte +18 -0
- package/examples/Composer.svelte +23 -0
- package/examples/CustomTooltip.svelte +21 -14
- package/examples/Facets.svelte +3 -3
- package/examples/FacetsCarousel.svelte +14 -0
- package/examples/FacetsRowsCols.svelte +23 -0
- package/examples/FirstPlot.svelte +7 -0
- package/examples/Gaps.svelte +21 -0
- package/examples/GroupedBars.svelte +15 -0
- package/examples/Heatmap.svelte +7 -0
- package/examples/HeatmapSegments.svelte +26 -0
- package/examples/Histogram.svelte +14 -0
- package/examples/HorizontalBars.svelte +15 -0
- package/examples/LegendSections.svelte +27 -0
- package/examples/LegendToggle.svelte +3 -3
- package/examples/LineByRegion.svelte +18 -0
- package/examples/LineDates.svelte +15 -0
- package/examples/Markers.svelte +27 -0
- package/examples/MovingAverage.svelte +15 -0
- package/examples/Pyramid.svelte +7 -0
- package/examples/Regression.svelte +14 -0
- package/examples/ResponsiveLine.svelte +7 -7
- package/examples/Scales.svelte +26 -0
- package/examples/Scatter.svelte +16 -0
- package/examples/SegmentHighlight.svelte +22 -0
- package/examples/Segments.svelte +11 -3
- package/examples/SegmentsBySeries.svelte +25 -0
- package/examples/StackedArea.svelte +16 -0
- package/examples/StackedBars.svelte +15 -0
- package/examples/TimelinePlayer.svelte +8 -7
- package/examples/TimelineRule.svelte +19 -0
- package/examples/Tooltip.svelte +3 -3
- package/examples/ValueLabels.svelte +21 -0
- package/examples/data.ts +320 -8
- package/llms-full.txt +33 -5
- package/llms.txt +30 -2
- package/package.json +3 -2
- package/dist/layout/scales/buildScale.d.ts +0 -8
- package/dist/layout/scales/buildScale.js +0 -67
- package/dist/layout/scales/niceDomain.d.ts +0 -23
- package/dist/layout/scales/niceDomain.js +0 -59
|
@@ -2,7 +2,12 @@ import type { Snippet } from 'svelte';
|
|
|
2
2
|
import type { AxisValue } from './mapping';
|
|
3
3
|
import type { Accessor } from '../plots/data/common';
|
|
4
4
|
import type { EasingName, EasingFn } from '../misc/easing';
|
|
5
|
+
/** A step as the timeline works with it: a number (a date as its timestamp). */
|
|
5
6
|
export type TimeValue = number;
|
|
7
|
+
/** A step as the rows hold it: a number, a `Date` or a date string. */
|
|
8
|
+
export type TimeInput = TimeValue | Date | string;
|
|
9
|
+
/** A step as authors see it: a `Date` on a timeline of dates, else the number. */
|
|
10
|
+
export type TimelineStep = number | Date;
|
|
6
11
|
export type Orientation = 'horizontal' | 'vertical';
|
|
7
12
|
export type TimelineChildContext<TRow extends Record<string, unknown>> = {
|
|
8
13
|
selectedValue: TimeValue;
|
|
@@ -42,8 +47,8 @@ export type PlayerConfig = {
|
|
|
42
47
|
speeds?: number[];
|
|
43
48
|
/** Milliseconds each step lasts at speed `1`. Defaults to the configuration's `timeline.player.stepDuration`. */
|
|
44
49
|
stepDuration?: number;
|
|
45
|
-
/** How
|
|
46
|
-
format?: (value:
|
|
50
|
+
/** How a step reads, in the player and along a rule. Defaults to the step, or a date label for dates. */
|
|
51
|
+
format?: (value: TimelineStep) => string;
|
|
47
52
|
onplay?: (playing: boolean) => void;
|
|
48
53
|
onspeed?: (speed: number) => void;
|
|
49
54
|
onloop?: (loop: boolean) => void;
|
|
@@ -53,10 +58,13 @@ export type TimelineCallbacks = {
|
|
|
53
58
|
/** Called with the new position, `0`-`1`, whenever the player or the reader moves it. */
|
|
54
59
|
onprogress?: (progress: number) => void;
|
|
55
60
|
/** Called with the step the position lands on, when that step changes. */
|
|
56
|
-
onvalue?: (value:
|
|
61
|
+
onvalue?: (value: TimelineStep) => void;
|
|
57
62
|
};
|
|
58
63
|
export type TimelineConfig<TRow extends Record<string, unknown>> = TimelineCallbacks & {
|
|
59
|
-
|
|
64
|
+
/** The field (or function) giving each row's step: a number, a `Date`, or an ISO or `dateFormat` date string. */
|
|
65
|
+
accessor: Accessor<TRow, TimeInput>;
|
|
66
|
+
/** How a non-ISO date string reads, with `YYYY`, `MM`, `DD`, `HH`, `mm`, `ss`, e.g. `'DD/MM/YYYY'`. */
|
|
67
|
+
dateFormat?: string;
|
|
60
68
|
/** How it draws. Defaults to `'player'`. */
|
|
61
69
|
type?: TimelineType;
|
|
62
70
|
/** The player bar's own options, when `type` is `'player'`. */
|
|
@@ -67,7 +75,8 @@ export type TimelineConfig<TRow extends Record<string, unknown>> = TimelineCallb
|
|
|
67
75
|
* with its own `timeline`, the composer's wins and nothing draws here.
|
|
68
76
|
*/
|
|
69
77
|
progress?: number;
|
|
70
|
-
|
|
78
|
+
/** The steps, when not every distinct value of `accessor`. */
|
|
79
|
+
values?: TimeInput[];
|
|
71
80
|
/** Which way a `'rule'` runs. A `'player'` is always horizontal. */
|
|
72
81
|
orientation?: Orientation;
|
|
73
82
|
show?: boolean;
|
|
@@ -17,6 +17,12 @@ export type HoverStageOptions<TState, TCtx = unknown> = {
|
|
|
17
17
|
* that isn't measured, or `'punctual'` with both axes holding categories.
|
|
18
18
|
*/
|
|
19
19
|
strategy?: HoverStrategy;
|
|
20
|
+
/** The rows the pointer hovers over, when they aren't the ones drawn (a box plot's one summary per box). Defaults to the processed rows. */
|
|
21
|
+
rows?: (state: TState) => Record<string | symbol, unknown>[];
|
|
22
|
+
/** How a hovered row reads in the tooltip unless the author sets `format`. Defaults to the shared tooltip text. */
|
|
23
|
+
format?: (state: TState) => (row: never) => string;
|
|
24
|
+
/** The order a tooltip's rows read in. Defaults to the shared order: highest value first. */
|
|
25
|
+
sort?: (state: TState) => (a: never, b: never) => number;
|
|
20
26
|
/** Where a row sits. Defaults to its processed position: a stacked row's top, a bin's centre. */
|
|
21
27
|
position?: (state: TState) => HoverPosition;
|
|
22
28
|
/** The short label drawn next to a hovered row. Defaults to its value. */
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Series } from '../plots/data/common';
|
|
1
|
+
import type { Accessor, Series } from '../plots/data/common';
|
|
2
2
|
import type { AxisBasedScalesConfig } from '../layout/scales';
|
|
3
3
|
import type { SegmentsConfig, MarginConfig, HoverConfig, TooltipConfig } from '../plots/props';
|
|
4
4
|
import type { HoverPosition } from './hover/phase';
|
|
@@ -9,7 +9,7 @@ import type { PlotStylesConfig } from '../configuration/styling';
|
|
|
9
9
|
import type { AxisBasedMarkersConfig } from '../markers/common';
|
|
10
10
|
import type { StatisticConfig } from '../layout/statistics';
|
|
11
11
|
import type { MarkerTransform } from '../transforms';
|
|
12
|
-
import type { ProcessedData } from './transforms/phase';
|
|
12
|
+
import type { ProcessedData, ProcessedRow } from './transforms/phase';
|
|
13
13
|
import type { FacetStageState } from './facet/phase';
|
|
14
14
|
import type { AxisBasedStylesConfig, ValuesStyle } from '../layout/styles';
|
|
15
15
|
import type { ValueLabels } from '../../layout/styles/ValueLabels';
|
|
@@ -48,6 +48,7 @@ export type AxisPlotState<TData extends Record<string, unknown>, TStyle extends
|
|
|
48
48
|
/** What each position channel is, which one holds the categories, and which way the plot runs. */
|
|
49
49
|
axes?: PlotAxes;
|
|
50
50
|
resolvedSeries?: TSeries[];
|
|
51
|
+
seriesGrouping?: Accessor<any, unknown>;
|
|
51
52
|
/** Every series name, before legend-omitted ones are dropped, so colors stay put. */
|
|
52
53
|
allSeriesNames?: string[];
|
|
53
54
|
groupedSeries?: VisualGroup<TSeries, TStyle>[];
|
|
@@ -58,9 +59,11 @@ export type AxisPlotState<TData extends Record<string, unknown>, TStyle extends
|
|
|
58
59
|
gapFillMaps?: Map<string, Map<TData, DiscreteGapFill<TStyle>>>;
|
|
59
60
|
channels?: ResolvedChannels<TData>;
|
|
60
61
|
processedData?: ProcessedData;
|
|
62
|
+
processedGroups?: VisualGroup<Series<ProcessedRow>, TStyle>[];
|
|
61
63
|
valueLabels?: ValueLabels;
|
|
62
64
|
markers?: AxisBasedMarkersConfig[];
|
|
63
65
|
tooltipFormat?: (row: Record<string, unknown>) => string;
|
|
66
|
+
tooltipSort?: (a: Record<string, unknown>, b: Record<string, unknown>) => number;
|
|
64
67
|
hover?: HoverConfig<any>;
|
|
65
68
|
hoverPosition?: HoverPosition;
|
|
66
69
|
hoverRows?: unknown[];
|
|
@@ -3,7 +3,7 @@ import type { Accessor, Series } from '../../plots/data/common';
|
|
|
3
3
|
import type { AxisValue } from '../../layout/mapping';
|
|
4
4
|
import type { StatisticConfig } from '../../layout/statistics';
|
|
5
5
|
import type { AxisBasedMarkersConfig } from '../../markers/common';
|
|
6
|
-
import type { ProcessedData } from '../transforms/phase';
|
|
6
|
+
import type { ProcessedData, TransformsStageState } from '../transforms/phase';
|
|
7
7
|
import type { FacetStageState } from '../facet/phase';
|
|
8
8
|
import type { PlotAxes } from '../../../phases/data/PlotAxes';
|
|
9
9
|
/**
|
|
@@ -25,6 +25,8 @@ export type StatisticsStageState = Pick<FacetStageState, 'panels'> & {
|
|
|
25
25
|
scopedGroups?: ScopedMarkerGroup<any>[];
|
|
26
26
|
/** The plot's rows after its own transforms, which an author marker without rows of its own draws. */
|
|
27
27
|
processedData?: ProcessedData;
|
|
28
|
+
/** Those rows split back into the plot's visual groups, which a series without a value of its own (a histogram's) is measured by. */
|
|
29
|
+
processedGroups?: TransformsStageState['processedGroups'];
|
|
28
30
|
markers?: AxisBasedMarkersConfig[];
|
|
29
31
|
};
|
|
30
32
|
/** What differs between plots in the shared "statistics" stage. */
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { Series } from '../../plots/data/common';
|
|
2
2
|
import type { MarkerTransform } from '../../transforms';
|
|
3
3
|
import type { PlotAxes } from '../../../phases/data/PlotAxes';
|
|
4
|
+
import type { VisualGroup } from '../../plots/segments/config';
|
|
4
5
|
/** A row the plot-level transforms produced: the source row's fields, plus whatever channels they add (often symbol-keyed). */
|
|
5
6
|
export type ProcessedRow = Record<string | symbol, unknown>;
|
|
6
7
|
/** A channel as svelteplot reads it off a processed row: a field key, a symbol key, or a function. */
|
|
@@ -32,9 +33,18 @@ export type TransformsStageState = {
|
|
|
32
33
|
}>>;
|
|
33
34
|
transforms?: MarkerTransform[];
|
|
34
35
|
processedData?: ProcessedData;
|
|
36
|
+
/** The processed rows split back into the visual groups the plot's own marks draw. */
|
|
37
|
+
processedGroups?: VisualGroup<Series<ProcessedRow>, any>[];
|
|
35
38
|
};
|
|
36
39
|
/** What differs between plots in the shared "transforms" stage. */
|
|
37
40
|
export type TransformsStageOptions<TState extends TransformsStageState, TCtx> = {
|
|
38
41
|
/** The transforms to run. Defaults to the plot's own `transforms`. */
|
|
39
42
|
transforms?: (state: TState, ctx: TCtx) => MarkerTransform[];
|
|
43
|
+
/**
|
|
44
|
+
* How the processed rows split into `processedGroups`: one group per series
|
|
45
|
+
* (the default), or `'runs'` of consecutive rows from the same series, in
|
|
46
|
+
* the rows' own order, so a reordering transform (`sort`, `reverse`,
|
|
47
|
+
* `shuffle`) decides which rows paint on top.
|
|
48
|
+
*/
|
|
49
|
+
groups?: 'series' | 'runs';
|
|
40
50
|
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { BasePlotProps, PlotCoreProps } from '../props';
|
|
2
|
+
import type { AxisBasedScalesConfig } from '../../layout/scales';
|
|
3
|
+
import type { AxisBasedMarkersConfig } from '../../markers/common';
|
|
4
|
+
import type { SeriesProps, DataProps } from '../data/common';
|
|
5
|
+
import type { AxisBasedStylesConfig } from '../../layout/styles';
|
|
6
|
+
import type { AreaStyle } from '../styling';
|
|
7
|
+
/** `BoxPlot`'s public props. */
|
|
8
|
+
export type BoxPlotProps<TData extends Record<string, unknown>> = BasePlotProps<SeriesProps<TData> | DataProps<TData>, TData, AxisBasedStylesConfig, AreaStyle, AxisBasedMarkersConfig, AxisBasedScalesConfig<TData>>;
|
|
9
|
+
/** What `BoxPlot`'s core draws from. */
|
|
10
|
+
export type BoxPlotCoreProps<TData extends Record<string, unknown>> = PlotCoreProps<SeriesProps<TData> | DataProps<TData>, TData, AxisBasedStylesConfig, AreaStyle, AxisBasedMarkersConfig, AxisBasedScalesConfig<TData>>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { AreaStyle } from '../styling';
|
|
2
|
+
import type { AxisPlotState, AxisPlotCtx, ProcessedRow } from '../../phases';
|
|
3
|
+
import type { AxisValue } from '../../layout/mapping';
|
|
4
|
+
/** BoxPlot's pipeline state: the shared axis-based state, with no extras of its own. */
|
|
5
|
+
export type BoxPlotState<TData extends Record<string, unknown>> = AxisPlotState<TData, AreaStyle>;
|
|
6
|
+
/** The environment every BoxPlot phase reads from, alongside the growing state. */
|
|
7
|
+
export type BoxPlotCtx = AxisPlotCtx;
|
|
8
|
+
/**
|
|
9
|
+
* One box's summary - what a hovered box reads as: its category, its
|
|
10
|
+
* quartiles, the whisker ends (the farthest values within 1.5 IQR of the
|
|
11
|
+
* box) and the values past them. It also keeps the series tags and, when the
|
|
12
|
+
* category channel is a field key, the category under that field.
|
|
13
|
+
*/
|
|
14
|
+
export type BoxSummaryRow = ProcessedRow & {
|
|
15
|
+
category: AxisValue;
|
|
16
|
+
min: number;
|
|
17
|
+
p25: number;
|
|
18
|
+
median: number;
|
|
19
|
+
p75: number;
|
|
20
|
+
max: number;
|
|
21
|
+
count: number;
|
|
22
|
+
outliers: number[];
|
|
23
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -48,8 +48,10 @@ export type HoverConfig<TRow extends Record<string, unknown>> = {
|
|
|
48
48
|
strategy?: HoverStrategy;
|
|
49
49
|
/** Resolved sync flag for the *local* hover store (ignored once a shared store is in scope). */
|
|
50
50
|
sync?: boolean;
|
|
51
|
+
/** The resolved tooltip: its `format`, and the order its text reads the rows in (a `content` snippet gets them as matched). */
|
|
51
52
|
tooltip?: TooltipConfig<TRow> & {
|
|
52
53
|
format: (row: TRow) => string;
|
|
54
|
+
sort?: (a: TRow, b: TRow) => number;
|
|
53
55
|
};
|
|
54
56
|
/** When it returns `true`, that pointer event is treated as a leave instead of a move - lets a nested layout opt part of the plot out of hover matching. */
|
|
55
57
|
exclude?: (e: PointerEvent) => boolean;
|
package/docs/chrome.md
CHANGED
|
@@ -2,20 +2,30 @@
|
|
|
2
2
|
|
|
3
3
|
The chrome is everything a figure draws around its plot: title, subtitle, legend, timeline player, caption. Every plot and every [composer](composer.md) takes the same chrome props.
|
|
4
4
|
|
|
5
|
+
<!-- example: examples/Chrome.svelte -->
|
|
6
|
+
|
|
5
7
|
```svelte
|
|
8
|
+
<script lang="ts">
|
|
9
|
+
import { LinePlot } from '@fundar/data-chart-telling';
|
|
10
|
+
import { PRICES } from './data';
|
|
11
|
+
</script>
|
|
12
|
+
|
|
13
|
+
<!-- A plot carries its own chrome: title, subtitle, legend and caption. -->
|
|
6
14
|
<LinePlot
|
|
7
|
-
{
|
|
8
|
-
{
|
|
9
|
-
title="
|
|
10
|
-
subtitle="
|
|
11
|
-
caption="Source:
|
|
12
|
-
data={
|
|
15
|
+
width={720}
|
|
16
|
+
height={440}
|
|
17
|
+
title="Commodity prices"
|
|
18
|
+
subtitle="Price index, January 2021 = 100"
|
|
19
|
+
caption="Source: IMF, Primary Commodity Prices"
|
|
20
|
+
data={PRICES}
|
|
13
21
|
x="date"
|
|
14
22
|
y="index"
|
|
15
|
-
color="
|
|
23
|
+
color="commodity"
|
|
16
24
|
/>
|
|
17
25
|
```
|
|
18
26
|
|
|
27
|
+
<!-- /example -->
|
|
28
|
+
|
|
19
29
|

|
|
20
30
|
|
|
21
31
|
| Prop | Type | Meaning |
|
|
@@ -55,10 +65,31 @@ A `header` takes the place of `title` and `subtitle`, and a `footer` the place o
|
|
|
55
65
|
- `'divider'` draws a line between the two blocks around it, and only when both are drawn. Several dividers may appear.
|
|
56
66
|
- A row puts two or three blocks side by side: `'(plot, timeline)'`, `'(legend, plot)'`. The `plot` takes the width the others leave; a row without `plot` shares its width among the blocks.
|
|
57
67
|
|
|
68
|
+
<!-- example: examples/ChromeOrder.svelte -->
|
|
69
|
+
|
|
58
70
|
```svelte
|
|
59
|
-
<
|
|
71
|
+
<script lang="ts">
|
|
72
|
+
import { LinePlot } from '@fundar/data-chart-telling';
|
|
73
|
+
import { PRICES } from './data';
|
|
74
|
+
</script>
|
|
75
|
+
|
|
76
|
+
<!-- `order` places the chrome blocks; `divider` draws a rule between them. -->
|
|
77
|
+
<LinePlot
|
|
78
|
+
width={720}
|
|
79
|
+
height={460}
|
|
80
|
+
title="Commodity prices"
|
|
81
|
+
subtitle="Price index"
|
|
82
|
+
caption="Source: IMF, Primary Commodity Prices"
|
|
83
|
+
data={PRICES}
|
|
84
|
+
x="date"
|
|
85
|
+
y="index"
|
|
86
|
+
color="commodity"
|
|
87
|
+
order={['title', 'subtitle', 'divider', 'legend', 'plot', 'divider', 'caption']}
|
|
88
|
+
/>
|
|
60
89
|
```
|
|
61
90
|
|
|
91
|
+
<!-- /example -->
|
|
92
|
+
|
|
62
93
|

|
|
63
94
|
|
|
64
95
|
The default order, from the configuration's `chrome.order`: `header`, `title`, `subtitle`, `divider`, `legend`, `plot`, `timeline`, `footer`, `caption`. It can be changed globally with `setConfiguration({ chrome: { order: [...] } })`.
|
package/docs/composer.md
CHANGED
|
@@ -2,20 +2,36 @@
|
|
|
2
2
|
|
|
3
3
|
`PlotComposer` puts several plots in one figure. It is a figure like any plot: it takes the same [chrome](chrome.md) (`title`, `subtitle`, `caption`, `legend`, `order`) and a `width` and `height`, and lays its children out on a grid.
|
|
4
4
|
|
|
5
|
+
<!-- example: examples/Composer.svelte -->
|
|
6
|
+
|
|
5
7
|
```svelte
|
|
8
|
+
<script lang="ts">
|
|
9
|
+
import { PlotComposer, LinePlot, BarPlot, ScatterPlot } from '@fundar/data-chart-telling';
|
|
10
|
+
import { REGION_VALUES, PROVINCE_VALUES, COUNTRIES } from './data';
|
|
11
|
+
</script>
|
|
12
|
+
|
|
13
|
+
<!-- Several plots in one figure, placed on named areas of a grid. -->
|
|
6
14
|
<PlotComposer
|
|
7
15
|
width={900}
|
|
8
16
|
height={560}
|
|
9
17
|
title="Three views, one figure"
|
|
18
|
+
legend={false}
|
|
10
19
|
tags="A"
|
|
11
20
|
layout={{ areas: ['a a b', 'a a c'] }}
|
|
12
21
|
>
|
|
13
|
-
<LinePlot area="a" data={
|
|
14
|
-
<BarPlot
|
|
15
|
-
|
|
22
|
+
<LinePlot area="a" data={REGION_VALUES} x="date" y="value" color="region" tooltip />
|
|
23
|
+
<BarPlot
|
|
24
|
+
area="b"
|
|
25
|
+
data={PROVINCE_VALUES.filter((row) => row.sex === 'female').slice(0, 3)}
|
|
26
|
+
x="province"
|
|
27
|
+
y="value"
|
|
28
|
+
/>
|
|
29
|
+
<ScatterPlot area="c" data={COUNTRIES} x="gdp" y="life" />
|
|
16
30
|
</PlotComposer>
|
|
17
31
|
```
|
|
18
32
|
|
|
33
|
+
<!-- /example -->
|
|
34
|
+
|
|
19
35
|

|
|
20
36
|
|
|
21
37
|
Plots drawn straight inside the composer take a cell of the grid and draw at the cell's size - they need no `width` or `height`.
|
package/docs/concepts.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
## Plots and figures
|
|
4
4
|
|
|
5
|
-
A **plot** is one graph: axes and marks. Every plot is a component named after what it draws - `LinePlot`, `AreaPlot`, `BarPlot`, `HistogramPlot`, `HeatmapPlot`, `PyramidPlot`, `ScatterPlot`, `GeoPlot` (see [Plots](plots.md)).
|
|
5
|
+
A **plot** is one graph: axes and marks. Every plot is a component named after what it draws - `LinePlot`, `AreaPlot`, `BarPlot`, `HistogramPlot`, `BoxPlot`, `HeatmapPlot`, `PyramidPlot`, `ScatterPlot`, `GeoPlot` (see [Plots](plots.md)).
|
|
6
6
|
|
|
7
7
|
A plot is drawn inside a **figure**: the box that holds the plot together with its [chrome](chrome.md) (title, subtitle, legend, timeline, caption). Every plot component is a figure, so a plot with a `title` and a `legend` is still one component. A [`PlotComposer`](composer.md) is a figure that holds several plots.
|
|
8
8
|
|
package/docs/development.md
CHANGED
|
@@ -35,7 +35,7 @@ pnpm test:stories # every story as a test (boots Storybook
|
|
|
35
35
|
|
|
36
36
|
- Component tests must end in `.svelte.test.ts`; that suffix is what routes a file to the browser project.
|
|
37
37
|
- Browser tests render with `vitest-browser-svelte`. A dot renders as a `<path>`, not a `<circle>`; styles live in the `style` attribute.
|
|
38
|
-
- `tests/unit/docs` and `tests/unit/examples.svelte.test.ts` guard the LLM-facing docs: every example mounts,
|
|
38
|
+
- `tests/unit/docs` and `tests/unit/examples.svelte.test.ts` guard the LLM-facing docs: every example mounts, the links and exports named in `llms.txt` and `llms-full.txt` exist, and the example snippets in the Markdown match their files.
|
|
39
39
|
|
|
40
40
|
## Storybook
|
|
41
41
|
|
|
@@ -53,14 +53,23 @@ Every option has a story under `stories/`, grouped as Plots, Markers, Transforms
|
|
|
53
53
|
|
|
54
54
|
## Documentation figures
|
|
55
55
|
|
|
56
|
-
Every screenshot in the docs is
|
|
56
|
+
Every screenshot in the docs is an example, rendered by Playwright to `docs/images/<name>.png` in kebab-case (`examples/LineByRegion.svelte` is `line-by-region.png`). The examples and the screenshots share one file of small real datasets, `examples/data.ts`, which names the source of each, and axes are set to English. `docs/images` is left out of the npm package.
|
|
57
57
|
|
|
58
58
|
```bash
|
|
59
|
-
pnpm docs:images
|
|
60
|
-
pnpm docs:images tooltip scales # only these
|
|
59
|
+
pnpm docs:images # regenerate docs/images/*.png
|
|
60
|
+
pnpm docs:images tooltip scales # only these
|
|
61
61
|
```
|
|
62
62
|
|
|
63
|
-
|
|
63
|
+
`scripts/docs-figures/figures.json` lists the examples that get no screenshot (`skip`) and where the pointer rests before a shot (`meta`, e.g. `{ "hover": { "x": 330, "y": 160 } }`). The maps are the exception: their GeoJSON is too large to ship in `examples/`, so they are components of their own in `scripts/docs-figures/figures/`, importing it from `stories/`, and their snippets in `geo.md` and `styles-and-segments.md` are written by hand.
|
|
64
|
+
|
|
65
|
+
The Markdown shows each example's source between `<!-- example: examples/Name.svelte -->` and `<!-- /example -->`:
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
pnpm docs:examples # copy each example's source into README.md and docs/*.md
|
|
69
|
+
pnpm docs:examples --check # fail when a block is out of date
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
`tests/unit/docs/examples-sync.test.ts` runs the check, so a snippet never drifts from its example or its screenshot.
|
|
64
73
|
|
|
65
74
|
## Continuous integration
|
|
66
75
|
|
package/docs/facets.md
CHANGED
|
@@ -2,18 +2,29 @@
|
|
|
2
2
|
|
|
3
3
|
`facet` splits a plot's rows into small multiples: one panel per value of a field, each drawn with the same plot configuration.
|
|
4
4
|
|
|
5
|
+
<!-- example: examples/Facets.svelte -->
|
|
6
|
+
|
|
5
7
|
```svelte
|
|
8
|
+
<script lang="ts">
|
|
9
|
+
import { LinePlot } from '@fundar/data-chart-telling';
|
|
10
|
+
import { REGION_VALUES } from './data';
|
|
11
|
+
</script>
|
|
12
|
+
|
|
13
|
+
<!-- One panel per region, three columns, sharing scales. A tooltip with `sync` shows in every panel on purpose. -->
|
|
6
14
|
<LinePlot
|
|
7
|
-
{
|
|
8
|
-
{
|
|
9
|
-
data={
|
|
10
|
-
x="
|
|
15
|
+
width={900}
|
|
16
|
+
height={360}
|
|
17
|
+
data={REGION_VALUES}
|
|
18
|
+
x="date"
|
|
11
19
|
y="value"
|
|
12
|
-
facet={{ by: 'region', columns: 3, gap: 12 }}
|
|
20
|
+
facet={{ by: 'region', columns: 3, gap: 12, scales: 'shared' }}
|
|
21
|
+
tooltip={{ sync: true }}
|
|
13
22
|
/>
|
|
14
23
|
```
|
|
15
24
|
|
|
16
|
-
|
|
25
|
+
<!-- /example -->
|
|
26
|
+
|
|
27
|
+

|
|
17
28
|
|
|
18
29
|
The data phases (grouping, transforms, statistics, shared scales) run once for all panels; each panel then lays itself out in its own box. Panels share legend toggles and hover, so one legend controls them all.
|
|
19
30
|
|
|
@@ -36,20 +47,63 @@ The data phases (grouping, transforms, statistics, shared scales) run once for a
|
|
|
36
47
|
| `scales` | `'shared'` (default), `'free'`, `'free-x'`, `'free-y'` |
|
|
37
48
|
| `axes` | `'all'` (default) draws every panel's axes; `'outer'` draws only the left column's y and the bottom row's x, with panels resized to equal boxes |
|
|
38
49
|
|
|
50
|
+
<!-- example: examples/FacetsCarousel.svelte -->
|
|
51
|
+
|
|
39
52
|
```svelte
|
|
40
|
-
<
|
|
53
|
+
<script lang="ts">
|
|
54
|
+
import { LinePlot } from '@fundar/data-chart-telling';
|
|
55
|
+
import { REGION_VALUES } from './data';
|
|
56
|
+
</script>
|
|
57
|
+
|
|
58
|
+
<!-- A carousel shows `visible` panels at a time, with navigation below. -->
|
|
59
|
+
<LinePlot
|
|
60
|
+
width={720}
|
|
61
|
+
height={340}
|
|
62
|
+
data={REGION_VALUES}
|
|
63
|
+
x="date"
|
|
64
|
+
y="value"
|
|
65
|
+
facet={{ by: 'region', columns: 3, mode: 'carousel', visible: 2, navLayout: 'below' }}
|
|
66
|
+
/>
|
|
41
67
|
```
|
|
42
68
|
|
|
69
|
+
<!-- /example -->
|
|
70
|
+
|
|
43
71
|

|
|
44
72
|
|
|
45
73
|
## Rows by columns
|
|
46
74
|
|
|
47
75
|
`{ rows, cols }` makes one panel per combination of a row field and a column field, with each value labelled. Combinations without data stay empty. `rowValues` and `colValues` choose and order the values. A free `x` scale is shared down each column and a free `y` along each row.
|
|
48
76
|
|
|
77
|
+
<!-- example: examples/FacetsRowsCols.svelte -->
|
|
78
|
+
|
|
49
79
|
```svelte
|
|
50
|
-
<
|
|
80
|
+
<script lang="ts">
|
|
81
|
+
import { LinePlot } from '@fundar/data-chart-telling';
|
|
82
|
+
import { REGION_VALUES } from './data';
|
|
83
|
+
|
|
84
|
+
// Each region's value split into two sectors.
|
|
85
|
+
const SECTORS = [
|
|
86
|
+
{ sector: 'Goods', share: 0.8 },
|
|
87
|
+
{ sector: 'Services', share: 1.4 }
|
|
88
|
+
];
|
|
89
|
+
const rows = REGION_VALUES.flatMap((row) =>
|
|
90
|
+
SECTORS.map(({ sector, share }) => ({ ...row, sector, value: row.value * share }))
|
|
91
|
+
);
|
|
92
|
+
</script>
|
|
93
|
+
|
|
94
|
+
<!-- One panel per region and sector; `scales: 'free'` fits each panel to its own data. -->
|
|
95
|
+
<LinePlot
|
|
96
|
+
width={720}
|
|
97
|
+
height={420}
|
|
98
|
+
data={rows}
|
|
99
|
+
x="date"
|
|
100
|
+
y="value"
|
|
101
|
+
facet={{ rows: 'region', cols: 'sector', scales: 'free' }}
|
|
102
|
+
/>
|
|
51
103
|
```
|
|
52
104
|
|
|
105
|
+
<!-- /example -->
|
|
106
|
+
|
|
53
107
|

|
|
54
108
|
|
|
55
109
|
## Alignment
|
package/docs/getting-started.md
CHANGED
|
@@ -13,24 +13,23 @@ pnpm add @fundar/data-chart-telling svelte svelteplot
|
|
|
13
13
|
|
|
14
14
|
## A first plot
|
|
15
15
|
|
|
16
|
+
<!-- example: examples/FirstPlot.svelte -->
|
|
17
|
+
|
|
16
18
|
```svelte
|
|
17
19
|
<script lang="ts">
|
|
18
20
|
import { LinePlot } from '@fundar/data-chart-telling';
|
|
19
|
-
|
|
20
|
-
const rows = [
|
|
21
|
-
{ year: 2020, region: 'North', value: 3.1 },
|
|
22
|
-
{ year: 2021, region: 'North', value: 3.8 },
|
|
23
|
-
{ year: 2020, region: 'South', value: 2.2 },
|
|
24
|
-
{ year: 2021, region: 'South', value: 2.9 }
|
|
25
|
-
];
|
|
21
|
+
import { REGION_VALUES } from './data';
|
|
26
22
|
</script>
|
|
27
23
|
|
|
28
|
-
|
|
24
|
+
<!-- The smallest useful plot: rows, the fields to map and a pixel size. -->
|
|
25
|
+
<LinePlot width={720} height={400} data={REGION_VALUES} x="date" y="value" color="region" />
|
|
29
26
|
```
|
|
30
27
|
|
|
28
|
+
<!-- /example -->
|
|
29
|
+
|
|
31
30
|

|
|
32
31
|
|
|
33
|
-
- `data` is an array of flat rows; `x` and `y` name the fields that position each row.
|
|
32
|
+
- `data` is an array of flat rows, here `{ date, region, value }` from [examples/data.ts](../examples/data.ts); `x` and `y` name the fields that position each row.
|
|
34
33
|
- `color="region"` splits the rows into one series per region and gives each its own color.
|
|
35
34
|
- `width` and `height` are pixels.
|
|
36
35
|
|
package/docs/legend.md
CHANGED
|
@@ -37,11 +37,39 @@ Segments and markers join the series section when they carry a `legend` label:
|
|
|
37
37
|
|
|
38
38
|
Items become clickable with `interactive` on the series section. `disabled` says what a hidden series becomes:
|
|
39
39
|
|
|
40
|
+
<!-- example: examples/LegendToggle.svelte -->
|
|
41
|
+
|
|
40
42
|
```svelte
|
|
41
|
-
<
|
|
43
|
+
<script lang="ts">
|
|
44
|
+
import { LinePlot } from '@fundar/data-chart-telling';
|
|
45
|
+
import { REGION_VALUES } from './data';
|
|
46
|
+
import LegendReader from './LegendReader.svelte';
|
|
47
|
+
</script>
|
|
48
|
+
|
|
49
|
+
<!--
|
|
50
|
+
The automatic legend can toggle series. `mode: 'omit'` drops a hidden series
|
|
51
|
+
and refits the axes to what is left; 'dim' and 'hide' keep the axes.
|
|
52
|
+
Anything inside the figure (here, the footer) reads the toggles through
|
|
53
|
+
`useFigure().legend` - see LegendReader.
|
|
54
|
+
-->
|
|
55
|
+
<LinePlot
|
|
56
|
+
width={720}
|
|
57
|
+
height={400}
|
|
58
|
+
data={REGION_VALUES}
|
|
59
|
+
x="date"
|
|
60
|
+
y="value"
|
|
61
|
+
color="region"
|
|
62
|
+
legend={{ series: { interactive: { enabled: true, disabled: { mode: 'omit' } } } }}
|
|
63
|
+
>
|
|
64
|
+
{#snippet footer()}
|
|
65
|
+
<LegendReader />
|
|
66
|
+
{/snippet}
|
|
67
|
+
</LinePlot>
|
|
42
68
|
```
|
|
43
69
|
|
|
44
|
-
|
|
70
|
+
<!-- /example -->
|
|
71
|
+
|
|
72
|
+

|
|
45
73
|
|
|
46
74
|
| `disabled.mode` | A hidden series... |
|
|
47
75
|
| ----------------- | ------------------------------------------------------------------------------------- |
|
|
@@ -77,21 +105,40 @@ Each channel key adjusts the section that channel draws: any section field excep
|
|
|
77
105
|
|
|
78
106
|
A list replaces the automatic legend with exactly those sections:
|
|
79
107
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
}
|
|
91
|
-
{
|
|
92
|
-
|
|
108
|
+
<!-- example: examples/LegendSections.svelte -->
|
|
109
|
+
|
|
110
|
+
```svelte
|
|
111
|
+
<script lang="ts">
|
|
112
|
+
import { LinePlot } from '@fundar/data-chart-telling';
|
|
113
|
+
import { REGION_VALUES } from './data';
|
|
114
|
+
</script>
|
|
115
|
+
|
|
116
|
+
<!-- An explicit legend: a discrete section of toggles and a continuous ramp. -->
|
|
117
|
+
<LinePlot
|
|
118
|
+
width={720}
|
|
119
|
+
height={400}
|
|
120
|
+
data={REGION_VALUES}
|
|
121
|
+
x="date"
|
|
122
|
+
y="value"
|
|
123
|
+
color="region"
|
|
124
|
+
legend={[
|
|
125
|
+
{
|
|
126
|
+
type: 'discrete',
|
|
127
|
+
title: 'Region',
|
|
128
|
+
interactive: { enabled: true },
|
|
129
|
+
items: [
|
|
130
|
+
{ name: 'GBA', color: '#4f46e5', symbol: 'line' },
|
|
131
|
+
{ name: 'Cuyo', color: '#e6580d', symbol: 'line' },
|
|
132
|
+
{ name: 'Northwest', color: '#16a34a', symbol: 'line' }
|
|
133
|
+
]
|
|
134
|
+
},
|
|
135
|
+
{ type: 'continuous', title: 'Share', min: 0, max: 100, scheme: 'blues' }
|
|
136
|
+
]}
|
|
137
|
+
/>
|
|
93
138
|
```
|
|
94
139
|
|
|
140
|
+
<!-- /example -->
|
|
141
|
+
|
|
95
142
|

|
|
96
143
|
|
|
97
144
|
Section types:
|
package/docs/markers.md
CHANGED
|
@@ -2,29 +2,40 @@
|
|
|
2
2
|
|
|
3
3
|
`markers` adds marks to a plot beyond the ones its kind draws: annotations, regions, derived shapes, hover highlights, or any Svelte component. It is an array of objects; `type` picks the marker and the other fields configure it.
|
|
4
4
|
|
|
5
|
+
<!-- example: examples/Markers.svelte -->
|
|
6
|
+
|
|
5
7
|
```svelte
|
|
8
|
+
<script lang="ts">
|
|
9
|
+
import { LinePlot } from '@fundar/data-chart-telling';
|
|
10
|
+
import { LIFE } from './data';
|
|
11
|
+
</script>
|
|
12
|
+
|
|
13
|
+
<!-- Markers add marks to a plot: a hover highlight, a reference rule and a delta between two series. -->
|
|
6
14
|
<LinePlot
|
|
7
|
-
{
|
|
8
|
-
{
|
|
9
|
-
data={
|
|
15
|
+
width={720}
|
|
16
|
+
height={400}
|
|
17
|
+
data={LIFE}
|
|
10
18
|
x="year"
|
|
11
19
|
y="value"
|
|
12
20
|
color="sex"
|
|
21
|
+
scales={{ x: { edgeTicks: true } }}
|
|
13
22
|
markers={[
|
|
14
23
|
{ type: 'hover' },
|
|
15
|
-
{ type: 'rule', axis: 'x', value:
|
|
24
|
+
{ type: 'rule', axis: 'x', value: 2001, styles: { stroke: '#999', strokeDasharray: '4 4' } },
|
|
16
25
|
{
|
|
17
26
|
type: 'delta',
|
|
18
27
|
axis: 'x',
|
|
19
|
-
at:
|
|
28
|
+
at: 1980,
|
|
20
29
|
from: { type: 'series', series: 'female' },
|
|
21
30
|
to: { type: 'series', series: 'male' },
|
|
22
|
-
formatDiff: (
|
|
31
|
+
formatDiff: (difference) => difference.toFixed(1)
|
|
23
32
|
}
|
|
24
33
|
]}
|
|
25
34
|
/>
|
|
26
35
|
```
|
|
27
36
|
|
|
37
|
+
<!-- /example -->
|
|
38
|
+
|
|
28
39
|

|
|
29
40
|
|
|
30
41
|
Markers draw in array order. Type a markers array with `satisfies AxisBasedMarkersConfig[]` (or `GeoMarkersConfig[]`) to get completion.
|