@smartnet360/svelte-grid 0.1.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/dist/components/Cell.svelte +249 -0
- package/dist/components/Cell.svelte.d.ts +39 -0
- package/dist/components/Grid.svelte +504 -0
- package/dist/components/Grid.svelte.d.ts +80 -0
- package/dist/components/GridBody.svelte +194 -0
- package/dist/components/GridBody.svelte.d.ts +49 -0
- package/dist/components/GridHeader.svelte +99 -0
- package/dist/components/GridHeader.svelte.d.ts +31 -0
- package/dist/components/GroupHeader.svelte +192 -0
- package/dist/components/GroupHeader.svelte.d.ts +35 -0
- package/dist/components/HeaderCell.svelte +623 -0
- package/dist/components/HeaderCell.svelte.d.ts +40 -0
- package/dist/components/Menu.svelte +215 -0
- package/dist/components/Menu.svelte.d.ts +33 -0
- package/dist/components/Popup.svelte +189 -0
- package/dist/components/Popup.svelte.d.ts +18 -0
- package/dist/components/Row.svelte +115 -0
- package/dist/components/Row.svelte.d.ts +36 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.js +20 -0
- package/dist/state/gridState.svelte.d.ts +299 -0
- package/dist/state/gridState.svelte.js +1025 -0
- package/dist/themes.d.ts +61 -0
- package/dist/themes.js +192 -0
- package/dist/types.d.ts +291 -0
- package/dist/types.js +4 -0
- package/package.json +66 -0
|
@@ -0,0 +1,504 @@
|
|
|
1
|
+
<script lang="ts" generics="T">
|
|
2
|
+
import { setContext, onMount, onDestroy } from 'svelte';
|
|
3
|
+
import type { Snippet } from 'svelte';
|
|
4
|
+
import type { ColumnDefinition, CellContext, GridEvents, MenuItemDefinition, MenuContext, PopupContent, PopupContext, GroupBy, GroupInfo } from '../types.js';
|
|
5
|
+
import { createGridState, GRID_CONTEXT_KEY } from '../state/gridState.svelte.js';
|
|
6
|
+
import { themes, themeToStyle, type GridTheme, type ThemeName } from '../themes.js';
|
|
7
|
+
import GridHeader from './GridHeader.svelte';
|
|
8
|
+
import GridBody from './GridBody.svelte';
|
|
9
|
+
import Popup from './Popup.svelte';
|
|
10
|
+
import Menu from './Menu.svelte';
|
|
11
|
+
|
|
12
|
+
// ============================================
|
|
13
|
+
// Props
|
|
14
|
+
// ============================================
|
|
15
|
+
|
|
16
|
+
interface Props {
|
|
17
|
+
/** Array of data objects */
|
|
18
|
+
data: T[];
|
|
19
|
+
/** Column definitions */
|
|
20
|
+
columns: ColumnDefinition<T>[];
|
|
21
|
+
/** Unique key field in data for row identification */
|
|
22
|
+
rowKey?: string;
|
|
23
|
+
/** Fixed height for the grid container */
|
|
24
|
+
height?: string | number;
|
|
25
|
+
/** Fixed row height in pixels */
|
|
26
|
+
rowHeight?: number;
|
|
27
|
+
/** Placeholder text when no data */
|
|
28
|
+
placeholder?: string;
|
|
29
|
+
/** Enable row hover highlighting */
|
|
30
|
+
rowHover?: boolean;
|
|
31
|
+
/** Enable alternating row colors */
|
|
32
|
+
stripedRows?: boolean;
|
|
33
|
+
/** Enable grid borders */
|
|
34
|
+
bordered?: boolean;
|
|
35
|
+
/** Grid density */
|
|
36
|
+
density?: 'compact' | 'normal' | 'comfortable';
|
|
37
|
+
/** Enable column resizing */
|
|
38
|
+
resizableColumns?: boolean;
|
|
39
|
+
/** Number of rows to freeze at the top */
|
|
40
|
+
frozenRows?: number;
|
|
41
|
+
/** CSS class for the grid container */
|
|
42
|
+
class?: string;
|
|
43
|
+
|
|
44
|
+
// Events
|
|
45
|
+
/** Row click handler */
|
|
46
|
+
onrowclick?: GridEvents<T>['rowclick'];
|
|
47
|
+
/** Row double-click handler */
|
|
48
|
+
onrowdblclick?: GridEvents<T>['rowdblclick'];
|
|
49
|
+
/** Cell click handler */
|
|
50
|
+
oncellclick?: GridEvents<T>['cellclick'];
|
|
51
|
+
/** Header click handler */
|
|
52
|
+
onheaderclick?: GridEvents<T>['headerclick'];
|
|
53
|
+
|
|
54
|
+
// Snippets for custom rendering
|
|
55
|
+
/** Custom cell renderer */
|
|
56
|
+
cell?: Snippet<[CellContext<T>]>;
|
|
57
|
+
/** Custom header cell renderer */
|
|
58
|
+
headerCell?: Snippet<[ColumnDefinition<T>]>;
|
|
59
|
+
/** Custom empty state renderer */
|
|
60
|
+
empty?: Snippet<[]>;
|
|
61
|
+
|
|
62
|
+
// Menus & Popups
|
|
63
|
+
/** Row context menu (right-click) */
|
|
64
|
+
rowContextMenu?: MenuItemDefinition<T>[] | ((row: T, rowIndex: number) => MenuItemDefinition<T>[]);
|
|
65
|
+
/** Row click popup content */
|
|
66
|
+
rowClickPopup?: PopupContent<T>;
|
|
67
|
+
|
|
68
|
+
// Grouping
|
|
69
|
+
/** Group data by field(s) */
|
|
70
|
+
groupBy?: GroupBy<T>;
|
|
71
|
+
/** Custom group header snippet */
|
|
72
|
+
groupHeader?: Snippet<[GroupInfo<T>]>;
|
|
73
|
+
/** Height of group header rows */
|
|
74
|
+
groupHeaderHeight?: number;
|
|
75
|
+
|
|
76
|
+
// Theming
|
|
77
|
+
/** Theme name or custom theme object */
|
|
78
|
+
theme?: ThemeName | GridTheme;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
let {
|
|
82
|
+
data,
|
|
83
|
+
columns,
|
|
84
|
+
rowKey = 'id',
|
|
85
|
+
height = '400px',
|
|
86
|
+
rowHeight = 40,
|
|
87
|
+
placeholder = 'No data available',
|
|
88
|
+
rowHover = true,
|
|
89
|
+
stripedRows = false,
|
|
90
|
+
bordered = true,
|
|
91
|
+
density = 'normal',
|
|
92
|
+
resizableColumns = false,
|
|
93
|
+
frozenRows = 0,
|
|
94
|
+
class: className = '',
|
|
95
|
+
onrowclick,
|
|
96
|
+
onrowdblclick,
|
|
97
|
+
oncellclick,
|
|
98
|
+
onheaderclick,
|
|
99
|
+
cell,
|
|
100
|
+
headerCell,
|
|
101
|
+
empty,
|
|
102
|
+
rowContextMenu,
|
|
103
|
+
rowClickPopup,
|
|
104
|
+
groupBy,
|
|
105
|
+
groupHeader,
|
|
106
|
+
groupHeaderHeight = 36,
|
|
107
|
+
theme
|
|
108
|
+
}: Props = $props();
|
|
109
|
+
|
|
110
|
+
// ============================================
|
|
111
|
+
// State Management
|
|
112
|
+
// ============================================
|
|
113
|
+
|
|
114
|
+
// Initialize state manager - typed with generic T
|
|
115
|
+
// Use empty defaults; $effect will sync actual values
|
|
116
|
+
const gridState = createGridState<T>({
|
|
117
|
+
data: [],
|
|
118
|
+
columns: [],
|
|
119
|
+
rowHeight: 40
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
// Provide state via context for child components
|
|
123
|
+
setContext(GRID_CONTEXT_KEY, gridState);
|
|
124
|
+
|
|
125
|
+
// ============================================
|
|
126
|
+
// Reactive Updates
|
|
127
|
+
// ============================================
|
|
128
|
+
|
|
129
|
+
// Sync props to state reactively
|
|
130
|
+
$effect(() => {
|
|
131
|
+
gridState.setData(data);
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
$effect(() => {
|
|
135
|
+
gridState.setColumns(columns);
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
$effect(() => {
|
|
139
|
+
gridState.rowHeight = rowHeight;
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
$effect(() => {
|
|
143
|
+
gridState.setFrozenRowCount(frozenRows);
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
$effect(() => {
|
|
147
|
+
gridState.setGroupBy(groupBy);
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
$effect(() => {
|
|
151
|
+
gridState.setGroupHeaderHeight(groupHeaderHeight);
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
// ============================================
|
|
155
|
+
// Scroll Handler
|
|
156
|
+
// ============================================
|
|
157
|
+
|
|
158
|
+
function handleBodyScroll(scrollTop: number, scrollLeft: number) {
|
|
159
|
+
gridState.setScrollPosition(scrollTop, scrollLeft);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// ============================================
|
|
163
|
+
// Context Menu State
|
|
164
|
+
// ============================================
|
|
165
|
+
|
|
166
|
+
let contextMenuOpen = $state(false);
|
|
167
|
+
let contextMenuRect = $state<DOMRect | null>(null);
|
|
168
|
+
let contextMenuRow = $state<T | null>(null);
|
|
169
|
+
let contextMenuRowIndex = $state<number>(-1);
|
|
170
|
+
|
|
171
|
+
// Get context menu items (resolve function if needed)
|
|
172
|
+
const contextMenuItems = $derived.by((): MenuItemDefinition<T>[] => {
|
|
173
|
+
if (!rowContextMenu || !contextMenuRow) return [];
|
|
174
|
+
if (typeof rowContextMenu === 'function') {
|
|
175
|
+
return rowContextMenu(contextMenuRow, contextMenuRowIndex);
|
|
176
|
+
}
|
|
177
|
+
return rowContextMenu;
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
// Context menu context
|
|
181
|
+
const contextMenuContext = $derived<MenuContext<T>>({
|
|
182
|
+
row: contextMenuRow ?? undefined,
|
|
183
|
+
rowIndex: contextMenuRowIndex,
|
|
184
|
+
closeMenu: () => {
|
|
185
|
+
contextMenuOpen = false;
|
|
186
|
+
}
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
function handleRowContextMenu(row: T, rowIndex: number, event: MouseEvent) {
|
|
190
|
+
if (!rowContextMenu) return;
|
|
191
|
+
|
|
192
|
+
event.preventDefault();
|
|
193
|
+
contextMenuRow = row;
|
|
194
|
+
contextMenuRowIndex = rowIndex;
|
|
195
|
+
|
|
196
|
+
// Create a rect at the mouse position
|
|
197
|
+
contextMenuRect = new DOMRect(event.clientX, event.clientY, 0, 0);
|
|
198
|
+
contextMenuOpen = true;
|
|
199
|
+
rowClickPopupOpen = false; // Close popup if open
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
function closeContextMenu() {
|
|
203
|
+
contextMenuOpen = false;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// ============================================
|
|
207
|
+
// Row Click Popup State
|
|
208
|
+
// ============================================
|
|
209
|
+
|
|
210
|
+
let rowClickPopupOpen = $state(false);
|
|
211
|
+
let rowClickPopupRect = $state<DOMRect | null>(null);
|
|
212
|
+
let rowClickPopupRow = $state<T | null>(null);
|
|
213
|
+
let rowClickPopupRowIndex = $state<number>(-1);
|
|
214
|
+
|
|
215
|
+
// Popup context
|
|
216
|
+
const rowPopupContext = $derived<PopupContext<T>>({
|
|
217
|
+
row: rowClickPopupRow ?? undefined,
|
|
218
|
+
rowIndex: rowClickPopupRowIndex,
|
|
219
|
+
closePopup: () => {
|
|
220
|
+
rowClickPopupOpen = false;
|
|
221
|
+
}
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
function handleRowClickForPopup(row: T, rowIndex: number, event: MouseEvent) {
|
|
225
|
+
if (!rowClickPopup) return;
|
|
226
|
+
|
|
227
|
+
const target = event.currentTarget as HTMLElement;
|
|
228
|
+
rowClickPopupRow = row;
|
|
229
|
+
rowClickPopupRowIndex = rowIndex;
|
|
230
|
+
rowClickPopupRect = target.getBoundingClientRect();
|
|
231
|
+
rowClickPopupOpen = true;
|
|
232
|
+
contextMenuOpen = false; // Close context menu if open
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
function closeRowPopup() {
|
|
236
|
+
rowClickPopupOpen = false;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// Combined row click handler
|
|
240
|
+
function handleRowClick(row: T, rowIndex: number, event: MouseEvent) {
|
|
241
|
+
// Call user's onrowclick if provided
|
|
242
|
+
onrowclick?.(row, rowIndex, event);
|
|
243
|
+
|
|
244
|
+
// Handle popup if configured
|
|
245
|
+
if (rowClickPopup) {
|
|
246
|
+
handleRowClickForPopup(row, rowIndex, event);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// ============================================
|
|
251
|
+
// Popup Content Rendering
|
|
252
|
+
// ============================================
|
|
253
|
+
|
|
254
|
+
function getRowPopupContent(): string | null {
|
|
255
|
+
if (!rowClickPopup || !rowClickPopupRow) return null;
|
|
256
|
+
if (typeof rowClickPopup === 'string') {
|
|
257
|
+
return rowClickPopup;
|
|
258
|
+
}
|
|
259
|
+
if (typeof rowClickPopup === 'function') {
|
|
260
|
+
const result = rowClickPopup(rowPopupContext);
|
|
261
|
+
return typeof result === 'string' ? result : null;
|
|
262
|
+
}
|
|
263
|
+
return null;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
const isSnippetRowPopup = $derived(
|
|
267
|
+
rowClickPopup !== undefined &&
|
|
268
|
+
typeof rowClickPopup !== 'string' &&
|
|
269
|
+
typeof rowClickPopup !== 'function'
|
|
270
|
+
);
|
|
271
|
+
|
|
272
|
+
// ============================================
|
|
273
|
+
// Container Reference & Resize Observer
|
|
274
|
+
// ============================================
|
|
275
|
+
|
|
276
|
+
let containerRef: HTMLDivElement | undefined = $state();
|
|
277
|
+
let resizeObserver: ResizeObserver | undefined;
|
|
278
|
+
|
|
279
|
+
onMount(() => {
|
|
280
|
+
if (containerRef) {
|
|
281
|
+
// Set initial dimensions
|
|
282
|
+
gridState.setContainerDimensions(containerRef.clientWidth, containerRef.clientHeight);
|
|
283
|
+
|
|
284
|
+
// Observe resize
|
|
285
|
+
resizeObserver = new ResizeObserver((entries) => {
|
|
286
|
+
for (const entry of entries) {
|
|
287
|
+
gridState.setContainerDimensions(
|
|
288
|
+
entry.contentRect.width,
|
|
289
|
+
entry.contentRect.height
|
|
290
|
+
);
|
|
291
|
+
}
|
|
292
|
+
});
|
|
293
|
+
resizeObserver.observe(containerRef);
|
|
294
|
+
}
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
onDestroy(() => {
|
|
298
|
+
resizeObserver?.disconnect();
|
|
299
|
+
gridState.destroy();
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
// ============================================
|
|
303
|
+
// Computed Styles
|
|
304
|
+
// ============================================
|
|
305
|
+
|
|
306
|
+
// Resolve theme to GridTheme object
|
|
307
|
+
const resolvedTheme = $derived.by((): GridTheme | undefined => {
|
|
308
|
+
if (!theme) return undefined;
|
|
309
|
+
if (typeof theme === 'string') {
|
|
310
|
+
return themes[theme];
|
|
311
|
+
}
|
|
312
|
+
return theme;
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
const containerStyle = $derived.by(() => {
|
|
316
|
+
const h = typeof height === 'number' ? `${height}px` : height;
|
|
317
|
+
const baseStyle = `height: ${h};`;
|
|
318
|
+
|
|
319
|
+
if (resolvedTheme) {
|
|
320
|
+
return `${baseStyle} ${themeToStyle(resolvedTheme)}`;
|
|
321
|
+
}
|
|
322
|
+
return baseStyle;
|
|
323
|
+
});
|
|
324
|
+
|
|
325
|
+
const densityClass = $derived.by(() => {
|
|
326
|
+
const densityMap = {
|
|
327
|
+
compact: 'sg-density-compact',
|
|
328
|
+
normal: 'sg-density-normal',
|
|
329
|
+
comfortable: 'sg-density-comfortable'
|
|
330
|
+
};
|
|
331
|
+
return densityMap[density];
|
|
332
|
+
});
|
|
333
|
+
|
|
334
|
+
const gridClasses = $derived.by(() => {
|
|
335
|
+
const classes = ['sg-grid', densityClass];
|
|
336
|
+
if (bordered) classes.push('sg-bordered');
|
|
337
|
+
if (stripedRows) classes.push('sg-striped');
|
|
338
|
+
if (rowHover) classes.push('sg-row-hover');
|
|
339
|
+
if (className) classes.push(className);
|
|
340
|
+
return classes.join(' ');
|
|
341
|
+
});
|
|
342
|
+
</script>
|
|
343
|
+
|
|
344
|
+
<div
|
|
345
|
+
bind:this={containerRef}
|
|
346
|
+
class={gridClasses}
|
|
347
|
+
style={containerStyle}
|
|
348
|
+
role="grid"
|
|
349
|
+
aria-rowcount={gridState.totalRows}
|
|
350
|
+
aria-colcount={gridState.visibleColumns.length}
|
|
351
|
+
>
|
|
352
|
+
<GridHeader
|
|
353
|
+
columns={gridState.visibleColumns}
|
|
354
|
+
{onheaderclick}
|
|
355
|
+
{headerCell}
|
|
356
|
+
resizable={resizableColumns}
|
|
357
|
+
/>
|
|
358
|
+
|
|
359
|
+
{#if gridState.totalRows === 0}
|
|
360
|
+
<div class="sg-empty" role="status">
|
|
361
|
+
{#if empty}
|
|
362
|
+
{@render empty()}
|
|
363
|
+
{:else}
|
|
364
|
+
<span class="sg-empty-text">{placeholder}</span>
|
|
365
|
+
{/if}
|
|
366
|
+
</div>
|
|
367
|
+
{:else}
|
|
368
|
+
<GridBody
|
|
369
|
+
displayRows={gridState.isGrouped ? gridState.visibleDisplayRows : undefined}
|
|
370
|
+
rows={gridState.isGrouped ? undefined : gridState.visibleRows}
|
|
371
|
+
columns={gridState.visibleColumns}
|
|
372
|
+
{rowKey}
|
|
373
|
+
startIndex={gridState.startIndex}
|
|
374
|
+
totalHeight={gridState.totalHeight}
|
|
375
|
+
offsetY={gridState.offsetY}
|
|
376
|
+
{rowHeight}
|
|
377
|
+
{groupHeaderHeight}
|
|
378
|
+
{groupHeader}
|
|
379
|
+
onrowclick={handleRowClick}
|
|
380
|
+
{onrowdblclick}
|
|
381
|
+
{oncellclick}
|
|
382
|
+
onscroll={handleBodyScroll}
|
|
383
|
+
{cell}
|
|
384
|
+
frozenRows={gridState.frozenRows}
|
|
385
|
+
frozenRowCount={gridState.frozenRowCount}
|
|
386
|
+
onrowcontextmenu={rowContextMenu ? handleRowContextMenu : undefined}
|
|
387
|
+
/>
|
|
388
|
+
{/if}
|
|
389
|
+
|
|
390
|
+
<!-- Row context menu -->
|
|
391
|
+
{#if rowContextMenu && contextMenuOpen}
|
|
392
|
+
<Popup open={contextMenuOpen} targetRect={contextMenuRect} position="bottom-start" onclose={closeContextMenu}>
|
|
393
|
+
<Menu items={contextMenuItems} context={contextMenuContext} />
|
|
394
|
+
</Popup>
|
|
395
|
+
{/if}
|
|
396
|
+
|
|
397
|
+
<!-- Row click popup -->
|
|
398
|
+
{#if rowClickPopup && rowClickPopupOpen}
|
|
399
|
+
<Popup open={rowClickPopupOpen} targetRect={rowClickPopupRect} position="bottom-start" onclose={closeRowPopup}>
|
|
400
|
+
<div class="sg-row-popup-content">
|
|
401
|
+
{#if isSnippetRowPopup && rowClickPopup}
|
|
402
|
+
{@render (rowClickPopup as import('svelte').Snippet<[PopupContext<T>]>)(rowPopupContext)}
|
|
403
|
+
{:else}
|
|
404
|
+
{@html getRowPopupContent() ?? ''}
|
|
405
|
+
{/if}
|
|
406
|
+
</div>
|
|
407
|
+
</Popup>
|
|
408
|
+
{/if}
|
|
409
|
+
</div>
|
|
410
|
+
|
|
411
|
+
<style>
|
|
412
|
+
/* ============================================
|
|
413
|
+
Grid Container - Modern Tailwind-inspired Design
|
|
414
|
+
============================================ */
|
|
415
|
+
.sg-grid {
|
|
416
|
+
/* Colors - Refined slate palette */
|
|
417
|
+
--sg-border-color: #e2e8f0;
|
|
418
|
+
--sg-header-bg: linear-gradient(to bottom, #f8fafc, #f1f5f9);
|
|
419
|
+
--sg-header-color: #0f172a;
|
|
420
|
+
--sg-header-border: #cbd5e1;
|
|
421
|
+
--sg-row-bg: #ffffff;
|
|
422
|
+
--sg-row-alt-bg: #f8fafc;
|
|
423
|
+
--sg-row-hover-bg: #f1f5f9;
|
|
424
|
+
--sg-primary-color: #3b82f6;
|
|
425
|
+
--sg-primary-light: #dbeafe;
|
|
426
|
+
|
|
427
|
+
/* Spacing */
|
|
428
|
+
--sg-cell-padding-x: 14px;
|
|
429
|
+
--sg-cell-padding-y: 10px;
|
|
430
|
+
|
|
431
|
+
/* Typography */
|
|
432
|
+
--sg-font-size: 13.5px;
|
|
433
|
+
--sg-font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
|
|
434
|
+
--sg-header-font-weight: 600;
|
|
435
|
+
--sg-header-font-size: 12px;
|
|
436
|
+
--sg-header-letter-spacing: 0.025em;
|
|
437
|
+
|
|
438
|
+
/* Shadows & Effects */
|
|
439
|
+
--sg-header-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
|
|
440
|
+
--sg-frozen-shadow: 4px 0 8px -2px rgba(0, 0, 0, 0.08);
|
|
441
|
+
|
|
442
|
+
/* Transitions */
|
|
443
|
+
--sg-transition-fast: 0.1s ease;
|
|
444
|
+
--sg-transition-normal: 0.15s ease;
|
|
445
|
+
|
|
446
|
+
display: flex;
|
|
447
|
+
flex-direction: column;
|
|
448
|
+
overflow: hidden;
|
|
449
|
+
font-family: var(--sg-font-family);
|
|
450
|
+
font-size: var(--sg-font-size);
|
|
451
|
+
background: var(--sg-row-bg);
|
|
452
|
+
color: #334155;
|
|
453
|
+
line-height: 1.5;
|
|
454
|
+
-webkit-font-smoothing: antialiased;
|
|
455
|
+
-moz-osx-font-smoothing: grayscale;
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
.sg-bordered {
|
|
459
|
+
border: 1px solid var(--sg-border-color);
|
|
460
|
+
border-radius: 8px;
|
|
461
|
+
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05), 0 1px 2px rgba(0, 0, 0, 0.03);
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
/* Density Variants */
|
|
465
|
+
.sg-density-compact {
|
|
466
|
+
--sg-cell-padding-x: 10px;
|
|
467
|
+
--sg-cell-padding-y: 6px;
|
|
468
|
+
--sg-font-size: 12.5px;
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
.sg-density-normal {
|
|
472
|
+
--sg-cell-padding-x: 14px;
|
|
473
|
+
--sg-cell-padding-y: 10px;
|
|
474
|
+
--sg-font-size: 13.5px;
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
.sg-density-comfortable {
|
|
478
|
+
--sg-cell-padding-x: 18px;
|
|
479
|
+
--sg-cell-padding-y: 14px;
|
|
480
|
+
--sg-font-size: 14px;
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
/* ============================================
|
|
484
|
+
Empty State
|
|
485
|
+
============================================ */
|
|
486
|
+
.sg-empty {
|
|
487
|
+
display: flex;
|
|
488
|
+
align-items: center;
|
|
489
|
+
justify-content: center;
|
|
490
|
+
flex: 1;
|
|
491
|
+
padding: 48px 24px;
|
|
492
|
+
color: #94a3b8;
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
.sg-empty-text {
|
|
496
|
+
font-size: 14px;
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
/* Row popup content */
|
|
500
|
+
.sg-row-popup-content {
|
|
501
|
+
padding: 12px;
|
|
502
|
+
font-size: 13px;
|
|
503
|
+
}
|
|
504
|
+
</style>
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
import type { ColumnDefinition, CellContext, GridEvents, MenuItemDefinition, PopupContent, GroupBy, GroupInfo } from '../types.js';
|
|
3
|
+
import { type GridTheme, type ThemeName } from '../themes.js';
|
|
4
|
+
declare function $$render<T>(): {
|
|
5
|
+
props: {
|
|
6
|
+
/** Array of data objects */
|
|
7
|
+
data: T[];
|
|
8
|
+
/** Column definitions */
|
|
9
|
+
columns: ColumnDefinition<T>[];
|
|
10
|
+
/** Unique key field in data for row identification */
|
|
11
|
+
rowKey?: string;
|
|
12
|
+
/** Fixed height for the grid container */
|
|
13
|
+
height?: string | number;
|
|
14
|
+
/** Fixed row height in pixels */
|
|
15
|
+
rowHeight?: number;
|
|
16
|
+
/** Placeholder text when no data */
|
|
17
|
+
placeholder?: string;
|
|
18
|
+
/** Enable row hover highlighting */
|
|
19
|
+
rowHover?: boolean;
|
|
20
|
+
/** Enable alternating row colors */
|
|
21
|
+
stripedRows?: boolean;
|
|
22
|
+
/** Enable grid borders */
|
|
23
|
+
bordered?: boolean;
|
|
24
|
+
/** Grid density */
|
|
25
|
+
density?: "compact" | "normal" | "comfortable";
|
|
26
|
+
/** Enable column resizing */
|
|
27
|
+
resizableColumns?: boolean;
|
|
28
|
+
/** Number of rows to freeze at the top */
|
|
29
|
+
frozenRows?: number;
|
|
30
|
+
/** CSS class for the grid container */
|
|
31
|
+
class?: string;
|
|
32
|
+
/** Row click handler */
|
|
33
|
+
onrowclick?: GridEvents<T>["rowclick"];
|
|
34
|
+
/** Row double-click handler */
|
|
35
|
+
onrowdblclick?: GridEvents<T>["rowdblclick"];
|
|
36
|
+
/** Cell click handler */
|
|
37
|
+
oncellclick?: GridEvents<T>["cellclick"];
|
|
38
|
+
/** Header click handler */
|
|
39
|
+
onheaderclick?: GridEvents<T>["headerclick"];
|
|
40
|
+
/** Custom cell renderer */
|
|
41
|
+
cell?: Snippet<[CellContext<T>]>;
|
|
42
|
+
/** Custom header cell renderer */
|
|
43
|
+
headerCell?: Snippet<[ColumnDefinition<T>]>;
|
|
44
|
+
/** Custom empty state renderer */
|
|
45
|
+
empty?: Snippet<[]>;
|
|
46
|
+
/** Row context menu (right-click) */
|
|
47
|
+
rowContextMenu?: MenuItemDefinition<T>[] | ((row: T, rowIndex: number) => MenuItemDefinition<T>[]);
|
|
48
|
+
/** Row click popup content */
|
|
49
|
+
rowClickPopup?: PopupContent<T>;
|
|
50
|
+
/** Group data by field(s) */
|
|
51
|
+
groupBy?: GroupBy<T>;
|
|
52
|
+
/** Custom group header snippet */
|
|
53
|
+
groupHeader?: Snippet<[GroupInfo<T>]>;
|
|
54
|
+
/** Height of group header rows */
|
|
55
|
+
groupHeaderHeight?: number;
|
|
56
|
+
/** Theme name or custom theme object */
|
|
57
|
+
theme?: ThemeName | GridTheme;
|
|
58
|
+
};
|
|
59
|
+
exports: {};
|
|
60
|
+
bindings: "";
|
|
61
|
+
slots: {};
|
|
62
|
+
events: {};
|
|
63
|
+
};
|
|
64
|
+
declare class __sveltets_Render<T> {
|
|
65
|
+
props(): ReturnType<typeof $$render<T>>['props'];
|
|
66
|
+
events(): ReturnType<typeof $$render<T>>['events'];
|
|
67
|
+
slots(): ReturnType<typeof $$render<T>>['slots'];
|
|
68
|
+
bindings(): "";
|
|
69
|
+
exports(): {};
|
|
70
|
+
}
|
|
71
|
+
interface $$IsomorphicComponent {
|
|
72
|
+
new <T>(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']>> & {
|
|
73
|
+
$$bindings?: ReturnType<__sveltets_Render<T>['bindings']>;
|
|
74
|
+
} & ReturnType<__sveltets_Render<T>['exports']>;
|
|
75
|
+
<T>(internal: unknown, props: ReturnType<__sveltets_Render<T>['props']> & {}): ReturnType<__sveltets_Render<T>['exports']>;
|
|
76
|
+
z_$$bindings?: ReturnType<__sveltets_Render<any>['bindings']>;
|
|
77
|
+
}
|
|
78
|
+
declare const Grid: $$IsomorphicComponent;
|
|
79
|
+
type Grid<T> = InstanceType<typeof Grid<T>>;
|
|
80
|
+
export default Grid;
|