@alaarab/ogrid-vue 2.1.2 → 2.1.4

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.
Files changed (44) hide show
  1. package/dist/esm/index.js +4336 -15
  2. package/package.json +4 -4
  3. package/dist/esm/components/MarchingAntsOverlay.js +0 -144
  4. package/dist/esm/components/SideBar.js +0 -1
  5. package/dist/esm/components/StatusBar.js +0 -49
  6. package/dist/esm/components/createDataGridTable.js +0 -514
  7. package/dist/esm/components/createInlineCellEditor.js +0 -194
  8. package/dist/esm/components/createOGrid.js +0 -383
  9. package/dist/esm/composables/index.js +0 -33
  10. package/dist/esm/composables/useActiveCell.js +0 -77
  11. package/dist/esm/composables/useCellEditing.js +0 -27
  12. package/dist/esm/composables/useCellSelection.js +0 -359
  13. package/dist/esm/composables/useClipboard.js +0 -87
  14. package/dist/esm/composables/useColumnChooserState.js +0 -74
  15. package/dist/esm/composables/useColumnHeaderFilterState.js +0 -189
  16. package/dist/esm/composables/useColumnHeaderMenuState.js +0 -113
  17. package/dist/esm/composables/useColumnPinning.js +0 -64
  18. package/dist/esm/composables/useColumnReorder.js +0 -110
  19. package/dist/esm/composables/useColumnResize.js +0 -73
  20. package/dist/esm/composables/useContextMenu.js +0 -23
  21. package/dist/esm/composables/useDataGridState.js +0 -425
  22. package/dist/esm/composables/useDataGridTableSetup.js +0 -66
  23. package/dist/esm/composables/useDateFilterState.js +0 -36
  24. package/dist/esm/composables/useDebounce.js +0 -60
  25. package/dist/esm/composables/useFillHandle.js +0 -205
  26. package/dist/esm/composables/useFilterOptions.js +0 -39
  27. package/dist/esm/composables/useInlineCellEditorState.js +0 -42
  28. package/dist/esm/composables/useKeyboardNavigation.js +0 -232
  29. package/dist/esm/composables/useLatestRef.js +0 -27
  30. package/dist/esm/composables/useMultiSelectFilterState.js +0 -59
  31. package/dist/esm/composables/useOGrid.js +0 -491
  32. package/dist/esm/composables/usePeopleFilterState.js +0 -66
  33. package/dist/esm/composables/useRichSelectState.js +0 -59
  34. package/dist/esm/composables/useRowSelection.js +0 -75
  35. package/dist/esm/composables/useSideBarState.js +0 -41
  36. package/dist/esm/composables/useTableLayout.js +0 -85
  37. package/dist/esm/composables/useTextFilterState.js +0 -26
  38. package/dist/esm/composables/useUndoRedo.js +0 -65
  39. package/dist/esm/composables/useVirtualScroll.js +0 -87
  40. package/dist/esm/types/columnTypes.js +0 -1
  41. package/dist/esm/types/dataGridTypes.js +0 -1
  42. package/dist/esm/types/index.js +0 -1
  43. package/dist/esm/utils/dataGridViewModel.js +0 -23
  44. package/dist/esm/utils/index.js +0 -1
