@alaarab/ogrid-vue 2.1.3 → 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,425 +0,0 @@
1
- import { ref, shallowRef, computed, watch, nextTick, triggerRef } from 'vue';
2
- import { flattenColumns, getDataGridStatusBarConfig, parseValue, computeAggregations, CHECKBOX_COLUMN_WIDTH, DEFAULT_MIN_COLUMN_WIDTH } from '@alaarab/ogrid-core';
3
- import { useRowSelection } from './useRowSelection';
4
- import { useCellEditing } from './useCellEditing';
5
- import { useActiveCell } from './useActiveCell';
6
- import { useCellSelection } from './useCellSelection';
7
- import { useContextMenu } from './useContextMenu';
8
- import { useClipboard } from './useClipboard';
9
- import { useKeyboardNavigation } from './useKeyboardNavigation';
10
- import { useFillHandle } from './useFillHandle';
11
- import { useUndoRedo } from './useUndoRedo';
12
- import { useTableLayout } from './useTableLayout';
13
- import { useColumnPinning } from './useColumnPinning';
14
- import { useColumnHeaderMenuState } from './useColumnHeaderMenuState';
15
- // Stable no-op handlers
16
- const NOOP = () => { };
17
- const NOOP_ASYNC = async () => { };
18
- const NOOP_MOUSE = (_e, _r, _c) => { };
19
- const NOOP_KEY = (_e) => { };
20
- const NOOP_CTX = (_e) => { };
21
- /**
22
- * Single orchestration composable for DataGridTable. Takes grid props and wrapper ref,
23
- * returns all derived state and handlers so UI packages can be thin view layers.
24
- */
25
- export function useDataGridState(params) {
26
- const { props, wrapperRef } = params;
27
- // --- Reactive refs for props consumed by sub-composables ---
28
- // Only properties that sub-composables need as Ref<...> get their own computed.
29
- // Everything else is read directly from props.value at the point of use to
30
- // avoid unnecessary intermediate reactive layers.
31
- const items = computed(() => props.value.items);
32
- const getRowId = props.value.getRowId; // stable function reference, no reactivity needed
33
- const rowSelectionProp = computed(() => props.value.rowSelection ?? 'none');
34
- const controlledSelectedRows = computed(() => props.value.selectedRows);
35
- const editableProp = computed(() => props.value.editable);
36
- const cellSelection = computed(() => props.value.cellSelection !== false);
37
- const pinnedColumnsProp = computed(() => props.value.pinnedColumns);
38
- // Undo/redo wrapping
39
- const undoRedo = useUndoRedo({ onCellValueChanged: props.value.onCellValueChanged });
40
- const onCellValueChanged = computed(() => undoRedo.onCellValueChanged);
41
- /**
42
- * Core's flattenColumns returns IColumnDef<unknown>[] because the generic T
43
- * cannot be propagated through the group-flattening algorithm. At this call
44
- * site the input is IColumnDef<T>[] (via columnsProp), so the output is
45
- * guaranteed to be IColumnDef<T>[] — the cast is safe.
46
- */
47
- const flatColumnsRaw = computed(() => flattenColumns(props.value.columns));
48
- const flatColumns = computed(() => {
49
- const pinned = pinnedColumnsProp.value;
50
- if (!pinned || Object.keys(pinned).length === 0)
51
- return flatColumnsRaw.value;
52
- return flatColumnsRaw.value.map((col) => {
53
- const override = pinned[col.columnId];
54
- if (override && col.pinned !== override)
55
- return { ...col, pinned: override };
56
- return col;
57
- });
58
- });
59
- const visibleCols = computed(() => {
60
- const vis = props.value.visibleColumns;
61
- const order = props.value.columnOrder;
62
- const filtered = vis ? flatColumns.value.filter((c) => vis.has(c.columnId)) : flatColumns.value;
63
- if (!order?.length)
64
- return filtered;
65
- // Build index map for O(1) lookup instead of repeated O(n) indexOf
66
- const orderMap = new Map();
67
- for (let i = 0; i < order.length; i++) {
68
- orderMap.set(order[i], i);
69
- }
70
- return [...filtered].sort((a, b) => {
71
- const ia = orderMap.get(a.columnId) ?? -1;
72
- const ib = orderMap.get(b.columnId) ?? -1;
73
- if (ia === -1 && ib === -1)
74
- return 0;
75
- if (ia === -1)
76
- return 1;
77
- if (ib === -1)
78
- return -1;
79
- return ia - ib;
80
- });
81
- });
82
- const visibleColumnCount = computed(() => visibleCols.value.length);
83
- const hasCheckboxCol = computed(() => rowSelectionProp.value === 'multiple');
84
- const hasRowNumbersCol = computed(() => !!props.value.showRowNumbers);
85
- const specialColsCount = computed(() => (hasCheckboxCol.value ? 1 : 0) + (hasRowNumbersCol.value ? 1 : 0));
86
- const totalColCount = computed(() => visibleColumnCount.value + specialColsCount.value);
87
- const colOffset = specialColsCount; // reactive computed ref instead of snapshot
88
- // shallowRef + mutate-in-place + triggerRef: the Map is mutated (clear/set)
89
- // rather than replaced, so Vue's shallow reactivity doesn't detect the change.
90
- // triggerRef forces dependents to re-evaluate after the in-place mutation.
91
- const rowIndexByRowId = shallowRef(new Map());
92
- watch(items, (newItems) => {
93
- const m = rowIndexByRowId.value;
94
- m.clear();
95
- newItems.forEach((item, idx) => m.set(getRowId(item), idx));
96
- triggerRef(rowIndexByRowId);
97
- }, { immediate: true });
98
- const rowSelectionResult = useRowSelection({
99
- items,
100
- getRowId,
101
- rowSelection: rowSelectionProp,
102
- controlledSelectedRows,
103
- onSelectionChange: props.value.onSelectionChange,
104
- });
105
- const { editingCell, setEditingCell, pendingEditorValue, setPendingEditorValue } = useCellEditing();
106
- const { activeCell, setActiveCell } = useActiveCell(wrapperRef, editingCell);
107
- const rowCount = computed(() => items.value.length);
108
- const visColCount = computed(() => visibleCols.value.length);
109
- const { selectionRange, setSelectionRange, handleCellMouseDown: handleCellMouseDownBase, handleSelectAllCells, isDragging, } = useCellSelection({
110
- colOffset,
111
- rowCount,
112
- visibleColCount: visColCount,
113
- setActiveCell,
114
- wrapperRef,
115
- });
116
- const { contextMenuPosition, setContextMenuPosition, handleCellContextMenu, closeContextMenu } = useContextMenu();
117
- const { handleCopy, handleCut, handlePaste, cutRange, copyRange, clearClipboardRanges } = useClipboard({
118
- items,
119
- visibleCols,
120
- colOffset,
121
- selectionRange,
122
- activeCell,
123
- editable: editableProp,
124
- onCellValueChanged,
125
- beginBatch: undoRedo.beginBatch,
126
- endBatch: undoRedo.endBatch,
127
- });
128
- const handleCellMouseDown = (e, rowIndex, globalColIndex) => {
129
- if (e.button !== 0)
130
- return;
131
- wrapperRef.value?.focus({ preventScroll: true });
132
- clearClipboardRanges();
133
- handleCellMouseDownBase(e, rowIndex, globalColIndex);
134
- };
135
- const { handleGridKeyDown } = useKeyboardNavigation({
136
- data: { items, visibleCols, colOffset, hasCheckboxCol, visibleColumnCount, getRowId },
137
- state: { activeCell, selectionRange, editingCell, selectedRowIds: rowSelectionResult.selectedRowIds },
138
- handlers: {
139
- setActiveCell, setSelectionRange, setEditingCell,
140
- handleRowCheckboxChange: rowSelectionResult.handleRowCheckboxChange,
141
- handleCopy, handleCut, handlePaste,
142
- setContextMenu: setContextMenuPosition,
143
- onUndo: undoRedo.undo,
144
- onRedo: undoRedo.redo,
145
- clearClipboardRanges,
146
- },
147
- features: {
148
- editable: editableProp,
149
- onCellValueChanged,
150
- rowSelection: rowSelectionProp,
151
- wrapperRef,
152
- },
153
- });
154
- const { handleFillHandleMouseDown } = useFillHandle({
155
- items,
156
- visibleCols,
157
- editable: editableProp,
158
- onCellValueChanged,
159
- selectionRange,
160
- setSelectionRange,
161
- setActiveCell,
162
- colOffset,
163
- wrapperRef,
164
- beginBatch: undoRedo.beginBatch,
165
- endBatch: undoRedo.endBatch,
166
- });
167
- const { containerWidth, minTableWidth, desiredTableWidth, columnSizingOverrides, setColumnSizingOverrides, } = useTableLayout({
168
- wrapperRef,
169
- visibleCols,
170
- flatColumns,
171
- hasCheckboxCol,
172
- initialColumnWidths: props.value.initialColumnWidths,
173
- onColumnResized: (columnId, width) => props.value.onColumnResized?.(columnId, width),
174
- });
175
- // --- Column pinning ---
176
- const pinningResult = useColumnPinning({
177
- columns: flatColumns,
178
- pinnedColumns: pinnedColumnsProp,
179
- onColumnPinned: props.value.onColumnPinned,
180
- });
181
- // Autosize callback — updates internal column sizing state + notifies external listener
182
- const handleAutosizeColumn = (columnId, width) => {
183
- setColumnSizingOverrides({ ...columnSizingOverrides.value, [columnId]: { widthPx: width } });
184
- props.value.onColumnResized?.(columnId, width);
185
- };
186
- const headerMenuResult = useColumnHeaderMenuState({
187
- columns: flatColumns,
188
- pinnedColumns: pinningResult.pinnedColumns,
189
- onPinColumn: pinningResult.pinColumn,
190
- onUnpinColumn: pinningResult.unpinColumn,
191
- onSort: props.value.onColumnSort,
192
- onColumnResized: props.value.onColumnResized,
193
- onAutosizeColumn: handleAutosizeColumn,
194
- sortBy: computed(() => props.value.sortBy),
195
- sortDirection: computed(() => props.value.sortDirection),
196
- });
197
- // Measure actual column widths from the DOM after layout changes.
198
- // Used as a minWidth floor to prevent columns from shrinking when new data
199
- // loads (e.g. during server-side pagination transitions).
200
- // nextTick() defers measurement to after Vue has flushed its DOM updates,
201
- // ensuring header cells reflect the latest column layout before we read widths.
202
- const measuredColumnWidths = ref({});
203
- watch([visibleCols, containerWidth, columnSizingOverrides], () => {
204
- void nextTick(() => {
205
- const wrapper = wrapperRef.value;
206
- if (!wrapper)
207
- return;
208
- const headerCells = wrapper.querySelectorAll('th[data-column-id]');
209
- if (headerCells.length === 0)
210
- return;
211
- const measured = {};
212
- headerCells.forEach((cell) => {
213
- const colId = cell.getAttribute('data-column-id');
214
- if (colId)
215
- measured[colId] = cell.offsetWidth;
216
- });
217
- // Only update if widths actually changed to avoid reactive loops
218
- const prev = measuredColumnWidths.value;
219
- const keys = Object.keys(measured);
220
- let changed = keys.length !== Object.keys(prev).length;
221
- if (!changed) {
222
- for (const key of keys) {
223
- if (prev[key] !== measured[key]) {
224
- changed = true;
225
- break;
226
- }
227
- }
228
- }
229
- if (changed)
230
- measuredColumnWidths.value = measured;
231
- });
232
- }, { flush: 'post' });
233
- // Build column width map for pinning offset computation
234
- const columnWidthMap = computed(() => {
235
- const map = {};
236
- for (const col of visibleCols.value) {
237
- const override = columnSizingOverrides.value[col.columnId];
238
- map[col.columnId] = override
239
- ? override.widthPx
240
- : (col.idealWidth ?? col.defaultWidth ?? col.minWidth ?? DEFAULT_MIN_COLUMN_WIDTH);
241
- }
242
- return map;
243
- });
244
- const leftOffsets = computed(() => pinningResult.computeLeftOffsets(visibleCols.value, columnWidthMap.value, DEFAULT_MIN_COLUMN_WIDTH, hasCheckboxCol.value, CHECKBOX_COLUMN_WIDTH));
245
- const rightOffsets = computed(() => pinningResult.computeRightOffsets(visibleCols.value, columnWidthMap.value, DEFAULT_MIN_COLUMN_WIDTH));
246
- const aggregation = computed(() => computeAggregations(items.value, visibleCols.value, cellSelection.value ? selectionRange.value : null));
247
- const statusBarConfig = computed(() => {
248
- const base = getDataGridStatusBarConfig(props.value.statusBar, items.value.length, rowSelectionResult.selectedRowIds.value.size);
249
- if (!base)
250
- return null;
251
- return { ...base, aggregation: aggregation.value ?? undefined };
252
- });
253
- const showEmptyInGrid = computed(() => items.value.length === 0 && !!props.value.emptyState && !props.value.isLoading);
254
- const hasCellSelection = computed(() => selectionRange.value != null || activeCell.value != null);
255
- // --- View-model inputs ---
256
- const headerFilterInput = computed(() => ({
257
- sortBy: props.value.sortBy,
258
- sortDirection: props.value.sortDirection,
259
- onColumnSort: props.value.onColumnSort,
260
- filters: props.value.filters,
261
- onFilterChange: props.value.onFilterChange,
262
- filterOptions: props.value.filterOptions,
263
- loadingFilterOptions: props.value.loadingFilterOptions,
264
- peopleSearch: props.value.peopleSearch,
265
- }));
266
- const cellDescriptorInput = computed(() => ({
267
- editingCell: editingCell.value,
268
- activeCell: cellSelection.value ? activeCell.value : null,
269
- selectionRange: cellSelection.value ? selectionRange.value : null,
270
- cutRange: cellSelection.value ? cutRange.value : null,
271
- copyRange: cellSelection.value ? copyRange.value : null,
272
- colOffset: colOffset.value,
273
- itemsLength: items.value.length,
274
- getRowId,
275
- editable: editableProp.value,
276
- onCellValueChanged: onCellValueChanged.value,
277
- isDragging: cellSelection.value ? isDragging.value : false,
278
- }));
279
- // --- Cell edit helpers ---
280
- const popoverAnchorEl = ref(null);
281
- const setPopoverAnchorEl = (el) => {
282
- popoverAnchorEl.value = el;
283
- };
284
- const commitCellEdit = (item, columnId, oldValue, newValue, rowIndex, globalColIndex) => {
285
- const col = visibleCols.value.find((c) => c.columnId === columnId);
286
- if (col) {
287
- const result = parseValue(newValue, oldValue, item, col);
288
- if (!result.valid) {
289
- setEditingCell(null);
290
- setPopoverAnchorEl(null);
291
- setPendingEditorValue(undefined);
292
- return;
293
- }
294
- newValue = result.value;
295
- }
296
- onCellValueChanged.value?.({
297
- item,
298
- columnId,
299
- oldValue,
300
- newValue,
301
- rowIndex,
302
- });
303
- setEditingCell(null);
304
- setPopoverAnchorEl(null);
305
- setPendingEditorValue(undefined);
306
- if (rowIndex < items.value.length - 1) {
307
- setActiveCell({ rowIndex: rowIndex + 1, columnIndex: globalColIndex });
308
- }
309
- };
310
- const cancelPopoverEdit = () => {
311
- setEditingCell(null);
312
- setPopoverAnchorEl(null);
313
- setPendingEditorValue(undefined);
314
- };
315
- // --- Memoize each sub-object ---
316
- const layoutState = computed(() => ({
317
- flatColumns: flatColumns.value,
318
- visibleCols: visibleCols.value,
319
- visibleColumnCount: visibleColumnCount.value,
320
- totalColCount: totalColCount.value,
321
- colOffset: colOffset.value,
322
- hasCheckboxCol: hasCheckboxCol.value,
323
- hasRowNumbersCol: hasRowNumbersCol.value,
324
- rowIndexByRowId: rowIndexByRowId.value,
325
- containerWidth: containerWidth.value,
326
- minTableWidth: minTableWidth.value,
327
- desiredTableWidth: desiredTableWidth.value,
328
- columnSizingOverrides: columnSizingOverrides.value,
329
- setColumnSizingOverrides,
330
- onColumnResized: props.value.onColumnResized,
331
- measuredColumnWidths: measuredColumnWidths.value,
332
- stickyHeader: props.value.stickyHeader ?? true,
333
- }));
334
- const rowSelectionState = computed(() => ({
335
- selectedRowIds: rowSelectionResult.selectedRowIds.value,
336
- updateSelection: rowSelectionResult.updateSelection,
337
- handleRowCheckboxChange: rowSelectionResult.handleRowCheckboxChange,
338
- handleSelectAll: rowSelectionResult.handleSelectAll,
339
- allSelected: rowSelectionResult.allSelected.value,
340
- someSelected: rowSelectionResult.someSelected.value,
341
- }));
342
- const editingState = computed(() => ({
343
- editingCell: editingCell.value,
344
- setEditingCell,
345
- pendingEditorValue: pendingEditorValue.value,
346
- setPendingEditorValue,
347
- commitCellEdit,
348
- cancelPopoverEdit,
349
- popoverAnchorEl: popoverAnchorEl.value,
350
- setPopoverAnchorEl,
351
- }));
352
- const interactionState = computed(() => ({
353
- activeCell: cellSelection.value ? activeCell.value : null,
354
- setActiveCell: cellSelection.value ? setActiveCell : NOOP,
355
- selectionRange: cellSelection.value ? selectionRange.value : null,
356
- setSelectionRange: cellSelection.value ? setSelectionRange : NOOP,
357
- handleCellMouseDown: cellSelection.value ? handleCellMouseDown : NOOP_MOUSE,
358
- handleSelectAllCells: cellSelection.value ? handleSelectAllCells : NOOP,
359
- hasCellSelection: cellSelection.value ? hasCellSelection.value : false,
360
- handleGridKeyDown: cellSelection.value ? handleGridKeyDown : NOOP_KEY,
361
- handleFillHandleMouseDown: cellSelection.value ? handleFillHandleMouseDown : NOOP,
362
- handleCopy: cellSelection.value ? handleCopy : NOOP,
363
- handleCut: cellSelection.value ? handleCut : NOOP,
364
- handlePaste: cellSelection.value ? handlePaste : NOOP_ASYNC,
365
- cutRange: cellSelection.value ? cutRange.value : null,
366
- copyRange: cellSelection.value ? copyRange.value : null,
367
- clearClipboardRanges: cellSelection.value ? clearClipboardRanges : NOOP,
368
- canUndo: undoRedo.canUndo.value,
369
- canRedo: undoRedo.canRedo.value,
370
- onUndo: undoRedo.undo,
371
- onRedo: undoRedo.redo,
372
- isDragging: cellSelection.value ? isDragging.value : false,
373
- }));
374
- const contextMenuState = computed(() => ({
375
- menuPosition: cellSelection.value ? contextMenuPosition.value : null,
376
- setMenuPosition: cellSelection.value ? setContextMenuPosition : NOOP,
377
- handleCellContextMenu: cellSelection.value ? handleCellContextMenu : NOOP_CTX,
378
- closeContextMenu: cellSelection.value ? closeContextMenu : NOOP,
379
- }));
380
- const viewModelsState = computed(() => ({
381
- headerFilterInput: headerFilterInput.value,
382
- cellDescriptorInput: cellDescriptorInput.value,
383
- statusBarConfig: statusBarConfig.value,
384
- showEmptyInGrid: showEmptyInGrid.value,
385
- onCellError: props.value.onCellError,
386
- }));
387
- const pinningState = computed(() => ({
388
- pinnedColumns: pinningResult.pinnedColumns.value,
389
- pinColumn: pinningResult.pinColumn,
390
- unpinColumn: pinningResult.unpinColumn,
391
- isPinned: pinningResult.isPinned,
392
- leftOffsets: leftOffsets.value,
393
- rightOffsets: rightOffsets.value,
394
- headerMenu: {
395
- isOpen: headerMenuResult.isOpen.value,
396
- openForColumn: headerMenuResult.openForColumn.value,
397
- anchorElement: headerMenuResult.anchorElement.value,
398
- open: headerMenuResult.open,
399
- close: headerMenuResult.close,
400
- handlePinLeft: headerMenuResult.handlePinLeft,
401
- handlePinRight: headerMenuResult.handlePinRight,
402
- handleUnpin: headerMenuResult.handleUnpin,
403
- handleSortAsc: headerMenuResult.handleSortAsc,
404
- handleSortDesc: headerMenuResult.handleSortDesc,
405
- handleClearSort: headerMenuResult.handleClearSort,
406
- handleAutosizeThis: headerMenuResult.handleAutosizeThis,
407
- handleAutosizeAll: headerMenuResult.handleAutosizeAll,
408
- canPinLeft: headerMenuResult.canPinLeft.value,
409
- canPinRight: headerMenuResult.canPinRight.value,
410
- canUnpin: headerMenuResult.canUnpin.value,
411
- currentSort: headerMenuResult.currentSort.value,
412
- isSortable: headerMenuResult.isSortable.value,
413
- isResizable: headerMenuResult.isResizable.value,
414
- },
415
- }));
416
- return {
417
- layout: layoutState,
418
- rowSelection: rowSelectionState,
419
- editing: editingState,
420
- interaction: interactionState,
421
- contextMenu: contextMenuState,
422
- viewModels: viewModelsState,
423
- pinning: pinningState,
424
- };
425
- }
@@ -1,66 +0,0 @@
1
- import { ref, computed } from 'vue';
2
- import { flattenColumns } from '@alaarab/ogrid-core';
3
- import { useDataGridState } from './useDataGridState';
4
- import { useColumnResize } from './useColumnResize';
5
- import { useColumnReorder } from './useColumnReorder';
6
- import { useVirtualScroll } from './useVirtualScroll';
7
- /**
8
- * Shared setup composable for Vue DataGridTable components.
9
- *
10
- * Encapsulates the common setup logic used by all Vue UI DataGridTable implementations
11
- * (Vuetify, PrimeVue, Radix). Each UI package only needs to handle its own template/render
12
- * function and framework-specific component bindings.
13
- */
14
- export function useDataGridTableSetup(params) {
15
- const { props: propsRef } = params;
16
- // --- Shared refs ---
17
- const wrapperRef = ref(null);
18
- const tableContainerRef = ref(null);
19
- const tableRef = ref(null);
20
- const lastMouseShift = ref(false);
21
- // --- Core state ---
22
- const state = useDataGridState({ props: propsRef, wrapperRef });
23
- // --- Column reorder ---
24
- const columnOrderRef = computed(() => {
25
- const p = propsRef.value;
26
- if (p.columnOrder)
27
- return p.columnOrder;
28
- return flattenColumns(p.columns)
29
- .filter(c => p.visibleColumns?.has(c.columnId) ?? true)
30
- .map(c => c.columnId);
31
- });
32
- const onColumnOrderChangeRef = computed(() => propsRef.value.onColumnOrderChange);
33
- const columnReorder = useColumnReorder({
34
- columnOrder: columnOrderRef,
35
- onColumnOrderChange: onColumnOrderChangeRef,
36
- tableRef,
37
- });
38
- // --- Virtual scrolling ---
39
- const virtualScrollEnabled = computed(() => propsRef.value.virtualScroll?.enabled ?? false);
40
- const totalRowsRef = computed(() => propsRef.value.items.length);
41
- const rowHeight = propsRef.value.virtualScroll?.rowHeight ?? 36;
42
- const overscan = propsRef.value.virtualScroll?.overscan ?? 5;
43
- const virtualScroll = useVirtualScroll({
44
- totalRows: totalRowsRef,
45
- rowHeight,
46
- enabled: virtualScrollEnabled,
47
- overscan,
48
- });
49
- // --- Column resize ---
50
- const columnSizingOverridesRef = computed(() => state.layout.value.columnSizingOverrides);
51
- const columnResize = useColumnResize({
52
- columnSizingOverrides: columnSizingOverridesRef,
53
- setColumnSizingOverrides: (v) => state.layout.value.setColumnSizingOverrides(v),
54
- });
55
- return {
56
- wrapperRef,
57
- tableContainerRef,
58
- tableRef,
59
- lastMouseShift,
60
- state,
61
- columnReorder,
62
- virtualScroll,
63
- virtualScrollEnabled,
64
- columnResize,
65
- };
66
- }
@@ -1,36 +0,0 @@
1
- import { ref, watch } from 'vue';
2
- export function useDateFilterState(params) {
3
- const { onDateChange } = params;
4
- const tempDateFrom = ref(params.dateValue?.from ?? '');
5
- const tempDateTo = ref(params.dateValue?.to ?? '');
6
- // Sync temp state when popover opens
7
- watch(params.isFilterOpen, (open) => {
8
- if (open) {
9
- tempDateFrom.value = params.dateValue?.from ?? '';
10
- tempDateTo.value = params.dateValue?.to ?? '';
11
- }
12
- });
13
- const setTempDateFrom = (v) => {
14
- tempDateFrom.value = v;
15
- };
16
- const setTempDateTo = (v) => {
17
- tempDateTo.value = v;
18
- };
19
- const handleDateApply = () => {
20
- const from = tempDateFrom.value || undefined;
21
- const to = tempDateTo.value || undefined;
22
- onDateChange?.(from || to ? { from, to } : undefined);
23
- };
24
- const handleDateClear = () => {
25
- tempDateFrom.value = '';
26
- tempDateTo.value = '';
27
- };
28
- return {
29
- tempDateFrom,
30
- setTempDateFrom,
31
- tempDateTo,
32
- setTempDateTo,
33
- handleDateApply,
34
- handleDateClear,
35
- };
36
- }
@@ -1,60 +0,0 @@
1
- import { ref, watch, onUnmounted } from 'vue';
2
- /**
3
- * Returns a debounced ref that updates after the specified delay when the source value changes.
4
- */
5
- export function useDebounce(value, delayMs) {
6
- const debouncedValue = ref(value.value);
7
- let timeoutId;
8
- watch(value, (newVal) => {
9
- if (timeoutId !== undefined)
10
- clearTimeout(timeoutId);
11
- timeoutId = setTimeout(() => {
12
- debouncedValue.value = newVal;
13
- }, delayMs);
14
- });
15
- onUnmounted(() => {
16
- if (timeoutId !== undefined)
17
- clearTimeout(timeoutId);
18
- });
19
- return debouncedValue;
20
- }
21
- /**
22
- * Returns a stable callback that invokes the given function after the specified delay.
23
- * Each new call resets the timer. Includes `.cancel()` and `.flush()` methods.
24
- */
25
- export function useDebouncedCallback(fn, delayMs) {
26
- let timeoutId;
27
- let latestFn = fn;
28
- let latestArgs;
29
- const debounced = ((...args) => {
30
- latestFn = fn;
31
- latestArgs = args;
32
- if (timeoutId !== undefined)
33
- clearTimeout(timeoutId);
34
- timeoutId = setTimeout(() => {
35
- latestFn(...args);
36
- latestArgs = undefined;
37
- timeoutId = undefined;
38
- }, delayMs);
39
- });
40
- debounced.cancel = () => {
41
- if (timeoutId !== undefined)
42
- clearTimeout(timeoutId);
43
- timeoutId = undefined;
44
- latestArgs = undefined;
45
- };
46
- debounced.flush = () => {
47
- if (timeoutId !== undefined && latestArgs !== undefined) {
48
- clearTimeout(timeoutId);
49
- timeoutId = undefined;
50
- const args = latestArgs;
51
- latestArgs = undefined;
52
- latestFn(...args);
53
- }
54
- };
55
- onUnmounted(() => {
56
- if (timeoutId !== undefined)
57
- clearTimeout(timeoutId);
58
- });
59
- return debounced;
60
- }