compote-ui 0.85.0 → 0.86.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/dist/components/data-table-v9/xlsx/index.d.ts +17 -0
- package/dist/components/data-table-v9/xlsx/index.js +10 -0
- package/dist/components/data-table-v9/xlsx/xlsx-rows.d.ts +31 -0
- package/dist/components/data-table-v9/xlsx/xlsx-rows.js +60 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/package.json +1 -1
- package/skills/data-table/SKILL.md +1 -0
- package/skills/data-table/references/export.md +49 -0
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import type { DataTableExportData } from '../export/export-data';
|
|
2
2
|
import type { DataTableExporter } from '../export/types';
|
|
3
3
|
import { type XlsxCell, type XlsxDateFormats } from './xlsx-cell';
|
|
4
|
+
import { type XlsxRowsColumn } from './xlsx-rows';
|
|
4
5
|
export { DEFAULT_XLSX_DATE_FORMATS, toXlsxCell, xlsxNumberFormat } from './xlsx-cell';
|
|
5
6
|
export type { XlsxCell, XlsxDateFormats, XlsxDatePart } from './xlsx-cell';
|
|
7
|
+
export { rowsToExportData } from './xlsx-rows';
|
|
8
|
+
export type { XlsxRowsColumn } from './xlsx-rows';
|
|
6
9
|
export type XlsxExporterOptions = Partial<XlsxDateFormats> & {
|
|
7
10
|
id?: string;
|
|
8
11
|
label?: string;
|
|
@@ -21,6 +24,20 @@ export declare function exportDataToSheet(data: DataTableExportData, formats?: X
|
|
|
21
24
|
* first call, so importing this module costs nothing until an export runs — and stays SSR-safe.
|
|
22
25
|
*/
|
|
23
26
|
export declare function exportDataToXlsxBlob(data: DataTableExportData, options?: XlsxExporterOptions): Promise<Blob>;
|
|
27
|
+
export type XlsxRowsOptions = XlsxExporterOptions & {
|
|
28
|
+
/**
|
|
29
|
+
* Locale the values are formatted in: the text that sets column widths, the text of cells
|
|
30
|
+
* written as text (a number format Excel can't express), and the decimals of a number format.
|
|
31
|
+
* Default `en-US`.
|
|
32
|
+
*/
|
|
33
|
+
locale?: string;
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* Plain row objects as an .xlsx file, for an export that isn't a DataTable: typed cells, a bold
|
|
37
|
+
* frozen header row and content-fitted column widths, exactly like a table export. Download
|
|
38
|
+
* the result with `downloadBlob(blob, 'name.xlsx')`.
|
|
39
|
+
*/
|
|
40
|
+
export declare function exportRowsToXlsxBlob(rows: ReadonlyArray<Record<string, unknown>>, columns: ReadonlyArray<XlsxRowsColumn>, options?: XlsxRowsOptions): Promise<Blob>;
|
|
24
41
|
/**
|
|
25
42
|
* An Excel exporter: numbers, dates and booleans as typed cells, formatted like the table.
|
|
26
43
|
* Needs `write-excel-file` installed.
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { PhMicrosoftExcelLogo } from '../../../icons';
|
|
2
2
|
import { downloadBlob } from '../../../utils/download';
|
|
3
3
|
import { DEFAULT_XLSX_DATE_FORMATS, toXlsxCell } from './xlsx-cell';
|
|
4
|
+
import { rowsToExportData } from './xlsx-rows';
|
|
4
5
|
export { DEFAULT_XLSX_DATE_FORMATS, toXlsxCell, xlsxNumberFormat } from './xlsx-cell';
|
|
6
|
+
export { rowsToExportData } from './xlsx-rows';
|
|
5
7
|
// Column widths in characters of Excel's default font.
|
|
6
8
|
const MIN_COLUMN_WIDTH = 8;
|
|
7
9
|
const MAX_COLUMN_WIDTH = 60;
|
|
@@ -58,6 +60,14 @@ export async function exportDataToXlsxBlob(data, options = {}) {
|
|
|
58
60
|
columns: xlsxColumnWidths(data).map((width) => ({ width }))
|
|
59
61
|
}).toBlob();
|
|
60
62
|
}
|
|
63
|
+
/**
|
|
64
|
+
* Plain row objects as an .xlsx file, for an export that isn't a DataTable: typed cells, a bold
|
|
65
|
+
* frozen header row and content-fitted column widths, exactly like a table export. Download
|
|
66
|
+
* the result with `downloadBlob(blob, 'name.xlsx')`.
|
|
67
|
+
*/
|
|
68
|
+
export async function exportRowsToXlsxBlob(rows, columns, options = {}) {
|
|
69
|
+
return exportDataToXlsxBlob(rowsToExportData(rows, columns, options.locale), options);
|
|
70
|
+
}
|
|
61
71
|
function pickFormats({ date, time, dateTime }) {
|
|
62
72
|
return {
|
|
63
73
|
...(date !== undefined ? { date } : {}),
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { DataTableExportData } from '../export/export-data';
|
|
2
|
+
import type { DataTableColumnType } from '../types';
|
|
3
|
+
/**
|
|
4
|
+
* A column of plain row objects for `exportRowsToXlsxBlob`: which key to read, and how the
|
|
5
|
+
* values are typed (the same `type` and `formatOptions` a DataTable column has).
|
|
6
|
+
*/
|
|
7
|
+
export type XlsxRowsColumn = {
|
|
8
|
+
/** The key to read from each row object. */
|
|
9
|
+
key: string;
|
|
10
|
+
/** The header cell; defaults to `key`. */
|
|
11
|
+
header?: string;
|
|
12
|
+
/**
|
|
13
|
+
* How the value is written. `number`, `currency` and `percent` become Excel numbers, `date`,
|
|
14
|
+
* `time` and `date-time` Excel dates, `boolean` TRUE/FALSE. Without a type (or `text`) the
|
|
15
|
+
* value is written as text, so a numeric column needs a type to stay computable.
|
|
16
|
+
*/
|
|
17
|
+
type?: DataTableColumnType;
|
|
18
|
+
/**
|
|
19
|
+
* Intl options for the Excel format of a typed column: `{ minimumFractionDigits: 2,
|
|
20
|
+
* maximumFractionDigits: 2 }` for fixed decimals, `{ useGrouping: false,
|
|
21
|
+
* maximumFractionDigits: 0 }` for whole numbers without a thousands separator (`useGrouping:
|
|
22
|
+
* false` alone keeps the decimals a value has). A `number` column with none shows as many
|
|
23
|
+
* decimals as each value has (up to 3).
|
|
24
|
+
*/
|
|
25
|
+
formatOptions?: Intl.NumberFormatOptions | Intl.DateTimeFormatOptions;
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
* Plain row objects as the export data the xlsx exporter takes, so they get the same typed
|
|
29
|
+
* cells, bold frozen header and content-fitted widths as a DataTable export.
|
|
30
|
+
*/
|
|
31
|
+
export declare function rowsToExportData(rows: ReadonlyArray<Record<string, unknown>>, columns: ReadonlyArray<XlsxRowsColumn>, locale?: string): DataTableExportData;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { TYPE_NUMBER_FORMAT_DEFAULTS } from '../data-table-utils';
|
|
2
|
+
import { formatDateCell } from '../date-cell';
|
|
3
|
+
const NUMBER_TYPES = new Set(['number', 'currency', 'percent']);
|
|
4
|
+
const isEmpty = (value) => value === null || value === undefined || value === '';
|
|
5
|
+
/**
|
|
6
|
+
* The text a DataTable cell of this type would show (same rules as the table's own cell text):
|
|
7
|
+
* what text and fallback cells write, and what column widths are measured on. `''` when the
|
|
8
|
+
* table would show `-`.
|
|
9
|
+
*/
|
|
10
|
+
function cellText(column, value, locale) {
|
|
11
|
+
if (isEmpty(value))
|
|
12
|
+
return '';
|
|
13
|
+
const { type } = column;
|
|
14
|
+
try {
|
|
15
|
+
if (type && NUMBER_TYPES.has(type)) {
|
|
16
|
+
// Like the table: numeric strings count as numbers, anything else shows nothing.
|
|
17
|
+
const number = Number(value);
|
|
18
|
+
if (Number.isNaN(number))
|
|
19
|
+
return '';
|
|
20
|
+
return new Intl.NumberFormat(locale, {
|
|
21
|
+
...TYPE_NUMBER_FORMAT_DEFAULTS[type],
|
|
22
|
+
...column.formatOptions
|
|
23
|
+
}).format(number);
|
|
24
|
+
}
|
|
25
|
+
if (type === 'date' || type === 'time' || type === 'date-time') {
|
|
26
|
+
// Reads a Date, an ISO/DB string, an epoch number... the way the table does.
|
|
27
|
+
return (formatDateCell(value, type, locale, column.formatOptions) ??
|
|
28
|
+
'');
|
|
29
|
+
}
|
|
30
|
+
if (type === 'boolean')
|
|
31
|
+
return value ? 'Yes' : 'No';
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
// Invalid Intl options: fall back to the plain string.
|
|
35
|
+
}
|
|
36
|
+
return String(value);
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Plain row objects as the export data the xlsx exporter takes, so they get the same typed
|
|
40
|
+
* cells, bold frozen header and content-fitted widths as a DataTable export.
|
|
41
|
+
*/
|
|
42
|
+
export function rowsToExportData(rows, columns, locale = 'en-US') {
|
|
43
|
+
return {
|
|
44
|
+
columns: columns.map((column) => ({
|
|
45
|
+
id: column.key,
|
|
46
|
+
header: column.header ?? column.key,
|
|
47
|
+
type: column.type,
|
|
48
|
+
formatOptions: column.formatOptions,
|
|
49
|
+
hasCustomCell: false,
|
|
50
|
+
hasExportValue: false,
|
|
51
|
+
locale,
|
|
52
|
+
// Only a table has a width in px; an xlsx export measures the content instead.
|
|
53
|
+
size: 0
|
|
54
|
+
})),
|
|
55
|
+
rows: rows.map((row) => columns.map((column) => {
|
|
56
|
+
const value = row[column.key];
|
|
57
|
+
return { value, text: cellText(column, value, locale) };
|
|
58
|
+
}))
|
|
59
|
+
};
|
|
60
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -11,6 +11,7 @@ export * as HoverCard from './components/hover-card';
|
|
|
11
11
|
export * as ScrollArea from './components/scroll-area';
|
|
12
12
|
export { loadImage, fileToDataUrl, cropImage, processImage } from './utils/image-processing';
|
|
13
13
|
export type { ProcessImageOptions, CropRegion } from './utils/image-processing';
|
|
14
|
+
export { downloadBlob } from './utils/download';
|
|
14
15
|
export * as Carousel from './components/carousel';
|
|
15
16
|
export { default as Checkbox } from './components/checkbox/checkbox.svelte';
|
|
16
17
|
export { default as CheckboxGroup } from './components/checkbox/checkbox-group.svelte';
|
package/dist/index.js
CHANGED
|
@@ -9,6 +9,7 @@ export * as Collapsible from './components/collapsible';
|
|
|
9
9
|
export * as HoverCard from './components/hover-card';
|
|
10
10
|
export * as ScrollArea from './components/scroll-area';
|
|
11
11
|
export { loadImage, fileToDataUrl, cropImage, processImage } from './utils/image-processing';
|
|
12
|
+
export { downloadBlob } from './utils/download';
|
|
12
13
|
export * as Carousel from './components/carousel';
|
|
13
14
|
export { default as Checkbox } from './components/checkbox/checkbox.svelte';
|
|
14
15
|
export { default as CheckboxGroup } from './components/checkbox/checkbox-group.svelte';
|
package/package.json
CHANGED
|
@@ -34,6 +34,7 @@ sources:
|
|
|
34
34
|
- src/lib/components/data-table-v9/toolbar/data-table-export.svelte
|
|
35
35
|
- src/lib/components/data-table-v9/xlsx/index.ts
|
|
36
36
|
- src/lib/components/data-table-v9/xlsx/xlsx-cell.ts
|
|
37
|
+
- src/lib/components/data-table-v9/xlsx/xlsx-rows.ts
|
|
37
38
|
- src/lib/components/data-table-v9/virtual/data-table-virtualized.svelte
|
|
38
39
|
- src/lib/components/data-table-v9/virtual/data-table-virtual-rows.svelte
|
|
39
40
|
---
|
|
@@ -107,6 +107,55 @@ const excel = createXlsxExporter({
|
|
|
107
107
|
`exportDataToXlsxBlob(data, options)` returns the file as a `Blob` (e.g. to upload instead of
|
|
108
108
|
download), and `toXlsxCell` maps one cell.
|
|
109
109
|
|
|
110
|
+
### Plain rows (an export that isn't a table)
|
|
111
|
+
|
|
112
|
+
`exportRowsToXlsxBlob(rows, columns, options)` writes row objects with the same typed cells, bold
|
|
113
|
+
frozen header and content-fitted widths. `columns` are `{ key, header?, type?, formatOptions? }`;
|
|
114
|
+
`options` are the exporter options plus `locale` (default `en-US`), the locale values are
|
|
115
|
+
formatted in: it sets the text column widths are measured on, the text of cells written as text
|
|
116
|
+
(a number format Excel can't express, such as `notation: 'compact'`), and the decimals of a
|
|
117
|
+
number format.
|
|
118
|
+
`downloadBlob` (exported from `compote-ui`) hands the file to the browser. Browser-only: call it
|
|
119
|
+
from an event handler.
|
|
120
|
+
|
|
121
|
+
```ts
|
|
122
|
+
import { downloadBlob } from 'compote-ui';
|
|
123
|
+
import { exportRowsToXlsxBlob } from 'compote-ui/data-table/xlsx';
|
|
124
|
+
|
|
125
|
+
const blob = await exportRowsToXlsxBlob(
|
|
126
|
+
rows, // Record<string, unknown>[]
|
|
127
|
+
[
|
|
128
|
+
{ key: 'sku', header: 'SKU' },
|
|
129
|
+
{
|
|
130
|
+
key: 'qty',
|
|
131
|
+
header: 'Qty',
|
|
132
|
+
type: 'number',
|
|
133
|
+
formatOptions: { useGrouping: false, maximumFractionDigits: 0 }
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
key: 'price',
|
|
137
|
+
header: 'Price',
|
|
138
|
+
type: 'number',
|
|
139
|
+
formatOptions: { minimumFractionDigits: 2, maximumFractionDigits: 2 }
|
|
140
|
+
}
|
|
141
|
+
],
|
|
142
|
+
{ sheet: 'Cart' }
|
|
143
|
+
);
|
|
144
|
+
downloadBlob(blob, 'cart.xlsx');
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
- A column without a `type` (or `text`) is written as text, so a numeric column needs
|
|
148
|
+
`type: 'number'` to stay computable in Excel; a text SKU such as `00123` keeps its zeros.
|
|
149
|
+
- A `number` column with no `formatOptions` shows as many decimals as each value has (up to 3):
|
|
150
|
+
give prices `minimumFractionDigits`/`maximumFractionDigits` for a fixed `#,##0.00`, and
|
|
151
|
+
`{ useGrouping: false, maximumFractionDigits: 0 }` for a plain `0` (quantities, ids);
|
|
152
|
+
`useGrouping: false` alone only drops the thousands separator, so `1.5` would be `0.0`.
|
|
153
|
+
- Values are read like the table reads them: a numeric string in a `number` column is a number,
|
|
154
|
+
a `date` column takes a `Date`, an ISO string or an epoch number, and `NaN` or text that isn't
|
|
155
|
+
a number or date is an empty cell.
|
|
156
|
+
- `null`, `undefined` and `''` are empty cells. `rowsToExportData(rows, columns, locale)` is the
|
|
157
|
+
conversion alone, if you want to pass the result to `exportDataToXlsxBlob` yourself.
|
|
158
|
+
|
|
110
159
|
## Custom exporter
|
|
111
160
|
|
|
112
161
|
An exporter is `{ id, label, icon?, export(context) }`. `context` holds `table`, `scope`
|