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

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,8 +11,9 @@ 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
+ - [x] Virtual elements
16
17
  - [ ] sorting
17
18
  - [ ] select
18
19
  - [ ] filtering
@@ -22,52 +23,48 @@ A high performant dynamic table
22
23
 
23
24
  ### Usage Notes
24
25
 
25
- Simple example.
26
-
27
- Create a state for your data and a state for your active panel:
28
-
29
- ```markdown
26
+ ```html
30
27
  <script lang='ts'>
31
- import { Table } from '$lib/index.js'
28
+ import Table from 'svelte-tably'
32
29
 
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
- ])
37
- </script>
30
+ const data = $state([
31
+ { name: 'John Doe', age: 30, email: 'johndoe@example.com' },
32
+ { name: 'Jane Doe', age: 25, email: 'janedoe@example.com' },
33
+ ])
38
34
 
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}
62
- {/snippet}
63
- </Table.Email>
64
- </Table>
65
- ```
35
+ let activePanel = $state('columns') as string | undefined
36
+ </script>
66
37
 
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.
38
+ <Table {data} panel={activePanel}>
39
+ {#snippet content({ Column, Panel, state, data })}
40
+ <Column id='name' sticky>
41
+ {#snippet header()}
42
+ Name
43
+ {/snippet}
44
+ {#snippet row(row)}
45
+ {row.name}
46
+ {/snippet}
68
47
 
69
- Inside the column component, you need to define three snippets:
48
+ <!-- Optional per column. -->
49
+ {#snippet statusbar()}
50
+ {data.length}
51
+ {/snippet}
52
+ </Column>
53
+ <Column ...>
54
+ ...
55
+ </Column>
56
+ <!-- If you want to sort/filter a virtual value, that does not exist in the data -->
57
+ <Column id='virtual' value={row => row.age > 18}>
58
+ ...
59
+ {#snippet row(row, virtual)}
60
+ {virtual ? 'Adult' : 'Adolescent'}
61
+ {/snippet}
62
+ ...
63
+ </Column>
70
64
 
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
65
+ <Panel id='columns'>
66
+ <!-- Anything you might like -->
67
+ </Panel>
68
+ {/snippet}
69
+ </Table>
70
+ ```
@@ -30,7 +30,7 @@
30
30
 
31
31
  </script>
32
32
 
33
- <script lang='ts' generics='T extends Record<PropertyKey, any>, V'>
33
+ <script lang='ts' generics='T extends Record<PropertyKey, any>, V = unknown'>
34
34
 
35
35
  import { onDestroy, type Snippet } from 'svelte'
36
36
  import { getTableState } from './Table.svelte'
@@ -40,6 +40,8 @@
40
40
  row: Column<T, V>['row']
41
41
  statusbar?: Column<T, V>['statusbar']
42
42
 
43
+ id: string
44
+
43
45
  // options
44
46
  sticky?: boolean
45
47
  sort?: boolean
@@ -49,17 +51,14 @@
49
51
  }
50
52
 
51
53
  let {
52
- header, row, statusbar,
54
+ header, row, statusbar, id,
53
55
 
54
56
  sticky = false,
55
57
  sort = false,
56
58
  show = true,
57
59
 
58
- value, sorting,
59
-
60
- ...rest
60
+ value, sorting
61
61
  }: Props = $props()
62
- const key = (rest as unknown as { __key: string }).__key
63
62
 
64
63
  const column: Column<T, V> = $state({
65
64
  header,
@@ -77,10 +76,10 @@
77
76
  })
78
77
 
79
78
  const table = getTableState()
80
- table.addColumn(key, column as Column)
79
+ table.addColumn(id, column as Column)
81
80
 
82
81
  onDestroy(() => {
83
- table.removeColumn(key)
82
+ table.removeColumn(id)
84
83
  })
85
84
 
86
85
  </script>
@@ -15,11 +15,12 @@ export interface Column<T = unknown, V = unknown> {
15
15
  };
16
16
  }
17
17
  import { type Snippet } from 'svelte';
