@internetstiftelsen/charts 0.7.0 → 0.7.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 CHANGED
@@ -20,6 +20,42 @@ A framework-agnostic, composable charting library built on D3.js with TypeScript
20
20
  npm install @internetstiftelsen/charts
21
21
  ```
22
22
 
23
+ ## Local Development
24
+
25
+ ```bash
26
+ pnpm dev
27
+ ```
28
+
29
+ Runs the interactive demo app (`index.html`) with sidebar controls and
30
+ Chart/Data/Showcase tabs.
31
+
32
+ ```bash
33
+ pnpm dev:docs
34
+ ```
35
+
36
+ Runs the marketing landing page (`docs.html`) built on
37
+ `@internetstiftelsen/styleguide`.
38
+
39
+ ## Build Targets
40
+
41
+ ```bash
42
+ pnpm build
43
+ ```
44
+
45
+ Builds the publishable chart library output into `dist`.
46
+
47
+ ```bash
48
+ pnpm build:docs
49
+ ```
50
+
51
+ Builds the static marketing site into `dist-docs` (used for Pages deploys).
52
+
53
+ ```bash
54
+ pnpm build:demo
55
+ ```
56
+
57
+ Builds the demo app using the default Vite config.
58
+
23
59
  ## Quick Start
24
60
 
25
61
  ```javascript
package/base-chart.js CHANGED
@@ -377,15 +377,12 @@ export class BaseChart {
377
377
  const matchesIndex = match.index === undefined || match.index === index;
378
378
  const matchesType = match.type === undefined || match.type === component.type;
379
379
  const matchesDataKey = match.dataKey === undefined ||
380
- (typeof dataKey === 'string' &&
381
- dataKey === match.dataKey);
380
+ (typeof dataKey === 'string' && dataKey === match.dataKey);
382
381
  if (!matchesIndex || !matchesType || !matchesDataKey) {
383
382
  return;
384
383
  }
385
384
  const existing = componentOverrides.get(component);
386
- componentOverrides.set(component, existing
387
- ? mergeDeep(existing, override)
388
- : { ...override });
385
+ componentOverrides.set(component, existing ? mergeDeep(existing, override) : { ...override });
389
386
  });
390
387
  });
391
388
  return {
package/package.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "0.7.0",
2
+ "version": "0.7.1",
3
3
  "name": "@internetstiftelsen/charts",
4
4
  "type": "module",
5
5
  "sideEffects": false,
@@ -16,13 +16,17 @@
16
16
  ],
17
17
  "scripts": {
18
18
  "dev": "vite",
19
- "build": "tsc -b && vite build",
19
+ "dev:docs": "vite --config vite.docs.config.ts --open",
20
+ "build": "tsc --project tsconfig.lib.json && tsc-alias --project tsconfig.lib.json",
21
+ "build:demo": "tsc -b && vite build",
22
+ "build:docs": "vite build --config vite.docs.config.ts && cp -R docs dist-docs/docs",
20
23
  "lint": "eslint .",
21
24
  "format": "prettier --write ./src",
22
25
  "preview": "vite preview",
26
+ "preview:docs": "vite preview --config vite.docs.config.ts",
23
27
  "test": "vitest",
24
28
  "test:run": "vitest run",
25
- "build:lib": "tsc --project tsconfig.lib.json && tsc-alias --project tsconfig.lib.json",
29
+ "build:lib": "npm run build",
26
30
  "prepub": "rm -rf dist && npm run build:lib && cp package.json dist && cp README.md dist",
27
31
  "pub": "npm run prepub && cd dist && npm publish --access public"
28
32
  },
@@ -49,6 +53,8 @@
49
53
  },
