svelte-tably 1.0.0-next.0 → 1.0.0-next.10

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/README.md CHANGED
@@ -1,6 +1,8 @@
1
1
  # svelte-tably
2
2
 
3
- Work in progress. I needed a break from my primary project, so here's a little side-project exploring that amazing capabilities of Svelte 5 with a Dynamic table!
3
+ Work in progress. I needed a break from my primary project, so here's a little side-project exploring the amazing capabilities of Svelte 5 with a Dynamic table!
4
+
5
+ Example on [Svelte 5 Playground](https://svelte.dev/playground/a16d71c97445455e80a55b77ec1cf915?version=5)
4
6
 
5
7
  A high performant dynamic table
6
8
 
@@ -9,11 +11,83 @@ A high performant dynamic table
9
11
  - [x] Re-order columns
10
12
  - [x] Resize columns
11
13
  - [x] Statusbar
12
- - [x] Virtual data (for sorting/filtering)
14
+ - [x] "Virtual" data (for sorting/filtering)
13
15
  - [x] Panels
14
- - [ ] sorting
15
- - [ ] select
16
+ - [x] Virtual elements
17
+ - [x] sorting
18
+ - [x] select
16
19
  - [ ] filtering
17
20
  - [ ] orderable table
18
21
  - [ ] row context-menu
19
- - [ ] dropout section
22
+ - [ ] dropout section
23
+ - [ ] csv export
24
+
25
+ ### Usage Notes
26
+
27
+ ```html
28
+ <script lang='ts'>
29
+ import Table from 'svelte-tably'
30
+
31
+ const data = $state([
32
+ { name: 'John Doe', age: 30, email: 'johndoe@example.com' },
33
+ { name: 'Jane Doe', age: 25, email: 'janedoe@example.com' },
34
+ ])
35
+
36
+ let activePanel = $state('columns') as string | undefined
37
+ let selected = $state([]) as typeof data
38
+ </script>
39
+
40
+ <Table {data} panel={activePanel} select bind:selected>
41
+ {#snippet content({ Column, Panel, state, data })}
42
+ <Column id='name' sticky>
43
+ {#snippet header()}
44
+ Name
45
+ {/snippet}
46
+ {#snippet row(row)}
47
+ {row.name}
48
+ {/snippet}
49
+
50
+ <!-- Optional per column. -->
51
+ {#snippet statusbar()}
52
+ {data.length}
53
+ {/snippet}
54
+ </Column>
55
+ <Column ...>
56
+ ...
57
+ </Column>
58
+ <!-- If you want to sort/filter a virtual value, that does not exist in the data -->
59
+ <Column id='virtual' value={row => row.age > 18}>
60
+ ...
61
+ {#snippet row(row, virtual)}
62
+ {virtual ? 'Adult' : 'Adolescent'}
63
+ {/snippet}
64
+ ...
65
+ </Column>
66
+
67
+ <Panel id='columns'>
68
+ <!-- Anything you might like -->
69
+ </Panel>
70
+ <Panel ... backdrop={false}>
71
+ ...
72
+ </Panel>
73
+ {/snippet}
74
+ </Table>
75
+ ```
76
+
77
+ #### Styling
78
+
79
+ For quick styling
80
+
81
+ | CSS Variable | Description | Default |
82
+ | - | - | - |
83
+ | --tably-bg | background-color | `hsl(0, 0%, 100%)` |
84
+ | --tably-color | color | `hsl(0, 0%, 0%)` |
85
+ | --tably-border | border | `hsl(0, 0%, 90%)` |
86
+ | --tably-statusbar | background-color for the statusbar | `hsl(0, 0%, 98%)` |
87
+ | --tably-padding-y | Padding above/below each column | `.5rem` |
88
+ | --tably-padding-x | Padding left of each column | `1rem` |
89
+ | --tably-radius | Table radius | `.25rem` |
90
+
91
+ Advanced styling can be done via `:global .svelte-tably`
92
+
93
+
@@ -0,0 +1,164 @@
1
+ <!-- @component
2
+
3
+ This is a description, \
4
+ on how to use this.
5
+
6
+ @example
7
+ <Component />
8
+
9
+ -->
10
+
11
+ <script module lang="ts">
12
+ export type RowCtx<V> = {
13
+ readonly value: V
14
+ readonly isHovered: boolean
15
+ readonly index: number
16
+ selected: boolean
17
+ }
18
+
19
+ export interface ColumnProps<T, V> {
20
+ header?: Snippet<
21
+ [
22
+ /**
23
+ * Is true when displaying in the header,
24
+ * so additional content can be shown if desired,
25
+ * so the header snippet can be re-used.
26
+ */
27
+ header?: boolean
28
+ ]
29
+ >
30
+ row: Snippet<[item: T, row: RowCtx<V>]>
31
+ statusbar?: Snippet
32
+
33
+ id: string
34
+
35
+ // options
36
+ /**
37
+ * Is this column sticky by default?
38
+ * @default false
39
+ */
40
+ sticky?: boolean
41
+ /**
42
+ * Fixed is like sticky, but in its own category — meant to not be moved/hidden ex. select-boxes
43
+ * @default false
44
+ */
45
+ fixed?: boolean
46
+ /**
47
+ * Is this column sorted by default?
48
+ * @default false
49
+ */
50
+ sortby?: boolean
51
+ /**
52
+ * Is this column visible by default?
53
+ * @default true
54
+ */
55
+ show?: boolean
56
+ /**
57
+ * The width of the column in pixels by default
58
+ * @default 150
59
+ */
60
+ width?: number
61
+ /**
62
+ * The value of the row. Required for sorting/filtering
63
+ * @example row => row.name
64
+ */
65
+ value?: (item: T) => V
66
+ /**
67
+ * Makes the column sortable. Sorts based of a sorting function.
68
+ *
69
+ * **Important**   `value`-attribute is required adjacent to this.
70
+ *
71
+ * If `true` uses the default `.sort()` algorithm.
72
+ *
73
+ * @default false
74
+ */
75
+ sort?: boolean | ((a: V, b: V) => number)
76
+ /**
77
+ * Is this column resizeable?
78
+ * Can not be resized if Table is marked as `resizeable={false}`
79
+ * @default true
80
+ */
81
+ resizeable?: boolean
82
+
83
+ /**
84
+ * Optional: Provide the table it is a part of
85
+ */
86
+ table?: TableState
87
+ }
88
+
89
+ export interface ColumnState<T = unknown, V = unknown, C extends ColumnProps<T, V> = ColumnProps<T, V>> {
90
+ id: C['id']
91
+ header?: C['header']
92
+ row: C['row']
93
+ statusbar?: C['statusbar']
94
+
95
+ fixed?: boolean
96
+
97
+ /** Default options for initial table */
98
+ defaults: {
99
+ sticky?: boolean
100
+ sort?: boolean
101
+ show?: boolean
102
+ width?: number
103
+ }
104
+ /** More options */
105
+ options: {
106
+ value?: C['value']
107
+ sorting?: boolean | ((a: any, b: any) => number)
108
+ resizeable: boolean
109
+ }
110
+ }
111
+ </script>
112
+
113
+ <script lang="ts">
114
+ import { onDestroy, type Snippet } from 'svelte'
115
+ import { getTableState, type TableState } from './Table.svelte'
116
+
117
+ type T = $$Generic<Record<PropertyKey, any>>
118
+ type V = $$Generic
119
+
120
+ let {
121
+ header,
122
+ row,
123
+ statusbar,
124
+ id,
125
+
126
+ sticky = false,
127
+ fixed = false,
128
+ sortby = false,
129
+ show = true,
130
+ width,
131
+
132
+ resizeable = true,
133
+ value,
134
+ sort,
135
+
136
+ table
137
+ }: ColumnProps<T, V> = $props()
138
+
139
+ const column: ColumnState<T, V> = $state({
140
+ id,
141
+ header,
142
+ row,
143
+ statusbar,
144
+ fixed,
145
+ defaults: {
146
+ sticky,
147
+ sort: sortby,
148
+ show,
149
+ width
150
+ },
151
+ options: {
152
+ value,
153
+ sorting: sort,
154
+ resizeable
155
+ }
156
+ })
157
+
158
+ table ??= getTableState()
159
+ table.addColumn(id, column as ColumnState)
160
+
161
+ onDestroy(() => {
162
+ table.removeColumn(id)
163
+ })
164
+ </script>
@@ -0,0 +1,115 @@
1
+ export type RowCtx<V> = {
2
+ readonly value: V;
3
+ readonly isHovered: boolean;
4
+ readonly index: number;
5
+ selected: boolean;
6
+ };
7
+ export interface ColumnProps<T, V> {
8
+ header?: Snippet<[
9
+ /**
10
+ * Is true when displaying in the header,
11
+ * so additional content can be shown if desired,
12
+ * so the header snippet can be re-used.
13
+ */
14
+ header?: boolean
15
+ ]>;
16
+ row: Snippet<[item: T, row: RowCtx<V>]>;
17
+ statusbar?: Snippet;
18
+ id: string;
19
+ /**
20
+ * Is this column sticky by default?
21
+ * @default false
22
+ */
23
+ sticky?: boolean;
24
+ /**
25
+ * Fixed is like sticky, but in its own category — meant to not be moved/hidden ex. select-boxes
26
+ * @default false
27
+ */
28
+ fixed?: boolean;
29
+ /**
30
+ * Is this column sorted by default?
31
+ * @default false
32
+ */
33
+ sortby?: boolean;
34
+ /**
35
+ * Is this column visible by default?
36
+ * @default true
37
+ */
38
+ show?: boolean;
39
+ /**
40
+ * The width of the column in pixels by default
41
+ * @default 150
42
+ */
43
+ width?: number;
44
+ /**
45
+ * The value of the row. Required for sorting/filtering
46
+ * @example row => row.name
47
+ */
48
+ value?: (item: T) => V;
49
+ /**
50
+ * Makes the column sortable. Sorts based of a sorting function.
51
+ *
52
+ * **Important**   `value`-attribute is required adjacent to this.
53
+ *
54
+ * If `true` uses the default `.sort()` algorithm.
55
+ *
56
+ * @default false
57
+ */
58
+ sort?: boolean | ((a: V, b: V) => number);
59
+ /**
60
+ * Is this column resizeable?
61
+ * Can not be resized if Table is marked as `resizeable={false}`
62
+ * @default true
63
+ */
64
+ resizeable?: boolean;
65
+ /**
66
+ * Optional: Provide the table it is a part of
67
+ */
68
+ table?: TableState;
69
+ }
70
+ export interface ColumnState<T = unknown, V = unknown, C extends ColumnProps<T, V> = ColumnProps<T, V>> {
71
+ id: C['id'];
72
+ header?: C['header'];
73
+ row: C['row'];
74
+ statusbar?: C['statusbar'];
75
+ fixed?: boolean;
76
+ /** Default options for initial table */
77
+ defaults: {
78
+ sticky?: boolean;
79
+ sort?: boolean;
80
+ show?: boolean;
81
+ width?: number;
82
+ };
83
+ /** More options */
84
+ options: {
85
+ value?: C['value'];
86
+ sorting?: boolean | ((a: any, b: any) => number);
87
+ resizeable: boolean;
88
+ };
89
+ }
90
+ import { type Snippet } from 'svelte';
91
+ import { type TableState } from './Table.svelte';
92
+ declare class __sveltets_Render<T extends Record<PropertyKey, any>, V> {
93
+ props(): ColumnProps<T, V>;
94
+ events(): {};
95
+ slots(): {};
96
+ bindings(): "";
97
+ exports(): {};
98
+ }
99
+ interface $$IsomorphicComponent {
100
+ new <T extends Record<PropertyKey, any>, V>(options: import('svelte').ComponentConstructorOptions<ReturnType<__sveltets_Render<T, V>['props']>>): import('svelte').SvelteComponent<ReturnType<__sveltets_Render<T, V>['props']>, ReturnType<__sveltets_Render<T, V>['events']>, ReturnType<__sveltets_Render<T, V>['slots']>> & {
101
+ $$bindings?: ReturnType<__sveltets_Render<T, V>['bindings']>;
102
+ } & ReturnType<__sveltets_Render<T, V>['exports']>;
103
+ <T extends Record<PropertyKey, any>, V>(internal: unknown, props: ReturnType<__sveltets_Render<T, V>['props']> & {}): ReturnType<__sveltets_Render<T, V>['exports']>;
104
+ z_$$bindings?: ReturnType<__sveltets_Render<any, any>['bindings']>;
105
+ }
106
+ /**
107
+ * This is a description, \
108
+ * on how to use this.
109
+ *
110
+ * @example
111
+ * <Component />
112
+ */
113
+ declare const Column: $$IsomorphicComponent;
114
+ type Column<T extends Record<PropertyKey, any>, V> = InstanceType<typeof Column<T, V>>;
115
+ export default Column;
@@ -10,10 +10,10 @@
10
10
 
11
11
  <script module lang='ts'>
12
12
 
13
- export interface Panel {
13
+ export interface Panel<T extends Record<PropertyKey, unknown> = Record<PropertyKey, unknown>> {
14
14
  /** A darkened backdrop? */
15
15
  backdrop: boolean
16
- content: Snippet<[table: TableState]>
16
+ content: Snippet<[context: { readonly table: TableState<T>, readonly data: T[] }]>
17
17
  }
18
18
 
19
19
  export class PanelTween {
@@ -29,45 +29,46 @@
29
29
  this.#tween.set(value).then(() => this.transitioning = false)
30
30
  }
31
31
 
32
- constructor(cb: () => string | undefined) {
32
+ constructor(cb: () => string | undefined, added = 0) {
33
33
  $effect.pre(() => {
34
- this.target = cb() ? this.width : 0
34
+ this.target = cb() ? this.width + added : 0
35
35
  })
36
36
  }
37
37
  }
38
38
 
39
39
  </script>
40
40
 
41
- <script lang='ts'>
41
+ <script lang='ts' generics='T extends Record<PropertyKey, unknown>'>
42
42
 
43
43
  import { onDestroy, type Snippet } from 'svelte'
44
- import { getTableState, type TableState } from './Table.svelte'
44
+ import Table, { getTableState, type TableState } from './Table.svelte'
45
45
  import { Tween } from 'svelte/motion'
46
46
  import { sineInOut } from 'svelte/easing'
47
47
 
48
48
  interface Props {
49
+ id: string
50
+
49
51
  /** A darkened backdrop? */
50
52
  backdrop?: boolean
51
- children: Snippet<[table: TableState]>
53
+ children: Snippet<[context: { readonly table: TableState<T>, readonly data: T[] }]>
52
54
  }
53
55
 
54
56
  let {
55
57
  backdrop = true,
56
58
  children,
57
- ...rest
59
+ id
58
60
  }: Props = $props()
59
- const key = (rest as unknown as { __key: string }).__key
60
-
61
- const panel: Panel = $state({
61
+
62
+ const panel: Panel<T> = $state({
62
63
  backdrop,
63
64
  content: children
64
65
  })
65
66
 
66
67
  const table = getTableState()
67
- table.panels[key] = panel
68
+ table.panels[id] = panel
68
69
 
69
70
  onDestroy(() => {
70
- delete table.panels[key]
71
+ delete table.panels[id]
71
72
  })
72
73
 
73
74
  </script>
@@ -1,27 +1,31 @@
1
- export interface TableState<T extends Record<PropertyKey, any> = Record<PropertyKey, any>> {
2
- columns: Record<string, Column<T, unknown>>;
3
- panels: Record<string, Panel>;
4
- sortby?: string;
5
- positions: {
6
- sticky: string[];
7
- scroll: string[];
8
- hidden: string[];
9
- toggle(key: string): void;
10
- };
11
- readonly data: T[];
12
- addColumn(key: string, options: Column<T, unknown>): void;
13
- removeColumn(key: string): void;
1
+ export interface Panel<T extends Record<PropertyKey, unknown> = Record<PropertyKey, unknown>> {
2
+ /** A darkened backdrop? */
3
+ backdrop: boolean;
4
+ content: Snippet<[context: {
5
+ readonly table: TableState<T>;
6
+ readonly data: T[];
7
+ }]>;
8
+ }
9
+ export declare class PanelTween {
10
+ #private;
11
+ current: number;
12
+ transitioning: boolean;
13
+ /** bind:clientWidth */
14
+ width: number;
15
+ set target(value: number);
16
+ constructor(cb: () => string | undefined, added?: number);
14
17
  }
15
- export declare function getTableState<T extends Record<PropertyKey, any> = Record<PropertyKey, any>>(): TableState<T>;
16
18
  import { type Snippet } from 'svelte';
17
- import { type Column } from './Column.svelte';
18
- import { type Panel } from './Panel.svelte';
19
+ import { type TableState } from './Table.svelte';
19
20
  declare class __sveltets_Render<T extends Record<PropertyKey, unknown>> {
20
21
  props(): {
21
- children?: Snippet;
22
- panel?: string;
23
- data?: T[] | undefined;
24
- id?: string;
22
+ id: string;
23
+ /** A darkened backdrop? */
24
+ backdrop?: boolean;
25
+ children: Snippet<[context: {
26
+ readonly table: TableState<T>;
27
+ readonly data: T[];
28
+ }]>;
25
29
  };
26
30
  events(): {};
27
31
  slots(): {};
@@ -42,6 +46,6 @@ interface $$IsomorphicComponent {
42
46
  * @example
43
47
  * <Component />
44
48
  */
45
- declare const Table: $$IsomorphicComponent;
46
- type Table<T extends Record<PropertyKey, unknown>> = InstanceType<typeof Table<T>>;
47
- export default Table;
49
+ declare const Panel: $$IsomorphicComponent;
50
+ type Panel<T extends Record<PropertyKey, unknown>> = InstanceType<typeof Panel<T>>;
51
+ export default Panel;