Skip to content

Commit

Permalink
Merge 09687aa into 284dad2
Browse files Browse the repository at this point in the history
  • Loading branch information
bryanmylee committed Jul 2, 2022
2 parents 284dad2 + 09687aa commit 61d2604
Show file tree
Hide file tree
Showing 6 changed files with 234 additions and 10 deletions.
110 changes: 110 additions & 0 deletions docs/src/routes/docs/[...3]plugins/[...14]add-data-export.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
---
title: addDataExport
description: Export the transformed table as a new dataset.
sidebar_title: addDataExport
---

<script>
import { useHljs } from '$lib/utils/useHljs';
useHljs('ts');
</script>

# {$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)}
<Subscribe rowProps={headerRow.props()} let:rowProps>
{rowProps.export} <!-- HeaderRow props -->
{#each headerRow.cells as cell (cell.id)}
<Subscribe props={cell.props()} let:props>
{props.export} <!-- HeaderCell props -->
</Subscribe>
{/each}
</Subscribe>
{/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<DataExport>`

The exported data. `DataExport` is:

- `Record<string, unknown>[]` 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.
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -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",
Expand Down
98 changes: 98 additions & 0 deletions src/lib/plugins/addDataExport.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
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' | 'csv';
type ExportForFormat = {
object: Record<string, unknown>[];
json: string;
csv: string;
};
export type DataExport<F extends DataExportFormat> = ExportForFormat[F];

export interface DataExportConfig<F extends DataExportFormat> {
childrenKey?: string;
format?: F;
}

export interface DataExportState<F extends DataExportFormat> {
exportedData: Readable<DataExport<F>>;
}

export interface DataExportColumnOptions {
exclude?: boolean;
}

const getObjectsFromRows = <Item>(
rows: BodyRow<Item>[],
ids: string[],
childrenKey: string
): Record<string, unknown>[] => {
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;
});
};

const getCsvFromRows = <Item>(rows: BodyRow<Item>[], 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 =
<Item, F extends DataExportFormat = 'object'>({
format = 'object' as F,
childrenKey = 'children',
}: DataExportConfig<F> = {}): TablePlugin<Item, DataExportState<F>, 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<F>;
case 'csv':
return getCsvFromRows($rows, $exportedIds) as DataExport<F>;
default:
return getObjectsFromRows($rows, $exportedIds, childrenKey) as DataExport<F>;
}
});

const pluginState: DataExportState<F> = { exportedData };

return {
pluginState,
};
};
1 change: 1 addition & 0 deletions src/lib/plugins/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down
29 changes: 22 additions & 7 deletions src/routes/index.svelte
Original file line number Diff line number Diff line change
@@ -1,21 +1,22 @@
<script lang="ts">
import { derived, readable } from 'svelte/store';
import { derived, readable, get } from 'svelte/store';
import { Render, Subscribe, createTable, createRender } from '$lib';
import {
addColumnFilters,
addColumnOrder,
addDataExport,
addExpandedRows,
addGroupBy,
addHiddenColumns,
addPagination,
addResizedColumns,
addSelectedRows,
addSortBy,
addSubRows,
addTableFilter,
addPagination,
addExpandedRows,
matchFilter,
numberRangeFilter,
textPrefixFilter,
addSubRows,
addGroupBy,
addSelectedRows,
addResizedColumns,
} from '$lib/plugins';
import { mean, sum } from '$lib/utils/math';
import { getShuffled } from './_getShuffled';
Expand Down Expand Up @@ -56,6 +57,13 @@
initialPageSize: 20,
}),
resize: addResizedColumns(),
export: addDataExport(),
exportJson: addDataExport({
format: 'json',
}),
exportCsv: addDataExport({
format: 'csv',
}),
});
const columns = table.createColumns([
Expand Down Expand Up @@ -238,6 +246,9 @@
const { hiddenColumnIds } = pluginStates.hideColumns;
$hiddenColumnIds = ['progress'];
const { columnWidths } = pluginStates.resize;
const { exportedData } = pluginStates.export;
const { exportedData: exportedJson } = pluginStates.exportJson;
const { exportedData: exportedCsv } = pluginStates.exportCsv;
</script>

<h1>svelte-headless-table</h1>
Expand All @@ -251,6 +262,10 @@
<input id="page-size" type="number" min={1} bind:value={$pageSize} />
</div>

<button on:click={() => console.log(get(exportedData))}>Export as object</button>
<button on:click={() => console.log(get(exportedJson))}>Export as JSON</button>
<button on:click={() => console.log(get(exportedCsv))}>Export as CSV</button>

<table {...$tableAttrs}>
<thead>
{#each $headerRows as headerRow (headerRow.id)}
Expand Down

0 comments on commit 61d2604

Please sign in to comment.