@internetstiftelsen/charts 0.4.1 → 0.4.3

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
@@ -43,6 +43,20 @@ chart
43
43
  chart.render('#chart-container');
44
44
  ```
45
45
 
46
+ ## Export
47
+
48
+ `chart.export()` supports `svg`, `json`, `csv`, `xlsx`, `png`, `jpg`, and `pdf`.
49
+
50
+ ```javascript
51
+ await chart.export('png', { download: true });
52
+ await chart.export('csv', { download: true, delimiter: ';' });
53
+ await chart.export('xlsx', { download: true, sheetName: 'Data' });
54
+ await chart.export('pdf', { download: true, pdfMargin: 16 });
55
+ ```
56
+
57
+ `xlsx` and `pdf` are lazy-loaded and require optional dependencies (`xlsx` and
58
+ `jspdf`) only when those formats are used.
59
+
46
60
  ## Documentation
47
61
 
48
62
  - [Getting Started](./docs/getting-started.md) - Installation, Vanilla JS, React integration
package/base-chart.d.ts CHANGED
@@ -8,6 +8,7 @@ import type { Tooltip } from './tooltip.js';
8
8
  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
+ type VisualExportFormat = 'svg' | 'png' | 'jpg' | 'pdf';
11
12
  export type BaseChartConfig = {
12
13
  data: DataItem[];
13
14
  theme?: Partial<ChartTheme>;
@@ -44,7 +45,8 @@ export declare abstract class BaseChart {
44
45
  /**
45
46
  * Renders the chart to the specified target element
46
47
  */
47
- render(target: string): HTMLElement | null;
48
+ render(target: string | HTMLElement): HTMLElement | null;
49
+ private resolveContainer;
48
50
  /**
49
51
  * Performs the actual rendering logic
50
52
  */
@@ -79,15 +81,22 @@ export declare abstract class BaseChart {
79
81
  protected parseValue(value: unknown): number;
80
82
  /**
81
83
  * Exports the chart in the specified format
82
- * @param format - The export format ('svg' or 'json')
84
+ * @param format - The export format
83
85
  * @param options - Optional export options (download, filename)
84
- * @returns The exported content as a string if download is false/undefined, void if download is true
86
+ * @returns The exported content when download is false/undefined, void if download is true
85
87
  */
86
- export(format: ExportFormat, options?: ExportOptions): string | void;
88
+ export(format: ExportFormat, options?: ExportOptions): Promise<string | Blob | void>;
87
89
  /**
88
90
  * Downloads the exported content as a file
89
91
  */
90
92
  private downloadContent;
91
- protected exportSVG(options?: ExportOptions): string;
93
+ private getMimeType;
94
+ private exportCSV;
95
+ private exportSize;
96
+ private exportXLSX;
97
+ private exportImage;
98
+ private exportPDF;
99
+ protected exportSVG(options?: ExportOptions, formatForHooks?: VisualExportFormat): string;
92
100
  protected exportJSON(): string;
93
101
  }
102
+ export {};
package/base-chart.js CHANGED
@@ -2,6 +2,10 @@ import { create } from 'd3';
2
2
  import { defaultTheme } from './theme.js';
3
3
  import { ChartValidator } from './validation.js';
4
4
  import { LayoutManager } from './layout-manager.js';
5
+ import { serializeCSV } from './export-tabular.js';
6
+ import { exportRasterBlob } from './export-image.js';
7
+ import { exportXLSXBlob } from './export-xlsx.js';
8
+ import { exportPDFBlob } from './export-pdf.js';
5
9
  /**
6
10
  * Base chart class that provides common functionality for all chart types
7
11
  */
@@ -134,10 +138,7 @@ export class BaseChart {
134
138
  * Renders the chart to the specified target element
135
139
  */
136
140
  render(target) {
137
- const container = document.querySelector(target);
138
- if (!container) {
139
- throw new Error(`Container "${target}" not found`);
140
- }
141
+ const container = this.resolveContainer(target);
141
142
  this.container = container;
142
143
  container.innerHTML = '';
143
144
  // Perform initial render
@@ -146,6 +147,19 @@ export class BaseChart {
146
147
  this.setupResizeObserver();
147
148
  return container;
148
149
  }
150
+ resolveContainer(target) {
151
+ if (target instanceof HTMLElement) {
152
+ return target;
153
+ }
154
+ const container = document.querySelector(target);
155
+ if (!container) {
156
+ throw new Error(`Container "${target}" not found`);
157
+ }
158
+ if (!(container instanceof HTMLElement)) {
159
+ throw new Error(`Container "${target}" is not an HTMLElement`);
160
+ }
161
+ return container;
162
+ }
149
163
  /**
150
164
  * Performs the actual rendering logic
151
165
  */
@@ -267,7 +281,6 @@ export class BaseChart {
267
281
  return svg;
268
282
  }
269
283
  // Hook for subclasses to update component layout estimates before layout calc
270
- // eslint-disable-next-line @typescript-eslint/no-empty-function
271
284
  prepareLayout() { }
272
285
  /**
273
286
  * Setup ResizeObserver for automatic resize handling
@@ -321,12 +334,30 @@ export class BaseChart {
321
334
  }
322
335
  /**
323
336
  * Exports the chart in the specified format
324
- * @param format - The export format ('svg' or 'json')
337
+ * @param format - The export format
325
338
  * @param options - Optional export options (download, filename)
326
- * @returns The exported content as a string if download is false/undefined, void if download is true
339
+ * @returns The exported content when download is false/undefined, void if download is true
327
340
  */
328
- export(format, options) {
329
- const content = format === 'svg' ? this.exportSVG(options) : this.exportJSON();
341
+ async export(format, options) {
342
+ let content;
343
+ if (format === 'svg') {
344
+ content = this.exportSVG(options, 'svg');
345
+ }
346
+ else if (format === 'json') {
347
+ content = this.exportJSON();
348
+ }
349
+ else if (format === 'csv') {
350
+ content = this.exportCSV(options);
351
+ }
352
+ else if (format === 'xlsx') {
353
+ content = await this.exportXLSX(options);
354
+ }
355
+ else if (format === 'png' || format === 'jpg') {
356
+ content = await this.exportImage(format, options);
357
+ }
358
+ else {
359
+ content = await this.exportPDF(options);
360
+ }
330
361
  if (options?.download) {
331
362
  this.downloadContent(content, format, options);
332
363
  return;
@@ -337,18 +368,84 @@ export class BaseChart {
337
368
  * Downloads the exported content as a file
338
369
  */
339
370
  downloadContent(content, format, options) {
340
- const mimeType = format === 'svg' ? 'image/svg+xml' : 'application/json';
341
- const blob = new Blob([content], { type: mimeType });
371
+ const mimeType = this.getMimeType(format);
372
+ const blob = content instanceof Blob ? content : new Blob([content], { type: mimeType });
373
+ const filename = options.filename || `chart.${format}`;
342
374
  const url = URL.createObjectURL(blob);
343
375
  const link = document.createElement('a');
344
376
  link.href = url;
345
- link.download = options.filename || `chart.${format}`;
377
+ link.download = filename;
346
378
  document.body.appendChild(link);
347
379
  link.click();
348
380
  document.body.removeChild(link);
349
381
  URL.revokeObjectURL(url);
350
382
  }
351
- exportSVG(options) {
383
+ getMimeType(format) {
384
+ if (format === 'svg') {
385
+ return 'image/svg+xml';
386
+ }
387
+ if (format === 'json') {
388
+ return 'application/json';
389
+ }
390
+ if (format === 'csv') {
391
+ return 'text/csv;charset=utf-8';
392
+ }
393
+ if (format === 'xlsx') {
394
+ return 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';
395
+ }
396
+ if (format === 'png') {
397
+ return 'image/png';
398
+ }
399
+ if (format === 'jpg') {
400
+ return 'image/jpeg';
401
+ }
402
+ return 'application/pdf';
403
+ }
404
+ exportCSV(options) {
405
+ return serializeCSV(this.data, options);
406
+ }
407
+ exportSize(options) {
408
+ return {
409
+ width: options?.width ?? this.width,
410
+ height: options?.height ?? this.height,
411
+ };
412
+ }
413
+ async exportXLSX(options) {
414
+ return exportXLSXBlob(this.data, options);
415
+ }
416
+ async exportImage(format, options) {
417
+ const { width, height } = this.exportSize(options);
418
+ const svg = this.exportSVG(options, format);
419
+ const backgroundColor = options?.backgroundColor ?? (format === 'jpg' ? '#ffffff' : undefined);
420
+ return exportRasterBlob({
421
+ format,
422
+ svg,
423
+ width,
424
+ height,
425
+ pixelRatio: options?.pixelRatio ?? 1,
426
+ backgroundColor,
427
+ jpegQuality: options?.jpegQuality ?? 0.92,
428
+ });
429
+ }
430
+ async exportPDF(options) {
431
+ const { width, height } = this.exportSize(options);
432
+ const svg = this.exportSVG(options, 'pdf');
433
+ const pngBlob = await exportRasterBlob({
434
+ format: 'png',
435
+ svg,
436
+ width,
437
+ height,
438
+ pixelRatio: options?.pixelRatio ?? 1,
439
+ backgroundColor: options?.backgroundColor ?? '#ffffff',
440
+ jpegQuality: options?.jpegQuality ?? 0.92,
441
+ });
442
+ return exportPDFBlob(pngBlob, {
443
+ width,
444
+ height,
445
+ margin: options?.pdfMargin ?? 0,
446
+ });
447
+ }
448
+ exportSVG(options, formatForHooks = 'svg') {
352
449
  if (!this.svg) {
353
450
  throw new Error('Chart must be rendered before export');
354
451
  }
@@ -360,7 +457,7 @@ export class BaseChart {
360
457
  clone.setAttribute('width', String(exportWidth));
361
458
  clone.setAttribute('height', String(exportHeight));
362
459
  const baseContext = {
363
- format: 'svg',
460
+ format: formatForHooks,
364
461
  options,
365
462
  width: exportWidth,
366
463
  height: exportHeight,
package/donut-chart.js CHANGED
@@ -157,6 +157,9 @@ export class DonutChart extends BaseChart {
157
157
  const cy = this.plotArea.top + this.plotArea.height / 2;
158
158
  const outerRadius = Math.min(this.plotArea.width, this.plotArea.height) / 2;
159
159
  const innerRadius = outerRadius * this.innerRadiusRatio;
160
+ if (this.tooltip) {
161
+ this.tooltip.initialize(this.theme);
162
+ }
160
163
  this.renderSegments(visibleSegments, cx, cy, innerRadius, outerRadius);
161
164
  if (this.centerContent) {
162
165
  this.centerContent.render(this.svg, cx, cy, this.theme);
@@ -169,9 +172,6 @@ export class DonutChart extends BaseChart {
169
172
  }));
170
173
  this.legend.render(this.svg, legendSeries, this.theme, this.width, pos.x, pos.y);
171
174
  }
172
- if (this.tooltip) {
173
- this.tooltip.initialize(this.theme);
174
- }
175
175
  }
176
176
  positionTooltip(event, tooltipDiv) {
177
177
  const node = tooltipDiv.node();
@@ -0,0 +1,12 @@
1
+ type RasterFormat = 'png' | 'jpg';
2
+ export type RasterExportOptions = {
3
+ format: RasterFormat;
4
+ svg: string;
5
+ width: number;
6
+ height: number;
7
+ pixelRatio: number;
8
+ backgroundColor?: string;
9
+ jpegQuality: number;
10
+ };
11
+ export declare function exportRasterBlob(options: RasterExportOptions): Promise<Blob>;
12
+ export {};
@@ -0,0 +1,48 @@
1
+ function loadImage(source) {
2
+ return new Promise((resolve, reject) => {
3
+ const image = new Image();
4
+ image.onload = () => resolve(image);
5
+ image.onerror = () => reject(new Error('Failed to load SVG for raster export'));
6
+ image.src = source;
7
+ });
8
+ }
9
+ function canvasToBlob(canvas, mimeType, quality) {
10
+ return new Promise((resolve, reject) => {
11
+ canvas.toBlob((blob) => {
12
+ if (!blob) {
13
+ reject(new Error('Failed to generate image export'));
14
+ return;
15
+ }
16
+ resolve(blob);
17
+ }, mimeType, quality);
18
+ });
19
+ }
20
+ export async function exportRasterBlob(options) {
21
+ const { svg, width, height, format, jpegQuality } = options;
22
+ const pixelRatio = Math.max(1, options.pixelRatio);
23
+ const objectUrl = URL.createObjectURL(new Blob([svg], { type: 'image/svg+xml;charset=utf-8' }));
24
+ try {
25
+ const image = await loadImage(objectUrl);
26
+ const canvas = document.createElement('canvas');
27
+ const scaledWidth = Math.max(1, Math.round(width * pixelRatio));
28
+ const scaledHeight = Math.max(1, Math.round(height * pixelRatio));
29
+ canvas.width = scaledWidth;
30
+ canvas.height = scaledHeight;
31
+ const context = canvas.getContext('2d');
32
+ if (!context) {
33
+ throw new Error('Failed to get 2D context for image export');
34
+ }
35
+ if (options.backgroundColor) {
36
+ context.fillStyle = options.backgroundColor;
37
+ context.fillRect(0, 0, scaledWidth, scaledHeight);
38
+ }
39
+ context.drawImage(image, 0, 0, scaledWidth, scaledHeight);
40
+ if (format === 'png') {
41
+ return canvasToBlob(canvas, 'image/png');
42
+ }
43
+ return canvasToBlob(canvas, 'image/jpeg', jpegQuality);
44
+ }
45
+ finally {
46
+ URL.revokeObjectURL(objectUrl);
47
+ }
48
+ }
@@ -0,0 +1,8 @@
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 {};
package/export-pdf.js ADDED
@@ -0,0 +1,67 @@
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
+ }
@@ -0,0 +1,9 @@
1
+ import type { DataItem, ExportOptions } from './types.js';
2
+ type TabularData = {
3
+ columns: string[];
4
+ rows: string[][];
5
+ };
6
+ export declare function resolveTableColumns(data: DataItem[], requestedColumns?: string[]): string[];
7
+ export declare function toTabularData(data: DataItem[], requestedColumns?: string[]): TabularData;
8
+ export declare function serializeCSV(data: DataItem[], options?: ExportOptions): string;
9
+ export {};
@@ -0,0 +1,61 @@
1
+ function normalizeTableValue(value) {
2
+ if (value === null || value === undefined) {
3
+ return '';
4
+ }
5
+ if (value instanceof Date) {
6
+ return value.toISOString();
7
+ }
8
+ if (typeof value === 'object') {
9
+ try {
10
+ return JSON.stringify(value);
11
+ }
12
+ catch {
13
+ return String(value);
14
+ }
15
+ }
16
+ return String(value);
17
+ }
18
+ function escapeCSVValue(value, delimiter) {
19
+ const escaped = value.replace(/"/g, '""');
20
+ const needsQuotes = escaped.includes('"') ||
21
+ escaped.includes(delimiter) ||
22
+ escaped.includes('\n') ||
23
+ escaped.includes('\r');
24
+ if (!needsQuotes) {
25
+ return escaped;
26
+ }
27
+ return `"${escaped}"`;
28
+ }
29
+ export function resolveTableColumns(data, requestedColumns) {
30
+ if (requestedColumns && requestedColumns.length > 0) {
31
+ return requestedColumns;
32
+ }
33
+ const columns = [];
34
+ const seen = new Set();
35
+ data.forEach((item) => {
36
+ Object.keys(item).forEach((key) => {
37
+ if (!seen.has(key)) {
38
+ seen.add(key);
39
+ columns.push(key);
40
+ }
41
+ });
42
+ });
43
+ return columns;
44
+ }
45
+ export function toTabularData(data, requestedColumns) {
46
+ const columns = resolveTableColumns(data, requestedColumns);
47
+ const rows = data.map((item) => columns.map((column) => normalizeTableValue(item[column])));
48
+ return {
49
+ columns,
50
+ rows,
51
+ };
52
+ }
53
+ export function serializeCSV(data, options) {
54
+ const delimiter = options?.delimiter ?? ',';
55
+ const { columns, rows } = toTabularData(data, options?.columns);
56
+ const serializedRows = [
57
+ columns.map((column) => escapeCSVValue(column, delimiter)).join(delimiter),
58
+ ...rows.map((row) => row.map((value) => escapeCSVValue(value, delimiter)).join(delimiter)),
59
+ ];
60
+ return serializedRows.join('\n');
61
+ }
@@ -0,0 +1,4 @@
1
+ import type { DataItem, ExportOptions } from './types.js';
2
+ type XlsxLoader = () => Promise<unknown>;
3
+ export declare function exportXLSXBlob(data: DataItem[], options?: ExportOptions, loader?: XlsxLoader): Promise<Blob>;
4
+ export {};
package/export-xlsx.js ADDED
@@ -0,0 +1,44 @@
1
+ import { toTabularData } from './export-tabular.js';
2
+ const XLSX_MIME_TYPE = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';
3
+ function isObject(value) {
4
+ return typeof value === 'object' && value !== null;
5
+ }
6
+ function resolveXlsxApi(moduleValue) {
7
+ if (!isObject(moduleValue)) {
8
+ throw new Error('Invalid "xlsx" module export');
9
+ }
10
+ const moduleCandidate = moduleValue;
11
+ const xlsxCandidate = moduleCandidate.default && isObject(moduleCandidate.default)
12
+ ? moduleCandidate.default
13
+ : moduleCandidate;
14
+ if (!xlsxCandidate.utils || !xlsxCandidate.write) {
15
+ throw new Error('Invalid "xlsx" module export');
16
+ }
17
+ return {
18
+ utils: xlsxCandidate.utils,
19
+ write: xlsxCandidate.write,
20
+ };
21
+ }
22
+ async function defaultXlsxLoader() {
23
+ return import('xlsx');
24
+ }
25
+ export async function exportXLSXBlob(data, options, loader = defaultXlsxLoader) {
26
+ let xlsxModule;
27
+ try {
28
+ xlsxModule = await loader();
29
+ }
30
+ catch {
31
+ throw new Error('XLSX export requires optional dependency "xlsx". Install optional dependency "xlsx".');
32
+ }
33
+ const xlsx = resolveXlsxApi(xlsxModule);
34
+ const { columns, rows } = toTabularData(data, options?.columns);
35
+ const worksheet = xlsx.utils.aoa_to_sheet([columns, ...rows]);
36
+ const workbook = xlsx.utils.book_new();
37
+ const sheetName = options?.sheetName?.trim() || 'Sheet1';
38
+ xlsx.utils.book_append_sheet(workbook, worksheet, sheetName);
39
+ const buffer = xlsx.write(workbook, {
40
+ type: 'array',
41
+ bookType: 'xlsx',
42
+ });
43
+ return new Blob([buffer], { type: XLSX_MIME_TYPE });
44
+ }
package/package.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "0.4.1",
2
+ "version": "0.4.3",
3
3
  "name": "@internetstiftelsen/charts",
4
4
  "type": "module",
5
5
  "sideEffects": false,
@@ -42,16 +42,20 @@
42
42
  "tailwind-merge": "^3.4.0",
43
43
  "tailwindcss": "^4.1.18"
44
44
  },
45
+ "optionalDependencies": {
46
+ "jspdf": "^4.1.0",
47
+ "xlsx": "^0.18.5"
48
+ },
45
49
  "devDependencies": {
46
50
  "@eslint/js": "^9.39.2",
47
51
  "@tailwindcss/vite": "^4.1.18",
48
52
  "@testing-library/dom": "^10.4.1",
49
53
  "@testing-library/jest-dom": "^6.9.1",
50
54
  "@testing-library/react": "^16.3.2",
51
- "@types/node": "^24.10.9",
52
- "@types/react": "^19.2.10",
55
+ "@types/node": "^24.10.13",
56
+ "@types/react": "^19.2.14",
53
57
  "@types/react-dom": "^19.2.3",
54
- "@vitejs/plugin-react-swc": "^4.2.2",
58
+ "@vitejs/plugin-react-swc": "^4.2.3",
55
59
  "eslint": "^9.39.2",
56
60
  "eslint-plugin-react-hooks": "^7.0.1",
57
61
  "eslint-plugin-react-refresh": "^0.4.26",
@@ -61,7 +65,7 @@
61
65
  "tsc-alias": "^1.8.16",
62
66
  "tw-animate-css": "^1.4.0",
63
67
  "typescript": "~5.9.3",
64
- "typescript-eslint": "^8.54.0",
68
+ "typescript-eslint": "^8.55.0",
65
69
  "vite": "^7.3.1",
66
70
  "vitest": "^4.0.18"
67
71
  }
package/types.d.ts CHANGED
@@ -1,11 +1,18 @@
1
1
  export type DataValue = string | number | boolean | Date | null | undefined;
2
2
  export type DataItem = Record<string, any>;
3
- export type ExportFormat = 'svg' | 'json';
3
+ export type ExportFormat = 'svg' | 'json' | 'csv' | 'xlsx' | 'png' | 'jpg' | 'pdf';
4
4
  export type ExportOptions = {
5
5
  download?: boolean;
6
6
  filename?: string;
7
7
  width?: number;
8
8
  height?: number;
9
+ columns?: string[];
10
+ delimiter?: string;
11
+ jpegQuality?: number;
12
+ pixelRatio?: number;
13
+ backgroundColor?: string;
14
+ sheetName?: string;
15
+ pdfMargin?: number;
9
16
  };
10
17
  export type ExportRenderContext = {
11
18
  format: ExportFormat;