@humanspeak/svelte-headless-table 5.0.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/LICENSE +22 -0
- package/README.md +122 -0
- package/dist/bodyCells.d.ts +53 -0
- package/dist/bodyCells.js +90 -0
- package/dist/bodyRows.d.ts +82 -0
- package/dist/bodyRows.js +256 -0
- package/dist/columns.d.ts +76 -0
- package/dist/columns.js +96 -0
- package/dist/constants.d.ts +1 -0
- package/dist/constants.js +1 -0
- package/dist/createTable.d.ts +17 -0
- package/dist/createTable.js +36 -0
- package/dist/createViewModel.d.ts +38 -0
- package/dist/createViewModel.js +230 -0
- package/dist/headerCells.d.ts +80 -0
- package/dist/headerCells.js +147 -0
- package/dist/headerRows.d.ts +35 -0
- package/dist/headerRows.js +185 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.js +12 -0
- package/dist/plugins/addColumnFilters.d.ts +39 -0
- package/dist/plugins/addColumnFilters.js +117 -0
- package/dist/plugins/addColumnOrder.d.ts +10 -0
- package/dist/plugins/addColumnOrder.js +24 -0
- package/dist/plugins/addDataExport.d.ts +21 -0
- package/dist/plugins/addDataExport.js +68 -0
- package/dist/plugins/addExpandedRows.d.ts +17 -0
- package/dist/plugins/addExpandedRows.js +51 -0
- package/dist/plugins/addFlatten.d.ts +19 -0
- package/dist/plugins/addFlatten.js +38 -0
- package/dist/plugins/addGridLayout.d.ts +2 -0
- package/dist/plugins/addGridLayout.js +73 -0
- package/dist/plugins/addGroupBy.d.ts +40 -0
- package/dist/plugins/addGroupBy.js +192 -0
- package/dist/plugins/addHiddenColumns.d.ts +9 -0
- package/dist/plugins/addHiddenColumns.js +17 -0
- package/dist/plugins/addPagination.d.ts +39 -0
- package/dist/plugins/addPagination.js +84 -0
- package/dist/plugins/addResizedColumns.d.ts +41 -0
- package/dist/plugins/addResizedColumns.js +252 -0
- package/dist/plugins/addSelectedRows.d.ts +29 -0
- package/dist/plugins/addSelectedRows.js +190 -0
- package/dist/plugins/addSortBy.d.ts +46 -0
- package/dist/plugins/addSortBy.js +176 -0
- package/dist/plugins/addSubRows.d.ts +9 -0
- package/dist/plugins/addSubRows.js +28 -0
- package/dist/plugins/addTableFilter.d.ts +28 -0
- package/dist/plugins/addTableFilter.js +95 -0
- package/dist/plugins/index.d.ts +15 -0
- package/dist/plugins/index.js +16 -0
- package/dist/plugins/package.json +5 -0
- package/dist/tableComponent.d.ts +19 -0
- package/dist/tableComponent.js +35 -0
- package/dist/types/Action.d.ts +5 -0
- package/dist/types/Action.js +1 -0
- package/dist/types/Entries.d.ts +3 -0
- package/dist/types/Entries.js +1 -0
- package/dist/types/KeyPath.d.ts +6 -0
- package/dist/types/KeyPath.js +1 -0
- package/dist/types/Label.d.ts +8 -0
- package/dist/types/Label.js +1 -0
- package/dist/types/Matrix.d.ts +1 -0
- package/dist/types/Matrix.js +1 -0
- package/dist/types/TablePlugin.d.ts +88 -0
- package/dist/types/TablePlugin.js +1 -0
- package/dist/utils/array.d.ts +2 -0
- package/dist/utils/array.js +9 -0
- package/dist/utils/attributes.d.ts +2 -0
- package/dist/utils/attributes.js +23 -0
- package/dist/utils/clone.d.ts +13 -0
- package/dist/utils/clone.js +18 -0
- package/dist/utils/compare.d.ts +2 -0
- package/dist/utils/compare.js +17 -0
- package/dist/utils/counter.d.ts +1 -0
- package/dist/utils/counter.js +7 -0
- package/dist/utils/css.d.ts +1 -0
- package/dist/utils/css.js +5 -0
- package/dist/utils/event.d.ts +1 -0
- package/dist/utils/event.js +5 -0
- package/dist/utils/filter.d.ts +4 -0
- package/dist/utils/filter.js +4 -0
- package/dist/utils/math.d.ts +2 -0
- package/dist/utils/math.js +2 -0
- package/dist/utils/matrix.d.ts +3 -0
- package/dist/utils/matrix.js +23 -0
- package/dist/utils/store.d.ts +37 -0
- package/dist/utils/store.js +123 -0
- package/package.json +98 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
Copyright (c) 2024-2025 Humanspeak, Inc.
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2022-2024 Bryan Lee
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
|
6
|
+
a copy of this software and associated documentation files (the
|
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
|
11
|
+
the following conditions:
|
|
12
|
+
|
|
13
|
+
The above copyright notice and this permission notice shall be
|
|
14
|
+
included in all copies or substantial portions of the Software.
|
|
15
|
+
|
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="https://user-images.githubusercontent.com/42545742/169733428-295e2678-e509-4175-aeb3-cb3a9c9894e1.svg" alt="svelte-headless-table" width="400px"/>
|
|
3
|
+
</p>
|
|
4
|
+
<h1 align="center">Svelte Headless Table</h1>
|
|
5
|
+
|
|
6
|
+
<div align="center">
|
|
7
|
+
|
|
8
|
+
[](https://www.npmjs.com/package/svelte-headless-table)
|
|
9
|
+
[](https://www.npmjs.com/package/svelte-headless-table)
|
|
10
|
+

|
|
11
|
+

|
|
12
|
+
|
|
13
|
+
<!--[](https://coveralls.io/github/humanspeak/svelte-headless-table?branch=main)-->
|
|
14
|
+
|
|
15
|
+
</div>
|
|
16
|
+
|
|
17
|
+
**Unopinionated and extensible data tables for Svelte**
|
|
18
|
+
|
|
19
|
+
> Build and design powerful datagrid experiences while retaining 100% control over styles and markup.
|
|
20
|
+
|
|
21
|
+
Visit the [documentation](https://svelte-headless-table.bryanmylee.com/) for code examples and API reference, and get started with the [quick start guide](https://svelte-headless-table.bryanmylee.com/docs/getting-started/quick-start)!
|
|
22
|
+
|
|
23
|
+
## Why Svelte Headless Table?
|
|
24
|
+
|
|
25
|
+
Svelte Headless Table is designed to work **seamlessly** with Svelte. If you love Svelte, you will love Svelte Headless Table.
|
|
26
|
+
|
|
27
|
+
- **Full TypeScript support**
|
|
28
|
+
- Compatible with **SvelteKit** and SSR
|
|
29
|
+
- Manage state with Svelte stores
|
|
30
|
+
- Headless and fully customizable
|
|
31
|
+
- Intuitive column-first declarative model
|
|
32
|
+
- Highly performant
|
|
33
|
+
- Feature-rich
|
|
34
|
+
|
|
35
|
+
## All the features you could ever need!
|
|
36
|
+
|
|
37
|
+
Svelte Headless Table comes with an extensive suite of plugins.
|
|
38
|
+
|
|
39
|
+
Easily extend Svelte Headless Table with complex **sorting**, **filtering**, **grouping**, **pagination**, and much more.
|
|
40
|
+
|
|
41
|
+
### Plugin roadmap
|
|
42
|
+
|
|
43
|
+
- [x] [addSortBy](https://svelte-headless-table.bryanmylee.com/docs/plugins/add-sort-by)
|
|
44
|
+
- [x] [addColumnFilters](https://svelte-headless-table.bryanmylee.com/docs/plugins/add-column-filters)
|
|
45
|
+
- [x] [addTableFilter](https://svelte-headless-table.bryanmylee.com/docs/plugins/add-table-filter)
|
|
46
|
+
- [x] [addColumnOrder](https://svelte-headless-table.bryanmylee.com/docs/plugins/add-column-order)
|
|
47
|
+
- [x] [addHiddenColumns](https://svelte-headless-table.bryanmylee.com/docs/plugins/add-hidden-columns)
|
|
48
|
+
- [x] [addPagination](https://svelte-headless-table.bryanmylee.com/docs/plugins/add-pagination)
|
|
49
|
+
- [x] [addSubRows](https://svelte-headless-table.bryanmylee.com/docs/plugins/add-sub-rows)
|
|
50
|
+
- [x] [addGroupBy](https://svelte-headless-table.bryanmylee.com/docs/plugins/add-group-by)
|
|
51
|
+
- [x] [addExpandedRows](https://svelte-headless-table.bryanmylee.com/docs/plugins/add-expanded-rows)
|
|
52
|
+
- [x] [addSelectedRows](https://svelte-headless-table.bryanmylee.com/docs/plugins/add-selected-rows)
|
|
53
|
+
- [x] [addResizedColumns](https://svelte-headless-table.bryanmylee.com/docs/plugins/add-resized-columns)
|
|
54
|
+
- [x] [addGridLayout](https://svelte-headless-table.bryanmylee.com/docs/plugins/add-grid-layout)
|
|
55
|
+
|
|
56
|
+
## Examples
|
|
57
|
+
|
|
58
|
+
<!-- prettier-ignore -->
|
|
59
|
+
```svelte
|
|
60
|
+
<script>
|
|
61
|
+
const data = readable([
|
|
62
|
+
{ name: 'Ada Lovelace', age: 21 },
|
|
63
|
+
{ name: 'Barbara Liskov', age: 52 },
|
|
64
|
+
{ name: 'Richard Hamming', age: 38 },
|
|
65
|
+
]);
|
|
66
|
+
|
|
67
|
+
const table = createTable(data);
|
|
68
|
+
|
|
69
|
+
const columns = table.createColumns([
|
|
70
|
+
table.column({
|
|
71
|
+
header: 'Name',
|
|
72
|
+
accessor: 'name',
|
|
73
|
+
}),
|
|
74
|
+
table.column({
|
|
75
|
+
header: 'Age',
|
|
76
|
+
accessor: 'age',
|
|
77
|
+
}),
|
|
78
|
+
]);
|
|
79
|
+
|
|
80
|
+
const {
|
|
81
|
+
headerRows,
|
|
82
|
+
rows,
|
|
83
|
+
tableAttrs,
|
|
84
|
+
tableBodyAttrs,
|
|
85
|
+
} = table.createViewModel(columns);
|
|
86
|
+
</script>
|
|
87
|
+
|
|
88
|
+
<table {...$tableAttrs}>
|
|
89
|
+
<thead>
|
|
90
|
+
{#each $headerRows as headerRow (headerRow.id)}
|
|
91
|
+
<Subscribe rowAttrs={headerRow.attrs()} let:rowAttrs>
|
|
92
|
+
<tr {...rowAttrs}>
|
|
93
|
+
{#each headerRow.cells as cell (cell.id)}
|
|
94
|
+
<Subscribe attrs={cell.attrs()} let:attrs>
|
|
95
|
+
<th {...attrs}>
|
|
96
|
+
<Render of={cell.render()} />
|
|
97
|
+
</th>
|
|
98
|
+
</Subscribe>
|
|
99
|
+
{/each}
|
|
100
|
+
</tr>
|
|
101
|
+
</Subscribe>
|
|
102
|
+
{/each}
|
|
103
|
+
</thead>
|
|
104
|
+
<tbody {...$tableBodyAttrs}>
|
|
105
|
+
{#each $rows as row (row.id)}
|
|
106
|
+
<Subscribe rowAttrs={row.attrs()} let:rowAttrs>
|
|
107
|
+
<tr {...rowAttrs}>
|
|
108
|
+
{#each row.cells as cell (cell.id)}
|
|
109
|
+
<Subscribe attrs={cell.attrs()} let:attrs>
|
|
110
|
+
<td {...attrs}>
|
|
111
|
+
<Render of={cell.render()} />
|
|
112
|
+
</td>
|
|
113
|
+
</Subscribe>
|
|
114
|
+
{/each}
|
|
115
|
+
</tr>
|
|
116
|
+
</Subscribe>
|
|
117
|
+
{/each}
|
|
118
|
+
</tbody>
|
|
119
|
+
</table>
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
For more complex examples with advanced features, visit the [documentation site](https://svelte-headless-table.bryanmylee.com/docs/plugins/overview).
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { type Readable } from 'svelte/store';
|
|
2
|
+
import type { BodyRow } from './bodyRows.js';
|
|
3
|
+
import type { DataColumn, DisplayColumn, FlatColumn } from './columns.js';
|
|
4
|
+
import { TableComponent } from './tableComponent.js';
|
|
5
|
+
import type { DataLabel, DisplayLabel } from './types/Label.js';
|
|
6
|
+
import type { AnyPlugins } from './types/TablePlugin.js';
|
|
7
|
+
import type { RenderConfig } from '@humanspeak/svelte-subscribe';
|
|
8
|
+
export type BodyCellInit<Item, Plugins extends AnyPlugins = AnyPlugins> = {
|
|
9
|
+
id: string;
|
|
10
|
+
row: BodyRow<Item, Plugins>;
|
|
11
|
+
};
|
|
12
|
+
export type BodyCellAttributes<Item, Plugins extends AnyPlugins = AnyPlugins> = {
|
|
13
|
+
role: 'cell';
|
|
14
|
+
};
|
|
15
|
+
export declare abstract class BodyCell<Item, Plugins extends AnyPlugins = AnyPlugins> extends TableComponent<Item, Plugins, 'tbody.tr.td'> {
|
|
16
|
+
abstract column: FlatColumn<Item, Plugins>;
|
|
17
|
+
row: BodyRow<Item, Plugins>;
|
|
18
|
+
constructor({ id, row }: BodyCellInit<Item, Plugins>);
|
|
19
|
+
abstract render(): RenderConfig;
|
|
20
|
+
attrs(): Readable<BodyCellAttributes<Item, Plugins>>;
|
|
21
|
+
abstract clone(): BodyCell<Item, Plugins>;
|
|
22
|
+
rowColId(): string;
|
|
23
|
+
dataRowColId(): string | undefined;
|
|
24
|
+
isData(): this is DataBodyCell<Item, Plugins>;
|
|
25
|
+
isDisplay(): this is DisplayBodyCell<Item, Plugins>;
|
|
26
|
+
}
|
|
27
|
+
export type DataBodyCellInit<Item, Plugins extends AnyPlugins = AnyPlugins, Value = unknown> = Omit<BodyCellInit<Item, Plugins>, 'id'> & {
|
|
28
|
+
column: DataColumn<Item, Plugins>;
|
|
29
|
+
label?: DataLabel<Item, Plugins, Value>;
|
|
30
|
+
value: Value;
|
|
31
|
+
};
|
|
32
|
+
export type DataBodyCellAttributes<Item, Plugins extends AnyPlugins = AnyPlugins> = BodyCellAttributes<Item, Plugins>;
|
|
33
|
+
export declare class DataBodyCell<Item, Plugins extends AnyPlugins = AnyPlugins, Value = unknown> extends BodyCell<Item, Plugins> {
|
|
34
|
+
__data: boolean;
|
|
35
|
+
column: DataColumn<Item, Plugins>;
|
|
36
|
+
label?: DataLabel<Item, Plugins, Value>;
|
|
37
|
+
value: Value;
|
|
38
|
+
constructor({ row, column, label, value }: DataBodyCellInit<Item, Plugins, Value>);
|
|
39
|
+
render(): RenderConfig;
|
|
40
|
+
clone(): DataBodyCell<Item, Plugins>;
|
|
41
|
+
}
|
|
42
|
+
export type DisplayBodyCellInit<Item, Plugins extends AnyPlugins = AnyPlugins> = Omit<BodyCellInit<Item, Plugins>, 'id'> & {
|
|
43
|
+
column: DisplayColumn<Item, Plugins>;
|
|
44
|
+
label: DisplayLabel<Item, Plugins>;
|
|
45
|
+
};
|
|
46
|
+
export declare class DisplayBodyCell<Item, Plugins extends AnyPlugins = AnyPlugins> extends BodyCell<Item, Plugins> {
|
|
47
|
+
__display: boolean;
|
|
48
|
+
column: DisplayColumn<Item, Plugins>;
|
|
49
|
+
label: DisplayLabel<Item, Plugins>;
|
|
50
|
+
constructor({ row, column, label }: DisplayBodyCellInit<Item, Plugins>);
|
|
51
|
+
render(): RenderConfig;
|
|
52
|
+
clone(): DisplayBodyCell<Item, Plugins>;
|
|
53
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { derived } from 'svelte/store';
|
|
2
|
+
import { TableComponent } from './tableComponent.js';
|
|
3
|
+
export class BodyCell extends TableComponent {
|
|
4
|
+
row;
|
|
5
|
+
constructor({ id, row }) {
|
|
6
|
+
super({ id });
|
|
7
|
+
this.row = row;
|
|
8
|
+
}
|
|
9
|
+
attrs() {
|
|
10
|
+
return derived(super.attrs(), ($baseAttrs) => {
|
|
11
|
+
return {
|
|
12
|
+
...$baseAttrs,
|
|
13
|
+
role: 'cell'
|
|
14
|
+
};
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
rowColId() {
|
|
18
|
+
return `${this.row.id}:${this.column.id}`;
|
|
19
|
+
}
|
|
20
|
+
dataRowColId() {
|
|
21
|
+
if (!this.row.isData()) {
|
|
22
|
+
return undefined;
|
|
23
|
+
}
|
|
24
|
+
return `${this.row.dataId}:${this.column.id}`;
|
|
25
|
+
}
|
|
26
|
+
// TODO Workaround for https://github.com/vitejs/vite/issues/9528
|
|
27
|
+
isData() {
|
|
28
|
+
return '__data' in this;
|
|
29
|
+
}
|
|
30
|
+
// TODO Workaround for https://github.com/vitejs/vite/issues/9528
|
|
31
|
+
isDisplay() {
|
|
32
|
+
return '__display' in this;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
export class DataBodyCell extends BodyCell {
|
|
36
|
+
// TODO Workaround for https://github.com/vitejs/vite/issues/9528
|
|
37
|
+
__data = true;
|
|
38
|
+
column;
|
|
39
|
+
label;
|
|
40
|
+
value;
|
|
41
|
+
constructor({ row, column, label, value }) {
|
|
42
|
+
super({ id: column.id, row });
|
|
43
|
+
this.column = column;
|
|
44
|
+
this.label = label;
|
|
45
|
+
this.value = value;
|
|
46
|
+
}
|
|
47
|
+
render() {
|
|
48
|
+
if (this.label === undefined) {
|
|
49
|
+
return `${this.value}`;
|
|
50
|
+
}
|
|
51
|
+
if (this.state === undefined) {
|
|
52
|
+
throw new Error('Missing `state` reference');
|
|
53
|
+
}
|
|
54
|
+
return this.label(this, this.state);
|
|
55
|
+
}
|
|
56
|
+
clone() {
|
|
57
|
+
const clonedCell = new DataBodyCell({
|
|
58
|
+
row: this.row,
|
|
59
|
+
column: this.column,
|
|
60
|
+
label: this.label,
|
|
61
|
+
value: this.value
|
|
62
|
+
});
|
|
63
|
+
return clonedCell;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
export class DisplayBodyCell extends BodyCell {
|
|
67
|
+
// TODO Workaround for https://github.com/vitejs/vite/issues/9528
|
|
68
|
+
__display = true;
|
|
69
|
+
column;
|
|
70
|
+
label;
|
|
71
|
+
constructor({ row, column, label }) {
|
|
72
|
+
super({ id: column.id, row });
|
|
73
|
+
this.column = column;
|
|
74
|
+
this.label = label;
|
|
75
|
+
}
|
|
76
|
+
render() {
|
|
77
|
+
if (this.state === undefined) {
|
|
78
|
+
throw new Error('Missing `state` reference');
|
|
79
|
+
}
|
|
80
|
+
return this.label(this, this.state);
|
|
81
|
+
}
|
|
82
|
+
clone() {
|
|
83
|
+
const clonedCell = new DisplayBodyCell({
|
|
84
|
+
row: this.row,
|
|
85
|
+
column: this.column,
|
|
86
|
+
label: this.label
|
|
87
|
+
});
|
|
88
|
+
return clonedCell;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { type Readable } from 'svelte/store';
|
|
2
|
+
import { BodyCell } from './bodyCells.js';
|
|
3
|
+
import type { FlatColumn } from './columns.js';
|
|
4
|
+
import { TableComponent } from './tableComponent.js';
|
|
5
|
+
import type { AnyPlugins } from './types/TablePlugin.js';
|
|
6
|
+
export type BodyRowInit<Item, Plugins extends AnyPlugins = AnyPlugins> = {
|
|
7
|
+
id: string;
|
|
8
|
+
cells: BodyCell<Item, Plugins>[];
|
|
9
|
+
cellForId: Record<string, BodyCell<Item, Plugins>>;
|
|
10
|
+
depth?: number;
|
|
11
|
+
parentRow?: BodyRow<Item, Plugins>;
|
|
12
|
+
};
|
|
13
|
+
export type BodyRowAttributes<Item, Plugins extends AnyPlugins = AnyPlugins> = {
|
|
14
|
+
role: 'row';
|
|
15
|
+
};
|
|
16
|
+
export declare abstract class BodyRow<Item, Plugins extends AnyPlugins = AnyPlugins> extends TableComponent<Item, Plugins, 'tbody.tr'> {
|
|
17
|
+
cells: BodyCell<Item, Plugins>[];
|
|
18
|
+
/**
|
|
19
|
+
* Get the cell with a given column id.
|
|
20
|
+
*
|
|
21
|
+
* **This includes hidden cells.**
|
|
22
|
+
*/
|
|
23
|
+
cellForId: Record<string, BodyCell<Item, Plugins>>;
|
|
24
|
+
depth: number;
|
|
25
|
+
parentRow?: BodyRow<Item, Plugins>;
|
|
26
|
+
subRows?: BodyRow<Item, Plugins>[];
|
|
27
|
+
constructor({ id, cells, cellForId, depth, parentRow }: BodyRowInit<Item, Plugins>);
|
|
28
|
+
attrs(): Readable<BodyRowAttributes<Item, Plugins>>;
|
|
29
|
+
abstract clone(props?: BodyRowCloneProps): BodyRow<Item, Plugins>;
|
|
30
|
+
isData(): this is DataBodyRow<Item, Plugins>;
|
|
31
|
+
isDisplay(): this is DisplayBodyRow<Item, Plugins>;
|
|
32
|
+
}
|
|
33
|
+
type BodyRowCloneProps = {
|
|
34
|
+
includeCells?: boolean;
|
|
35
|
+
includeSubRows?: boolean;
|
|
36
|
+
};
|
|
37
|
+
export type DataBodyRowInit<Item, Plugins extends AnyPlugins = AnyPlugins> = BodyRowInit<Item, Plugins> & {
|
|
38
|
+
dataId: string;
|
|
39
|
+
original: Item;
|
|
40
|
+
};
|
|
41
|
+
export declare class DataBodyRow<Item, Plugins extends AnyPlugins = AnyPlugins> extends BodyRow<Item, Plugins> {
|
|
42
|
+
__data: boolean;
|
|
43
|
+
dataId: string;
|
|
44
|
+
original: Item;
|
|
45
|
+
constructor({ id, dataId, original, cells, cellForId, depth, parentRow }: DataBodyRowInit<Item, Plugins>);
|
|
46
|
+
clone({ includeCells, includeSubRows }?: BodyRowCloneProps): DataBodyRow<Item, Plugins>;
|
|
47
|
+
}
|
|
48
|
+
export type DisplayBodyRowInit<Item, Plugins extends AnyPlugins = AnyPlugins> = BodyRowInit<Item, Plugins>;
|
|
49
|
+
export declare class DisplayBodyRow<Item, Plugins extends AnyPlugins = AnyPlugins> extends BodyRow<Item, Plugins> {
|
|
50
|
+
__display: boolean;
|
|
51
|
+
constructor({ id, cells, cellForId, depth, parentRow }: DisplayBodyRowInit<Item, Plugins>);
|
|
52
|
+
clone({ includeCells, includeSubRows }?: BodyRowCloneProps): DisplayBodyRow<Item, Plugins>;
|
|
53
|
+
}
|
|
54
|
+
export interface BodyRowsOptions<Item> {
|
|
55
|
+
rowDataId?: (item: Item, index: number) => string;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Converts an array of items into an array of table `BodyRow`s based on the column structure.
|
|
59
|
+
* @param data The data to display.
|
|
60
|
+
* @param flatColumns The column structure.
|
|
61
|
+
* @returns An array of `BodyRow`s representing the table structure.
|
|
62
|
+
*/
|
|
63
|
+
export declare const getBodyRows: <Item, Plugins extends AnyPlugins = AnyPlugins>(data: Item[], flatColumns: FlatColumn<Item, Plugins>[], { rowDataId }?: BodyRowsOptions<Item>) => BodyRow<Item, Plugins>[];
|
|
64
|
+
/**
|
|
65
|
+
* Arranges and hides columns in an array of `BodyRow`s based on
|
|
66
|
+
* `columnIdOrder` by transforming the `cells` property of each row.
|
|
67
|
+
*
|
|
68
|
+
* `cellForId` should remain unaffected.
|
|
69
|
+
*
|
|
70
|
+
* @param rows The rows to transform.
|
|
71
|
+
* @param columnIdOrder The column order to transform to.
|
|
72
|
+
* @returns A new array of `BodyRow`s with corrected row references.
|
|
73
|
+
*/
|
|
74
|
+
export declare const getColumnedBodyRows: <Item, Plugins extends AnyPlugins = AnyPlugins>(rows: BodyRow<Item, Plugins>[], columnIdOrder: string[]) => BodyRow<Item, Plugins>[];
|
|
75
|
+
/**
|
|
76
|
+
* Converts an array of items into an array of table `BodyRow`s based on a parent row.
|
|
77
|
+
* @param subItems The sub data to display.
|
|
78
|
+
* @param parentRow The parent row.
|
|
79
|
+
* @returns An array of `BodyRow`s representing the child rows of `parentRow`.
|
|
80
|
+
*/
|
|
81
|
+
export declare const getSubRows: <Item, Plugins extends AnyPlugins = AnyPlugins>(subItems: Item[], parentRow: BodyRow<Item, Plugins>, { rowDataId }?: BodyRowsOptions<Item>) => BodyRow<Item, Plugins>[];
|
|
82
|
+
export {};
|
package/dist/bodyRows.js
ADDED
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
import { derived } from 'svelte/store';
|
|
2
|
+
import { BodyCell, DataBodyCell, DisplayBodyCell } from './bodyCells.js';
|
|
3
|
+
import { TableComponent } from './tableComponent.js';
|
|
4
|
+
import { nonUndefined } from './utils/filter.js';
|
|
5
|
+
export class BodyRow extends TableComponent {
|
|
6
|
+
cells;
|
|
7
|
+
/**
|
|
8
|
+
* Get the cell with a given column id.
|
|
9
|
+
*
|
|
10
|
+
* **This includes hidden cells.**
|
|
11
|
+
*/
|
|
12
|
+
cellForId;
|
|
13
|
+
depth;
|
|
14
|
+
parentRow;
|
|
15
|
+
subRows;
|
|
16
|
+
constructor({ id, cells, cellForId, depth = 0, parentRow }) {
|
|
17
|
+
super({ id });
|
|
18
|
+
this.cells = cells;
|
|
19
|
+
this.cellForId = cellForId;
|
|
20
|
+
this.depth = depth;
|
|
21
|
+
this.parentRow = parentRow;
|
|
22
|
+
}
|
|
23
|
+
attrs() {
|
|
24
|
+
return derived(super.attrs(), ($baseAttrs) => {
|
|
25
|
+
return {
|
|
26
|
+
...$baseAttrs,
|
|
27
|
+
role: 'row'
|
|
28
|
+
};
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
// TODO Workaround for https://github.com/vitejs/vite/issues/9528
|
|
32
|
+
isData() {
|
|
33
|
+
return '__data' in this;
|
|
34
|
+
}
|
|
35
|
+
// TODO Workaround for https://github.com/vitejs/vite/issues/9528
|
|
36
|
+
isDisplay() {
|
|
37
|
+
return '__display' in this;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
export class DataBodyRow extends BodyRow {
|
|
41
|
+
// TODO Workaround for https://github.com/vitejs/vite/issues/9528
|
|
42
|
+
__data = true;
|
|
43
|
+
dataId;
|
|
44
|
+
original;
|
|
45
|
+
constructor({ id, dataId, original, cells, cellForId, depth = 0, parentRow }) {
|
|
46
|
+
super({ id, cells, cellForId, depth, parentRow });
|
|
47
|
+
this.dataId = dataId;
|
|
48
|
+
this.original = original;
|
|
49
|
+
}
|
|
50
|
+
clone({ includeCells = false, includeSubRows = false } = {}) {
|
|
51
|
+
const clonedRow = new DataBodyRow({
|
|
52
|
+
id: this.id,
|
|
53
|
+
dataId: this.dataId,
|
|
54
|
+
cellForId: this.cellForId,
|
|
55
|
+
cells: this.cells,
|
|
56
|
+
original: this.original,
|
|
57
|
+
depth: this.depth
|
|
58
|
+
});
|
|
59
|
+
if (includeCells) {
|
|
60
|
+
const clonedCellsForId = Object.fromEntries(Object.entries(clonedRow.cellForId).map(([id, cell]) => {
|
|
61
|
+
const clonedCell = cell.clone();
|
|
62
|
+
clonedCell.row = clonedRow;
|
|
63
|
+
return [id, clonedCell];
|
|
64
|
+
}));
|
|
65
|
+
const clonedCells = clonedRow.cells.map(({ id }) => clonedCellsForId[id]);
|
|
66
|
+
clonedRow.cellForId = clonedCellsForId;
|
|
67
|
+
clonedRow.cells = clonedCells;
|
|
68
|
+
}
|
|
69
|
+
if (includeSubRows) {
|
|
70
|
+
const clonedSubRows = this.subRows?.map((row) => row.clone({ includeCells, includeSubRows }));
|
|
71
|
+
clonedRow.subRows = clonedSubRows;
|
|
72
|
+
}
|
|
73
|
+
else {
|
|
74
|
+
clonedRow.subRows = this.subRows;
|
|
75
|
+
}
|
|
76
|
+
return clonedRow;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
export class DisplayBodyRow extends BodyRow {
|
|
80
|
+
// TODO Workaround for https://github.com/vitejs/vite/issues/9528
|
|
81
|
+
__display = true;
|
|
82
|
+
constructor({ id, cells, cellForId, depth = 0, parentRow }) {
|
|
83
|
+
super({ id, cells, cellForId, depth, parentRow });
|
|
84
|
+
}
|
|
85
|
+
clone({ includeCells = false, includeSubRows = false } = {}) {
|
|
86
|
+
const clonedRow = new DisplayBodyRow({
|
|
87
|
+
id: this.id,
|
|
88
|
+
cellForId: this.cellForId,
|
|
89
|
+
cells: this.cells,
|
|
90
|
+
depth: this.depth
|
|
91
|
+
});
|
|
92
|
+
clonedRow.subRows = this.subRows;
|
|
93
|
+
if (includeCells) {
|
|
94
|
+
const clonedCellsForId = Object.fromEntries(Object.entries(clonedRow.cellForId).map(([id, cell]) => {
|
|
95
|
+
const clonedCell = cell.clone();
|
|
96
|
+
clonedCell.row = clonedRow;
|
|
97
|
+
return [id, clonedCell];
|
|
98
|
+
}));
|
|
99
|
+
const clonedCells = clonedRow.cells.map(({ id }) => clonedCellsForId[id]);
|
|
100
|
+
clonedRow.cellForId = clonedCellsForId;
|
|
101
|
+
clonedRow.cells = clonedCells;
|
|
102
|
+
}
|
|
103
|
+
if (includeSubRows) {
|
|
104
|
+
const clonedSubRows = this.subRows?.map((row) => row.clone({ includeCells, includeSubRows }));
|
|
105
|
+
clonedRow.subRows = clonedSubRows;
|
|
106
|
+
}
|
|
107
|
+
else {
|
|
108
|
+
clonedRow.subRows = this.subRows;
|
|
109
|
+
}
|
|
110
|
+
return clonedRow;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Converts an array of items into an array of table `BodyRow`s based on the column structure.
|
|
115
|
+
* @param data The data to display.
|
|
116
|
+
* @param flatColumns The column structure.
|
|
117
|
+
* @returns An array of `BodyRow`s representing the table structure.
|
|
118
|
+
*/
|
|
119
|
+
export const getBodyRows = (data,
|
|
120
|
+
/**
|
|
121
|
+
* Flat columns before column transformations.
|
|
122
|
+
*/
|
|
123
|
+
flatColumns, { rowDataId } = {}) => {
|
|
124
|
+
const rows = data.map((item, idx) => {
|
|
125
|
+
const id = idx.toString();
|
|
126
|
+
return new DataBodyRow({
|
|
127
|
+
id,
|
|
128
|
+
dataId: rowDataId !== undefined ? rowDataId(item, idx) : id,
|
|
129
|
+
original: item,
|
|
130
|
+
cells: [],
|
|
131
|
+
cellForId: {}
|
|
132
|
+
});
|
|
133
|
+
});
|
|
134
|
+
data.forEach((item, rowIdx) => {
|
|
135
|
+
const cells = flatColumns.map((col) => {
|
|
136
|
+
if (col.isData()) {
|
|
137
|
+
const dataCol = col;
|
|
138
|
+
const value = dataCol.getValue(item);
|
|
139
|
+
return new DataBodyCell({
|
|
140
|
+
row: rows[rowIdx],
|
|
141
|
+
column: dataCol,
|
|
142
|
+
label: col.cell,
|
|
143
|
+
value
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
if (col.isDisplay()) {
|
|
147
|
+
const displayCol = col;
|
|
148
|
+
return new DisplayBodyCell({
|
|
149
|
+
row: rows[rowIdx],
|
|
150
|
+
column: displayCol,
|
|
151
|
+
label: col.cell
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
throw new Error('Unrecognized `FlatColumn` implementation');
|
|
155
|
+
});
|
|
156
|
+
rows[rowIdx].cells = cells;
|
|
157
|
+
flatColumns.forEach((c, colIdx) => {
|
|
158
|
+
rows[rowIdx].cellForId[c.id] = cells[colIdx];
|
|
159
|
+
});
|
|
160
|
+
});
|
|
161
|
+
return rows;
|
|
162
|
+
};
|
|
163
|
+
/**
|
|
164
|
+
* Arranges and hides columns in an array of `BodyRow`s based on
|
|
165
|
+
* `columnIdOrder` by transforming the `cells` property of each row.
|
|
166
|
+
*
|
|
167
|
+
* `cellForId` should remain unaffected.
|
|
168
|
+
*
|
|
169
|
+
* @param rows The rows to transform.
|
|
170
|
+
* @param columnIdOrder The column order to transform to.
|
|
171
|
+
* @returns A new array of `BodyRow`s with corrected row references.
|
|
172
|
+
*/
|
|
173
|
+
export const getColumnedBodyRows = (rows, columnIdOrder) => {
|
|
174
|
+
const columnedRows = rows.map((row) => {
|
|
175
|
+
const clonedRow = row.clone();
|
|
176
|
+
clonedRow.cells = [];
|
|
177
|
+
clonedRow.cellForId = {};
|
|
178
|
+
return clonedRow;
|
|
179
|
+
});
|
|
180
|
+
if (rows.length === 0 || columnIdOrder.length === 0)
|
|
181
|
+
return rows;
|
|
182
|
+
rows.forEach((row, rowIdx) => {
|
|
183
|
+
// Create a shallow copy of `row.cells` to reassign each `cell`'s `row`
|
|
184
|
+
// reference.
|
|
185
|
+
const cells = row.cells.map((cell) => {
|
|
186
|
+
const clonedCell = cell.clone();
|
|
187
|
+
clonedCell.row = columnedRows[rowIdx];
|
|
188
|
+
return clonedCell;
|
|
189
|
+
});
|
|
190
|
+
const visibleCells = columnIdOrder
|
|
191
|
+
.map((cid) => {
|
|
192
|
+
return cells.find((c) => c.id === cid);
|
|
193
|
+
})
|
|
194
|
+
.filter(nonUndefined);
|
|
195
|
+
columnedRows[rowIdx].cells = visibleCells;
|
|
196
|
+
// Include hidden cells in `cellForId` to allow row transformations on
|
|
197
|
+
// hidden cells.
|
|
198
|
+
cells.forEach((cell) => {
|
|
199
|
+
columnedRows[rowIdx].cellForId[cell.id] = cell;
|
|
200
|
+
});
|
|
201
|
+
});
|
|
202
|
+
return columnedRows;
|
|
203
|
+
};
|
|
204
|
+
/**
|
|
205
|
+
* Converts an array of items into an array of table `BodyRow`s based on a parent row.
|
|
206
|
+
* @param subItems The sub data to display.
|
|
207
|
+
* @param parentRow The parent row.
|
|
208
|
+
* @returns An array of `BodyRow`s representing the child rows of `parentRow`.
|
|
209
|
+
*/
|
|
210
|
+
export const getSubRows = (subItems, parentRow, { rowDataId } = {}) => {
|
|
211
|
+
const subRows = subItems.map((item, idx) => {
|
|
212
|
+
const id = `${parentRow.id}>${idx}`;
|
|
213
|
+
return new DataBodyRow({
|
|
214
|
+
id,
|
|
215
|
+
dataId: rowDataId !== undefined ? rowDataId(item, idx) : id,
|
|
216
|
+
original: item,
|
|
217
|
+
cells: [],
|
|
218
|
+
cellForId: {},
|
|
219
|
+
depth: parentRow.depth + 1,
|
|
220
|
+
parentRow
|
|
221
|
+
});
|
|
222
|
+
});
|
|
223
|
+
subItems.forEach((item, rowIdx) => {
|
|
224
|
+
// parentRow.cells only include visible cells.
|
|
225
|
+
// We have to derive all cells from parentRow.cellForId
|
|
226
|
+
const cellForId = Object.fromEntries(Object.values(parentRow.cellForId).map((cell) => {
|
|
227
|
+
const { column } = cell;
|
|
228
|
+
if (column.isData()) {
|
|
229
|
+
const dataCol = column;
|
|
230
|
+
const value = dataCol.getValue(item);
|
|
231
|
+
return [
|
|
232
|
+
column.id,
|
|
233
|
+
new DataBodyCell({
|
|
234
|
+
row: subRows[rowIdx],
|
|
235
|
+
column,
|
|
236
|
+
label: column.cell,
|
|
237
|
+
value
|
|
238
|
+
})
|
|
239
|
+
];
|
|
240
|
+
}
|
|
241
|
+
if (column.isDisplay()) {
|
|
242
|
+
return [
|
|
243
|
+
column.id,
|
|
244
|
+
new DisplayBodyCell({ row: subRows[rowIdx], column, label: column.cell })
|
|
245
|
+
];
|
|
246
|
+
}
|
|
247
|
+
throw new Error('Unrecognized `FlatColumn` implementation');
|
|
248
|
+
}));
|
|
249
|
+
subRows[rowIdx].cellForId = cellForId;
|
|
250
|
+
const cells = parentRow.cells.map((cell) => {
|
|
251
|
+
return cellForId[cell.id];
|
|
252
|
+
});
|
|
253
|
+
subRows[rowIdx].cells = cells;
|
|
254
|
+
});
|
|
255
|
+
return subRows;
|
|
256
|
+
};
|