18
- declare class __sveltets_Render<T extends Record<PropertyKey, any>, V> {
18
+ declare class __sveltets_Render<T extends Record<PropertyKey, any>, V = unknown> {
19
19
  props(): {
20
20
  header: Column<T_1, V_1>["header"];
21
21
  row: Column<T_1, V_1>["row"];
22
22
  statusbar?: Column<T_1, V_1>["statusbar"];
23
+ id: string;
23
24
  sticky?: boolean;
24
25
  sort?: boolean;
25
26
  show?: boolean;
@@ -32,10 +33,10 @@ declare class __sveltets_Render<T extends Record<PropertyKey, any>, V> {
32
33
  exports(): {};
33
34
  }
34
35
  interface $$IsomorphicComponent {
35
- 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']>> & {
36
+ new <T extends Record<PropertyKey, any>, V = unknown>(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']>> & {
36
37
  $$bindings?: ReturnType<__sveltets_Render<T, V>['bindings']>;
37
38
  } & ReturnType<__sveltets_Render<T, V>['exports']>;
38
- <T extends Record<PropertyKey, any>, V>(internal: unknown, props: ReturnType<__sveltets_Render<T, V>['props']> & {}): ReturnType<__sveltets_Render<T, V>['exports']>;
39
+ <T extends Record<PropertyKey, any>, V = unknown>(internal: unknown, props: ReturnType<__sveltets_Render<T, V>['props']> & {}): ReturnType<__sveltets_Render<T, V>['exports']>;
39
40
  z_$$bindings?: ReturnType<__sveltets_Render<any, any>['bindings']>;
40
41
  }
41
42
  /**
@@ -46,5 +47,5 @@ interface $$IsomorphicComponent {
46
47
  * <Component />
47
48
  */
48
49
  declare const Column: $$IsomorphicComponent;
49
- type Column<T extends Record<PropertyKey, any>, V> = InstanceType<typeof Column<T, V>>;
50
+ type Column<T extends Record<PropertyKey, any>, V = unknown> = InstanceType<typeof Column<T, V>>;
50
51
  export default Column;
@@ -46,6 +46,8 @@
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
53
  children: Snippet<[table: TableState]>
@@ -54,20 +56,19 @@
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
+
61
62
  const panel: Panel = $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>
@@ -15,6 +15,7 @@ export declare class PanelTween {
15
15
  import { type Snippet } from 'svelte';
16
16
  import { type TableState } from './Table.svelte';
17
17
  interface Props {
18
+ id: string;
18
19
  /** A darkened backdrop? */
19
20
  backdrop?: boolean;
20
21
  children: Snippet<[table: TableState]>;
@@ -11,8 +11,8 @@
11
11
  <script module lang='ts'>
12
12
 
13
13
  export interface TableState<T extends Record<PropertyKey, any> = Record<PropertyKey, any>> {
14
- columns: Record<string, Column<T, unknown>>
15
- panels: Record<string, Panel>
14
+ columns: Record<string, TColumn<T, unknown>>
15
+ panels: Record<string, TPanel>
16
16
  sortby?: string
17
17
  positions: {
18
18
  sticky: string[]
@@ -21,7 +21,7 @@
21
21
  toggle(key: string): void
22
22
  }
23
23
  readonly data: T[]
24
- addColumn(key: string, options: Column<T, unknown>): void
24
+ addColumn(key: string, options: TColumn<T, unknown>): void
25
25
  removeColumn(key: string): void
26
26
  }
27
27
 
@@ -33,21 +33,23 @@
33
33
 
34
34
  <script lang='ts' generics='T extends Record<PropertyKey, unknown>'>
35
35
 
36
- import { getContext, setContext, type Snippet } from 'svelte'
37
- import { type Column } from './Column.svelte'
38
- import { PanelTween, type Panel } from './Panel.svelte'
36
+ import { getContext, setContext, untrack, type Snippet } from 'svelte'
37
+ import Column, { type Column as TColumn } from './Column.svelte'
38
+ import Panel, { PanelTween, type Panel as TPanel } from './Panel.svelte'
39
39
  import { fly } from 'svelte/transition'
40
40
  import { sineInOut } from 'svelte/easing'
41
41
 
42
42
  interface Props {
43
- children?: Snippet
43
+ content: Snippet<[context: { Column: typeof Column<T>, Panel: typeof Panel, state: TableState<T> }]>
44
+
44
45
  panel?: string
45
46
  data?: T[]
46
47
  id?: string
47
48
  }
48
49
 
49
50
  let {
50
- children,
51
+ content,
52
+
51
53
  panel,
52
54
  data: _data = [],
53
55
  id = Array.from({length: 12}, () => String.fromCharCode(Math.floor(Math.random() * 26) + 97)).join('')
@@ -55,6 +57,48 @@
55
57
 
56
58
  const data = $derived(_data.toSorted())
57
59
 
60
+ const elements = $state({}) as Record<'headers' | 'statusbar' | 'rows', HTMLElement>
61
+
62
+
63
+ // * --- Virtualization --- *
64
+ let scrollTop = $state(0)
65
+ let viewportHeight = $state(0)
66
+
67
+ let _heightPerItem = 24
68
+ let renderItemLength = $derived(Math.ceil(Math.max(30, viewportHeight / (_heightPerItem / 3))))
69
+
70
+ let heightPerItem = $derived.by(() => {
71
+ data
72
+ if(!elements.rows)
73
+ return 8
74
+ const result = elements.rows.scrollHeight / elements.rows.childNodes.length
75
+ _heightPerItem = result
76
+ return result
77
+ })
78
+
79
+ let virtualTop = $derived.by(() => {
80
+ let spacing = untrack(() => (renderItemLength/3)) * heightPerItem
81
+ let scroll = scrollTop - spacing
82
+ let virtualTop = Math.max(scroll, 0)
83
+ virtualTop -= virtualTop % heightPerItem
84
+ return virtualTop
85
+ })
86
+ let virtualBottom = $derived.by(() => {
87
+ const virtualBottom = (heightPerItem * data.length) - virtualTop
88
+ return virtualBottom
89
+ })
90
+
91
+ /** The area of data that is rendered */
92
+ const area = $derived.by(() => {
93
+ const index = (virtualTop / heightPerItem) || 0
94
+ return data.slice(
95
+ index,
96
+ index + untrack(() => renderItemLength)
97
+ )
98
+ })
99
+ // * --- Virtualization --- *
100
+
101
+
58
102
  const table: TableState<T> = $state({
59
103
  columns: {},
60
104
  panels: {},
@@ -99,7 +143,6 @@
99
143
  // * --- *
100
144
 
101
145
  const panelTween = new PanelTween(() => panel)
102
- const elements = $state({}) as Record<'headers' | 'statusbar', HTMLElement>
103
146
 
104
147
  /** Order of columns */
105
148
  const columns = $derived([...table.positions.sticky, ...table.positions.scroll].filter(key => !table.positions.hidden.includes(key)))
@@ -109,7 +152,7 @@
109
152
 
110
153
  /** grid-template-columns for widths */
111
154
  const style = $derived(`
112
- #${id} > .headers, #${id} > .rows > .row, #${id} > .statusbar {
155
+ #${id} > .headers, #${id} > .content > .rows > .row, #${id} > .statusbar, #${id} > .content > .virtual.bottom {
113
156
  grid-template-columns: ${columns.map((key, i, arr) => i === arr.length - 1 ? `minmax(${widths[key] || 150}px, 1fr)` : `${widths[key] || 150}px`).join(' ')};
114
157
  }
115
158
  `)
@@ -119,13 +162,19 @@
119
162
  widths[target.getAttribute('data-column')!] = parseFloat(target.style.width)
120
163
  })
121
164
 
122
- function observe(node: HTMLDivElement, column: string) {
165
+ function observe(node: HTMLDivElement, isHeader = false) {
166
+ if(!isHeader) return
123
167
  observer?.observe(node, {attributes: true})
124
168
  return { destroy: () => observer?.disconnect() }
125
169
  }
126
170
 
127
171
  function onscroll(event: Event) {
128
172
  const target = event.target as HTMLDivElement
173
+ if(target.scrollTop !== scrollTop) {
174
+ scrollTop = target.scrollTop || 0
175
+ }
176
+
177
+ if(!elements.headers) return
129
178
  elements.headers.scrollLeft = target.scrollLeft
130
179
  elements.statusbar.scrollLeft = target.scrollLeft
131
180
  }
@@ -137,69 +186,66 @@
137
186
  {@html `<style>${style}</style>`}
138
187
  </svelte:head>
139
188
 
189
+ {#snippet columnsSnippet(
190
+ renderable: (column: string) => Snippet<[arg0?: any, arg1?: any, arg2?: any, arg3?: any]> | undefined,
191
+ arg: null | ((column: string) => any[]) = null,
192
+ isHeader = false
193
+ )}
194
+ {#each table.positions.sticky as column, i (column)}
195
+ {#if !table.positions.hidden.includes(column)}
196
+ {@const args = arg ? arg(column) : []}
197
+ {@const props = isHeader ? { 'data-column': column } : {}}
198
+ <div class='column sticky' {...props} use:observe={isHeader} class:border={i == table.positions.sticky.length - 1}>
199
+ {@render renderable(column)?.(args[0], args[1], args[2], args[3])}
200
+ </div>
201
+ {/if}
202
+ {/each}
203
+ {#each table.positions.scroll as column, i (column)}
204
+ {#if !table.positions.hidden.includes(column)}
205
+ {@const args = arg ? arg(column) : []}
206
+ {@const props = isHeader ? { 'data-column': column } : {}}
207
+ <div class='column' {...props} use:observe={isHeader}>
208
+ {@render renderable(column)?.(args[0], args[1], args[2], args[3])}
209
+ </div>
210
+ {/if}
211
+ {/each}
212
+ {/snippet}
213
+
140
214
  <div id={id} class='table'>
141
215
 
142
216
  <div class='headers' bind:this={elements.headers}>
143
- {#each table.positions.sticky as column, i (column)}
144
- {#if !table.positions.hidden.includes(column)}
145
- <div class='column sticky' data-column="{column}" use:observe={column}>
146
- {@render table.columns[column]?.header()}
147
- </div>
148
- {/if}
149
- {/each}
150
- {#each table.positions.scroll as column, i (column)}
151
- {#if !table.positions.hidden.includes(column)}
152
- <div class='column' use:observe={column}>
153
- {@render table.columns[column]?.header()}
154
- </div>
155
- {/if}
156
- {/each}
217
+ {@render columnsSnippet((column) => table.columns[column]?.header, null, true)}
157
218
  </div>
158
219
 
159
- <div class="rows" {onscroll}>
160
- {#each data as item}
161
- <div class='row'>
162
- {#each table.positions.sticky as column, i (column)}
163
- {#if !table.positions.hidden.includes(column)}
164
- {@const col = table.columns[column]}
165
- <div class='column sticky' class:border={i == table.positions.sticky.length - 1}>
166
- {@render col.row(item, col.options.value ? col.options.value(item) : undefined)}
167
- </div>
168
- {/if}
169
- {/each}
170
- {#each table.positions.scroll as column, i (column)}
171
- {#if !table.positions.hidden.includes(column)}
172
- {@const col = table.columns[column]}
173
- <div class='column'>
174
- {@render col.row(item, col.options.value ? col.options.value(item) : undefined)}
175
- </div>
176
- {/if}
177
- {/each}
178
- </div>
179
- {/each}
220
+ <div class='content' {onscroll} bind:clientHeight={viewportHeight}>
221
+ <div class='virtual top' style='height: {virtualTop}px'></div>
222
+
223
+ <div class='rows' bind:this={elements.rows}>
224
+ {#each area as item, i (item)}
225
+ <div class='row'>
226
+ {@render columnsSnippet(
227
+ (column) => table.columns[column]!.row,
228
+ (column) => {
229
+ const col = table.columns[column]!
230
+ return [item, col.options.value ? col.options.value(item) : undefined]
231
+ }
232
+ )}
233
+ </div>
234
+ {/each}
235
+ </div>
236
+ <div class='virtual bottom' style='height: {virtualBottom}px'>
237
+ {@render columnsSnippet(() => undefined)}
238
+ </div>
180
239
  </div>
181
240
 
182
241
  <div class='statusbar' bind:this={elements.statusbar}>
183
- {#each table.positions.sticky as column, i (column)}
184
- {#if !table.positions.hidden.includes(column)}
185
- <div class='column sticky' class:border={i == table.positions.sticky.length - 1}>
186
- {@render table.columns[column]?.statusbar?.(data)}
187
- </div>
188
- {/if}
189
- {/each}
190
- {#each table.positions.scroll as column, i (column)}
191
- {#if !table.positions.hidden.includes(column)}
192
- <div class='column'>
193
- {@render table.columns[column]?.statusbar?.(data)}
194
- </div>
195
- {/if}
196
- {/each}
242
+ {@render columnsSnippet((column) => table.columns[column]?.statusbar, () => [data])}
197
243
  </div>
198
244
 
199
- <div class='panel' style='width: {panelTween.current}px;' style:overflow={panelTween.transitioning ? 'hidden' : 'auto'}>
245
+ <div class='panel' style='width: {panelTween.current + 30}px;' style:overflow={panelTween.transitioning ? 'hidden' : 'auto'}>
200
246
  {#if panel && panel in table.panels}
201
247
  <div
202
- class="panel-content"
248
+ class='panel-content'
203
249
  bind:clientWidth={panelTween.width}
204
250
  in:fly={{ x: 100, easing: sineInOut, duration:300 }}
205
251
  out:fly={{ x:100, duration:200, easing: sineInOut }}
@@ -211,7 +257,7 @@
211
257
  </div>
212
258
 
213
259
 
214
- {@render children?.()}
260
+ {@render content?.({ Column, Panel, state: table })}
215
261
 
216
262
 
217
263
 
@@ -226,16 +272,16 @@
226
272
  position: sticky;
227
273
  left: 0px;
228
274
  /* right: 100px; */
229
- background-color: white;
275
+ background-color: var(--tably-bg, hsl(0, 0%, 100%));
230
276
  z-index: 1;
231
277
  }
232
278
 
233
279
  .sticky.border {
234
- border-right: 1px solid hsla(0, 0%, 90%);
280
+ border-right: 1px solid var(--tably-border, hsl(0, 0%, 90%));
235
281
  }
236
282
 
237
283
  .headers > .column {
238
- border-right: 1px solid hsla(0, 0%, 90%);
284
+ border-right: 1px solid var(--tably-border, hsl(0, 0%, 90%));
239
285
  resize: horizontal;
240
286
  overflow: hidden;
241
287
  padding: var(--padding-y) 0;
@@ -249,17 +295,18 @@
249
295
  --header-height: 2.5rem;
250
296
 
251
297
  display: grid;
298
+ height: 100%;
252
299
 
253
300
  grid-template-areas:
254
- "headers panel"
255
- "rows panel"
256
- "statusbar panel"
301
+ 'headers panel'
302
+ 'rows panel'
303
+ 'statusbar panel'
257
304
  ;
258
305
 
259
306
  grid-template-columns: auto min-content;
260
307
  grid-template-rows: auto 1fr auto;
261
308
 
262
- border: 1px solid hsla(0, 0%, 90%);
309
+ border: 1px solid var(--tably-border, hsl(0, 0%, 90%));
263
310
  border-radius: .25rem;
264
311
 
265
312
  max-height: 100%;
@@ -269,32 +316,38 @@
269
316
  grid-area: headers;
270
317
  z-index: 2;
271
318
  overflow: hidden;
272
- padding-right: 1rem;
273
319
  }
274
320
 
275
321
  .headers > .column {
276
322
  width: auto !important;
277
- background-color: hsla(0, 0%, 100%);
278
- border-bottom: 1px solid hsla(0, 0%, 90%);
323
+ background-color: var(--tably-bg, hsl(0, 0%, 100%));
324
+ border-bottom: 1px solid var(--tably-border, hsl(0, 0%, 90%));
279
325
  }
280
326
 
281
- .rows {
327
+ .content {
282
328
  grid-area: rows;
283
329
  display: grid;
284
- overflow: auto;
285
330
  scrollbar-width: thin;
286
- background-color: hsla(0, 0%, 100%);
331
+ overflow: auto;
332
+ height: 100%;
333
+ grid-template-rows: auto auto 1fr;
334
+
335
+ > .rows, > .virtual.bottom {
336
+ display: grid;
337
+ }
338
+ > .virtual.bottom {
339
+ min-height: 100%;
340
+ }
287
341
  }
288
342
 
289
343
  .statusbar {
290
344
  grid-area: statusbar;
291
345
  overflow: hidden;
292
- padding-right: 1rem;
293
346
  }
294
347
 
295
348
  .statusbar > .column {
296
- background-color: hsla(0, 0%, 99%);
297
- border-top: 1px solid hsla(0, 0%, 90%);
349
+ background-color: var(--tably-bg-statusbar, hsl(0, 0%, 99%));
350
+ border-top: 1px solid var(--tably-border, hsl(0, 0%, 90%));
298
351
  padding: calc(var(--padding-y) / 2) 0;
299
352
  }
300
353
 
@@ -315,12 +368,12 @@
315
368
  }
316
369
  }
317
370
 
318
- .row:nth-child(1) > * {
371
+ /* .row:nth-child(1) > * {
319
372
  padding-top: calc(var(--padding-y) + var(--gap));
320
373
  }
321
374
  .row:nth-last-child(1) > * {
322
375
  padding-bottom: calc(var(--padding-y) + var(--gap));
323
- }
376
+ } */
324
377
 
325
378
  .row > * {
326
379
  padding: var(--gap) 0;
@@ -329,19 +382,20 @@
329
382
  .panel {
330
383
  position: relative;
331
384
  grid-area: panel;
332
- width: var(--panel);
333
385
  height: 100%;
334
- background-color: white;
386
+ background-color: var(--tably-bg, hsl(0, 0%, 100%));
335
387
 
336
- border-left: 1px solid hsla(0, 0%, 90%);
388
+ border-left: 1px solid var(--tably-border, hsl(0, 0%, 90%));
389
+ scrollbar-gutter: stable both-edges;
390
+ scrollbar-width: thin;
337
391
 
338
392
  > .panel-content {
339
393
  position: absolute;
340
394
  top: 0;
341
395
  right: 0;
342
396
  width: min-content;
343
- overflow: hidden;
344
- padding: var(--padding-y) var(--padding-x);
397
+ overflow: auto;
398
+ padding: var(--padding-y) 0;
345
399
  }
346
400
  }
347
401
 
@@ -1,6 +1,7 @@
1
+ import { SvelteComponent } from "svelte";
1
2
  export interface TableState<T extends Record<PropertyKey, any> = Record<PropertyKey, any>> {
2
- columns: Record<string, Column<T, unknown>>;
3
- panels: Record<string, Panel>;
3
+ columns: Record<string, TColumn<T, unknown>>;
4
+ panels: Record<string, TPanel>;
4
5
  sortby?: string;
5
6
  positions: {
6
7
  sticky: string[];
@@ -9,16 +10,56 @@ export interface TableState<T extends Record<PropertyKey, any> = Record<Property
9
10
  toggle(key: string): void;
10
11
  };
11
12
  readonly data: T[];
12
- addColumn(key: string, options: Column<T, unknown>): void;
13
+ addColumn(key: string, options: TColumn<T, unknown>): void;
13
14
  removeColumn(key: string): void;
14
15
  }
15
16
  export declare function getTableState<T extends Record<PropertyKey, any> = Record<PropertyKey, any>>(): TableState<T>;
16
17
  import { type Snippet } from 'svelte';
17
- import { type Column } from './Column.svelte';
18
- import { type Panel } from './Panel.svelte';
18
+ import Column, { type Column as TColumn } from './Column.svelte';
19
+ import Panel, { type Panel as TPanel } from './Panel.svelte';
19
20
  declare class __sveltets_Render<T extends Record<PropertyKey, unknown>> {
20
21
  props(): {
21
- children?: Snippet;
22
+ content: Snippet<[context: {
23
+ Column: {
24
+ (internal: unknown, props: {
25
+ header: Column<T_1, V>["header"];
26
+ row: Column<T_1, V>["row"];
27
+ statusbar?: Column<T_1, V>["statusbar"];
28
+ id: string;
29
+ sticky?: boolean;
30
+ sort?: boolean;
31
+ show?: boolean;
32
+ value?: Column<T_1, V>["options"]["value"];
33
+ sorting?: Column<T_1, V>["options"]["sorting"];
34
+ }): {};
35
+ new (options: import("svelte").ComponentConstructorOptions<{
36
+ header: Column<T_1, V>["header"];
37
+ row: Column<T_1, V>["row"];
38
+ statusbar?: Column<T_1, V>["statusbar"];
39
+ id: string;
40
+ sticky?: boolean;
41
+ sort?: boolean;
42
+ show?: boolean;
43
+ value?: Column<T_1, V>["options"]["value"];
44
+ sorting?: Column<T_1, V>["options"]["sorting"];
45
+ }>): SvelteComponent<{
46
+ header: Column<T_1, V>["header"];
47
+ row: Column<T_1, V>["row"];
48
+ statusbar?: Column<T_1, V>["statusbar"];
49
+ id: string;
50
+ sticky?: boolean;
51
+ sort?: boolean;
52
+ show?: boolean;
53
+ value?: Column<T_1, V>["options"]["value"];
54
+ sorting?: Column<T_1, V>["options"]["sorting"];
55
+ }, {}, {}> & {
56
+ $$bindings?: ReturnType<() => "">;
57
+ };
58
+ z_$$bindings?: ReturnType<() => "">;
59
+ };
60
+ Panel: typeof Panel;
61
+ state: TableState<T>;
62
+ }]>;
22
63
  panel?: string;
23
64
  data?: T[] | undefined;
24
65
  id?: string;
@@ -1,12 +1 @@
1
- import { default as _Table } from './Table.svelte';
2
- import Column from './Column.svelte';
3
- import { default as _Panel } from './Panel.svelte';
4
- declare const LATIN: readonly ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"];
5
- type Capital = typeof LATIN[number];
6
- declare const Table: typeof _Table & {
7
- [key: `${Capital}${string}`]: typeof Column;
8
- };
9
- declare const Panel: {
10
- [key: `${Capital}${string}`]: typeof _Panel;
11
- };
12
- export { Table, Panel };
1
+ export { default as Table } from './Table.svelte';
@@ -1,31 +1 @@
1
- import { default as _Table } from './Table.svelte';
2
- import Column from './Column.svelte';
3
- import { default as _Panel } from './Panel.svelte';
4
- const LATIN = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'];
5
- const Table = new Proxy(_Table, {
6
- get(target, p, receiver) {
7
- if (typeof p !== 'string' || !LATIN.includes(p[0])) {
8
- return Reflect.get(target, p, receiver);
9
- }
10
- return new Proxy(Column, {
11
- apply(_, __, [anchor, props]) {
12
- Object.assign(props, { __key: p });
13
- return Column(anchor, props);
14
- },
15
- });
16
- }
17
- });
18
- const Panel = new Proxy(_Panel, {
19
- get(target, p, receiver) {
20
- if (typeof p !== 'string' || !LATIN.includes(p[0])) {
21
- return Reflect.get(target, p, receiver);
22
- }
23
- return new Proxy(_Panel, {
24
- apply(_, __, [anchor, props]) {
25
- Object.assign(props, { __key: p });
26
- return _Panel(anchor, props);
27
- },
28
- });
29
- },
30
- });
31
- export { Table, Panel };
1
+ export { default as Table } from './Table.svelte';
package/dist/index.d.ts CHANGED
@@ -1 +1,2 @@
1
- export * from './Table/index.js';
1
+ import { Table } from './Table/index.js';
2
+ export default Table;
package/dist/index.js CHANGED
@@ -1 +1,2 @@
1
- export * from './Table/index.js';
1
+ import { Table } from './Table/index.js';
2
+ export default Table;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "svelte-tably",
3
- "version": "1.0.0-next.1",
3
+ "version": "1.0.0-next.4",
4
4
  "repository": "github:refzlund/svelte-tably",
5
5
  "homepage": "https://github.com/Refzlund/svelte-tably",
6
6
  "bugs": {