svelte-tably 1.1.2 → 1.2.0
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 +8 -1
- package/dist/column/Column.svelte +1 -1
- package/dist/column/Column.svelte.d.ts +5 -5
- package/dist/column/column-state.svelte.d.ts +2 -1
- package/dist/column/column-state.svelte.js +2 -1
- package/dist/expandable/Expandable.svelte +1 -2
- package/dist/expandable/Expandable.svelte.d.ts +4 -5
- package/dist/index.d.ts +4 -0
- package/dist/panel/Panel.svelte +1 -1
- package/dist/panel/Panel.svelte.d.ts +4 -4
- package/dist/row/Row.svelte +1 -2
- package/dist/row/Row.svelte.d.ts +4 -5
- package/dist/row/row-state.svelte.d.ts +4 -1
- package/dist/size-tween.svelte.js +9 -1
- package/dist/table/Table.svelte +339 -115
- package/dist/table/Table.svelte.d.ts +24 -95
- package/dist/table/data.svelte.js +21 -8
- package/dist/table/table-state.svelte.d.ts +1 -1
- package/dist/table/virtualization.svelte.d.ts +2 -2
- package/dist/table/virtualization.svelte.js +67 -23
- package/dist/utility.svelte.d.ts +1 -1
- package/dist/utility.svelte.js +2 -2
- package/package.json +1 -1
package/dist/table/Table.svelte
CHANGED
|
@@ -8,12 +8,8 @@
|
|
|
8
8
|
|
|
9
9
|
-->
|
|
10
10
|
|
|
11
|
-
<script lang="ts">
|
|
12
|
-
import {
|
|
13
|
-
import { fly } from 'svelte/transition'
|
|
14
|
-
import { sineInOut } from 'svelte/easing'
|
|
15
|
-
import reorder, { type ItemState } from 'runic-reorder'
|
|
16
|
-
import { Virtualization } from './virtualization.svelte.js'
|
|
11
|
+
<script module lang="ts">
|
|
12
|
+
import type { Snippet } from 'svelte'
|
|
17
13
|
import {
|
|
18
14
|
TableState,
|
|
19
15
|
type HeaderSelectCtx,
|
|
@@ -21,36 +17,43 @@
|
|
|
21
17
|
type RowSelectCtx,
|
|
22
18
|
type TableProps
|
|
23
19
|
} from './table-state.svelte.js'
|
|
24
|
-
import Panel from '../panel/Panel.svelte'
|
|
25
20
|
import Column from '../column/Column.svelte'
|
|
26
|
-
import
|
|
27
|
-
import { conditional } from '../conditional.svelte.js'
|
|
28
|
-
import { ColumnState, type RowColumnCtx } from '../column/column-state.svelte.js'
|
|
21
|
+
import Panel from '../panel/Panel.svelte'
|
|
29
22
|
import Expandable from '../expandable/Expandable.svelte'
|
|
30
|
-
import { SizeTween } from '../size-tween.svelte.js'
|
|
31
|
-
import { on } from 'svelte/events'
|
|
32
23
|
import Row from '../row/Row.svelte'
|
|
33
|
-
import type { CSVOptions } from './csv.js'
|
|
34
|
-
|
|
35
|
-
type T = $$Generic<Record<PropertyKey, unknown>>
|
|
36
24
|
|
|
37
|
-
type ConstructorReturnType<
|
|
38
|
-
|
|
39
|
-
type ConstructorParams<
|
|
40
|
-
|
|
25
|
+
type ConstructorReturnType<C extends new (...args: any[]) => any> =
|
|
26
|
+
C extends new (...args: any[]) => infer K ? K : never
|
|
27
|
+
type ConstructorParams<C extends new (...args: any[]) => any> =
|
|
28
|
+
C extends new (...args: infer K) => any ? K : never
|
|
41
29
|
|
|
42
|
-
type ContentCtx<
|
|
30
|
+
export type ContentCtx<Item = any> = {
|
|
43
31
|
Column: {
|
|
44
|
-
new <V>(...args: ConstructorParams<typeof Column<
|
|
45
|
-
<V>(...args: Parameters<typeof Column<
|
|
32
|
+
new <V>(...args: ConstructorParams<typeof Column<Item, V>>): ConstructorReturnType<typeof Column<Item, V>>
|
|
33
|
+
<V>(...args: Parameters<typeof Column<Item, V>>): ReturnType<typeof Column<Item, V>>
|
|
46
34
|
}
|
|
47
|
-
Panel: typeof Panel<
|
|
48
|
-
Expandable: typeof Expandable<
|
|
49
|
-
Row: typeof Row<
|
|
50
|
-
readonly table: TableState<
|
|
35
|
+
Panel: typeof Panel<Item>
|
|
36
|
+
Expandable: typeof Expandable<Item>
|
|
37
|
+
Row: typeof Row<Item>
|
|
38
|
+
readonly table: TableState<Item>
|
|
51
39
|
}
|
|
52
40
|
|
|
53
|
-
type ContentSnippet = Snippet<[context: ContentCtx<
|
|
41
|
+
export type ContentSnippet<Item = any> = Snippet<[context: ContentCtx<Item>]>
|
|
42
|
+
</script>
|
|
43
|
+
|
|
44
|
+
<script lang="ts">
|
|
45
|
+
import { fly } from 'svelte/transition'
|
|
46
|
+
import { sineInOut } from 'svelte/easing'
|
|
47
|
+
import reorder, { type ItemState } from 'runic-reorder'
|
|
48
|
+
import { Virtualization } from './virtualization.svelte.js'
|
|
49
|
+
import { assignDescriptors, capitalize, fromProps, mounted, segmentize } from '../utility.svelte.js'
|
|
50
|
+
import { conditional } from '../conditional.svelte.js'
|
|
51
|
+
import { ColumnState, type RowColumnCtx } from '../column/column-state.svelte.js'
|
|
52
|
+
import { SizeTween } from '../size-tween.svelte.js'
|
|
53
|
+
import { on } from 'svelte/events'
|
|
54
|
+
import type { CSVOptions } from './csv.js'
|
|
55
|
+
|
|
56
|
+
type T = $$Generic
|
|
54
57
|
|
|
55
58
|
let {
|
|
56
59
|
content,
|
|
@@ -59,7 +62,7 @@
|
|
|
59
62
|
data: _data = $bindable([]),
|
|
60
63
|
table: _table = $bindable(),
|
|
61
64
|
...restProps
|
|
62
|
-
}: TableProps<T> & { content?: ContentSnippet } = $props()
|
|
65
|
+
}: TableProps<T> & { content?: ContentSnippet<T> } = $props()
|
|
63
66
|
|
|
64
67
|
const properties = fromProps(restProps, {
|
|
65
68
|
selected: [() => _selected, (v) => (_selected = v)],
|
|
@@ -69,14 +72,39 @@
|
|
|
69
72
|
|
|
70
73
|
const mount = mounted()
|
|
71
74
|
|
|
75
|
+
|
|
76
|
+
const getRowLabel = (item: T, index: number) => {
|
|
77
|
+
const labelColumn = columns.find((c) => c.id !== '__fixed')
|
|
78
|
+
const raw = labelColumn?.options.value?.(item)
|
|
79
|
+
|
|
80
|
+
if (raw === null || raw === undefined) return `Row ${index + 1}`
|
|
81
|
+
if (typeof raw === 'string' || typeof raw === 'number' || typeof raw === 'boolean') {
|
|
82
|
+
const text = String(raw).trim()
|
|
83
|
+
return text ? text : `Row ${index + 1}`
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return `Row ${index + 1}`
|
|
87
|
+
}
|
|
88
|
+
|
|
72
89
|
const reorderArea = reorder(rowSnippet)
|
|
73
90
|
|
|
74
|
-
const elements = $state({}) as Record<
|
|
75
|
-
'headers' | 'statusbar' | 'rows' | 'virtualTop' | 'virtualBottom' | 'selects',
|
|
76
|
-
HTMLElement
|
|
77
|
-
>
|
|
91
|
+
const elements = $state({}) as Record<'headers' | 'statusbar', HTMLElement>
|
|
78
92
|
|
|
79
93
|
const table = new TableState<T>(properties) as TableState<T>
|
|
94
|
+
const uid = table.cssId
|
|
95
|
+
let expandIdCounter = 0
|
|
96
|
+
const expandIds = new WeakMap<object, string>()
|
|
97
|
+
const getExpandId = (item: T) => {
|
|
98
|
+
if (item && typeof item === 'object') {
|
|
99
|
+
let id = expandIds.get(item)
|
|
100
|
+
if (!id) {
|
|
101
|
+
id = `${uid}-expand-${++expandIdCounter}`
|
|
102
|
+
expandIds.set(item, id)
|
|
103
|
+
}
|
|
104
|
+
return id
|
|
105
|
+
}
|
|
106
|
+
return `${uid}-expand-${String(item)}`
|
|
107
|
+
}
|
|
80
108
|
|
|
81
109
|
const virtualization = new Virtualization(table)
|
|
82
110
|
|
|
@@ -86,13 +114,38 @@
|
|
|
86
114
|
let hoveredColumn: ColumnState | null = $state(null)
|
|
87
115
|
|
|
88
116
|
/** Order of columns */
|
|
89
|
-
const
|
|
90
|
-
|
|
91
|
-
const
|
|
117
|
+
const isColumn = (value: unknown): value is ColumnState =>
|
|
118
|
+
value instanceof ColumnState
|
|
119
|
+
const fixed = $derived(table.positions.fixed.filter(isColumn))
|
|
120
|
+
const hidden = $derived(table.positions.hidden.filter(isColumn))
|
|
121
|
+
const notHidden = (column: ColumnState | undefined) =>
|
|
122
|
+
!!column && !table.positions.hidden.includes(column)
|
|
92
123
|
const sticky = $derived(table.positions.sticky.filter(notHidden))
|
|
93
124
|
const scrolled = $derived(table.positions.scroll.filter(notHidden))
|
|
94
125
|
const columns = $derived([...fixed, ...sticky, ...scrolled])
|
|
95
126
|
|
|
127
|
+
const autoSchema = $derived.by(() => {
|
|
128
|
+
const rows = table.dataState.origin as any[]
|
|
129
|
+
const keys = [] as string[]
|
|
130
|
+
const seen = new Set<string>()
|
|
131
|
+
const sample = {} as Record<string, unknown>
|
|
132
|
+
|
|
133
|
+
for (const row of rows.slice(0, 50)) {
|
|
134
|
+
if (!row || typeof row !== 'object') continue
|
|
135
|
+
for (const key of Object.keys(row)) {
|
|
136
|
+
if (!seen.has(key)) {
|
|
137
|
+
seen.add(key)
|
|
138
|
+
keys.push(key)
|
|
139
|
+
}
|
|
140
|
+
if (sample[key] === undefined && row[key] !== undefined) {
|
|
141
|
+
sample[key] = row[key]
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
return { keys, sample }
|
|
147
|
+
})
|
|
148
|
+
|
|
96
149
|
const getWidth = (key: string, def: number = 150) =>
|
|
97
150
|
table.columnWidths[key] ??= table.columns[key]?.defaults.width ?? def
|
|
98
151
|
|
|
@@ -104,7 +157,7 @@
|
|
|
104
157
|
return
|
|
105
158
|
}
|
|
106
159
|
|
|
107
|
-
const context = table.row?.snippets.context ? table.row?.options.context.width : ''
|
|
160
|
+
const context = table.row?.snippets.context ? ` ${table.row?.options.context.width}` : ''
|
|
108
161
|
|
|
109
162
|
const templateColumns =
|
|
110
163
|
columns
|
|
@@ -115,7 +168,7 @@
|
|
|
115
168
|
})
|
|
116
169
|
.join(' ') + context
|
|
117
170
|
|
|
118
|
-
const
|
|
171
|
+
const theadTemplateColumns = `
|
|
119
172
|
[data-svelte-tably="${table.cssId}"] > thead > tr,
|
|
120
173
|
[data-svelte-tably="${table.cssId}"] > tfoot > tr {
|
|
121
174
|
grid-template-columns: ${templateColumns};
|
|
@@ -124,6 +177,7 @@
|
|
|
124
177
|
|
|
125
178
|
const tbodyTemplateColumns = `
|
|
126
179
|
[data-area-class='${table.cssId}'] tr.row,
|
|
180
|
+
[data-area-class='${table.cssId}'] tr.filler,
|
|
127
181
|
[data-svelte-tably="${table.cssId}"] > tbody::after {
|
|
128
182
|
grid-template-columns: ${templateColumns};
|
|
129
183
|
}
|
|
@@ -154,7 +208,7 @@
|
|
|
154
208
|
)
|
|
155
209
|
.join('')
|
|
156
210
|
|
|
157
|
-
style =
|
|
211
|
+
style = theadTemplateColumns + tbodyTemplateColumns + stickyLeft + columnStyling
|
|
158
212
|
})
|
|
159
213
|
|
|
160
214
|
function observeColumnWidth(node: HTMLDivElement, isHeader = false) {
|
|
@@ -188,16 +242,32 @@
|
|
|
188
242
|
}
|
|
189
243
|
|
|
190
244
|
let tbody = $state({
|
|
191
|
-
|
|
245
|
+
scrollbar: 0
|
|
192
246
|
})
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
if (
|
|
196
|
-
|
|
247
|
+
|
|
248
|
+
function observeScrollbar(node: HTMLElement) {
|
|
249
|
+
if (typeof ResizeObserver === 'undefined') return
|
|
250
|
+
|
|
251
|
+
const update = () => {
|
|
252
|
+
// Reserve the same gutter in header/footer as the scrollable body
|
|
253
|
+
tbody.scrollbar = Math.max(0, node.offsetWidth - node.clientWidth)
|
|
197
254
|
}
|
|
198
255
|
|
|
199
|
-
|
|
200
|
-
|
|
256
|
+
update()
|
|
257
|
+
const observer = new ResizeObserver(update)
|
|
258
|
+
observer.observe(node)
|
|
259
|
+
|
|
260
|
+
return {
|
|
261
|
+
destroy() {
|
|
262
|
+
observer.disconnect()
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
function onscroll() {
|
|
267
|
+
const target = virtualization.viewport.element
|
|
268
|
+
if (!target) return
|
|
269
|
+
if (target.scrollTop !== virtualization.scrollTop) {
|
|
270
|
+
virtualization.scrollTop = target?.scrollTop ?? virtualization.scrollTop
|
|
201
271
|
}
|
|
202
272
|
|
|
203
273
|
if (elements.headers) {
|
|
@@ -415,7 +485,11 @@
|
|
|
415
485
|
class:fixed={true}
|
|
416
486
|
use:addRowColumnEvents={[where, column, () => args[1]]}
|
|
417
487
|
data-column={column.id}
|
|
418
|
-
class:pad={
|
|
488
|
+
class:pad={
|
|
489
|
+
(where === 'header' && column.options.padHeader) ||
|
|
490
|
+
(where === 'row' && column.options.padRow) ||
|
|
491
|
+
(where === 'statusbar' && column.options.padStatusbar)
|
|
492
|
+
}
|
|
419
493
|
class:header={isHeader}
|
|
420
494
|
class:sortable
|
|
421
495
|
use:conditional={[isHeader, (node) => table.dataState.sortAction(node, column.id)]}
|
|
@@ -443,7 +517,11 @@
|
|
|
443
517
|
use:addRowColumnEvents={[where, column, () => args[1]]}
|
|
444
518
|
use:observeColumnWidth={isHeader}
|
|
445
519
|
data-column={column.id}
|
|
446
|
-
class:pad={
|
|
520
|
+
class:pad={
|
|
521
|
+
(where === 'header' && column.options.padHeader) ||
|
|
522
|
+
(where === 'row' && column.options.padRow) ||
|
|
523
|
+
(where === 'statusbar' && column.options.padStatusbar)
|
|
524
|
+
}
|
|
447
525
|
class:header={isHeader}
|
|
448
526
|
class:resizeable={isHeader && column.options.resizeable && table.options.resizeable}
|
|
449
527
|
class:border={i == sticky.length - 1}
|
|
@@ -470,7 +548,11 @@
|
|
|
470
548
|
class={column.options.class ?? ''}
|
|
471
549
|
class:column={true}
|
|
472
550
|
data-column={column.id}
|
|
473
|
-
class:pad={
|
|
551
|
+
class:pad={
|
|
552
|
+
(where === 'header' && column.options.padHeader) ||
|
|
553
|
+
(where === 'row' && column.options.padRow) ||
|
|
554
|
+
(where === 'statusbar' && column.options.padStatusbar)
|
|
555
|
+
}
|
|
474
556
|
use:addRowColumnEvents={[where, column, () => args[1]]}
|
|
475
557
|
use:observeColumnWidth={isHeader}
|
|
476
558
|
class:resizeable={isHeader && column.options.resizeable && table.options.resizeable}
|
|
@@ -490,7 +572,7 @@
|
|
|
490
572
|
{/each}
|
|
491
573
|
{/snippet}
|
|
492
574
|
|
|
493
|
-
{#snippet defaultRow(item:
|
|
575
|
+
{#snippet defaultRow(item: any, ctx: RowColumnCtx<any, any>)}
|
|
494
576
|
{ctx.value}
|
|
495
577
|
{/snippet}
|
|
496
578
|
|
|
@@ -566,30 +648,46 @@
|
|
|
566
648
|
'row'
|
|
567
649
|
)}
|
|
568
650
|
{#if table.row?.snippets.context}
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
hoveredRow !== item}
|
|
576
|
-
>
|
|
577
|
-
{@render table.row?.snippets.context?.(item, ctx)}
|
|
578
|
-
</td>
|
|
579
|
-
{/if}
|
|
651
|
+
<td
|
|
652
|
+
class="context-col"
|
|
653
|
+
class:hidden={table.row?.options.context.hover && hoveredRow !== item}
|
|
654
|
+
>
|
|
655
|
+
{@render table.row?.snippets.context?.(item, ctx)}
|
|
656
|
+
</td>
|
|
580
657
|
{/if}
|
|
581
658
|
</tr>
|
|
582
659
|
|
|
583
|
-
{@const expandableTween = new SizeTween(() => table.expandable && expandedRow.includes(item), {
|
|
584
|
-
min:
|
|
660
|
+
{@const expandableTween = new SizeTween(() => !!table.expandable && expandedRow.includes(item), {
|
|
661
|
+
min: 0,
|
|
585
662
|
duration: table.expandable?.options.slide.duration,
|
|
586
663
|
easing: table.expandable?.options.slide.easing
|
|
587
664
|
})}
|
|
588
|
-
{
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
665
|
+
{@const expanded = !!table.expandable && expandedRow.includes(item)}
|
|
666
|
+
{#if table.expandable && (expanded || expandableTween.current > 0 || expandableTween.transitioning)}
|
|
667
|
+
{@const expandId = getExpandId(item)}
|
|
668
|
+
{@const expandLabelId = `${expandId}-label`}
|
|
669
|
+
<tr class="expandable">
|
|
670
|
+
<td
|
|
671
|
+
colspan={columns.length + (table.row?.snippets.context ? 1 : 0)}
|
|
672
|
+
style="padding: 0"
|
|
673
|
+
>
|
|
674
|
+
<div
|
|
675
|
+
class="expandable-clip"
|
|
676
|
+
style="height: {Math.round(expandableTween.current)}px"
|
|
677
|
+
id={expandId}
|
|
678
|
+
role="region"
|
|
679
|
+
aria-labelledby={expandLabelId}
|
|
680
|
+
aria-hidden={!expanded}
|
|
681
|
+
>
|
|
682
|
+
<span class="sr-only" id={expandLabelId}>
|
|
683
|
+
Expanded content for {getRowLabel(item, index)}
|
|
684
|
+
</span>
|
|
685
|
+
<div
|
|
686
|
+
class="expandable-content"
|
|
687
|
+
bind:offsetHeight={expandableTween.size}
|
|
688
|
+
>
|
|
689
|
+
{@render table.expandable?.snippets.content?.(item, ctx)}
|
|
690
|
+
</div>
|
|
593
691
|
</div>
|
|
594
692
|
</td>
|
|
595
693
|
</tr>
|
|
@@ -600,7 +698,7 @@
|
|
|
600
698
|
id={table.id}
|
|
601
699
|
data-svelte-tably={table.cssId}
|
|
602
700
|
class="table svelte-tably"
|
|
603
|
-
style="--t: {virtualization.virtualTop}px; --b: {virtualization.virtualBottom}px;"
|
|
701
|
+
style="--t: {virtualization.virtualTop}px; --b: {virtualization.virtualBottom}px; --scrollbar: {tbody.scrollbar}px;"
|
|
604
702
|
aria-rowcount={table.data.length}
|
|
605
703
|
>
|
|
606
704
|
{#if columns.some((v) => v.snippets.header)}
|
|
@@ -620,23 +718,28 @@
|
|
|
620
718
|
],
|
|
621
719
|
'header'
|
|
622
720
|
)}
|
|
623
|
-
{#if table.row?.snippets.
|
|
624
|
-
<th
|
|
625
|
-
|
|
721
|
+
{#if table.row?.snippets.context}
|
|
722
|
+
<th
|
|
723
|
+
class="context-col"
|
|
724
|
+
aria-hidden={table.row?.snippets.contextHeader ? undefined : true}
|
|
725
|
+
role={table.row?.snippets.contextHeader ? undefined : 'presentation'}
|
|
726
|
+
>
|
|
727
|
+
{#if table.row?.snippets.contextHeader}
|
|
728
|
+
{@render table.row?.snippets.contextHeader()}
|
|
729
|
+
{/if}
|
|
626
730
|
</th>
|
|
627
731
|
{/if}
|
|
628
732
|
</tr>
|
|
629
|
-
<tr style="width:400px;background:none;pointer-events:none;"></tr>
|
|
630
733
|
</thead>
|
|
631
734
|
{/if}
|
|
632
735
|
|
|
633
736
|
<tbody
|
|
634
737
|
class="content"
|
|
635
738
|
use:reorderArea={{ axis: 'y', class: table.cssId }}
|
|
739
|
+
use:observeScrollbar
|
|
636
740
|
bind:this={virtualization.viewport.element}
|
|
637
741
|
onscrollcapture={onscroll}
|
|
638
742
|
bind:clientHeight={virtualization.viewport.height}
|
|
639
|
-
bind:clientWidth={tbody.width}
|
|
640
743
|
>
|
|
641
744
|
{#if table.options.reorderable}
|
|
642
745
|
{@render reorderArea({
|
|
@@ -655,6 +758,39 @@
|
|
|
655
758
|
{@render rowSnippet(item, { index: i + virtualization.topIndex } as ItemState)}
|
|
656
759
|
{/each}
|
|
657
760
|
{/if}
|
|
761
|
+
|
|
762
|
+
{#if columns.length > 0 && virtualization.virtualTop === 0 && virtualization.virtualBottom === 0}
|
|
763
|
+
<tr class="filler" aria-hidden="true">
|
|
764
|
+
{#each fixed as column (column)}
|
|
765
|
+
{#if !hidden.includes(column)}
|
|
766
|
+
<td
|
|
767
|
+
class={`column sticky fixed ${column.options.class ?? ''}`}
|
|
768
|
+
data-column={column.id}
|
|
769
|
+
></td>
|
|
770
|
+
{/if}
|
|
771
|
+
{/each}
|
|
772
|
+
{#each sticky as column, i (column)}
|
|
773
|
+
{#if !hidden.includes(column)}
|
|
774
|
+
<td
|
|
775
|
+
class={`column sticky ${column.options.class ?? ''}`}
|
|
776
|
+
class:border={i == sticky.length - 1}
|
|
777
|
+
data-column={column.id}
|
|
778
|
+
></td>
|
|
779
|
+
{/if}
|
|
780
|
+
{/each}
|
|
781
|
+
{#each scrolled as column (column)}
|
|
782
|
+
{#if !hidden.includes(column)}
|
|
783
|
+
<td
|
|
784
|
+
class={`column ${column.options.class ?? ''}`}
|
|
785
|
+
data-column={column.id}
|
|
786
|
+
></td>
|
|
787
|
+
{/if}
|
|
788
|
+
{/each}
|
|
789
|
+
{#if table.row?.snippets.context}
|
|
790
|
+
<td class="context-col" aria-hidden="true"></td>
|
|
791
|
+
{/if}
|
|
792
|
+
</tr>
|
|
793
|
+
{/if}
|
|
658
794
|
</tbody>
|
|
659
795
|
|
|
660
796
|
{#if columns.some((v) => v.snippets.statusbar)}
|
|
@@ -671,8 +807,10 @@
|
|
|
671
807
|
],
|
|
672
808
|
'statusbar'
|
|
673
809
|
)}
|
|
810
|
+
{#if table.row?.snippets.context}
|
|
811
|
+
<td class="context-col" aria-hidden="true"></td>
|
|
812
|
+
{/if}
|
|
674
813
|
</tr>
|
|
675
|
-
<tr style="width:400px;background:none;pointer-events:none;"></tr>
|
|
676
814
|
</tfoot>
|
|
677
815
|
{/if}
|
|
678
816
|
|
|
@@ -708,11 +846,11 @@
|
|
|
708
846
|
</caption>
|
|
709
847
|
</table>
|
|
710
848
|
|
|
711
|
-
{#snippet headerSelected(ctx: HeaderSelectCtx<
|
|
849
|
+
{#snippet headerSelected(ctx: HeaderSelectCtx<any>)}
|
|
712
850
|
<input type="checkbox" indeterminate={ctx.indeterminate} bind:checked={ctx.isSelected} />
|
|
713
851
|
{/snippet}
|
|
714
852
|
|
|
715
|
-
{#snippet rowSelected(ctx: RowSelectCtx<
|
|
853
|
+
{#snippet rowSelected(ctx: RowSelectCtx<any>)}
|
|
716
854
|
<input type="checkbox" bind:checked={ctx.isSelected} tabindex="-1" />
|
|
717
855
|
{/snippet}
|
|
718
856
|
|
|
@@ -798,9 +936,20 @@
|
|
|
798
936
|
})}
|
|
799
937
|
{/if}
|
|
800
938
|
{#if expandable && expandable?.options.chevron !== 'never'}
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
939
|
+
{@const expandId = getExpandId(item)}
|
|
940
|
+
{@const expanded = row.expanded}
|
|
941
|
+
{@const label = expanded ? 'Collapse row' : 'Expand row'}
|
|
942
|
+
<button
|
|
943
|
+
class="expand-row"
|
|
944
|
+
tabindex="-1"
|
|
945
|
+
type="button"
|
|
946
|
+
aria-label={label}
|
|
947
|
+
aria-expanded={expanded}
|
|
948
|
+
aria-controls={expandId}
|
|
949
|
+
onclick={() => (row.expanded = !row.expanded)}
|
|
950
|
+
>
|
|
951
|
+
{#if expanded || expandable.options.chevron === 'always' || (row.rowHovered && expandable.options.chevron === 'hover')}
|
|
952
|
+
{@render chevronSnippet(expanded ? 180 : 90)}
|
|
804
953
|
{/if}
|
|
805
954
|
</button>
|
|
806
955
|
{/if}
|
|
@@ -811,12 +960,12 @@
|
|
|
811
960
|
{/if}
|
|
812
961
|
|
|
813
962
|
{#if table.options.auto}
|
|
814
|
-
{#each
|
|
963
|
+
{#each autoSchema.keys as key}
|
|
815
964
|
<Column
|
|
816
965
|
id={key}
|
|
817
|
-
|
|
966
|
+
value={(r) => (r as any)?.[key]}
|
|
818
967
|
header={capitalize(segmentize(key))}
|
|
819
|
-
sort={typeof
|
|
968
|
+
sort={typeof autoSchema.sample?.[key] === 'number' ?
|
|
820
969
|
(a, b) => a - b
|
|
821
970
|
: (a, b) => String(a).localeCompare(String(b))}
|
|
822
971
|
/>
|
|
@@ -850,14 +999,9 @@
|
|
|
850
999
|
height: 100%;
|
|
851
1000
|
z-index: 3;
|
|
852
1001
|
padding: 0;
|
|
853
|
-
|
|
854
|
-
&.hover {
|
|
855
|
-
position: absolute;
|
|
856
|
-
}
|
|
857
1002
|
&.hidden {
|
|
858
1003
|
pointer-events: none;
|
|
859
1004
|
user-select: none;
|
|
860
|
-
border-left: none;
|
|
861
1005
|
background: none;
|
|
862
1006
|
> :global(*) {
|
|
863
1007
|
opacity: 0;
|
|
@@ -865,6 +1009,30 @@
|
|
|
865
1009
|
}
|
|
866
1010
|
}
|
|
867
1011
|
|
|
1012
|
+
.table::before {
|
|
1013
|
+
content: '';
|
|
1014
|
+
grid-area: headers;
|
|
1015
|
+
justify-self: end;
|
|
1016
|
+
align-self: stretch;
|
|
1017
|
+
width: var(--scrollbar, 0px);
|
|
1018
|
+
background-color: var(--tably-bg);
|
|
1019
|
+
pointer-events: none;
|
|
1020
|
+
position: relative;
|
|
1021
|
+
z-index: 4;
|
|
1022
|
+
}
|
|
1023
|
+
|
|
1024
|
+
.table::after {
|
|
1025
|
+
content: '';
|
|
1026
|
+
grid-area: statusbar;
|
|
1027
|
+
justify-self: end;
|
|
1028
|
+
align-self: stretch;
|
|
1029
|
+
width: var(--scrollbar, 0px);
|
|
1030
|
+
background-color: var(--tably-statusbar);
|
|
1031
|
+
pointer-events: none;
|
|
1032
|
+
position: relative;
|
|
1033
|
+
z-index: 4;
|
|
1034
|
+
}
|
|
1035
|
+
|
|
868
1036
|
:global(:root) {
|
|
869
1037
|
--tably-color: hsl(0, 0%, 0%);
|
|
870
1038
|
--tably-bg: hsl(0, 0%, 100%);
|
|
@@ -881,24 +1049,32 @@
|
|
|
881
1049
|
|
|
882
1050
|
.svelte-tably {
|
|
883
1051
|
position: relative;
|
|
884
|
-
overflow:
|
|
1052
|
+
overflow: hidden;
|
|
1053
|
+
border-collapse: collapse;
|
|
1054
|
+
border-spacing: 0;
|
|
885
1055
|
}
|
|
886
1056
|
|
|
887
1057
|
.expandable {
|
|
888
|
-
position: relative;
|
|
889
|
-
|
|
890
1058
|
& > td {
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
> div {
|
|
894
|
-
position: absolute;
|
|
895
|
-
overflow: auto;
|
|
896
|
-
top: -1.5px;
|
|
897
|
-
left: 0;
|
|
898
|
-
}
|
|
1059
|
+
padding: 0;
|
|
1060
|
+
border: none;
|
|
899
1061
|
}
|
|
900
1062
|
}
|
|
901
1063
|
|
|
1064
|
+
.expandable-clip {
|
|
1065
|
+
overflow: hidden;
|
|
1066
|
+
width: 100%;
|
|
1067
|
+
background-color: var(--tably-bg);
|
|
1068
|
+
box-shadow: inset 0 -1px 0 var(--tably-border-grid);
|
|
1069
|
+
}
|
|
1070
|
+
|
|
1071
|
+
.expandable-content {
|
|
1072
|
+
overflow: auto;
|
|
1073
|
+
width: 100%;
|
|
1074
|
+
background-color: var(--tably-bg);
|
|
1075
|
+
box-sizing: border-box;
|
|
1076
|
+
}
|
|
1077
|
+
|
|
902
1078
|
.expand-row {
|
|
903
1079
|
display: flex;
|
|
904
1080
|
justify-content: center;
|
|
@@ -965,20 +1141,16 @@
|
|
|
965
1141
|
}
|
|
966
1142
|
|
|
967
1143
|
tbody::before,
|
|
968
|
-
tbody::after
|
|
969
|
-
selects::before,
|
|
970
|
-
selects::after {
|
|
1144
|
+
tbody::after {
|
|
971
1145
|
content: '';
|
|
972
|
-
display:
|
|
973
|
-
|
|
1146
|
+
display: block;
|
|
1147
|
+
flex: 0 0 auto;
|
|
974
1148
|
}
|
|
975
1149
|
|
|
976
|
-
tbody::before
|
|
977
|
-
selects::before {
|
|
1150
|
+
tbody::before {
|
|
978
1151
|
height: var(--t);
|
|
979
1152
|
}
|
|
980
|
-
tbody::after
|
|
981
|
-
selects::after {
|
|
1153
|
+
tbody::after {
|
|
982
1154
|
height: var(--b);
|
|
983
1155
|
}
|
|
984
1156
|
|
|
@@ -1046,7 +1218,10 @@
|
|
|
1046
1218
|
|
|
1047
1219
|
.table {
|
|
1048
1220
|
display: grid;
|
|
1049
|
-
|
|
1221
|
+
width: 100%;
|
|
1222
|
+
min-width: 0;
|
|
1223
|
+
min-height: 0;
|
|
1224
|
+
height: 100%;
|
|
1050
1225
|
max-height: 100%;
|
|
1051
1226
|
position: relative;
|
|
1052
1227
|
|
|
@@ -1058,8 +1233,8 @@
|
|
|
1058
1233
|
'rows panel'
|
|
1059
1234
|
'statusbar panel';
|
|
1060
1235
|
|
|
1061
|
-
grid-template-columns:
|
|
1062
|
-
grid-template-rows: auto 1fr auto;
|
|
1236
|
+
grid-template-columns: 1fr min-content;
|
|
1237
|
+
grid-template-rows: auto minmax(0, 1fr) auto;
|
|
1063
1238
|
|
|
1064
1239
|
border: 1px solid var(--tably-border);
|
|
1065
1240
|
border-radius: var(--tably-radius);
|
|
@@ -1070,6 +1245,8 @@
|
|
|
1070
1245
|
grid-area: headers;
|
|
1071
1246
|
z-index: 2;
|
|
1072
1247
|
overflow: hidden;
|
|
1248
|
+
min-width: 0;
|
|
1249
|
+
padding-right: var(--scrollbar, 0px);
|
|
1073
1250
|
}
|
|
1074
1251
|
|
|
1075
1252
|
.headers > tr > .column {
|
|
@@ -1082,13 +1259,30 @@
|
|
|
1082
1259
|
border-left: 1px solid var(--tably-border-grid);
|
|
1083
1260
|
}
|
|
1084
1261
|
|
|
1262
|
+
.headers > tr > .context-col {
|
|
1263
|
+
background-color: var(--tably-bg);
|
|
1264
|
+
}
|
|
1265
|
+
|
|
1085
1266
|
.content {
|
|
1086
|
-
display:
|
|
1087
|
-
|
|
1267
|
+
display: flex;
|
|
1268
|
+
flex-direction: column;
|
|
1269
|
+
min-width: 0;
|
|
1270
|
+
min-height: 0;
|
|
1088
1271
|
|
|
1089
1272
|
grid-area: rows;
|
|
1090
1273
|
scrollbar-width: thin;
|
|
1091
|
-
overflow: auto;
|
|
1274
|
+
overflow-x: auto;
|
|
1275
|
+
overflow-y: scroll;
|
|
1276
|
+
}
|
|
1277
|
+
|
|
1278
|
+
.content > tr.row,
|
|
1279
|
+
.content > tr.expandable {
|
|
1280
|
+
flex: 0 0 auto;
|
|
1281
|
+
}
|
|
1282
|
+
|
|
1283
|
+
.content > tr.filler {
|
|
1284
|
+
flex: 1 0 0px;
|
|
1285
|
+
min-height: 0;
|
|
1092
1286
|
}
|
|
1093
1287
|
|
|
1094
1288
|
.statusbar {
|
|
@@ -1096,19 +1290,30 @@
|
|
|
1096
1290
|
grid-area: statusbar;
|
|
1097
1291
|
overflow: hidden;
|
|
1098
1292
|
background-color: var(--tably-statusbar);
|
|
1293
|
+
min-width: 0;
|
|
1294
|
+
padding-right: var(--scrollbar, 0px);
|
|
1099
1295
|
}
|
|
1100
1296
|
|
|
1101
1297
|
.statusbar > tr > .column {
|
|
1102
1298
|
border-top: 1px solid var(--tably-border);
|
|
1103
1299
|
padding: calc(var(--tably-padding-y) / 2) 0;
|
|
1104
1300
|
}
|
|
1301
|
+
.statusbar > tr > .context-col {
|
|
1302
|
+
border-top: 1px solid var(--tably-border);
|
|
1303
|
+
border-left: 1px solid var(--tably-border-grid);
|
|
1304
|
+
}
|
|
1305
|
+
|
|
1306
|
+
.statusbar > tr > .context-col {
|
|
1307
|
+
background-color: var(--tably-statusbar);
|
|
1308
|
+
}
|
|
1105
1309
|
|
|
1106
1310
|
.headers > tr,
|
|
1107
1311
|
.row,
|
|
1312
|
+
.expandable,
|
|
1313
|
+
.filler,
|
|
1108
1314
|
.statusbar > tr {
|
|
1109
1315
|
display: grid;
|
|
1110
1316
|
width: 100%;
|
|
1111
|
-
height: 100%;
|
|
1112
1317
|
min-width: max-content;
|
|
1113
1318
|
|
|
1114
1319
|
& > .column {
|
|
@@ -1145,11 +1350,30 @@
|
|
|
1145
1350
|
border-top: 2px solid var(--tably-border-grid);
|
|
1146
1351
|
}
|
|
1147
1352
|
|
|
1148
|
-
.row >
|
|
1353
|
+
.row > *,
|
|
1354
|
+
.filler > * {
|
|
1149
1355
|
border-left: 1px solid var(--tably-border-grid);
|
|
1150
1356
|
border-bottom: 1px solid var(--tably-border-grid);
|
|
1151
1357
|
}
|
|
1152
1358
|
|
|
1359
|
+
.filler {
|
|
1360
|
+
pointer-events: none;
|
|
1361
|
+
user-select: none;
|
|
1362
|
+
background: none;
|
|
1363
|
+
}
|
|
1364
|
+
|
|
1365
|
+
.sr-only {
|
|
1366
|
+
position: absolute;
|
|
1367
|
+
width: 1px;
|
|
1368
|
+
height: 1px;
|
|
1369
|
+
padding: 0;
|
|
1370
|
+
margin: -1px;
|
|
1371
|
+
overflow: hidden;
|
|
1372
|
+
clip: rect(0, 0, 0, 0);
|
|
1373
|
+
white-space: nowrap;
|
|
1374
|
+
border: 0;
|
|
1375
|
+
}
|
|
1376
|
+
|
|
1153
1377
|
.panel {
|
|
1154
1378
|
position: relative;
|
|
1155
1379
|
grid-area: panel;
|