@internetstiftelsen/charts 0.14.2 → 0.15.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 CHANGED
@@ -385,17 +385,16 @@ their own centers on initial render and `chart.update(...)`.
385
385
 
386
386
  ## Export
387
387
 
388
- `chart.export()` supports `svg`, `json`, `csv`, `xlsx`, `png`, `jpg`, and `pdf`.
388
+ `chart.export()` supports `svg`, `json`, `csv`, `xlsx`, `png`, and `jpg`.
389
389
 
390
390
  ```javascript
391
391
  await chart.export('png', { download: true });
392
392
  await chart.export('csv', { download: true, delimiter: ';' });
393
393
  await chart.export('xlsx', { download: true, sheetName: 'Data' });
394
- await chart.export('pdf', { download: true, pdfMargin: 16 });
395
394
  ```
396
395
 
397
- `xlsx` and `pdf` are lazy-loaded and require optional dependencies
398
- (`write-excel-file` and `jspdf`) only when those formats are used.
396
+ `xlsx` is lazy-loaded and requires the optional `write-excel-file` dependency
397
+ only when that format is used.
399
398
 
400
399
  ## Import
401
400
 
@@ -9,7 +9,7 @@ import type { Legend } from './legend.js';
9
9
  import type { Title } from './title.js';
10
10
  import { LayoutManager, type PlotAreaBounds } from './layout-manager.js';
11
11
  import { LegendStateController } from './legend-state.js';
