@smartnet360/svelte-components 0.0.103 → 0.0.104
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/dist/apps/site-check/SiteCheck.svelte +11 -77
- package/dist/apps/site-check/SiteCheckControls.svelte +0 -7
- package/dist/apps/site-check/helper.js +0 -33
- package/dist/apps/site-check/transforms.js +15 -65
- package/dist/core/CellTable/CellTable.svelte +456 -0
- package/dist/core/CellTable/CellTable.svelte.d.ts +27 -0
- package/dist/core/CellTable/CellTablePanel.svelte +211 -0
- package/dist/core/CellTable/CellTablePanel.svelte.d.ts +49 -0
- package/dist/core/CellTable/CellTableToolbar.svelte +218 -0
- package/dist/core/CellTable/CellTableToolbar.svelte.d.ts +32 -0
- package/dist/core/CellTable/column-config.d.ts +63 -0
- package/dist/core/CellTable/column-config.js +465 -0
- package/dist/core/CellTable/index.d.ts +10 -0
- package/dist/core/CellTable/index.js +11 -0
- package/dist/core/CellTable/types.d.ts +166 -0
- package/dist/core/CellTable/types.js +6 -0
- package/dist/core/Charts/ChartCard.svelte +0 -23
- package/dist/core/Charts/ChartComponent.svelte +0 -25
- package/dist/core/Charts/data-processor.js +1 -19
- package/dist/core/index.d.ts +1 -0
- package/dist/core/index.js +2 -0
- package/package.json +3 -2
- package/dist/apps/site-check/transforms-old.d.ts +0 -56
- package/dist/apps/site-check/transforms-old.js +0 -273
|
@@ -0,0 +1,456 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { onMount, onDestroy } from 'svelte';
|
|
3
|
+
import { TabulatorFull as Tabulator } from 'tabulator-tables';
|
|
4
|
+
// Import Bootstrap 5 theme for proper styling
|
|
5
|
+
import 'tabulator-tables/dist/css/tabulator_bootstrap5.min.css';
|
|
6
|
+
import type { Options, RowComponent } from 'tabulator-tables';
|
|
7
|
+
import type {
|
|
8
|
+
CellTableProps,
|
|
9
|
+
CellData,
|
|
10
|
+
RowSelectionEvent,
|
|
11
|
+
RowClickEvent,
|
|
12
|
+
RowDblClickEvent,
|
|
13
|
+
DataChangeEvent
|
|
14
|
+
} from './types';
|
|
15
|
+
import {
|
|
16
|
+
getColumnsForPreset,
|
|
17
|
+
getGroupHeaderFormatter,
|
|
18
|
+
DEFAULT_TECH_COLORS,
|
|
19
|
+
DEFAULT_STATUS_COLORS
|
|
20
|
+
} from './column-config';
|
|
21
|
+
|
|
22
|
+
interface Props extends CellTableProps {
|
|
23
|
+
/** Row selection change event */
|
|
24
|
+
onselectionchange?: (event: RowSelectionEvent) => void;
|
|
25
|
+
/** Row click event */
|
|
26
|
+
onrowclick?: (event: RowClickEvent) => void;
|
|
27
|
+
/** Row double-click event */
|
|
28
|
+
onrowdblclick?: (event: RowDblClickEvent) => void;
|
|
29
|
+
/** Data change event (filter, sort, etc.) */
|
|
30
|
+
ondatachange?: (event: DataChangeEvent) => void;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
let {
|
|
34
|
+
cells = [],
|
|
35
|
+
groupBy = 'none',
|
|
36
|
+
columnPreset = 'default',
|
|
37
|
+
columnVisibility,
|
|
38
|
+
selectable = false,
|
|
39
|
+
multiSelect = true,
|
|
40
|
+
height = '100%',
|
|
41
|
+
virtualDom = true,
|
|
42
|
+
tabulatorOptions,
|
|
43
|
+
techColors = DEFAULT_TECH_COLORS,
|
|
44
|
+
statusColors = DEFAULT_STATUS_COLORS,
|
|
45
|
+
headerFilters = true,
|
|
46
|
+
resizableColumns = true,
|
|
47
|
+
movableColumns = true,
|
|
48
|
+
persistLayout = false,
|
|
49
|
+
storageKey = 'cell-table-layout',
|
|
50
|
+
onselectionchange,
|
|
51
|
+
onrowclick,
|
|
52
|
+
onrowdblclick,
|
|
53
|
+
ondatachange
|
|
54
|
+
}: Props = $props();
|
|
55
|
+
|
|
56
|
+
let tableContainer: HTMLDivElement;
|
|
57
|
+
let table: Tabulator | null = null;
|
|
58
|
+
let isInitialized = $state(false);
|
|
59
|
+
|
|
60
|
+
// Reactive column configuration
|
|
61
|
+
let columns = $derived(getColumnsForPreset(columnPreset, techColors, statusColors, headerFilters));
|
|
62
|
+
|
|
63
|
+
// Build Tabulator options
|
|
64
|
+
function buildOptions(): Options {
|
|
65
|
+
const baseOptions: Options = {
|
|
66
|
+
data: cells,
|
|
67
|
+
columns: columns,
|
|
68
|
+
layout: 'fitDataFill',
|
|
69
|
+
height: height,
|
|
70
|
+
placeholder: 'No cells to display',
|
|
71
|
+
|
|
72
|
+
// Virtual DOM for performance with large datasets
|
|
73
|
+
renderVertical: virtualDom ? 'virtual' : 'basic',
|
|
74
|
+
|
|
75
|
+
// Interactivity
|
|
76
|
+
resizableColumns: resizableColumns,
|
|
77
|
+
movableColumns: movableColumns,
|
|
78
|
+
|
|
79
|
+
// Sorting
|
|
80
|
+
initialSort: [{ column: 'siteId', dir: 'asc' }],
|
|
81
|
+
|
|
82
|
+
// Row selection
|
|
83
|
+
selectable: selectable ? (multiSelect ? true : 1) : false,
|
|
84
|
+
selectableRangeMode: 'click',
|
|
85
|
+
|
|
86
|
+
// Persistence
|
|
87
|
+
persistence: persistLayout ? {
|
|
88
|
+
sort: true,
|
|
89
|
+
filter: true,
|
|
90
|
+
columns: ['width', 'visible'],
|
|
91
|
+
} : false,
|
|
92
|
+
persistenceID: persistLayout ? storageKey : undefined,
|
|
93
|
+
|
|
94
|
+
// Grouping
|
|
95
|
+
...(groupBy !== 'none' ? {
|
|
96
|
+
groupBy: groupBy,
|
|
97
|
+
groupStartOpen: true,
|
|
98
|
+
groupHeader: getGroupHeaderFormatter(groupBy),
|
|
99
|
+
groupToggleElement: 'header',
|
|
100
|
+
} : {}),
|
|
101
|
+
|
|
102
|
+
// Pagination (optional, disabled by default for virtual scrolling)
|
|
103
|
+
// pagination: true,
|
|
104
|
+
// paginationSize: 50,
|
|
105
|
+
|
|
106
|
+
// Clipboard
|
|
107
|
+
clipboard: true,
|
|
108
|
+
clipboardCopyRowRange: 'selected',
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
// Merge with custom options
|
|
112
|
+
return { ...baseOptions, ...tabulatorOptions };
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Initialize table
|
|
116
|
+
function initTable() {
|
|
117
|
+
if (!tableContainer) return;
|
|
118
|
+
|
|
119
|
+
const options = buildOptions();
|
|
120
|
+
table = new Tabulator(tableContainer, options);
|
|
121
|
+
|
|
122
|
+
// Bind events
|
|
123
|
+
table.on('rowClick', (e, row) => {
|
|
124
|
+
if (onrowclick) {
|
|
125
|
+
onrowclick({
|
|
126
|
+
row: (row as RowComponent).getData() as CellData,
|
|
127
|
+
event: e as MouseEvent
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
table.on('rowDblClick', (e, row) => {
|
|
133
|
+
if (onrowdblclick) {
|
|
134
|
+
onrowdblclick({
|
|
135
|
+
row: (row as RowComponent).getData() as CellData,
|
|
136
|
+
event: e as MouseEvent
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
table.on('rowSelectionChanged', (data, rows) => {
|
|
142
|
+
if (onselectionchange) {
|
|
143
|
+
const cellData = data as CellData[];
|
|
144
|
+
onselectionchange({
|
|
145
|
+
rows: cellData,
|
|
146
|
+
ids: cellData.map(d => d.id)
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
table.on('dataFiltered', (filters, rows) => {
|
|
152
|
+
if (ondatachange) {
|
|
153
|
+
ondatachange({
|
|
154
|
+
type: 'filter',
|
|
155
|
+
rowCount: cells.length,
|
|
156
|
+
filteredCount: (rows as RowComponent[]).length
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
table.on('dataSorted', () => {
|
|
162
|
+
if (ondatachange) {
|
|
163
|
+
ondatachange({
|
|
164
|
+
type: 'sort',
|
|
165
|
+
rowCount: cells.length,
|
|
166
|
+
filteredCount: table?.getDataCount('active') ?? 0
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
// Mark as initialized after table is ready
|
|
172
|
+
table.on('tableBuilt', () => {
|
|
173
|
+
isInitialized = true;
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// Update table data when cells change
|
|
178
|
+
$effect(() => {
|
|
179
|
+
if (isInitialized && table && cells) {
|
|
180
|
+
table.replaceData(cells);
|
|
181
|
+
if (ondatachange) {
|
|
182
|
+
ondatachange({
|
|
183
|
+
type: 'load',
|
|
184
|
+
rowCount: cells.length,
|
|
185
|
+
filteredCount: cells.length
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
// Update grouping when groupBy changes
|
|
192
|
+
$effect(() => {
|
|
193
|
+
if (isInitialized && table) {
|
|
194
|
+
if (groupBy === 'none') {
|
|
195
|
+
table.setGroupBy(false);
|
|
196
|
+
} else {
|
|
197
|
+
table.setGroupBy(groupBy);
|
|
198
|
+
table.setGroupHeader(getGroupHeaderFormatter(groupBy));
|
|
199
|
+
}
|
|
200
|
+
// Force redraw after grouping change
|
|
201
|
+
table.redraw(true);
|
|
202
|
+
}
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
// Update columns when preset changes
|
|
206
|
+
$effect(() => {
|
|
207
|
+
if (isInitialized && table && columns) {
|
|
208
|
+
table.setColumns(columns);
|
|
209
|
+
table.redraw(true);
|
|
210
|
+
}
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
onMount(() => {
|
|
214
|
+
initTable();
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
onDestroy(() => {
|
|
218
|
+
if (table) {
|
|
219
|
+
table.destroy();
|
|
220
|
+
table = null;
|
|
221
|
+
}
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
// Public API methods
|
|
225
|
+
export function getTable(): Tabulator | null {
|
|
226
|
+
return table;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
export function getSelectedRows(): CellData[] {
|
|
230
|
+
return table?.getSelectedData() as CellData[] ?? [];
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
export function clearSelection(): void {
|
|
234
|
+
table?.deselectRow();
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
export function selectRow(id: string): void {
|
|
238
|
+
const row = table?.getRow(id);
|
|
239
|
+
if (row) row.select();
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
export function scrollToRow(id: string): void {
|
|
243
|
+
table?.scrollToRow(id, 'top', true);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
export function downloadCSV(filename: string = 'cells.csv'): void {
|
|
247
|
+
table?.download('csv', filename);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
export function downloadJSON(filename: string = 'cells.json'): void {
|
|
251
|
+
table?.download('json', filename);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
export function setFilter(field: string, type: string, value: unknown): void {
|
|
255
|
+
table?.setFilter(field, type as any, value);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
export function clearFilters(): void {
|
|
259
|
+
table?.clearFilter();
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
export function redraw(): void {
|
|
263
|
+
table?.redraw(true);
|
|
264
|
+
}
|
|
265
|
+
</script>
|
|
266
|
+
|
|
267
|
+
<div class="cell-table-container">
|
|
268
|
+
<div bind:this={tableContainer} class="cell-table"></div>
|
|
269
|
+
</div>
|
|
270
|
+
|
|
271
|
+
<style>
|
|
272
|
+
.cell-table-container {
|
|
273
|
+
width: 100%;
|
|
274
|
+
height: 100%;
|
|
275
|
+
display: flex;
|
|
276
|
+
flex-direction: column;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
.cell-table {
|
|
280
|
+
flex: 1;
|
|
281
|
+
min-height: 0;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/* Bootstrap-aligned Tabulator theme overrides */
|
|
285
|
+
:global(.cell-table-container .tabulator) {
|
|
286
|
+
font-family: var(--bs-body-font-family, system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif);
|
|
287
|
+
font-size: 0.875rem;
|
|
288
|
+
border: 1px solid var(--bs-border-color, #dee2e6);
|
|
289
|
+
border-radius: var(--bs-border-radius, 0.375rem);
|
|
290
|
+
background-color: var(--bs-body-bg, #fff);
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
/* Header styling */
|
|
294
|
+
:global(.cell-table-container .tabulator-header) {
|
|
295
|
+
background-color: var(--bs-tertiary-bg, #f8f9fa);
|
|
296
|
+
border-bottom: 2px solid var(--bs-border-color, #dee2e6);
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
:global(.cell-table-container .tabulator-col) {
|
|
300
|
+
background-color: var(--bs-tertiary-bg, #f8f9fa);
|
|
301
|
+
border-right: 1px solid var(--bs-border-color, #dee2e6);
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
:global(.cell-table-container .tabulator-col-title) {
|
|
305
|
+
font-weight: 600;
|
|
306
|
+
color: var(--bs-body-color, #212529);
|
|
307
|
+
padding: 0.5rem 0.75rem;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
:global(.cell-table-container .tabulator-col-sorter) {
|
|
311
|
+
color: var(--bs-secondary-color, #6c757d);
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
/* Header filter inputs */
|
|
315
|
+
:global(.cell-table-container .tabulator-header-filter input) {
|
|
316
|
+
font-size: 0.75rem;
|
|
317
|
+
padding: 0.25rem 0.5rem;
|
|
318
|
+
border: 1px solid var(--bs-border-color, #dee2e6);
|
|
319
|
+
border-radius: var(--bs-border-radius-sm, 0.25rem);
|
|
320
|
+
background-color: var(--bs-body-bg, #fff);
|
|
321
|
+
color: var(--bs-body-color, #212529);
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
:global(.cell-table-container .tabulator-header-filter input:focus) {
|
|
325
|
+
border-color: var(--bs-primary, #0d6efd);
|
|
326
|
+
outline: 0;
|
|
327
|
+
box-shadow: 0 0 0 0.2rem rgba(13, 110, 253, 0.25);
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
/* Row styling */
|
|
331
|
+
:global(.cell-table-container .tabulator-row) {
|
|
332
|
+
border-bottom: 1px solid var(--bs-border-color-translucent, rgba(0, 0, 0, 0.1));
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
:global(.cell-table-container .tabulator-row:hover) {
|
|
336
|
+
background-color: var(--bs-tertiary-bg, #f8f9fa);
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
:global(.cell-table-container .tabulator-row.tabulator-row-even) {
|
|
340
|
+
background-color: var(--bs-body-bg, #fff);
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
:global(.cell-table-container .tabulator-row.tabulator-row-odd) {
|
|
344
|
+
background-color: rgba(var(--bs-tertiary-bg-rgb, 248, 249, 250), 0.5);
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
:global(.cell-table-container .tabulator-row.tabulator-selected) {
|
|
348
|
+
background-color: rgba(var(--bs-primary-rgb, 13, 110, 253), 0.1);
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
:global(.cell-table-container .tabulator-row.tabulator-selected:hover) {
|
|
352
|
+
background-color: rgba(var(--bs-primary-rgb, 13, 110, 253), 0.15);
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
/* Cell styling */
|
|
356
|
+
:global(.cell-table-container .tabulator-cell) {
|
|
357
|
+
padding: 0.5rem 0.75rem;
|
|
358
|
+
border-right: 1px solid var(--bs-border-color-translucent, rgba(0, 0, 0, 0.05));
|
|
359
|
+
color: var(--bs-body-color, #212529);
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
:global(.cell-table-container .tabulator-cell.tabulator-frozen) {
|
|
363
|
+
background-color: var(--bs-tertiary-bg, #f8f9fa);
|
|
364
|
+
border-right: 2px solid var(--bs-border-color, #dee2e6);
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
/* Group header styling */
|
|
368
|
+
:global(.cell-table-container .tabulator-row.tabulator-group) {
|
|
369
|
+
background-color: var(--bs-secondary-bg, #e9ecef);
|
|
370
|
+
border-bottom: 1px solid var(--bs-border-color, #dee2e6);
|
|
371
|
+
font-weight: 500;
|
|
372
|
+
min-height: 36px;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
:global(.cell-table-container .tabulator-row.tabulator-group:hover) {
|
|
376
|
+
background-color: var(--bs-secondary-bg, #e9ecef);
|
|
377
|
+
cursor: pointer;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
:global(.cell-table-container .tabulator-row.tabulator-group span) {
|
|
381
|
+
color: var(--bs-body-color, #212529);
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
/* Group toggle arrow */
|
|
385
|
+
:global(.cell-table-container .tabulator-group-toggle) {
|
|
386
|
+
margin-right: 0.5rem;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
/* Scrollbar styling */
|
|
390
|
+
:global(.cell-table-container .tabulator-tableholder::-webkit-scrollbar) {
|
|
391
|
+
width: 8px;
|
|
392
|
+
height: 8px;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
:global(.cell-table-container .tabulator-tableholder::-webkit-scrollbar-track) {
|
|
396
|
+
background: var(--bs-tertiary-bg, #f8f9fa);
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
:global(.cell-table-container .tabulator-tableholder::-webkit-scrollbar-thumb) {
|
|
400
|
+
background: var(--bs-secondary-color, #6c757d);
|
|
401
|
+
border-radius: 4px;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
:global(.cell-table-container .tabulator-tableholder::-webkit-scrollbar-thumb:hover) {
|
|
405
|
+
background: var(--bs-body-color, #212529);
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
/* Placeholder */
|
|
409
|
+
:global(.cell-table-container .tabulator-placeholder) {
|
|
410
|
+
color: var(--bs-secondary-color, #6c757d);
|
|
411
|
+
font-style: italic;
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
/* Resize handle */
|
|
415
|
+
:global(.cell-table-container .tabulator-col-resize-handle) {
|
|
416
|
+
width: 6px;
|
|
417
|
+
right: 0;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
:global(.cell-table-container .tabulator-col-resize-handle:hover) {
|
|
421
|
+
background-color: var(--bs-primary, #0d6efd);
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
/* Footer/pagination (if enabled) */
|
|
425
|
+
:global(.cell-table-container .tabulator-footer) {
|
|
426
|
+
background-color: var(--bs-tertiary-bg, #f8f9fa);
|
|
427
|
+
border-top: 1px solid var(--bs-border-color, #dee2e6);
|
|
428
|
+
padding: 0.5rem;
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
:global(.cell-table-container .tabulator-page) {
|
|
432
|
+
padding: 0.25rem 0.5rem;
|
|
433
|
+
margin: 0 0.125rem;
|
|
434
|
+
border: 1px solid var(--bs-border-color, #dee2e6);
|
|
435
|
+
border-radius: var(--bs-border-radius-sm, 0.25rem);
|
|
436
|
+
background-color: var(--bs-body-bg, #fff);
|
|
437
|
+
color: var(--bs-body-color, #212529);
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
:global(.cell-table-container .tabulator-page:hover) {
|
|
441
|
+
background-color: var(--bs-tertiary-bg, #f8f9fa);
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
:global(.cell-table-container .tabulator-page.active) {
|
|
445
|
+
background-color: var(--bs-primary, #0d6efd);
|
|
446
|
+
border-color: var(--bs-primary, #0d6efd);
|
|
447
|
+
color: white;
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
/* Badge styling in cells */
|
|
451
|
+
:global(.cell-table-container .badge) {
|
|
452
|
+
font-weight: 500;
|
|
453
|
+
padding: 0.25em 0.5em;
|
|
454
|
+
border-radius: var(--bs-border-radius-sm, 0.25rem);
|
|
455
|
+
}
|
|
456
|
+
</style>
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { TabulatorFull as Tabulator } from 'tabulator-tables';
|
|
2
|
+
import 'tabulator-tables/dist/css/tabulator_bootstrap5.min.css';
|
|
3
|
+
import type { CellTableProps, CellData, RowSelectionEvent, RowClickEvent, RowDblClickEvent, DataChangeEvent } from './types';
|
|
4
|
+
interface Props extends CellTableProps {
|
|
5
|
+
/** Row selection change event */
|
|
6
|
+
onselectionchange?: (event: RowSelectionEvent) => void;
|
|
7
|
+
/** Row click event */
|
|
8
|
+
onrowclick?: (event: RowClickEvent) => void;
|
|
9
|
+
/** Row double-click event */
|
|
10
|
+
onrowdblclick?: (event: RowDblClickEvent) => void;
|
|
11
|
+
/** Data change event (filter, sort, etc.) */
|
|
12
|
+
ondatachange?: (event: DataChangeEvent) => void;
|
|
13
|
+
}
|
|
14
|
+
declare const CellTable: import("svelte").Component<Props, {
|
|
15
|
+
getTable: () => Tabulator | null;
|
|
16
|
+
getSelectedRows: () => CellData[];
|
|
17
|
+
clearSelection: () => void;
|
|
18
|
+
selectRow: (id: string) => void;
|
|
19
|
+
scrollToRow: (id: string) => void;
|
|
20
|
+
downloadCSV: (filename?: string) => void;
|
|
21
|
+
downloadJSON: (filename?: string) => void;
|
|
22
|
+
setFilter: (field: string, type: string, value: unknown) => void;
|
|
23
|
+
clearFilters: () => void;
|
|
24
|
+
redraw: () => void;
|
|
25
|
+
}, "">;
|
|
26
|
+
type CellTable = ReturnType<typeof CellTable>;
|
|
27
|
+
export default CellTable;
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Snippet } from 'svelte';
|
|
3
|
+
import CellTable from './CellTable.svelte';
|
|
4
|
+
import CellTableToolbar from './CellTableToolbar.svelte';
|
|
5
|
+
import type {
|
|
6
|
+
CellData,
|
|
7
|
+
CellTableGroupField,
|
|
8
|
+
ColumnPreset,
|
|
9
|
+
RowSelectionEvent,
|
|
10
|
+
RowClickEvent,
|
|
11
|
+
RowDblClickEvent,
|
|
12
|
+
DataChangeEvent,
|
|
13
|
+
TechColorMap,
|
|
14
|
+
StatusColorMap
|
|
15
|
+
} from './types';
|
|
16
|
+
|
|
17
|
+
interface Props {
|
|
18
|
+
/** Cell data array to display */
|
|
19
|
+
cells: CellData[];
|
|
20
|
+
/** Initial grouping field */
|
|
21
|
+
groupBy?: CellTableGroupField;
|
|
22
|
+
/** Initial column preset */
|
|
23
|
+
columnPreset?: ColumnPreset;
|
|
24
|
+
/** Enable row selection */
|
|
25
|
+
selectable?: boolean;
|
|
26
|
+
/** Enable multi-row selection */
|
|
27
|
+
multiSelect?: boolean;
|
|
28
|
+
/** Panel height (CSS value) */
|
|
29
|
+
height?: string;
|
|
30
|
+
/** Show toolbar */
|
|
31
|
+
showToolbar?: boolean;
|
|
32
|
+
/** Show export buttons */
|
|
33
|
+
showExport?: boolean;
|
|
34
|
+
/** Technology color mapping */
|
|
35
|
+
techColors?: TechColorMap;
|
|
36
|
+
/** Status color mapping */
|
|
37
|
+
statusColors?: StatusColorMap;
|
|
38
|
+
/** Enable header filters */
|
|
39
|
+
headerFilters?: boolean;
|
|
40
|
+
/** Panel title */
|
|
41
|
+
title?: string;
|
|
42
|
+
/** Row selection change event */
|
|
43
|
+
onselectionchange?: (event: RowSelectionEvent) => void;
|
|
44
|
+
/** Row click event */
|
|
45
|
+
onrowclick?: (event: RowClickEvent) => void;
|
|
46
|
+
/** Row double-click event */
|
|
47
|
+
onrowdblclick?: (event: RowDblClickEvent) => void;
|
|
48
|
+
/** Custom header actions slot */
|
|
49
|
+
headerActions?: Snippet;
|
|
50
|
+
/** Custom footer slot */
|
|
51
|
+
footer?: Snippet<[{ selectedRows: CellData[]; selectedCount: number }]>;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
let {
|
|
55
|
+
cells = [],
|
|
56
|
+
groupBy = $bindable('none'),
|
|
57
|
+
columnPreset = $bindable('default'),
|
|
58
|
+
selectable = true,
|
|
59
|
+
multiSelect = true,
|
|
60
|
+
height = '100%',
|
|
61
|
+
showToolbar = true,
|
|
62
|
+
showExport = true,
|
|
63
|
+
techColors,
|
|
64
|
+
statusColors,
|
|
65
|
+
headerFilters = true,
|
|
66
|
+
title = 'Cell Data',
|
|
67
|
+
onselectionchange,
|
|
68
|
+
onrowclick,
|
|
69
|
+
onrowdblclick,
|
|
70
|
+
headerActions,
|
|
71
|
+
footer
|
|
72
|
+
}: Props = $props();
|
|
73
|
+
|
|
74
|
+
let cellTable: CellTable;
|
|
75
|
+
let selectedCount = $state(0);
|
|
76
|
+
let selectedRows = $state<CellData[]>([]);
|
|
77
|
+
let filteredCount = $state(cells.length);
|
|
78
|
+
|
|
79
|
+
function handleSelectionChange(event: RowSelectionEvent) {
|
|
80
|
+
selectedCount = event.rows.length;
|
|
81
|
+
selectedRows = event.rows;
|
|
82
|
+
onselectionchange?.(event);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function handleDataChange(event: DataChangeEvent) {
|
|
86
|
+
filteredCount = event.filteredCount;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function handleGroupChange(group: CellTableGroupField) {
|
|
90
|
+
groupBy = group;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function handlePresetChange(preset: ColumnPreset) {
|
|
94
|
+
columnPreset = preset;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function handleExportCSV() {
|
|
98
|
+
cellTable?.downloadCSV(`cells-${new Date().toISOString().slice(0, 10)}.csv`);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function handleExportJSON() {
|
|
102
|
+
cellTable?.downloadJSON(`cells-${new Date().toISOString().slice(0, 10)}.json`);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function handleClearFilters() {
|
|
106
|
+
cellTable?.clearFilters();
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Expose table methods
|
|
110
|
+
export function getSelectedRows(): CellData[] {
|
|
111
|
+
return cellTable?.getSelectedRows() ?? [];
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export function clearSelection(): void {
|
|
115
|
+
cellTable?.clearSelection();
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export function scrollToRow(id: string): void {
|
|
119
|
+
cellTable?.scrollToRow(id);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export function redraw(): void {
|
|
123
|
+
cellTable?.redraw();
|
|
124
|
+
}
|
|
125
|
+
</script>
|
|
126
|
+
|
|
127
|
+
<div class="cell-table-panel d-flex flex-column" style:height>
|
|
128
|
+
<!-- Header -->
|
|
129
|
+
<div class="panel-header d-flex align-items-center justify-content-between px-3 py-2 bg-body-secondary border-bottom">
|
|
130
|
+
<h6 class="mb-0 d-flex align-items-center gap-2">
|
|
131
|
+
<i class="bi bi-table text-primary"></i>
|
|
132
|
+
{title}
|
|
133
|
+
</h6>
|
|
134
|
+
{#if headerActions}
|
|
135
|
+
<div class="header-actions">
|
|
136
|
+
{@render headerActions()}
|
|
137
|
+
</div>
|
|
138
|
+
{/if}
|
|
139
|
+
</div>
|
|
140
|
+
|
|
141
|
+
<!-- Toolbar -->
|
|
142
|
+
{#if showToolbar}
|
|
143
|
+
<CellTableToolbar
|
|
144
|
+
{groupBy}
|
|
145
|
+
{columnPreset}
|
|
146
|
+
totalCount={cells.length}
|
|
147
|
+
{filteredCount}
|
|
148
|
+
{selectedCount}
|
|
149
|
+
{showExport}
|
|
150
|
+
ongroupchange={handleGroupChange}
|
|
151
|
+
onpresetchange={handlePresetChange}
|
|
152
|
+
onexportcsv={handleExportCSV}
|
|
153
|
+
onexportjson={handleExportJSON}
|
|
154
|
+
onclearfilters={handleClearFilters}
|
|
155
|
+
/>
|
|
156
|
+
{/if}
|
|
157
|
+
|
|
158
|
+
<!-- Table -->
|
|
159
|
+
<div class="table-wrapper flex-grow-1">
|
|
160
|
+
<CellTable
|
|
161
|
+
bind:this={cellTable}
|
|
162
|
+
{cells}
|
|
163
|
+
{groupBy}
|
|
164
|
+
{columnPreset}
|
|
165
|
+
{selectable}
|
|
166
|
+
{multiSelect}
|
|
167
|
+
height="100%"
|
|
168
|
+
{techColors}
|
|
169
|
+
{statusColors}
|
|
170
|
+
{headerFilters}
|
|
171
|
+
onselectionchange={handleSelectionChange}
|
|
172
|
+
ondatachange={handleDataChange}
|
|
173
|
+
{onrowclick}
|
|
174
|
+
{onrowdblclick}
|
|
175
|
+
/>
|
|
176
|
+
</div>
|
|
177
|
+
|
|
178
|
+
<!-- Footer -->
|
|
179
|
+
{#if footer}
|
|
180
|
+
<div class="panel-footer border-top p-2 bg-body-tertiary">
|
|
181
|
+
{@render footer({ selectedRows, selectedCount })}
|
|
182
|
+
</div>
|
|
183
|
+
{/if}
|
|
184
|
+
</div>
|
|
185
|
+
|
|
186
|
+
<style>
|
|
187
|
+
.cell-table-panel {
|
|
188
|
+
background-color: var(--bs-body-bg, #fff);
|
|
189
|
+
border: 1px solid var(--bs-border-color, #dee2e6);
|
|
190
|
+
border-radius: var(--bs-border-radius, 0.375rem);
|
|
191
|
+
overflow: hidden;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
.panel-header {
|
|
195
|
+
min-height: 48px;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
.panel-header h6 {
|
|
199
|
+
font-weight: 600;
|
|
200
|
+
color: var(--bs-body-color, #212529);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
.table-wrapper {
|
|
204
|
+
min-height: 0;
|
|
205
|
+
overflow: hidden;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
.panel-footer {
|
|
209
|
+
min-height: 48px;
|
|
210
|
+
}
|
|
211
|
+
</style>
|