svelte-tably 1.0.0-next.3 → 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
@@ -23,52 +23,48 @@ A high performant dynamic table
23
23
 
24
24
  ### Usage Notes
25
25
 
26
- Simple example.
27
-
28
- Create a state for your data and a state for your active panel:
29
-
30
- ```markdown
26
+ ```html
31
27
  <script lang='ts'>
32
- import { Table } from '$lib/index.js'
28
+ import Table from 'svelte-tably'
33
29
 
34
- const data = $state([
35
- { name: 'John Doe', age: 30, email: 'johndoe@example.com' },
36
- { name: 'Jane Doe', age: 25, email: 'janedoe@example.com' },
37
- ])
38
- </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
+ ])
39
34
 
40
- <Table {data}>
41
- <Table.Name>
42
- {#snippet header()}
43
- Name
44
- {/snippet}
45
- {#snippet row(item)}
46
- {item.name}
47
- {/snippet}
48
- </Table.Name>
49
- <Table.Age>
50
- {#snippet header()}
51
- Age
52
- {/snippet}
53
- {#snippet row(item)}
54
- {item.age}
55
- {/snippet}
56
- </Table.Age>
57
- <Table.Email>
58
- {#snippet header()}
59
- Email
60
- {/snippet}
61
- {#snippet row(item)}
62
- {item.email}
63
- {/snippet}
64
- </Table.Email>
65
- </Table>
66
- ```
35
+ let activePanel = $state('columns') as string | undefined
36
+ </script>
67
37
 
68
- 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}
69
47
 
70
- 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>
71
64
 
72
- * `header`: the content of the column header
73
- * `row`: the content of each row in the column
74
- * `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
 
@@ -34,20 +34,22 @@
34
34
  <script lang='ts' generics='T extends Record<PropertyKey, unknown>'>
35
35
 
36
36
  import { getContext, setContext, untrack, type Snippet } from 'svelte'
37
- import { type Column } from './Column.svelte'
38
- import { PanelTween, type Panel } from './Panel.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,7 +57,7 @@
55
57
 
56
58
  const data = $derived(_data.toSorted())
57
59
 
58
- const elements = $state({}) as Record<'headers' | 'statusbar' | 'content', HTMLElement>
60
+ const elements = $state({}) as Record<'headers' | 'statusbar' | 'rows', HTMLElement>
59
61
 
60
62
 
61
63
  // * --- Virtualization --- *
@@ -66,12 +68,10 @@
66
68
  let renderItemLength = $derived(Math.ceil(Math.max(30, viewportHeight / (_heightPerItem / 3))))
67
69
 
68
70
  let heightPerItem = $derived.by(() => {
69
- if(!elements.content)
71
+ data
72
+ if(!elements.rows)
70
73
  return 8
71
- const rows = elements.content.querySelectorAll('.row') as NodeListOf<HTMLDivElement>
72
- const result = ((
73
- rows[rows.length - 1].offsetTop - rows[0].offsetTop
74
- ) / rows.length)
74
+ const result = elements.rows.scrollHeight / elements.rows.childNodes.length
75
75
  _heightPerItem = result
76
76
  return result
77
77
  })
@@ -80,17 +80,17 @@
80
80
  let spacing = untrack(() => (renderItemLength/3)) * heightPerItem
81
81
  let scroll = scrollTop - spacing
82
82
  let virtualTop = Math.max(scroll, 0)
83
- virtualTop -= virtualTop % untrack(() => heightPerItem)
83
+ virtualTop -= virtualTop % heightPerItem
84
84
  return virtualTop
85
85
  })
86
86
  let virtualBottom = $derived.by(() => {
87
- const virtualBottom = (untrack(() => heightPerItem) * data.length) - virtualTop
87
+ const virtualBottom = (heightPerItem * data.length) - virtualTop
88
88
  return virtualBottom
89
89
  })
90
90
 
91
91
  /** The area of data that is rendered */
92
92
  const area = $derived.by(() => {
93
- const index = (virtualTop / untrack(() => heightPerItem)) || 0
93
+ const index = (virtualTop / heightPerItem) || 0
94
94
  return data.slice(
95
95
  index,
96
96
  index + untrack(() => renderItemLength)
@@ -217,10 +217,10 @@
217
217
  {@render columnsSnippet((column) => table.columns[column]?.header, null, true)}
218
218
  </div>
219
219
 
220
- <div class='content' {onscroll} bind:clientHeight={viewportHeight} bind:this={elements.content}>
220
+ <div class='content' {onscroll} bind:clientHeight={viewportHeight}>
221
221
  <div class='virtual top' style='height: {virtualTop}px'></div>
222
222
 
223
- <div class="rows">
223
+ <div class='rows' bind:this={elements.rows}>
224
224
  {#each area as item, i (item)}
225
225
  <div class='row'>
226
226
  {@render columnsSnippet(
@@ -245,7 +245,7 @@
245
245
  <div class='panel' style='width: {panelTween.current + 30}px;' style:overflow={panelTween.transitioning ? 'hidden' : 'auto'}>
246
246
  {#if panel && panel in table.panels}
247
247
  <div
248
- class="panel-content"
248
+ class='panel-content'
249
249
  bind:clientWidth={panelTween.width}
250
250
  in:fly={{ x: 100, easing: sineInOut, duration:300 }}
251
251
  out:fly={{ x:100, duration:200, easing: sineInOut }}
@@ -257,7 +257,7 @@
257
257
  </div>
258
258
 
259
259
 
260
- {@render children?.()}
260
+ {@render content?.({ Column, Panel, state: table })}
261
261
 
262
262
 
263
263
 
@@ -298,9 +298,9 @@
298
298
  height: 100%;
299
299
 
300
300
  grid-template-areas:
301
- "headers panel"
302
- "rows panel"
303
- "statusbar panel"
301
+ 'headers panel'
302
+ 'rows panel'
303
+ 'statusbar panel'
304
304
  ;
305
305
 
306
306
  grid-template-columns: auto min-content;
@@ -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.3",
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": {