@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.
- package/dist/esm/index.js +4336 -15
- package/package.json +4 -4
- package/dist/esm/components/MarchingAntsOverlay.js +0 -144
- package/dist/esm/components/SideBar.js +0 -1
- package/dist/esm/components/StatusBar.js +0 -49
- package/dist/esm/components/createDataGridTable.js +0 -514
- package/dist/esm/components/createInlineCellEditor.js +0 -194
- package/dist/esm/components/createOGrid.js +0 -383
- package/dist/esm/composables/index.js +0 -33
- package/dist/esm/composables/useActiveCell.js +0 -77
- package/dist/esm/composables/useCellEditing.js +0 -27
- package/dist/esm/composables/useCellSelection.js +0 -359
- package/dist/esm/composables/useClipboard.js +0 -87
- package/dist/esm/composables/useColumnChooserState.js +0 -74
- package/dist/esm/composables/useColumnHeaderFilterState.js +0 -189
- package/dist/esm/composables/useColumnHeaderMenuState.js +0 -113
- package/dist/esm/composables/useColumnPinning.js +0 -64
- package/dist/esm/composables/useColumnReorder.js +0 -110
- package/dist/esm/composables/useColumnResize.js +0 -73
- package/dist/esm/composables/useContextMenu.js +0 -23
- package/dist/esm/composables/useDataGridState.js +0 -425
- package/dist/esm/composables/useDataGridTableSetup.js +0 -66
- package/dist/esm/composables/useDateFilterState.js +0 -36
- package/dist/esm/composables/useDebounce.js +0 -60
- package/dist/esm/composables/useFillHandle.js +0 -205
- package/dist/esm/composables/useFilterOptions.js +0 -39
- package/dist/esm/composables/useInlineCellEditorState.js +0 -42
- package/dist/esm/composables/useKeyboardNavigation.js +0 -232
- package/dist/esm/composables/useLatestRef.js +0 -27
- package/dist/esm/composables/useMultiSelectFilterState.js +0 -59
- package/dist/esm/composables/useOGrid.js +0 -491
- package/dist/esm/composables/usePeopleFilterState.js +0 -66
- package/dist/esm/composables/useRichSelectState.js +0 -59
- package/dist/esm/composables/useRowSelection.js +0 -75
- package/dist/esm/composables/useSideBarState.js +0 -41
- package/dist/esm/composables/useTableLayout.js +0 -85
- package/dist/esm/composables/useTextFilterState.js +0 -26
- package/dist/esm/composables/useUndoRedo.js +0 -65
- package/dist/esm/composables/useVirtualScroll.js +0 -87
- package/dist/esm/types/columnTypes.js +0 -1
- package/dist/esm/types/dataGridTypes.js +0 -1
- package/dist/esm/types/index.js +0 -1
- package/dist/esm/utils/dataGridViewModel.js +0 -23
- 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
|
-
}
|