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

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
@@ -32,39 +32,59 @@ A high performant dynamic table
32
32
  { name: 'Jane Doe', age: 25, email: 'janedoe@example.com' },
33
33
  ])
34
34
 
35
- let activePanel = $state('columns') as string | undefined
35
+ let activePanel = $state('columns') as string | undefined
36
36
  </script>
37
37
 
38
38
  <Table {data} panel={activePanel}>
39
39
  {#snippet content({ Column, Panel, state, data })}
40
40
  <Column id='name' sticky>
41
- {#snippet header()}
42
- Name
43
- {/snippet}
44
- {#snippet row(row)}
45
- {row.name}
46
- {/snippet}
47
-
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>
64
-
65
- <Panel id='columns'>
66
- <!-- Anything you might like -->
67
- </Panel>
41
+ {#snippet header()}
42
+ Name
43
+ {/snippet}
44
+ {#snippet row(row)}
45
+ {row.name}
46
+ {/snippet}
47
+
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>
64
+
65
+ <Panel id='columns'>
66
+ <!-- Anything you might like -->
67
+ </Panel>
68
+ <Panel ...>
69
+ ...
70
+ </Panel>
68
71
  {/snippet}
69
72
  </Table>
70
- ```
73
+ ```
74
+
75
+ #### Styling
76
+
77
+ For quick styling
78
+
79
+ | CSS Variable | Description | Default |
80
+ | - | - | - |
81
+ | --tably-bg | background-color | `hsl(0, 0%, 100%)` |
82
+ | --tably-color | color | `hsl(0, 0%, 0%)` |
83
+ | --tably-border | border | `hsl(0, 0%, 90%)` |
84
+ | --tably-padding-y | Padding above/below each column | `.5rem` |
85
+ | --tably-padding-x | Padding left of each column | `1rem` |
86
+ | --tably-radius | Table radius | `.25rem` |
87
+
88
+ Advanced styling can be done via `:global .svelte-tably`
89
+
90
+
@@ -13,7 +13,7 @@
13
13
  export interface Column<T = unknown, V = unknown> {
14
14
  header: Snippet
15
15
  row: Snippet<[item: T, value?: V]>
16
- statusbar?: Snippet<[data: T[]]>
16
+ statusbar?: Snippet
17
17
 
18
18
  /** Default options for initial table */
19
19
  defaults: {
@@ -1,7 +1,7 @@
1
1
  export interface Column<T = unknown, V = unknown> {
2
2
  header: Snippet;
3
3
  row: Snippet<[item: T, value?: V]>;
4
- statusbar?: Snippet<[data: T[]]>;
4
+ statusbar?: Snippet;
5
5
  /** Default options for initial table */
6
6
  defaults: {
7
7
  sticky?: boolean;
@@ -13,7 +13,7 @@
13
13
  export interface Panel {
14
14
  /** A darkened backdrop? */
15
15
  backdrop: boolean
16
- content: Snippet<[table: TableState]>
16
+ content: Snippet
17
17
  }
18
18
 
19
19
  export class PanelTween {
@@ -29,9 +29,9 @@
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
  }
@@ -50,7 +50,7 @@
50
50
 
51
51
  /** A darkened backdrop? */
52
52
  backdrop?: boolean
53
- children: Snippet<[table: TableState]>
53
+ children: Snippet
54
54
  }
55
55
 
56
56
  let {
@@ -1,7 +1,7 @@
1
1
  export interface Panel {
2
2
  /** A darkened backdrop? */
3
3
  backdrop: boolean;
4
- content: Snippet<[table: TableState]>;
4
+ content: Snippet;
5
5
  }
6
6
  export declare class PanelTween {
7
7
  #private;
@@ -10,15 +10,14 @@ export declare class PanelTween {
10
10
  /** bind:clientWidth */
11
11
  width: number;
12
12
  set target(value: number);
13
- constructor(cb: () => string | undefined);
13
+ constructor(cb: () => string | undefined, added?: number);
14
14
  }
15
15
  import { type Snippet } from 'svelte';
16
- import { type TableState } from './Table.svelte';
17
16
  interface Props {
18
17
  id: string;
19
18
  /** A darkened backdrop? */
20
19
  backdrop?: boolean;
21
- children: Snippet<[table: TableState]>;
20
+ children: Snippet;
22
21
  }
23
22
  /**
24
23
  * This is a description, \
@@ -40,7 +40,7 @@
40
40
  import { sineInOut } from 'svelte/easing'
41
41
 
42
42
  interface Props {
43
- content: Snippet<[context: { Column: typeof Column<T>, Panel: typeof Panel, state: TableState<T> }]>
43
+ content: Snippet<[context: { Column: typeof Column<T>, Panel: typeof Panel, readonly state: TableState<T>, readonly data: T[] }]>
44
44
 
45
45
  panel?: string
46
46
  data?: T[]
@@ -50,7 +50,7 @@
50
50
  let {
51
51
  content,
52
52
 
53
- panel,
53
+ panel = $bindable(),
54
54
  data: _data = [],
55
55
  id = Array.from({length: 12}, () => String.fromCharCode(Math.floor(Math.random() * 26) + 97)).join('')
56
56
  }: Props = $props()
@@ -142,7 +142,7 @@
142
142
 
143
143
  // * --- *
144
144
 
145
- const panelTween = new PanelTween(() => panel)
145
+ const panelTween = new PanelTween(() => panel, 24)
146
146
 
147
147
  /** Order of columns */
148
148
  const columns = $derived([...table.positions.sticky, ...table.positions.scroll].filter(key => !table.positions.hidden.includes(key)))
@@ -187,7 +187,7 @@
187
187
  </svelte:head>
188
188
 
189
189
  {#snippet columnsSnippet(
190
- renderable: (column: string) => Snippet<[arg0?: any, arg1?: any, arg2?: any, arg3?: any]> | undefined,
190
+ renderable: (column: string) => Snippet<[arg0?: any, arg1?: any]> | undefined,
191
191
  arg: null | ((column: string) => any[]) = null,
192
192
  isHeader = false
193
193
  )}
@@ -196,7 +196,7 @@
196
196
  {@const args = arg ? arg(column) : []}
197
197
  {@const props = isHeader ? { 'data-column': column } : {}}
198
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])}
199
+ {@render renderable(column)?.(args[0], args[1])}
200
200
  </div>
201
201
  {/if}
202
202
  {/each}
@@ -205,13 +205,13 @@
205
205
  {@const args = arg ? arg(column) : []}
206
206
  {@const props = isHeader ? { 'data-column': column } : {}}
207
207
  <div class='column' {...props} use:observe={isHeader}>
208
- {@render renderable(column)?.(args[0], args[1], args[2], args[3])}
208
+ {@render renderable(column)?.(args[0], args[1])}
209
209
  </div>
210
210
  {/if}
211
211
  {/each}
212
212
  {/snippet}
213
213
 
214
- <div id={id} class='table'>
214
+ <div id={id} class='table svelte-tably'>
215
215
 
216
216
  <div class='headers' bind:this={elements.headers}>
217
217
  {@render columnsSnippet((column) => table.columns[column]?.header, null, true)}
@@ -239,10 +239,10 @@
239
239
  </div>
240
240
 
241
241
  <div class='statusbar' bind:this={elements.statusbar}>
242
- {@render columnsSnippet((column) => table.columns[column]?.statusbar, () => [data])}
242
+ {@render columnsSnippet((column) => table.columns[column]?.statusbar)}
243
243
  </div>
244
244
 
245
- <div class='panel' style='width: {panelTween.current + 30}px;' style:overflow={panelTween.transitioning ? 'hidden' : 'auto'}>
245
+ <div class='panel' style='width: {(panelTween.current)}px;' style:overflow={panelTween.transitioning ? 'hidden' : 'auto'}>
246
246
  {#if panel && panel in table.panels}
247
247
  <div
248
248
  class='panel-content'
@@ -250,14 +250,21 @@
250
250
  in:fly={{ x: 100, easing: sineInOut, duration:300 }}
251
251
  out:fly={{ x:100, duration:200, easing: sineInOut }}
252
252
  >
253
- {@render table.panels[panel].content(table as TableState)}
253
+ {@render table.panels[panel].content()}
254
254
  </div>
255
255
  {/if}
256
256
  </div>
257
+ <button
258
+ class='backdrop'
259
+ aria-label='Panel backdrop'
260
+ tabindex='-1'
261
+ aria-hidden={panel && table.panels[panel]?.backdrop ? false : true}
262
+ onclick={() => panel = undefined}
263
+ ></button>
257
264
  </div>
258
265
 
259
266
 
260
- {@render content?.({ Column, Panel, state: table })}
267
+ {@render content?.({ Column, Panel, get state() { return table }, get data() { return data } })}
261
268
 
262
269
 
263
270
 
@@ -266,13 +273,47 @@
266
273
 
267
274
  .table, .table * {
268
275
  box-sizing: border-box;
276
+ background-color: inherit;
277
+ }
278
+
279
+ .backdrop {
280
+ position: absolute;
281
+ left: 0px;
282
+ top: 0px;
283
+ bottom: 0px;
284
+ right: 0px;
285
+ background-color: hsla(0, 0%, 0%, .3);
286
+ z-index: 3;
287
+ opacity: 1;
288
+ transition: .15s ease;
289
+ border: none;
290
+ outline: none;
291
+ cursor: pointer;
292
+
293
+ &[aria-hidden='true'] {
294
+ opacity: 0;
295
+ pointer-events: none;
296
+ }
297
+ }
298
+
299
+ .headers, .statusbar {
300
+ /* So that the scrollbar doesn't cause the headers/statusbar to shift */
301
+ padding-right: 11px;
302
+ }
303
+
304
+ .table {
305
+ color: var(--tably-color, hsl(0, 0%, 0%));
306
+ background-color: var(--tably-bg, hsl(0, 0%, 100%));
307
+
308
+ --tably-padding-x: 1rem;
309
+ --tably-padding-y: .5rem;
310
+ --tably-radius: .25rem;
269
311
  }
270
312
 
271
313
  .sticky {
272
314
  position: sticky;
273
315
  left: 0px;
274
316
  /* right: 100px; */
275
- background-color: var(--tably-bg, hsl(0, 0%, 100%));
276
317
  z-index: 1;
277
318
  }
278
319
 
@@ -284,18 +325,13 @@
284
325
  border-right: 1px solid var(--tably-border, hsl(0, 0%, 90%));
285
326
  resize: horizontal;
286
327
  overflow: hidden;
287
- padding: var(--padding-y) 0;
328
+ padding: var(--tably-padding-y) 0;
288
329
  }
289
330
 
290
331
  .table {
291
- --panel: 250px;
292
- --padding-x: 1rem;
293
- --padding-y: .5rem;
294
- --gap: .25rem;
295
- --header-height: 2.5rem;
296
-
297
332
  display: grid;
298
333
  height: 100%;
334
+ position: relative;
299
335
 
300
336
  grid-template-areas:
301
337
  'headers panel'
@@ -307,7 +343,7 @@
307
343
  grid-template-rows: auto 1fr auto;
308
344
 
309
345
  border: 1px solid var(--tably-border, hsl(0, 0%, 90%));
310
- border-radius: .25rem;
346
+ border-radius: var(--tably-radius, .25rem);
311
347
 
312
348
  max-height: 100%;
313
349
  }
@@ -320,7 +356,6 @@
320
356
 
321
357
  .headers > .column {
322
358
  width: auto !important;
323
- background-color: var(--tably-bg, hsl(0, 0%, 100%));
324
359
  border-bottom: 1px solid var(--tably-border, hsl(0, 0%, 90%));
325
360
  }
326
361
 
@@ -346,9 +381,8 @@
346
381
  }
347
382
 
348
383
  .statusbar > .column {
349
- background-color: var(--tably-bg-statusbar, hsl(0, 0%, 99%));
350
384
  border-top: 1px solid var(--tably-border, hsl(0, 0%, 90%));
351
- padding: calc(var(--padding-y) / 2) 0;
385
+ padding: calc(var(--tably-padding-y) / 2) 0;
352
386
  }
353
387
 
354
388
  .headers, .row, .statusbar {
@@ -358,36 +392,36 @@
358
392
 
359
393
  & > .column {
360
394
  display: flex;
361
- padding-left: var(--padding-x);
395
+ padding-left: var(--tably-padding-x);
362
396
  overflow: hidden;
363
397
  }
364
398
 
365
399
  & > *:last-child {
366
400
  width: 100%;
367
- padding-right: var(--padding-x);
401
+ padding-right: var(--tably-padding-x);
368
402
  }
369
403
  }
370
404
 
371
- /* .row:nth-child(1) > * {
372
- padding-top: calc(var(--padding-y) + var(--gap));
405
+ .row:first-child > * {
406
+ padding-top: calc(var(--tably-padding-y) + calc(var(--tably-padding-y) / 2));
407
+ }
408
+ .row:last-child > * {
409
+ padding-bottom: calc(var(--tably-padding-y) + calc(var(--tably-padding-y) / 2));
373
410
  }
374
- .row:nth-last-child(1) > * {
375
- padding-bottom: calc(var(--padding-y) + var(--gap));
376
- } */
377
411
 
378
412
  .row > * {
379
- padding: var(--gap) 0;
413
+ padding: calc(var(--tably-padding-y) / 2) 0;
380
414
  }
381
415
 
382
416
  .panel {
383
417
  position: relative;
384
418
  grid-area: panel;
385
419
  height: 100%;
386
- background-color: var(--tably-bg, hsl(0, 0%, 100%));
387
420
 
388
421
  border-left: 1px solid var(--tably-border, hsl(0, 0%, 90%));
389
422
  scrollbar-gutter: stable both-edges;
390
423
  scrollbar-width: thin;
424
+ z-index: 4;
391
425
 
392
426
  > .panel-content {
393
427
  position: absolute;
@@ -395,7 +429,7 @@
395
429
  right: 0;
396
430
  width: min-content;
397
431
  overflow: auto;
398
- padding: var(--padding-y) 0;
432
+ padding: var(--tably-padding-y) 0;
399
433
  }
400
434
  }
401
435
 
@@ -58,7 +58,8 @@ declare class __sveltets_Render<T extends Record<PropertyKey, unknown>> {
58
58
  z_$$bindings?: ReturnType<() => "">;
59
59
  };
60
60
  Panel: typeof Panel;
61
- state: TableState<T>;
61
+ readonly state: TableState<T>;
62
+ readonly data: T[];
62
63
  }]>;
63
64
  panel?: string;
64
65
  data?: T[] | undefined;
@@ -66,7 +67,7 @@ declare class __sveltets_Render<T extends Record<PropertyKey, unknown>> {
66
67
  };
67
68
  events(): {};
68
69
  slots(): {};
69
- bindings(): "";
70
+ bindings(): "panel";
70
71
  exports(): {};
71
72
  }
72
73
  interface $$IsomorphicComponent {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "svelte-tably",
3
- "version": "1.0.0-next.4",
3
+ "version": "1.0.0-next.5",
4
4
  "repository": "github:refzlund/svelte-tably",
5
5
  "homepage": "https://github.com/Refzlund/svelte-tably",
6
6
  "bugs": {