@@ -1,514 +0,0 @@
1
- /**
2
- * Shared DataGridTable factory for Vue UI packages.
3
- *
4
- * Both vue-vuetify and vue-primevue DataGridTable components are 97% identical —
5
- * they only differ in which checkbox and spinner components they render.
6
- * This factory extracts all shared logic into one place.
7
- */
8
- import { defineComponent, computed, h, Teleport } from 'vue';
9
- import { useDataGridTableSetup, } from '../composables';
10
- import { getHeaderFilterConfig, getCellRenderDescriptor, resolveCellDisplayContent, resolveCellStyle, buildInlineEditorProps, buildPopoverEditorProps, getCellInteractionProps, } from '../utils';
11
- import { buildHeaderRows, CHECKBOX_COLUMN_WIDTH, ROW_NUMBER_COLUMN_WIDTH, DEFAULT_MIN_COLUMN_WIDTH } from '@alaarab/ogrid-core';
12
- import { StatusBar } from './StatusBar';
13
- import { MarchingAntsOverlay } from './MarchingAntsOverlay';
14
- const NOOP = () => { };
15
- /**
16
- * Creates a DataGridTable component with framework-specific UI bindings.
17
- * All grid logic, layout, and interaction handling is shared.
18
- */
19
- export function createDataGridTable(ui) {
20
- return defineComponent({
21
- name: 'DataGridTable',
22
- props: {
23
- gridProps: { type: Object, required: true },
24
- },
25
- setup(props) {
26
- const propsRef = computed(() => props.gridProps);
27
- const { wrapperRef, tableContainerRef, tableRef, lastMouseShift, state, columnReorder: { isDragging: isReorderDragging, dropIndicatorX, handleHeaderMouseDown: handleReorderMouseDown }, virtualScroll: { containerRef: vsContainerRef, visibleRange, totalHeight: _totalHeight, scrollToRow: _scrollToRow }, virtualScrollEnabled, columnResize: { handleResizeStart, getColumnWidth }, } = useDataGridTableSetup({ props: propsRef });
28
- // Stable handlers — avoid creating new closures per render
29
- const onWrapperMousedown = (e) => { lastMouseShift.value = e.shiftKey; };
30
- const onContextmenu = (e) => e.preventDefault();
31
- const stopPropagation = (e) => e.stopPropagation();
32
- // Pre-compute header rows so buildHeaderRows is not called on every render
33
- const headerRowsComputed = computed(() => buildHeaderRows(propsRef.value.columns, propsRef.value.visibleColumns));
34
- // Pre-compute per-column layout metadata so it's only recalculated when
35
- // column config, sizing, pinning, or measured widths change — not on every
36
- // render (parity with React's columnMeta useMemo).
37
- const columnMetaCache = computed(() => {
38
- const layout = state.layout.value;
39
- const pinning = state.pinning.value;
40
- const { visibleCols, columnSizingOverrides, measuredColumnWidths } = layout;
41
- const { leftOffsets, rightOffsets } = pinning;
42
- const cellStyles = {};
43
- const cellClasses = {};
44
- const hdrStyles = {};
45
- const hdrClasses = {};
46
- for (let colIdx = 0; colIdx < visibleCols.length; colIdx++) {
47
- const col = visibleCols[colIdx];
48
- const isPinnedLeft = col.pinned === 'left';
49
- const isPinnedRight = col.pinned === 'right';
50
- const columnWidth = getColumnWidth(col);
51
- const hasResizeOverride = !!columnSizingOverrides[col.columnId];
52
- const measuredW = measuredColumnWidths[col.columnId];
53
- const baseMinWidth = col.minWidth ?? DEFAULT_MIN_COLUMN_WIDTH;
54
- const effectiveMinWidth = hasResizeOverride ? columnWidth : Math.max(baseMinWidth, measuredW ?? 0);
55
- const tdStyle = {
56
- minWidth: `${effectiveMinWidth}px`,
57
- width: `${columnWidth}px`,
58
- maxWidth: `${columnWidth}px`,
59
- };
60
- const hdrStyle = {
61
- minWidth: `${effectiveMinWidth}px`,
62
- width: `${columnWidth}px`,
63
- maxWidth: `${columnWidth}px`,
64
- };
65
- const tdClassParts = ['ogrid-data-cell'];
66
- const hdrClassParts = ['ogrid-header-cell'];
67
- if (isPinnedLeft) {
68
- tdClassParts.push('ogrid-data-cell--pinned-left');
69
- tdStyle.left = `${leftOffsets[col.columnId] ?? 0}px`;
70
- hdrClassParts.push('ogrid-header-cell--pinned-left');
71
- hdrStyle.left = `${leftOffsets[col.columnId] ?? 0}px`;
72
- }
73
- else if (isPinnedRight) {
74
- tdClassParts.push('ogrid-data-cell--pinned-right');
75
- tdStyle.right = `${rightOffsets[col.columnId] ?? 0}px`;
76
- hdrClassParts.push('ogrid-header-cell--pinned-right');
77
- hdrStyle.right = `${rightOffsets[col.columnId] ?? 0}px`;
78
- }
79
- cellStyles[col.columnId] = tdStyle;
80
- cellClasses[col.columnId] = tdClassParts.join(' ');
81
- hdrStyles[col.columnId] = hdrStyle;
82
- hdrClasses[col.columnId] = hdrClassParts.join(' ');
83
- }
84
- return { cellStyles, cellClasses, hdrStyles, hdrClasses };
85
- });
86
- return () => {
87
- const p = props.gridProps;
88
- const layout = state.layout.value;
89
- const rowSel = state.rowSelection.value;
90
- const editing = state.editing.value;
91
- const interaction = state.interaction.value;
92
- const ctxMenu = state.contextMenu.value;
93
- const viewModels = state.viewModels.value;
94
- const pinning = state.pinning.value;
95
- const { headerMenu } = pinning;
96
- const { visibleCols, hasCheckboxCol, hasRowNumbersCol, colOffset: _colOffset, containerWidth, minTableWidth, desiredTableWidth, } = layout;
97
- const currentPage = p.currentPage ?? 1;
98
- const pageSize = p.pageSize ?? 25;
99
- const rowNumberOffset = hasRowNumbersCol ? (currentPage - 1) * pageSize : 0;
100
- const { selectedRowIds, handleRowCheckboxChange, handleSelectAll, allSelected, someSelected } = rowSel;
101
- const { editingCell: _editingCell, setEditingCell, pendingEditorValue, setPendingEditorValue, commitCellEdit, cancelPopoverEdit, popoverAnchorEl, setPopoverAnchorEl } = editing;
102
- const { setActiveCell, setSelectionRange, handleCellMouseDown, handleSelectAllCells, selectionRange, hasCellSelection, handleGridKeyDown, handleFillHandleMouseDown, handleCopy, handleCut, handlePaste, cutRange: _cutRange, copyRange: _copyRange, canUndo, canRedo, onUndo, onRedo, isDragging: _isDragging, } = interaction;
103
- const { menuPosition, handleCellContextMenu, closeContextMenu } = ctxMenu;
104
- const { headerFilterInput, cellDescriptorInput, statusBarConfig, showEmptyInGrid, onCellError: _onCellError } = viewModels;
105
- const items = p.items;
106
- const getRowId = p.getRowId;
107
- const layoutMode = p.layoutMode ?? 'fill';
108
- const rowSelection = p.rowSelection ?? 'none';
109
- const suppressHorizontalScroll = p.suppressHorizontalScroll;
110
- const stickyHeader = p.stickyHeader ?? true;
111
- const isLoading = p.isLoading ?? false;
112
- const loadingMessage = p.loadingMessage ?? 'Loading\u2026';
113
- const ariaLabel = p['aria-label'];
114
- const ariaLabelledBy = p['aria-labelledby'];
115
- const fitToContent = layoutMode === 'content';
116
- const allowOverflowX = !suppressHorizontalScroll && containerWidth > 0 && (minTableWidth > containerWidth || desiredTableWidth > containerWidth);
117
- const headerRows = headerRowsComputed.value;
118
- const editCallbacks = { commitCellEdit, setEditingCell, setPendingEditorValue, cancelPopoverEdit };
119
- const interactionHandlers = { handleCellMouseDown, setActiveCell, setEditingCell, handleCellContextMenu };
120
- const handleSingleRowClick = (e) => {
121
- if (rowSelection !== 'single')
122
- return;
123
- const tr = e.currentTarget;
124
- const rowId = tr.dataset.rowId;
125
- if (!rowId)
126
- return;
127
- rowSel.updateSelection(selectedRowIds.has(rowId) ? new Set() : new Set([rowId]));
128
- };
129
- // Render a cell's content
130
- const renderCellContent = (item, col, rowIndex, colIdx) => {
131
- const descriptor = getCellRenderDescriptor(item, col, rowIndex, colIdx, cellDescriptorInput);
132
- if (descriptor.mode === 'editing-inline') {
133
- const editorProps = buildInlineEditorProps(item, col, descriptor, editCallbacks);
134
- return h('div', { class: 'ogrid-editing-cell' }, h(ui.InlineCellEditor, {
135
- value: editorProps.value,
136
- item: editorProps.item,
137
- column: editorProps.column,
138
- rowIndex: editorProps.rowIndex,
139
- editorType: editorProps.editorType,
140
- onCommit: editorProps.onCommit,
141
- onCancel: editorProps.onCancel,
142
- }));
143
- }
144
- if (descriptor.mode === 'editing-popover' && typeof col.cellEditor === 'function') {
145
- const editorProps = buildPopoverEditorProps(item, col, descriptor, pendingEditorValue, editCallbacks);
146
- const CustomEditor = col.cellEditor;
147
- return h('div', [
148
- h('div', {
149
- ref: (el) => { if (el)
150
- setPopoverAnchorEl(el); },
151
- class: 'ogrid-popover-anchor',
152
- 'aria-hidden': 'true',
153
- }),
154
- popoverAnchorEl
155
- ? h(CustomEditor, editorProps)
156
- : null,
157
- ]);
158
- }
159
- // Display mode
160
- const content = resolveCellDisplayContent(col, item, descriptor.displayValue);
161
- const cellStyle = resolveCellStyle(col, item);
162
- const interactionProps2 = getCellInteractionProps(descriptor, col.columnId, interactionHandlers);
163
- const cellClasses = ['ogrid-cell-content'];
164
- if (col.type === 'numeric')
165
- cellClasses.push('ogrid-cell-content--numeric');
166
- else if (col.type === 'boolean')
167
- cellClasses.push('ogrid-cell-content--boolean');
168
- if (descriptor.canEditAny)
169
- cellClasses.push('ogrid-cell-content--editable');
170
- if (descriptor.isActive && !descriptor.isInRange)
171
- cellClasses.push('ogrid-cell-content--active');
172
- if (descriptor.isInRange)
173
- cellClasses.push('ogrid-cell-in-range');
174
- if (descriptor.isInCutRange)
175
- cellClasses.push('ogrid-cell-cut');
176
- const styledContent = cellStyle
177
- ? h('span', { style: cellStyle }, content)
178
- : content;
179
- return h('div', {
180
- ...interactionProps2,
181
- class: cellClasses.join(' '),
182
- }, [
183
- styledContent,
184
- ...(descriptor.canEditAny && descriptor.isSelectionEndCell ? [
185
- h('div', {
186
- onMousedown: handleFillHandleMouseDown,
187
- 'aria-label': 'Fill handle',
188
- class: 'ogrid-fill-handle',
189
- }),
190
- ] : []),
191
- ]);
192
- };
193
- // Use the pre-computed column metadata cache (computed in setup() for memoization)
194
- const { cellStyles: colCellStyles, cellClasses: colCellClasses, hdrStyles: colHdrStyles, hdrClasses: colHdrClasses } = columnMetaCache.value;
195
- // Build column layouts using cached metadata
196
- const columnLayouts = visibleCols.map((col) => ({
197
- col,
198
- tdClasses: colCellClasses[col.columnId] || 'ogrid-data-cell',
199
- tdDynamicStyle: colCellStyles[col.columnId] || {},
200
- }));
201
- // Header class+style lookup using cached metadata
202
- const getHeaderClassAndStyle = (col) => {
203
- const base = colHdrStyles[col.columnId] || {};
204
- // cursor depends on drag state — add it at render time (not cached)
205
- return {
206
- classes: colHdrClasses[col.columnId] || 'ogrid-header-cell',
207
- style: { ...base, cursor: isReorderDragging.value ? 'grabbing' : 'grab' },
208
- };
209
- };
210
- // Dynamic wrapper style
211
- const wrapperStyle = {
212
- position: 'relative',
213
- flex: '1',
214
- minHeight: isLoading && items.length === 0 ? '200px' : '0',
215
- width: fitToContent ? 'fit-content' : '100%',
216
- maxWidth: '100%',
217
- overflowX: suppressHorizontalScroll ? 'hidden' : allowOverflowX ? 'auto' : 'hidden',
218
- overflowY: 'auto',
219
- backgroundColor: '#fff',
220
- willChange: 'scroll-position',
221
- };
222
- if (p.rowHeight) {
223
- wrapperStyle['--ogrid-row-height'] = `${p.rowHeight}px`;
224
- }
225
- return h('div', { class: 'ogrid-outer-container' }, [
226
- // Scrollable wrapper
227
- h('div', {
228
- ref: (el) => { wrapperRef.value = el; vsContainerRef.value = el; },
229
- tabindex: 0,
230
- role: 'region',
231
- 'aria-label': ariaLabel ?? (ariaLabelledBy ? undefined : 'Data grid'),
232
- 'aria-labelledby': ariaLabelledBy,
233
- onMousedown: onWrapperMousedown,
234
- onKeydown: handleGridKeyDown,
235
- onContextmenu,
236
- 'data-overflow-x': allowOverflowX ? 'true' : 'false',
237
- style: wrapperStyle,
238
- }, [
239
- h('div', { class: 'ogrid-scroll-wrapper' }, [
240
- h('div', { style: { minWidth: allowOverflowX ? `${minTableWidth}px` : undefined } }, [
241
- h('div', {
242
- ref: (el) => { tableContainerRef.value = el; },
243
- class: ['ogrid-table-container', isLoading && items.length > 0 ? 'ogrid-table-container--loading' : ''],
244
- }, [
245
- // Drop indicator for column reorder
246
- ...(isReorderDragging.value && dropIndicatorX.value !== null ? [
247
- h('div', {
248
- class: 'ogrid-drop-indicator',
249
- style: { left: `${dropIndicatorX.value}px` },
250
- }),
251
- ] : []),
252
- // Table
253
- h('table', {
254
- ref: (el) => { tableRef.value = el; },
255
- class: 'ogrid-table',
256
- style: { minWidth: `${minTableWidth}px` },
257
- }, [
258
- // Header
259
- h('thead', { class: stickyHeader ? 'ogrid-thead ogrid-sticky-header' : 'ogrid-thead' }, headerRows.map((row, rowIdx) => h('tr', { key: rowIdx, class: 'ogrid-header-row' }, [
260
- // Checkbox header cell
261
- ...(rowIdx === headerRows.length - 1 && hasCheckboxCol ? [
262
- h('th', {
263
- class: 'ogrid-checkbox-header',
264
- style: {
265
- width: `${CHECKBOX_COLUMN_WIDTH}px`,
266
- minWidth: `${CHECKBOX_COLUMN_WIDTH}px`,
267
- maxWidth: `${CHECKBOX_COLUMN_WIDTH}px`,
268
- },
269
- }, ui.renderCheckbox({
270
- modelValue: allSelected,
271
- // Indeterminate only when some (but not all) rows are selected
272
- indeterminate: someSelected && !allSelected,
273
- ariaLabel: 'Select all rows',
274
- onChange: (c) => handleSelectAll(!!c),
275
- })),
276
- ] : []),
277
- // Checkbox spacer in group header row
278
- ...(rowIdx === 0 && rowIdx < headerRows.length - 1 && hasCheckboxCol ? [
279
- h('th', {
280
- rowSpan: headerRows.length - 1,
281
- class: 'ogrid-checkbox-spacer',
282
- style: { width: `${CHECKBOX_COLUMN_WIDTH}px`, minWidth: `${CHECKBOX_COLUMN_WIDTH}px` },
283
- }),
284
- ] : []),
285
- // Row numbers header
286
- ...(rowIdx === headerRows.length - 1 && hasRowNumbersCol ? [
287
- h('th', {
288
- class: 'ogrid-row-number-header',
289
- style: {
290
- width: `${ROW_NUMBER_COLUMN_WIDTH}px`,
291
- minWidth: `${ROW_NUMBER_COLUMN_WIDTH}px`,
292
- maxWidth: `${ROW_NUMBER_COLUMN_WIDTH}px`,
293
- position: 'sticky',
294
- left: hasCheckboxCol ? `${CHECKBOX_COLUMN_WIDTH}px` : '0',
295
- zIndex: 3,
296
- },
297
- }, '#'),
298
- ] : []),
299
- // Row numbers spacer
300
- ...(rowIdx === 0 && rowIdx < headerRows.length - 1 && hasRowNumbersCol ? [
301
- h('th', {
302
- rowSpan: headerRows.length - 1,
303
- class: 'ogrid-row-number-spacer',
304
- style: {
305
- width: `${ROW_NUMBER_COLUMN_WIDTH}px`,
306
- position: 'sticky',
307
- left: hasCheckboxCol ? `${CHECKBOX_COLUMN_WIDTH}px` : '0',
308
- zIndex: 3,
309
- },
310
- }),
311
- ] : []),
312
- // Header cells
313
- ...row.map((cell, cellIdx) => {
314
- if (cell.isGroup) {
315
- return h('th', {
316
- key: cellIdx,
317
- colSpan: cell.colSpan,
318
- scope: 'colgroup',
319
- class: 'ogrid-column-group-header',
320
- }, cell.label);
321
- }
322
- if (!cell.columnDef)
323
- return null;
324
- const col = cell.columnDef;
325
- const { classes: headerClasses, style: headerStyle } = getHeaderClassAndStyle(col);
326
- return h('th', {
327
- key: col.columnId,
328
- scope: 'col',
329
- 'data-column-id': col.columnId,
330
- rowSpan: headerRows.length > 1 ? headerRows.length - rowIdx : undefined,
331
- class: headerClasses,
332
- style: headerStyle,
333
- onMousedown: (e) => handleReorderMouseDown(col.columnId, e),
334
- }, [
335
- h('div', { class: 'ogrid-header-content' }, [
336
- h(ui.ColumnHeaderFilter, getHeaderFilterConfig(col, headerFilterInput)),
337
- h('button', {
338
- onClick: (e) => {
339
- e.stopPropagation();
340
- headerMenu.open(col.columnId, e.currentTarget);
341
- },
342
- 'aria-label': 'Column options',
343
- title: 'Column options',
344
- class: 'ogrid-column-menu-btn',
345
- }, '\u22EE'),
346
- ]),
347
- h('div', {
348
- onMousedown: (e) => {
349
- // Clear cell selection/focus before resize so outlines
350
- // and focus rings don't persist during drag (parity with React).
351
- setActiveCell(null);
352
- setSelectionRange(null);
353
- wrapperRef.value?.focus({ preventScroll: true });
354
- e.stopPropagation();
355
- handleResizeStart(e, col);
356
- },
357
- class: 'ogrid-resize-handle',
358
- }),
359
- ]);
360
- }),
361
- ]))),
362
- // Body
363
- ...(!showEmptyInGrid ? [
364
- h('tbody', {}, (() => {
365
- const vsEnabled = virtualScrollEnabled.value;
366
- const vr = visibleRange.value;
367
- const startIdx = vsEnabled ? vr.startIndex : 0;
368
- const endIdx = vsEnabled ? Math.min(vr.endIndex, items.length - 1) : items.length - 1;
369
- const rows = [];
370
- if (vsEnabled && vr.offsetTop > 0) {
371
- rows.push(h('tr', { key: '__vs-top', style: { height: `${vr.offsetTop}px` } }));
372
- }
373
- for (let rowIndex = startIdx; rowIndex <= endIdx; rowIndex++) {
374
- const item = items[rowIndex];
375
- if (!item)
376
- continue;
377
- const rowIdStr = getRowId(item);
378
- const isSelected = selectedRowIds.has(rowIdStr);
379
- rows.push(h('tr', {
380
- key: rowIdStr,
381
- 'data-row-id': rowIdStr,
382
- onClick: handleSingleRowClick,
383
- style: { cursor: rowSelection === 'single' ? 'pointer' : undefined },
384
- }, [
385
- // Checkbox cell
386
- ...(hasCheckboxCol ? [
387
- h('td', {
388
- class: 'ogrid-checkbox-cell',
389
- style: {
390
- width: `${CHECKBOX_COLUMN_WIDTH}px`,
391
- minWidth: `${CHECKBOX_COLUMN_WIDTH}px`,
392
- maxWidth: `${CHECKBOX_COLUMN_WIDTH}px`,
393
- },
394
- }, h('div', {
395
- 'data-row-index': rowIndex,
396
- 'data-col-index': 0,
397
- onClick: stopPropagation,
398
- class: 'ogrid-checkbox-wrapper',
399
- }, ui.renderCheckbox({
400
- modelValue: isSelected,
401
- ariaLabel: `Select row ${rowIndex + 1}`,
402
- onChange: (checked) => handleRowCheckboxChange(rowIdStr, checked, rowIndex, lastMouseShift.value),
403
- }))),
404
- ] : []),
405
- // Row numbers cell
406
- ...(hasRowNumbersCol ? [
407
- h('td', {
408
- class: 'ogrid-row-number-cell',
409
- style: {
410
- width: `${ROW_NUMBER_COLUMN_WIDTH}px`,
411
- minWidth: `${ROW_NUMBER_COLUMN_WIDTH}px`,
412
- maxWidth: `${ROW_NUMBER_COLUMN_WIDTH}px`,
413
- padding: '6px',
414
- position: 'sticky',
415
- left: hasCheckboxCol ? `${CHECKBOX_COLUMN_WIDTH}px` : '0',
416
- zIndex: 2,
417
- },
418
- }, String(rowNumberOffset + rowIndex + 1)),
419
- ] : []),
420
- // Data cells
421
- ...columnLayouts.map((cl, colIdx) => h('td', {
422
- key: cl.col.columnId,
423
- 'data-column-id': cl.col.columnId,
424
- class: cl.tdClasses,
425
- style: cl.tdDynamicStyle,
426
- }, [renderCellContent(item, cl.col, rowIndex, colIdx)])),
427
- ]));
428
- }
429
- if (vsEnabled && vr.offsetBottom > 0) {
430
- rows.push(h('tr', { key: '__vs-bottom', style: { height: `${vr.offsetBottom}px` } }));
431
- }
432
- return rows;
433
- })()),
434
- ] : []),
435
- ]),
436
- // Empty state
437
- ...(showEmptyInGrid && p.emptyState ? [
438
- ui.renderEmptyState(p.emptyState),
439
- ] : []),
440
- ]),
441
- ]),
442
- ]),
443
- ]),
444
- // Context menu (teleported to body)
445
- ...(menuPosition ? [
446
- h(Teleport, { to: 'body' }, h(ui.GridContextMenu, {
447
- x: menuPosition.x,
448
- y: menuPosition.y,
449
- hasSelection: hasCellSelection,
450
- canUndo,
451
- canRedo,
452
- onUndo: onUndo ?? NOOP,
453
- onRedo: onRedo ?? NOOP,
454
- onCopy: handleCopy,
455
- onCut: handleCut,
456
- onPaste: () => { void handlePaste(); },
457
- onSelectAll: handleSelectAllCells,
458
- onClose: closeContextMenu,
459
- })),
460
- ] : []),
461
- // Marching ants overlay
462
- h(MarchingAntsOverlay, {
463
- containerRef: tableContainerRef,
464
- selectionRange,
465
- copyRange: _copyRange,
466
- cutRange: _cutRange,
467
- colOffset: _colOffset,
468
- items,
469
- visibleColumns: p.visibleColumns instanceof Set ? Array.from(p.visibleColumns) : p.visibleColumns,
470
- columnSizingOverrides: layout.columnSizingOverrides,
471
- columnOrder: p.columnOrder,
472
- }),
473
- // Column header menu
474
- h(ui.ColumnHeaderMenu, {
475
- isOpen: headerMenu.isOpen,
476
- anchorElement: headerMenu.anchorElement,
477
- onClose: headerMenu.close,
478
- onPinLeft: headerMenu.handlePinLeft,
479
- onPinRight: headerMenu.handlePinRight,
480
- onUnpin: headerMenu.handleUnpin,
481
- onSortAsc: headerMenu.handleSortAsc,
482
- onSortDesc: headerMenu.handleSortDesc,
483
- onClearSort: headerMenu.handleClearSort,
484
- onAutosizeThis: headerMenu.handleAutosizeThis,
485
- onAutosizeAll: headerMenu.handleAutosizeAll,
486
- canPinLeft: headerMenu.canPinLeft,
487
- canPinRight: headerMenu.canPinRight,
488
- canUnpin: headerMenu.canUnpin,
489
- currentSort: headerMenu.currentSort,
490
- isSortable: headerMenu.isSortable,
491
- isResizable: headerMenu.isResizable,
492
- }),
493
- // Status bar
494
- ...(statusBarConfig ? [
495
- h(StatusBar, {
496
- totalCount: statusBarConfig.totalCount,
497
- filteredCount: statusBarConfig.filteredCount,
498
- selectedCount: statusBarConfig.selectedCount ?? selectedRowIds.size,
499
- selectedCellCount: selectionRange
500
- ? (Math.abs(selectionRange.endRow - selectionRange.startRow) + 1) * (Math.abs(selectionRange.endCol - selectionRange.startCol) + 1)
501
- : undefined,
502
- aggregation: statusBarConfig.aggregation,
503
- suppressRowCount: statusBarConfig.suppressRowCount,
504
- }),
505
- ] : []),
506
- // Loading overlay
507
- ...(isLoading ? [
508
- h('div', { class: 'ogrid-loading-overlay' }, ui.renderSpinner(loadingMessage)),
509
- ] : []),
510
- ]);
511
- };
512
- },
513
- });
514
- }