@alaarab/ogrid-vue 2.2.0 → 2.4.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/esm/index.js +583 -45
- package/dist/esm/styles/ogrid-layout.css +26 -0
- package/dist/types/components/FormulaBar.d.ts +52 -0
- package/dist/types/components/FormulaRefOverlay.d.ts +39 -0
- package/dist/types/components/SheetTabs.d.ts +45 -0
- package/dist/types/composables/index.d.ts +4 -0
- package/dist/types/composables/useFormulaBar.d.ts +44 -0
- package/dist/types/composables/useFormulaEngine.d.ts +52 -0
- package/dist/types/composables/useOGrid.d.ts +4 -0
- package/dist/types/index.d.ts +5 -2
- package/dist/types/types/dataGridTypes.d.ts +54 -2
- package/dist/types/types/index.d.ts +1 -1
- package/dist/types/utils/dataGridViewModel.d.ts +1 -0
- package/package.json +2 -2
package/dist/esm/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { injectGlobalStyles, Z_INDEX, getStatusBarParts, measureRange, flattenColumns, getMultiSelectFilterFields, deriveFilterOptionsFromData, processClientSideData, processClientSideDataAsync, validateColumns, validateRowIds, computeRowSelectionState, buildCellIndex, UndoRedoStack, CHECKBOX_COLUMN_WIDTH, DEFAULT_MIN_COLUMN_WIDTH, CELL_PADDING, computeAggregations, getDataGridStatusBarConfig, validateVirtualScrollConfig, computeVisibleRange, computeTotalHeight, computeVisibleColumnRange, partitionColumnsForVirtualization, buildHeaderRows, ROW_NUMBER_COLUMN_WIDTH, getHeaderFilterConfig, getCellRenderDescriptor, buildInlineEditorProps, buildPopoverEditorProps, resolveCellDisplayContent, resolveCellStyle, rangesEqual, normalizeSelectionRange, formatSelectionAsTsv, parseTsvClipboard, applyPastedValues, applyCutClear, measureColumnContentWidth, getPinStateForColumn, parseValue, applyFillValues, applyCellDeletion, computeTabNavigation, computeArrowNavigation, computeNextSortState, mergeFilter, applyRangeRowSelection, getScrollTopForRow,
|
|
1
|
+
import { injectGlobalStyles, Z_INDEX, getStatusBarParts, FORMULA_BAR_STYLES, handleFormulaBarKeyDown, measureRange, FORMULA_REF_COLORS, deriveFormulaBarText, extractFormulaReferences, flattenColumns, getMultiSelectFilterFields, deriveFilterOptionsFromData, processClientSideData, processClientSideDataAsync, validateColumns, validateRowIds, computeRowSelectionState, buildCellIndex, UndoRedoStack, CHECKBOX_COLUMN_WIDTH, DEFAULT_MIN_COLUMN_WIDTH, CELL_PADDING, computeAggregations, getDataGridStatusBarConfig, validateVirtualScrollConfig, computeVisibleRange, computeTotalHeight, computeVisibleColumnRange, partitionColumnsForVirtualization, formatCellReference, buildHeaderRows, indexToColumnLetter, ROW_NUMBER_COLUMN_WIDTH, getHeaderFilterConfig, getCellRenderDescriptor, buildInlineEditorProps, buildPopoverEditorProps, resolveCellDisplayContent, resolveCellStyle, rangesEqual, normalizeSelectionRange, formatSelectionAsTsv, parseTsvClipboard, applyPastedValues, applyCutClear, measureColumnContentWidth, getPinStateForColumn, parseValue, applyFillValues, processFormulaBarCommit, applyCellDeletion, computeTabNavigation, computeArrowNavigation, computeNextSortState, mergeFilter, columnLetterToIndex, getCellValue, applyRangeRowSelection, getScrollTopForRow, FormulaEngine, calculateDropTarget, reorderColumnArray, createGridDataAccessor, computeAutoScrollSpeed } from '@alaarab/ogrid-core';
|
|
2
2
|
export * from '@alaarab/ogrid-core';
|
|
3
3
|
export { buildInlineEditorProps, buildPopoverEditorProps, getCellRenderDescriptor, getHeaderFilterConfig, isInSelectionRange, normalizeSelectionRange, resolveCellDisplayContent, resolveCellStyle, toUserLike } from '@alaarab/ogrid-core';
|
|
4
4
|
import { defineComponent, ref, computed, onMounted, watch, toValue, onUnmounted, h, shallowRef, triggerRef, nextTick, Teleport, isRef, isReadonly, unref, customRef } from 'vue';
|
|
@@ -173,6 +173,211 @@ var StatusBar = defineComponent({
|
|
|
173
173
|
};
|
|
174
174
|
}
|
|
175
175
|
});
|
|
176
|
+
var FormulaBar = defineComponent({
|
|
177
|
+
name: "FormulaBar",
|
|
178
|
+
props: {
|
|
179
|
+
cellRef: { type: [String, null], default: null },
|
|
180
|
+
formulaText: { type: String, required: true },
|
|
181
|
+
isEditing: { type: Boolean, required: true }
|
|
182
|
+
},
|
|
183
|
+
emits: ["inputChange", "commit", "cancel", "startEditing"],
|
|
184
|
+
setup(props, { emit }) {
|
|
185
|
+
const inputRef = ref(null);
|
|
186
|
+
watch(() => props.isEditing, (editing) => {
|
|
187
|
+
if (editing && inputRef.value) {
|
|
188
|
+
inputRef.value.focus();
|
|
189
|
+
}
|
|
190
|
+
});
|
|
191
|
+
return () => h("div", { style: FORMULA_BAR_STYLES.bar, role: "toolbar", "aria-label": "Formula bar" }, [
|
|
192
|
+
h(
|
|
193
|
+
"div",
|
|
194
|
+
{ style: FORMULA_BAR_STYLES.nameBox, "aria-label": "Active cell reference" },
|
|
195
|
+
props.cellRef ?? "\u2014"
|
|
196
|
+
),
|
|
197
|
+
h("div", { style: FORMULA_BAR_STYLES.fxLabel, "aria-hidden": "true" }, "fx"),
|
|
198
|
+
h("input", {
|
|
199
|
+
ref: inputRef,
|
|
200
|
+
type: "text",
|
|
201
|
+
style: FORMULA_BAR_STYLES.input,
|
|
202
|
+
value: props.formulaText,
|
|
203
|
+
readonly: !props.isEditing,
|
|
204
|
+
onInput: (e) => emit("inputChange", e.target.value),
|
|
205
|
+
onKeydown: (e) => handleFormulaBarKeyDown(e.key, () => e.preventDefault(), () => emit("commit"), () => emit("cancel")),
|
|
206
|
+
onClick: () => {
|
|
207
|
+
if (!props.isEditing) emit("startEditing");
|
|
208
|
+
},
|
|
209
|
+
onDblclick: () => {
|
|
210
|
+
if (!props.isEditing) emit("startEditing");
|
|
211
|
+
},
|
|
212
|
+
"aria-label": "Formula input",
|
|
213
|
+
spellcheck: false,
|
|
214
|
+
autocomplete: "off"
|
|
215
|
+
})
|
|
216
|
+
]);
|
|
217
|
+
}
|
|
218
|
+
});
|
|
219
|
+
var barStyle = {
|
|
220
|
+
display: "flex",
|
|
221
|
+
alignItems: "center",
|
|
222
|
+
borderTop: "1px solid var(--ogrid-border, #e0e0e0)",
|
|
223
|
+
background: "var(--ogrid-header-bg, #f5f5f5)",
|
|
224
|
+
minHeight: "30px",
|
|
225
|
+
overflowX: "auto",
|
|
226
|
+
overflowY: "hidden",
|
|
227
|
+
gap: "0",
|
|
228
|
+
fontSize: "12px"
|
|
229
|
+
};
|
|
230
|
+
var addBtnStyle = {
|
|
231
|
+
background: "none",
|
|
232
|
+
border: "none",
|
|
233
|
+
cursor: "pointer",
|
|
234
|
+
padding: "4px 10px",
|
|
235
|
+
fontSize: "16px",
|
|
236
|
+
lineHeight: "22px",
|
|
237
|
+
color: "var(--ogrid-fg-secondary, #666)",
|
|
238
|
+
flexShrink: 0
|
|
239
|
+
};
|
|
240
|
+
var tabBaseStyle = {
|
|
241
|
+
background: "none",
|
|
242
|
+
border: "none",
|
|
243
|
+
borderBottom: "2px solid transparent",
|
|
244
|
+
cursor: "pointer",
|
|
245
|
+
padding: "4px 16px",
|
|
246
|
+
fontSize: "12px",
|
|
247
|
+
lineHeight: "22px",
|
|
248
|
+
color: "var(--ogrid-fg, #242424)",
|
|
249
|
+
whiteSpace: "nowrap",
|
|
250
|
+
position: "relative"
|
|
251
|
+
};
|
|
252
|
+
var activeTabStyle = {
|
|
253
|
+
...tabBaseStyle,
|
|
254
|
+
fontWeight: 600,
|
|
255
|
+
borderBottomColor: "var(--ogrid-primary, #217346)",
|
|
256
|
+
background: "var(--ogrid-bg, #fff)"
|
|
257
|
+
};
|
|
258
|
+
var SheetTabs = defineComponent({
|
|
259
|
+
name: "SheetTabs",
|
|
260
|
+
props: {
|
|
261
|
+
sheets: { type: Array, required: true },
|
|
262
|
+
activeSheet: { type: String, required: true },
|
|
263
|
+
showAddButton: { type: Boolean, default: false }
|
|
264
|
+
},
|
|
265
|
+
emits: ["sheetChange", "sheetAdd"],
|
|
266
|
+
setup(props, { emit }) {
|
|
267
|
+
return () => h("div", { style: barStyle, role: "tablist", "aria-label": "Sheet tabs" }, [
|
|
268
|
+
props.showAddButton ? h("button", {
|
|
269
|
+
type: "button",
|
|
270
|
+
style: addBtnStyle,
|
|
271
|
+
onClick: () => emit("sheetAdd"),
|
|
272
|
+
title: "Add sheet",
|
|
273
|
+
"aria-label": "Add sheet"
|
|
274
|
+
}, "+") : null,
|
|
275
|
+
...props.sheets.map((sheet) => {
|
|
276
|
+
const isActive = sheet.id === props.activeSheet;
|
|
277
|
+
const base = isActive ? activeTabStyle : tabBaseStyle;
|
|
278
|
+
const style = isActive && sheet.color ? { ...base, borderBottomColor: sheet.color } : base;
|
|
279
|
+
return h("button", {
|
|
280
|
+
key: sheet.id,
|
|
281
|
+
type: "button",
|
|
282
|
+
role: "tab",
|
|
283
|
+
"aria-selected": isActive,
|
|
284
|
+
style,
|
|
285
|
+
onClick: () => emit("sheetChange", sheet.id)
|
|
286
|
+
}, sheet.name);
|
|
287
|
+
})
|
|
288
|
+
]);
|
|
289
|
+
}
|
|
290
|
+
});
|
|
291
|
+
function measureRef(container, r, colOffset) {
|
|
292
|
+
const startCol = r.col + colOffset;
|
|
293
|
+
const endCol = (r.endCol ?? r.col) + colOffset;
|
|
294
|
+
const endRow = r.endRow ?? r.row;
|
|
295
|
+
const tl = container.querySelector(
|
|
296
|
+
`[data-row-index="${r.row}"][data-col-index="${startCol}"]`
|
|
297
|
+
);
|
|
298
|
+
const br = container.querySelector(
|
|
299
|
+
`[data-row-index="${endRow}"][data-col-index="${endCol}"]`
|
|
300
|
+
);
|
|
301
|
+
if (!tl || !br) return null;
|
|
302
|
+
const cRect = container.getBoundingClientRect();
|
|
303
|
+
const tlRect = tl.getBoundingClientRect();
|
|
304
|
+
const brRect = br.getBoundingClientRect();
|
|
305
|
+
return {
|
|
306
|
+
top: Math.round(tlRect.top - cRect.top),
|
|
307
|
+
left: Math.round(tlRect.left - cRect.left),
|
|
308
|
+
width: Math.round(brRect.right - tlRect.left),
|
|
309
|
+
height: Math.round(brRect.bottom - tlRect.top),
|
|
310
|
+
color: FORMULA_REF_COLORS[r.colorIndex % FORMULA_REF_COLORS.length]
|
|
311
|
+
};
|
|
312
|
+
}
|
|
313
|
+
var FormulaRefOverlay = defineComponent({
|
|
314
|
+
name: "FormulaRefOverlay",
|
|
315
|
+
props: {
|
|
316
|
+
containerEl: { type: Object, default: null },
|
|
317
|
+
references: { type: Array, required: true },
|
|
318
|
+
colOffset: { type: Number, required: true }
|
|
319
|
+
},
|
|
320
|
+
setup(props) {
|
|
321
|
+
const rects = ref([]);
|
|
322
|
+
let rafId = 0;
|
|
323
|
+
function measureAll() {
|
|
324
|
+
const container = props.containerEl;
|
|
325
|
+
const refs = props.references;
|
|
326
|
+
if (!container || refs.length === 0) {
|
|
327
|
+
rects.value = [];
|
|
328
|
+
return;
|
|
329
|
+
}
|
|
330
|
+
const measured = [];
|
|
331
|
+
for (const r of refs) {
|
|
332
|
+
const rect = measureRef(container, r, props.colOffset);
|
|
333
|
+
if (rect) measured.push(rect);
|
|
334
|
+
}
|
|
335
|
+
rects.value = measured;
|
|
336
|
+
}
|
|
337
|
+
watch(
|
|
338
|
+
() => [props.references, props.containerEl, props.colOffset],
|
|
339
|
+
() => {
|
|
340
|
+
cancelAnimationFrame(rafId);
|
|
341
|
+
if (!props.containerEl || props.references.length === 0) {
|
|
342
|
+
rects.value = [];
|
|
343
|
+
return;
|
|
344
|
+
}
|
|
345
|
+
rafId = requestAnimationFrame(measureAll);
|
|
346
|
+
},
|
|
347
|
+
{ immediate: true }
|
|
348
|
+
);
|
|
349
|
+
return () => {
|
|
350
|
+
if (rects.value.length === 0) return null;
|
|
351
|
+
return rects.value.map(
|
|
352
|
+
(r, i) => h("svg", {
|
|
353
|
+
key: i,
|
|
354
|
+
style: {
|
|
355
|
+
position: "absolute",
|
|
356
|
+
top: `${r.top}px`,
|
|
357
|
+
left: `${r.left}px`,
|
|
358
|
+
width: `${r.width}px`,
|
|
359
|
+
height: `${r.height}px`,
|
|
360
|
+
pointerEvents: "none",
|
|
361
|
+
zIndex: 3,
|
|
362
|
+
overflow: "visible"
|
|
363
|
+
},
|
|
364
|
+
"aria-hidden": "true"
|
|
365
|
+
}, [
|
|
366
|
+
h("rect", {
|
|
367
|
+
x: "1",
|
|
368
|
+
y: "1",
|
|
369
|
+
width: Math.max(0, r.width - 2),
|
|
370
|
+
height: Math.max(0, r.height - 2),
|
|
371
|
+
fill: "none",
|
|
372
|
+
stroke: r.color,
|
|
373
|
+
"stroke-width": "2",
|
|
374
|
+
style: "shape-rendering: crispEdges"
|
|
375
|
+
})
|
|
376
|
+
])
|
|
377
|
+
);
|
|
378
|
+
};
|
|
379
|
+
}
|
|
380
|
+
});
|
|
176
381
|
function useFilterOptions(dataSource, fields) {
|
|
177
382
|
const filterOptions = ref({});
|
|
178
383
|
const loadingOptions = ref({});
|
|
@@ -210,6 +415,163 @@ function useFilterOptions(dataSource, fields) {
|
|
|
210
415
|
}, { immediate: true });
|
|
211
416
|
return { filterOptions, loadingOptions };
|
|
212
417
|
}
|
|
418
|
+
function useLatestRef(source) {
|
|
419
|
+
let value = unref(source);
|
|
420
|
+
return customRef((track, trigger) => ({
|
|
421
|
+
get() {
|
|
422
|
+
if (isRef(source)) {
|
|
423
|
+
value = source.value;
|
|
424
|
+
}
|
|
425
|
+
return value;
|
|
426
|
+
},
|
|
427
|
+
set(newValue) {
|
|
428
|
+
value = newValue;
|
|
429
|
+
trigger();
|
|
430
|
+
}
|
|
431
|
+
}));
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
// src/composables/useFormulaEngine.ts
|
|
435
|
+
function useFormulaEngine(params) {
|
|
436
|
+
const {
|
|
437
|
+
formulas,
|
|
438
|
+
items,
|
|
439
|
+
flatColumns,
|
|
440
|
+
initialFormulas,
|
|
441
|
+
onFormulaRecalc,
|
|
442
|
+
formulaFunctions,
|
|
443
|
+
namedRanges,
|
|
444
|
+
sheets
|
|
445
|
+
} = params;
|
|
446
|
+
const itemsRef = useLatestRef(items);
|
|
447
|
+
const flatColumnsRef = useLatestRef(flatColumns);
|
|
448
|
+
const onFormulaRecalcRef = useLatestRef(onFormulaRecalc);
|
|
449
|
+
const engineRef = shallowRef(null);
|
|
450
|
+
let initialLoaded = false;
|
|
451
|
+
const enabled = computed(() => formulas?.value ?? false);
|
|
452
|
+
function createAccessor() {
|
|
453
|
+
return createGridDataAccessor(itemsRef.value, flatColumnsRef.value);
|
|
454
|
+
}
|
|
455
|
+
watch(
|
|
456
|
+
enabled,
|
|
457
|
+
(isEnabled) => {
|
|
458
|
+
if (isEnabled && !engineRef.value) {
|
|
459
|
+
engineRef.value = new FormulaEngine({
|
|
460
|
+
customFunctions: formulaFunctions,
|
|
461
|
+
namedRanges
|
|
462
|
+
});
|
|
463
|
+
if (sheets) {
|
|
464
|
+
for (const [name, accessor] of Object.entries(sheets)) {
|
|
465
|
+
engineRef.value.registerSheet(name, accessor);
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
if (initialFormulas && !initialLoaded) {
|
|
469
|
+
initialLoaded = true;
|
|
470
|
+
const accessor = createAccessor();
|
|
471
|
+
const result = engineRef.value.loadFormulas(initialFormulas, accessor);
|
|
472
|
+
if (result.updatedCells.length > 0) {
|
|
473
|
+
onFormulaRecalcRef.value?.(result);
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
} else if (!isEnabled && engineRef.value) {
|
|
477
|
+
engineRef.value = null;
|
|
478
|
+
}
|
|
479
|
+
},
|
|
480
|
+
{ immediate: true }
|
|
481
|
+
);
|
|
482
|
+
function getFormulaValue(col, row) {
|
|
483
|
+
return engineRef.value?.getValue(col, row);
|
|
484
|
+
}
|
|
485
|
+
function hasFormula(col, row) {
|
|
486
|
+
return engineRef.value?.hasFormula(col, row) ?? false;
|
|
487
|
+
}
|
|
488
|
+
function getFormula(col, row) {
|
|
489
|
+
return engineRef.value?.getFormula(col, row);
|
|
490
|
+
}
|
|
491
|
+
function setFormula(col, row, formula) {
|
|
492
|
+
if (!engineRef.value) return;
|
|
493
|
+
const accessor = createAccessor();
|
|
494
|
+
const result = engineRef.value.setFormula(col, row, formula, accessor);
|
|
495
|
+
if (result.updatedCells.length > 0) {
|
|
496
|
+
onFormulaRecalcRef.value?.(result);
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
function onCellChanged(col, row) {
|
|
500
|
+
if (!engineRef.value) return;
|
|
501
|
+
const accessor = createAccessor();
|
|
502
|
+
const result = engineRef.value.onCellChanged(col, row, accessor);
|
|
503
|
+
if (result.updatedCells.length > 0) {
|
|
504
|
+
onFormulaRecalcRef.value?.(result);
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
function getPrecedents(col, row) {
|
|
508
|
+
return engineRef.value?.getPrecedents(col, row) ?? [];
|
|
509
|
+
}
|
|
510
|
+
function getDependents(col, row) {
|
|
511
|
+
return engineRef.value?.getDependents(col, row) ?? [];
|
|
512
|
+
}
|
|
513
|
+
function getAuditTrail(col, row) {
|
|
514
|
+
return engineRef.value?.getAuditTrail(col, row) ?? null;
|
|
515
|
+
}
|
|
516
|
+
return {
|
|
517
|
+
enabled,
|
|
518
|
+
getFormulaValue,
|
|
519
|
+
hasFormula,
|
|
520
|
+
getFormula,
|
|
521
|
+
setFormula,
|
|
522
|
+
onCellChanged,
|
|
523
|
+
getPrecedents,
|
|
524
|
+
getDependents,
|
|
525
|
+
getAuditTrail
|
|
526
|
+
};
|
|
527
|
+
}
|
|
528
|
+
function useFormulaBar(params) {
|
|
529
|
+
const { activeCol, activeRow, activeCellRef, getFormula, getRawValue, setFormula, onCellValueChanged } = params;
|
|
530
|
+
const isEditing = ref(false);
|
|
531
|
+
const editText = ref("");
|
|
532
|
+
const isFormulaBarEditing = ref(false);
|
|
533
|
+
const displayText = computed(
|
|
534
|
+
() => deriveFormulaBarText(activeCol.value, activeRow.value, getFormula, getRawValue)
|
|
535
|
+
);
|
|
536
|
+
watch([activeCol, activeRow], () => {
|
|
537
|
+
isEditing.value = false;
|
|
538
|
+
isFormulaBarEditing.value = false;
|
|
539
|
+
});
|
|
540
|
+
const startEditing = () => {
|
|
541
|
+
editText.value = displayText.value;
|
|
542
|
+
isEditing.value = true;
|
|
543
|
+
isFormulaBarEditing.value = true;
|
|
544
|
+
};
|
|
545
|
+
const onInputChange = (text) => {
|
|
546
|
+
editText.value = text;
|
|
547
|
+
};
|
|
548
|
+
const onCommit = () => {
|
|
549
|
+
const col = activeCol.value;
|
|
550
|
+
const row = activeRow.value;
|
|
551
|
+
if (col == null || row == null || !setFormula) return;
|
|
552
|
+
processFormulaBarCommit(editText.value, col, row, setFormula, onCellValueChanged);
|
|
553
|
+
isEditing.value = false;
|
|
554
|
+
isFormulaBarEditing.value = false;
|
|
555
|
+
};
|
|
556
|
+
const onCancel = () => {
|
|
557
|
+
isEditing.value = false;
|
|
558
|
+
isFormulaBarEditing.value = false;
|
|
559
|
+
editText.value = "";
|
|
560
|
+
};
|
|
561
|
+
const formulaText = computed(() => isEditing.value ? editText.value : displayText.value);
|
|
562
|
+
const referencedCells = computed(() => extractFormulaReferences(formulaText.value));
|
|
563
|
+
return {
|
|
564
|
+
cellRef: activeCellRef,
|
|
565
|
+
formulaText,
|
|
566
|
+
isEditing,
|
|
567
|
+
onInputChange,
|
|
568
|
+
onCommit,
|
|
569
|
+
onCancel,
|
|
570
|
+
startEditing,
|
|
571
|
+
referencedCells,
|
|
572
|
+
isFormulaBarEditing
|
|
573
|
+
};
|
|
574
|
+
}
|
|
213
575
|
var DEFAULT_PANELS = ["columns", "filters"];
|
|
214
576
|
function useSideBarState(params) {
|
|
215
577
|
const { config } = params;
|
|
@@ -494,6 +856,22 @@ function useOGrid(props) {
|
|
|
494
856
|
const displayTotalCount = computed(
|
|
495
857
|
() => isClientSide.value && resolvedClientItems.value ? resolvedClientItems.value.totalCount : serverTotalCount.value
|
|
496
858
|
);
|
|
859
|
+
const formulasRef = computed(() => !!props.value.formulas);
|
|
860
|
+
const formulaVersionRef = ref(0);
|
|
861
|
+
const wrappedOnFormulaRecalc = (result) => {
|
|
862
|
+
formulaVersionRef.value += 1;
|
|
863
|
+
props.value.onFormulaRecalc?.(result);
|
|
864
|
+
};
|
|
865
|
+
const formulaEngine = useFormulaEngine({
|
|
866
|
+
formulas: formulasRef,
|
|
867
|
+
items: displayItems,
|
|
868
|
+
flatColumns: columns,
|
|
869
|
+
initialFormulas: props.value.initialFormulas,
|
|
870
|
+
onFormulaRecalc: wrappedOnFormulaRecalc,
|
|
871
|
+
formulaFunctions: props.value.formulaFunctions,
|
|
872
|
+
namedRanges: props.value.namedRanges,
|
|
873
|
+
sheets: props.value.sheets
|
|
874
|
+
});
|
|
497
875
|
let firstDataRendered = false;
|
|
498
876
|
let rowIdsValidated = false;
|
|
499
877
|
watch(displayItems, (items) => {
|
|
@@ -581,6 +959,37 @@ function useOGrid(props) {
|
|
|
581
959
|
});
|
|
582
960
|
const clearAllFilters = () => setFilters({});
|
|
583
961
|
const isLoadingResolved = computed(() => isServerSide.value && loading.value || displayLoading.value);
|
|
962
|
+
const activeCellRef = ref(null);
|
|
963
|
+
const activeCellCoords = ref(null);
|
|
964
|
+
const onActiveCellChange = (cellRef) => {
|
|
965
|
+
activeCellRef.value = cellRef;
|
|
966
|
+
if (cellRef) {
|
|
967
|
+
const m = cellRef.match(/^([A-Z]+)(\d+)$/);
|
|
968
|
+
if (m) {
|
|
969
|
+
activeCellCoords.value = { col: columnLetterToIndex(m[1]), row: parseInt(m[2], 10) - 1 };
|
|
970
|
+
} else {
|
|
971
|
+
activeCellCoords.value = null;
|
|
972
|
+
}
|
|
973
|
+
} else {
|
|
974
|
+
activeCellCoords.value = null;
|
|
975
|
+
}
|
|
976
|
+
};
|
|
977
|
+
const formulaBarActiveCol = computed(() => activeCellCoords.value?.col ?? null);
|
|
978
|
+
const formulaBarActiveRow = computed(() => activeCellCoords.value?.row ?? null);
|
|
979
|
+
const getRawValue = (col, row) => {
|
|
980
|
+
const items = displayItems.value;
|
|
981
|
+
const cols = columns.value;
|
|
982
|
+
if (row < 0 || row >= items.length || col < 0 || col >= cols.length) return void 0;
|
|
983
|
+
return getCellValue(items[row], cols[col]);
|
|
984
|
+
};
|
|
985
|
+
const formulaBarState = useFormulaBar({
|
|
986
|
+
activeCol: formulaBarActiveCol,
|
|
987
|
+
activeRow: formulaBarActiveRow,
|
|
988
|
+
activeCellRef,
|
|
989
|
+
getFormula: formulaEngine.enabled.value ? formulaEngine.getFormula : void 0,
|
|
990
|
+
getRawValue,
|
|
991
|
+
setFormula: formulaEngine.enabled.value ? formulaEngine.setFormula : void 0
|
|
992
|
+
});
|
|
584
993
|
const dataGridProps = computed(() => {
|
|
585
994
|
const p = props.value;
|
|
586
995
|
const ds = dataProps.value.dataSource;
|
|
@@ -608,7 +1017,11 @@ function useOGrid(props) {
|
|
|
608
1017
|
rowSelection: p.rowSelection ?? "none",
|
|
609
1018
|
selectedRows: effectiveSelectedRows.value,
|
|
610
1019
|
onSelectionChange: handleSelectionChange,
|
|
611
|
-
showRowNumbers: p.showRowNumbers,
|
|
1020
|
+
showRowNumbers: p.showRowNumbers || p.cellReferences || p.formulas,
|
|
1021
|
+
showColumnLetters: !!(p.cellReferences || p.formulas),
|
|
1022
|
+
showNameBox: !!(p.cellReferences && !p.formulas),
|
|
1023
|
+
// formula bar includes name box
|
|
1024
|
+
onActiveCellChange: p.cellReferences || p.formulas ? onActiveCellChange : void 0,
|
|
612
1025
|
currentPage: page.value,
|
|
613
1026
|
pageSize: pageSize.value,
|
|
614
1027
|
statusBar: statusBarConfig.value,
|
|
@@ -633,7 +1046,20 @@ function useOGrid(props) {
|
|
|
633
1046
|
onClearAll: clearAllFilters,
|
|
634
1047
|
message: p.emptyState?.message,
|
|
635
1048
|
render: p.emptyState?.render
|
|
636
|
-
}
|
|
1049
|
+
},
|
|
1050
|
+
formulas: p.formulas,
|
|
1051
|
+
formulaVersion: formulaVersionRef.value,
|
|
1052
|
+
...formulaEngine.enabled.value ? {
|
|
1053
|
+
getFormulaValue: formulaEngine.getFormulaValue,
|
|
1054
|
+
hasFormula: formulaEngine.hasFormula,
|
|
1055
|
+
getFormula: formulaEngine.getFormula,
|
|
1056
|
+
setFormula: formulaEngine.setFormula,
|
|
1057
|
+
onFormulaCellChanged: formulaEngine.onCellChanged,
|
|
1058
|
+
getPrecedents: formulaEngine.getPrecedents,
|
|
1059
|
+
getDependents: formulaEngine.getDependents,
|
|
1060
|
+
getAuditTrail: formulaEngine.getAuditTrail
|
|
1061
|
+
} : {},
|
|
1062
|
+
formulaReferences: formulaBarState.referencedCells.value.length > 0 ? formulaBarState.referencedCells.value : void 0
|
|
637
1063
|
};
|
|
638
1064
|
});
|
|
639
1065
|
const pagination = computed(() => ({
|
|
@@ -651,14 +1077,59 @@ function useOGrid(props) {
|
|
|
651
1077
|
onVisibilityChange: handleVisibilityChange,
|
|
652
1078
|
placement: columnChooserPlacement.value
|
|
653
1079
|
}));
|
|
654
|
-
const layout = computed(() =>
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
1080
|
+
const layout = computed(() => {
|
|
1081
|
+
const p = props.value;
|
|
1082
|
+
const formulas = !!p.formulas;
|
|
1083
|
+
const showNameBox = !!p.cellReferences && !formulas;
|
|
1084
|
+
let resolvedToolbar = p.toolbar;
|
|
1085
|
+
if (showNameBox) {
|
|
1086
|
+
const nameBoxEl = h("div", {
|
|
1087
|
+
style: {
|
|
1088
|
+
display: "inline-flex",
|
|
1089
|
+
alignItems: "center",
|
|
1090
|
+
padding: "0 8px",
|
|
1091
|
+
fontFamily: "'Consolas', 'Courier New', monospace",
|
|
1092
|
+
fontSize: "12px",
|
|
1093
|
+
border: "1px solid rgba(0,0,0,0.12)",
|
|
1094
|
+
borderRadius: "3px",
|
|
1095
|
+
height: "24px",
|
|
1096
|
+
marginRight: "8px",
|
|
1097
|
+
background: "#fff",
|
|
1098
|
+
minWidth: "40px",
|
|
1099
|
+
color: "rgba(0,0,0,0.6)"
|
|
1100
|
+
},
|
|
1101
|
+
"aria-label": "Active cell reference"
|
|
1102
|
+
}, activeCellRef.value ?? "\u2014");
|
|
1103
|
+
resolvedToolbar = [nameBoxEl, resolvedToolbar];
|
|
1104
|
+
}
|
|
1105
|
+
const formulaBarEl = formulas ? h(FormulaBar, {
|
|
1106
|
+
cellRef: formulaBarState.cellRef.value,
|
|
1107
|
+
formulaText: formulaBarState.formulaText.value,
|
|
1108
|
+
isEditing: formulaBarState.isEditing.value,
|
|
1109
|
+
onInputChange: formulaBarState.onInputChange,
|
|
1110
|
+
onCommit: formulaBarState.onCommit,
|
|
1111
|
+
onCancel: formulaBarState.onCancel,
|
|
1112
|
+
onStartEditing: formulaBarState.startEditing
|
|
1113
|
+
}) : void 0;
|
|
1114
|
+
const sheetTabsEl = p.sheetDefs && p.sheetDefs.length > 0 && p.activeSheet && p.onSheetChange ? h(SheetTabs, {
|
|
1115
|
+
sheets: p.sheetDefs,
|
|
1116
|
+
activeSheet: p.activeSheet,
|
|
1117
|
+
showAddButton: !!p.onSheetAdd,
|
|
1118
|
+
onSheetChange: p.onSheetChange,
|
|
1119
|
+
onSheetAdd: p.onSheetAdd ?? (() => {
|
|
1120
|
+
})
|
|
1121
|
+
}) : void 0;
|
|
1122
|
+
return {
|
|
1123
|
+
toolbar: resolvedToolbar,
|
|
1124
|
+
toolbarBelow: p.toolbarBelow,
|
|
1125
|
+
className: p.className,
|
|
1126
|
+
emptyState: p.emptyState,
|
|
1127
|
+
sideBarProps: sideBarProps.value,
|
|
1128
|
+
fullScreen: p.fullScreen,
|
|
1129
|
+
formulaBar: formulaBarEl,
|
|
1130
|
+
sheetTabs: sheetTabsEl
|
|
1131
|
+
};
|
|
1132
|
+
});
|
|
662
1133
|
const filtersResult = computed(() => ({
|
|
663
1134
|
hasActiveFilters: hasActiveFilters.value,
|
|
664
1135
|
setFilters
|
|
@@ -880,23 +1351,6 @@ function useActiveCell(wrapperRef, editingCell) {
|
|
|
880
1351
|
});
|
|
881
1352
|
return { activeCell, setActiveCell };
|
|
882
1353
|
}
|
|
883
|
-
function useLatestRef(source) {
|
|
884
|
-
let value = unref(source);
|
|
885
|
-
return customRef((track, trigger) => ({
|
|
886
|
-
get() {
|
|
887
|
-
if (isRef(source)) {
|
|
888
|
-
value = source.value;
|
|
889
|
-
}
|
|
890
|
-
return value;
|
|
891
|
-
},
|
|
892
|
-
set(newValue) {
|
|
893
|
-
value = newValue;
|
|
894
|
-
trigger();
|
|
895
|
-
}
|
|
896
|
-
}));
|
|
897
|
-
}
|
|
898
|
-
|
|
899
|
-
// src/composables/useCellSelection.ts
|
|
900
1354
|
var DRAG_ATTR = "data-drag-range";
|
|
901
1355
|
var DRAG_ANCHOR_ATTR = "data-drag-anchor";
|
|
902
1356
|
var AUTO_SCROLL_EDGE = 40;
|
|
@@ -1313,7 +1767,7 @@ function useKeyboardNavigation(params) {
|
|
|
1313
1767
|
const maxColIndex = visibleColumnCount - 1 + colOffset;
|
|
1314
1768
|
if (items.length === 0) return;
|
|
1315
1769
|
if (activeCell === null) {
|
|
1316
|
-
if (["ArrowDown", "ArrowUp", "ArrowLeft", "ArrowRight", "Tab", "Enter", "Home", "End"].includes(e.key)) {
|
|
1770
|
+
if (["ArrowDown", "ArrowUp", "ArrowLeft", "ArrowRight", "Tab", "Enter", "Home", "End", "PageDown", "PageUp"].includes(e.key)) {
|
|
1317
1771
|
setActiveCell({ rowIndex: 0, columnIndex: colOffset });
|
|
1318
1772
|
e.preventDefault();
|
|
1319
1773
|
}
|
|
@@ -1413,6 +1867,36 @@ function useKeyboardNavigation(params) {
|
|
|
1413
1867
|
setActiveCell({ rowIndex: newRowEnd, columnIndex: maxColIndex });
|
|
1414
1868
|
break;
|
|
1415
1869
|
}
|
|
1870
|
+
case "PageDown":
|
|
1871
|
+
case "PageUp": {
|
|
1872
|
+
e.preventDefault();
|
|
1873
|
+
const wrapperEl = wrapperRef.value;
|
|
1874
|
+
let pageSize = 10;
|
|
1875
|
+
if (wrapperEl) {
|
|
1876
|
+
const row = wrapperEl.querySelector("tbody tr");
|
|
1877
|
+
if (row && row.offsetHeight > 0) pageSize = Math.max(1, Math.floor(wrapperEl.clientHeight / row.offsetHeight));
|
|
1878
|
+
}
|
|
1879
|
+
const pgDirection = e.key === "PageDown" ? 1 : -1;
|
|
1880
|
+
const newRowPage = Math.max(0, Math.min(rowIndex + pgDirection * pageSize, maxRowIndex));
|
|
1881
|
+
if (shift) {
|
|
1882
|
+
setSelectionRange({
|
|
1883
|
+
startRow: selectionRange?.startRow ?? rowIndex,
|
|
1884
|
+
startCol: selectionRange?.startCol ?? dataColIndex,
|
|
1885
|
+
endRow: newRowPage,
|
|
1886
|
+
endCol: selectionRange?.endCol ?? dataColIndex
|
|
1887
|
+
});
|
|
1888
|
+
} else {
|
|
1889
|
+
setSelectionRange({
|
|
1890
|
+
startRow: newRowPage,
|
|
1891
|
+
startCol: dataColIndex,
|
|
1892
|
+
endRow: newRowPage,
|
|
1893
|
+
endCol: dataColIndex
|
|
1894
|
+
});
|
|
1895
|
+
}
|
|
1896
|
+
setActiveCell({ rowIndex: newRowPage, columnIndex });
|
|
1897
|
+
scrollToRow?.(newRowPage, "center");
|
|
1898
|
+
break;
|
|
1899
|
+
}
|
|
1416
1900
|
case "Enter":
|
|
1417
1901
|
case "F2": {
|
|
1418
1902
|
e.preventDefault();
|
|
@@ -2263,7 +2747,10 @@ function useDataGridState(params) {
|
|
|
2263
2747
|
getRowId,
|
|
2264
2748
|
editable: editableProp.value,
|
|
2265
2749
|
onCellValueChanged: onCellValueChanged.value,
|
|
2266
|
-
isDragging: cellSelection.value ? isDragging.value : false
|
|
2750
|
+
isDragging: cellSelection.value ? isDragging.value : false,
|
|
2751
|
+
getFormulaValue: props.value.getFormulaValue,
|
|
2752
|
+
hasFormula: props.value.hasFormula,
|
|
2753
|
+
formulaVersion: props.value.formulaVersion
|
|
2267
2754
|
}));
|
|
2268
2755
|
const popoverAnchorEl = ref(null);
|
|
2269
2756
|
const setPopoverAnchorEl = (el) => {
|
|
@@ -2952,9 +3439,14 @@ function useColumnChooserState(params) {
|
|
|
2952
3439
|
}
|
|
2953
3440
|
function useInlineCellEditorState(params) {
|
|
2954
3441
|
const { value, editorType, onCommit, onCancel } = params;
|
|
2955
|
-
const localValue = ref(
|
|
2956
|
-
value
|
|
2957
|
-
|
|
3442
|
+
const localValue = ref((() => {
|
|
3443
|
+
if (value === null || value === void 0) return "";
|
|
3444
|
+
if (editorType === "date") {
|
|
3445
|
+
const str = String(value);
|
|
3446
|
+
return str.match(/^\d{4}-\d{2}-\d{2}/) ? str.substring(0, 10) : str;
|
|
3447
|
+
}
|
|
3448
|
+
return String(value);
|
|
3449
|
+
})());
|
|
2958
3450
|
const setLocalValue = (v) => {
|
|
2959
3451
|
localValue.value = v;
|
|
2960
3452
|
};
|
|
@@ -2970,14 +3462,14 @@ function useInlineCellEditorState(params) {
|
|
|
2970
3462
|
e.stopPropagation();
|
|
2971
3463
|
cancel();
|
|
2972
3464
|
}
|
|
2973
|
-
if (e.key === "Enter" && editorType === "text") {
|
|
3465
|
+
if (e.key === "Enter" && (editorType === "text" || editorType === "date")) {
|
|
2974
3466
|
e.preventDefault();
|
|
2975
3467
|
e.stopPropagation();
|
|
2976
3468
|
commit(localValue.value);
|
|
2977
3469
|
}
|
|
2978
3470
|
};
|
|
2979
3471
|
const handleBlur = () => {
|
|
2980
|
-
if (editorType === "text") {
|
|
3472
|
+
if (editorType === "text" || editorType === "date") {
|
|
2981
3473
|
commit(localValue.value);
|
|
2982
3474
|
}
|
|
2983
3475
|
};
|
|
@@ -3336,6 +3828,7 @@ function getCellInteractionProps(descriptor, columnId, handlers) {
|
|
|
3336
3828
|
const base = {
|
|
3337
3829
|
"data-row-index": descriptor.rowIndex,
|
|
3338
3830
|
"data-col-index": descriptor.globalColIndex,
|
|
3831
|
+
...descriptor.isActive ? { "data-active-cell": "true" } : {},
|
|
3339
3832
|
...descriptor.isInRange ? { "data-in-range": "true" } : {},
|
|
3340
3833
|
tabindex: descriptor.isActive ? 0 : -1,
|
|
3341
3834
|
onMousedown: (e) => handlers.handleCellMouseDown(e, descriptor.rowIndex, descriptor.globalColIndex),
|
|
@@ -3371,6 +3864,24 @@ function createDataGridTable(ui) {
|
|
|
3371
3864
|
columnPartition,
|
|
3372
3865
|
globalColIndexMap
|
|
3373
3866
|
} = useDataGridTableSetup({ props: propsRef });
|
|
3867
|
+
const rowNumberOffset = computed(() => {
|
|
3868
|
+
const p = propsRef.value;
|
|
3869
|
+
const hasRowNumbers = p.showRowNumbers || p.showColumnLetters;
|
|
3870
|
+
return hasRowNumbers ? ((p.currentPage ?? 1) - 1) * (p.pageSize ?? 25) : 0;
|
|
3871
|
+
});
|
|
3872
|
+
watch(
|
|
3873
|
+
[() => state.interaction.value.activeCell, rowNumberOffset],
|
|
3874
|
+
([ac, offset]) => {
|
|
3875
|
+
const cb = propsRef.value.onActiveCellChange;
|
|
3876
|
+
if (!cb) return;
|
|
3877
|
+
if (ac) {
|
|
3878
|
+
cb(formatCellReference(ac.columnIndex - state.layout.value.colOffset, offset + ac.rowIndex + 1));
|
|
3879
|
+
} else {
|
|
3880
|
+
cb(null);
|
|
3881
|
+
}
|
|
3882
|
+
},
|
|
3883
|
+
{ immediate: true }
|
|
3884
|
+
);
|
|
3374
3885
|
const onWrapperMousedown = (e) => {
|
|
3375
3886
|
lastMouseShift.value = e.shiftKey;
|
|
3376
3887
|
};
|
|
@@ -3446,7 +3957,7 @@ function createDataGridTable(ui) {
|
|
|
3446
3957
|
} = layout;
|
|
3447
3958
|
const currentPage = p.currentPage ?? 1;
|
|
3448
3959
|
const pageSize = p.pageSize ?? 25;
|
|
3449
|
-
const
|
|
3960
|
+
const rowNumberOffset2 = hasRowNumbersCol ? (currentPage - 1) * pageSize : 0;
|
|
3450
3961
|
const { selectedRowIds, handleRowCheckboxChange, handleSelectAll, allSelected, someSelected } = rowSel;
|
|
3451
3962
|
const { editingCell: _editingCell, setEditingCell, pendingEditorValue, setPendingEditorValue, commitCellEdit, cancelPopoverEdit, popoverAnchorEl, setPopoverAnchorEl } = editing;
|
|
3452
3963
|
const {
|
|
@@ -3536,13 +4047,14 @@ function createDataGridTable(ui) {
|
|
|
3536
4047
|
]);
|
|
3537
4048
|
}
|
|
3538
4049
|
const content = resolveCellDisplayContent(col, item, descriptor.displayValue);
|
|
3539
|
-
const cellStyle = resolveCellStyle(col, item);
|
|
4050
|
+
const cellStyle = resolveCellStyle(col, item, descriptor.displayValue);
|
|
3540
4051
|
const interactionProps2 = getCellInteractionProps(descriptor, col.columnId, interactionHandlers);
|
|
3541
4052
|
const cellClasses = ["ogrid-cell-content"];
|
|
3542
4053
|
if (col.type === "numeric") cellClasses.push("ogrid-cell-content--numeric");
|
|
3543
4054
|
else if (col.type === "boolean") cellClasses.push("ogrid-cell-content--boolean");
|
|
3544
4055
|
if (descriptor.canEditAny) cellClasses.push("ogrid-cell-content--editable");
|
|
3545
4056
|
if (descriptor.isActive) cellClasses.push("ogrid-cell-content--active");
|
|
4057
|
+
if (descriptor.isActive && descriptor.isInRange) cellClasses.push("ogrid-cell-content--active-in-range");
|
|
3546
4058
|
if (descriptor.isInRange && !descriptor.isActive) cellClasses.push("ogrid-cell-in-range");
|
|
3547
4059
|
if (descriptor.isInCutRange) cellClasses.push("ogrid-cell-cut");
|
|
3548
4060
|
const styledContent = cellStyle ? h("span", { style: cellStyle }, content) : content;
|
|
@@ -3644,10 +4156,23 @@ function createDataGridTable(ui) {
|
|
|
3644
4156
|
...virtualScrollEnabled.value ? { "data-virtual-scroll": "" } : {}
|
|
3645
4157
|
}, [
|
|
3646
4158
|
// Header
|
|
3647
|
-
h(
|
|
3648
|
-
|
|
3649
|
-
|
|
3650
|
-
|
|
4159
|
+
h("thead", { class: stickyHeader ? "ogrid-thead ogrid-sticky-header" : "ogrid-thead" }, [
|
|
4160
|
+
// Column letter row (A, B, C...) for cell references
|
|
4161
|
+
...p.showColumnLetters ? [
|
|
4162
|
+
h("tr", { class: "ogrid-column-letter-row" }, [
|
|
4163
|
+
...hasCheckboxCol ? [h("th", { class: "ogrid-column-letter-cell" })] : [],
|
|
4164
|
+
...hasRowNumbersCol ? [h("th", { class: "ogrid-column-letter-cell" })] : [],
|
|
4165
|
+
...visibleCols.map((col, colIdx) => {
|
|
4166
|
+
const { classes: hdrCls, style: hdrSty } = getHeaderClassAndStyle(col);
|
|
4167
|
+
return h("th", {
|
|
4168
|
+
key: col.columnId,
|
|
4169
|
+
class: `ogrid-column-letter-cell ${hdrCls}`,
|
|
4170
|
+
style: hdrSty
|
|
4171
|
+
}, indexToColumnLetter(colIdx));
|
|
4172
|
+
})
|
|
4173
|
+
])
|
|
4174
|
+
] : [],
|
|
4175
|
+
...headerRows.map(
|
|
3651
4176
|
(row, rowIdx) => h("tr", { key: rowIdx, class: "ogrid-header-row" }, [
|
|
3652
4177
|
// Checkbox header cell
|
|
3653
4178
|
...rowIdx === headerRows.length - 1 && hasCheckboxCol ? [
|
|
@@ -3757,7 +4282,7 @@ function createDataGridTable(ui) {
|
|
|
3757
4282
|
})
|
|
3758
4283
|
])
|
|
3759
4284
|
)
|
|
3760
|
-
),
|
|
4285
|
+
]),
|
|
3761
4286
|
// Body
|
|
3762
4287
|
...!showEmptyInGrid ? [
|
|
3763
4288
|
h("tbody", {}, (() => {
|
|
@@ -3822,7 +4347,7 @@ function createDataGridTable(ui) {
|
|
|
3822
4347
|
left: hasCheckboxCol ? `${CHECKBOX_COLUMN_WIDTH}px` : "0",
|
|
3823
4348
|
zIndex: 2
|
|
3824
4349
|
}
|
|
3825
|
-
}, String(
|
|
4350
|
+
}, String(rowNumberOffset2 + rowIndex + 1))
|
|
3826
4351
|
] : [],
|
|
3827
4352
|
// Left spacer for column virtualization
|
|
3828
4353
|
...leftSpacerWidth > 0 ? [
|
|
@@ -3893,6 +4418,14 @@ function createDataGridTable(ui) {
|
|
|
3893
4418
|
columnSizingOverrides: layout.columnSizingOverrides,
|
|
3894
4419
|
columnOrder: p.columnOrder
|
|
3895
4420
|
}),
|
|
4421
|
+
// Formula reference overlay
|
|
4422
|
+
...p.formulaReferences && p.formulaReferences.length > 0 ? [
|
|
4423
|
+
h(FormulaRefOverlay, {
|
|
4424
|
+
containerEl: tableContainerRef.value,
|
|
4425
|
+
references: p.formulaReferences,
|
|
4426
|
+
colOffset: _colOffset
|
|
4427
|
+
})
|
|
4428
|
+
] : [],
|
|
3896
4429
|
// Column header menu
|
|
3897
4430
|
h(ui.ColumnHeaderMenu, {
|
|
3898
4431
|
isOpen: headerMenu.isOpen,
|
|
@@ -3978,6 +4511,7 @@ function createInlineCellEditor(options) {
|
|
|
3978
4511
|
dropdown.style.maxHeight = `${maxH}px`;
|
|
3979
4512
|
dropdown.style.zIndex = "9999";
|
|
3980
4513
|
dropdown.style.right = "auto";
|
|
4514
|
+
dropdown.style.textAlign = "left";
|
|
3981
4515
|
if (flipUp) {
|
|
3982
4516
|
dropdown.style.top = "auto";
|
|
3983
4517
|
dropdown.style.bottom = `${window.innerHeight - rect.top}px`;
|
|
@@ -4083,7 +4617,7 @@ function createInlineCellEditor(options) {
|
|
|
4083
4617
|
selectDropdownRef.value = el;
|
|
4084
4618
|
},
|
|
4085
4619
|
role: "listbox",
|
|
4086
|
-
style: { position: "absolute", top: "100%", left: "0", right: "0", maxHeight: "200px", overflowY: "auto", background: "var(--ogrid-bg, #fff)", border: "1px solid var(--ogrid-border, rgba(0,0,0,0.12))", zIndex: "10", boxShadow: "0 4px 16px rgba(0,0,0,0.2)" }
|
|
4620
|
+
style: { position: "absolute", top: "100%", left: "0", right: "0", maxHeight: "200px", overflowY: "auto", background: "var(--ogrid-bg, #fff)", border: "1px solid var(--ogrid-border, rgba(0,0,0,0.12))", zIndex: "10", boxShadow: "0 4px 16px rgba(0,0,0,0.2)", textAlign: "left" }
|
|
4087
4621
|
}, values.map(
|
|
4088
4622
|
(v, i) => h("div", {
|
|
4089
4623
|
key: String(v),
|
|
@@ -4493,8 +5027,12 @@ function createOGrid(ui) {
|
|
|
4493
5027
|
style: { padding: "8px 12px", borderBottom: "1px solid var(--ogrid-border, rgba(0,0,0,0.12))" }
|
|
4494
5028
|
}, [layout.value.toolbarBelow])
|
|
4495
5029
|
] : [],
|
|
5030
|
+
// Formula bar (between toolbar and grid)
|
|
5031
|
+
...layout.value.formulaBar ? [layout.value.formulaBar] : [],
|
|
4496
5032
|
// Main content area (sidebar + grid)
|
|
4497
5033
|
h("div", { style: { display: "flex", flex: "1", minHeight: "0" } }, mainAreaChildren),
|
|
5034
|
+
// Sheet tabs (between grid and footer)
|
|
5035
|
+
...layout.value.sheetTabs ? [layout.value.sheetTabs] : [],
|
|
4498
5036
|
// Footer strip (pagination)
|
|
4499
5037
|
h("div", {
|
|
4500
5038
|
style: {
|
|
@@ -4511,4 +5049,4 @@ function createOGrid(ui) {
|
|
|
4511
5049
|
});
|
|
4512
5050
|
}
|
|
4513
5051
|
|
|
4514
|
-
export { MarchingAntsOverlay, StatusBar, createDataGridTable, createInlineCellEditor, createOGrid, getCellInteractionProps, useActiveCell, useCellEditing, useCellSelection, useClipboard, useColumnChooserState, useColumnHeaderFilterState, useColumnHeaderMenuState, useColumnPinning, useColumnReorder, useColumnResize, useContextMenu, useDataGridState, useDataGridTableSetup, useDateFilterState, useDebounce, useDebouncedCallback, useFillHandle, useFilterOptions, useInlineCellEditorState, useKeyboardNavigation, useMultiSelectFilterState, useOGrid, usePeopleFilterState, useRichSelectState, useRowSelection, useSideBarState, useTableLayout, useTextFilterState, useUndoRedo, useVirtualScroll };
|
|
5052
|
+
export { FormulaBar, FormulaRefOverlay, MarchingAntsOverlay, SheetTabs, StatusBar, createDataGridTable, createInlineCellEditor, createOGrid, getCellInteractionProps, useActiveCell, useCellEditing, useCellSelection, useClipboard, useColumnChooserState, useColumnHeaderFilterState, useColumnHeaderMenuState, useColumnPinning, useColumnReorder, useColumnResize, useContextMenu, useDataGridState, useDataGridTableSetup, useDateFilterState, useDebounce, useDebouncedCallback, useFillHandle, useFilterOptions, useFormulaBar, useInlineCellEditorState, useKeyboardNavigation, useMultiSelectFilterState, useOGrid, usePeopleFilterState, useRichSelectState, useRowSelection, useSideBarState, useTableLayout, useTextFilterState, useUndoRedo, useVirtualScroll };
|
|
@@ -140,6 +140,24 @@
|
|
|
140
140
|
color: var(--ogrid-fg);
|
|
141
141
|
}
|
|
142
142
|
|
|
143
|
+
/* === Column letter row (cell references) === */
|
|
144
|
+
|
|
145
|
+
.ogrid-column-letter-row {
|
|
146
|
+
background: var(--ogrid-column-letter-bg, var(--ogrid-header-bg));
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
.ogrid-column-letter-cell {
|
|
150
|
+
text-align: center;
|
|
151
|
+
font-size: 11px;
|
|
152
|
+
font-weight: 500;
|
|
153
|
+
color: var(--ogrid-fg-muted, rgba(0, 0, 0, 0.5));
|
|
154
|
+
padding: 2px 4px;
|
|
155
|
+
background: var(--ogrid-column-letter-bg, var(--ogrid-header-bg));
|
|
156
|
+
border-bottom: 1px solid var(--ogrid-border, rgba(0, 0, 0, 0.12));
|
|
157
|
+
user-select: none;
|
|
158
|
+
font-variant-numeric: tabular-nums;
|
|
159
|
+
}
|
|
160
|
+
|
|
143
161
|
/* === Checkbox column === */
|
|
144
162
|
|
|
145
163
|
.ogrid-checkbox-header,
|
|
@@ -254,6 +272,14 @@
|
|
|
254
272
|
overflow: visible;
|
|
255
273
|
}
|
|
256
274
|
|
|
275
|
+
/* Active cell inside a selection range: suppress outline (Excel behavior).
|
|
276
|
+
The range overlay border is sufficient; the active cell is distinguished
|
|
277
|
+
by its white background vs. the dimmed range cells. */
|
|
278
|
+
.ogrid-cell-content--active-in-range {
|
|
279
|
+
outline: none;
|
|
280
|
+
background: var(--ogrid-bg, #fff);
|
|
281
|
+
}
|
|
282
|
+
|
|
257
283
|
/* === Editing cell wrapper === */
|
|
258
284
|
|
|
259
285
|
.ogrid-editing-cell {
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* FormulaBar — Headless Excel-style formula bar component.
|
|
3
|
+
*
|
|
4
|
+
* Layout: [Name Box] [fx] [Formula Input]
|
|
5
|
+
*
|
|
6
|
+
* Uses --ogrid-* CSS variables for theming.
|
|
7
|
+
*/
|
|
8
|
+
import { type PropType } from 'vue';
|
|
9
|
+
export interface FormulaBarProps {
|
|
10
|
+
/** Active cell reference (e.g. "A1"). */
|
|
11
|
+
cellRef: string | null;
|
|
12
|
+
/** Text displayed/edited in the formula input. */
|
|
13
|
+
formulaText: string;
|
|
14
|
+
/** Whether the input is in editing mode. */
|
|
15
|
+
isEditing: boolean;
|
|
16
|
+
}
|
|
17
|
+
export declare const FormulaBar: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
|
|
18
|
+
cellRef: {
|
|
19
|
+
type: PropType<string | null>;
|
|
20
|
+
default: null;
|
|
21
|
+
};
|
|
22
|
+
formulaText: {
|
|
23
|
+
type: StringConstructor;
|
|
24
|
+
required: true;
|
|
25
|
+
};
|
|
26
|
+
isEditing: {
|
|
27
|
+
type: BooleanConstructor;
|
|
28
|
+
required: true;
|
|
29
|
+
};
|
|
30
|
+
}>, () => import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
|
|
31
|
+
[key: string]: any;
|
|
32
|
+
}>, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, ("cancel" | "inputChange" | "commit" | "startEditing")[], "cancel" | "inputChange" | "commit" | "startEditing", import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
|
|
33
|
+
cellRef: {
|
|
34
|
+
type: PropType<string | null>;
|
|
35
|
+
default: null;
|
|
36
|
+
};
|
|
37
|
+
formulaText: {
|
|
38
|
+
type: StringConstructor;
|
|
39
|
+
required: true;
|
|
40
|
+
};
|
|
41
|
+
isEditing: {
|
|
42
|
+
type: BooleanConstructor;
|
|
43
|
+
required: true;
|
|
44
|
+
};
|
|
45
|
+
}>> & Readonly<{
|
|
46
|
+
onCancel?: ((...args: any[]) => any) | undefined;
|
|
47
|
+
onInputChange?: ((...args: any[]) => any) | undefined;
|
|
48
|
+
onCommit?: ((...args: any[]) => any) | undefined;
|
|
49
|
+
onStartEditing?: ((...args: any[]) => any) | undefined;
|
|
50
|
+
}>, {
|
|
51
|
+
cellRef: string | null;
|
|
52
|
+
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* FormulaRefOverlay — Renders colored border overlays on cells referenced by
|
|
3
|
+
* the active formula, like Excel's reference highlighting.
|
|
4
|
+
*
|
|
5
|
+
* Port of React's FormulaRefOverlay component.
|
|
6
|
+
*/
|
|
7
|
+
import { type PropType } from 'vue';
|
|
8
|
+
import { type FormulaReference } from '@alaarab/ogrid-core';
|
|
9
|
+
export declare const FormulaRefOverlay: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
|
|
10
|
+
containerEl: {
|
|
11
|
+
type: PropType<HTMLElement | null>;
|
|
12
|
+
default: null;
|
|
13
|
+
};
|
|
14
|
+
references: {
|
|
15
|
+
type: PropType<FormulaReference[]>;
|
|
16
|
+
required: true;
|
|
17
|
+
};
|
|
18
|
+
colOffset: {
|
|
19
|
+
type: NumberConstructor;
|
|
20
|
+
required: true;
|
|
21
|
+
};
|
|
22
|
+
}>, () => import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
|
|
23
|
+
[key: string]: any;
|
|
24
|
+
}>[] | null, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
|
|
25
|
+
containerEl: {
|
|
26
|
+
type: PropType<HTMLElement | null>;
|
|
27
|
+
default: null;
|
|
28
|
+
};
|
|
29
|
+
references: {
|
|
30
|
+
type: PropType<FormulaReference[]>;
|
|
31
|
+
required: true;
|
|
32
|
+
};
|
|
33
|
+
colOffset: {
|
|
34
|
+
type: NumberConstructor;
|
|
35
|
+
required: true;
|
|
36
|
+
};
|
|
37
|
+
}>> & Readonly<{}>, {
|
|
38
|
+
containerEl: HTMLElement | null;
|
|
39
|
+
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SheetTabs — Excel-style sheet tab bar at the bottom of the grid.
|
|
3
|
+
*
|
|
4
|
+
* Layout: [+] [Sheet1] [Sheet2] [Sheet3]
|
|
5
|
+
*/
|
|
6
|
+
import { type PropType } from 'vue';
|
|
7
|
+
import type { ISheetDef } from '@alaarab/ogrid-core';
|
|
8
|
+
export interface SheetTabsProps {
|
|
9
|
+
sheets: ISheetDef[];
|
|
10
|
+
activeSheet: string;
|
|
11
|
+
}
|
|
12
|
+
export declare const SheetTabs: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
|
|
13
|
+
sheets: {
|
|
14
|
+
type: PropType<ISheetDef[]>;
|
|
15
|
+
required: true;
|
|
16
|
+
};
|
|
17
|
+
activeSheet: {
|
|
18
|
+
type: StringConstructor;
|
|
19
|
+
required: true;
|
|
20
|
+
};
|
|
21
|
+
showAddButton: {
|
|
22
|
+
type: BooleanConstructor;
|
|
23
|
+
default: boolean;
|
|
24
|
+
};
|
|
25
|
+
}>, () => import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
|
|
26
|
+
[key: string]: any;
|
|
27
|
+
}>, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, ("sheetChange" | "sheetAdd")[], "sheetChange" | "sheetAdd", import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
|
|
28
|
+
sheets: {
|
|
29
|
+
type: PropType<ISheetDef[]>;
|
|
30
|
+
required: true;
|
|
31
|
+
};
|
|
32
|
+
activeSheet: {
|
|
33
|
+
type: StringConstructor;
|
|
34
|
+
required: true;
|
|
35
|
+
};
|
|
36
|
+
showAddButton: {
|
|
37
|
+
type: BooleanConstructor;
|
|
38
|
+
default: boolean;
|
|
39
|
+
};
|
|
40
|
+
}>> & Readonly<{
|
|
41
|
+
onSheetChange?: ((...args: any[]) => any) | undefined;
|
|
42
|
+
onSheetAdd?: ((...args: any[]) => any) | undefined;
|
|
43
|
+
}>, {
|
|
44
|
+
showAddButton: boolean;
|
|
45
|
+
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
|
|
@@ -30,6 +30,10 @@ export { useLatestRef } from './useLatestRef';
|
|
|
30
30
|
export type { MaybeShallowRef } from './useLatestRef';
|
|
31
31
|
export { useTableLayout } from './useTableLayout';
|
|
32
32
|
export type { UseTableLayoutParams, UseTableLayoutResult } from './useTableLayout';
|
|
33
|
+
export { useFormulaEngine } from './useFormulaEngine';
|
|
34
|
+
export type { UseFormulaEngineParams, UseFormulaEngineResult } from './useFormulaEngine';
|
|
35
|
+
export { useFormulaBar } from './useFormulaBar';
|
|
36
|
+
export type { UseFormulaBarParams, UseFormulaBarResult } from './useFormulaBar';
|
|
33
37
|
export { useColumnHeaderFilterState } from './useColumnHeaderFilterState';
|
|
34
38
|
export type { UseColumnHeaderFilterStateParams, UseColumnHeaderFilterStateResult, } from './useColumnHeaderFilterState';
|
|
35
39
|
export { useTextFilterState } from './useTextFilterState';
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* useFormulaBar — Vue composable for formula bar state.
|
|
3
|
+
*
|
|
4
|
+
* Manages the formula bar text, editing mode, and reference extraction.
|
|
5
|
+
*/
|
|
6
|
+
import { type Ref } from 'vue';
|
|
7
|
+
import { type FormulaReference } from '@alaarab/ogrid-core';
|
|
8
|
+
export interface UseFormulaBarParams {
|
|
9
|
+
/** Active cell column index (0-based). */
|
|
10
|
+
activeCol: Ref<number | null>;
|
|
11
|
+
/** Active cell row index (0-based). */
|
|
12
|
+
activeRow: Ref<number | null>;
|
|
13
|
+
/** Active cell reference string (e.g. "A1"). */
|
|
14
|
+
activeCellRef: Ref<string | null>;
|
|
15
|
+
/** Get formula string for a cell. */
|
|
16
|
+
getFormula?: (col: number, row: number) => string | undefined;
|
|
17
|
+
/** Get raw display value for a cell. */
|
|
18
|
+
getRawValue?: (col: number, row: number) => unknown;
|
|
19
|
+
/** Set formula for a cell. */
|
|
20
|
+
setFormula?: (col: number, row: number, formula: string | null) => void;
|
|
21
|
+
/** Commit a non-formula value change. */
|
|
22
|
+
onCellValueChanged?: (col: number, row: number, value: unknown) => void;
|
|
23
|
+
}
|
|
24
|
+
export interface UseFormulaBarResult {
|
|
25
|
+
/** Cell reference string (e.g. "A1"). */
|
|
26
|
+
cellRef: Ref<string | null>;
|
|
27
|
+
/** Text shown in the formula bar input. */
|
|
28
|
+
formulaText: Ref<string>;
|
|
29
|
+
/** Whether the formula bar input is being edited. */
|
|
30
|
+
isEditing: Ref<boolean>;
|
|
31
|
+
/** Update the formula bar input text. */
|
|
32
|
+
onInputChange: (text: string) => void;
|
|
33
|
+
/** Commit the current edit. */
|
|
34
|
+
onCommit: () => void;
|
|
35
|
+
/** Cancel the current edit. */
|
|
36
|
+
onCancel: () => void;
|
|
37
|
+
/** Start editing the formula bar. */
|
|
38
|
+
startEditing: () => void;
|
|
39
|
+
/** References extracted from the current formula text (for highlighting). */
|
|
40
|
+
referencedCells: Ref<FormulaReference[]>;
|
|
41
|
+
/** Whether the formula bar is actively being edited (for click-to-insert-ref guards). */
|
|
42
|
+
isFormulaBarEditing: Ref<boolean>;
|
|
43
|
+
}
|
|
44
|
+
export declare function useFormulaBar(params: UseFormulaBarParams): UseFormulaBarResult;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* useFormulaEngine — Vue composable for integrating the formula engine with the grid.
|
|
3
|
+
*
|
|
4
|
+
* Lazily creates a FormulaEngine instance when `formulas` ref is true.
|
|
5
|
+
* Provides accessor bridge between grid data and formula coordinates.
|
|
6
|
+
* Tree-shakeable: if `formulas` is false, no formula code is loaded.
|
|
7
|
+
*/
|
|
8
|
+
import { type Ref } from 'vue';
|
|
9
|
+
import { type IGridDataAccessor, type IFormulaFunction, type IRecalcResult, type IColumnDef, type IAuditEntry, type IAuditTrail } from '@alaarab/ogrid-core';
|
|
10
|
+
export interface UseFormulaEngineParams<T> {
|
|
11
|
+
/** Enable formula support. */
|
|
12
|
+
formulas?: Ref<boolean>;
|
|
13
|
+
/** Grid data items. */
|
|
14
|
+
items: Ref<T[]>;
|
|
15
|
+
/** Flat leaf columns (for mapping column index <-> columnId). */
|
|
16
|
+
flatColumns: Ref<IColumnDef<T>[]>;
|
|
17
|
+
/** Initial formulas to load. */
|
|
18
|
+
initialFormulas?: Array<{
|
|
19
|
+
col: number;
|
|
20
|
+
row: number;
|
|
21
|
+
formula: string;
|
|
22
|
+
}>;
|
|
23
|
+
/** Called when recalculation produces cascading updates. */
|
|
24
|
+
onFormulaRecalc?: (result: IRecalcResult) => void;
|
|
25
|
+
/** Custom formula functions. */
|
|
26
|
+
formulaFunctions?: Record<string, IFormulaFunction>;
|
|
27
|
+
/** Named ranges: name → cell/range reference string. */
|
|
28
|
+
namedRanges?: Record<string, string>;
|
|
29
|
+
/** Sheet accessors for cross-sheet references. */
|
|
30
|
+
sheets?: Record<string, IGridDataAccessor>;
|
|
31
|
+
}
|
|
32
|
+
export interface UseFormulaEngineResult {
|
|
33
|
+
/** Whether formula support is active. */
|
|
34
|
+
enabled: Ref<boolean>;
|
|
35
|
+
/** Get the formula engine's computed value for a cell coordinate. */
|
|
36
|
+
getFormulaValue: (col: number, row: number) => unknown;
|
|
37
|
+
/** Check if a cell has a formula. */
|
|
38
|
+
hasFormula: (col: number, row: number) => boolean;
|
|
39
|
+
/** Get the formula string for a cell. */
|
|
40
|
+
getFormula: (col: number, row: number) => string | undefined;
|
|
41
|
+
/** Set or clear a formula for a cell. Triggers recalculation. */
|
|
42
|
+
setFormula: (col: number, row: number, formula: string | null) => void;
|
|
43
|
+
/** Notify the engine that a non-formula cell value changed. Triggers dependent recalc. */
|
|
44
|
+
onCellChanged: (col: number, row: number) => void;
|
|
45
|
+
/** Get all cells that a cell depends on (deep, transitive). */
|
|
46
|
+
getPrecedents: (col: number, row: number) => IAuditEntry[];
|
|
47
|
+
/** Get all cells that depend on a cell (deep, transitive). */
|
|
48
|
+
getDependents: (col: number, row: number) => IAuditEntry[];
|
|
49
|
+
/** Get full audit trail for a cell. */
|
|
50
|
+
getAuditTrail: (col: number, row: number) => IAuditTrail | null;
|
|
51
|
+
}
|
|
52
|
+
export declare function useFormulaEngine<T>(params: UseFormulaEngineParams<T>): UseFormulaEngineResult;
|
|
@@ -31,6 +31,10 @@ export interface UseOGridLayout {
|
|
|
31
31
|
};
|
|
32
32
|
sideBarProps: SideBarProps | null;
|
|
33
33
|
fullScreen?: boolean;
|
|
34
|
+
/** Formula bar element (rendered between toolbar and grid). */
|
|
35
|
+
formulaBar?: unknown;
|
|
36
|
+
/** Sheet tabs element (rendered between grid and footer). */
|
|
37
|
+
sheetTabs?: unknown;
|
|
34
38
|
}
|
|
35
39
|
/** Filter state. */
|
|
36
40
|
export interface UseOGridFilters {
|
package/dist/types/index.d.ts
CHANGED
|
@@ -4,8 +4,11 @@ export type { ColumnFilterType, IColumnFilterDef, IColumnMeta, IColumnGroupDef,
|
|
|
4
4
|
export { toUserLike, isInSelectionRange, normalizeSelectionRange } from './types';
|
|
5
5
|
export { MarchingAntsOverlay } from './components/MarchingAntsOverlay';
|
|
6
6
|
export { StatusBar, type StatusBarProps } from './components/StatusBar';
|
|
7
|
-
export {
|
|
8
|
-
export
|
|
7
|
+
export { FormulaBar, type FormulaBarProps } from './components/FormulaBar';
|
|
8
|
+
export { SheetTabs, type SheetTabsProps } from './components/SheetTabs';
|
|
9
|
+
export { FormulaRefOverlay } from './components/FormulaRefOverlay';
|
|
10
|
+
export { useOGrid, useDataGridState, useActiveCell, useCellEditing, useCellSelection, useClipboard, useRowSelection, useKeyboardNavigation, useFillHandle, useUndoRedo, useContextMenu, useColumnResize, useColumnReorder, useVirtualScroll, useFilterOptions, useDebounce, useDebouncedCallback, useTableLayout, useColumnHeaderFilterState, useTextFilterState, useMultiSelectFilterState, usePeopleFilterState, useDateFilterState, useColumnChooserState, useInlineCellEditorState, useRichSelectState, useSideBarState, useColumnPinning, useColumnHeaderMenuState, useDataGridTableSetup, useFormulaBar, } from './composables';
|
|
11
|
+
export type { UseOGridResult, UseOGridPagination, UseOGridColumnChooser, UseOGridLayout, UseOGridFilters, ColumnChooserPlacement, UseDataGridStateParams, UseDataGridStateResult, DataGridLayoutState, DataGridRowSelectionState, DataGridEditingState, DataGridCellInteractionState, DataGridContextMenuState, DataGridViewModelState, DataGridPinningState, UseActiveCellResult, EditingCell, UseCellEditingParams, UseCellEditingResult, UseCellSelectionParams, UseCellSelectionResult, UseClipboardParams, UseClipboardResult, UseRowSelectionParams, UseRowSelectionResult, UseKeyboardNavigationParams, UseKeyboardNavigationResult, UseFillHandleParams, UseFillHandleResult, UseUndoRedoParams, UseUndoRedoResult, ContextMenuPosition, UseContextMenuResult, UseColumnResizeParams, UseColumnResizeResult, UseColumnReorderParams, UseColumnReorderResult, UseVirtualScrollParams, UseVirtualScrollResult, UseFilterOptionsResult, UseTableLayoutParams, UseTableLayoutResult, UseColumnHeaderFilterStateParams, UseColumnHeaderFilterStateResult, UseTextFilterStateParams, UseTextFilterStateResult, UseMultiSelectFilterStateParams, UseMultiSelectFilterStateResult, UsePeopleFilterStateParams, UsePeopleFilterStateResult, UseDateFilterStateParams, UseDateFilterStateResult, UseColumnChooserStateParams, UseColumnChooserStateResult, InlineCellEditorType, UseInlineCellEditorStateParams, UseInlineCellEditorStateResult, UseRichSelectStateParams, UseRichSelectStateResult, UseSideBarStateParams, UseSideBarStateResult, DebouncedFn, UseColumnPinningParams, UseColumnPinningResult, UseColumnHeaderMenuStateParams, UseColumnHeaderMenuStateResult, UseDataGridTableSetupParams, UseDataGridTableSetupResult, MaybeShallowRef, UseFormulaBarParams, UseFormulaBarResult, } from './composables';
|
|
9
12
|
export { getHeaderFilterConfig, getCellRenderDescriptor, resolveCellDisplayContent, resolveCellStyle, buildInlineEditorProps, buildPopoverEditorProps, getCellInteractionProps, } from './utils';
|
|
10
13
|
export type { HeaderFilterConfigInput, HeaderFilterConfig, CellRenderDescriptorInput, CellRenderDescriptor, CellRenderMode, CellInteractionHandlers, CellInteractionProps, } from './utils';
|
|
11
14
|
export { createDataGridTable, type IDataGridTableUIBindings } from './components/createDataGridTable';
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { IColumnDef, IColumnGroupDef, ICellValueChangedEvent } from './columnTypes';
|
|
2
|
-
export type { RowId, UserLike, UserLikeInput, FilterValue, IFilters, IFetchParams, IPageResult, IDataSource, IGridColumnState, RowSelectionMode, IRowSelectionChangeEvent, StatusBarPanel, IStatusBarProps, IActiveCell, ISelectionRange, SideBarPanelId, ISideBarDef, IOGridApi, IVirtualScrollConfig, } from '@alaarab/ogrid-core';
|
|
2
|
+
export type { RowId, UserLike, UserLikeInput, FilterValue, IFilters, IFetchParams, IPageResult, IDataSource, IGridColumnState, RowSelectionMode, IRowSelectionChangeEvent, StatusBarPanel, IStatusBarProps, IActiveCell, ISelectionRange, SideBarPanelId, ISideBarDef, IOGridApi, IVirtualScrollConfig, IFormulaFunction, IRecalcResult, IGridDataAccessor, IAuditEntry, IAuditTrail, ISheetDef, FormulaReference, } from '@alaarab/ogrid-core';
|
|
3
3
|
export { toUserLike, isInSelectionRange, normalizeSelectionRange } from '@alaarab/ogrid-core';
|
|
4
|
-
import type { RowId, UserLike, IFilters, FilterValue, RowSelectionMode, IRowSelectionChangeEvent, IStatusBarProps, IDataSource, ISideBarDef, IVirtualScrollConfig } from '@alaarab/ogrid-core';
|
|
4
|
+
import type { RowId, UserLike, IFilters, FilterValue, RowSelectionMode, IRowSelectionChangeEvent, IStatusBarProps, IDataSource, ISideBarDef, IVirtualScrollConfig, IFormulaFunction, IRecalcResult, IGridDataAccessor, IAuditEntry, IAuditTrail, ISheetDef, FormulaReference } from '@alaarab/ogrid-core';
|
|
5
5
|
/** Base props shared by both client-side and server-side OGrid modes. */
|
|
6
6
|
interface IOGridBaseProps<T> {
|
|
7
7
|
columns: (IColumnDef<T> | IColumnGroupDef<T>)[];
|
|
@@ -42,6 +42,8 @@ interface IOGridBaseProps<T> {
|
|
|
42
42
|
onSelectionChange?: (event: IRowSelectionChangeEvent<T>) => void;
|
|
43
43
|
/** Show Excel-style row numbers column at the start of the grid (1, 2, 3...). Default: false. */
|
|
44
44
|
showRowNumbers?: boolean;
|
|
45
|
+
/** Enable Excel-style cell references: column letter headers, row numbers, and name box. Implies showRowNumbers. */
|
|
46
|
+
cellReferences?: boolean;
|
|
45
47
|
statusBar?: boolean | IStatusBarProps;
|
|
46
48
|
defaultPageSize?: number;
|
|
47
49
|
defaultSortBy?: string;
|
|
@@ -87,6 +89,30 @@ interface IOGridBaseProps<T> {
|
|
|
87
89
|
rowHeight?: number;
|
|
88
90
|
/** Cell spacing/density preset. Controls cell padding throughout the grid. Default: 'normal'. */
|
|
89
91
|
density?: 'compact' | 'normal' | 'comfortable';
|
|
92
|
+
/** Enable Excel-like formula support. When true, cells starting with '=' are treated as formulas. Default: false. */
|
|
93
|
+
formulas?: boolean;
|
|
94
|
+
/** Initial formulas to load when the formula engine initializes. */
|
|
95
|
+
initialFormulas?: Array<{
|
|
96
|
+
col: number;
|
|
97
|
+
row: number;
|
|
98
|
+
formula: string;
|
|
99
|
+
}>;
|
|
100
|
+
/** Called when formula recalculation produces updated cell values (e.g. cascade from an edited cell). */
|
|
101
|
+
onFormulaRecalc?: (result: IRecalcResult) => void;
|
|
102
|
+
/** Custom formula functions to register with the formula engine (e.g. { MYFUNC: { minArgs: 1, maxArgs: 1, evaluate: ... } }). */
|
|
103
|
+
formulaFunctions?: Record<string, IFormulaFunction>;
|
|
104
|
+
/** Named ranges for the formula engine: name → cell/range ref string (e.g. { Revenue: 'A1:A10' }). */
|
|
105
|
+
namedRanges?: Record<string, string>;
|
|
106
|
+
/** Sheet accessors for cross-sheet formula references (e.g. { Sheet2: accessor }). */
|
|
107
|
+
sheets?: Record<string, IGridDataAccessor>;
|
|
108
|
+
/** Sheet definitions for the tab bar at grid bottom. */
|
|
109
|
+
sheetDefs?: ISheetDef[];
|
|
110
|
+
/** Active sheet ID (controlled). */
|
|
111
|
+
activeSheet?: string;
|
|
112
|
+
/** Called when user clicks a sheet tab. */
|
|
113
|
+
onSheetChange?: (sheetId: string) => void;
|
|
114
|
+
/** Called when user clicks the "+" add sheet button. */
|
|
115
|
+
onSheetAdd?: () => void;
|
|
90
116
|
'aria-label'?: string;
|
|
91
117
|
'aria-labelledby'?: string;
|
|
92
118
|
}
|
|
@@ -144,6 +170,10 @@ export interface IOGridDataGridProps<T> {
|
|
|
144
170
|
selectedRows?: Set<RowId>;
|
|
145
171
|
onSelectionChange?: (event: IRowSelectionChangeEvent<T>) => void;
|
|
146
172
|
showRowNumbers?: boolean;
|
|
173
|
+
showColumnLetters?: boolean;
|
|
174
|
+
showNameBox?: boolean;
|
|
175
|
+
/** Callback when the active cell changes. Used by the name box to display the current cell reference. */
|
|
176
|
+
onActiveCellChange?: (ref: string | null) => void;
|
|
147
177
|
currentPage?: number;
|
|
148
178
|
pageSize?: number;
|
|
149
179
|
statusBar?: IStatusBarProps;
|
|
@@ -175,4 +205,26 @@ export interface IOGridDataGridProps<T> {
|
|
|
175
205
|
'aria-labelledby'?: string;
|
|
176
206
|
/** Custom keydown handler. Called before grid's built-in handling. Call event.preventDefault() to suppress grid default. */
|
|
177
207
|
onKeyDown?: (event: KeyboardEvent) => void;
|
|
208
|
+
/** Enable formula support. When true, cell values starting with '=' are treated as formulas. */
|
|
209
|
+
formulas?: boolean;
|
|
210
|
+
/** Get the formula engine's computed value for a cell, or undefined if no formula. */
|
|
211
|
+
getFormulaValue?: (col: number, row: number) => unknown;
|
|
212
|
+
/** Check if a cell has a formula. */
|
|
213
|
+
hasFormula?: (col: number, row: number) => boolean;
|
|
214
|
+
/** Get the formula string for a cell. */
|
|
215
|
+
getFormula?: (col: number, row: number) => string | undefined;
|
|
216
|
+
/** Set a formula for a cell (called from edit commit when value starts with '='). */
|
|
217
|
+
setFormula?: (col: number, row: number, formula: string | null) => void;
|
|
218
|
+
/** Notify the formula engine that a non-formula cell changed. */
|
|
219
|
+
onFormulaCellChanged?: (col: number, row: number) => void;
|
|
220
|
+
/** Get all cells that a cell depends on (deep, transitive). */
|
|
221
|
+
getPrecedents?: (col: number, row: number) => IAuditEntry[];
|
|
222
|
+
/** Get all cells that depend on a cell (deep, transitive). */
|
|
223
|
+
getDependents?: (col: number, row: number) => IAuditEntry[];
|
|
224
|
+
/** Get full audit trail for a cell. */
|
|
225
|
+
getAuditTrail?: (col: number, row: number) => IAuditTrail | null;
|
|
226
|
+
/** Monotonic counter incremented on each formula recalculation — used for cache invalidation. */
|
|
227
|
+
formulaVersion?: number;
|
|
228
|
+
/** Cell references to highlight (from active formula in formula bar). */
|
|
229
|
+
formulaReferences?: FormulaReference[];
|
|
178
230
|
}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
export type { ColumnFilterType, IColumnFilterDef, IColumnMeta, IColumnDef, IColumnGroupDef, IColumnDefinition, ICellValueChangedEvent, ICellEditorProps, CellEditorParams, IValueParserParams, IDateFilterValue, HeaderCell, HeaderRow, } from './columnTypes';
|
|
2
|
-
export type { RowId, UserLike, UserLikeInput, FilterValue, IFilters, IFetchParams, IPageResult, IDataSource, IGridColumnState, IOGridApi, IOGridProps, IOGridClientProps, IOGridServerProps, IOGridDataGridProps, RowSelectionMode, IRowSelectionChangeEvent, StatusBarPanel, IStatusBarProps, IActiveCell, ISelectionRange, SideBarPanelId, ISideBarDef, IVirtualScrollConfig, } from './dataGridTypes';
|
|
2
|
+
export type { RowId, UserLike, UserLikeInput, FilterValue, IFilters, IFetchParams, IPageResult, IDataSource, IGridColumnState, IOGridApi, IOGridProps, IOGridClientProps, IOGridServerProps, IOGridDataGridProps, RowSelectionMode, IRowSelectionChangeEvent, StatusBarPanel, IStatusBarProps, IActiveCell, ISelectionRange, SideBarPanelId, ISideBarDef, IVirtualScrollConfig, ISheetDef, FormulaReference, } from './dataGridTypes';
|
|
3
3
|
export { toUserLike, isInSelectionRange, normalizeSelectionRange } from './dataGridTypes';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@alaarab/ogrid-vue",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.4.0",
|
|
4
4
|
"description": "OGrid Vue – Vue 3 composables, headless components, and utilities for OGrid data grids.",
|
|
5
5
|
"main": "dist/esm/index.js",
|
|
6
6
|
"module": "dist/esm/index.js",
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
"node": ">=18"
|
|
37
37
|
},
|
|
38
38
|
"dependencies": {
|
|
39
|
-
"@alaarab/ogrid-core": "2.
|
|
39
|
+
"@alaarab/ogrid-core": "2.4.0"
|
|
40
40
|
},
|
|
41
41
|
"peerDependencies": {
|
|
42
42
|
"vue": "^3.3.0"
|