50
54
  "devDependencies": {
51
55
  "@eslint/js": "^9.39.2",
56
+ "@internetstiftelsen/styleguide": "^5.1.23",
57
+ "@speed-highlight/core": "^1.2.14",
52
58
  "@tailwindcss/vite": "^4.1.18",
53
59
  "@testing-library/dom": "^10.4.1",
54
60
  "@testing-library/jest-dom": "^6.9.1",
@@ -63,6 +69,7 @@
63
69
  "globals": "^16.5.0",
64
70
  "jsdom": "^27.4.0",
65
71
  "prettier": "3.6.2",
72
+ "sass": "^1.97.3",
66
73
  "tsc-alias": "^1.8.16",
67
74
  "tw-animate-css": "^1.4.0",
68
75
  "typescript": "~5.9.3",
package/title.d.ts CHANGED
@@ -3,6 +3,7 @@ import type { TitleConfig, ChartTheme, ExportHooks, TitleConfigBase } from './ty
3
3
  import type { LayoutAwareComponent, ComponentSpace } from './chart-interface.js';
4
4
  export declare class Title implements LayoutAwareComponent<TitleConfigBase> {
5
5
  readonly type: "title";
6
+ readonly display: boolean;
6
7
  readonly text: string;
7
8
  readonly exportHooks?: ExportHooks<TitleConfigBase>;
8
9
  private readonly fontSize;
package/title.js CHANGED
@@ -7,6 +7,12 @@ export class Title {
7
7
  writable: true,
8
8
  value: 'title'
9
9
  });
10
+ Object.defineProperty(this, "display", {
11
+ enumerable: true,
12
+ configurable: true,
13
+ writable: true,
14
+ value: void 0
15
+ });
10
16
  Object.defineProperty(this, "text", {
11
17
  enumerable: true,
12
18
  configurable: true,
@@ -55,6 +61,7 @@ export class Title {
55
61
  writable: true,
56
62
  value: void 0
57
63
  });
64
+ this.display = config.display ?? true;
58
65
  this.text = config.text;
59
66
  this.fontSize = config.fontSize ?? 18;
60
67
  this.fontWeight = config.fontWeight ?? 'bold';
@@ -66,6 +73,7 @@ export class Title {
66
73
  }
67
74
  getExportConfig() {
68
75
  return {
76
+ display: this.display,
69
77
  text: this.text,
70
78
  fontSize: this.fontSize,
71
79
  fontWeight: this.fontWeight,
@@ -86,6 +94,13 @@ export class Title {
86
94
  * Returns the space required by the title
87
95
  */
88
96
  getRequiredSpace() {
97
+ if (!this.display) {
98
+ return {
99
+ width: 0,
100
+ height: 0,
101
+ position: 'top',
102
+ };
103
+ }
89
104
  return {
90
105
  width: 0, // Title spans full width
91
106
  height: this.marginTop + this.fontSize + this.marginBottom,
@@ -93,6 +108,9 @@ export class Title {
93
108
  };
94
109
  }
95
110
  render(svg, theme, width, x = 0, y = 0) {
111
+ if (!this.display) {
112
+ return;
113
+ }
96
114
  const titleGroup = svg
97
115
  .append('g')
98
116
  .attr('class', 'title')
package/types.d.ts CHANGED
@@ -218,6 +218,7 @@ export declare function getSeriesColor(series: {
218
218
  }): string;
219
219
  export type LabelOversizedBehavior = 'truncate' | 'wrap' | 'hide';
220
220
  export type XAxisConfigBase = {
221
+ display?: boolean;
221
222
  dataKey?: string;
222
223
  labelKey?: string;
223
224
  groupLabelKey?: string;
@@ -235,6 +236,7 @@ export type XAxisConfig = XAxisConfigBase & {
235
236
  exportHooks?: ExportHooks<XAxisConfigBase>;
236
237
  };
237
238
  export type YAxisConfigBase = {
239
+ display?: boolean;
238
240
  tickFormat?: string | ((value: number) => string) | null;
239
241
  rotatedLabels?: boolean;
240
242
  maxLabelWidth?: number;
@@ -287,6 +289,7 @@ export type LegendItem = {
287
289
  visible: boolean;
288
290
  };
289
291
  export type TitleConfigBase = {
292
+ display?: boolean;
290
293
  text: string;
291
294
  fontSize?: number;
292
295
  fontWeight?: string;
package/x-axis.d.ts CHANGED
@@ -3,6 +3,7 @@ import type { XAxisConfig, ChartTheme, D3Scale, DataItem, ExportHooks, XAxisConf
3
3
  import type { LayoutAwareComponent, ComponentSpace } from './chart-interface.js';
4
4
  export declare class XAxis implements LayoutAwareComponent<XAxisConfigBase> {
5
5
  readonly type: "xAxis";
6
+ readonly display: boolean;
6
7
  readonly dataKey?: string;
7
8
  readonly labelKey?: string;
8
9
  readonly groupLabelKey?: string;
@@ -16,6 +17,7 @@ export declare class XAxis implements LayoutAwareComponent<XAxisConfigBase> {
16
17
  private readonly tickFormat;
17
18
  private wrapLineCount;
18
19
  private estimatedHeight;
20
+ private estimatedTickLabelVerticalFootprint;
19
21
  private readonly autoHideOverlapping;
20
22
  private readonly minLabelGap;
21
23
  private readonly preserveEndLabels;
@@ -30,6 +32,7 @@ export declare class XAxis implements LayoutAwareComponent<XAxisConfigBase> {
30
32
  getRequiredSpace(): ComponentSpace;
31
33
  estimateLayoutSpace(labels: unknown[], theme: ChartTheme, svg: SVGSVGElement): void;
32
34
  clearEstimatedSpace(): void;
35
+ private getTickLabelVerticalFootprint;
33
36
  render(svg: Selection<SVGSVGElement, undefined, null, undefined>, x: D3Scale, theme: ChartTheme, yPosition: number, data?: DataItem[]): void;
34
37
  private buildLabelLookup;
35
38
  private renderGroupLabels;
package/x-axis.js CHANGED
@@ -25,6 +25,12 @@ export class XAxis {
25
25
  writable: true,
26
26
  value: 'xAxis'
27
27
  });
28
+ Object.defineProperty(this, "display", {
29
+ enumerable: true,
30
+ configurable: true,
31
+ writable: true,
32
+ value: void 0
33
+ });
28
34
  Object.defineProperty(this, "dataKey", {
29
35
  enumerable: true,
30
36
  configurable: true,
@@ -104,6 +110,12 @@ export class XAxis {
104
110
  writable: true,
105
111
  value: null
106
112
  });
113
+ Object.defineProperty(this, "estimatedTickLabelVerticalFootprint", {
114
+ enumerable: true,
115
+ configurable: true,
116
+ writable: true,
117
+ value: null
118
+ });
107
119
  Object.defineProperty(this, "autoHideOverlapping", {
108
120
  enumerable: true,
109
121
  configurable: true,
@@ -128,6 +140,7 @@ export class XAxis {
128
140
  writable: true,
129
141
  value: void 0
130
142
  });
143
+ this.display = config?.display ?? true;
131
144
  this.dataKey = config?.dataKey;
132
145
  this.labelKey = config?.labelKey;
133
146
  this.groupLabelKey = config?.groupLabelKey;
@@ -144,6 +157,7 @@ export class XAxis {
144
157
  }
145
158
  getExportConfig() {
146
159
  return {
160
+ display: this.display,
147
161
  dataKey: this.dataKey,
148
162
  labelKey: this.labelKey,
149
163
  groupLabelKey: this.groupLabelKey,
@@ -169,6 +183,13 @@ export class XAxis {
169
183
  * Returns the space required by the x-axis
170
184
  */
171
185
  getRequiredSpace() {
186
+ if (!this.display) {
187
+ return {
188
+ width: 0,
189
+ height: 0,
190
+ position: 'bottom',
191
+ };
192
+ }
172
193
  if (this.estimatedHeight !== null) {
173
194
  return {
174
195
  width: 0,
@@ -198,6 +219,8 @@ export class XAxis {
198
219
  estimateLayoutSpace(labels, theme, svg) {
199
220
  if (!labels.length) {
200
221
  this.estimatedHeight = null;
222
+ this.estimatedTickLabelVerticalFootprint = null;
223
+ this.wrapLineCount = 1;
201
224
  return;
202
225
  }
203
226
  const parsedFontSize = typeof theme.axis.fontSize === 'string'
@@ -232,9 +255,13 @@ export class XAxis {
232
255
  if (this.rotatedLabels) {
233
256
  const radians = Math.PI / 4;
234
257
  const verticalFootprint = Math.sin(radians) * maxWidth + Math.cos(radians) * textHeight;
258
+ this.estimatedTickLabelVerticalFootprint = verticalFootprint;
235
259
  this.estimatedHeight = this.tickPadding + verticalFootprint + 5;
236
260
  }
237
261
  else {
262
+ const wrappedExtraHeight = Math.max(0, maxLines - 1) * lineHeight;
263
+ this.estimatedTickLabelVerticalFootprint =
264
+ this.fontSize + wrappedExtraHeight;
238
265
  this.estimatedHeight = this.tickPadding + textHeight + 5;
239
266
  }
240
267
  if (this.showGroupLabels) {
@@ -242,12 +269,29 @@ export class XAxis {
242
269
  this.estimatedHeight +=
243
270
  this.groupLabelGap + groupLabelStyle.fontSize + 5;
244
271
  }
245
- this.wrapLineCount = Math.max(this.wrapLineCount, maxLines);
272
+ this.wrapLineCount = maxLines;
246
273
  }
247
274
  clearEstimatedSpace() {
248
275
  this.estimatedHeight = null;
276
+ this.estimatedTickLabelVerticalFootprint = null;
277
+ }
278
+ getTickLabelVerticalFootprint() {
279
+ if (this.estimatedTickLabelVerticalFootprint !== null) {
280
+ return this.estimatedTickLabelVerticalFootprint;
281
+ }
282
+ if (this.rotatedLabels) {
283
+ // Fallback to the same rough factor used by getRequiredSpace().
284
+ const baseHeight = this.tickPadding + this.fontSize + 5;
285
+ return Math.max(baseHeight * 2.5 - this.tickPadding - 5, 0);
286
+ }
287
+ const lineHeight = this.fontSize * 1.2;
288
+ const wrappedExtraHeight = Math.max(0, this.wrapLineCount - 1) * lineHeight;
289
+ return this.fontSize + wrappedExtraHeight;
249
290
  }
250
291
  render(svg, x, theme, yPosition, data = []) {
292
+ if (!this.display) {
293
+ return;
294
+ }
251
295
  const labelLookup = this.buildLabelLookup(data);
252
296
  const axisGenerator = axisBottom(x)
253
297
  .tickSizeOuter(0)
@@ -321,7 +365,8 @@ export class XAxis {
321
365
  if (groupRanges.length === 0) {
322
366
  return;
323
367
  }
324
- const yOffset = this.tickPadding + this.fontSize + this.groupLabelGap;
368
+ const tickLabelVerticalFootprint = this.getTickLabelVerticalFootprint();
369
+ const yOffset = this.tickPadding + tickLabelVerticalFootprint + this.groupLabelGap;
325
370
  const groupLabelStyle = this.resolveGroupLabelStyle(theme);
326
371
  const groupLayer = svg
327
372
  .append('g')
package/y-axis.d.ts CHANGED
@@ -3,6 +3,7 @@ import type { ChartTheme, YAxisConfig, D3Scale, ExportHooks, YAxisConfigBase } f
3
3
  import type { LayoutAwareComponent, ComponentSpace } from './chart-interface.js';
4
4
  export declare class YAxis implements LayoutAwareComponent<YAxisConfigBase> {
5
5
  readonly type: "yAxis";
6
+ readonly display: boolean;
6
7
  private readonly tickPadding;
7
8
  private readonly fontSize;
8
9
  private readonly maxLabelWidth;
package/y-axis.js CHANGED
@@ -8,6 +8,12 @@ export class YAxis {
8
8
  writable: true,
9
9
  value: 'yAxis'
10
10
  });
11
+ Object.defineProperty(this, "display", {
12
+ enumerable: true,
13
+ configurable: true,
14
+ writable: true,
15
+ value: void 0
16
+ });
11
17
  Object.defineProperty(this, "tickPadding", {
12
18
  enumerable: true,
13
19
  configurable: true,
@@ -51,6 +57,7 @@ export class YAxis {
51
57
  writable: true,
52
58
  value: void 0
53
59
  });
60
+ this.display = config?.display ?? true;
54
61
  this.tickFormat = config?.tickFormat ?? null;
55
62
  this.rotatedLabels = config?.rotatedLabels ?? false;
56
63
  this.maxLabelWidth = config?.maxLabelWidth ?? 40; // Default 40 for backward compatibility
@@ -59,6 +66,7 @@ export class YAxis {
59
66
  }
60
67
  getExportConfig() {
61
68
  return {
69
+ display: this.display,
62
70
  tickFormat: this.tickFormat,
63
71
  rotatedLabels: this.rotatedLabels,
64
72
  maxLabelWidth: this.maxLabelWidth,
@@ -76,6 +84,13 @@ export class YAxis {
76
84
  * Returns the space required by the y-axis
77
85
  */
78
86
  getRequiredSpace() {
87
+ if (!this.display) {
88
+ return {
89
+ width: 0,
90
+ height: 0,
91
+ position: 'left',
92
+ };
93
+ }
79
94
  // Width = max label width + tick padding
80
95
  // Rotated labels need less width (cos(45°) ≈ 0.7 of horizontal width)
81
96
  const baseWidth = this.maxLabelWidth + this.tickPadding;
@@ -87,6 +102,9 @@ export class YAxis {
87
102
  };
88
103
  }
89
104
  render(svg, y, theme, xPosition) {
105
+ if (!this.display) {
106
+ return;
107
+ }
90
108
  const axis = axisLeft(y).tickSize(0).tickPadding(this.tickPadding);
91
109
  // Apply tick formatting if specified
92
110
  if (this.tickFormat) {