@internetstiftelsen/charts 0.12.0 → 0.13.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/README.md +10 -1
- package/dist/base-chart.d.ts +12 -1
- package/dist/base-chart.js +58 -18
- package/dist/chart-group.d.ts +15 -2
- package/dist/chart-group.js +181 -45
- package/dist/chart-interface.d.ts +3 -3
- package/dist/grouped-data.d.ts +1 -0
- package/dist/grouped-data.js +80 -40
- package/dist/grouped-tabular.js +12 -3
- package/dist/layout-manager.js +4 -4
- package/dist/text.d.ts +40 -0
- package/dist/text.js +217 -0
- package/dist/theme.js +56 -0
- package/dist/title.d.ts +6 -12
- package/dist/title.js +29 -82
- package/dist/types.d.ts +34 -1
- package/docs/chart-group.md +24 -5
- package/docs/components.md +99 -15
- package/docs/donut-chart.md +2 -1
- package/docs/gauge-chart.md +2 -1
- package/docs/getting-started.md +1 -1
- package/docs/pie-chart.md +2 -1
- package/docs/theming.md +35 -0
- package/docs/word-cloud-chart.md +1 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -198,6 +198,7 @@ import { XYChart } from '@internetstiftelsen/charts/xy-chart';
|
|
|
198
198
|
import { Line } from '@internetstiftelsen/charts/line';
|
|
199
199
|
import { Bar } from '@internetstiftelsen/charts/bar';
|
|
200
200
|
import { Legend } from '@internetstiftelsen/charts/legend';
|
|
201
|
+
import { Text } from '@internetstiftelsen/charts/text';
|
|
201
202
|
import { Title } from '@internetstiftelsen/charts/title';
|
|
202
203
|
|
|
203
204
|
const lineChart = new XYChart({ data: lineData });
|
|
@@ -215,6 +216,14 @@ const group = new ChartGroup({
|
|
|
215
216
|
|
|
216
217
|
group
|
|
217
218
|
.addChild(new Title({ text: 'Revenue vs Expenses' }))
|
|
219
|
+
.addChild(
|
|
220
|
+
new Text({
|
|
221
|
+
text: 'Source: finance team',
|
|
222
|
+
position: 'bottom',
|
|
223
|
+
variant: 'caption',
|
|
224
|
+
align: 'left',
|
|
225
|
+
}),
|
|
226
|
+
)
|
|
218
227
|
.addChart(barChart)
|
|
219
228
|
.addChart(lineChart)
|
|
220
229
|
.addChild(new Legend());
|
|
@@ -426,7 +435,7 @@ Grouped parsing rules:
|
|
|
426
435
|
- [DonutChart](./docs/donut-chart.md) - Donut/pie charts API
|
|
427
436
|
- [PieChart](./docs/pie-chart.md) - Pie chart API
|
|
428
437
|
- [GaugeChart](./docs/gauge-chart.md) - Gauge chart API
|
|
429
|
-
- [Components](./docs/components.md) - Axes, Grid, Tooltip, Legend, Title
|
|
438
|
+
- [Components](./docs/components.md) - Axes, Grid, Tooltip, Legend, Text, Title
|
|
430
439
|
- [Theming](./docs/theming.md) - Colors, fonts, and styling
|
|
431
440
|
|
|
432
441
|
## Browser Support
|
package/dist/base-chart.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type Selection } from 'd3';
|
|
2
|
-
import type { ChartData, DataItem, ChartTheme, ResolvedChartTheme, AxisScaleConfig, ExportFormat, ExportOptions, D3Scale, ExportHookContext, ExportRenderContext, LegendItem, LegendMode, LegendSeries, ResponsiveConfig, ResponsiveRenderContext, DeepPartial } from './types.js';
|
|
2
|
+
import type { ChartData, DataItem, ChartTheme, ResolvedChartTheme, AxisScaleConfig, ExportFormat, ExportOptions, D3Scale, ExportHookContext, ExportRenderContext, LegendItem, LegendMode, LegendSeries, ResponsiveConfig, ResponsiveRenderContext, DeepPartial, TextPosition } from './types.js';
|
|
3
3
|
import type { ChartComponentBase, LayoutAwareComponentBase } from './chart-interface.js';
|
|
4
4
|
import type { XAxis } from './x-axis.js';
|
|
5
5
|
import type { YAxis } from './y-axis.js';
|
|
@@ -75,6 +75,13 @@ type ComponentSlot<TComponent extends ChartComponentBase = ChartComponentBase> =
|
|
|
75
75
|
set: (component: TComponent | null) => void;
|
|
76
76
|
onRegister?: (component: TComponent) => void;
|
|
77
77
|
};
|
|
78
|
+
type TextLayoutComponent = LayoutAwareComponentBase & ChartComponentBase & {
|
|
79
|
+
display: boolean;
|
|
80
|
+
text: string;
|
|
81
|
+
position: TextPosition;
|
|
82
|
+
variant: string;
|
|
83
|
+
render: (svg: Selection<SVGSVGElement, undefined, null, undefined>, theme: ChartTheme, width: number, x?: number, y?: number) => void;
|
|
84
|
+
};
|
|
78
85
|
export type BaseChartConfig = {
|
|
79
86
|
data: ChartData;
|
|
80
87
|
width?: number;
|
|
@@ -102,6 +109,7 @@ export declare abstract class BaseChart {
|
|
|
102
109
|
protected tooltip: Tooltip | null;
|
|
103
110
|
protected legend: Legend | null;
|
|
104
111
|
protected title: Title | null;
|
|
112
|
+
protected textComponents: TextLayoutComponent[];
|
|
105
113
|
protected svg: Selection<SVGSVGElement, undefined, null, undefined> | null;
|
|
106
114
|
protected plotGroup: Selection<SVGGElement, undefined, null, undefined> | null;
|
|
107
115
|
protected container: HTMLElement | null;
|
|
@@ -139,6 +147,7 @@ export declare abstract class BaseChart {
|
|
|
139
147
|
private pushIfIncluded;
|
|
140
148
|
private resolveAccessibleLabel;
|
|
141
149
|
private syncAccessibleLabelFromSvg;
|
|
150
|
+
private resolvePrimaryTextLabel;
|
|
142
151
|
protected resolveResponsiveContext(context: {
|
|
143
152
|
width: number;
|
|
144
153
|
height: number;
|
|
@@ -169,6 +178,7 @@ export declare abstract class BaseChart {
|
|
|
169
178
|
*/
|
|
170
179
|
protected getLayoutComponents(): LayoutAwareComponentBase[];
|
|
171
180
|
protected getBaseLayoutComponents(options: BaseLayoutComponentsOptions): LayoutAwareComponentBase[];
|
|
181
|
+
private getTextComponents;
|
|
172
182
|
protected getExportComponents(): ChartComponentBase[];
|
|
173
183
|
protected getOverrideableComponents(): ChartComponentBase[];
|
|
174
184
|
protected getBaseExportComponents(options: BaseExportComponentsOptions): ChartComponentBase[];
|
|
@@ -226,6 +236,7 @@ export declare abstract class BaseChart {
|
|
|
226
236
|
protected tryRegisterComponent(component: ChartComponentBase, slots: readonly ComponentSlot[]): boolean;
|
|
227
237
|
protected applySlotOverrides(overrides: Map<ChartComponentBase, ChartComponentBase>, slots: readonly ComponentSlot[]): () => void;
|
|
228
238
|
protected applyArrayComponentOverrides<TComponent extends ChartComponentBase>(components: TComponent[], overrides: Map<ChartComponentBase, ChartComponentBase>, isComponent: (component: ChartComponentBase) => component is TComponent): () => void;
|
|
239
|
+
private isTextComponent;
|
|
229
240
|
private getBaseComponentSlots;
|
|
230
241
|
/**
|
|
231
242
|
* Exports the chart in the specified format
|
package/dist/base-chart.js
CHANGED
|
@@ -135,6 +135,12 @@ export class BaseChart {
|
|
|
135
135
|
writable: true,
|
|
136
136
|
value: null
|
|
137
137
|
});
|
|
138
|
+
Object.defineProperty(this, "textComponents", {
|
|
139
|
+
enumerable: true,
|
|
140
|
+
configurable: true,
|
|
141
|
+
writable: true,
|
|
142
|
+
value: []
|
|
143
|
+
});
|
|
138
144
|
Object.defineProperty(this, "svg", {
|
|
139
145
|
enumerable: true,
|
|
140
146
|
configurable: true,
|
|
@@ -410,20 +416,31 @@ export class BaseChart {
|
|
|
410
416
|
}
|
|
411
417
|
}
|
|
412
418
|
resolveAccessibleLabel() {
|
|
413
|
-
const titleText = this.
|
|
419
|
+
const titleText = this.resolvePrimaryTextLabel();
|
|
414
420
|
if (titleText) {
|
|
415
421
|
return titleText;
|
|
416
422
|
}
|
|
417
423
|
return 'Chart';
|
|
418
424
|
}
|
|
419
425
|
syncAccessibleLabelFromSvg(svg) {
|
|
420
|
-
const titleText = svg
|
|
426
|
+
const titleText = svg
|
|
427
|
+
.querySelector('.title text, .text--title text')
|
|
428
|
+
?.textContent?.trim();
|
|
421
429
|
if (titleText) {
|
|
422
430
|
svg.setAttribute('aria-label', titleText);
|
|
423
431
|
return;
|
|
424
432
|
}
|
|
425
433
|
svg.setAttribute('aria-label', this.resolveAccessibleLabel());
|
|
426
434
|
}
|
|
435
|
+
resolvePrimaryTextLabel() {
|
|
436
|
+
return this.textComponents
|
|
437
|
+
.find((component) => {
|
|
438
|
+
return (component.display &&
|
|
439
|
+
(component.type === 'title' ||
|
|
440
|
+
component.variant === 'title'));
|
|
441
|
+
})
|
|
442
|
+
?.text.trim();
|
|
443
|
+
}
|
|
427
444
|
resolveResponsiveContext(context) {
|
|
428
445
|
const activeBreakpoints = this.resolveActiveBreakpoints(context.width).map(({ name }) => name);
|
|
429
446
|
return {
|
|
@@ -574,8 +591,8 @@ export class BaseChart {
|
|
|
574
591
|
}
|
|
575
592
|
getBaseLayoutComponents(options) {
|
|
576
593
|
const components = [];
|
|
577
|
-
if (options.title
|
|
578
|
-
components.push(this.
|
|
594
|
+
if (options.title) {
|
|
595
|
+
components.push(...this.getTextComponents('top'));
|
|
579
596
|
}
|
|
580
597
|
if (options.xAxis && this.xAxis) {
|
|
581
598
|
components.push(this.xAxis);
|
|
@@ -586,8 +603,16 @@ export class BaseChart {
|
|
|
586
603
|
if (options.inlineLegend && this.legend && this.hasInlineLegend()) {
|
|
587
604
|
components.push(this.legend);
|
|
588
605
|
}
|
|
606
|
+
if (options.title) {
|
|
607
|
+
components.push(...this.getTextComponents('bottom'));
|
|
608
|
+
}
|
|
589
609
|
return components;
|
|
590
610
|
}
|
|
611
|
+
getTextComponents(position) {
|
|
612
|
+
return this.textComponents.filter((component) => {
|
|
613
|
+
return component.position === position;
|
|
614
|
+
});
|
|
615
|
+
}
|
|
591
616
|
getExportComponents() {
|
|
592
617
|
return this.getBaseExportComponents({
|
|
593
618
|
title: true,
|
|
@@ -603,7 +628,9 @@ export class BaseChart {
|
|
|
603
628
|
}
|
|
604
629
|
getBaseExportComponents(options) {
|
|
605
630
|
const components = [];
|
|
606
|
-
|
|
631
|
+
if (options.title) {
|
|
632
|
+
components.push(...this.textComponents);
|
|
633
|
+
}
|
|
607
634
|
this.pushIfIncluded(components, options.grid, this.grid);
|
|
608
635
|
this.pushIfIncluded(components, options.xAxis, this.xAxis);
|
|
609
636
|
this.pushIfIncluded(components, options.yAxis, this.yAxis);
|
|
@@ -612,6 +639,13 @@ export class BaseChart {
|
|
|
612
639
|
return components;
|
|
613
640
|
}
|
|
614
641
|
registerBaseComponent(component) {
|
|
642
|
+
if (this.isTextComponent(component)) {
|
|
643
|
+
this.textComponents.push(component);
|
|
644
|
+
if (component.type === 'title' && !this.title) {
|
|
645
|
+
this.title = component;
|
|
646
|
+
}
|
|
647
|
+
return true;
|
|
648
|
+
}
|
|
615
649
|
return this.tryRegisterComponent(component, this.getBaseComponentSlots());
|
|
616
650
|
}
|
|
617
651
|
collectExportOverrides(context) {
|
|
@@ -739,7 +773,18 @@ export class BaseChart {
|
|
|
739
773
|
return overrideComponents;
|
|
740
774
|
}
|
|
741
775
|
applyComponentOverrides(overrides) {
|
|
742
|
-
|
|
776
|
+
const previousTitle = this.title;
|
|
777
|
+
const restoreSlots = this.applySlotOverrides(overrides, this.getBaseComponentSlots());
|
|
778
|
+
const restoreText = this.applyArrayComponentOverrides(this.textComponents, overrides, this.isTextComponent);
|
|
779
|
+
this.title =
|
|
780
|
+
this.textComponents.find((component) => {
|
|
781
|
+
return component.type === 'title';
|
|
782
|
+
}) ?? null;
|
|
783
|
+
return () => {
|
|
784
|
+
restoreText();
|
|
785
|
+
restoreSlots();
|
|
786
|
+
this.title = previousTitle;
|
|
787
|
+
};
|
|
743
788
|
}
|
|
744
789
|
renderExportChart(chart, width, height) {
|
|
745
790
|
const container = document.createElement('div');
|
|
@@ -770,11 +815,10 @@ export class BaseChart {
|
|
|
770
815
|
});
|
|
771
816
|
}
|
|
772
817
|
renderTitle(svg) {
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
this.title.render(svg, this.renderTheme, this.width, position.x, position.y);
|
|
818
|
+
this.textComponents.forEach((component) => {
|
|
819
|
+
const position = this.layoutManager.getComponentPosition(component);
|
|
820
|
+
component.render(svg, this.renderTheme, this.width, position.x, position.y);
|
|
821
|
+
});
|
|
778
822
|
}
|
|
779
823
|
renderInlineLegend(svg) {
|
|
780
824
|
if (!this.legend || !this.hasInlineLegend()) {
|
|
@@ -1060,15 +1104,11 @@ export class BaseChart {
|
|
|
1060
1104
|
components.splice(0, components.length, ...previousState);
|
|
1061
1105
|
};
|
|
1062
1106
|
}
|
|
1107
|
+
isTextComponent(component) {
|
|
1108
|
+
return component.type === 'text' || component.type === 'title';
|
|
1109
|
+
}
|
|
1063
1110
|
getBaseComponentSlots() {
|
|
1064
1111
|
return [
|
|
1065
|
-
{
|
|
1066
|
-
type: 'title',
|
|
1067
|
-
get: () => this.title,
|
|
1068
|
-
set: (component) => {
|
|
1069
|
-
this.title = component;
|
|
1070
|
-
},
|
|
1071
|
-
},
|
|
1072
1112
|
{
|
|
1073
1113
|
type: 'grid',
|
|
1074
1114
|
get: () => this.grid,
|
package/dist/chart-group.d.ts
CHANGED
|
@@ -56,7 +56,7 @@ export declare class ChartGroup {
|
|
|
56
56
|
private readonly warnedColorConflicts;
|
|
57
57
|
private container;
|
|
58
58
|
private legend;
|
|
59
|
-
private
|
|
59
|
+
private readonly textComponents;
|
|
60
60
|
private resizeObserver;
|
|
61
61
|
private readyPromise;
|
|
62
62
|
private childLegendSnapshot;
|
|
@@ -69,6 +69,7 @@ export declare class ChartGroup {
|
|
|
69
69
|
addChild(component: {
|
|
70
70
|
type: string;
|
|
71
71
|
}): this;
|
|
72
|
+
private isTextComponent;
|
|
72
73
|
render(target: string | HTMLElement): HTMLElement | null;
|
|
73
74
|
refresh(): HTMLElement | null;
|
|
74
75
|
whenReady(): Promise<void>;
|
|
@@ -79,6 +80,7 @@ export declare class ChartGroup {
|
|
|
79
80
|
toggleLegendSeries(dataKey: string): this;
|
|
80
81
|
setLegendVisibility(visibility: Record<string, boolean>): this;
|
|
81
82
|
onLegendChange(callback: () => void): () => void;
|
|
83
|
+
private resolveAccessibleLabel;
|
|
82
84
|
export(format: ChartGroupExportFormat, options?: ExportOptions): Promise<string | Blob | void>;
|
|
83
85
|
destroy(): void;
|
|
84
86
|
private bindChartRenderCallback;
|
|
@@ -105,15 +107,19 @@ export declare class ChartGroup {
|
|
|
105
107
|
private serializeChildLegendState;
|
|
106
108
|
private serializeChildYDomains;
|
|
107
109
|
private renderLegendIntoContainer;
|
|
108
|
-
private
|
|
110
|
+
private renderTextSvg;
|
|
111
|
+
private renderTextSvgs;
|
|
109
112
|
private renderLegendSvg;
|
|
110
113
|
private setupResizeObserver;
|
|
111
114
|
private exportSVG;
|
|
112
115
|
private resolveChartOptions;
|
|
113
116
|
private refreshIfMounted;
|
|
114
117
|
private prepareRenderState;
|
|
118
|
+
private sumRenderedTextHeight;
|
|
115
119
|
private createRenderHosts;
|
|
116
120
|
private appendRenderedSection;
|
|
121
|
+
private appendRenderedTextSections;
|
|
122
|
+
private resolveTextSectionClassName;
|
|
117
123
|
private renderLayoutItems;
|
|
118
124
|
private createChartHost;
|
|
119
125
|
private createReadyPromise;
|
|
@@ -122,9 +128,16 @@ export declare class ChartGroup {
|
|
|
122
128
|
private createRasterExportOptions;
|
|
123
129
|
private exportPdfContent;
|
|
124
130
|
private resolveExportLayoutState;
|
|
131
|
+
private createExportLayoutState;
|
|
132
|
+
private createExportRenderContext;
|
|
133
|
+
private resolveBaseExportHeight;
|
|
134
|
+
private createExportComponent;
|
|
135
|
+
private runExportHooks;
|
|
125
136
|
private resolveLiveChartHeightOverride;
|
|
126
137
|
private exportLayoutItems;
|
|
127
138
|
private composeExportSvg;
|
|
139
|
+
private syncAccessibleLabelFromSvg;
|
|
140
|
+
private appendExportTextSections;
|
|
128
141
|
private validateResponsiveConfig;
|
|
129
142
|
private validateItemResponsiveConfig;
|
|
130
143
|
private resolveResponsiveConfigForWidth;
|