From e66a55a03dcd058fe83265048b34e46cd7cbf2b1 Mon Sep 17 00:00:00 2001 From: Bryan Lee Date: Sat, 2 Jul 2022 16:09:34 +0800 Subject: [PATCH 1/4] feat: export data as json and JS object --- src/lib/plugins/addDataExport.ts | 80 ++++++++++++++++++++++++++++++++ src/lib/plugins/index.ts | 1 + src/routes/index.svelte | 24 +++++++--- 3 files changed, 98 insertions(+), 7 deletions(-) create mode 100644 src/lib/plugins/addDataExport.ts diff --git a/src/lib/plugins/addDataExport.ts b/src/lib/plugins/addDataExport.ts new file mode 100644 index 0000000..e2094c5 --- /dev/null +++ b/src/lib/plugins/addDataExport.ts @@ -0,0 +1,80 @@ +import { DataBodyCell } from '$lib/bodyCells'; +import type { BodyRow } from '$lib/bodyRows'; +import type { TablePlugin } from '$lib/types/TablePlugin'; +import { derived, type Readable } from 'svelte/store'; + +export type DataExportFormat = 'object' | 'json'; +type ExportForFormat = { + object: Record[]; + json: string; +}; +export type DataExport = ExportForFormat[F]; + +export interface DataExportConfig { + childrenKey?: string; + format?: F; +} + +export interface DataExportState { + exportedData: Readable>; +} + +export interface DataExportColumnOptions { + exclude?: boolean; +} + +const getObjectsFromRows = ( + rows: BodyRow[], + ids: string[], + childrenKey: string +): Record[] => { + return rows.map((row) => { + const dataObject = Object.fromEntries( + ids.map((id) => { + const cell = row.cellForId[id]; + if (cell instanceof DataBodyCell) { + return [id, cell.value]; + } + return [id, null]; + }) + ); + if (row.subRows !== undefined) { + dataObject[childrenKey] = getObjectsFromRows(row.subRows, ids, childrenKey); + } + return dataObject; + }); +}; + +export const addDataExport = + ({ + format = 'object' as F, + childrenKey = 'children', + }: DataExportConfig = {}): TablePlugin, DataExportColumnOptions> => + ({ tableState, columnOptions }) => { + const excludedIds = Object.entries(columnOptions) + .filter(([, option]) => option.exclude === true) + .map(([columnId]) => columnId); + + const { visibleColumns, rows } = tableState; + + const exportedIds = derived(visibleColumns, ($visibleColumns) => + $visibleColumns.map((c) => c.id).filter((id) => !excludedIds.includes(id)) + ); + + const exportedData = derived([rows, exportedIds], ([$rows, $exportedIds]) => { + switch (format) { + case 'json': + return JSON.stringify( + getObjectsFromRows($rows, $exportedIds, childrenKey) + ) as DataExport; + default: + return getObjectsFromRows($rows, $exportedIds, childrenKey) as DataExport; + } + }); + + const pluginState: DataExportState = { exportedData }; + + return { + pluginState, + }; + }; diff --git a/src/lib/plugins/index.ts b/src/lib/plugins/index.ts index 190bedd..db186fe 100644 --- a/src/lib/plugins/index.ts +++ b/src/lib/plugins/index.ts @@ -7,6 +7,7 @@ export { type ColumnFilterFnProps, } from './addColumnFilters'; export { addColumnOrder } from './addColumnOrder'; +export { addDataExport } from './addDataExport'; export { addExpandedRows } from './addExpandedRows'; export { addGridLayout } from './addGridLayout'; export { addGroupBy } from './addGroupBy'; diff --git a/src/routes/index.svelte b/src/routes/index.svelte index 098be0a..12dca1d 100644 --- a/src/routes/index.svelte +++ b/src/routes/index.svelte @@ -1,21 +1,22 @@

svelte-headless-table

