@alaarab/ogrid-angular 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 +854 -28
- package/dist/types/components/base-column-header-menu.component.d.ts +30 -0
- package/dist/types/components/base-datagrid-table.component.d.ts +4 -1
- package/dist/types/components/formula-bar.component.d.ts +29 -0
- package/dist/types/components/formula-ref-overlay.component.d.ts +27 -0
- package/dist/types/components/inline-cell-editor-template.d.ts +1 -1
- package/dist/types/components/ogrid-layout.component.d.ts +9 -0
- package/dist/types/components/sheet-tabs.component.d.ts +16 -0
- package/dist/types/index.d.ts +7 -1
- package/dist/types/services/datagrid-state.service.d.ts +3 -0
- package/dist/types/services/formula-engine.service.d.ts +131 -0
- package/dist/types/services/ogrid.service.d.ts +66 -1
- package/dist/types/types/dataGridTypes.d.ts +53 -2
- package/dist/types/types/index.d.ts +1 -1
- package/package.json +2 -2
package/dist/esm/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { flattenColumns, getMultiSelectFilterFields, deriveFilterOptionsFromData, processClientSideData, validateColumns, processClientSideDataAsync, validateRowIds, computeNextSortState, mergeFilter, CHECKBOX_COLUMN_WIDTH, DEFAULT_MIN_COLUMN_WIDTH, CELL_PADDING, parseValue, UndoRedoStack, rangesEqual, normalizeSelectionRange, formatSelectionAsTsv, parseTsvClipboard, getCellValue, computeTabNavigation, applyFillValues, computeAggregations, getDataGridStatusBarConfig, computeVisibleRange, computeTotalHeight, computeVisibleColumnRange,
|
|
1
|
+
import { handleFormulaBarKeyDown, FormulaEngine, createGridDataAccessor, columnLetterToIndex, flattenColumns, getMultiSelectFilterFields, deriveFilterOptionsFromData, processClientSideData, deriveFormulaBarText, extractFormulaReferences, validateColumns, processClientSideDataAsync, validateRowIds, computeNextSortState, mergeFilter, CHECKBOX_COLUMN_WIDTH, DEFAULT_MIN_COLUMN_WIDTH, CELL_PADDING, parseValue, UndoRedoStack, rangesEqual, normalizeSelectionRange, formatSelectionAsTsv, parseTsvClipboard, getCellValue, applyCellDeletion, getScrollTopForRow, computeTabNavigation, applyFillValues, computeAggregations, getDataGridStatusBarConfig, computeVisibleRange, computeTotalHeight, computeVisibleColumnRange, validateVirtualScrollConfig, GRID_BORDER_RADIUS, getStatusBarParts, GRID_CONTEXT_MENU_ITEMS, formatShortcut, injectGlobalStyles, partitionColumnsForVirtualization, buildHeaderRows, getHeaderFilterConfig, getCellRenderDescriptor, resolveCellDisplayContent, resolveCellStyle, buildPopoverEditorProps, measureColumnContentWidth, getPaginationViewModel, getColumnHeaderMenuItems, ROW_NUMBER_COLUMN_WIDTH, reorderColumnArray, findCtrlArrowTarget, measureRange, FORMULA_REF_COLORS } from '@alaarab/ogrid-core';
|
|
2
2
|
export * from '@alaarab/ogrid-core';
|
|
3
3
|
export { CELL_PADDING, CHECKBOX_COLUMN_WIDTH, DEFAULT_DEBOUNCE_MS, DEFAULT_MIN_COLUMN_WIDTH, GRID_BORDER_RADIUS, PEOPLE_SEARCH_DEBOUNCE_MS, ROW_NUMBER_COLUMN_WIDTH, SIDEBAR_TRANSITION_MS, Z_INDEX, debounce, getCellRenderDescriptor, getHeaderFilterConfig, isInSelectionRange, normalizeSelectionRange, resolveCellDisplayContent, resolveCellStyle, toUserLike } from '@alaarab/ogrid-core';
|
|
4
|
-
import { Injectable, Input, Component, ChangeDetectionStrategy, ViewEncapsulation, Output, ViewChild, inject, DestroyRef, signal, computed,
|
|
4
|
+
import { Injectable, Input, Component, ChangeDetectionStrategy, ViewEncapsulation, Output, ViewChild, input, output, viewChild, effect, inject, DestroyRef, signal, computed, NgZone, EventEmitter, Injector, EnvironmentInjector, createComponent } from '@angular/core';
|
|
5
5
|
import { NgTemplateOutlet } from '@angular/common';
|
|
6
6
|
|
|
7
7
|
var __defProp = Object.defineProperty;
|
|
@@ -14,6 +14,201 @@ var __decorateClass = (decorators, target, key, kind) => {
|
|
|
14
14
|
if (kind && result) __defProp(target, key, result);
|
|
15
15
|
return result;
|
|
16
16
|
};
|
|
17
|
+
var FormulaEngineService = class {
|
|
18
|
+
constructor() {
|
|
19
|
+
this.destroyRef = inject(DestroyRef);
|
|
20
|
+
// --- Internal state ---
|
|
21
|
+
this.engine = null;
|
|
22
|
+
this.initialLoaded = false;
|
|
23
|
+
// --- Data references (updated via configure or setData) ---
|
|
24
|
+
this.items = [];
|
|
25
|
+
this.flatColumns = [];
|
|
26
|
+
// --- Signals ---
|
|
27
|
+
/** Whether formula support is currently enabled. */
|
|
28
|
+
this.enabled = signal(false);
|
|
29
|
+
/** Last recalculation result, for UI to react to formula changes. */
|
|
30
|
+
this.lastRecalcResult = signal(null);
|
|
31
|
+
/** Number of formulas currently registered. */
|
|
32
|
+
this.formulaCount = computed(() => {
|
|
33
|
+
this.lastRecalcResult();
|
|
34
|
+
return this.engine?.getAllFormulas().length ?? 0;
|
|
35
|
+
});
|
|
36
|
+
this.destroyRef.onDestroy(() => {
|
|
37
|
+
this.engine?.clear();
|
|
38
|
+
this.engine = null;
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Configure the formula engine. Call this when the grid component initializes.
|
|
43
|
+
*
|
|
44
|
+
* Lazily creates the FormulaEngine only when `formulas: true`.
|
|
45
|
+
*/
|
|
46
|
+
configure(options) {
|
|
47
|
+
const { formulas, initialFormulas, formulaFunctions, onFormulaRecalc, namedRanges, sheets } = options;
|
|
48
|
+
this.onFormulaRecalcFn = onFormulaRecalc;
|
|
49
|
+
if (formulas && !this.engine) {
|
|
50
|
+
this.engine = new FormulaEngine({
|
|
51
|
+
customFunctions: formulaFunctions,
|
|
52
|
+
namedRanges
|
|
53
|
+
});
|
|
54
|
+
if (sheets) {
|
|
55
|
+
for (const [name, accessor] of Object.entries(sheets)) {
|
|
56
|
+
this.engine.registerSheet(name, accessor);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
this.enabled.set(true);
|
|
60
|
+
} else if (!formulas && this.engine) {
|
|
61
|
+
this.engine.clear();
|
|
62
|
+
this.engine = null;
|
|
63
|
+
this.enabled.set(false);
|
|
64
|
+
this.lastRecalcResult.set(null);
|
|
65
|
+
this.initialLoaded = false;
|
|
66
|
+
}
|
|
67
|
+
if (formulas && this.engine && initialFormulas && !this.initialLoaded) {
|
|
68
|
+
this.initialLoaded = true;
|
|
69
|
+
const accessor = this.createAccessor();
|
|
70
|
+
const result = this.engine.loadFormulas(initialFormulas, accessor);
|
|
71
|
+
if (result.updatedCells.length > 0) {
|
|
72
|
+
this.lastRecalcResult.set(result);
|
|
73
|
+
this.onFormulaRecalcFn?.(result);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Update the data references used by the accessor bridge.
|
|
79
|
+
* Call this whenever the grid's items or columns change.
|
|
80
|
+
*/
|
|
81
|
+
setData(items, flatColumns) {
|
|
82
|
+
this.items = items;
|
|
83
|
+
this.flatColumns = flatColumns;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Set or clear a formula for a cell. Triggers recalculation of dependents.
|
|
87
|
+
*/
|
|
88
|
+
setFormula(col, row, formula, accessor) {
|
|
89
|
+
if (!this.engine) return;
|
|
90
|
+
const acc = accessor ?? this.createAccessor();
|
|
91
|
+
const result = this.engine.setFormula(col, row, formula, acc);
|
|
92
|
+
if (result.updatedCells.length > 0) {
|
|
93
|
+
this.lastRecalcResult.set(result);
|
|
94
|
+
this.onFormulaRecalcFn?.(result);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Notify the engine that a non-formula cell's value changed.
|
|
99
|
+
* Triggers recalculation of any formulas that depend on this cell.
|
|
100
|
+
*/
|
|
101
|
+
onCellChanged(col, row, accessor) {
|
|
102
|
+
if (!this.engine) return;
|
|
103
|
+
const acc = accessor ?? this.createAccessor();
|
|
104
|
+
const result = this.engine.onCellChanged(col, row, acc);
|
|
105
|
+
if (result.updatedCells.length > 0) {
|
|
106
|
+
this.lastRecalcResult.set(result);
|
|
107
|
+
this.onFormulaRecalcFn?.(result);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Get the formula engine's computed value for a cell coordinate.
|
|
112
|
+
*/
|
|
113
|
+
getValue(col, row) {
|
|
114
|
+
return this.engine?.getValue(col, row);
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Check if a cell has a formula.
|
|
118
|
+
*/
|
|
119
|
+
hasFormula(col, row) {
|
|
120
|
+
return this.engine?.hasFormula(col, row) ?? false;
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Get the formula string for a cell.
|
|
124
|
+
*/
|
|
125
|
+
getFormula(col, row) {
|
|
126
|
+
return this.engine?.getFormula(col, row);
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Trigger a full recalculation of all formulas.
|
|
130
|
+
*/
|
|
131
|
+
recalcAll(accessor) {
|
|
132
|
+
if (!this.engine) return;
|
|
133
|
+
const acc = accessor ?? this.createAccessor();
|
|
134
|
+
const result = this.engine.recalcAll(acc);
|
|
135
|
+
if (result.updatedCells.length > 0) {
|
|
136
|
+
this.lastRecalcResult.set(result);
|
|
137
|
+
this.onFormulaRecalcFn?.(result);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Get all formulas for serialization.
|
|
142
|
+
*/
|
|
143
|
+
getAllFormulas() {
|
|
144
|
+
return this.engine?.getAllFormulas() ?? [];
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Register a custom function at runtime.
|
|
148
|
+
*/
|
|
149
|
+
registerFunction(name, fn) {
|
|
150
|
+
this.engine?.registerFunction(name, fn);
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Clear all formulas and cached values.
|
|
154
|
+
*/
|
|
155
|
+
clear() {
|
|
156
|
+
this.engine?.clear();
|
|
157
|
+
this.lastRecalcResult.set(null);
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Define a named range.
|
|
161
|
+
*/
|
|
162
|
+
defineNamedRange(name, ref) {
|
|
163
|
+
this.engine?.defineNamedRange(name, ref);
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Remove a named range.
|
|
167
|
+
*/
|
|
168
|
+
removeNamedRange(name) {
|
|
169
|
+
this.engine?.removeNamedRange(name);
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Register a sheet accessor for cross-sheet references.
|
|
173
|
+
*/
|
|
174
|
+
registerSheet(name, accessor) {
|
|
175
|
+
this.engine?.registerSheet(name, accessor);
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Unregister a sheet accessor.
|
|
179
|
+
*/
|
|
180
|
+
unregisterSheet(name) {
|
|
181
|
+
this.engine?.unregisterSheet(name);
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Get all cells that a cell depends on (deep, transitive).
|
|
185
|
+
*/
|
|
186
|
+
getPrecedents(col, row) {
|
|
187
|
+
return this.engine?.getPrecedents(col, row) ?? [];
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Get all cells that depend on a cell (deep, transitive).
|
|
191
|
+
*/
|
|
192
|
+
getDependents(col, row) {
|
|
193
|
+
return this.engine?.getDependents(col, row) ?? [];
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Get full audit trail for a cell.
|
|
197
|
+
*/
|
|
198
|
+
getAuditTrail(col, row) {
|
|
199
|
+
return this.engine?.getAuditTrail(col, row) ?? null;
|
|
200
|
+
}
|
|
201
|
+
// --- Private helpers ---
|
|
202
|
+
/** Create a data accessor that bridges grid data to formula coordinates. */
|
|
203
|
+
createAccessor() {
|
|
204
|
+
return createGridDataAccessor(this.items, this.flatColumns);
|
|
205
|
+
}
|
|
206
|
+
};
|
|
207
|
+
FormulaEngineService = __decorateClass([
|
|
208
|
+
Injectable()
|
|
209
|
+
], FormulaEngineService);
|
|
210
|
+
|
|
211
|
+
// src/services/ogrid.service.ts
|
|
17
212
|
var DEFAULT_PAGE_SIZE = 25;
|
|
18
213
|
var EMPTY_LOADING_OPTIONS = {};
|
|
19
214
|
var DEFAULT_PANELS = ["columns", "filters"];
|
|
@@ -76,6 +271,52 @@ var OGridService = class {
|
|
|
76
271
|
this.ariaLabel = signal(void 0);
|
|
77
272
|
this.ariaLabelledBy = signal(void 0);
|
|
78
273
|
this.workerSort = signal(false);
|
|
274
|
+
this.showRowNumbers = signal(false);
|
|
275
|
+
this.cellReferences = signal(false);
|
|
276
|
+
this.formulasEnabled = signal(false);
|
|
277
|
+
this.initialFormulas = signal(void 0);
|
|
278
|
+
this.onFormulaRecalc = signal(void 0);
|
|
279
|
+
this.formulaFunctions = signal(void 0);
|
|
280
|
+
this.namedRanges = signal(void 0);
|
|
281
|
+
this.sheets = signal(void 0);
|
|
282
|
+
this.sheetDefs = signal(void 0);
|
|
283
|
+
this.activeSheet = signal(void 0);
|
|
284
|
+
this.onSheetChange = signal(void 0);
|
|
285
|
+
this.onSheetAdd = signal(void 0);
|
|
286
|
+
/** Active cell reference string (e.g. 'A1') updated by DataGridTable when cellReferences is enabled. */
|
|
287
|
+
this.activeCellRef = signal(null);
|
|
288
|
+
/** Active cell coordinates (0-based col/row). */
|
|
289
|
+
this.activeCellCoords = signal(null);
|
|
290
|
+
/** Stable callback passed to DataGridTable to update activeCellRef + coords. */
|
|
291
|
+
this.handleActiveCellChange = (ref) => {
|
|
292
|
+
this.activeCellRef.set(ref);
|
|
293
|
+
if (ref) {
|
|
294
|
+
const m = ref.match(/^([A-Z]+)(\d+)$/);
|
|
295
|
+
if (m) {
|
|
296
|
+
this.activeCellCoords.set({ col: columnLetterToIndex(m[1]), row: parseInt(m[2], 10) - 1 });
|
|
297
|
+
} else {
|
|
298
|
+
this.activeCellCoords.set(null);
|
|
299
|
+
}
|
|
300
|
+
} else {
|
|
301
|
+
this.activeCellCoords.set(null);
|
|
302
|
+
}
|
|
303
|
+
};
|
|
304
|
+
// --- Formula bar state ---
|
|
305
|
+
this.formulaBarEditing = signal(false);
|
|
306
|
+
this.formulaBarEditText = signal("");
|
|
307
|
+
// --- Formula engine ---
|
|
308
|
+
this.formulaService = new FormulaEngineService();
|
|
309
|
+
/** Monotonic counter incremented on formula recalculation — drives cache invalidation. */
|
|
310
|
+
this.formulaVersion = signal(0);
|
|
311
|
+
// Stable formula method references for dataGridProps (avoid per-recompute arrow functions)
|
|
312
|
+
this.getFormulaValueFn = (col, row) => this.formulaService.getValue(col, row);
|
|
313
|
+
this.hasFormulaFn = (col, row) => this.formulaService.hasFormula(col, row);
|
|
314
|
+
this.getFormulaFn = (col, row) => this.formulaService.getFormula(col, row);
|
|
315
|
+
this.setFormulaFn = (col, row, formula) => this.formulaService.setFormula(col, row, formula ?? "");
|
|
316
|
+
this.onFormulaCellChangedFn = (col, row) => this.formulaService.onCellChanged(col, row);
|
|
317
|
+
this.getPrecedentsFn = (col, row) => this.formulaService.getPrecedents(col, row);
|
|
318
|
+
this.getDependentsFn = (col, row) => this.formulaService.getDependents(col, row);
|
|
319
|
+
this.getAuditTrailFn = (col, row) => this.formulaService.getAuditTrail(col, row);
|
|
79
320
|
// --- Internal state signals ---
|
|
80
321
|
this.internalData = signal([]);
|
|
81
322
|
this.internalLoading = signal(false);
|
|
@@ -228,6 +469,52 @@ var OGridService = class {
|
|
|
228
469
|
toggle: (panel) => this.sideBarActivePanel.update((p) => p === panel ? null : panel),
|
|
229
470
|
close: () => this.sideBarActivePanel.set(null)
|
|
230
471
|
}));
|
|
472
|
+
// --- Formula bar derived state ---
|
|
473
|
+
/** Display text derived from active cell (formula string or raw value). */
|
|
474
|
+
this.formulaBarDisplayText = computed(() => {
|
|
475
|
+
const coords = this.activeCellCoords();
|
|
476
|
+
if (!coords) return "";
|
|
477
|
+
const getFormula = this.formulaService.enabled() ? (c, r) => this.formulaService.getFormula(c, r) : void 0;
|
|
478
|
+
const items = this.displayItems();
|
|
479
|
+
const cols = this.columns();
|
|
480
|
+
const getRawValue = (c, r) => {
|
|
481
|
+
if (r < 0 || r >= items.length || c < 0 || c >= cols.length) return void 0;
|
|
482
|
+
return getCellValue(items[r], cols[c]);
|
|
483
|
+
};
|
|
484
|
+
return deriveFormulaBarText(coords.col, coords.row, getFormula, getRawValue);
|
|
485
|
+
});
|
|
486
|
+
/** Formula text shown in the bar: edit text when editing, display text otherwise. */
|
|
487
|
+
this.formulaBarText = computed(
|
|
488
|
+
() => this.formulaBarEditing() ? this.formulaBarEditText() : this.formulaBarDisplayText()
|
|
489
|
+
);
|
|
490
|
+
/** References extracted from the current formula text (for highlighting). */
|
|
491
|
+
this.formulaBarReferences = computed(
|
|
492
|
+
() => extractFormulaReferences(this.formulaBarText())
|
|
493
|
+
);
|
|
494
|
+
// Stable formula bar callbacks (avoid new closures per computed)
|
|
495
|
+
this.formulaBarOnInputChangeFn = (text) => {
|
|
496
|
+
this.formulaBarEditText.set(text);
|
|
497
|
+
};
|
|
498
|
+
this.formulaBarOnCommitFn = () => {
|
|
499
|
+
this.commitFormulaBar();
|
|
500
|
+
};
|
|
501
|
+
this.formulaBarOnCancelFn = () => {
|
|
502
|
+
this.cancelFormulaBar();
|
|
503
|
+
};
|
|
504
|
+
this.formulaBarStartEditingFn = () => {
|
|
505
|
+
this.startFormulaBarEditing();
|
|
506
|
+
};
|
|
507
|
+
/** Aggregate formula bar state for template consumption. */
|
|
508
|
+
this.formulaBarState = computed(() => ({
|
|
509
|
+
cellRef: this.activeCellRef(),
|
|
510
|
+
formulaText: this.formulaBarText(),
|
|
511
|
+
isEditing: this.formulaBarEditing(),
|
|
512
|
+
onInputChange: this.formulaBarOnInputChangeFn,
|
|
513
|
+
onCommit: this.formulaBarOnCommitFn,
|
|
514
|
+
onCancel: this.formulaBarOnCancelFn,
|
|
515
|
+
startEditing: this.formulaBarStartEditingFn,
|
|
516
|
+
referencedCells: this.formulaBarReferences()
|
|
517
|
+
}));
|
|
231
518
|
// --- Pre-computed stable callback references for dataGridProps ---
|
|
232
519
|
// These avoid recreating arrow functions on every dataGridProps recomputation.
|
|
233
520
|
this.handleSortFn = (columnKey, direction) => this.handleSort(columnKey, direction);
|
|
@@ -267,6 +554,12 @@ var OGridService = class {
|
|
|
267
554
|
rowSelection: this.rowSelection(),
|
|
268
555
|
selectedRows: this.effectiveSelectedRows(),
|
|
269
556
|
onSelectionChange: this.handleSelectionChangeFn,
|
|
557
|
+
showRowNumbers: this.showRowNumbers() || this.cellReferences() || this.formulasEnabled(),
|
|
558
|
+
showColumnLetters: !!(this.cellReferences() || this.formulasEnabled()),
|
|
559
|
+
showNameBox: !!(this.cellReferences() && !this.formulasEnabled()),
|
|
560
|
+
onActiveCellChange: this.cellReferences() || this.formulasEnabled() ? this.handleActiveCellChange : void 0,
|
|
561
|
+
currentPage: this.page(),
|
|
562
|
+
pageSize: this.pageSize(),
|
|
270
563
|
statusBar: this.statusBarConfig(),
|
|
271
564
|
isLoading: this.isLoadingResolved(),
|
|
272
565
|
filters: this.filters(),
|
|
@@ -287,7 +580,20 @@ var OGridService = class {
|
|
|
287
580
|
onClearAll: this.clearAllFiltersFn,
|
|
288
581
|
message: this.emptyState()?.message,
|
|
289
582
|
render: this.emptyState()?.render
|
|
290
|
-
}
|
|
583
|
+
},
|
|
584
|
+
formulas: this.formulasEnabled(),
|
|
585
|
+
formulaVersion: this.formulaVersion(),
|
|
586
|
+
formulaReferences: this.formulaBarReferences().length > 0 ? this.formulaBarReferences() : void 0,
|
|
587
|
+
...this.formulaService.enabled() ? {
|
|
588
|
+
getFormulaValue: this.getFormulaValueFn,
|
|
589
|
+
hasFormula: this.hasFormulaFn,
|
|
590
|
+
getFormula: this.getFormulaFn,
|
|
591
|
+
setFormula: this.setFormulaFn,
|
|
592
|
+
onFormulaCellChanged: this.onFormulaCellChangedFn,
|
|
593
|
+
getPrecedents: this.getPrecedentsFn,
|
|
594
|
+
getDependents: this.getDependentsFn,
|
|
595
|
+
getAuditTrail: this.getAuditTrailFn
|
|
596
|
+
} : {}
|
|
291
597
|
}));
|
|
292
598
|
this.pagination = computed(() => ({
|
|
293
599
|
page: this.page(),
|
|
@@ -444,6 +750,29 @@ var OGridService = class {
|
|
|
444
750
|
this.sideBarActivePanel.set(parsed.defaultPanel);
|
|
445
751
|
}
|
|
446
752
|
});
|
|
753
|
+
effect(() => {
|
|
754
|
+
this.activeCellCoords();
|
|
755
|
+
this.formulaBarEditing.set(false);
|
|
756
|
+
});
|
|
757
|
+
effect(() => {
|
|
758
|
+
const userRecalcCb = this.onFormulaRecalc();
|
|
759
|
+
this.formulaService.configure({
|
|
760
|
+
formulas: this.formulasEnabled(),
|
|
761
|
+
initialFormulas: this.initialFormulas(),
|
|
762
|
+
formulaFunctions: this.formulaFunctions(),
|
|
763
|
+
onFormulaRecalc: (result) => {
|
|
764
|
+
this.formulaVersion.update((v) => v + 1);
|
|
765
|
+
userRecalcCb?.(result);
|
|
766
|
+
},
|
|
767
|
+
namedRanges: this.namedRanges(),
|
|
768
|
+
sheets: this.sheets()
|
|
769
|
+
});
|
|
770
|
+
});
|
|
771
|
+
effect(() => {
|
|
772
|
+
const items = this.displayItems();
|
|
773
|
+
const cols = this.columns();
|
|
774
|
+
this.formulaService.setData(items, cols);
|
|
775
|
+
});
|
|
447
776
|
this.destroyRef.onDestroy(() => {
|
|
448
777
|
this.fetchAbortController?.abort();
|
|
449
778
|
this.filterAbortController?.abort();
|
|
@@ -522,6 +851,33 @@ var OGridService = class {
|
|
|
522
851
|
});
|
|
523
852
|
this.onColumnPinned()?.(columnId, pinned);
|
|
524
853
|
}
|
|
854
|
+
// --- Formula bar methods ---
|
|
855
|
+
startFormulaBarEditing() {
|
|
856
|
+
this.formulaBarEditText.set(this.formulaBarDisplayText());
|
|
857
|
+
this.formulaBarEditing.set(true);
|
|
858
|
+
}
|
|
859
|
+
commitFormulaBar() {
|
|
860
|
+
const coords = this.activeCellCoords();
|
|
861
|
+
if (!coords) return;
|
|
862
|
+
const text = this.formulaBarEditText().trim();
|
|
863
|
+
if (text.startsWith("=")) {
|
|
864
|
+
this.formulaService.setFormula(coords.col, coords.row, text);
|
|
865
|
+
this.formulaVersion.update((v) => v + 1);
|
|
866
|
+
} else {
|
|
867
|
+
this.formulaService.setFormula(coords.col, coords.row, null);
|
|
868
|
+
this.onCellValueChanged()?.({
|
|
869
|
+
rowIndex: coords.row,
|
|
870
|
+
columnId: this.columns()[coords.col]?.columnId ?? "",
|
|
871
|
+
oldValue: void 0,
|
|
872
|
+
newValue: text
|
|
873
|
+
});
|
|
874
|
+
}
|
|
875
|
+
this.formulaBarEditing.set(false);
|
|
876
|
+
}
|
|
877
|
+
cancelFormulaBar() {
|
|
878
|
+
this.formulaBarEditing.set(false);
|
|
879
|
+
this.formulaBarEditText.set("");
|
|
880
|
+
}
|
|
525
881
|
// --- Configure from props ---
|
|
526
882
|
configure(props) {
|
|
527
883
|
this.columnsProp.set(props.columns);
|
|
@@ -575,6 +931,18 @@ var OGridService = class {
|
|
|
575
931
|
if (props.columnReorder !== void 0) this.columnReorder.set(props.columnReorder);
|
|
576
932
|
if (props.virtualScroll !== void 0) this.virtualScroll.set(props.virtualScroll);
|
|
577
933
|
if (props.workerSort !== void 0) this.workerSort.set(props.workerSort);
|
|
934
|
+
if (props.showRowNumbers !== void 0) this.showRowNumbers.set(props.showRowNumbers);
|
|
935
|
+
if (props.cellReferences !== void 0) this.cellReferences.set(props.cellReferences);
|
|
936
|
+
if (props.formulas !== void 0) this.formulasEnabled.set(props.formulas);
|
|
937
|
+
if (props.initialFormulas !== void 0) this.initialFormulas.set(props.initialFormulas);
|
|
938
|
+
if (props.onFormulaRecalc) this.onFormulaRecalc.set(props.onFormulaRecalc);
|
|
939
|
+
if (props.formulaFunctions !== void 0) this.formulaFunctions.set(props.formulaFunctions);
|
|
940
|
+
if (props.namedRanges !== void 0) this.namedRanges.set(props.namedRanges);
|
|
941
|
+
if (props.sheets !== void 0) this.sheets.set(props.sheets);
|
|
942
|
+
if (props.sheetDefs !== void 0) this.sheetDefs.set(props.sheetDefs);
|
|
943
|
+
if (props.activeSheet !== void 0) this.activeSheet.set(props.activeSheet);
|
|
944
|
+
if (props.onSheetChange) this.onSheetChange.set(props.onSheetChange);
|
|
945
|
+
if (props.onSheetAdd) this.onSheetAdd.set(props.onSheetAdd);
|
|
578
946
|
if (props.entityLabelPlural !== void 0) this.entityLabelPlural.set(props.entityLabelPlural);
|
|
579
947
|
if (props.className !== void 0) this.className.set(props.className);
|
|
580
948
|
if (props.layoutMode !== void 0) this.layoutMode.set(props.layoutMode);
|
|
@@ -1112,7 +1480,7 @@ var DataGridInteractionHelper = class {
|
|
|
1112
1480
|
const maxColIndex = visibleColumnCount - 1 + colOffset;
|
|
1113
1481
|
if (items.length === 0) return;
|
|
1114
1482
|
if (activeCell === null) {
|
|
1115
|
-
if (["ArrowDown", "ArrowUp", "ArrowLeft", "ArrowRight", "Tab", "Enter", "Home", "End"].includes(e.key)) {
|
|
1483
|
+
if (["ArrowDown", "ArrowUp", "ArrowLeft", "ArrowRight", "Tab", "Enter", "Home", "End", "PageDown", "PageUp"].includes(e.key)) {
|
|
1116
1484
|
this.setActiveCell({ rowIndex: 0, columnIndex: colOffset });
|
|
1117
1485
|
e.preventDefault();
|
|
1118
1486
|
}
|
|
@@ -1268,6 +1636,36 @@ var DataGridInteractionHelper = class {
|
|
|
1268
1636
|
this.setActiveCell({ rowIndex: newRowEnd, columnIndex: maxColIndex });
|
|
1269
1637
|
break;
|
|
1270
1638
|
}
|
|
1639
|
+
case "PageDown":
|
|
1640
|
+
case "PageUp": {
|
|
1641
|
+
e.preventDefault();
|
|
1642
|
+
let pageSize = 10;
|
|
1643
|
+
let rowHeight = 36;
|
|
1644
|
+
if (wrapperEl) {
|
|
1645
|
+
const firstRow = wrapperEl.querySelector("tbody tr");
|
|
1646
|
+
if (firstRow && firstRow.offsetHeight > 0) {
|
|
1647
|
+
rowHeight = firstRow.offsetHeight;
|
|
1648
|
+
pageSize = Math.max(1, Math.floor(wrapperEl.clientHeight / rowHeight));
|
|
1649
|
+
}
|
|
1650
|
+
}
|
|
1651
|
+
const pgDir = e.key === "PageDown" ? 1 : -1;
|
|
1652
|
+
const newRowPage = Math.max(0, Math.min(rowIndex + pgDir * pageSize, maxRowIndex));
|
|
1653
|
+
if (shift) {
|
|
1654
|
+
this.setSelectionRange(normalizeSelectionRange({
|
|
1655
|
+
startRow: selectionRange?.startRow ?? rowIndex,
|
|
1656
|
+
startCol: selectionRange?.startCol ?? dataColIndex,
|
|
1657
|
+
endRow: newRowPage,
|
|
1658
|
+
endCol: selectionRange?.endCol ?? dataColIndex
|
|
1659
|
+
}));
|
|
1660
|
+
} else {
|
|
1661
|
+
this.setSelectionRange({ startRow: newRowPage, startCol: dataColIndex, endRow: newRowPage, endCol: dataColIndex });
|
|
1662
|
+
}
|
|
1663
|
+
this.setActiveCell({ rowIndex: newRowPage, columnIndex });
|
|
1664
|
+
if (wrapperEl) {
|
|
1665
|
+
wrapperEl.scrollTop = getScrollTopForRow(newRowPage, rowHeight, wrapperEl.clientHeight, "center");
|
|
1666
|
+
}
|
|
1667
|
+
break;
|
|
1668
|
+
}
|
|
1271
1669
|
case "Enter":
|
|
1272
1670
|
case "F2": {
|
|
1273
1671
|
e.preventDefault();
|
|
@@ -1341,20 +1739,8 @@ var DataGridInteractionHelper = class {
|
|
|
1341
1739
|
const range = selectionRange ?? (activeCell != null ? { startRow: activeCell.rowIndex, startCol: activeCell.columnIndex - colOffset, endRow: activeCell.rowIndex, endCol: activeCell.columnIndex - colOffset } : null);
|
|
1342
1740
|
if (range == null) break;
|
|
1343
1741
|
e.preventDefault();
|
|
1344
|
-
const
|
|
1345
|
-
for (
|
|
1346
|
-
for (let c = norm.startCol; c <= norm.endCol; c++) {
|
|
1347
|
-
if (r >= items.length || c >= visibleCols.length) continue;
|
|
1348
|
-
const item = items[r];
|
|
1349
|
-
const col = visibleCols[c];
|
|
1350
|
-
const colEditable = col.editable === true || typeof col.editable === "function" && col.editable(item);
|
|
1351
|
-
if (!colEditable) continue;
|
|
1352
|
-
const oldValue = getCellValue(item, col);
|
|
1353
|
-
const result = parseValue("", oldValue, item, col);
|
|
1354
|
-
if (!result.valid) continue;
|
|
1355
|
-
wrappedOnCellValueChanged({ item, columnId: col.columnId, oldValue, newValue: result.value, rowIndex: r });
|
|
1356
|
-
}
|
|
1357
|
-
}
|
|
1742
|
+
const deleteEvents = applyCellDeletion(normalizeSelectionRange(range), items, visibleCols);
|
|
1743
|
+
for (const evt of deleteEvents) wrappedOnCellValueChanged(evt);
|
|
1358
1744
|
break;
|
|
1359
1745
|
}
|
|
1360
1746
|
case "F10":
|
|
@@ -1987,7 +2373,10 @@ var DataGridStateService = class {
|
|
|
1987
2373
|
getRowId: p?.getRowId ?? ((item) => item["id"]),
|
|
1988
2374
|
editable: p?.editable,
|
|
1989
2375
|
onCellValueChanged: this.wrappedOnCellValueChanged(),
|
|
1990
|
-
isDragging: cellSel ? this.interactionHelper.isDraggingSig() : false
|
|
2376
|
+
isDragging: cellSel ? this.interactionHelper.isDraggingSig() : false,
|
|
2377
|
+
getFormulaValue: p?.getFormulaValue,
|
|
2378
|
+
hasFormula: p?.hasFormula,
|
|
2379
|
+
formulaVersion: p?.formulaVersion
|
|
1991
2380
|
},
|
|
1992
2381
|
statusBarConfig: this.statusBarConfig(),
|
|
1993
2382
|
showEmptyInGrid: this.showEmptyInGrid(),
|
|
@@ -2634,6 +3023,205 @@ SideBarComponent = __decorateClass([
|
|
|
2634
3023
|
`
|
|
2635
3024
|
})
|
|
2636
3025
|
], SideBarComponent);
|
|
3026
|
+
var FormulaBarComponent = class {
|
|
3027
|
+
constructor() {
|
|
3028
|
+
/** Active cell reference (e.g. "A1"). */
|
|
3029
|
+
this.cellRef = input(null);
|
|
3030
|
+
/** Text displayed/edited in the formula input. */
|
|
3031
|
+
this.formulaText = input("");
|
|
3032
|
+
/** Whether the input is in editing mode. */
|
|
3033
|
+
this.isEditing = input(false);
|
|
3034
|
+
/** Called when the user changes the input text. */
|
|
3035
|
+
this.inputChange = output();
|
|
3036
|
+
/** Commit the formula bar value. */
|
|
3037
|
+
this.commit = output();
|
|
3038
|
+
/** Cancel editing. */
|
|
3039
|
+
this.cancel = output();
|
|
3040
|
+
/** Start editing the formula bar. */
|
|
3041
|
+
this.startEditing = output();
|
|
3042
|
+
this.inputEl = viewChild("formulaInput");
|
|
3043
|
+
effect(() => {
|
|
3044
|
+
if (this.isEditing()) {
|
|
3045
|
+
const el = this.inputEl()?.nativeElement;
|
|
3046
|
+
if (el) el.focus();
|
|
3047
|
+
}
|
|
3048
|
+
});
|
|
3049
|
+
}
|
|
3050
|
+
onInput(event) {
|
|
3051
|
+
this.inputChange.emit(event.target.value);
|
|
3052
|
+
}
|
|
3053
|
+
onKeyDown(event) {
|
|
3054
|
+
handleFormulaBarKeyDown(
|
|
3055
|
+
event.key,
|
|
3056
|
+
() => event.preventDefault(),
|
|
3057
|
+
() => this.commit.emit(),
|
|
3058
|
+
() => this.cancel.emit()
|
|
3059
|
+
);
|
|
3060
|
+
}
|
|
3061
|
+
onClick() {
|
|
3062
|
+
if (!this.isEditing()) {
|
|
3063
|
+
this.startEditing.emit();
|
|
3064
|
+
}
|
|
3065
|
+
}
|
|
3066
|
+
};
|
|
3067
|
+
FormulaBarComponent = __decorateClass([
|
|
3068
|
+
Component({
|
|
3069
|
+
selector: "ogrid-formula-bar",
|
|
3070
|
+
standalone: true,
|
|
3071
|
+
encapsulation: ViewEncapsulation.None,
|
|
3072
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
3073
|
+
styles: [`
|
|
3074
|
+
.ogrid-formula-bar {
|
|
3075
|
+
display: flex;
|
|
3076
|
+
align-items: center;
|
|
3077
|
+
border-bottom: 1px solid var(--ogrid-border, #e0e0e0);
|
|
3078
|
+
background: var(--ogrid-bg, #fff);
|
|
3079
|
+
min-height: 28px;
|
|
3080
|
+
font-size: 13px;
|
|
3081
|
+
}
|
|
3082
|
+
.ogrid-formula-bar__name-box {
|
|
3083
|
+
font-family: monospace;
|
|
3084
|
+
font-size: 12px;
|
|
3085
|
+
font-weight: 500;
|
|
3086
|
+
padding: 2px 8px;
|
|
3087
|
+
border-right: 1px solid var(--ogrid-border, #e0e0e0);
|
|
3088
|
+
background: var(--ogrid-bg, #fff);
|
|
3089
|
+
color: var(--ogrid-fg, #242424);
|
|
3090
|
+
min-width: 52px;
|
|
3091
|
+
text-align: center;
|
|
3092
|
+
line-height: 24px;
|
|
3093
|
+
user-select: none;
|
|
3094
|
+
white-space: nowrap;
|
|
3095
|
+
}
|
|
3096
|
+
.ogrid-formula-bar__fx {
|
|
3097
|
+
padding: 2px 8px;
|
|
3098
|
+
font-style: italic;
|
|
3099
|
+
font-weight: 600;
|
|
3100
|
+
color: var(--ogrid-muted-fg, #888);
|
|
3101
|
+
user-select: none;
|
|
3102
|
+
border-right: 1px solid var(--ogrid-border, #e0e0e0);
|
|
3103
|
+
line-height: 24px;
|
|
3104
|
+
font-size: 12px;
|
|
3105
|
+
}
|
|
3106
|
+
.ogrid-formula-bar__input {
|
|
3107
|
+
flex: 1;
|
|
3108
|
+
border: none;
|
|
3109
|
+
outline: none;
|
|
3110
|
+
padding: 2px 8px;
|
|
3111
|
+
font-family: monospace;
|
|
3112
|
+
font-size: 12px;
|
|
3113
|
+
line-height: 24px;
|
|
3114
|
+
background: transparent;
|
|
3115
|
+
color: var(--ogrid-fg, #242424);
|
|
3116
|
+
min-width: 0;
|
|
3117
|
+
}
|
|
3118
|
+
`],
|
|
3119
|
+
template: `
|
|
3120
|
+
<div class="ogrid-formula-bar" role="toolbar" aria-label="Formula bar">
|
|
3121
|
+
<div class="ogrid-formula-bar__name-box" aria-label="Active cell reference">
|
|
3122
|
+
{{ cellRef() ?? '\u2014' }}
|
|
3123
|
+
</div>
|
|
3124
|
+
<div class="ogrid-formula-bar__fx" aria-hidden="true">fx</div>
|
|
3125
|
+
<input
|
|
3126
|
+
#formulaInput
|
|
3127
|
+
type="text"
|
|
3128
|
+
class="ogrid-formula-bar__input"
|
|
3129
|
+
[value]="formulaText()"
|
|
3130
|
+
[readOnly]="!isEditing()"
|
|
3131
|
+
(input)="onInput($event)"
|
|
3132
|
+
(keydown)="onKeyDown($event)"
|
|
3133
|
+
(click)="onClick()"
|
|
3134
|
+
(dblclick)="onClick()"
|
|
3135
|
+
aria-label="Formula input"
|
|
3136
|
+
[attr.spellcheck]="false"
|
|
3137
|
+
autocomplete="off"
|
|
3138
|
+
/>
|
|
3139
|
+
</div>
|
|
3140
|
+
`
|
|
3141
|
+
})
|
|
3142
|
+
], FormulaBarComponent);
|
|
3143
|
+
var SheetTabsComponent = class {
|
|
3144
|
+
constructor() {
|
|
3145
|
+
this.sheets = input.required();
|
|
3146
|
+
this.activeSheet = input.required();
|
|
3147
|
+
this.showAddButton = input(false);
|
|
3148
|
+
this.sheetChange = output();
|
|
3149
|
+
this.sheetAdd = output();
|
|
3150
|
+
}
|
|
3151
|
+
};
|
|
3152
|
+
SheetTabsComponent = __decorateClass([
|
|
3153
|
+
Component({
|
|
3154
|
+
selector: "ogrid-sheet-tabs",
|
|
3155
|
+
standalone: true,
|
|
3156
|
+
encapsulation: ViewEncapsulation.None,
|
|
3157
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
3158
|
+
styles: [`
|
|
3159
|
+
.ogrid-sheet-tabs {
|
|
3160
|
+
display: flex;
|
|
3161
|
+
align-items: center;
|
|
3162
|
+
border-top: 1px solid var(--ogrid-border, #e0e0e0);
|
|
3163
|
+
background: var(--ogrid-header-bg, #f5f5f5);
|
|
3164
|
+
min-height: 30px;
|
|
3165
|
+
overflow-x: auto;
|
|
3166
|
+
overflow-y: hidden;
|
|
3167
|
+
gap: 0;
|
|
3168
|
+
font-size: 12px;
|
|
3169
|
+
}
|
|
3170
|
+
.ogrid-sheet-tabs__add-btn {
|
|
3171
|
+
background: none;
|
|
3172
|
+
border: none;
|
|
3173
|
+
cursor: pointer;
|
|
3174
|
+
padding: 4px 10px;
|
|
3175
|
+
font-size: 16px;
|
|
3176
|
+
line-height: 22px;
|
|
3177
|
+
color: var(--ogrid-fg-secondary, #666);
|
|
3178
|
+
flex-shrink: 0;
|
|
3179
|
+
}
|
|
3180
|
+
.ogrid-sheet-tabs__tab {
|
|
3181
|
+
background: none;
|
|
3182
|
+
border: none;
|
|
3183
|
+
border-bottom: 2px solid transparent;
|
|
3184
|
+
cursor: pointer;
|
|
3185
|
+
padding: 4px 16px;
|
|
3186
|
+
font-size: 12px;
|
|
3187
|
+
line-height: 22px;
|
|
3188
|
+
color: var(--ogrid-fg, #242424);
|
|
3189
|
+
white-space: nowrap;
|
|
3190
|
+
position: relative;
|
|
3191
|
+
}
|
|
3192
|
+
.ogrid-sheet-tabs__tab--active {
|
|
3193
|
+
font-weight: 600;
|
|
3194
|
+
border-bottom-color: var(--ogrid-primary, #217346);
|
|
3195
|
+
background: var(--ogrid-bg, #fff);
|
|
3196
|
+
}
|
|
3197
|
+
`],
|
|
3198
|
+
template: `
|
|
3199
|
+
<div class="ogrid-sheet-tabs" role="tablist" aria-label="Sheet tabs">
|
|
3200
|
+
@if (showAddButton()) {
|
|
3201
|
+
<button
|
|
3202
|
+
type="button"
|
|
3203
|
+
class="ogrid-sheet-tabs__add-btn"
|
|
3204
|
+
(click)="sheetAdd.emit()"
|
|
3205
|
+
title="Add sheet"
|
|
3206
|
+
aria-label="Add sheet"
|
|
3207
|
+
>+</button>
|
|
3208
|
+
}
|
|
3209
|
+
@for (sheet of sheets(); track sheet.id) {
|
|
3210
|
+
@let isActive = sheet.id === activeSheet();
|
|
3211
|
+
<button
|
|
3212
|
+
type="button"
|
|
3213
|
+
role="tab"
|
|
3214
|
+
class="ogrid-sheet-tabs__tab"
|
|
3215
|
+
[class.ogrid-sheet-tabs__tab--active]="isActive"
|
|
3216
|
+
[attr.aria-selected]="isActive"
|
|
3217
|
+
[style.border-bottom-color]="isActive && sheet.color ? sheet.color : null"
|
|
3218
|
+
(click)="sheetChange.emit(sheet.id)"
|
|
3219
|
+
>{{ sheet.name }}</button>
|
|
3220
|
+
}
|
|
3221
|
+
</div>
|
|
3222
|
+
`
|
|
3223
|
+
})
|
|
3224
|
+
], SheetTabsComponent);
|
|
2637
3225
|
|
|
2638
3226
|
// src/styles/ogrid-theme-vars.ts
|
|
2639
3227
|
var OGRID_THEME_VARS_CSS = `
|
|
@@ -2750,6 +3338,9 @@ var OGridLayoutComponent = class {
|
|
|
2750
3338
|
this.hasPagination = false;
|
|
2751
3339
|
this.sideBar = null;
|
|
2752
3340
|
this.fullScreen = false;
|
|
3341
|
+
this.showNameBox = false;
|
|
3342
|
+
this.activeCellRef = null;
|
|
3343
|
+
this.formulaBar = null;
|
|
2753
3344
|
this.isFullScreen = false;
|
|
2754
3345
|
this.borderRadius = GRID_BORDER_RADIUS;
|
|
2755
3346
|
this.escListener = null;
|
|
@@ -2797,13 +3388,34 @@ __decorateClass([
|
|
|
2797
3388
|
__decorateClass([
|
|
2798
3389
|
Input()
|
|
2799
3390
|
], OGridLayoutComponent.prototype, "fullScreen", 2);
|
|
3391
|
+
__decorateClass([
|
|
3392
|
+
Input()
|
|
3393
|
+
], OGridLayoutComponent.prototype, "showNameBox", 2);
|
|
3394
|
+
__decorateClass([
|
|
3395
|
+
Input()
|
|
3396
|
+
], OGridLayoutComponent.prototype, "activeCellRef", 2);
|
|
3397
|
+
__decorateClass([
|
|
3398
|
+
Input()
|
|
3399
|
+
], OGridLayoutComponent.prototype, "formulaBar", 2);
|
|
3400
|
+
__decorateClass([
|
|
3401
|
+
Input()
|
|
3402
|
+
], OGridLayoutComponent.prototype, "sheetDefs", 2);
|
|
3403
|
+
__decorateClass([
|
|
3404
|
+
Input()
|
|
3405
|
+
], OGridLayoutComponent.prototype, "activeSheet", 2);
|
|
3406
|
+
__decorateClass([
|
|
3407
|
+
Input()
|
|
3408
|
+
], OGridLayoutComponent.prototype, "onSheetChange", 2);
|
|
3409
|
+
__decorateClass([
|
|
3410
|
+
Input()
|
|
3411
|
+
], OGridLayoutComponent.prototype, "onSheetAdd", 2);
|
|
2800
3412
|
OGridLayoutComponent = __decorateClass([
|
|
2801
3413
|
Component({
|
|
2802
3414
|
selector: "ogrid-layout",
|
|
2803
3415
|
standalone: true,
|
|
2804
3416
|
encapsulation: ViewEncapsulation.None,
|
|
2805
3417
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
2806
|
-
imports: [SideBarComponent],
|
|
3418
|
+
imports: [SideBarComponent, FormulaBarComponent, SheetTabsComponent],
|
|
2807
3419
|
styles: [OGRID_THEME_VARS_CSS, `
|
|
2808
3420
|
:host { display: block; height: 100%; }
|
|
2809
3421
|
.ogrid-layout-root { display: flex; flex-direction: column; height: 100%; }
|
|
@@ -2846,18 +3458,28 @@ OGridLayoutComponent = __decorateClass([
|
|
|
2846
3458
|
color: var(--ogrid-fg, rgba(0, 0, 0, 0.87));
|
|
2847
3459
|
}
|
|
2848
3460
|
.ogrid-fullscreen-btn:hover { background: var(--ogrid-hover-bg, rgba(0, 0, 0, 0.04)); }
|
|
3461
|
+
.ogrid-name-box {
|
|
3462
|
+
display: inline-flex; align-items: center; padding: 0 8px;
|
|
3463
|
+
font-family: 'Consolas', 'Courier New', monospace; font-size: 12px;
|
|
3464
|
+
border: 1px solid var(--ogrid-border, rgba(0, 0, 0, 0.12)); border-radius: 3px;
|
|
3465
|
+
height: 24px; margin-right: 8px; background: var(--ogrid-bg, #fff);
|
|
3466
|
+
min-width: 40px; color: var(--ogrid-fg-secondary, rgba(0, 0, 0, 0.6));
|
|
3467
|
+
}
|
|
2849
3468
|
`],
|
|
2850
3469
|
template: `
|
|
2851
3470
|
<div [class]="rootClass">
|
|
2852
3471
|
<div class="ogrid-layout-container" [style.border-radius.px]="isFullScreen ? 0 : borderRadius">
|
|
2853
3472
|
<!-- Toolbar strip -->
|
|
2854
|
-
@if (hasToolbar || fullScreen) {
|
|
3473
|
+
@if (hasToolbar || fullScreen || showNameBox) {
|
|
2855
3474
|
<div
|
|
2856
3475
|
class="ogrid-layout-toolbar"
|
|
2857
3476
|
[class.ogrid-layout-toolbar--has-below]="hasToolbarBelow"
|
|
2858
3477
|
[class.ogrid-layout-toolbar--no-below]="!hasToolbarBelow"
|
|
2859
3478
|
>
|
|
2860
3479
|
<div class="ogrid-layout-toolbar-left">
|
|
3480
|
+
@if (showNameBox) {
|
|
3481
|
+
<div class="ogrid-name-box">{{ activeCellRef ?? '\u2014' }}</div>
|
|
3482
|
+
}
|
|
2861
3483
|
<ng-content select="[toolbar]"></ng-content>
|
|
2862
3484
|
</div>
|
|
2863
3485
|
<div class="ogrid-layout-toolbar-right">
|
|
@@ -2895,6 +3517,19 @@ OGridLayoutComponent = __decorateClass([
|
|
|
2895
3517
|
</div>
|
|
2896
3518
|
}
|
|
2897
3519
|
|
|
3520
|
+
<!-- Formula bar (between toolbar and grid) -->
|
|
3521
|
+
@if (formulaBar) {
|
|
3522
|
+
<ogrid-formula-bar
|
|
3523
|
+
[cellRef]="formulaBar.cellRef"
|
|
3524
|
+
[formulaText]="formulaBar.formulaText"
|
|
3525
|
+
[isEditing]="formulaBar.isEditing"
|
|
3526
|
+
(inputChange)="formulaBar.onInputChange($event)"
|
|
3527
|
+
(commit)="formulaBar.onCommit()"
|
|
3528
|
+
(cancel)="formulaBar.onCancel()"
|
|
3529
|
+
(startEditing)="formulaBar.startEditing()"
|
|
3530
|
+
/>
|
|
3531
|
+
}
|
|
3532
|
+
|
|
2898
3533
|
<!-- Grid area -->
|
|
2899
3534
|
<div class="ogrid-layout-grid-area">
|
|
2900
3535
|
@if (sideBar && sideBar.position === 'left') {
|
|
@@ -2908,6 +3543,17 @@ OGridLayoutComponent = __decorateClass([
|
|
|
2908
3543
|
}
|
|
2909
3544
|
</div>
|
|
2910
3545
|
|
|
3546
|
+
<!-- Sheet tabs (between grid and footer) -->
|
|
3547
|
+
@if (sheetDefs && sheetDefs.length > 0 && activeSheet) {
|
|
3548
|
+
<ogrid-sheet-tabs
|
|
3549
|
+
[sheets]="sheetDefs"
|
|
3550
|
+
[activeSheet]="activeSheet"
|
|
3551
|
+
[showAddButton]="!!onSheetAdd"
|
|
3552
|
+
(sheetChange)="onSheetChange?.($event)"
|
|
3553
|
+
(sheetAdd)="onSheetAdd?.()"
|
|
3554
|
+
/>
|
|
3555
|
+
}
|
|
3556
|
+
|
|
2911
3557
|
<!-- Footer strip (pagination) -->
|
|
2912
3558
|
@if (hasPagination) {
|
|
2913
3559
|
<div class="ogrid-layout-footer">
|
|
@@ -3271,6 +3917,94 @@ MarchingAntsOverlayComponent = __decorateClass([
|
|
|
3271
3917
|
`
|
|
3272
3918
|
})
|
|
3273
3919
|
], MarchingAntsOverlayComponent);
|
|
3920
|
+
function measureRef(container, ref, colOffset) {
|
|
3921
|
+
const startCol = ref.col + colOffset;
|
|
3922
|
+
const endCol = (ref.endCol ?? ref.col) + colOffset;
|
|
3923
|
+
const endRow = ref.endRow ?? ref.row;
|
|
3924
|
+
const tl = container.querySelector(
|
|
3925
|
+
`[data-row-index="${ref.row}"][data-col-index="${startCol}"]`
|
|
3926
|
+
);
|
|
3927
|
+
const br = container.querySelector(
|
|
3928
|
+
`[data-row-index="${endRow}"][data-col-index="${endCol}"]`
|
|
3929
|
+
);
|
|
3930
|
+
if (!tl || !br) return null;
|
|
3931
|
+
const cRect = container.getBoundingClientRect();
|
|
3932
|
+
const tlRect = tl.getBoundingClientRect();
|
|
3933
|
+
const brRect = br.getBoundingClientRect();
|
|
3934
|
+
return {
|
|
3935
|
+
top: Math.round(tlRect.top - cRect.top),
|
|
3936
|
+
left: Math.round(tlRect.left - cRect.left),
|
|
3937
|
+
width: Math.round(brRect.right - tlRect.left),
|
|
3938
|
+
height: Math.round(brRect.bottom - tlRect.top),
|
|
3939
|
+
color: FORMULA_REF_COLORS[ref.colorIndex % FORMULA_REF_COLORS.length]
|
|
3940
|
+
};
|
|
3941
|
+
}
|
|
3942
|
+
var FormulaRefOverlayComponent = class {
|
|
3943
|
+
constructor() {
|
|
3944
|
+
/** The positioned container that wraps the table. */
|
|
3945
|
+
this.containerEl = input(null);
|
|
3946
|
+
/** References to highlight. */
|
|
3947
|
+
this.references = input([]);
|
|
3948
|
+
/** Column offset (1 when checkbox/row-number columns are present). */
|
|
3949
|
+
this.colOffset = input(0);
|
|
3950
|
+
this.rects = signal([]);
|
|
3951
|
+
this.rafId = 0;
|
|
3952
|
+
effect(() => {
|
|
3953
|
+
const refs = this.references();
|
|
3954
|
+
const container = this.containerEl();
|
|
3955
|
+
const colOff = this.colOffset();
|
|
3956
|
+
cancelAnimationFrame(this.rafId);
|
|
3957
|
+
if (!container || refs.length === 0) {
|
|
3958
|
+
this.rects.set([]);
|
|
3959
|
+
return;
|
|
3960
|
+
}
|
|
3961
|
+
this.rafId = requestAnimationFrame(() => {
|
|
3962
|
+
const measured = [];
|
|
3963
|
+
for (const ref of refs) {
|
|
3964
|
+
const r = measureRef(container, ref, colOff);
|
|
3965
|
+
if (r) measured.push(r);
|
|
3966
|
+
}
|
|
3967
|
+
this.rects.set(measured);
|
|
3968
|
+
});
|
|
3969
|
+
});
|
|
3970
|
+
}
|
|
3971
|
+
max0(v) {
|
|
3972
|
+
return Math.max(0, v);
|
|
3973
|
+
}
|
|
3974
|
+
};
|
|
3975
|
+
FormulaRefOverlayComponent = __decorateClass([
|
|
3976
|
+
Component({
|
|
3977
|
+
selector: "ogrid-formula-ref-overlay",
|
|
3978
|
+
standalone: true,
|
|
3979
|
+
encapsulation: ViewEncapsulation.None,
|
|
3980
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
3981
|
+
template: `
|
|
3982
|
+
@for (r of rects(); track $index) {
|
|
3983
|
+
<svg
|
|
3984
|
+
[style.position]="'absolute'"
|
|
3985
|
+
[style.top.px]="r.top"
|
|
3986
|
+
[style.left.px]="r.left"
|
|
3987
|
+
[style.width.px]="r.width"
|
|
3988
|
+
[style.height.px]="r.height"
|
|
3989
|
+
[style.pointerEvents]="'none'"
|
|
3990
|
+
[style.zIndex]="3"
|
|
3991
|
+
[style.overflow]="'visible'"
|
|
3992
|
+
aria-hidden="true"
|
|
3993
|
+
>
|
|
3994
|
+
<rect
|
|
3995
|
+
x="1" y="1"
|
|
3996
|
+
[attr.width]="max0(r.width - 2)"
|
|
3997
|
+
[attr.height]="max0(r.height - 2)"
|
|
3998
|
+
fill="none"
|
|
3999
|
+
[attr.stroke]="r.color"
|
|
4000
|
+
stroke-width="2"
|
|
4001
|
+
style="shape-rendering: crispEdges"
|
|
4002
|
+
/>
|
|
4003
|
+
</svg>
|
|
4004
|
+
}
|
|
4005
|
+
`
|
|
4006
|
+
})
|
|
4007
|
+
], FormulaRefOverlayComponent);
|
|
3274
4008
|
var EmptyStateComponent = class {
|
|
3275
4009
|
constructor() {
|
|
3276
4010
|
this.message = void 0;
|
|
@@ -3787,8 +4521,8 @@ var BaseDataGridTableComponent = class {
|
|
|
3787
4521
|
return "";
|
|
3788
4522
|
}
|
|
3789
4523
|
}
|
|
3790
|
-
resolveCellStyleFn(col, item) {
|
|
3791
|
-
return resolveCellStyle(col, item);
|
|
4524
|
+
resolveCellStyleFn(col, item, displayValue) {
|
|
4525
|
+
return resolveCellStyle(col, item, displayValue);
|
|
3792
4526
|
}
|
|
3793
4527
|
buildPopoverEditorProps(item, col, descriptor) {
|
|
3794
4528
|
return buildPopoverEditorProps(item, col, descriptor, this.pendingEditorValue(), {
|
|
@@ -4495,7 +5229,11 @@ var BaseInlineCellEditorComponent = class {
|
|
|
4495
5229
|
}
|
|
4496
5230
|
syncFromInputs() {
|
|
4497
5231
|
const v = this.value;
|
|
4498
|
-
|
|
5232
|
+
let strVal = v != null ? String(v) : "";
|
|
5233
|
+
if (this.editorType === "date" && strVal.match(/^\d{4}-\d{2}-\d{2}/)) {
|
|
5234
|
+
strVal = strVal.substring(0, 10);
|
|
5235
|
+
}
|
|
5236
|
+
this.localValue.set(strVal);
|
|
4499
5237
|
const col = this.column;
|
|
4500
5238
|
if (col?.cellEditorParams?.values) {
|
|
4501
5239
|
const vals = col.cellEditorParams.values;
|
|
@@ -4522,7 +5260,12 @@ var BaseInlineCellEditorComponent = class {
|
|
|
4522
5260
|
const el = this.inputEl?.nativeElement;
|
|
4523
5261
|
if (el) {
|
|
4524
5262
|
el.focus();
|
|
4525
|
-
if (el instanceof HTMLInputElement && el.type === "
|
|
5263
|
+
if (el instanceof HTMLInputElement && el.type === "date") {
|
|
5264
|
+
try {
|
|
5265
|
+
el.showPicker();
|
|
5266
|
+
} catch {
|
|
5267
|
+
}
|
|
5268
|
+
} else if (el instanceof HTMLInputElement && el.type === "text") {
|
|
4526
5269
|
el.select();
|
|
4527
5270
|
}
|
|
4528
5271
|
}
|
|
@@ -4644,6 +5387,7 @@ var BaseInlineCellEditorComponent = class {
|
|
|
4644
5387
|
dropdown.style.maxHeight = `${maxH}px`;
|
|
4645
5388
|
dropdown.style.zIndex = "9999";
|
|
4646
5389
|
dropdown.style.right = "auto";
|
|
5390
|
+
dropdown.style.textAlign = "left";
|
|
4647
5391
|
if (flipUp) {
|
|
4648
5392
|
dropdown.style.top = "auto";
|
|
4649
5393
|
dropdown.style.bottom = `${window.innerHeight - rect.top}px`;
|
|
@@ -4700,6 +5444,88 @@ __decorateClass([
|
|
|
4700
5444
|
__decorateClass([
|
|
4701
5445
|
ViewChild("richSelectDropdown")
|
|
4702
5446
|
], BaseInlineCellEditorComponent.prototype, "richSelectDropdown", 2);
|
|
5447
|
+
var BaseColumnHeaderMenuComponent = class {
|
|
5448
|
+
constructor() {
|
|
5449
|
+
// Signal-backed inputs so computed() tracks changes reactively
|
|
5450
|
+
this._canPinLeft = signal(true);
|
|
5451
|
+
this._canPinRight = signal(true);
|
|
5452
|
+
this._canUnpin = signal(false);
|
|
5453
|
+
this._currentSort = signal(null);
|
|
5454
|
+
this._isSortable = signal(true);
|
|
5455
|
+
this._isResizable = signal(true);
|
|
5456
|
+
this.handlers = {};
|
|
5457
|
+
this.menuItems = computed(
|
|
5458
|
+
() => getColumnHeaderMenuItems({
|
|
5459
|
+
canPinLeft: this._canPinLeft(),
|
|
5460
|
+
canPinRight: this._canPinRight(),
|
|
5461
|
+
canUnpin: this._canUnpin(),
|
|
5462
|
+
currentSort: this._currentSort(),
|
|
5463
|
+
isSortable: this._isSortable(),
|
|
5464
|
+
isResizable: this._isResizable()
|
|
5465
|
+
})
|
|
5466
|
+
);
|
|
5467
|
+
}
|
|
5468
|
+
set canPinLeft(v) {
|
|
5469
|
+
this._canPinLeft.set(v);
|
|
5470
|
+
}
|
|
5471
|
+
set canPinRight(v) {
|
|
5472
|
+
this._canPinRight.set(v);
|
|
5473
|
+
}
|
|
5474
|
+
set canUnpin(v) {
|
|
5475
|
+
this._canUnpin.set(v);
|
|
5476
|
+
}
|
|
5477
|
+
set currentSort(v) {
|
|
5478
|
+
this._currentSort.set(v);
|
|
5479
|
+
}
|
|
5480
|
+
set isSortable(v) {
|
|
5481
|
+
this._isSortable.set(v);
|
|
5482
|
+
}
|
|
5483
|
+
set isResizable(v) {
|
|
5484
|
+
this._isResizable.set(v);
|
|
5485
|
+
}
|
|
5486
|
+
handleMenuItemClick(itemId) {
|
|
5487
|
+
const h = this.handlers;
|
|
5488
|
+
const actionMap = {
|
|
5489
|
+
pinLeft: h.onPinLeft,
|
|
5490
|
+
pinRight: h.onPinRight,
|
|
5491
|
+
unpin: h.onUnpin,
|
|
5492
|
+
sortAsc: h.onSortAsc,
|
|
5493
|
+
sortDesc: h.onSortDesc,
|
|
5494
|
+
clearSort: h.onClearSort,
|
|
5495
|
+
autosizeThis: h.onAutosizeThis,
|
|
5496
|
+
autosizeAll: h.onAutosizeAll
|
|
5497
|
+
};
|
|
5498
|
+
const action = actionMap[itemId];
|
|
5499
|
+
if (action) {
|
|
5500
|
+
action();
|
|
5501
|
+
h.onClose?.();
|
|
5502
|
+
}
|
|
5503
|
+
}
|
|
5504
|
+
};
|
|
5505
|
+
__decorateClass([
|
|
5506
|
+
Input({ required: true })
|
|
5507
|
+
], BaseColumnHeaderMenuComponent.prototype, "columnId", 2);
|
|
5508
|
+
__decorateClass([
|
|
5509
|
+
Input()
|
|
5510
|
+
], BaseColumnHeaderMenuComponent.prototype, "canPinLeft", 1);
|
|
5511
|
+
__decorateClass([
|
|
5512
|
+
Input()
|
|
5513
|
+
], BaseColumnHeaderMenuComponent.prototype, "canPinRight", 1);
|
|
5514
|
+
__decorateClass([
|
|
5515
|
+
Input()
|
|
5516
|
+
], BaseColumnHeaderMenuComponent.prototype, "canUnpin", 1);
|
|
5517
|
+
__decorateClass([
|
|
5518
|
+
Input()
|
|
5519
|
+
], BaseColumnHeaderMenuComponent.prototype, "currentSort", 1);
|
|
5520
|
+
__decorateClass([
|
|
5521
|
+
Input()
|
|
5522
|
+
], BaseColumnHeaderMenuComponent.prototype, "isSortable", 1);
|
|
5523
|
+
__decorateClass([
|
|
5524
|
+
Input()
|
|
5525
|
+
], BaseColumnHeaderMenuComponent.prototype, "isResizable", 1);
|
|
5526
|
+
__decorateClass([
|
|
5527
|
+
Input()
|
|
5528
|
+
], BaseColumnHeaderMenuComponent.prototype, "handlers", 2);
|
|
4703
5529
|
|
|
4704
5530
|
// src/components/inline-cell-editor-template.ts
|
|
4705
5531
|
var INLINE_CELL_EDITOR_TEMPLATE = `
|
|
@@ -4728,7 +5554,7 @@ var INLINE_CELL_EDITOR_TEMPLATE = `
|
|
|
4728
5554
|
style="width:100%;padding:0;border:none;background:transparent;color:inherit;font:inherit;font-size:13px;outline:none;min-width:0"
|
|
4729
5555
|
/>
|
|
4730
5556
|
<div #richSelectDropdown role="listbox"
|
|
4731
|
-
style="position:absolute;top:100%;left:0;right:0;max-height:200px;overflow-y:auto;background:var(--ogrid-bg, #fff);border:1px solid var(--ogrid-border, rgba(0,0,0,0.12));z-index:10;box-shadow:0 4px 16px rgba(0,0,0,0.2)">
|
|
5557
|
+
style="position:absolute;top:100%;left:0;right:0;max-height:200px;overflow-y:auto;background:var(--ogrid-bg, #fff);border:1px solid var(--ogrid-border, rgba(0,0,0,0.12));z-index:10;box-shadow:0 4px 16px rgba(0,0,0,0.2);text-align:left">
|
|
4732
5558
|
@for (opt of filteredOptions(); track opt; let i = $index) {
|
|
4733
5559
|
<div role="option"
|
|
4734
5560
|
[attr.aria-selected]="i === highlightedIndex()"
|
|
@@ -4752,7 +5578,7 @@ var INLINE_CELL_EDITOR_TEMPLATE = `
|
|
|
4752
5578
|
<span style="margin-left:4px;font-size:10px;opacity:0.5">▾</span>
|
|
4753
5579
|
</div>
|
|
4754
5580
|
<div #selectDropdown role="listbox"
|
|
4755
|
-
style="position:absolute;top:100%;left:0;right:0;max-height:200px;overflow-y:auto;background:var(--ogrid-bg, #fff);border:1px solid var(--ogrid-border, rgba(0,0,0,0.12));z-index:10;box-shadow:0 4px 16px rgba(0,0,0,0.2)">
|
|
5581
|
+
style="position:absolute;top:100%;left:0;right:0;max-height:200px;overflow-y:auto;background:var(--ogrid-bg, #fff);border:1px solid var(--ogrid-border, rgba(0,0,0,0.12));z-index:10;box-shadow:0 4px 16px rgba(0,0,0,0.2);text-align:left">
|
|
4756
5582
|
@for (opt of selectOptions(); track opt; let i = $index) {
|
|
4757
5583
|
<div role="option"
|
|
4758
5584
|
[attr.aria-selected]="i === highlightedIndex()"
|
|
@@ -4892,4 +5718,4 @@ __decorateClass([
|
|
|
4892
5718
|
ViewChild("editorContainer")
|
|
4893
5719
|
], BasePopoverCellEditorComponent.prototype, "editorContainerRef", 2);
|
|
4894
5720
|
|
|
4895
|
-
export { BaseColumnChooserComponent, BaseColumnHeaderFilterComponent, BaseDataGridTableComponent, BaseInlineCellEditorComponent, BaseOGridComponent, BasePaginationControlsComponent, BasePopoverCellEditorComponent, ColumnReorderService, DataGridEditingHelper, DataGridInteractionHelper, DataGridLayoutHelper, DataGridStateService, EmptyStateComponent, GridContextMenuComponent, INLINE_CELL_EDITOR_STYLES, INLINE_CELL_EDITOR_TEMPLATE, MarchingAntsOverlayComponent, OGRID_THEME_VARS_CSS, OGridLayoutComponent, OGridService, POPOVER_CELL_EDITOR_OVERLAY_STYLES, POPOVER_CELL_EDITOR_TEMPLATE, SideBarComponent, StatusBarComponent, VirtualScrollService, createDebouncedCallback, createDebouncedSignal, createLatestCallback };
|
|
5721
|
+
export { BaseColumnChooserComponent, BaseColumnHeaderFilterComponent, BaseColumnHeaderMenuComponent, BaseDataGridTableComponent, BaseInlineCellEditorComponent, BaseOGridComponent, BasePaginationControlsComponent, BasePopoverCellEditorComponent, ColumnReorderService, DataGridEditingHelper, DataGridInteractionHelper, DataGridLayoutHelper, DataGridStateService, EmptyStateComponent, FormulaBarComponent, FormulaEngineService, FormulaRefOverlayComponent, GridContextMenuComponent, INLINE_CELL_EDITOR_STYLES, INLINE_CELL_EDITOR_TEMPLATE, MarchingAntsOverlayComponent, OGRID_THEME_VARS_CSS, OGridLayoutComponent, OGridService, POPOVER_CELL_EDITOR_OVERLAY_STYLES, POPOVER_CELL_EDITOR_TEMPLATE, SheetTabsComponent, SideBarComponent, StatusBarComponent, VirtualScrollService, createDebouncedCallback, createDebouncedSignal, createLatestCallback };
|