@alaarab/ogrid-core 2.1.2 → 2.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/esm/index.js +1426 -54
- package/package.json +3 -3
- package/dist/esm/constants/index.js +0 -3
- package/dist/esm/constants/layout.js +0 -13
- package/dist/esm/constants/timing.js +0 -10
- package/dist/esm/constants/zIndex.js +0 -16
- package/dist/esm/types/columnTypes.js +0 -1
- package/dist/esm/types/dataGridTypes.js +0 -27
- package/dist/esm/types/index.js +0 -2
- package/dist/esm/utils/aggregationUtils.js +0 -48
- package/dist/esm/utils/cellValue.js +0 -14
- package/dist/esm/utils/clientSideData.js +0 -155
- package/dist/esm/utils/clipboardHelpers.js +0 -142
- package/dist/esm/utils/columnAutosize.js +0 -38
- package/dist/esm/utils/columnReorder.js +0 -99
- package/dist/esm/utils/columnUtils.js +0 -122
- package/dist/esm/utils/dataGridStatusBar.js +0 -15
- package/dist/esm/utils/dataGridViewModel.js +0 -206
- package/dist/esm/utils/debounce.js +0 -40
- package/dist/esm/utils/dom.js +0 -53
- package/dist/esm/utils/exportToCsv.js +0 -50
- package/dist/esm/utils/fillHelpers.js +0 -47
- package/dist/esm/utils/gridContextMenuHelpers.js +0 -80
- package/dist/esm/utils/gridRowComparator.js +0 -78
- package/dist/esm/utils/index.js +0 -25
- package/dist/esm/utils/keyboardNavigation.js +0 -181
- package/dist/esm/utils/ogridHelpers.js +0 -67
- package/dist/esm/utils/paginationHelpers.js +0 -58
- package/dist/esm/utils/selectionHelpers.js +0 -94
- package/dist/esm/utils/sortHelpers.js +0 -28
- package/dist/esm/utils/statusBarHelpers.js +0 -27
- package/dist/esm/utils/undoRedoStack.js +0 -130
- package/dist/esm/utils/validation.js +0 -43
- package/dist/esm/utils/valueParsers.js +0 -121
- package/dist/esm/utils/virtualScroll.js +0 -46
|
@@ -1,130 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Pure undo/redo stack data structure shared across React, Vue, Angular, and JS.
|
|
3
|
-
* No framework dependencies — all state is plain arrays.
|
|
4
|
-
*
|
|
5
|
-
* Usage:
|
|
6
|
-
* const stack = new UndoRedoStack<MyEvent>(100);
|
|
7
|
-
* stack.push([event]); // single event
|
|
8
|
-
* stack.beginBatch();
|
|
9
|
-
* stack.push([event1]);
|
|
10
|
-
* stack.push([event2]);
|
|
11
|
-
* stack.endBatch(); // event1 + event2 are one undo step
|
|
12
|
-
* const batch = stack.undo(); // returns the batch to reverse
|
|
13
|
-
* const batch = stack.redo(); // returns the batch to re-apply
|
|
14
|
-
*/
|
|
15
|
-
export class UndoRedoStack {
|
|
16
|
-
constructor(maxDepth = 100) {
|
|
17
|
-
this.history = [];
|
|
18
|
-
this.redoStack = [];
|
|
19
|
-
this.batch = null;
|
|
20
|
-
this.maxDepth = maxDepth;
|
|
21
|
-
}
|
|
22
|
-
/** Whether there are undo steps available. */
|
|
23
|
-
get canUndo() {
|
|
24
|
-
return this.history.length > 0;
|
|
25
|
-
}
|
|
26
|
-
/** Whether there are redo steps available. */
|
|
27
|
-
get canRedo() {
|
|
28
|
-
return this.redoStack.length > 0;
|
|
29
|
-
}
|
|
30
|
-
/** Number of history entries. */
|
|
31
|
-
get historyLength() {
|
|
32
|
-
return this.history.length;
|
|
33
|
-
}
|
|
34
|
-
/** Number of redo entries. */
|
|
35
|
-
get redoLength() {
|
|
36
|
-
return this.redoStack.length;
|
|
37
|
-
}
|
|
38
|
-
/** Whether a batch is currently open. */
|
|
39
|
-
get isBatching() {
|
|
40
|
-
return this.batch !== null;
|
|
41
|
-
}
|
|
42
|
-
/**
|
|
43
|
-
* Record a group of events as a single undoable step.
|
|
44
|
-
* If a batch is open, accumulates into the batch instead.
|
|
45
|
-
* Clears the redo stack on any new entry.
|
|
46
|
-
*/
|
|
47
|
-
push(events) {
|
|
48
|
-
if (events.length === 0)
|
|
49
|
-
return;
|
|
50
|
-
if (this.batch !== null) {
|
|
51
|
-
this.batch.push(...events);
|
|
52
|
-
}
|
|
53
|
-
else {
|
|
54
|
-
this.history.push(events);
|
|
55
|
-
if (this.history.length > this.maxDepth) {
|
|
56
|
-
this.history.splice(0, this.history.length - this.maxDepth);
|
|
57
|
-
}
|
|
58
|
-
// Clear redo stack in-place — avoids allocating a new array on every edit
|
|
59
|
-
this.redoStack.length = 0;
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
/**
|
|
63
|
-
* Record a single event as a step (shorthand for push([event])).
|
|
64
|
-
* If a batch is open, accumulates into the batch instead.
|
|
65
|
-
*/
|
|
66
|
-
record(event) {
|
|
67
|
-
this.push([event]);
|
|
68
|
-
}
|
|
69
|
-
/**
|
|
70
|
-
* Start a batch — subsequent record/push calls accumulate into one undo step.
|
|
71
|
-
* Has no effect if a batch is already open.
|
|
72
|
-
*/
|
|
73
|
-
beginBatch() {
|
|
74
|
-
if (this.batch === null) {
|
|
75
|
-
this.batch = [];
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
/**
|
|
79
|
-
* End a batch — commits all accumulated events as one undo step.
|
|
80
|
-
* Has no effect if no batch is open or if the batch is empty.
|
|
81
|
-
*/
|
|
82
|
-
endBatch() {
|
|
83
|
-
const b = this.batch;
|
|
84
|
-
this.batch = null;
|
|
85
|
-
if (!b || b.length === 0)
|
|
86
|
-
return;
|
|
87
|
-
this.history.push(b);
|
|
88
|
-
if (this.history.length > this.maxDepth) {
|
|
89
|
-
this.history.splice(0, this.history.length - this.maxDepth);
|
|
90
|
-
}
|
|
91
|
-
this.redoStack.length = 0;
|
|
92
|
-
}
|
|
93
|
-
/**
|
|
94
|
-
* Pop the most recent history entry for undo.
|
|
95
|
-
* Returns the batch of events (in original order) to be reversed by the caller,
|
|
96
|
-
* or null if there is nothing to undo.
|
|
97
|
-
*
|
|
98
|
-
* The caller is responsible for applying the events in reverse order.
|
|
99
|
-
*/
|
|
100
|
-
undo() {
|
|
101
|
-
const lastBatch = this.history.pop();
|
|
102
|
-
if (!lastBatch)
|
|
103
|
-
return null;
|
|
104
|
-
this.redoStack.push(lastBatch);
|
|
105
|
-
return lastBatch;
|
|
106
|
-
}
|
|
107
|
-
/**
|
|
108
|
-
* Pop the most recent redo entry.
|
|
109
|
-
* Returns the batch of events (in original order) to be re-applied by the caller,
|
|
110
|
-
* or null if there is nothing to redo.
|
|
111
|
-
*/
|
|
112
|
-
redo() {
|
|
113
|
-
const nextBatch = this.redoStack.pop();
|
|
114
|
-
if (!nextBatch)
|
|
115
|
-
return null;
|
|
116
|
-
this.history.push(nextBatch);
|
|
117
|
-
return nextBatch;
|
|
118
|
-
}
|
|
119
|
-
/**
|
|
120
|
-
* Clear all history and redo state.
|
|
121
|
-
* Does not affect any open batch — call endBatch() first if needed.
|
|
122
|
-
*/
|
|
123
|
-
clear() {
|
|
124
|
-
this.history = [];
|
|
125
|
-
this.redoStack = [];
|
|
126
|
-
// Intentionally leaves this.batch untouched. If a batch is open,
|
|
127
|
-
// subsequent records will still accumulate until endBatch() is called.
|
|
128
|
-
// Callers that want to abort an open batch should call endBatch() first.
|
|
129
|
-
}
|
|
130
|
-
}
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Validate column definitions at grid initialization.
|
|
3
|
-
* Called once (not per render). Warns on empty/missing/duplicate columnIds.
|
|
4
|
-
*/
|
|
5
|
-
export function validateColumns(columns) {
|
|
6
|
-
if (!Array.isArray(columns) || columns.length === 0) {
|
|
7
|
-
console.warn('[OGrid] columns prop is empty or not an array');
|
|
8
|
-
return;
|
|
9
|
-
}
|
|
10
|
-
const ids = new Set();
|
|
11
|
-
for (const col of columns) {
|
|
12
|
-
if (!col.columnId) {
|
|
13
|
-
console.warn('[OGrid] Column missing columnId:', col);
|
|
14
|
-
}
|
|
15
|
-
if (ids.has(col.columnId)) {
|
|
16
|
-
console.warn(`[OGrid] Duplicate columnId: "${col.columnId}"`);
|
|
17
|
-
}
|
|
18
|
-
ids.add(col.columnId);
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
/**
|
|
22
|
-
* Validate that getRowId returns unique, non-null values.
|
|
23
|
-
* Dev-only (skipped in production). Samples the first 100 rows.
|
|
24
|
-
* Called once on first data render via a hasValidated flag in the caller.
|
|
25
|
-
*/
|
|
26
|
-
export function validateRowIds(items, getRowId) {
|
|
27
|
-
if (typeof process !== 'undefined' && process.env.NODE_ENV === 'production')
|
|
28
|
-
return;
|
|
29
|
-
const ids = new Set();
|
|
30
|
-
const limit = Math.min(items.length, 100);
|
|
31
|
-
for (let i = 0; i < limit; i++) {
|
|
32
|
-
const id = getRowId(items[i]);
|
|
33
|
-
if (id == null) {
|
|
34
|
-
console.warn(`[OGrid] getRowId returned null/undefined for row ${i}`);
|
|
35
|
-
return;
|
|
36
|
-
}
|
|
37
|
-
if (ids.has(id)) {
|
|
38
|
-
console.warn(`[OGrid] Duplicate row ID "${id}" at index ${i}. getRowId must return unique values.`);
|
|
39
|
-
return;
|
|
40
|
-
}
|
|
41
|
-
ids.add(id);
|
|
42
|
-
}
|
|
43
|
-
}
|
|
@@ -1,121 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Run the column's valueParser (if any), or auto-validate select columns.
|
|
3
|
-
* Returns `{ valid: true, value }` with the parsed value, or `{ valid: false }` to reject.
|
|
4
|
-
*/
|
|
5
|
-
export function parseValue(newValue, oldValue, item, col) {
|
|
6
|
-
// 1. Custom valueParser takes priority
|
|
7
|
-
if (col.valueParser) {
|
|
8
|
-
const params = {
|
|
9
|
-
newValue,
|
|
10
|
-
oldValue,
|
|
11
|
-
data: item,
|
|
12
|
-
column: col,
|
|
13
|
-
};
|
|
14
|
-
const parsed = col.valueParser(params);
|
|
15
|
-
if (parsed === undefined) {
|
|
16
|
-
return { valid: false, value: undefined };
|
|
17
|
-
}
|
|
18
|
-
return { valid: true, value: parsed };
|
|
19
|
-
}
|
|
20
|
-
// 2. Auto-validate select columns against allowed values
|
|
21
|
-
if (col.cellEditor === 'select' &&
|
|
22
|
-
col.cellEditorParams?.values != null &&
|
|
23
|
-
Array.isArray(col.cellEditorParams.values)) {
|
|
24
|
-
const allowedValues = col.cellEditorParams.values;
|
|
25
|
-
const strValue = typeof newValue === 'string' ? newValue : String(newValue ?? '');
|
|
26
|
-
// Allow clearing (empty string)
|
|
27
|
-
if (strValue === '') {
|
|
28
|
-
return { valid: true, value: '' };
|
|
29
|
-
}
|
|
30
|
-
// Case-insensitive match; return canonical value from the options list
|
|
31
|
-
const match = allowedValues.find((v) => String(v).toLowerCase() === strValue.toLowerCase());
|
|
32
|
-
if (match !== undefined) {
|
|
33
|
-
return { valid: true, value: match };
|
|
34
|
-
}
|
|
35
|
-
return { valid: false, value: undefined };
|
|
36
|
-
}
|
|
37
|
-
// 3. Auto-validate built-in column types
|
|
38
|
-
const colType = col.type;
|
|
39
|
-
if (colType === 'date') {
|
|
40
|
-
const parsed = dateParser({ newValue, oldValue, data: item, column: col });
|
|
41
|
-
return parsed === undefined ? { valid: false, value: undefined } : { valid: true, value: parsed };
|
|
42
|
-
}
|
|
43
|
-
if (colType === 'boolean') {
|
|
44
|
-
const parsed = booleanParser({ newValue, oldValue, data: item, column: col });
|
|
45
|
-
return parsed === undefined ? { valid: false, value: undefined } : { valid: true, value: parsed };
|
|
46
|
-
}
|
|
47
|
-
if (colType === 'numeric') {
|
|
48
|
-
const parsed = numberParser({ newValue, oldValue, data: item, column: col });
|
|
49
|
-
return parsed === undefined ? { valid: false, value: undefined } : { valid: true, value: parsed };
|
|
50
|
-
}
|
|
51
|
-
// 4. No parser, not a select column, no built-in type — pass through unchanged
|
|
52
|
-
return { valid: true, value: newValue };
|
|
53
|
-
}
|
|
54
|
-
// --- Built-in parser helpers ---
|
|
55
|
-
// Consumers assign these to columns: { valueParser: numberParser }
|
|
56
|
-
// Return `undefined` to reject; `null` to clear the cell.
|
|
57
|
-
/**
|
|
58
|
-
* Parses a value as a number. Strips whitespace and commas.
|
|
59
|
-
* Returns `undefined` (reject) if result is NaN.
|
|
60
|
-
*/
|
|
61
|
-
export function numberParser(params) {
|
|
62
|
-
const { newValue } = params;
|
|
63
|
-
if (newValue === '' || newValue == null)
|
|
64
|
-
return null;
|
|
65
|
-
const str = String(newValue).replace(/[\s,]/g, '');
|
|
66
|
-
const num = Number(str);
|
|
67
|
-
return Number.isNaN(num) ? undefined : num;
|
|
68
|
-
}
|
|
69
|
-
/**
|
|
70
|
-
* Parses a currency string. Strips currency symbols ($, €, £, ¥), whitespace, commas.
|
|
71
|
-
* Returns `undefined` (reject) if result is NaN.
|
|
72
|
-
*/
|
|
73
|
-
export function currencyParser(params) {
|
|
74
|
-
const { newValue } = params;
|
|
75
|
-
if (newValue === '' || newValue == null)
|
|
76
|
-
return null;
|
|
77
|
-
const str = String(newValue)
|
|
78
|
-
.replace(/[$\u20AC\u00A3\u00A5]/g, '') // $, €, £, ¥
|
|
79
|
-
.replace(/[\s,]/g, '');
|
|
80
|
-
const num = Number(str);
|
|
81
|
-
return Number.isNaN(num) ? undefined : num;
|
|
82
|
-
}
|
|
83
|
-
/**
|
|
84
|
-
* Parses a date string via `new Date()`. Returns ISO string or `undefined` if invalid.
|
|
85
|
-
*/
|
|
86
|
-
export function dateParser(params) {
|
|
87
|
-
const { newValue } = params;
|
|
88
|
-
if (newValue === '' || newValue == null)
|
|
89
|
-
return null;
|
|
90
|
-
const str = String(newValue).trim();
|
|
91
|
-
const date = new Date(str);
|
|
92
|
-
if (Number.isNaN(date.getTime()))
|
|
93
|
-
return undefined;
|
|
94
|
-
return date.toISOString();
|
|
95
|
-
}
|
|
96
|
-
/**
|
|
97
|
-
* Validates an email address with a basic regex.
|
|
98
|
-
* Returns the trimmed string or `undefined` if invalid.
|
|
99
|
-
*/
|
|
100
|
-
export function emailParser(params) {
|
|
101
|
-
const { newValue } = params;
|
|
102
|
-
if (newValue === '' || newValue == null)
|
|
103
|
-
return null;
|
|
104
|
-
const str = String(newValue).trim();
|
|
105
|
-
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(str) ? str : undefined;
|
|
106
|
-
}
|
|
107
|
-
/**
|
|
108
|
-
* Parses boolean-like values: true/false/yes/no/1/0.
|
|
109
|
-
* Returns `undefined` if not recognized.
|
|
110
|
-
*/
|
|
111
|
-
export function booleanParser(params) {
|
|
112
|
-
const { newValue } = params;
|
|
113
|
-
if (newValue === '' || newValue == null)
|
|
114
|
-
return null;
|
|
115
|
-
const str = String(newValue).trim().toLowerCase();
|
|
116
|
-
if (['true', 'yes', '1'].includes(str))
|
|
117
|
-
return true;
|
|
118
|
-
if (['false', 'no', '0'].includes(str))
|
|
119
|
-
return false;
|
|
120
|
-
return undefined;
|
|
121
|
-
}
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Compute the range of rows that should be rendered for a given scroll position.
|
|
3
|
-
*
|
|
4
|
-
* @param scrollTop - Current vertical scroll offset (px)
|
|
5
|
-
* @param rowHeight - Fixed height of each row (px)
|
|
6
|
-
* @param containerHeight - Visible height of the scroll container (px)
|
|
7
|
-
* @param totalRows - Total number of rows in the dataset
|
|
8
|
-
* @param overscan - Number of extra rows to render above and below the visible area (default: 5)
|
|
9
|
-
* @returns The visible range with start/end indices and top/bottom spacer offsets
|
|
10
|
-
*/
|
|
11
|
-
export function computeVisibleRange(scrollTop, rowHeight, containerHeight, totalRows, overscan = 5) {
|
|
12
|
-
if (totalRows <= 0 || rowHeight <= 0 || containerHeight <= 0) {
|
|
13
|
-
return { startIndex: 0, endIndex: 0, offsetTop: 0, offsetBottom: 0 };
|
|
14
|
-
}
|
|
15
|
-
const startIndex = Math.max(0, Math.floor(scrollTop / rowHeight) - overscan);
|
|
16
|
-
const endIndex = Math.min(totalRows - 1, Math.ceil((scrollTop + containerHeight) / rowHeight) + overscan);
|
|
17
|
-
const offsetTop = startIndex * rowHeight;
|
|
18
|
-
const offsetBottom = Math.max(0, (totalRows - endIndex - 1) * rowHeight);
|
|
19
|
-
return { startIndex, endIndex, offsetTop, offsetBottom };
|
|
20
|
-
}
|
|
21
|
-
/**
|
|
22
|
-
* Compute the total scrollable height for all rows.
|
|
23
|
-
*/
|
|
24
|
-
export function computeTotalHeight(totalRows, rowHeight) {
|
|
25
|
-
return totalRows * rowHeight;
|
|
26
|
-
}
|
|
27
|
-
/**
|
|
28
|
-
* Compute the scrollTop value needed to bring a specific row into view.
|
|
29
|
-
*
|
|
30
|
-
* @param rowIndex - The row to scroll to
|
|
31
|
-
* @param rowHeight - Fixed height of each row (px)
|
|
32
|
-
* @param containerHeight - Visible height of the scroll container (px)
|
|
33
|
-
* @param align - Where to position the row: 'start' (top), 'center', or 'end' (bottom). Default: 'start'.
|
|
34
|
-
* @returns The scrollTop value to set on the container
|
|
35
|
-
*/
|
|
36
|
-
export function getScrollTopForRow(rowIndex, rowHeight, containerHeight, align = 'start') {
|
|
37
|
-
const rowTop = rowIndex * rowHeight;
|
|
38
|
-
switch (align) {
|
|
39
|
-
case 'start':
|
|
40
|
-
return rowTop;
|
|
41
|
-
case 'center':
|
|
42
|
-
return Math.max(0, rowTop - (containerHeight - rowHeight) / 2);
|
|
43
|
-
case 'end':
|
|
44
|
-
return Math.max(0, rowTop - containerHeight + rowHeight);
|
|
45
|
-
}
|
|
46
|
-
}
|