@fundar/data-chart-telling 1.11.0 → 1.12.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
CHANGED
|
@@ -2,6 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project are documented here. The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and the project adheres to [Semantic Versioning](https://semver.org/).
|
|
4
4
|
|
|
5
|
+
## [1.12.0] - 2026-11-04
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- `labels` on a discrete legend section (`legend.series.labels`, or on an explicit section): `prefix`, `suffix` and `replace` rewrite the label of every item - to add a unit (`' years'`) or shorten a word (`{ Quintile: 'Q' }`, or `[[/^Quartile /, 'Q: ']]`) - without removing the automatic items to write them again. The names are left as they are, so turning series off still goes by them.
|
|
10
|
+
|
|
5
11
|
## [1.11.0] - 2026-11-04
|
|
6
12
|
|
|
7
13
|
### Changed
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
} from '../../configuration/config.svelte';
|
|
14
14
|
import { HoverState } from '../hover/HoverState.svelte';
|
|
15
15
|
import { LegendState } from '../legend/LegendState.svelte';
|
|
16
|
+
import { LegendLabels } from '../legend/LegendLabels';
|
|
16
17
|
import type { HoverStrategy } from '../../types/layout/hover';
|
|
17
18
|
import type { FigureBoxProps, PlotChromeConfig } from '../../types/plots/props';
|
|
18
19
|
import type { PlotBoxSnippet } from '../../types/layout/figure';
|
|
@@ -112,11 +113,14 @@
|
|
|
112
113
|
: 'auto'
|
|
113
114
|
);
|
|
114
115
|
const sections = $derived(
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
116
|
+
LegendLabels.apply(
|
|
117
|
+
legendMode === 'explicit'
|
|
118
|
+
? legendState.colorize(legend as LegendSection[])
|
|
119
|
+
: legendMode === 'auto'
|
|
120
|
+
? legendState.sections(extension)
|
|
121
|
+
: [],
|
|
122
|
+
cfg
|
|
123
|
+
)
|
|
120
124
|
);
|
|
121
125
|
|
|
122
126
|
/**
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { PlotStylesConfig } from '../../types/configuration/styling';
|
|
2
|
+
import type { LegendLabels as LegendLabelsConfig, LegendSection } from '../../types/layout/legend';
|
|
3
|
+
/** How a discrete section rewrites the labels its items show: swaps, then a prefix and a suffix. */
|
|
4
|
+
export declare class LegendLabels {
|
|
5
|
+
/**
|
|
6
|
+
* `sections`, with the labels of every discrete section that sets `labels` rewritten. An item is
|
|
7
|
+
* rewritten from the text it would show - its `label`, else its name as the legend writes it.
|
|
8
|
+
*/
|
|
9
|
+
static apply(sections: LegendSection[], cfg: Pick<PlotStylesConfig, 'legend'>): LegendSection[];
|
|
10
|
+
/** `text` after `labels`' swaps, in order (each replaces every match), with its prefix and suffix around it. */
|
|
11
|
+
static rewrite(text: string, labels: LegendLabelsConfig): string;
|
|
12
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { LegendText } from './LegendText';
|
|
2
|
+
/** How a discrete section rewrites the labels its items show: swaps, then a prefix and a suffix. */
|
|
3
|
+
export class LegendLabels {
|
|
4
|
+
/**
|
|
5
|
+
* `sections`, with the labels of every discrete section that sets `labels` rewritten. An item is
|
|
6
|
+
* rewritten from the text it would show - its `label`, else its name as the legend writes it.
|
|
7
|
+
*/
|
|
8
|
+
static apply(sections, cfg) {
|
|
9
|
+
return sections.map((section) => {
|
|
10
|
+
if (section.type !== 'discrete' || !section.labels)
|
|
11
|
+
return section;
|
|
12
|
+
const { labels } = section;
|
|
13
|
+
return {
|
|
14
|
+
...section,
|
|
15
|
+
items: section.items.map((item) => ({
|
|
16
|
+
...item,
|
|
17
|
+
label: LegendLabels.rewrite(item.label || LegendText.of(item.name, cfg), labels)
|
|
18
|
+
}))
|
|
19
|
+
};
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
/** `text` after `labels`' swaps, in order (each replaces every match), with its prefix and suffix around it. */
|
|
23
|
+
static rewrite(text, labels) {
|
|
24
|
+
const swaps = Array.isArray(labels.replace)
|
|
25
|
+
? labels.replace
|
|
26
|
+
: Object.entries(labels.replace ?? {});
|
|
27
|
+
let result = text;
|
|
28
|
+
for (const [pattern, replacement] of swaps)
|
|
29
|
+
result =
|
|
30
|
+
typeof pattern === 'string'
|
|
31
|
+
? result.replaceAll(pattern, () => replacement)
|
|
32
|
+
: result.replace(pattern.global ? pattern : new RegExp(pattern.source, `${pattern.flags}g`), replacement);
|
|
33
|
+
return `${labels.prefix ?? ''}${result}${labels.suffix ?? ''}`;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -89,11 +89,27 @@ export type DiscreteInteractiveConfig = {
|
|
|
89
89
|
/** Shows the symbol icon inside a `'badge'` pill. Defaults to `true`; ignored by `'basic'`, which always shows it. */
|
|
90
90
|
showIcon?: boolean;
|
|
91
91
|
};
|
|
92
|
+
/**
|
|
93
|
+
* How a discrete section rewrites the label of each of its items, to add a unit or shorten a word without
|
|
94
|
+
* writing the items again. `replace` goes first, then `prefix` and `suffix` go around the result.
|
|
95
|
+
*/
|
|
96
|
+
export type LegendLabels = {
|
|
97
|
+
prefix?: string;
|
|
98
|
+
suffix?: string;
|
|
99
|
+
/**
|
|
100
|
+
* What to swap in every label, in order: a text for the text that replaces it (every match), or a list of
|
|
101
|
+
* `[pattern, replacement]` pairs where the pattern may be a regular expression - `{ Quintile: 'Q' }`,
|
|
102
|
+
* `[[/^Quartile /, 'Q: ']]`.
|
|
103
|
+
*/
|
|
104
|
+
replace?: Record<string, string> | [string | RegExp, string][];
|
|
105
|
+
};
|
|
92
106
|
export type DiscreteLegendSection = {
|
|
93
107
|
type: 'discrete';
|
|
94
108
|
enabled?: boolean;
|
|
95
109
|
title?: string;
|
|
96
110
|
items: LegendItem[];
|
|
111
|
+
/** Rewrites the label of each item - see {@link LegendLabels}. */
|
|
112
|
+
labels?: LegendLabels;
|
|
97
113
|
columns?: number;
|
|
98
114
|
/** Item flow direction. Defaults to horizontal for a lone section, vertical once there's more than one. */
|
|
99
115
|
layout?: LegendItemsLayout;
|
package/docs/legend.md
CHANGED
|
@@ -118,6 +118,18 @@ legend={{ color: { match: 'temperature', enabled: false } }} // not that ramp
|
|
|
118
118
|
legend={{ removeItems: ['2017/18'] }}
|
|
119
119
|
```
|
|
120
120
|
|
|
121
|
+
### Rewriting the labels
|
|
122
|
+
|
|
123
|
+
`labels` on a discrete section rewrites the label of every item without writing the items again - to add a unit or shorten a word. Each is optional; `replace` goes first, then `prefix` and `suffix` go around the result. An item is rewritten from the text it would show: its `label`, else its name as the legend writes it.
|
|
124
|
+
|
|
125
|
+
```ts
|
|
126
|
+
legend={{ series: { labels: { suffix: ' years' } } }} // 25-29 → 25-29 years
|
|
127
|
+
legend={{ series: { labels: { replace: { Quintile: 'Q' }, suffix: '%' } } }} // Quintile 1 → Q 1%
|
|
128
|
+
legend={{ series: { labels: { replace: [[/^Quartile /, 'Q: ']] } } }} // Quartile 3 → Q: 3 (a pattern, in order)
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
`replace` is a record of text → replacement (every match; a text with spaces or dashes is a quoted key, `{ 'Quintile ': 'Q: ' }`), or a list of `[pattern, replacement]` pairs where the pattern may be a regular expression. The names are left as they are, so `off` and the toggles still go by them. An explicit section takes `labels` too.
|
|
132
|
+
|
|
121
133
|
### Turning series off from code
|
|
122
134
|
|
|
123
135
|
`off` turns series off as if the reader had clicked them, without a click: a list of names (each dimmed, to `legend.disabledOpacity`), or a name → opacity or name → action map, where an action is the same `{ mode: 'dim' | 'hide' | 'omit' }` a toggle does. `highlight` is its inverse: it keeps the series it names on and dims all the others. An `off` entry for a series wins over `highlight`.
|