svelte-tably 1.0.0-next.1 → 1.0.0-next.11

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
@@ -11,63 +11,83 @@ A high performant dynamic table
11
11
  - [x] Re-order columns
12
12
  - [x] Resize columns
13
13
  - [x] Statusbar
14
- - [x] Virtual data (for sorting/filtering)
14
+ - [x] "Virtual" data (for sorting/filtering)
15
15
  - [x] Panels
16
- - [ ] sorting
17
- - [ ] select
18
- - [ ] filtering
16
+ - [x] Virtual elements
17
+ - [x] sorting
18
+ - [x] select
19
+ - [x] filtering
19
20
  - [ ] orderable table
20
21
  - [ ] row context-menu
21
22
  - [ ] dropout section
23
+ - [ ] csv export
22
24
 
23
25
  ### Usage Notes
24
26
 
25
- Simple example.
26
-
27
- Create a state for your data and a state for your active panel:
28
-
29
- ```markdown
27
+ ```html
30
28
  <script lang='ts'>
31
- import { Table } from '$lib/index.js'
29
+ import Table from 'svelte-tably'
32
30
 
33
- const data = $state([
34
- { name: 'John Doe', age: 30, email: 'johndoe@example.com' },
35
- { name: 'Jane Doe', age: 25, email: 'janedoe@example.com' },
36
- ])
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
37
38
  </script>
38
39
 
39
- <Table {data}>
40
- <Table.Name>
41
- {#snippet header()}
42
- Name
43
- {/snippet}
44
- {#snippet row(item)}
45
- {item.name}
46
- {/snippet}
47
- </Table.Name>
48
- <Table.Age>
49
- {#snippet header()}
50
- Age
51
- {/snippet}
52
- {#snippet row(item)}
53
- {item.age}
54
- {/snippet}
55
- </Table.Age>
56
- <Table.Email>
57
- {#snippet header()}
58
- Email
59
- {/snippet}
60
- {#snippet row(item)}
61
- {item.email}
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>
62
73
  {/snippet}
63
- </Table.Email>
64
74
  </Table>
65
75
  ```
66
76
 
67
- To create a column, simply add a new `<Table.ColumnName>` component inside the `<Table>` component. Replace `ColumnName` with the actual name of the column you want to create.
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`
68
92
 
69
- Inside the column component, you need to define three snippets:
70
93
 
71
- * `header`: the content of the column header
72
- * `row`: the content of each row in the column
73
- * `statusbar`: (optional) the content of the status bar for the column
@@ -0,0 +1,174 @@
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
+ *
85
+ * @example (value) => value.includes(search)
86
+ */
87
+ filter?: (value: V) => boolean
88
+
89
+ /**
90
+ * Optional: Provide the table it is a part of
91
+ */
92
+ table?: TableState
93
+ }
94
+
95
+ export interface ColumnState<T = unknown, V = unknown, C extends ColumnProps<T, V> = ColumnProps<T, V>> {
96
+ id: C['id']
97
+ header?: C['header']
98
+ row: C['row']
99
+ statusbar?: C['statusbar']
100
+
101
+ fixed?: boolean
102
+ filter?: C['filter']
103
+
104
+ /** Default options for initial table */
105
+ defaults: {
106
+ sticky?: boolean
107
+ sort?: boolean
108
+ show?: boolean
109
+ width?: number
110
+ }
111
+ /** More options */
112
+ options: {
113
+ value?: C['value']
114
+ sorting?: boolean | ((a: any, b: any) => number)
115
+ resizeable: boolean
116
+ }
117
+ }
118
+ </script>
119
+
120
+ <script lang="ts">
121
+ import { onDestroy, type Snippet } from 'svelte'
122
+ import { getTableState, type TableState } from './Table.svelte'
123
+
124
+ type T = $$Generic<Record<PropertyKey, any>>
125
+ type V = $$Generic
126
+
127
+ let {
128
+ header,
129
+ row,
130
+ statusbar,
131
+ id,
132
+
133
+ sticky = false,
134
+ fixed = false,
135
+ sortby = false,
136
+ show = true,
137
+ width,
138
+
139
+ resizeable = true,
140
+ value,
141
+ sort,
142
+
143
+ filter,
144
+
145
+ table
146
+ }: ColumnProps<T, V> = $props()
147
+
148
+ const column: ColumnState<T, V> = $state({
149
+ id,
150
+ header,
151
+ row,
152
+ statusbar,
153
+ fixed,
154
+ filter,
155
+ defaults: {
156
+ sticky,
157
+ sort: sortby,
158
+ show,
159
+ width
160
+ },
161
+ options: {
162
+ value,
163
+ sorting: sort,
164
+ resizeable
165
+ }
166
+ })
167
+
168
+ table ??= getTableState()
169
+ table.addColumn(id, column as ColumnState)
170
+
171
+ onDestroy(() => {
172
+ table.removeColumn(id)
173
+ })
174
+ </script>
@@ -0,0 +1,121 @@
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
+ *
67
+ * @example (value) => value.includes(search)
68
+ */
69
+ filter?: (value: V) => boolean;
70
+ /**
71
+ * Optional: Provide the table it is a part of
72
+ */
73
+ table?: TableState;
74
+ }
75
+ export interface ColumnState<T = unknown, V = unknown, C extends ColumnProps<T, V> = ColumnProps<T, V>> {
76
+ id: C['id'];
77
+ header?: C['header'];
78
+ row: C['row'];
79
+ statusbar?: C['statusbar'];
80
+ fixed?: boolean;
81
+ filter?: C['filter'];
82
+ /** Default options for initial table */
83
+ defaults: {
84
+ sticky?: boolean;
85
+ sort?: boolean;
86
+ show?: boolean;
87
+ width?: number;
88
+ };
89
+ /** More options */
90
+ options: {
91
+ value?: C['value'];
92
+ sorting?: boolean | ((a: any, b: any) => number);
93
+ resizeable: boolean;
94
+ };
95
+ }
96
+ import { type Snippet } from 'svelte';
97
+ import { type TableState } from './Table.svelte';
98
+ declare class __sveltets_Render<T extends Record<PropertyKey, any>, V> {
99
+ props(): ColumnProps<T, V>;
100
+ events(): {};
101
+ slots(): {};
102
+ bindings(): "";
103
+ exports(): {};
104
+ }
105
+ interface $$IsomorphicComponent {
106
+ 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']>> & {
107
+ $$bindings?: ReturnType<__sveltets_Render<T, V>['bindings']>;
108
+ } & ReturnType<__sveltets_Render<T, V>['exports']>;
109
+ <T extends Record<PropertyKey, any>, V>(internal: unknown, props: ReturnType<__sveltets_Render<T, V>['props']> & {}): ReturnType<__sveltets_Render<T, V>['exports']>;
110
+ z_$$bindings?: ReturnType<__sveltets_Render<any, any>['bindings']>;
111
+ }
112
+ /**
113
+ * This is a description, \
114
+ * on how to use this.
115
+ *
116
+ * @example
117
+ * <Component />
118
+ */
119
+ declare const Column: $$IsomorphicComponent;
120
+ type Column<T extends Record<PropertyKey, any>, V> = InstanceType<typeof Column<T, V>>;
121
+ 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;