@@ -251,6 +258,9 @@ + + + {#each $headerRows as headerRow (headerRow.id)} From 593364792b0c9046cbc79a0e38ed22142286c82b Mon Sep 17 00:00:00 2001 From: Bryan Lee Date: Sat, 2 Jul 2022 16:17:12 +0800 Subject: [PATCH 2/4] feat: export as csv --- src/lib/plugins/addDataExport.ts | 20 +++++++++++++++++++- src/routes/index.svelte | 5 +++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/src/lib/plugins/addDataExport.ts b/src/lib/plugins/addDataExport.ts index e2094c5..bc44421 100644 --- a/src/lib/plugins/addDataExport.ts +++ b/src/lib/plugins/addDataExport.ts @@ -3,10 +3,11 @@ import type { BodyRow } from '$lib/bodyRows'; import type { TablePlugin } from '$lib/types/TablePlugin'; import { derived, type Readable } from 'svelte/store'; -export type DataExportFormat = 'object' | 'json'; +export type DataExportFormat = 'object' | 'json' | 'csv'; type ExportForFormat = { object: Record[]; json: string; + csv: string; }; export type DataExport = ExportForFormat[F]; @@ -45,6 +46,21 @@ const getObjectsFromRows = ( }); }; +const getCsvFromRows = (rows: BodyRow[], ids: string[]): string => { + const dataLines = rows.map((row) => { + const line = ids.map((id) => { + const cell = row.cellForId[id]; + if (cell instanceof DataBodyCell) { + return cell.value; + } + return null; + }); + return line.join(','); + }); + const headerLine = ids.join(','); + return headerLine + '\n' + dataLines.join('\n'); +}; + export const addDataExport = ({ format = 'object' as F, @@ -67,6 +83,8 @@ export const addDataExport = return JSON.stringify( getObjectsFromRows($rows, $exportedIds, childrenKey) ) as DataExport; + case 'csv': + return getCsvFromRows($rows, $exportedIds) as DataExport; default: return getObjectsFromRows($rows, $exportedIds, childrenKey) as DataExport; } diff --git a/src/routes/index.svelte b/src/routes/index.svelte index 12dca1d..9f6a3fd 100644 --- a/src/routes/index.svelte +++ b/src/routes/index.svelte @@ -61,6 +61,9 @@ exportJson: addDataExport({ format: 'json', }), + exportCsv: addDataExport({ + format: 'csv', + }), }); const columns = table.createColumns([ @@ -245,6 +248,7 @@ const { columnWidths } = pluginStates.resize; const { exportedData } = pluginStates.export; const { exportedData: exportedJson } = pluginStates.exportJson; + const { exportedData: exportedCsv } = pluginStates.exportCsv;

svelte-headless-table

@@ -260,6 +264,7 @@ +
From 17d677da62fa06f064e809c61835bfe7cc9844b4 Mon Sep 17 00:00:00 2001 From: Bryan Lee Date: Sat, 2 Jul 2022 16:18:06 +0800 Subject: [PATCH 3/4] bump version --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 69717d4..d476b32 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "svelte-headless-table", - "version": "0.10.4", + "version": "0.11.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "svelte-headless-table", - "version": "0.10.4", + "version": "0.11.0", "license": "MIT", "dependencies": { "svelte-keyed": "^1.1.5", diff --git a/package.json b/package.json index af58889..ebe8e71 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "svelte-headless-table", - "version": "0.10.4", + "version": "0.11.0", "scripts": { "dev": "svelte-kit dev", "build": "svelte-kit build", From 09687aab3fd689c1bf651c1f0f156597d21cc9ed Mon Sep 17 00:00:00 2001 From: Bryan Lee Date: Sat, 2 Jul 2022 22:39:50 +0800 Subject: [PATCH 4/4] doc: document addDataExport --- .../[...3]plugins/[...14]add-data-export.md | 110 ++++++++++++++++++ 1 file changed, 110 insertions(+) create mode 100644 docs/src/routes/docs/[...3]plugins/[...14]add-data-export.md diff --git a/docs/src/routes/docs/[...3]plugins/[...14]add-data-export.md b/docs/src/routes/docs/[...3]plugins/[...14]add-data-export.md new file mode 100644 index 0000000..02b4bb3 --- /dev/null +++ b/docs/src/routes/docs/[...3]plugins/[...14]add-data-export.md @@ -0,0 +1,110 @@ +--- +title: addDataExport +description: Export the transformed table as a new dataset. +sidebar_title: addDataExport +--- + + + +# {$frontmatter.title} + +`addDataExport` allows for reading the data source as it is currently transformed by the table. + +This is useful if you need to export data from the table with all plugin transformations applied. + +## Options + +:::callout +Options passed into `addDataExport`. +::: + +```ts {3} +const table = createTable(data, { + export: addDataExport({ ... }), +}); +``` + +### `format?: 'object' | 'json' | 'csv'` + +Sets the exported data format. + +_Defaults to `'object'`_. + +### `childrenKey?: string` + +The property key to store sub-rows under. + +This only applies if `format` is `'object'` or `'json'`. + +_Defaults to `'children'`_. + +## Column Options + +:::callout +Options passed into column definitions. +::: + +```ts {7} +const columns = table.createColumns([ + table.column({ + header: 'Name', + accessor: 'name', + plugins: { + export: { ... }, + }, + }), +]); +``` + +### `exclude?: boolean` + +Excludes the column from the data export. + +_Defaults to `false`_. + +## Prop Set + +:::callout +Extensions to the view model. + +Subscribe to `.props()` on the respective table components. +::: + +```svelte +{#each $headerRows as headerRow (headerRow.id)} + + {rowProps.export} + {#each headerRow.cells as cell (cell.id)} + + {props.export} + + {/each} + +{/each} +``` + +_Nothing here so far_. + +## Plugin State + +:::callout +State provided by `addDataExport`. +::: + +```ts {3} +const { headerRows, rows, pluginStates } = table.createViewModel(columns); +const { ... } = pluginStates.export; +``` + +### `exportedData: Readable` + +The exported data. `DataExport` is: + +- `Record[]` if `format` is `'object'`, +- `string` if `format` is `'json'`, +- `string` if `format` is `'csv'`, + +Either subscribe to `exportedData` or use [`get`](https://svelte.dev/docs#run-time-svelte-store-get) to compute the exported data once.