12
- type VisualExportFormat = 'svg' | 'png' | 'jpg' | 'pdf';
12
+ type VisualExportFormat = 'svg' | 'png' | 'jpg';
13
13
  type RenderDimensions = {
14
14
  width: number;
15
15
  height: number;
@@ -262,7 +262,6 @@ export declare abstract class BaseChart {
262
262
  private exportSize;
263
263
  private exportXLSX;
264
264
  private exportImage;
265
- private exportPDF;
266
265
  protected exportSVG(options?: ExportOptions, formatForHooks?: VisualExportFormat): Promise<string>;
267
266
  private requireRenderedSvg;
268
267
  private resolveExportContext;
@@ -5,7 +5,6 @@ import { LayoutManager } from './layout-manager.js';
5
5
  import { serializeCSV } from './export-tabular.js';
6
6
  import { exportRasterBlob } from './export-image.js';
7
7
  import { exportXLSXBlob } from './export-xlsx.js';
8
- import { exportPDFBlob } from './export-pdf.js';
9
8
  import { normalizeChartData } from './grouped-data.js';
10
9
  import { LegendStateController } from './legend-state.js';
11
10
  import { mergeDeep } from './utils.js';
@@ -1251,7 +1250,7 @@ export class BaseChart {
1251
1250
  content = await this.exportImage(format, options);
1252
1251
  }
1253
1252
  else {
1254
- content = await this.exportPDF(options);
1253
+ throw new Error(`Unsupported export format: ${format}`);
1255
1254
  }
1256
1255
  if (options?.download) {
1257
1256
  this.downloadContent(content, format, options);
@@ -1296,7 +1295,7 @@ export class BaseChart {
1296
1295
  if (format === 'jpg') {
1297
1296
  return 'image/jpeg';
1298
1297
  }
1299
- return 'application/pdf';
1298
+ throw new Error(`Unsupported export format: ${format}`);
1300
1299
  }
1301
1300
  exportCSV(options) {
1302
1301
  return serializeCSV(this.sourceData, options);
@@ -1325,24 +1324,6 @@ export class BaseChart {
1325
1324
  jpegQuality: options?.jpegQuality ?? 0.92,
1326
1325
  });
1327
1326
  }
1328
- async exportPDF(options) {
1329
- const { width, height } = this.exportSize(options);
1330
- const svg = await this.exportSVG(options, 'pdf');
1331
- const pngBlob = await exportRasterBlob({
1332
- format: 'png',
1333
- svg,
1334
- width,
1335
- height,
1336
- pixelRatio: options?.pixelRatio ?? 1,
1337
- backgroundColor: options?.backgroundColor ?? '#ffffff',
1338
- jpegQuality: options?.jpegQuality ?? 0.92,
1339
- });
1340
- return exportPDFBlob(pngBlob, {
1341
- width,
1342
- height,
1343
- margin: options?.pdfMargin ?? 0,
1344
- });
1345
- }
1346
1327
  async exportSVG(options, formatForHooks = 'svg') {
1347
1328
  const liveSvg = this.requireRenderedSvg();
1348
1329
  await this.whenReady();
@@ -40,7 +40,7 @@ export type ChartGroupChartOptions = {
40
40
  order?: number;
41
41
  responsive?: ChartGroupItemResponsiveConfig;
42
42
  };
43
- export type ChartGroupExportFormat = 'svg' | 'png' | 'jpg' | 'pdf';
43
+ export type ChartGroupExportFormat = 'svg' | 'png' | 'jpg';
44
44
  export declare class ChartGroup {
45
45
  private readonly cols;
46
46
  private readonly gap;
@@ -127,7 +127,6 @@ export declare class ChartGroup {
127
127
  private requireRenderedContainer;
128
128
  private resolveExportContent;
129
129
  private createRasterExportOptions;
130
- private exportPdfContent;
131
130
  private resolveExportLayoutState;
132
131
  private createExportLayoutState;
133
132
  private createExportRenderContext;
@@ -1,6 +1,5 @@
1
1
  import { create } from 'd3';
2
2
  import { exportRasterBlob } from './export-image.js';
3
- import { exportPDFBlob } from './export-pdf.js';
4
3
  import { LegendStateController } from './legend-state.js';
5
4
  import { DEFAULT_CHART_HEIGHT, DEFAULT_CHART_WIDTH, defaultTheme, } from './theme.js';
6
5
  import { ChartValidator } from './validation.js';
@@ -988,9 +987,6 @@ export class ChartGroup {
988
987
  return svg;
989
988
  }
990
989
  const rasterOptions = this.createRasterExportOptions(svg, width, height, options);
991
- if (format === 'pdf') {
992
- return this.exportPdfContent(rasterOptions, width, height, options);
993
- }
994
990
  return exportRasterBlob({
995
991
  format,
996
992
  ...rasterOptions,
@@ -1006,17 +1002,6 @@ export class ChartGroup {
1006
1002
  jpegQuality: options?.jpegQuality ?? 0.92,
1007
1003
  };
1008
1004
  }
1009
- async exportPdfContent(rasterOptions, width, height, options) {
1010
- const pngBlob = await exportRasterBlob({
1011
- format: 'png',
1012
- ...rasterOptions,
1013
- });
1014
- return exportPDFBlob(pngBlob, {
1015
- width,
1016
- height,
1017
- margin: options?.pdfMargin ?? 0,
1018
- });
1019
- }
1020
1005
  resolveExportLayoutState(width, context) {
1021
1006
  const textComponents = this.textComponents
1022
1007
  .map((component) => {
@@ -1327,6 +1312,6 @@ export class ChartGroup {
1327
1312
  if (format === 'jpg') {
1328
1313
  return 'image/jpeg';
1329
1314
  }
1330
- return 'application/pdf';
1315
+ throw new Error(`Unsupported export format: ${format}`);
1331
1316
  }
1332
1317
  }
package/dist/pie-chart.js CHANGED
@@ -492,9 +492,9 @@ export class PieChart extends RadialChartBase {
492
492
  const insideArc = arc()
493
493
  .innerRadius(insideLabelRadius)
494
494
  .outerRadius(insideLabelRadius);
495
- const fontSize = this.renderTheme.legend.fontSize * fontScale;
496
- const fontFamily = this.renderTheme.axis.fontFamily;
497
- const fontWeight = this.renderTheme.axis.fontWeight ?? 'normal';
495
+ const fontSize = this.renderTheme.valueLabel.fontSize * fontScale;
496
+ const fontFamily = this.renderTheme.valueLabel.fontFamily;
497
+ const fontWeight = this.renderTheme.valueLabel.fontWeight;
498
498
  const labelOverflowOptions = {
499
499
  maxLabelWidth: this.valueLabel.maxLabelWidth,
500
500
  oversizedBehavior: this.valueLabel.oversizedBehavior,
package/dist/types.d.ts CHANGED
@@ -9,7 +9,7 @@ export type GroupedDataGroup = {
9
9
  data: DataItem[];
10
10
  };
11
11
  export type ChartData = DataItem[] | GroupedDataGroup[];
12
- export type ExportFormat = 'svg' | 'json' | 'csv' | 'xlsx' | 'png' | 'jpg' | 'pdf';
12
+ export type ExportFormat = 'svg' | 'json' | 'csv' | 'xlsx' | 'png' | 'jpg';
13
13
  export type ExportOptions = {
14
14
  download?: boolean;
15
15
  filename?: string;
@@ -21,7 +21,6 @@ export type ExportOptions = {
21
21
  pixelRatio?: number;
22
22
  backgroundColor?: string;
23
23
  sheetName?: string;
24
- pdfMargin?: number;
25
24
  };
26
25
  export type ExportRenderContext = {
27
26
  format: ExportFormat;
@@ -219,7 +219,6 @@ group.onLegendChange(() => {
219
219
  await group.export('svg');
220
220
  await group.export('png');
221
221
  await group.export('jpg');
222
- await group.export('pdf');
223
222
  ```
224
223
 
225
224
  - Export width can be overridden with `options.width`
@@ -344,7 +344,7 @@ new Text({
344
344
 
345
345
  Components can provide `exportHooks` to adjust export-only settings or mutate
346
346
  the exported SVG right before serialization. Hooks run only for visual exports
347
- (`svg`, `png`, `jpg`, `pdf`) and never touch the live chart instance. Use
347
+ (`svg`, `png`, `jpg`) and never touch the live chart instance. Use
348
348
  `beforeRender` to return config overrides and `before` to mutate the export
349
349
  SVG.
350
350
 
@@ -403,24 +403,21 @@ breakpoints in chart constructor config (see
403
403
  - `xlsx` (lazy-loads optional `write-excel-file`)
404
404
  - `png`
405
405
  - `jpg`
406
- - `pdf` (lazy-loads optional `jspdf`)
407
406
 
408
407
  ```typescript
409
408
  await chart.export('png', { download: true });
410
409
  await chart.export('csv', { download: true, delimiter: ';' });
411
410
  await chart.export('xlsx', { download: true, sheetName: 'Data' });
412
- await chart.export('pdf', { download: true, pdfMargin: 16 });
413
411
  ```
414
412
 
415
413
  Export option highlights:
416
414
 
417
415
  - `columns`: choose specific columns for `csv` and `xlsx`
418
416
  - `delimiter`: CSV delimiter (default `,`)
419
- - `pixelRatio`: render scale for `png`, `jpg`, `pdf`
420
- - `backgroundColor`: defaults transparent for `png`, white for `jpg`/`pdf`
417
+ - `pixelRatio`: render scale for `png` and `jpg`
418
+ - `backgroundColor`: defaults transparent for `png` and white for `jpg`
421
419
  - `jpegQuality`: JPEG quality for `jpg` (default `0.92`)
422
420
  - `sheetName`: sheet name for `xlsx` (default `Sheet1`)
423
- - `pdfMargin`: page margin for `pdf` (default `0`)
424
421
 
425
422
  `json` export returns only the chart data payload
426
423
 
package/docs/theming.md CHANGED
@@ -70,6 +70,10 @@ through the `themes` map as `themes.default`, `themes.ruby`,
70
70
  | `tooltip.fontFamily` | `string` | - | Tooltip text font |
71
71
  | `tooltip.fontSize` | `number` | `12` | Tooltip text size in pixels |
72
72
  | `tooltip.fontWeight` | `string` | `'normal'` | Tooltip text weight |
73
+ | `valueLabel.color` | `string` | `'#1f2a36'` | Value label text color |
74
+ | `valueLabel.fontFamily` | `string` | - | Value label text font |
75
+ | `valueLabel.fontSize` | `number` | `12` | Value label text size in pixels |
76
+ | `valueLabel.fontWeight` | `string` | `'600'` | Value label text weight |
73
77
 
74
78
  ---
75
79
 
package/package.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "0.14.2",
2
+ "version": "0.15.0",
3
3
  "name": "@internetstiftelsen/charts",
4
4
  "type": "module",
5
5
  "sideEffects": false,
@@ -38,48 +38,47 @@
38
38
  "d3-cloud": "^1.2.9"
39
39
  },
40
40
  "optionalDependencies": {
41
- "jspdf": "^4.2.1",
42
- "write-excel-file": "^4.0.7"
41
+ "write-excel-file": "^4.1.1"
43
42
  },
44
43
  "devDependencies": {
45
44
  "@eslint/js": "^10.0.1",
46
45
  "@handsontable/react-wrapper": "^17.1.0",
47
46
  "@internetstiftelsen/styleguide": "^5.1.27",
48
- "@radix-ui/react-label": "^2.1.8",
49
- "@radix-ui/react-select": "^2.2.6",
50
- "@radix-ui/react-switch": "^1.2.6",
51
- "@radix-ui/react-tabs": "^1.1.13",
52
- "@speed-highlight/core": "^1.2.15",
53
- "@tailwindcss/vite": "^4.3.0",
47
+ "@radix-ui/react-label": "^2.1.9",
48
+ "@radix-ui/react-select": "^2.3.0",
49
+ "@radix-ui/react-switch": "^1.3.0",
50
+ "@radix-ui/react-tabs": "^1.1.14",
51
+ "@speed-highlight/core": "^1.2.17",
52
+ "@tailwindcss/vite": "^4.3.1",
54
53
  "@testing-library/dom": "^10.4.1",
55
54
  "@testing-library/jest-dom": "^6.9.1",
56
55
  "@testing-library/react": "^16.3.2",
57
56
  "@types/d3": "^7.4.3",
58
57
  "@types/d3-cloud": "^1.2.9",
59
- "@types/node": "^25.9.1",
60
- "@types/react": "^19.2.16",
58
+ "@types/node": "^25.9.3",
59
+ "@types/react": "^19.2.17",
61
60
  "@types/react-dom": "^19.2.3",
62
61
  "@vitejs/plugin-react-swc": "^4.3.1",
63
62
  "class-variance-authority": "^0.7.1",
64
63
  "clsx": "^2.1.1",
65
- "eslint": "^10.4.1",
64
+ "eslint": "^10.5.0",
66
65
  "eslint-plugin-react-hooks": "^7.1.1",
67
66
  "eslint-plugin-react-refresh": "^0.5.2",
68
67
  "globals": "^17.6.0",
69
68
  "handsontable": "^17.1.0",
70
69
  "jsdom": "^29.1.1",
71
- "lucide-react": "^1.17.0",
72
- "prettier": "3.8.3",
73
- "radix-ui": "^1.4.3",
70
+ "lucide-react": "^1.18.0",
71
+ "prettier": "3.8.4",
72
+ "radix-ui": "^1.5.0",
74
73
  "react": "^19.2.7",
75
74
  "react-dom": "^19.2.7",
76
- "sass": "^1.100.0",
75
+ "sass": "^1.101.0",
77
76
  "tailwind-merge": "^3.6.0",
78
- "tailwindcss": "^4.3.0",
77
+ "tailwindcss": "^4.3.1",
79
78
  "tsc-alias": "^1.8.17",
80
79
  "tw-animate-css": "^1.4.0",
81
80
  "typescript": "~6.0.3",
82
- "typescript-eslint": "^8.60.1",
81
+ "typescript-eslint": "^8.61.0",
83
82
  "vite": "^8.0.16",
84
83
  "vitest": "^4.1.8"
85
84
  }
@@ -1,8 +0,0 @@
1
- type JsPdfLoader = () => Promise<unknown>;
2
- export type PDFExportOptions = {
3
- width: number;
4
- height: number;
5
- margin: number;
6
- };
7
- export declare function exportPDFBlob(imageBlob: Blob, options: PDFExportOptions, loader?: JsPdfLoader): Promise<Blob>;
8
- export {};
@@ -1,67 +0,0 @@
1
- function isObject(value) {
2
- return typeof value === 'object' && value !== null;
3
- }
4
- function resolveJsPdfConstructor(moduleValue) {
5
- if (!isObject(moduleValue)) {
6
- throw new Error('Invalid "jspdf" module export');
7
- }
8
- const moduleCandidate = moduleValue;
9
- const defaultCandidate = moduleCandidate.default;
10
- let ctor = moduleCandidate.jsPDF;
11
- if (!ctor && isObject(defaultCandidate)) {
12
- ctor = defaultCandidate.jsPDF;
13
- }
14
- if (!ctor && typeof defaultCandidate === 'function') {
15
- ctor = defaultCandidate;
16
- }
17
- if (typeof ctor !== 'function') {
18
- throw new Error('Invalid "jspdf" module export');
19
- }
20
- return ctor;
21
- }
22
- async function defaultJsPdfLoader() {
23
- return import('jspdf');
24
- }
25
- function blobToDataUrl(blob) {
26
- return new Promise((resolve, reject) => {
27
- const reader = new FileReader();
28
- reader.onload = () => resolve(String(reader.result));
29
- reader.onerror = () => reject(new Error('Failed to create PDF image data'));
30
- reader.readAsDataURL(blob);
31
- });
32
- }
33
- export async function exportPDFBlob(imageBlob, options, loader = defaultJsPdfLoader) {
34
- let jsPdfModule;
35
- try {
36
- jsPdfModule = await loader();
37
- }
38
- catch {
39
- throw new Error('PDF export requires optional dependency "jspdf". Install optional dependency "jspdf".');
40
- }
41
- const JsPdfCtor = resolveJsPdfConstructor(jsPdfModule);
42
- const margin = Math.max(0, options.margin);
43
- const pageWidth = Math.max(1, options.width + margin * 2);
44
- const pageHeight = Math.max(1, options.height + margin * 2);
45
- const orientation = pageWidth >= pageHeight ? 'landscape' : 'portrait';
46
- const pdf = new JsPdfCtor({
47
- unit: 'px',
48
- format: [pageWidth, pageHeight],
49
- orientation,
50
- hotfixes: ['px_scaling'],
51
- });
52
- const actualPageWidth = pdf.internal.pageSize.getWidth();
53
- const actualPageHeight = pdf.internal.pageSize.getHeight();
54
- const availableWidth = actualPageWidth - margin * 2;
55
- const availableHeight = actualPageHeight - margin * 2;
56
- if (availableWidth <= 0 || availableHeight <= 0) {
57
- throw new Error('Invalid PDF margin: no drawable area remains');
58
- }
59
- const imageDataUrl = await blobToDataUrl(imageBlob);
60
- const scale = Math.min(availableWidth / options.width, availableHeight / options.height);
61
- const drawWidth = options.width * scale;
62
- const drawHeight = options.height * scale;
63
- const x = (actualPageWidth - drawWidth) / 2;
64
- const y = (actualPageHeight - drawHeight) / 2;
65
- pdf.addImage(imageDataUrl, 'PNG', x, y, drawWidth, drawHeight);
66
- return pdf.output('blob');
67
- }