svelte-tably 1.0.0-next.10 → 1.0.0-next.11
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 +1 -1
- package/dist/Column.svelte +10 -0
- package/dist/Column.svelte.d.ts +6 -0
- package/dist/Table.svelte +89 -40
- package/dist/Table.svelte.d.ts +2 -7
- package/package.json +1 -1
package/README.md
CHANGED
package/dist/Column.svelte
CHANGED
|
@@ -80,6 +80,12 @@
|
|
|
80
80
|
*/
|
|
81
81
|
resizeable?: boolean
|
|
82
82
|
|
|
83
|
+
/**
|
|
84
|
+
*
|
|
85
|
+
* @example (value) => value.includes(search)
|
|
86
|
+
*/
|
|
87
|
+
filter?: (value: V) => boolean
|
|
88
|
+
|
|
83
89
|
/**
|
|
84
90
|
* Optional: Provide the table it is a part of
|
|
85
91
|
*/
|
|
@@ -93,6 +99,7 @@
|
|
|
93
99
|
statusbar?: C['statusbar']
|
|
94
100
|
|
|
95
101
|
fixed?: boolean
|
|
102
|
+
filter?: C['filter']
|
|
96
103
|
|
|
97
104
|
/** Default options for initial table */
|
|
98
105
|
defaults: {
|
|
@@ -133,6 +140,8 @@
|
|
|
133
140
|
value,
|
|
134
141
|
sort,
|
|
135
142
|
|
|
143
|
+
filter,
|
|
144
|
+
|
|
136
145
|
table
|
|
137
146
|
}: ColumnProps<T, V> = $props()
|
|
138
147
|
|
|
@@ -142,6 +151,7 @@
|
|
|
142
151
|
row,
|
|
143
152
|
statusbar,
|
|
144
153
|
fixed,
|
|
154
|
+
filter,
|
|
145
155
|
defaults: {
|
|
146
156
|
sticky,
|
|
147
157
|
sort: sortby,
|
package/dist/Column.svelte.d.ts
CHANGED
|
@@ -62,6 +62,11 @@ export interface ColumnProps<T, V> {
|
|
|
62
62
|
* @default true
|
|
63
63
|
*/
|
|
64
64
|
resizeable?: boolean;
|
|
65
|
+
/**
|
|
66
|
+
*
|
|
67
|
+
* @example (value) => value.includes(search)
|
|
68
|
+
*/
|
|
69
|
+
filter?: (value: V) => boolean;
|
|
65
70
|
/**
|
|
66
71
|
* Optional: Provide the table it is a part of
|
|
67
72
|
*/
|
|
@@ -73,6 +78,7 @@ export interface ColumnState<T = unknown, V = unknown, C extends ColumnProps<T,
|
|
|
73
78
|
row: C['row'];
|
|
74
79
|
statusbar?: C['statusbar'];
|
|
75
80
|
fixed?: boolean;
|
|
81
|
+
filter?: C['filter'];
|
|
76
82
|
/** Default options for initial table */
|
|
77
83
|
defaults: {
|
|
78
84
|
sticky?: boolean;
|
package/dist/Table.svelte
CHANGED
|
@@ -60,6 +60,7 @@
|
|
|
60
60
|
import { fly } from 'svelte/transition'
|
|
61
61
|
import { sineInOut } from 'svelte/easing'
|
|
62
62
|
import { on } from 'svelte/events'
|
|
63
|
+
import { Trigger } from './trigger.svelte.js'
|
|
63
64
|
|
|
64
65
|
type T = $$Generic<Record<PropertyKey, unknown>>
|
|
65
66
|
|
|
@@ -89,6 +90,8 @@
|
|
|
89
90
|
*/
|
|
90
91
|
resizeable?: boolean
|
|
91
92
|
|
|
93
|
+
filters?: ((item: T) => boolean)[]
|
|
94
|
+
|
|
92
95
|
selected?: T[]
|
|
93
96
|
select?:
|
|
94
97
|
| boolean
|
|
@@ -151,20 +154,35 @@
|
|
|
151
154
|
id = Array.from({ length: 12 }, () => String.fromCharCode(Math.floor(Math.random() * 26) + 97)).join(''),
|
|
152
155
|
href,
|
|
153
156
|
resizeable = true,
|
|
154
|
-
select
|
|
157
|
+
select,
|
|
158
|
+
filters: _filters = []
|
|
155
159
|
}: Props = $props()
|
|
156
160
|
|
|
157
161
|
let mounted = $state(false)
|
|
158
162
|
onMount(() => (mounted = true))
|
|
159
163
|
|
|
160
|
-
|
|
164
|
+
let cols: TableState<T>['columns'] = $state({})
|
|
165
|
+
|
|
166
|
+
let sortedData = $state([]) as T[]
|
|
167
|
+
let data = $state([]) as T[]
|
|
168
|
+
$effect(() => {
|
|
169
|
+
const filters = [..._filters] as ((item: T) => boolean)[]
|
|
170
|
+
for(const key in cols) {
|
|
171
|
+
const filter = table.columns[key].filter
|
|
172
|
+
const valueOf = table.columns[key].options.value
|
|
173
|
+
if(filter && valueOf) {
|
|
174
|
+
filters.push((item) => filter(valueOf(item)))
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
data = filters.length === 0 ? [...sortedData] : sortedData.filter((value) => filters.every((filter) => filter(value)))
|
|
179
|
+
})
|
|
161
180
|
|
|
162
181
|
const elements = $state({}) as Record<
|
|
163
182
|
'headers' | 'statusbar' | 'rows' | 'virtualTop' | 'virtualBottom' | 'selects',
|
|
164
183
|
HTMLElement
|
|
165
184
|
>
|
|
166
|
-
|
|
167
|
-
let cols: TableState<T>['columns'] = $state({})
|
|
185
|
+
|
|
168
186
|
let positions: TableState<T>['positions'] = $state({
|
|
169
187
|
fixed: [],
|
|
170
188
|
sticky: [],
|
|
@@ -226,32 +244,57 @@
|
|
|
226
244
|
// #region Virtualization
|
|
227
245
|
let scrollTop = $state(0)
|
|
228
246
|
let viewportHeight = $state(0)
|
|
229
|
-
|
|
247
|
+
let topIndex = 0
|
|
230
248
|
let heightPerItem = $state(8)
|
|
249
|
+
let virtualTop = $state(0)
|
|
250
|
+
let virtualBottom = $state(0)
|
|
251
|
+
/** The area of data being rendered */
|
|
252
|
+
let area = $state([]) as T[]
|
|
231
253
|
|
|
232
254
|
const spacing = () => viewportHeight / 2
|
|
233
255
|
|
|
234
|
-
let
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
let virtualBottom = $derived.by(() => {
|
|
240
|
-
let result = heightPerItem * data.length - virtualTop - spacing() * 4
|
|
241
|
-
result = Math.max(result, 0)
|
|
242
|
-
return result
|
|
256
|
+
let renderItemLength = $derived(Math.ceil(Math.max(30, (viewportHeight / heightPerItem) * 2)))
|
|
257
|
+
|
|
258
|
+
$effect(() => {
|
|
259
|
+
data
|
|
260
|
+
untrack(calculateHeightPerItem)
|
|
243
261
|
})
|
|
244
262
|
|
|
245
|
-
let
|
|
263
|
+
let waitAnimationFrame = false
|
|
246
264
|
|
|
247
|
-
|
|
248
|
-
|
|
265
|
+
$effect(() => {
|
|
266
|
+
scrollTop
|
|
267
|
+
heightPerItem
|
|
268
|
+
data.length
|
|
269
|
+
data
|
|
270
|
+
untrack(() => {
|
|
271
|
+
if(!waitAnimationFrame) {
|
|
272
|
+
setTimeout(() => {
|
|
273
|
+
waitAnimationFrame = false
|
|
274
|
+
|
|
275
|
+
virtualTop = Math.max(scrollTop - spacing(), 0)
|
|
276
|
+
virtualTop -= virtualTop % heightPerItem
|
|
277
|
+
|
|
278
|
+
virtualBottom = heightPerItem * data.length - virtualTop - spacing() * 4
|
|
279
|
+
virtualBottom = Math.max(virtualBottom, 0)
|
|
280
|
+
}, 1000 / 60)
|
|
281
|
+
}
|
|
282
|
+
waitAnimationFrame = true
|
|
283
|
+
})
|
|
284
|
+
})
|
|
285
|
+
|
|
286
|
+
$effect(() => {
|
|
249
287
|
table.sortReverse
|
|
250
288
|
table.sortby
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
289
|
+
heightPerItem
|
|
290
|
+
virtualTop
|
|
291
|
+
data.length
|
|
292
|
+
data
|
|
293
|
+
untrack(() => {
|
|
294
|
+
topIndex = Math.round(virtualTop / heightPerItem || 0)
|
|
295
|
+
const end = topIndex + renderItemLength
|
|
296
|
+
area = data.slice(topIndex, end)
|
|
297
|
+
})
|
|
255
298
|
})
|
|
256
299
|
|
|
257
300
|
function calculateHeightPerItem() {
|
|
@@ -260,22 +303,20 @@
|
|
|
260
303
|
return
|
|
261
304
|
}
|
|
262
305
|
tick().then(() => {
|
|
263
|
-
|
|
306
|
+
if(elements.rows.children.length === 0) return
|
|
307
|
+
const firstRow = elements.rows.children[0]?.getBoundingClientRect().top
|
|
264
308
|
const lastRow =
|
|
265
309
|
elements.rows.children[elements.rows.children.length - 1].getBoundingClientRect().bottom
|
|
266
310
|
heightPerItem = (lastRow - firstRow) / area.length
|
|
267
311
|
})
|
|
268
312
|
}
|
|
269
313
|
|
|
270
|
-
|
|
271
|
-
data
|
|
272
|
-
untrack(calculateHeightPerItem)
|
|
273
|
-
})
|
|
314
|
+
|
|
274
315
|
// #endregion
|
|
275
316
|
// * --- Virtualization --- *
|
|
276
|
-
|
|
277
317
|
|
|
278
|
-
|
|
318
|
+
// * --- Sorting --- *
|
|
319
|
+
// #region sorting
|
|
279
320
|
function sortBy(column: string) {
|
|
280
321
|
const { sorting, value } = table.columns[column]!.options
|
|
281
322
|
if(!sorting || !value) return
|
|
@@ -293,26 +334,34 @@
|
|
|
293
334
|
}
|
|
294
335
|
|
|
295
336
|
function sortTable() {
|
|
296
|
-
|
|
337
|
+
sortedData = [..._data]
|
|
338
|
+
if (!table.sortby) {
|
|
339
|
+
return
|
|
340
|
+
}
|
|
297
341
|
const column = table.columns[table.sortby]
|
|
298
342
|
let { sorting, value } = column.options
|
|
299
|
-
if(!sorting || !value)
|
|
343
|
+
if(!sorting || !value) {
|
|
344
|
+
return
|
|
345
|
+
}
|
|
300
346
|
if(sorting === true) {
|
|
301
347
|
sorting = (a, b) => String(a).localeCompare(String(b))
|
|
302
348
|
}
|
|
303
349
|
if(table.sortReverse) {
|
|
304
|
-
|
|
350
|
+
sortedData.sort((a, b) => sorting(value(b), value(a)))
|
|
305
351
|
} else {
|
|
306
|
-
|
|
352
|
+
sortedData.sort((a, b) => sorting(value(a), value(b)))
|
|
307
353
|
}
|
|
308
354
|
}
|
|
309
355
|
|
|
310
356
|
$effect.pre(() => {
|
|
311
|
-
|
|
357
|
+
_data
|
|
358
|
+
_data.length
|
|
312
359
|
table.sortby
|
|
313
360
|
table.sortReverse
|
|
314
361
|
untrack(sortTable)
|
|
315
362
|
})
|
|
363
|
+
// #endregion
|
|
364
|
+
// * --- Sorting --- *
|
|
316
365
|
|
|
317
366
|
const panelTween = new PanelTween(() => panel, 24)
|
|
318
367
|
|
|
@@ -523,16 +572,16 @@
|
|
|
523
572
|
<tbody class="content" bind:this={elements.rows} onscrollcapture={onscroll} bind:clientHeight={viewportHeight}>
|
|
524
573
|
{#each area as item, i (item)}
|
|
525
574
|
{@const props = table.href ? { href: table.href(item) } : {}}
|
|
526
|
-
{@const index =
|
|
575
|
+
{@const index = i + topIndex}
|
|
527
576
|
<svelte:element
|
|
528
577
|
this={table.href ? 'a' : 'tr'}
|
|
578
|
+
aria-rowindex="{index+1}"
|
|
529
579
|
class="row"
|
|
530
580
|
class:hover={hoveredRow === item}
|
|
531
581
|
class:selected={table.selected?.includes(item)}
|
|
532
582
|
class:first={i === 0}
|
|
533
583
|
class:last={i === area.length - 1}
|
|
534
584
|
{...props}
|
|
535
|
-
aria-rowindex={index}
|
|
536
585
|
onpointerenter={() => (hoveredRow = item)}
|
|
537
586
|
onpointerleave={() => (hoveredRow = null)}
|
|
538
587
|
>
|
|
@@ -544,7 +593,7 @@
|
|
|
544
593
|
item,
|
|
545
594
|
{
|
|
546
595
|
get index() {
|
|
547
|
-
return index
|
|
596
|
+
return index
|
|
548
597
|
},
|
|
549
598
|
get value() {
|
|
550
599
|
return col.options.value ? col.options.value(item) : undefined
|
|
@@ -556,9 +605,9 @@
|
|
|
556
605
|
return table.selected?.includes(item)
|
|
557
606
|
},
|
|
558
607
|
set selected(value) {
|
|
559
|
-
value
|
|
560
|
-
table.selected!.push(item)
|
|
561
|
-
|
|
608
|
+
value
|
|
609
|
+
? table.selected!.push(item)
|
|
610
|
+
: table.selected!.splice(table.selected!.indexOf(item), 1)
|
|
562
611
|
}
|
|
563
612
|
}
|
|
564
613
|
]
|
|
@@ -623,7 +672,7 @@
|
|
|
623
672
|
<div class="__fixed">
|
|
624
673
|
{@render headerSnippet({
|
|
625
674
|
get isSelected() {
|
|
626
|
-
return table.data.length === table.selected?.length
|
|
675
|
+
return table.data.length === table.selected?.length && table.data.length > 0
|
|
627
676
|
},
|
|
628
677
|
set isSelected(value) {
|
|
629
678
|
if (value) {
|
package/dist/Table.svelte.d.ts
CHANGED
|
@@ -59,6 +59,7 @@ declare class __sveltets_Render<T extends Record<PropertyKey, unknown>> {
|
|
|
59
59
|
* @default true
|
|
60
60
|
*/
|
|
61
61
|
resizeable?: boolean;
|
|
62
|
+
filters?: ((item: T) => boolean)[] | undefined;
|
|
62
63
|
selected?: T[] | undefined;
|
|
63
64
|
select?: boolean | {
|
|
64
65
|
/**
|
|
@@ -85,13 +86,7 @@ declare class __sveltets_Render<T extends Record<PropertyKey, unknown>> {
|
|
|
85
86
|
events(): {};
|
|
86
87
|
slots(): {};
|
|
87
88
|
bindings(): "selected" | "panel";
|
|
88
|
-
exports(): {
|
|
89
|
-
selected: T[];
|
|
90
|
-
positions: TableState<T_1>["positions"];
|
|
91
|
-
data: T[];
|
|
92
|
-
href: ((item: T) => string) | undefined;
|
|
93
|
-
columns: Record<string, ColumnState<T, unknown, ColumnProps<T, unknown>>>;
|
|
94
|
-
};
|
|
89
|
+
exports(): {};
|
|
95
90
|
}
|
|
96
91
|
interface $$IsomorphicComponent {
|
|
97
92
|
new <T extends Record<PropertyKey, unknown>>(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']>> & {
|