svelte-tably 1.0.0-next.15 → 1.0.0-next.16

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
@@ -18,9 +18,10 @@ A high performant, feature rich, dynamic table
18
18
  - [x] select
19
19
  - [x] filtering
20
20
  - [x] reorderable table
21
- - [ ] row context-menu
21
+ - [x] Row context menus
22
22
  - [x] Expandable rows
23
23
  - [x] to CSV
24
+ - [ ] Auto: Table based on data, sortable
24
25
 
25
26
  ### Usage Notes
26
27
 
@@ -38,8 +39,8 @@ A high performant, feature rich, dynamic table
38
39
  </script>
39
40
 
40
41
  <Table {data} panel={activePanel} select bind:selected>
41
- {#snippet content({ Column, Panel, state, data })}
42
- <Column id='name' sticky>
42
+ {#snippet content({ Column, Panel, Expandable, Row, state, data })}
43
+ <Column id='name' sticky sort>
43
44
  {#snippet header()}
44
45
  Name
45
46
  {/snippet}
@@ -52,9 +53,10 @@ A high performant, feature rich, dynamic table
52
53
  {data.length}
53
54
  {/snippet}
54
55
  </Column>
55
- <Column ...>
56
- ...
57
- </Column>
56
+
57
+ <!-- Simplified -->
58
+ <Column id='age' header='Age' value={r => r.age} sort={(a,b) => a - b} />
59
+
58
60
  <!-- If you want to sort/filter a virtual value, that does not exist in the data -->
59
61
  <Column id='virtual' value={row => row.age > 18}>
60
62
  ...
@@ -64,6 +66,21 @@ A high performant, feature rich, dynamic table
64
66
  ...
65
67
  </Column>
66
68
 
69
+ <Expandable click={false}>
70
+ {#snippet content(item, ctx)}
71
+ ...
72
+ {/snippet}
73
+ </Expandable>
74
+
75
+ <Row onclick={...} oncontextmenu={...}>
76
+ {#snippet contextHeader()}
77
+ ...
78
+ {/snippet}
79
+ {#snippet context(item, ctx)}
80
+ ...
81
+ {/snippet}
82
+ </Row>
83
+
67
84
  <Panel id='columns'>
68
85
  <!-- Anything you might like -->
69
86
  </Panel>
@@ -10,8 +10,9 @@
10
10
 
11
11
  <script lang="ts">
12
12
 
13
- import { fromProps } from '../utility.svelte.js'
14
- import { ColumnState, type ColumnProps } from './column.svelte.js'
13
+ import { fromProps, type AnyRecord } from '../utility.svelte.js'
14
+ import type { Snippet } from 'svelte'
15
+ import { ColumnState, type ColumnProps, type HeaderCtx, type ColumnSnippets } from './column.svelte.js'
15
16
 
16
17
  type T = $$Generic<Record<PropertyKey, any>>
17
18
  type V = $$Generic
@@ -21,4 +22,20 @@
21
22
 
22
23
  new ColumnState<T, V>(properties)
23
24
 
24
- </script>
25
+ </script>
26
+
27
+ <script module lang='ts'>
28
+
29
+ declare const defaultHeader: <T extends AnyRecord>(anchor: unknown, title: () => string, ctx: HeaderCtx<T>) => ReturnType<Snippet>
30
+
31
+ export function getDefaultHeader<T extends AnyRecord,V>(title: string) {
32
+ return (
33
+ (anchor: unknown, ctx: HeaderCtx<T>) => defaultHeader<T>(anchor, () => title, ctx)
34
+ ) as Exclude<ColumnSnippets<T, V>['header'], string>
35
+ }
36
+
37
+ </script>
38
+
39
+ {#snippet defaultHeader(title: string, ctx: HeaderCtx<T>)}
40
+ {title}
41
+ {/snippet}
@@ -1,3 +1,6 @@
1
+ export declare function getDefaultHeader<T extends AnyRecord, V>(title: string): Exclude<ColumnSnippets<T, V>["header"], string>;
2
+ import { type AnyRecord } from '../utility.svelte.js';
3
+ import { type ColumnSnippets } from './column.svelte.js';
1
4
  declare class __sveltets_Render<T extends Record<PropertyKey, any>, V> {
2
5
  props(): ColumnProps<T_1, V_1>;
3
6
  events(): {};
@@ -1,6 +1,7 @@
1
1
  import {} from 'svelte';
2
2
  import { TableState } from '../table/table.svelte.js';
3
3
  import { assign, pick } from '../utility.svelte.js';
4
+ import { getDefaultHeader } from './Column.svelte';
4
5
  export class ColumnState {
5
6
  #props = {};
6
7
  id = $derived(this.#props.id);
@@ -9,11 +10,11 @@ export class ColumnState {
9
10
  */
10
11
  table;
11
12
  snippets = $derived({
12
- header: this.#props.header,
13
+ header: typeof this.#props.header === 'string' ? getDefaultHeader(this.#props.header) : this.#props.header,
13
14
  /** Title is the header-snippet, with header-ctx: `{ header: false }` */
14
15
  title: (...args) => {
15
16
  const getData = () => this.table.data.current;
16
- return this.#props.header?.(...[args[0], () => ({
17
+ return this.snippets.header?.(...[args[0], () => ({
17
18
  get header() { return false; },
18
19
  get data() {
19
20
  return getData();
@@ -8,30 +8,6 @@
8
8
 
9
9
  -->
10
10
 
11
- <script module lang='ts'>
12
-
13
- export class PanelTween {
14
- #tween = new Tween(0, { duration: 300, easing: sineInOut })
15
- current = $derived(this.#tween.current)
16
- transitioning = $state(false)
17
-
18
- /** bind:clientWidth */
19
- width = $state(0)
20
-
21
- set target(value: number) {
22
- this.transitioning = true
23
- this.#tween.set(value).then(() => this.transitioning = false)
24
- }
25
-
26
- constructor(cb: () => string | undefined, added = 0) {
27
- $effect.pre(() => {
28
- this.target = cb() ? this.width + added : 0
29
- })
30
- }
31
- }
32
-
33
- </script>
34
-
35
11
  <script lang='ts' generics='T extends Record<PropertyKey, unknown>'>
36
12
 
37
13
  import { Tween } from 'svelte/motion'
@@ -1,12 +1,3 @@
1
- export declare class PanelTween {
2
- #private;
3
- current: number;
4
- transitioning: boolean;
5
- /** bind:clientWidth */
6
- width: number;
7
- set target(value: number);
8
- constructor(cb: () => string | undefined, added?: number);
9
- }
10
1
  declare class __sveltets_Render<T extends Record<PropertyKey, unknown>> {
11
2
  props(): PanelProps<T_1>;
12
3
  events(): {};
@@ -0,0 +1,24 @@
1
+ <!-- @component
2
+
3
+ This is a description, \
4
+ on how to use this.
5
+
6
+ @example
7
+ <Component />
8
+
9
+ -->
10
+
11
+ <script lang='ts'>
12
+
13
+ import { RowState, type RowProps } from './row.svelte.js'
14
+ import type { AnyRecord } from '../utility.svelte.js'
15
+ import { fromProps } from '../utility.svelte.js'
16
+
17
+ type T = $$Generic<AnyRecord>
18
+
19
+ let { ...restProps }: RowProps<T> = $props()
20
+
21
+ const properties = fromProps(restProps)
22
+ new RowState<T>(properties)
23
+
24
+ </script>
@@ -0,0 +1,25 @@
1
+ import type { AnyRecord } from '../utility.svelte.js';
2
+ declare class __sveltets_Render<T extends AnyRecord> {
3
+ props(): RowProps<T_1>;
4
+ events(): {};
5
+ slots(): {};
6
+ bindings(): "";
7
+ exports(): {};
8
+ }
9
+ interface $$IsomorphicComponent {
10
+ new <T extends AnyRecord>(options: import('svelte').ComponentConstructorOptions<ReturnType<__sveltets_Render<T>['props']>>): import('svelte').SvelteComponent<ReturnType<__sveltets_Render<T>['props']>, ReturnType<__sveltets_Render<T>['events']>, ReturnType<__sveltets_Render<T>['slots']>> & {
11
+ $$bindings?: ReturnType<__sveltets_Render<T>['bindings']>;
12
+ } & ReturnType<__sveltets_Render<T>['exports']>;
13
+ <T extends AnyRecord>(internal: unknown, props: ReturnType<__sveltets_Render<T>['props']> & {}): ReturnType<__sveltets_Render<T>['exports']>;
14
+ z_$$bindings?: ReturnType<__sveltets_Render<any>['bindings']>;
15
+ }
16
+ /**
17
+ * This is a description, \
18
+ * on how to use this.
19
+ *
20
+ * @example
21
+ * <Component />
22
+ */
23
+ declare const Row: $$IsomorphicComponent;
24
+ type Row<T extends AnyRecord> = InstanceType<typeof Row<T>>;
25
+ export default Row;
@@ -0,0 +1,28 @@
1
+ import { TableState } from '../table/table.svelte.js';
2
+ export class RowState {
3
+ #table;
4
+ #props = {};
5
+ snippets = $derived({
6
+ context: this.#props.context,
7
+ contextHeader: this.#props.contextHeader
8
+ });
9
+ events = $derived({
10
+ onclick: this.#props.onclick,
11
+ oncontextmenu: this.#props.oncontextmenu
12
+ });
13
+ options = $derived({
14
+ context: {
15
+ hover: this.#props.contextOptions?.hover ?? true,
16
+ width: this.#props.contextOptions?.width ?? 'max-content'
17
+ }
18
+ });
19
+ constructor(props) {
20
+ this.#props = props;
21
+ this.#table = TableState.getContext();
22
+ if (!this.#table) {
23
+ throw new Error('svelte-tably: Expandable must be associated with a Table');
24
+ }
25
+ this.#table.row = this;
26
+ $effect(() => () => this.#table.row === this && (this.#table.row = undefined));
27
+ }
28
+ }
@@ -13,13 +13,13 @@
13
13
  </script>
14
14
 
15
15
  <script lang="ts">
16
- import { tick, untrack, type Snippet } from 'svelte'
16
+ import { type Snippet } from 'svelte'
17
17
  import { fly } from 'svelte/transition'
18
18
  import { sineInOut } from 'svelte/easing'
19
19
  import reorder, { type ItemState } from 'runic-reorder'
20
20
  import { Virtualization } from './virtualization.svelte.js'
21
21
  import { TableState, type HeaderSelectCtx, type RowCtx, type RowSelectCtx, type TableProps } from './table.svelte.js'
22
- import Panel, { PanelTween } from '../panel/Panel.svelte'
22
+ import Panel from '../panel/Panel.svelte'
23
23
  import Column from '../column/Column.svelte'
24
24
  import { assignDescriptors, fromProps, mounted } from '../utility.svelte.js'
25
25
  import { conditional } from '../conditional.svelte.js'
@@ -27,6 +27,8 @@
27
27
  import Expandable from '../expandable/Expandable.svelte'
28
28
  import { SizeTween } from '../size-tween.svelte.js'
29
29
  import { on } from 'svelte/events'
30
+ import Row from '../row/Row.svelte'
31
+
30
32
 
31
33
  type T = $$Generic<Record<PropertyKey, unknown>>
32
34
 
@@ -40,6 +42,7 @@
40
42
  }
41
43
  Panel: typeof Panel<T>
42
44
  Expandable: typeof Expandable<T>
45
+ Row: typeof Row<T>
43
46
  readonly table: TableState<T>
44
47
  readonly data: T[]
45
48
  }
@@ -95,18 +98,28 @@
95
98
  /** grid-template-columns for widths */
96
99
  const style = $derived.by(() => {
97
100
  if (!mount.isMounted) return ''
98
- const templateColumns = `
99
- #${table.id} > .headers,
100
- tr.row[data-svelte-tably='${table.id}'],
101
- #${table.id} > tfoot > tr,
102
- #${table.id} > .content > .virtual.bottom {
103
- grid-template-columns: ${columns
101
+
102
+ const context = table.row?.snippets.context ? table.row?.options.context.width : ''
103
+
104
+ const templateColumns = columns
104
105
  .map((column, i, arr) => {
105
106
  const width = getWidth(column.id)
106
107
  if (i === arr.length - 1) return `minmax(${width}px, 1fr)`
107
108
  return `${width}px`
108
109
  })
109
- .join(' ')};
110
+ .join(' ') + context
111
+
112
+ const theadTempla3teColumns = `
113
+ #${table.id} > thead > tr,
114
+ #${table.id} > tfoot > tr {
115
+ grid-template-columns: ${templateColumns};
116
+ }
117
+ `
118
+
119
+ const tbodyTemplateColumns = `
120
+ [data-area-class='${table.id}'] tr.row,
121
+ #${table.id} > tbody::after {
122
+ grid-template-columns: ${templateColumns};
110
123
  }
111
124
  `
112
125
 
@@ -124,12 +137,12 @@
124
137
  .join('')
125
138
 
126
139
  const columnStyling = columns.map(column => !column.options.style ? '' : `
127
- #${table.id} .column[data-column='${column.id}'] {
140
+ [data-area-class='${table.id}'] .column[data-column='${column.id}'] {
128
141
  ${column.options.style}
129
142
  }
130
143
  `).join('')
131
144
 
132
- return templateColumns + stickyLeft + columnStyling
145
+ return theadTempla3teColumns + tbodyTemplateColumns + stickyLeft + columnStyling
133
146
  })
134
147
 
135
148
  function observeColumnWidth(node: HTMLDivElement, isHeader = false) {
@@ -264,6 +277,18 @@
264
277
  }
265
278
  }
266
279
 
280
+ function addRowEvents(
281
+ node: HTMLTableRowElement,
282
+ ctx: RowCtx<T>
283
+ ) {
284
+ if(table.row?.events.onclick) {
285
+ $effect(() => on(node, 'click', e => table.row?.events.onclick!(e, ctx)))
286
+ }
287
+ if(table.row?.events.oncontextmenu) {
288
+ $effect(() => on(node, 'contextmenu', e => table.row?.events.oncontextmenu!(e, ctx)))
289
+ }
290
+ }
291
+
267
292
  </script>
268
293
 
269
294
  <!---------------------------------------------------->
@@ -413,6 +438,10 @@
413
438
  {/each}
414
439
  {/snippet}
415
440
 
441
+ {#snippet defaultRow(item: T, ctx: RowColumnCtx<T, any>)}
442
+ {ctx.value}
443
+ {/snippet}
444
+
416
445
  {#snippet rowSnippet(item: T, itemState?: ItemState<T>)}
417
446
  {@const index = itemState?.index ?? 0}
418
447
 
@@ -444,10 +473,8 @@
444
473
 
445
474
  <tr
446
475
  aria-rowindex={index + 1}
447
- data-svelte-tably={table.id}
448
476
  style:opacity={itemState?.positioning ? 0 : 1}
449
477
  class='row'
450
- class:hover={hoveredRow === item}
451
478
  class:dragging={itemState?.dragging}
452
479
  class:selected={table.selected?.includes(item)}
453
480
  class:first={index === 0}
@@ -456,6 +483,7 @@
456
483
  {...(itemState?.dragging ? { 'data-svelte-tably': table.id } : {})}
457
484
  onpointerenter={() => (hoveredRow = item)}
458
485
  onpointerleave={() => (hoveredRow = null)}
486
+ use:addRowEvents={ctx}
459
487
  onclick={(e) => {
460
488
  if (table.expandable?.options.click === true) {
461
489
  let target = e.target as HTMLElement
@@ -467,7 +495,7 @@
467
495
  }}
468
496
  >
469
497
  {@render columnsSnippet(
470
- (column) => column.snippets.row,
498
+ (column) => column.snippets.row ?? defaultRow,
471
499
  (column) => {
472
500
  return [
473
501
  item,
@@ -480,6 +508,17 @@
480
508
  },
481
509
  'row'
482
510
  )}
511
+ {#if table.row?.snippets.context}
512
+ {#if table.row?.snippets.contextHeader || !table.row?.options.context.hover || hoveredRow === item}
513
+ <td
514
+ class='context-col'
515
+ class:hover={!table.row?.snippets.contextHeader && table.row?.options.context.hover}
516
+ class:hidden={table.row?.options.context.hover && table.row?.snippets.contextHeader && hoveredRow !== item}
517
+ >
518
+ {@render table.row?.snippets.context?.(item, ctx)}
519
+ </td>
520
+ {/if}
521
+ {/if}
483
522
  </tr>
484
523
 
485
524
  {@const expandableTween = new SizeTween(
@@ -510,20 +549,30 @@
510
549
  >
511
550
  {#if columns.some(v => v.snippets.header)}
512
551
  <thead class='headers' bind:this={elements.headers}>
513
- {@render columnsSnippet(
514
- (column) => column.snippets.header,
515
- () => [{
516
- get header() { return true },
517
- get data() { return data.current }
518
- }],
519
- 'header'
520
- )}
552
+ <tr style='min-width: {tbody.width}px'>
553
+ {@render columnsSnippet(
554
+ (column) => column.snippets.header,
555
+ () => [{
556
+ get header() { return true },
557
+ get data() { return data.current }
558
+ }],
559
+ 'header'
560
+ )}
561
+ {#if table.row?.snippets.contextHeader}
562
+ <th
563
+ class='context-col'
564
+ >
565
+ {@render table.row?.snippets.contextHeader()}
566
+ </th>
567
+ {/if}
568
+ </tr>
569
+ <tr style='width:400px;background:none;pointer-events:none;'></tr>
521
570
  </thead>
522
571
  {/if}
523
572
 
524
573
  <tbody
525
574
  class='content'
526
- use:reorderArea={{ axis: 'y' }}
575
+ use:reorderArea={{ axis: 'y', class: table.id }}
527
576
  bind:this={virtualization.viewport.element}
528
577
  onscrollcapture={onscroll}
529
578
  bind:clientHeight={virtualization.viewport.height}
@@ -559,6 +608,7 @@
559
608
  'statusbar'
560
609
  )}
561
610
  </tr>
611
+ <tr style='width:400px;background:none;pointer-events:none;'></tr>
562
612
  </tfoot>
563
613
  {/if}
564
614
 
@@ -595,7 +645,7 @@
595
645
  {/snippet}
596
646
 
597
647
  {#snippet rowSelected(ctx: RowSelectCtx<T>)}
598
- <input type='checkbox' bind:checked={ctx.isSelected} />
648
+ <input type='checkbox' bind:checked={ctx.isSelected} tabindex='-1' />
599
649
  {/snippet}
600
650
 
601
651
  {#if table.options.select || table.options.reorderable || table.expandable}
@@ -677,9 +727,11 @@
677
727
  }
678
728
  })}
679
729
  {/if}
680
- {#if expandable && (row.expanded || expandable.options.chevron === 'always' || (row.isHovered && expandable.options.chevron === 'hover'))}
681
- <button class='expand-row' onclick={() => row.expanded = !row.expanded}>
682
- {@render chevronSnippet(row.expanded ? 180 : 90)}
730
+ {#if expandable && expandable?.options.chevron !== 'never'}
731
+ <button class='expand-row' tabindex='-1' onclick={() => row.expanded = !row.expanded}>
732
+ {#if row.expanded || expandable.options.chevron === 'always' || (row.isHovered && expandable.options.chevron === 'hover')}
733
+ {@render chevronSnippet(row.expanded ? 180 : 90)}
734
+ {/if}
683
735
  </button>
684
736
  {/if}
685
737
  </div>
@@ -692,6 +744,7 @@
692
744
  Column,
693
745
  Panel,
694
746
  Expandable,
747
+ Row,
695
748
  get table() {
696
749
  return table
697
750
  },
@@ -708,11 +761,49 @@
708
761
  background-color: inherit;
709
762
  }
710
763
 
764
+ .context-col {
765
+ display: flex;
766
+ align-items: center;
767
+ justify-content: center;
768
+ position: sticky;
769
+ right: 0;
770
+ height: 100%;
771
+ z-index: 3;
772
+ padding: 0;
773
+
774
+ &.hover {
775
+ position: absolute;
776
+ }
777
+ &.hidden {
778
+ pointer-events: none;
779
+ user-select: none;
780
+ border-left: none;
781
+ background: none;
782
+ > :global(*) {
783
+ opacity: 0;
784
+ }
785
+ }
786
+ }
787
+
788
+ :global(:root) {
789
+ --tably-color: hsl(0, 0%, 0%);
790
+ --tably-bg: hsl(0, 0%, 100%);
791
+ --tably-statusbar: hsl(0, 0%, 98%);
792
+
793
+ --tably-border: hsl(0, 0%, 90%);
794
+ --tably-border-grid: hsl(0, 0%, 98%);
795
+
796
+ --tably-padding-x: 1rem;
797
+ --tably-padding-y: 0.5rem;
798
+
799
+ --tably-radius: 0.25rem;
800
+ }
801
+
711
802
  .svelte-tably {
712
803
  position: relative;
713
804
  overflow: visible;
714
805
  }
715
-
806
+
716
807
  .expandable {
717
808
  position: relative;
718
809
 
@@ -738,7 +829,7 @@
738
829
  cursor: pointer;
739
830
  background-color: transparent;
740
831
  color: inherit;
741
- width: 22px;
832
+ width: 20px;
742
833
  height: 100%;
743
834
 
744
835
  > svg {
@@ -767,12 +858,15 @@
767
858
  justify-items: end;
768
859
  margin: 0;
769
860
  margin-left: auto;
770
- margin-right: var(--tably-padding-x, 1rem);
771
861
  > svg {
772
862
  transition: transform 0.15s ease;
773
863
  }
774
864
  }
775
865
 
866
+ th:not(:last-child) .sorting-icon {
867
+ margin-right: var(--tably-padding-x);
868
+ }
869
+
776
870
  .__fixed {
777
871
  display: flex;
778
872
  align-items: center;
@@ -786,13 +880,6 @@
786
880
  width: 100%;
787
881
  }
788
882
 
789
- .first .__fixed {
790
- top: var(--tably-padding-y, 0.5rem);
791
- }
792
- .last .__fixed {
793
- bottom: var(--tably-padding-y, 0.5rem);
794
- }
795
-
796
883
  tbody::before,
797
884
  tbody::after,
798
885
  selects::before,
@@ -844,17 +931,6 @@
844
931
  }
845
932
  }
846
933
 
847
- .headers,
848
- .statusbar {
849
- /* So that the scrollbar doesn't cause the headers/statusbar to shift */
850
- padding-right: 11px;
851
- }
852
-
853
- .table {
854
- color: var(--tably-color, hsl(0, 0%, 0%));
855
- background-color: var(--tably-bg, hsl(0, 0%, 100%));
856
- }
857
-
858
934
  .sticky {
859
935
  position: sticky;
860
936
  /* right: 100px; */
@@ -862,16 +938,23 @@
862
938
  }
863
939
 
864
940
  .sticky.border {
865
- border-right: 1px solid var(--tably-border, hsl(0, 0%, 90%));
941
+ border-right: 1px solid var(--tably-border);
866
942
  }
867
943
 
868
- .headers > .column {
869
- border-right: 1px solid var(--tably-border, hsl(0, 0%, 90%));
944
+ .headers > tr > .column {
870
945
  overflow: hidden;
871
- padding: var(--tably-padding-y, 0.5rem) 0;
946
+ padding: var(--tably-padding-y) 0;
872
947
  cursor: default;
873
948
  user-select: none;
874
949
 
950
+ &.sticky.border {
951
+ border-right-color: var(--tably-border);
952
+ }
953
+
954
+ &:last-child {
955
+ border-right: none;
956
+ }
957
+
875
958
  &.sortable {
876
959
  cursor: pointer;
877
960
  }
@@ -883,32 +966,41 @@
883
966
 
884
967
  .table {
885
968
  display: grid;
886
- height: 100%;
969
+ height: auto;
970
+ max-height: 100%;
887
971
  position: relative;
888
972
 
973
+ color: var(--tably-color);
974
+ background-color: var(--tably-bg);
975
+
889
976
  grid-template-areas:
890
- 'headers panel'
891
- 'rows panel'
892
- 'statusbar panel';
977
+ 'headers panel'
978
+ 'rows panel'
979
+ 'statusbar panel';
893
980
 
894
981
  grid-template-columns: auto min-content;
895
982
  grid-template-rows: auto 1fr auto;
896
983
 
897
- border: 1px solid var(--tably-border, hsl(0, 0%, 90%));
898
- border-radius: var(--tably-radius, 0.25rem);
899
-
900
- max-height: 100%;
984
+ border: 1px solid var(--tably-border);
985
+ border-radius: var(--tably-radius);
901
986
  }
902
987
 
903
988
  .headers {
989
+ display: flex;
904
990
  grid-area: headers;
905
991
  z-index: 2;
906
992
  overflow: hidden;
907
993
  }
908
994
 
909
- .headers > .column {
995
+ .headers > tr > .column {
910
996
  width: auto !important;
911
- border-bottom: 1px solid var(--tably-border, hsl(0, 0%, 90%));
997
+ border-bottom: 1px solid var(--tably-border);
998
+ }
999
+ .headers > tr {
1000
+ > .column, > .context-col {
1001
+ border-bottom: 1px solid var(--tably-border);
1002
+ border-left: 1px solid var(--tably-border-grid);
1003
+ }
912
1004
  }
913
1005
 
914
1006
  .content {
@@ -918,21 +1010,21 @@
918
1010
  grid-area: rows;
919
1011
  scrollbar-width: thin;
920
1012
  overflow: auto;
921
- /* height: 100%; */
922
1013
  }
923
1014
 
924
1015
  .statusbar {
1016
+ display: flex;
925
1017
  grid-area: statusbar;
926
1018
  overflow: hidden;
927
- background-color: var(--tably-statusbar, hsl(0, 0%, 98%));
1019
+ background-color: var(--tably-statusbar);
928
1020
  }
929
1021
 
930
1022
  .statusbar > tr > .column {
931
- border-top: 1px solid var(--tably-border, hsl(0, 0%, 90%));
932
- padding: calc(var(--tably-padding-y, 0.5rem) / 2) 0;
1023
+ border-top: 1px solid var(--tably-border);
1024
+ padding: calc(var(--tably-padding-y) / 2) 0;
933
1025
  }
934
1026
 
935
- .headers,
1027
+ .headers > tr,
936
1028
  .row,
937
1029
  .statusbar > tr {
938
1030
  position: relative;
@@ -942,25 +1034,22 @@
942
1034
 
943
1035
  & > .column {
944
1036
  display: flex;
945
- padding-left: var(--tably-padding-x, 1rem);
1037
+ padding-left: var(--tably-padding-x);
946
1038
  overflow: hidden;
947
1039
  }
948
1040
 
949
- & > *:last-child {
1041
+ & > *:last-child:not(.context-col) {
950
1042
  width: 100%;
951
- padding-right: var(--tably-padding-x, 1rem);
1043
+ padding-right: var(--tably-padding-x);
952
1044
  }
953
1045
  }
954
1046
 
955
- .row:first-child:not(.dragging) > * {
956
- padding-top: calc(var(--tably-padding-y, 0.5rem) + calc(var(--tably-padding-y, 0.5rem) / 2));
1047
+ .row > .column {
1048
+ padding: var(--tably-padding-y) 0;
957
1049
  }
958
- .row:last-child:not(.dragging) > * {
959
- padding-bottom: calc(var(--tably-padding-y, 0.5rem) + calc(var(--tably-padding-y, 0.5rem) / 2));
960
- }
961
-
962
1050
  .row > * {
963
- padding: calc(var(--tably-padding-y, 0.5rem) / 2) 0;
1051
+ border-left: 1px solid var(--tably-border-grid);
1052
+ border-bottom: 1px solid var(--tably-border-grid);
964
1053
  }
965
1054
 
966
1055
  .panel {
@@ -968,7 +1057,7 @@
968
1057
  grid-area: panel;
969
1058
  height: 100%;
970
1059
  overflow: hidden;
971
- border-left: 1px solid var(--tably-border, hsl(0, 0%, 90%));
1060
+ border-left: 1px solid var(--tably-border);
972
1061
 
973
1062
  z-index: 4;
974
1063
 
@@ -980,7 +1069,7 @@
980
1069
  width: min-content;
981
1070
  overflow: auto;
982
1071
  scrollbar-width: thin;
983
- padding: var(--tably-padding-y, 0.5rem) 0;
1072
+ padding: var(--tably-padding-y) 0;
984
1073
  }
985
1074
  }
986
1075
  </style>
@@ -10,6 +10,7 @@ export class TableState {
10
10
  columns = $state({});
11
11
  panels = $state({});
12
12
  expandable = $state();
13
+ row = $state();
13
14
  /** Currently selected items */
14
15
  get selected() { return this.#props.selected ??= []; }
15
16
  set selected(items) { this.#props.selected = items; }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "svelte-tably",
3
- "version": "1.0.0-next.15",
3
+ "version": "1.0.0-next.16",
4
4
  "repository": "github:refzlund/svelte-tably",
5
5
  "homepage": "https://github.com/Refzlund/svelte-tably",
6
6
  "bugs": {