@smartnet360/svelte-components 0.0.103 → 0.0.105
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/CellTableDemo.svelte +145 -0
- package/dist/core/CellTable/CellTableDemo.svelte.d.ts +3 -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/demo-data.d.ts +5 -0
- package/dist/core/CellTable/demo-data.js +501 -0
- package/dist/core/CellTable/index.d.ts +12 -0
- package/dist/core/CellTable/index.js +14 -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,145 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import CellTablePanel from './CellTablePanel.svelte';
|
|
3
|
+
import type { CellTableGroupField, ColumnPreset, RowClickEvent, RowSelectionEvent, CellData } from './types';
|
|
4
|
+
import { demoCells } from './demo-data';
|
|
5
|
+
|
|
6
|
+
let groupBy: CellTableGroupField = $state('none');
|
|
7
|
+
let columnPreset: ColumnPreset = $state('default');
|
|
8
|
+
let lastClickedCell: CellData | null = $state(null);
|
|
9
|
+
let selectedIds: string[] = $state([]);
|
|
10
|
+
|
|
11
|
+
function handleRowClick(event: RowClickEvent) {
|
|
12
|
+
lastClickedCell = event.row;
|
|
13
|
+
console.log('Row clicked:', event.row);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function handleSelectionChange(event: RowSelectionEvent) {
|
|
17
|
+
selectedIds = event.ids;
|
|
18
|
+
console.log('Selection changed:', event.ids);
|
|
19
|
+
}
|
|
20
|
+
</script>
|
|
21
|
+
|
|
22
|
+
<div class="cell-table-demo container-fluid py-3">
|
|
23
|
+
<div class="row mb-3">
|
|
24
|
+
<div class="col">
|
|
25
|
+
<h2 class="d-flex align-items-center gap-2">
|
|
26
|
+
<i class="bi bi-table text-primary"></i>
|
|
27
|
+
CellTable Component Demo
|
|
28
|
+
</h2>
|
|
29
|
+
<p class="text-muted">
|
|
30
|
+
A Bootstrap-themed Tabulator wrapper for displaying cell data with grouping, filtering, and export capabilities.
|
|
31
|
+
</p>
|
|
32
|
+
</div>
|
|
33
|
+
</div>
|
|
34
|
+
|
|
35
|
+
<div class="row" style="height: calc(100vh - 200px);">
|
|
36
|
+
<div class="col-12 col-lg-9 h-100">
|
|
37
|
+
<CellTablePanel
|
|
38
|
+
cells={demoCells}
|
|
39
|
+
bind:groupBy
|
|
40
|
+
bind:columnPreset
|
|
41
|
+
selectable={true}
|
|
42
|
+
multiSelect={true}
|
|
43
|
+
title="Cell Data Table"
|
|
44
|
+
showToolbar={true}
|
|
45
|
+
showExport={true}
|
|
46
|
+
headerFilters={true}
|
|
47
|
+
onrowclick={handleRowClick}
|
|
48
|
+
onselectionchange={handleSelectionChange}
|
|
49
|
+
>
|
|
50
|
+
{#snippet footer({ selectedRows, selectedCount })}
|
|
51
|
+
<div class="d-flex align-items-center justify-content-between">
|
|
52
|
+
<span class="text-muted small">
|
|
53
|
+
{#if selectedCount > 0}
|
|
54
|
+
{selectedCount} cell(s) selected
|
|
55
|
+
{:else}
|
|
56
|
+
Click rows to select
|
|
57
|
+
{/if}
|
|
58
|
+
</span>
|
|
59
|
+
<div class="btn-group">
|
|
60
|
+
<button
|
|
61
|
+
type="button"
|
|
62
|
+
class="btn btn-sm btn-outline-primary"
|
|
63
|
+
disabled={selectedCount === 0}
|
|
64
|
+
onclick={() => console.log('Process:', selectedRows.map(r => r.id))}
|
|
65
|
+
>
|
|
66
|
+
<i class="bi bi-gear"></i> Process Selected
|
|
67
|
+
</button>
|
|
68
|
+
<button
|
|
69
|
+
type="button"
|
|
70
|
+
class="btn btn-sm btn-outline-secondary"
|
|
71
|
+
disabled={selectedCount === 0}
|
|
72
|
+
onclick={() => console.log('View:', selectedRows.map(r => r.id))}
|
|
73
|
+
>
|
|
74
|
+
<i class="bi bi-eye"></i> View Details
|
|
75
|
+
</button>
|
|
76
|
+
</div>
|
|
77
|
+
</div>
|
|
78
|
+
{/snippet}
|
|
79
|
+
</CellTablePanel>
|
|
80
|
+
</div>
|
|
81
|
+
|
|
82
|
+
<div class="col-12 col-lg-3 h-100 overflow-auto">
|
|
83
|
+
<div class="card h-100">
|
|
84
|
+
<div class="card-header">
|
|
85
|
+
<h6 class="mb-0">
|
|
86
|
+
<i class="bi bi-info-circle"></i> Cell Details
|
|
87
|
+
</h6>
|
|
88
|
+
</div>
|
|
89
|
+
<div class="card-body">
|
|
90
|
+
{#if lastClickedCell}
|
|
91
|
+
<dl class="row mb-0 small">
|
|
92
|
+
<dt class="col-5">ID</dt>
|
|
93
|
+
<dd class="col-7"><code>{lastClickedCell.id}</code></dd>
|
|
94
|
+
|
|
95
|
+
<dt class="col-5">Cell Name</dt>
|
|
96
|
+
<dd class="col-7">{lastClickedCell.cellName}</dd>
|
|
97
|
+
|
|
98
|
+
<dt class="col-5">Site</dt>
|
|
99
|
+
<dd class="col-7">{lastClickedCell.siteId}</dd>
|
|
100
|
+
|
|
101
|
+
<dt class="col-5">Technology</dt>
|
|
102
|
+
<dd class="col-7">
|
|
103
|
+
<span class="badge bg-secondary">{lastClickedCell.tech}</span>
|
|
104
|
+
</dd>
|
|
105
|
+
|
|
106
|
+
<dt class="col-5">Band</dt>
|
|
107
|
+
<dd class="col-7">
|
|
108
|
+
<span class="badge bg-info">{lastClickedCell.fband}</span>
|
|
109
|
+
</dd>
|
|
110
|
+
|
|
111
|
+
<dt class="col-5">Status</dt>
|
|
112
|
+
<dd class="col-7">
|
|
113
|
+
<span class="badge bg-success">{lastClickedCell.status.replace(/_/g, ' ')}</span>
|
|
114
|
+
</dd>
|
|
115
|
+
|
|
116
|
+
<dt class="col-5">Azimuth</dt>
|
|
117
|
+
<dd class="col-7">{lastClickedCell.azimuth}°</dd>
|
|
118
|
+
|
|
119
|
+
<dt class="col-5">Height</dt>
|
|
120
|
+
<dd class="col-7">{lastClickedCell.height}m</dd>
|
|
121
|
+
|
|
122
|
+
<dt class="col-5">Antenna</dt>
|
|
123
|
+
<dd class="col-7 text-truncate" title={lastClickedCell.antenna}>
|
|
124
|
+
{lastClickedCell.antenna}
|
|
125
|
+
</dd>
|
|
126
|
+
|
|
127
|
+
<dt class="col-5">Planner</dt>
|
|
128
|
+
<dd class="col-7">{lastClickedCell.planner}</dd>
|
|
129
|
+
|
|
130
|
+
{#if lastClickedCell.comment}
|
|
131
|
+
<dt class="col-5">Comment</dt>
|
|
132
|
+
<dd class="col-7 text-muted">{lastClickedCell.comment}</dd>
|
|
133
|
+
{/if}
|
|
134
|
+
</dl>
|
|
135
|
+
{:else}
|
|
136
|
+
<p class="text-muted text-center">
|
|
137
|
+
<i class="bi bi-hand-index"></i><br>
|
|
138
|
+
Click a row to see details
|
|
139
|
+
</p>
|
|
140
|
+
{/if}
|
|
141
|
+
</div>
|
|
142
|
+
</div>
|
|
143
|
+
</div>
|
|
144
|
+
</div>
|
|
145
|
+
</div>
|