@ims360/svelte-ivory 0.0.36 → 0.0.38
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/table/Column.svelte +6 -2
- package/dist/components/table/Column.svelte.d.ts +1 -0
- package/dist/components/table/Column.svelte.d.ts.map +1 -1
- package/dist/components/table/Table.svelte +94 -76
- package/dist/components/table/Table.svelte.d.ts +9 -3
- package/dist/components/table/Table.svelte.d.ts.map +1 -1
- package/dist/components/table/controller.svelte.d.ts +14 -14
- package/dist/components/table/controller.svelte.d.ts.map +1 -1
- package/dist/components/table/controller.svelte.js +93 -38
- package/dist/components/table/index.d.ts +2 -2
- package/dist/components/table/index.d.ts.map +1 -1
- package/dist/components/table/index.js +1 -1
- package/package.json +1 -1
- package/src/lib/components/table/Column.svelte +6 -2
- package/src/lib/components/table/Table.svelte +94 -76
- package/src/lib/components/table/controller.svelte.ts +106 -42
- package/src/lib/components/table/index.ts +7 -2
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
onclick?: (e: Event) => void | Promise<void>;
|
|
20
20
|
/** Cannot be used with resizable columns*/
|
|
21
21
|
ignoreWidth?: boolean;
|
|
22
|
+
offsetNestingLevel?: number;
|
|
22
23
|
}
|
|
23
24
|
</script>
|
|
24
25
|
|
|
@@ -30,11 +31,12 @@
|
|
|
30
31
|
ignoreWidth = false,
|
|
31
32
|
// ColumnConfig
|
|
32
33
|
resizable = true,
|
|
34
|
+
offsetNestingLevel = 0,
|
|
33
35
|
...props
|
|
34
36
|
}: ColumnProps = $props();
|
|
35
37
|
|
|
36
38
|
// Register the new column if this is the first table row that was rendered
|
|
37
|
-
const table = getTableContext();
|
|
39
|
+
const { table, nestingInset } = getTableContext();
|
|
38
40
|
const column = table.registerColumn({ resizable, ...props });
|
|
39
41
|
const allowClicking = $derived(!!onclick);
|
|
40
42
|
|
|
@@ -60,7 +62,9 @@
|
|
|
60
62
|
this={allowClicking ? 'button' : 'div'}
|
|
61
63
|
onclick={allowClicking ? onClick : undefined}
|
|
62
64
|
type={onclick ? 'button' : undefined}
|
|
63
|
-
style={ignoreWidth
|
|
65
|
+
style={ignoreWidth
|
|
66
|
+
? ''
|
|
67
|
+
: `width: calc(var(--spacing) * ${offsetNestingLevel * nestingInset} - ${column.width ?? 0}px) !important;`}
|
|
64
68
|
class={twMerge(
|
|
65
69
|
clsx(
|
|
66
70
|
'flex shrink-0 flex-row items-stretch justify-start truncate',
|
|
@@ -9,6 +9,7 @@ export interface ColumnProps extends ColumnConfig {
|
|
|
9
9
|
onclick?: (e: Event) => void | Promise<void>;
|
|
10
10
|
/** Cannot be used with resizable columns*/
|
|
11
11
|
ignoreWidth?: boolean;
|
|
12
|
+
offsetNestingLevel?: number;
|
|
12
13
|
}
|
|
13
14
|
declare const Column: import("svelte").Component<ColumnProps, {}, "">;
|
|
14
15
|
type Column = ReturnType<typeof Column>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Column.svelte.d.ts","sourceRoot":"","sources":["../../../src/lib/components/table/Column.svelte.ts"],"names":[],"mappings":"AAII,OAAO,EAAE,KAAK,OAAO,EAAE,MAAM,QAAQ,CAAC;AACtC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAElD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AAK9D,wBAAgB,UAAU,CAAC,CAAC,EAAE,UAAU,QAEvC;AAED,MAAM,WAAW,WAAY,SAAQ,YAAY;IAC7C,KAAK,CAAC,EAAE,UAAU,CAAC;IACnB,2EAA2E;IAC3E,QAAQ,EAAE,OAAO,CAAC;IAClB,OAAO,CAAC,EAAE,CAAC,CAAC,EAAE,KAAK,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7C,2CAA2C;IAC3C,WAAW,CAAC,EAAE,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"Column.svelte.d.ts","sourceRoot":"","sources":["../../../src/lib/components/table/Column.svelte.ts"],"names":[],"mappings":"AAII,OAAO,EAAE,KAAK,OAAO,EAAE,MAAM,QAAQ,CAAC;AACtC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAElD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AAK9D,wBAAgB,UAAU,CAAC,CAAC,EAAE,UAAU,QAEvC;AAED,MAAM,WAAW,WAAY,SAAQ,YAAY;IAC7C,KAAK,CAAC,EAAE,UAAU,CAAC;IACnB,2EAA2E;IAC3E,QAAQ,EAAE,OAAO,CAAC;IAClB,OAAO,CAAC,EAAE,CAAC,CAAC,EAAE,KAAK,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7C,2CAA2C;IAC3C,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC/B;AA0DL,QAAA,MAAM,MAAM,iDAAwC,CAAC;AACrD,KAAK,MAAM,GAAG,UAAU,CAAC,OAAO,MAAM,CAAC,CAAC;AACxC,eAAe,MAAM,CAAC"}
|
|
@@ -5,10 +5,11 @@
|
|
|
5
5
|
import { getContext, setContext, type Snippet } from 'svelte';
|
|
6
6
|
import type { ClassValue } from 'svelte/elements';
|
|
7
7
|
import { twMerge } from 'tailwind-merge';
|
|
8
|
-
import { Column,
|
|
8
|
+
import { Column, type TableConfig, type TablePlugin, type TableRow } from '.';
|
|
9
9
|
import ColumnHead from './ColumnHead.svelte';
|
|
10
10
|
import Row from './Row.svelte';
|
|
11
11
|
import VirtualList from './VirtualList.svelte';
|
|
12
|
+
import { createTableConfig, treeWalker } from './controller.svelte';
|
|
12
13
|
|
|
13
14
|
export interface TableProps<T extends TableRow<T>> {
|
|
14
15
|
class?: ClassValue;
|
|
@@ -23,20 +24,26 @@
|
|
|
23
24
|
rowClass?: ClassValue;
|
|
24
25
|
headerClass?: ClassValue;
|
|
25
26
|
plugins?: TablePlugin<T>[];
|
|
26
|
-
|
|
27
|
+
config?: TableConfig<T>;
|
|
27
28
|
/**
|
|
28
29
|
* **Bindable**
|
|
29
30
|
*/
|
|
30
31
|
b_scrollTop?: number;
|
|
32
|
+
/** Equal to tailwinds `spacing` variable (e.g. `w-4` would be `4`) */
|
|
33
|
+
nestingInset?: number;
|
|
31
34
|
}
|
|
32
35
|
|
|
33
36
|
const TABLE_CONTEXT = {};
|
|
34
|
-
|
|
35
|
-
|
|
37
|
+
export type TableContext<T extends TableRow<T>> = {
|
|
38
|
+
table: TableConfig<T>;
|
|
39
|
+
nestingInset: number;
|
|
40
|
+
};
|
|
41
|
+
function setTableContext<T extends TableRow<T>>(context: TableContext<T>) {
|
|
42
|
+
setContext(TABLE_CONTEXT, context);
|
|
36
43
|
}
|
|
37
44
|
|
|
38
|
-
export function getTableContext<T extends TableRow<T>>():
|
|
39
|
-
return getContext<
|
|
45
|
+
export function getTableContext<T extends TableRow<T>>(): TableContext<T> {
|
|
46
|
+
return getContext<TableContext<T>>(TABLE_CONTEXT);
|
|
40
47
|
}
|
|
41
48
|
</script>
|
|
42
49
|
|
|
@@ -54,83 +61,94 @@
|
|
|
54
61
|
onclick,
|
|
55
62
|
href,
|
|
56
63
|
plugins = [],
|
|
57
|
-
|
|
64
|
+
config: table = createTableConfig<T>(),
|
|
65
|
+
nestingInset = 4
|
|
58
66
|
}: Props<T> = $props();
|
|
59
67
|
|
|
60
68
|
$effect(() => {
|
|
61
|
-
table.
|
|
62
|
-
|
|
63
|
-
plugins
|
|
64
|
-
});
|
|
69
|
+
table.data = data;
|
|
70
|
+
table.plugins = plugins;
|
|
65
71
|
});
|
|
66
72
|
|
|
67
|
-
setTableContext(
|
|
73
|
+
setTableContext({
|
|
74
|
+
get table() {
|
|
75
|
+
return table;
|
|
76
|
+
},
|
|
77
|
+
get nestingInset() {
|
|
78
|
+
return nestingInset;
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
const results = $derived(treeWalker(table));
|
|
83
|
+
|
|
68
84
|
const treeIndicatorId = pseudoRandomId('tree-indicator-');
|
|
69
85
|
</script>
|
|
70
86
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
{
|
|
89
|
-
{
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
{@render firstColumn?.({ row: node })}
|
|
108
|
-
<Column
|
|
109
|
-
id={treeIndicatorId}
|
|
110
|
-
resizable={false}
|
|
111
|
-
header=""
|
|
112
|
-
onclick={() => {
|
|
113
|
-
table.toggleExpansion(node.id);
|
|
114
|
-
}}
|
|
115
|
-
ignoreWidth={table.results.someHaveChildren}
|
|
116
|
-
width={table.results.someHaveChildren ? 24 : 0}
|
|
117
|
-
minWidth={0}
|
|
87
|
+
{#key table.id}
|
|
88
|
+
<VirtualList
|
|
89
|
+
data={results.entries}
|
|
90
|
+
class={['border-transparent', clazz, 'flex flex-col overflow-hidden']}
|
|
91
|
+
bind:b_scrollTop={table.scrollTop}
|
|
92
|
+
{rowHeight}
|
|
93
|
+
>
|
|
94
|
+
{#snippet header()}
|
|
95
|
+
<div
|
|
96
|
+
class={twMerge(
|
|
97
|
+
clsx(
|
|
98
|
+
'flex w-fit min-w-full flex-row gap-2 border-b border-inherit pr-4 pl-2',
|
|
99
|
+
headerClass
|
|
100
|
+
)
|
|
101
|
+
)}
|
|
102
|
+
>
|
|
103
|
+
{#each table.columns as column (column.id)}
|
|
104
|
+
<ColumnHead {column}>
|
|
105
|
+
{#if typeof column.header === 'function'}
|
|
106
|
+
{@render column.header()}
|
|
107
|
+
{:else}
|
|
108
|
+
<div
|
|
109
|
+
class="flex grow flex-row items-center justify-start gap-4 py-2 text-start select-none"
|
|
110
|
+
>
|
|
111
|
+
{column.header}
|
|
112
|
+
</div>
|
|
113
|
+
{/if}
|
|
114
|
+
</ColumnHead>
|
|
115
|
+
{/each}
|
|
116
|
+
</div>
|
|
117
|
+
{/snippet}
|
|
118
|
+
{#snippet children({ row: { node, id, nestingLevel }, index })}
|
|
119
|
+
<Row
|
|
120
|
+
onclick={onclick ? () => onclick(node) : undefined}
|
|
121
|
+
href={href?.(node)}
|
|
122
|
+
class={rowClass}
|
|
118
123
|
>
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
124
|
+
{@render firstColumn?.({ row: node })}
|
|
125
|
+
<Column
|
|
126
|
+
id={treeIndicatorId}
|
|
127
|
+
resizable={false}
|
|
128
|
+
header=""
|
|
129
|
+
onclick={() => {
|
|
130
|
+
table.toggleExpansion(node.id);
|
|
131
|
+
}}
|
|
132
|
+
ignoreWidth={results.someHaveChildren}
|
|
133
|
+
width={results.someHaveChildren ? 24 : 0}
|
|
134
|
+
minWidth={0}
|
|
122
135
|
>
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
</
|
|
136
|
+
<div
|
|
137
|
+
class="flex h-full items-center justify-end"
|
|
138
|
+
style="width: calc(var(--spacing) * {nestingLevel * nestingInset} + 24px);"
|
|
139
|
+
>
|
|
140
|
+
{#if node.children}
|
|
141
|
+
<ChevronRight
|
|
142
|
+
class={[
|
|
143
|
+
'ml-auto aspect-square shrink-0 transition-transform duration-100',
|
|
144
|
+
table.expanded.has(id) && 'rotate-90'
|
|
145
|
+
]}
|
|
146
|
+
/>
|
|
147
|
+
{/if}
|
|
148
|
+
</div>
|
|
149
|
+
</Column>
|
|
150
|
+
{@render passedChildren?.({ row: node, nestingLevel, index })}
|
|
151
|
+
</Row>
|
|
152
|
+
{/snippet}
|
|
153
|
+
</VirtualList>
|
|
154
|
+
{/key}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type Snippet } from 'svelte';
|
|
2
2
|
import type { ClassValue } from 'svelte/elements';
|
|
3
|
-
import {
|
|
3
|
+
import { type TableConfig, type TablePlugin, type TableRow } from '.';
|
|
4
4
|
export interface TableProps<T extends TableRow<T>> {
|
|
5
5
|
class?: ClassValue;
|
|
6
6
|
data: T[];
|
|
@@ -20,13 +20,19 @@ export interface TableProps<T extends TableRow<T>> {
|
|
|
20
20
|
rowClass?: ClassValue;
|
|
21
21
|
headerClass?: ClassValue;
|
|
22
22
|
plugins?: TablePlugin<T>[];
|
|
23
|
-
|
|
23
|
+
config?: TableConfig<T>;
|
|
24
24
|
/**
|
|
25
25
|
* **Bindable**
|
|
26
26
|
*/
|
|
27
27
|
b_scrollTop?: number;
|
|
28
|
+
/** Equal to tailwinds `spacing` variable (e.g. `w-4` would be `4`) */
|
|
29
|
+
nestingInset?: number;
|
|
28
30
|
}
|
|
29
|
-
export
|
|
31
|
+
export type TableContext<T extends TableRow<T>> = {
|
|
32
|
+
table: TableConfig<T>;
|
|
33
|
+
nestingInset: number;
|
|
34
|
+
};
|
|
35
|
+
export declare function getTableContext<T extends TableRow<T>>(): TableContext<T>;
|
|
30
36
|
interface Props<TI extends {
|
|
31
37
|
id: string;
|
|
32
38
|
}> extends TableProps<TI> {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Table.svelte.d.ts","sourceRoot":"","sources":["../../../src/lib/components/table/Table.svelte.ts"],"names":[],"mappings":"AAMI,OAAO,EAA0B,KAAK,OAAO,EAAE,MAAM,QAAQ,CAAC;AAC9D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAElD,OAAO,EAAU,
|
|
1
|
+
{"version":3,"file":"Table.svelte.d.ts","sourceRoot":"","sources":["../../../src/lib/components/table/Table.svelte.ts"],"names":[],"mappings":"AAMI,OAAO,EAA0B,KAAK,OAAO,EAAE,MAAM,QAAQ,CAAC;AAC9D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAElD,OAAO,EAAU,KAAK,WAAW,EAAE,KAAK,WAAW,EAAE,KAAK,QAAQ,EAAE,MAAM,GAAG,CAAC;AAM9E,MAAM,WAAW,UAAU,CAAC,CAAC,SAAS,QAAQ,CAAC,CAAC,CAAC;IAC7C,KAAK,CAAC,EAAE,UAAU,CAAC;IACnB,IAAI,EAAE,CAAC,EAAE,CAAC;IACV,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,CAAC,KAAK,IAAI,CAAC;IAC3B,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,CAAC,KAAK,MAAM,GAAG,SAAS,CAAC;IACtC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,uBAAuB;IACvB,QAAQ,CAAC,EAAE,OAAO,CAAC,CAAC;QAAE,GAAG,EAAE,CAAC,CAAC;QAAC,YAAY,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC,CAAC;IACvE,iDAAiD;IACjD,WAAW,CAAC,EAAE,OAAO,CAAC,CAAC;QAAE,GAAG,EAAE,CAAC,CAAA;KAAE,CAAC,CAAC,CAAC;IACpC,QAAQ,CAAC,EAAE,UAAU,CAAC;IACtB,WAAW,CAAC,EAAE,UAAU,CAAC;IACzB,OAAO,CAAC,EAAE,WAAW,CAAC,CAAC,CAAC,EAAE,CAAC;IAC3B,MAAM,CAAC,EAAE,WAAW,CAAC,CAAC,CAAC,CAAC;IACxB;;OAEG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,sEAAsE;IACtE,YAAY,CAAC,EAAE,MAAM,CAAC;CACzB;AAGD,MAAM,MAAM,YAAY,CAAC,CAAC,SAAS,QAAQ,CAAC,CAAC,CAAC,IAAI;IAC9C,KAAK,EAAE,WAAW,CAAC,CAAC,CAAC,CAAC;IACtB,YAAY,EAAE,MAAM,CAAC;CACxB,CAAC;AAKF,wBAAgB,eAAe,CAAC,CAAC,SAAS,QAAQ,CAAC,CAAC,CAAC,KAAK,YAAY,CAAC,CAAC,CAAC,CAExE;AAED,UAAU,KAAK,CAAC,EAAE,SAAS;IAAE,EAAE,EAAE,MAAM,CAAA;CAAE,CAAE,SAAQ,UAAU,CAAC,EAAE,CAAC;CAAG;AAqFxE,cAAM,iBAAiB,CAAC,CAAC,SAAS,QAAQ,CAAC,CAAC,CAAC;IACzC,KAAK;IAGL,MAAM;IAGN,KAAK;IAGL,QAAQ;IACR,OAAO;CACV;AAED,UAAU,qBAAqB;IAC3B,KAAK,CAAC,SAAS,QAAQ,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,QAAQ,EAAE,2BAA2B,CAAC,UAAU,CAAC,iBAAiB,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,QAAQ,EAAE,eAAe,CAAC,UAAU,CAAC,iBAAiB,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,EAAE,UAAU,CAAC,iBAAiB,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,UAAU,CAAC,iBAAiB,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG;QAAE,UAAU,CAAC,EAAE,UAAU,CAAC,iBAAiB,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAA;KAAE,GAAG,UAAU,CAAC,iBAAiB,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC;IAChZ,CAAC,CAAC,SAAS,QAAQ,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,OAAO,EAAE,KAAK,EAAE,UAAU,CAAC,iBAAiB,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,GAAG,EAAE,GAAG,UAAU,CAAC,iBAAiB,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC;IAC/I,YAAY,CAAC,EAAE,UAAU,CAAC,iBAAiB,CAAC,GAAG,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC;CACjE;AACD,QAAA,MAAM,KAAK,EAAE,qBAAmC,CAAC;AAC/B,KAAK,KAAK,CAAC,CAAC,SAAS,QAAQ,CAAC,CAAC,CAAC,IAAI,YAAY,CAAC,OAAO,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;AACpE,eAAe,KAAK,CAAC"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { SvelteSet } from 'svelte/reactivity';
|
|
1
2
|
import { Column, type ColumnConfig } from './columnController.svelte';
|
|
2
3
|
export type TableRow<T> = {
|
|
3
4
|
id: string;
|
|
@@ -7,7 +8,14 @@ export type TablePlugin<T extends TableRow<T>> = (state: TableState<T>) => Table
|
|
|
7
8
|
export interface TableConfig<T extends TableRow<T>> {
|
|
8
9
|
data: T[];
|
|
9
10
|
plugins?: TablePlugin<T>[];
|
|
11
|
+
id?: string;
|
|
12
|
+
expanded: SvelteSet<string>;
|
|
13
|
+
columns: Column[];
|
|
14
|
+
scrollTop: number;
|
|
15
|
+
registerColumn: (config: ColumnConfig) => Column;
|
|
16
|
+
toggleExpansion: (id: string) => void;
|
|
10
17
|
}
|
|
18
|
+
export declare function createTableConfig<T extends TableRow<T>>(): TableConfig<T>;
|
|
11
19
|
export interface TableState<T extends TableRow<T>> {
|
|
12
20
|
data: T[];
|
|
13
21
|
expanded: Set<string>;
|
|
@@ -17,20 +25,12 @@ interface TreeRow<T> {
|
|
|
17
25
|
nestingLevel: number;
|
|
18
26
|
id: string;
|
|
19
27
|
}
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
entries: TreeRow<T>[];
|
|
27
|
-
someHaveChildren: boolean;
|
|
28
|
-
maxNestingLevel: number;
|
|
29
|
-
};
|
|
30
|
-
refresh(conf: TableConfig<T>): void;
|
|
31
|
-
registerColumn(config: ColumnConfig): Column;
|
|
32
|
-
toggleExpansion(id: string): void;
|
|
33
|
-
}
|
|
28
|
+
/** Walks though a tree strucure and turns it into a flat list, needed since the `VirtualList` needs a list, not a tree */
|
|
29
|
+
export declare function treeWalker<T extends TableRow<T>>(config: TableConfig<T>): {
|
|
30
|
+
entries: TreeRow<T>[];
|
|
31
|
+
someHaveChildren: boolean;
|
|
32
|
+
maxNestingLevel: number;
|
|
33
|
+
};
|
|
34
34
|
export declare function getAllIds<T extends TableRow<T>>(...nodes: T[]): string[];
|
|
35
35
|
export {};
|
|
36
36
|
//# sourceMappingURL=controller.svelte.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"controller.svelte.d.ts","sourceRoot":"","sources":["../../../src/lib/components/table/controller.svelte.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"controller.svelte.d.ts","sourceRoot":"","sources":["../../../src/lib/components/table/controller.svelte.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAC9C,OAAO,EAAE,MAAM,EAAE,KAAK,YAAY,EAAE,MAAM,2BAA2B,CAAC;AAEtE,MAAM,MAAM,QAAQ,CAAC,CAAC,IAAI;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,CAAC,EAAE,CAAA;CAAE,CAAC;AACzD,MAAM,MAAM,WAAW,CAAC,CAAC,SAAS,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,EAAE,UAAU,CAAC,CAAC,CAAC,KAAK,UAAU,CAAC,CAAC,CAAC,CAAC;AAEzF,MAAM,WAAW,WAAW,CAAC,CAAC,SAAS,QAAQ,CAAC,CAAC,CAAC;IAC9C,IAAI,EAAE,CAAC,EAAE,CAAC;IACV,OAAO,CAAC,EAAE,WAAW,CAAC,CAAC,CAAC,EAAE,CAAC;IAC3B,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,SAAS,CAAC,MAAM,CAAC,CAAC;IAC5B,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,CAAC,MAAM,EAAE,YAAY,KAAK,MAAM,CAAC;IACjD,eAAe,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;CACzC;AAED,wBAAgB,iBAAiB,CAAC,CAAC,SAAS,QAAQ,CAAC,CAAC,CAAC,KAAK,WAAW,CAAC,CAAC,CAAC,CAiDzE;AAED,MAAM,WAAW,UAAU,CAAC,CAAC,SAAS,QAAQ,CAAC,CAAC,CAAC;IAC7C,IAAI,EAAE,CAAC,EAAE,CAAC;IACV,QAAQ,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;CACzB;AAED,UAAU,OAAO,CAAC,CAAC;IACf,IAAI,EAAE,CAAC,CAAC;IACR,YAAY,EAAE,MAAM,CAAC;IACrB,EAAE,EAAE,MAAM,CAAC;CACd;AAkDD,0HAA0H;AAC1H,wBAAgB,UAAU,CAAC,CAAC,SAAS,QAAQ,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,WAAW,CAAC,CAAC,CAAC;;;;EAmDvE;AAED,wBAAgB,SAAS,CAAC,CAAC,SAAS,QAAQ,CAAC,CAAC,CAAC,EAAE,GAAG,KAAK,EAAE,CAAC,EAAE,GAAG,MAAM,EAAE,CAMxE"}
|
|
@@ -1,46 +1,101 @@
|
|
|
1
1
|
import { SvelteSet } from 'svelte/reactivity';
|
|
2
2
|
import { Column } from './columnController.svelte';
|
|
3
|
-
export
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
3
|
+
export function createTableConfig() {
|
|
4
|
+
let data = $state([]);
|
|
5
|
+
let plugins = $state([]);
|
|
6
|
+
let scrollTop = $state(0);
|
|
7
|
+
const expanded = new SvelteSet();
|
|
8
|
+
const columns = $state([]);
|
|
9
|
+
return {
|
|
10
|
+
get data() {
|
|
11
|
+
return data;
|
|
12
|
+
},
|
|
13
|
+
set data(d) {
|
|
14
|
+
data = d;
|
|
15
|
+
},
|
|
16
|
+
get plugins() {
|
|
17
|
+
return plugins;
|
|
18
|
+
},
|
|
19
|
+
set plugins(p) {
|
|
20
|
+
plugins = p;
|
|
21
|
+
},
|
|
22
|
+
get scrollTop() {
|
|
23
|
+
return scrollTop;
|
|
24
|
+
},
|
|
25
|
+
set scrollTop(s) {
|
|
26
|
+
scrollTop = s;
|
|
27
|
+
},
|
|
28
|
+
get expanded() {
|
|
29
|
+
return expanded;
|
|
30
|
+
},
|
|
31
|
+
get columns() {
|
|
32
|
+
return columns;
|
|
33
|
+
},
|
|
34
|
+
registerColumn(config) {
|
|
35
|
+
let existingColumn = undefined;
|
|
36
|
+
for (const column of columns) {
|
|
37
|
+
if (column.id !== config.id)
|
|
38
|
+
continue;
|
|
39
|
+
existingColumn = column;
|
|
40
|
+
break;
|
|
41
|
+
}
|
|
42
|
+
if (existingColumn)
|
|
43
|
+
return existingColumn;
|
|
44
|
+
const col = new Column(config);
|
|
45
|
+
columns.push(col);
|
|
46
|
+
return col;
|
|
47
|
+
},
|
|
48
|
+
toggleExpansion(id) {
|
|
49
|
+
if (expanded.has(id))
|
|
50
|
+
expanded.delete(id);
|
|
51
|
+
else
|
|
52
|
+
expanded.add(id);
|
|
28
53
|
}
|
|
29
|
-
|
|
30
|
-
return existingColumn;
|
|
31
|
-
const col = new Column(config);
|
|
32
|
-
this.columns.push(col);
|
|
33
|
-
return col;
|
|
34
|
-
}
|
|
35
|
-
toggleExpansion(id) {
|
|
36
|
-
if (this.expanded.has(id))
|
|
37
|
-
this.expanded.delete(id);
|
|
38
|
-
else
|
|
39
|
-
this.expanded.add(id);
|
|
40
|
-
}
|
|
54
|
+
};
|
|
41
55
|
}
|
|
56
|
+
// export class TableController<T extends TableRow<T>> {
|
|
57
|
+
// id = $state();
|
|
58
|
+
// columns = $state<Column[]>([]);
|
|
59
|
+
// data = $state<T[]>([]);
|
|
60
|
+
// expanded: Set<string> = new SvelteSet<string>();
|
|
61
|
+
// scrollTop = $state(0);
|
|
62
|
+
// constructor(id?: string) {
|
|
63
|
+
// this.id = id;
|
|
64
|
+
// }
|
|
65
|
+
// readonly results = $derived(treeWalker(this.data, this.expanded));
|
|
66
|
+
// refresh(conf: TableConfig<T>) {
|
|
67
|
+
// let intitalState: TableState<T> = {
|
|
68
|
+
// data: [...(conf.data ?? [])],
|
|
69
|
+
// expanded: this.expanded
|
|
70
|
+
// };
|
|
71
|
+
// for (const plugin of conf.plugins ?? []) {
|
|
72
|
+
// intitalState = plugin(intitalState);
|
|
73
|
+
// }
|
|
74
|
+
// this.data = intitalState.data;
|
|
75
|
+
// this.expanded = new SvelteSet(intitalState.expanded);
|
|
76
|
+
// this.id = conf.id ?? this.id ?? pseudoRandomId('table-');
|
|
77
|
+
// }
|
|
78
|
+
// registerColumn(config: ColumnConfig): Column {
|
|
79
|
+
// // only register a column once
|
|
80
|
+
// let existingColumn: Column | undefined = undefined;
|
|
81
|
+
// for (const column of this.columns) {
|
|
82
|
+
// if (column.id !== config.id) continue;
|
|
83
|
+
// existingColumn = column;
|
|
84
|
+
// break;
|
|
85
|
+
// }
|
|
86
|
+
// if (existingColumn) return existingColumn;
|
|
87
|
+
// const col = new Column(config);
|
|
88
|
+
// this.columns.push(col);
|
|
89
|
+
// return col;
|
|
90
|
+
// }
|
|
91
|
+
// toggleExpansion(id: string) {
|
|
92
|
+
// if (this.expanded.has(id)) this.expanded.delete(id);
|
|
93
|
+
// else this.expanded.add(id);
|
|
94
|
+
// }
|
|
95
|
+
// }
|
|
42
96
|
/** Walks though a tree strucure and turns it into a flat list, needed since the `VirtualList` needs a list, not a tree */
|
|
43
|
-
function treeWalker(
|
|
97
|
+
export function treeWalker(config) {
|
|
98
|
+
const { data, expanded } = config;
|
|
44
99
|
const stack = [];
|
|
45
100
|
// push the root nodes of the trees onto the stack
|
|
46
101
|
for (let i = 0; i < data.length; i++) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export { default as Column, type ColumnProps } from './Column.svelte';
|
|
2
2
|
export { getColumnHeadContext } from './ColumnHead.svelte';
|
|
3
|
-
export {
|
|
3
|
+
export { createTableConfig, getAllIds, type TableConfig, type TablePlugin, type TableRow, type TableState } from './controller.svelte';
|
|
4
4
|
export { expandAllPlugin } from './plugins/expandAll.svelte';
|
|
5
5
|
export { searchPlugin } from './plugins/search.svelte';
|
|
6
|
-
export { getTableContext, default as Table, type TableProps } from './Table.svelte';
|
|
6
|
+
export { getTableContext, default as Table, type TableContext, type TableProps } from './Table.svelte';
|
|
7
7
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/lib/components/table/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,MAAM,EAAE,KAAK,WAAW,EAAE,MAAM,iBAAiB,CAAC;AACtE,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,EACH,SAAS,EACT,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/lib/components/table/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,MAAM,EAAE,KAAK,WAAW,EAAE,MAAM,iBAAiB,CAAC;AACtE,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,EACH,iBAAiB,EACjB,SAAS,EACT,KAAK,WAAW,EAChB,KAAK,WAAW,EAChB,KAAK,QAAQ,EACb,KAAK,UAAU,EAClB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAC7D,OAAO,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AACvD,OAAO,EACH,eAAe,EACf,OAAO,IAAI,KAAK,EAChB,KAAK,YAAY,EACjB,KAAK,UAAU,EAClB,MAAM,gBAAgB,CAAC"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export { default as Column } from './Column.svelte';
|
|
2
2
|
export { getColumnHeadContext } from './ColumnHead.svelte';
|
|
3
|
-
export {
|
|
3
|
+
export { createTableConfig, getAllIds } from './controller.svelte';
|
|
4
4
|
export { expandAllPlugin } from './plugins/expandAll.svelte';
|
|
5
5
|
export { searchPlugin } from './plugins/search.svelte';
|
|
6
6
|
export { getTableContext, default as Table } from './Table.svelte';
|
package/package.json
CHANGED
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
onclick?: (e: Event) => void | Promise<void>;
|
|
20
20
|
/** Cannot be used with resizable columns*/
|
|
21
21
|
ignoreWidth?: boolean;
|
|
22
|
+
offsetNestingLevel?: number;
|
|
22
23
|
}
|
|
23
24
|
</script>
|
|
24
25
|
|
|
@@ -30,11 +31,12 @@
|
|
|
30
31
|
ignoreWidth = false,
|
|
31
32
|
// ColumnConfig
|
|
32
33
|
resizable = true,
|
|
34
|
+
offsetNestingLevel = 0,
|
|
33
35
|
...props
|
|
34
36
|
}: ColumnProps = $props();
|
|
35
37
|
|
|
36
38
|
// Register the new column if this is the first table row that was rendered
|
|
37
|
-
const table = getTableContext();
|
|
39
|
+
const { table, nestingInset } = getTableContext();
|
|
38
40
|
const column = table.registerColumn({ resizable, ...props });
|
|
39
41
|
const allowClicking = $derived(!!onclick);
|
|
40
42
|
|
|
@@ -60,7 +62,9 @@
|
|
|
60
62
|
this={allowClicking ? 'button' : 'div'}
|
|
61
63
|
onclick={allowClicking ? onClick : undefined}
|
|
62
64
|
type={onclick ? 'button' : undefined}
|
|
63
|
-
style={ignoreWidth
|
|
65
|
+
style={ignoreWidth
|
|
66
|
+
? ''
|
|
67
|
+
: `width: calc(var(--spacing) * ${offsetNestingLevel * nestingInset} - ${column.width ?? 0}px) !important;`}
|
|
64
68
|
class={twMerge(
|
|
65
69
|
clsx(
|
|
66
70
|
'flex shrink-0 flex-row items-stretch justify-start truncate',
|
|
@@ -5,10 +5,11 @@
|
|
|
5
5
|
import { getContext, setContext, type Snippet } from 'svelte';
|
|
6
6
|
import type { ClassValue } from 'svelte/elements';
|
|
7
7
|
import { twMerge } from 'tailwind-merge';
|
|
8
|
-
import { Column,
|
|
8
|
+
import { Column, type TableConfig, type TablePlugin, type TableRow } from '.';
|
|
9
9
|
import ColumnHead from './ColumnHead.svelte';
|
|
10
10
|
import Row from './Row.svelte';
|
|
11
11
|
import VirtualList from './VirtualList.svelte';
|
|
12
|
+
import { createTableConfig, treeWalker } from './controller.svelte';
|
|
12
13
|
|
|
13
14
|
export interface TableProps<T extends TableRow<T>> {
|
|
14
15
|
class?: ClassValue;
|
|
@@ -23,20 +24,26 @@
|
|
|
23
24
|
rowClass?: ClassValue;
|
|
24
25
|
headerClass?: ClassValue;
|
|
25
26
|
plugins?: TablePlugin<T>[];
|
|
26
|
-
|
|
27
|
+
config?: TableConfig<T>;
|
|
27
28
|
/**
|
|
28
29
|
* **Bindable**
|
|
29
30
|
*/
|
|
30
31
|
b_scrollTop?: number;
|
|
32
|
+
/** Equal to tailwinds `spacing` variable (e.g. `w-4` would be `4`) */
|
|
33
|
+
nestingInset?: number;
|
|
31
34
|
}
|
|
32
35
|
|
|
33
36
|
const TABLE_CONTEXT = {};
|
|
34
|
-
|
|
35
|
-
|
|
37
|
+
export type TableContext<T extends TableRow<T>> = {
|
|
38
|
+
table: TableConfig<T>;
|
|
39
|
+
nestingInset: number;
|
|
40
|
+
};
|
|
41
|
+
function setTableContext<T extends TableRow<T>>(context: TableContext<T>) {
|
|
42
|
+
setContext(TABLE_CONTEXT, context);
|
|
36
43
|
}
|
|
37
44
|
|
|
38
|
-
export function getTableContext<T extends TableRow<T>>():
|
|
39
|
-
return getContext<
|
|
45
|
+
export function getTableContext<T extends TableRow<T>>(): TableContext<T> {
|
|
46
|
+
return getContext<TableContext<T>>(TABLE_CONTEXT);
|
|
40
47
|
}
|
|
41
48
|
</script>
|
|
42
49
|
|
|
@@ -54,83 +61,94 @@
|
|
|
54
61
|
onclick,
|
|
55
62
|
href,
|
|
56
63
|
plugins = [],
|
|
57
|
-
|
|
64
|
+
config: table = createTableConfig<T>(),
|
|
65
|
+
nestingInset = 4
|
|
58
66
|
}: Props<T> = $props();
|
|
59
67
|
|
|
60
68
|
$effect(() => {
|
|
61
|
-
table.
|
|
62
|
-
|
|
63
|
-
plugins
|
|
64
|
-
});
|
|
69
|
+
table.data = data;
|
|
70
|
+
table.plugins = plugins;
|
|
65
71
|
});
|
|
66
72
|
|
|
67
|
-
setTableContext(
|
|
73
|
+
setTableContext({
|
|
74
|
+
get table() {
|
|
75
|
+
return table;
|
|
76
|
+
},
|
|
77
|
+
get nestingInset() {
|
|
78
|
+
return nestingInset;
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
const results = $derived(treeWalker(table));
|
|
83
|
+
|
|
68
84
|
const treeIndicatorId = pseudoRandomId('tree-indicator-');
|
|
69
85
|
</script>
|
|
70
86
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
{
|
|
89
|
-
{
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
{@render firstColumn?.({ row: node })}
|
|
108
|
-
<Column
|
|
109
|
-
id={treeIndicatorId}
|
|
110
|
-
resizable={false}
|
|
111
|
-
header=""
|
|
112
|
-
onclick={() => {
|
|
113
|
-
table.toggleExpansion(node.id);
|
|
114
|
-
}}
|
|
115
|
-
ignoreWidth={table.results.someHaveChildren}
|
|
116
|
-
width={table.results.someHaveChildren ? 24 : 0}
|
|
117
|
-
minWidth={0}
|
|
87
|
+
{#key table.id}
|
|
88
|
+
<VirtualList
|
|
89
|
+
data={results.entries}
|
|
90
|
+
class={['border-transparent', clazz, 'flex flex-col overflow-hidden']}
|
|
91
|
+
bind:b_scrollTop={table.scrollTop}
|
|
92
|
+
{rowHeight}
|
|
93
|
+
>
|
|
94
|
+
{#snippet header()}
|
|
95
|
+
<div
|
|
96
|
+
class={twMerge(
|
|
97
|
+
clsx(
|
|
98
|
+
'flex w-fit min-w-full flex-row gap-2 border-b border-inherit pr-4 pl-2',
|
|
99
|
+
headerClass
|
|
100
|
+
)
|
|
101
|
+
)}
|
|
102
|
+
>
|
|
103
|
+
{#each table.columns as column (column.id)}
|
|
104
|
+
<ColumnHead {column}>
|
|
105
|
+
{#if typeof column.header === 'function'}
|
|
106
|
+
{@render column.header()}
|
|
107
|
+
{:else}
|
|
108
|
+
<div
|
|
109
|
+
class="flex grow flex-row items-center justify-start gap-4 py-2 text-start select-none"
|
|
110
|
+
>
|
|
111
|
+
{column.header}
|
|
112
|
+
</div>
|
|
113
|
+
{/if}
|
|
114
|
+
</ColumnHead>
|
|
115
|
+
{/each}
|
|
116
|
+
</div>
|
|
117
|
+
{/snippet}
|
|
118
|
+
{#snippet children({ row: { node, id, nestingLevel }, index })}
|
|
119
|
+
<Row
|
|
120
|
+
onclick={onclick ? () => onclick(node) : undefined}
|
|
121
|
+
href={href?.(node)}
|
|
122
|
+
class={rowClass}
|
|
118
123
|
>
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
124
|
+
{@render firstColumn?.({ row: node })}
|
|
125
|
+
<Column
|
|
126
|
+
id={treeIndicatorId}
|
|
127
|
+
resizable={false}
|
|
128
|
+
header=""
|
|
129
|
+
onclick={() => {
|
|
130
|
+
table.toggleExpansion(node.id);
|
|
131
|
+
}}
|
|
132
|
+
ignoreWidth={results.someHaveChildren}
|
|
133
|
+
width={results.someHaveChildren ? 24 : 0}
|
|
134
|
+
minWidth={0}
|
|
122
135
|
>
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
</
|
|
136
|
+
<div
|
|
137
|
+
class="flex h-full items-center justify-end"
|
|
138
|
+
style="width: calc(var(--spacing) * {nestingLevel * nestingInset} + 24px);"
|
|
139
|
+
>
|
|
140
|
+
{#if node.children}
|
|
141
|
+
<ChevronRight
|
|
142
|
+
class={[
|
|
143
|
+
'ml-auto aspect-square shrink-0 transition-transform duration-100',
|
|
144
|
+
table.expanded.has(id) && 'rotate-90'
|
|
145
|
+
]}
|
|
146
|
+
/>
|
|
147
|
+
{/if}
|
|
148
|
+
</div>
|
|
149
|
+
</Column>
|
|
150
|
+
{@render passedChildren?.({ row: node, nestingLevel, index })}
|
|
151
|
+
</Row>
|
|
152
|
+
{/snippet}
|
|
153
|
+
</VirtualList>
|
|
154
|
+
{/key}
|
|
@@ -7,6 +7,63 @@ export type TablePlugin<T extends TableRow<T>> = (state: TableState<T>) => Table
|
|
|
7
7
|
export interface TableConfig<T extends TableRow<T>> {
|
|
8
8
|
data: T[];
|
|
9
9
|
plugins?: TablePlugin<T>[];
|
|
10
|
+
id?: string;
|
|
11
|
+
expanded: SvelteSet<string>;
|
|
12
|
+
columns: Column[];
|
|
13
|
+
scrollTop: number;
|
|
14
|
+
registerColumn: (config: ColumnConfig) => Column;
|
|
15
|
+
toggleExpansion: (id: string) => void;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function createTableConfig<T extends TableRow<T>>(): TableConfig<T> {
|
|
19
|
+
let data = $state<T[]>([]);
|
|
20
|
+
let plugins = $state<TablePlugin<T>[]>([]);
|
|
21
|
+
let scrollTop = $state(0);
|
|
22
|
+
const expanded = new SvelteSet<string>();
|
|
23
|
+
const columns = $state<Column[]>([]);
|
|
24
|
+
|
|
25
|
+
return {
|
|
26
|
+
get data() {
|
|
27
|
+
return data;
|
|
28
|
+
},
|
|
29
|
+
set data(d) {
|
|
30
|
+
data = d;
|
|
31
|
+
},
|
|
32
|
+
get plugins() {
|
|
33
|
+
return plugins;
|
|
34
|
+
},
|
|
35
|
+
set plugins(p) {
|
|
36
|
+
plugins = p;
|
|
37
|
+
},
|
|
38
|
+
get scrollTop() {
|
|
39
|
+
return scrollTop;
|
|
40
|
+
},
|
|
41
|
+
set scrollTop(s) {
|
|
42
|
+
scrollTop = s;
|
|
43
|
+
},
|
|
44
|
+
get expanded() {
|
|
45
|
+
return expanded;
|
|
46
|
+
},
|
|
47
|
+
get columns() {
|
|
48
|
+
return columns;
|
|
49
|
+
},
|
|
50
|
+
registerColumn(config: ColumnConfig) {
|
|
51
|
+
let existingColumn: Column | undefined = undefined;
|
|
52
|
+
for (const column of columns) {
|
|
53
|
+
if (column.id !== config.id) continue;
|
|
54
|
+
existingColumn = column;
|
|
55
|
+
break;
|
|
56
|
+
}
|
|
57
|
+
if (existingColumn) return existingColumn;
|
|
58
|
+
const col = new Column(config);
|
|
59
|
+
columns.push(col);
|
|
60
|
+
return col;
|
|
61
|
+
},
|
|
62
|
+
toggleExpansion(id: string) {
|
|
63
|
+
if (expanded.has(id)) expanded.delete(id);
|
|
64
|
+
else expanded.add(id);
|
|
65
|
+
}
|
|
66
|
+
};
|
|
10
67
|
}
|
|
11
68
|
|
|
12
69
|
export interface TableState<T extends TableRow<T>> {
|
|
@@ -20,50 +77,57 @@ interface TreeRow<T> {
|
|
|
20
77
|
id: string;
|
|
21
78
|
}
|
|
22
79
|
|
|
23
|
-
export class TableController<T extends TableRow<T>> {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
}
|
|
80
|
+
// export class TableController<T extends TableRow<T>> {
|
|
81
|
+
// id = $state();
|
|
82
|
+
// columns = $state<Column[]>([]);
|
|
83
|
+
// data = $state<T[]>([]);
|
|
84
|
+
// expanded: Set<string> = new SvelteSet<string>();
|
|
85
|
+
// scrollTop = $state(0);
|
|
86
|
+
|
|
87
|
+
// constructor(id?: string) {
|
|
88
|
+
// this.id = id;
|
|
89
|
+
// }
|
|
90
|
+
|
|
91
|
+
// readonly results = $derived(treeWalker(this.data, this.expanded));
|
|
92
|
+
|
|
93
|
+
// refresh(conf: TableConfig<T>) {
|
|
94
|
+
// let intitalState: TableState<T> = {
|
|
95
|
+
// data: [...(conf.data ?? [])],
|
|
96
|
+
// expanded: this.expanded
|
|
97
|
+
// };
|
|
98
|
+
|
|
99
|
+
// for (const plugin of conf.plugins ?? []) {
|
|
100
|
+
// intitalState = plugin(intitalState);
|
|
101
|
+
// }
|
|
102
|
+
|
|
103
|
+
// this.data = intitalState.data;
|
|
104
|
+
// this.expanded = new SvelteSet(intitalState.expanded);
|
|
105
|
+
// this.id = conf.id ?? this.id ?? pseudoRandomId('table-');
|
|
106
|
+
// }
|
|
107
|
+
|
|
108
|
+
// registerColumn(config: ColumnConfig): Column {
|
|
109
|
+
// // only register a column once
|
|
110
|
+
// let existingColumn: Column | undefined = undefined;
|
|
111
|
+
// for (const column of this.columns) {
|
|
112
|
+
// if (column.id !== config.id) continue;
|
|
113
|
+
// existingColumn = column;
|
|
114
|
+
// break;
|
|
115
|
+
// }
|
|
116
|
+
// if (existingColumn) return existingColumn;
|
|
117
|
+
// const col = new Column(config);
|
|
118
|
+
// this.columns.push(col);
|
|
119
|
+
// return col;
|
|
120
|
+
// }
|
|
121
|
+
|
|
122
|
+
// toggleExpansion(id: string) {
|
|
123
|
+
// if (this.expanded.has(id)) this.expanded.delete(id);
|
|
124
|
+
// else this.expanded.add(id);
|
|
125
|
+
// }
|
|
126
|
+
// }
|
|
64
127
|
|
|
65
128
|
/** Walks though a tree strucure and turns it into a flat list, needed since the `VirtualList` needs a list, not a tree */
|
|
66
|
-
function treeWalker<T extends TableRow<T>>(
|
|
129
|
+
export function treeWalker<T extends TableRow<T>>(config: TableConfig<T>) {
|
|
130
|
+
const { data, expanded } = config;
|
|
67
131
|
const stack: { node: T; nestingLevel: number }[] = [];
|
|
68
132
|
|
|
69
133
|
// push the root nodes of the trees onto the stack
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
export { default as Column, type ColumnProps } from './Column.svelte';
|
|
2
2
|
export { getColumnHeadContext } from './ColumnHead.svelte';
|
|
3
3
|
export {
|
|
4
|
+
createTableConfig,
|
|
4
5
|
getAllIds,
|
|
5
|
-
TableController,
|
|
6
6
|
type TableConfig,
|
|
7
7
|
type TablePlugin,
|
|
8
8
|
type TableRow,
|
|
@@ -10,4 +10,9 @@ export {
|
|
|
10
10
|
} from './controller.svelte';
|
|
11
11
|
export { expandAllPlugin } from './plugins/expandAll.svelte';
|
|
12
12
|
export { searchPlugin } from './plugins/search.svelte';
|
|
13
|
-
export {
|
|
13
|
+
export {
|
|
14
|
+
getTableContext,
|
|
15
|
+
default as Table,
|
|
16
|
+
type TableContext,
|
|
17
|
+
type TableProps
|
|
18
|
+
} from './Table.svelte';
|