@alaarab/ogrid-core 2.0.19 → 2.0.21
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/utils/aggregationUtils.js +13 -16
- package/dist/esm/utils/clientSideData.js +7 -5
- package/dist/esm/utils/clipboardHelpers.js +3 -2
- package/dist/esm/utils/columnAutosize.js +2 -2
- package/dist/esm/utils/ogridHelpers.js +26 -12
- package/dist/types/types/columnTypes.d.ts +2 -0
- package/dist/types/utils/columnAutosize.d.ts +2 -2
- package/package.json +1 -1
|
@@ -9,8 +9,11 @@ export function computeAggregations(items, visibleCols, selectionRange) {
|
|
|
9
9
|
if (!selectionRange)
|
|
10
10
|
return null;
|
|
11
11
|
const norm = normalizeSelectionRange(selectionRange);
|
|
12
|
-
const numericValues = [];
|
|
13
12
|
let totalCells = 0;
|
|
13
|
+
let sum = 0;
|
|
14
|
+
let min = Infinity;
|
|
15
|
+
let max = -Infinity;
|
|
16
|
+
let count = 0;
|
|
14
17
|
for (let r = norm.startRow; r <= norm.endRow; r++) {
|
|
15
18
|
for (let c = norm.startCol; c <= norm.endCol; c++) {
|
|
16
19
|
if (r >= items.length || c >= visibleCols.length)
|
|
@@ -23,29 +26,23 @@ export function computeAggregations(items, visibleCols, selectionRange) {
|
|
|
23
26
|
// return NaN instead of partially parsing to 2020
|
|
24
27
|
const num = typeof raw === 'number' ? raw : Number(raw);
|
|
25
28
|
if (!isNaN(num) && isFinite(num)) {
|
|
26
|
-
|
|
29
|
+
sum += num;
|
|
30
|
+
if (num < min)
|
|
31
|
+
min = num;
|
|
32
|
+
if (num > max)
|
|
33
|
+
max = num;
|
|
34
|
+
count++;
|
|
27
35
|
}
|
|
28
36
|
}
|
|
29
37
|
}
|
|
30
38
|
// Need at least 2 cells selected and at least 1 numeric value to show aggregation
|
|
31
|
-
if (totalCells < 2 ||
|
|
39
|
+
if (totalCells < 2 || count === 0)
|
|
32
40
|
return null;
|
|
33
|
-
let sum = 0;
|
|
34
|
-
let min = numericValues[0];
|
|
35
|
-
let max = numericValues[0];
|
|
36
|
-
for (let i = 0; i < numericValues.length; i++) {
|
|
37
|
-
const v = numericValues[i];
|
|
38
|
-
sum += v;
|
|
39
|
-
if (v < min)
|
|
40
|
-
min = v;
|
|
41
|
-
if (v > max)
|
|
42
|
-
max = v;
|
|
43
|
-
}
|
|
44
41
|
return {
|
|
45
42
|
sum,
|
|
46
|
-
avg: sum /
|
|
43
|
+
avg: sum / count,
|
|
47
44
|
min,
|
|
48
45
|
max,
|
|
49
|
-
count
|
|
46
|
+
count,
|
|
50
47
|
};
|
|
51
48
|
}
|
|
@@ -56,17 +56,19 @@ export function processClientSideData(data, columns, filters, sortBy, sortDirect
|
|
|
56
56
|
}
|
|
57
57
|
case 'date': {
|
|
58
58
|
const dv = val.value;
|
|
59
|
+
// Pre-compute filter boundary timestamps to avoid repeated Date parsing in the filter loop
|
|
60
|
+
const fromTs = dv.from ? new Date(dv.from + 'T00:00:00').getTime() : NaN;
|
|
61
|
+
const toTs = dv.to ? new Date(dv.to + 'T23:59:59.999').getTime() : NaN;
|
|
59
62
|
predicates.push((r) => {
|
|
60
63
|
const cellVal = getCellValue(r, col);
|
|
61
64
|
if (cellVal == null)
|
|
62
65
|
return false;
|
|
63
|
-
const
|
|
64
|
-
if (Number.isNaN(
|
|
66
|
+
const cellTs = new Date(String(cellVal)).getTime();
|
|
67
|
+
if (Number.isNaN(cellTs))
|
|
65
68
|
return false;
|
|
66
|
-
|
|
67
|
-
if (dv.from && cellDateStr < dv.from)
|
|
69
|
+
if (!Number.isNaN(fromTs) && cellTs < fromTs)
|
|
68
70
|
return false;
|
|
69
|
-
if (
|
|
71
|
+
if (!Number.isNaN(toTs) && cellTs > toTs)
|
|
70
72
|
return false;
|
|
71
73
|
return true;
|
|
72
74
|
});
|
|
@@ -12,7 +12,7 @@ export function formatCellValueForTsv(raw, formatted) {
|
|
|
12
12
|
const val = formatted != null && formatted !== '' ? formatted : raw;
|
|
13
13
|
if (val == null || val === '')
|
|
14
14
|
return '';
|
|
15
|
-
return String(val).replace(
|
|
15
|
+
return String(val).replace(/[\t\n]/g, ' ');
|
|
16
16
|
}
|
|
17
17
|
/**
|
|
18
18
|
* Serialize a rectangular cell range to a TSV (tab-separated values) string
|
|
@@ -34,7 +34,8 @@ export function formatSelectionAsTsv(items, visibleCols, range) {
|
|
|
34
34
|
const item = items[r];
|
|
35
35
|
const col = visibleCols[c];
|
|
36
36
|
const raw = getCellValue(item, col);
|
|
37
|
-
const
|
|
37
|
+
const clipboard = col.clipboardFormatter ? col.clipboardFormatter(raw, item) : null;
|
|
38
|
+
const formatted = clipboard ?? (col.valueFormatter ? col.valueFormatter(raw, item) : raw);
|
|
38
39
|
cells.push(formatCellValueForTsv(raw, formatted));
|
|
39
40
|
}
|
|
40
41
|
rows.push(cells.join('\t'));
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
* Column autosize DOM measurement utilities shared across all frameworks.
|
|
3
3
|
*/
|
|
4
4
|
import { DEFAULT_MIN_COLUMN_WIDTH } from '../constants/layout';
|
|
5
|
-
/** Extra pixels added to header label width to account for
|
|
6
|
-
export const AUTOSIZE_EXTRA_PX =
|
|
5
|
+
/** Extra pixels added to header label width to account for filter icon + padding. */
|
|
6
|
+
export const AUTOSIZE_EXTRA_PX = 28;
|
|
7
7
|
/** Maximum column width from autosize. */
|
|
8
8
|
export const AUTOSIZE_MAX_PX = 520;
|
|
9
9
|
/**
|
|
@@ -22,20 +22,34 @@ export function mergeFilter(prev, key, value) {
|
|
|
22
22
|
}
|
|
23
23
|
/** Derive filter options for multiSelect columns from client-side data. */
|
|
24
24
|
export function deriveFilterOptionsFromData(items, columns) {
|
|
25
|
-
|
|
26
|
-
|
|
25
|
+
// Collect multiSelect columns upfront
|
|
26
|
+
const filterCols = [];
|
|
27
|
+
for (let i = 0; i < columns.length; i++) {
|
|
28
|
+
const col = columns[i];
|
|
27
29
|
const f = col.filterable && typeof col.filterable === 'object' ? col.filterable : null;
|
|
28
|
-
if (f?.type
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
30
|
+
if (f?.type === 'multiSelect') {
|
|
31
|
+
filterCols.push({ col, field: getFilterField(col) });
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
if (filterCols.length === 0)
|
|
35
|
+
return {};
|
|
36
|
+
// Single pass through items, collecting values for all filter columns simultaneously
|
|
37
|
+
const valueSets = new Map();
|
|
38
|
+
for (let i = 0; i < filterCols.length; i++) {
|
|
39
|
+
valueSets.set(filterCols[i].field, new Set());
|
|
40
|
+
}
|
|
41
|
+
for (let i = 0; i < items.length; i++) {
|
|
42
|
+
const item = items[i];
|
|
43
|
+
for (let j = 0; j < filterCols.length; j++) {
|
|
44
|
+
const v = getCellValue(item, filterCols[j].col);
|
|
34
45
|
if (v != null && v !== '')
|
|
35
|
-
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
}
|
|
46
|
+
valueSets.get(filterCols[j].field).add(String(v));
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
const out = {};
|
|
50
|
+
for (let i = 0; i < filterCols.length; i++) {
|
|
51
|
+
out[filterCols[i].field] = Array.from(valueSets.get(filterCols[i].field)).sort();
|
|
52
|
+
}
|
|
39
53
|
return out;
|
|
40
54
|
}
|
|
41
55
|
/** Get list of filter fields that use multiSelect (for useFilterOptions). */
|
|
@@ -44,6 +44,8 @@ export interface IColumnDef<T = unknown> extends IColumnMeta {
|
|
|
44
44
|
valueGetter?: (item: T) => unknown;
|
|
45
45
|
/** Format the cell value for display (used when no renderCell). */
|
|
46
46
|
valueFormatter?: (value: unknown, item: T) => string;
|
|
47
|
+
/** Format the cell value for clipboard copy. When set, overrides valueFormatter for copy/paste. */
|
|
48
|
+
clipboardFormatter?: (value: unknown, item: T) => string;
|
|
47
49
|
/**
|
|
48
50
|
* Parse/validate a new value before it is committed to the cell.
|
|
49
51
|
* Called on paste, inline edit commit, fill handle, and delete.
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Column autosize DOM measurement utilities shared across all frameworks.
|
|
3
3
|
*/
|
|
4
|
-
/** Extra pixels added to header label width to account for
|
|
5
|
-
export declare const AUTOSIZE_EXTRA_PX =
|
|
4
|
+
/** Extra pixels added to header label width to account for filter icon + padding. */
|
|
5
|
+
export declare const AUTOSIZE_EXTRA_PX = 28;
|
|
6
6
|
/** Maximum column width from autosize. */
|
|
7
7
|
export declare const AUTOSIZE_MAX_PX = 520;
|
|
8
8
|
/**
|
package/package.json
CHANGED