@opencorestack/opengridx 3.7.1 → 3.8.1
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/CHANGELOG.md +41 -0
- package/dist/ai.cjs.map +1 -1
- package/dist/ai.es.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/opengridx.css +1 -1
- package/dist/opengridx.es.js +6043 -5979
- package/dist/opengridx.es.js.map +1 -1
- package/dist/opengridx.umd.js +16 -16
- package/dist/opengridx.umd.js.map +1 -1
- package/docs/API_REFERENCE.md +28 -0
- package/docs/features/filtering.md +2 -0
- package/lib/components/AiAssistant/GridAiAssistantPanel.css +2 -2
- package/lib/components/Cell/Cell.css +1 -3
- package/lib/components/ColumnGroupHeader/ColumnGroupHeader.css +1 -2
- package/lib/components/DataGrid/DataGrid.css +1 -28
- package/lib/components/DataGrid/DataGrid.tsx +2 -4
- package/lib/components/DataGrid/GridListView.tsx +1 -1
- package/lib/components/DataGrid/GridPinnedRows.tsx +1 -1
- package/lib/components/DataGrid/GridVirtualRows.tsx +3 -2
- package/lib/components/FilterPanel/FilterPanel.tsx +7 -39
- package/lib/components/Header/Header.css +2 -11
- package/lib/components/HeaderFilter/HeaderFilterCell.tsx +6 -37
- package/lib/components/ListView/ListView.css +0 -16
- package/lib/components/Row/Row.tsx +4 -1
- package/lib/hooks/core/useDataGrid.ts +40 -35
- package/lib/hooks/core/useGridAggregationApi.ts +1 -1
- package/lib/hooks/core/useGridCellSelection.ts +3 -4
- package/lib/hooks/core/useGridClipboardPaste.ts +1 -1
- package/lib/hooks/core/useGridDetailPanel.ts +1 -1
- package/lib/hooks/core/useGridFillHandle.ts +1 -1
- package/lib/hooks/core/useGridHierarchy.ts +2 -2
- package/lib/hooks/core/useGridKeyboardNavigation.ts +1 -1
- package/lib/hooks/core/useGridLiveRowSelection.ts +9 -2
- package/lib/hooks/core/useGridRangeEditing.ts +1 -1
- package/lib/hooks/core/useGridRowInteractions.ts +3 -2
- package/lib/hooks/features/useGridSpanning.ts +4 -4
- package/lib/hooks/useRowGrouping.ts +19 -10
- package/lib/hooks/useRowReorder.ts +9 -3
- package/lib/styles/base/reset.css +6 -7
- package/lib/theme/__baseline__/default-theme.darwin.chromium.json +1 -1503
- package/lib/theme/__baseline__/default-theme.darwin.firefox.json +1 -1503
- package/lib/theme/__baseline__/default-theme.darwin.webkit.json +1 -1503
- package/lib/types/rows.ts +0 -2
- package/lib/types/state.ts +9 -0
- package/lib/utils/cellSelection.ts +0 -2
- package/lib/utils/editing/index.ts +1 -1
- package/lib/utils/editing/targets.ts +2 -3
- package/lib/utils/filtering/filterInput.ts +45 -0
- package/lib/utils/filtering/index.ts +0 -28
- package/lib/utils/gridApiRows.ts +1 -1
- package/lib/utils/grouping/groupedExportRows.ts +1 -1
- package/lib/utils/grouping/layeredMap.ts +52 -0
- package/lib/utils/pinning/index.ts +0 -107
- package/lib/utils/rowIndex.ts +67 -0
- package/lib/utils/sorting/index.ts +42 -33
- package/lib/utils/values.ts +5 -1
- package/llms.txt +7 -3
- package/package.json +1 -1
- package/lib/theme/defaultStyles.ts +0 -0
|
@@ -47,7 +47,7 @@ export interface UseGridFillHandleParams<R extends GridRowModel> {
|
|
|
47
47
|
navigationColumns: ReadonlyArray<GridColDef<R>>;
|
|
48
48
|
columnIndexMap: Map<string, number>;
|
|
49
49
|
isCellEditable?: (params: GridCellParams<R>) => boolean;
|
|
50
|
-
rowMetaMap:
|
|
50
|
+
rowMetaMap: ReadonlyMap<GridRowId, GridRowMeta>;
|
|
51
51
|
getSpanOrigin?: (rowId: GridRowId, field: string) => { rowId: GridRowId; field: string } | null;
|
|
52
52
|
batchEdit: UseGridBatchEditReturn<R>;
|
|
53
53
|
onFill?: (result: GridFillResult) => void;
|
|
@@ -3,7 +3,7 @@ import { useTreeData } from '../useTreeData';
|
|
|
3
3
|
import { useRowGrouping, type UseRowGroupingParams } from '../useRowGrouping';
|
|
4
4
|
import type { GridRowGroupingModel, GridRowId, GridRowMeta, GridRowModel } from '../../types';
|
|
5
5
|
|
|
6
|
-
const EMPTY_ROW_META_MAP:
|
|
6
|
+
const EMPTY_ROW_META_MAP: ReadonlyMap<GridRowId, GridRowMeta> = new Map();
|
|
7
7
|
|
|
8
8
|
export interface UseGridHierarchyParams<R extends GridRowModel> extends Pick<
|
|
9
9
|
UseRowGroupingParams<R>,
|
|
@@ -71,7 +71,7 @@ export function useGridHierarchy<R extends GridRowModel>(params: UseGridHierarch
|
|
|
71
71
|
|
|
72
72
|
const activeHierarchyHandlers = isTreeData ? treeDataHandlers : (isRowGrouping ? rowGroupingHandlers : null);
|
|
73
73
|
|
|
74
|
-
const rowMetaMap = useMemo<
|
|
74
|
+
const rowMetaMap = useMemo<ReadonlyMap<GridRowId, GridRowMeta>>(() => {
|
|
75
75
|
if (isTreeData) return treeDataHandlers.rowMetaMap;
|
|
76
76
|
if (isRowGrouping) return rowGroupingHandlers.rowMetaMap;
|
|
77
77
|
return EMPTY_ROW_META_MAP;
|
|
@@ -71,7 +71,7 @@ export interface UseGridKeyboardNavigationParams<R extends GridRowModel> {
|
|
|
71
71
|
/** Resolves a row's id (getRowId); defaults to `row.id`. */
|
|
72
72
|
getRowId?: (row: R) => GridRowId;
|
|
73
73
|
/** Hierarchy metadata. Synthetic group rows are never edited or selected from the keyboard. */
|
|
74
|
-
rowMetaMap?:
|
|
74
|
+
rowMetaMap?: ReadonlyMap<GridRowId, GridRowMeta>;
|
|
75
75
|
/** Enter on a non-editable cell: the same handler a row click runs. */
|
|
76
76
|
onRowActivate?: (params: GridRowParams<R>) => void;
|
|
77
77
|
/**
|
|
@@ -37,13 +37,20 @@ export function useGridLiveRowSelection(params: UseGridLiveRowSelectionParams):
|
|
|
37
37
|
|
|
38
38
|
const liveRowIds = useMemo(() => {
|
|
39
39
|
if (!enabled || selectedRowIds.size === 0) return selectedRowIds;
|
|
40
|
+
// Check first, copy only when something is stale: select-all at 1M rows selects 1M ids.
|
|
40
41
|
let stale = false;
|
|
42
|
+
for (const id of selectedRowIds) {
|
|
43
|
+
if (!rowsLookup.has(id) && !rowMetaMap.has(id)) {
|
|
44
|
+
stale = true;
|
|
45
|
+
break;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
if (!stale) return selectedRowIds;
|
|
41
49
|
const live = new Set<GridRowId>();
|
|
42
50
|
for (const id of selectedRowIds) {
|
|
43
51
|
if (rowsLookup.has(id) || rowMetaMap.has(id)) live.add(id);
|
|
44
|
-
else stale = true;
|
|
45
52
|
}
|
|
46
|
-
return
|
|
53
|
+
return live;
|
|
47
54
|
}, [enabled, selectedRowIds, rowsLookup, rowMetaMap]);
|
|
48
55
|
|
|
49
56
|
// Latest callback, so an inline onRowSelectionModelChange does not report again on every render.
|
|
@@ -29,7 +29,7 @@ export interface UseGridRangeEditingParams<R extends GridRowModel> {
|
|
|
29
29
|
navigationColumns: GridColDef<R>[];
|
|
30
30
|
columnIndexMap: Map<string, number>;
|
|
31
31
|
isCellEditable?: (params: GridCellParams<R>) => boolean;
|
|
32
|
-
rowMetaMap:
|
|
32
|
+
rowMetaMap: ReadonlyMap<GridRowId, GridRowMeta>;
|
|
33
33
|
getSpanOrigin?: (rowId: GridRowId, field: string) => { rowId: GridRowId; field: string } | null;
|
|
34
34
|
isEditing: boolean;
|
|
35
35
|
batchEdit: UseGridBatchEditReturn<R>;
|
|
@@ -3,6 +3,7 @@ import { useGridRowSelection, type UseGridRowSelectionReturn } from './useGridRo
|
|
|
3
3
|
import { PIVOT_GRAND_TOTAL_ID } from '../../utils/pivot';
|
|
4
4
|
import { isSyntheticRowId } from '../../utils/syntheticRows';
|
|
5
5
|
import type { GridRowId, GridRowMeta, GridRowModel, GridRowParams } from '../../types';
|
|
6
|
+
import { getRowIndex } from '../../utils/rowIndex';
|
|
6
7
|
|
|
7
8
|
export interface UseGridRowInteractionsParams<R extends GridRowModel> {
|
|
8
9
|
/** Data rows that pass the filter (pinned included, synthetic group rows excluded). */
|
|
@@ -14,7 +15,7 @@ export interface UseGridRowInteractionsParams<R extends GridRowModel> {
|
|
|
14
15
|
disableMultipleRowSelection: boolean;
|
|
15
16
|
disableRowSelectionOnClick: boolean;
|
|
16
17
|
isHierarchyEnabled: boolean;
|
|
17
|
-
rowMetaMap:
|
|
18
|
+
rowMetaMap: ReadonlyMap<GridRowId, GridRowMeta>;
|
|
18
19
|
/** The active tree-data / row-grouping handlers, if any. */
|
|
19
20
|
activeHierarchyHandlers: { toggleExpansion: (id: GridRowId) => void } | null;
|
|
20
21
|
onRowClick: ((params: GridRowParams<R>) => void) | undefined;
|
|
@@ -41,7 +42,7 @@ export function useGridRowInteractions<R extends GridRowModel>(
|
|
|
41
42
|
// filter: never rows hidden by it, synthetic group rows, the pivot grand total, or stale
|
|
42
43
|
// ids in the selection.
|
|
43
44
|
const selectableRowIds = useMemo(() => {
|
|
44
|
-
const ids = dataRows
|
|
45
|
+
const { rowIds: ids } = getRowIndex(dataRows, getRowId, 'data');
|
|
45
46
|
return isPivotActive ? ids.filter(id => id !== PIVOT_GRAND_TOTAL_ID) : ids;
|
|
46
47
|
}, [dataRows, getRowId, isPivotActive]);
|
|
47
48
|
const rowSelection = useGridRowSelection({
|
|
@@ -63,7 +63,7 @@ export interface UseGridSpanningParams<R extends GridRowModel> {
|
|
|
63
63
|
rightPinnedColumns: GridColDef<R>[];
|
|
64
64
|
/** Rows whose detail panel is expanded. A row span ends at such a row, above its panel. */
|
|
65
65
|
expandedRowIds?: Set<GridRowId>;
|
|
66
|
-
rowMetaMap?:
|
|
66
|
+
rowMetaMap?: ReadonlyMap<GridRowId, GridRowMeta>;
|
|
67
67
|
/** Resolves a row's id (the grid's getRowId); span caches are keyed by it. Defaults to `row.id`. */
|
|
68
68
|
getRowId?: (row: R) => GridRowId;
|
|
69
69
|
}
|
|
@@ -246,7 +246,7 @@ export function useGridSpanning<R extends GridRowModel>(params: UseGridSpanningP
|
|
|
246
246
|
interface SectionContext<R extends GridRowModel> {
|
|
247
247
|
spanColumns: { columns: SpanColumn<R>[]; leftCount: number; unpinnedCount: number };
|
|
248
248
|
expandedRowIds?: Set<GridRowId>;
|
|
249
|
-
rowMetaMap?:
|
|
249
|
+
rowMetaMap?: ReadonlyMap<GridRowId, GridRowMeta>;
|
|
250
250
|
getRowId: (row: R) => GridRowId;
|
|
251
251
|
warned: Set<string>;
|
|
252
252
|
out: SpanComputation;
|
|
@@ -356,7 +356,7 @@ function computeSection<R extends GridRowModel>(
|
|
|
356
356
|
|
|
357
357
|
for (let r = 0; r < rows.length; r++) {
|
|
358
358
|
const row = rows[r];
|
|
359
|
-
if (!row
|
|
359
|
+
if (!row) continue;
|
|
360
360
|
// Synthetic rows hold no data of their own: they are never a span origin (spans from above
|
|
361
361
|
// already end before them).
|
|
362
362
|
if (isSyntheticRowId(getRowId(row), rowMetaMap)) continue;
|
|
@@ -390,7 +390,7 @@ function buildParams<R extends GridRowModel>(
|
|
|
390
390
|
colDef: GridColDef<R>,
|
|
391
391
|
rowIndex: number,
|
|
392
392
|
colIndex: number,
|
|
393
|
-
rowMetaMap:
|
|
393
|
+
rowMetaMap: ReadonlyMap<GridRowId, GridRowMeta> | undefined,
|
|
394
394
|
warned: Set<string>,
|
|
395
395
|
): GridRenderCellParams<R> {
|
|
396
396
|
const raw = row[colDef.field as keyof R];
|
|
@@ -15,6 +15,7 @@ import { sortItemsBySortModel, type GridSortValue } from '../utils/sorting';
|
|
|
15
15
|
import { callSafely, getCellValue } from '../utils/values';
|
|
16
16
|
import type { GridColumnLookup } from '../utils/columnLookup';
|
|
17
17
|
import { computeAggregations } from '../utils/aggregation';
|
|
18
|
+
import { LayeredMap } from '../utils/grouping/layeredMap';
|
|
18
19
|
|
|
19
20
|
/** Where a group's aggregates are shown: on the group row, on a subtotal row after its children, or not at all. */
|
|
20
21
|
export type GridGroupAggregationPosition = 'inline' | 'footer' | null;
|
|
@@ -436,16 +437,24 @@ export function useRowGrouping<R extends GridRowModel>(params: UseRowGroupingPar
|
|
|
436
437
|
|
|
437
438
|
const getNode = useCallback((id: GridRowId) => treeNodes.get(id), [treeNodes]);
|
|
438
439
|
|
|
439
|
-
|
|
440
|
+
// Leaf entries do not depend on expansion: built once per tree, so expanding or collapsing a
|
|
441
|
+
// group rebuilds only the group entries (a LayeredMap reads both without copying).
|
|
442
|
+
const leafMetaMap = useMemo<Map<GridRowId, GridRowMeta>>(() => {
|
|
440
443
|
const map = new Map<GridRowId, GridRowMeta>();
|
|
441
444
|
treeNodes.forEach((node, id) => {
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
445
|
+
if (groupRowVariants.has(id)) return;
|
|
446
|
+
// One object per row (not shared per depth): `params.rowMeta` is public, and a consumer that
|
|
447
|
+
// writes to it must not change other rows.
|
|
448
|
+
map.set(id, { hasChildren: false, treeDepth: node.depth, isGroupRow: false });
|
|
449
|
+
});
|
|
450
|
+
return map;
|
|
451
|
+
}, [treeNodes, groupRowVariants]);
|
|
452
|
+
|
|
453
|
+
const rowMetaMap = useMemo<ReadonlyMap<GridRowId, GridRowMeta>>(() => {
|
|
454
|
+
const map = new Map<GridRowId, GridRowMeta>();
|
|
455
|
+
groupRowVariants.forEach((variants, id) => {
|
|
456
|
+
const node = treeNodes.get(id);
|
|
457
|
+
if (!node) return;
|
|
449
458
|
const groupMeta: GridRowMeta = {
|
|
450
459
|
hasChildren: true,
|
|
451
460
|
treeDepth: node.depth,
|
|
@@ -467,8 +476,8 @@ export function useRowGrouping<R extends GridRowModel>(params: UseRowGroupingPar
|
|
|
467
476
|
});
|
|
468
477
|
}
|
|
469
478
|
});
|
|
470
|
-
return map;
|
|
471
|
-
}, [treeNodes, groupRowVariants, expandedGroupIds, aggregationPositions]);
|
|
479
|
+
return new LayeredMap(leafMetaMap, map);
|
|
480
|
+
}, [leafMetaMap, treeNodes, groupRowVariants, expandedGroupIds, aggregationPositions]);
|
|
472
481
|
|
|
473
482
|
return useMemo(() => ({
|
|
474
483
|
treeNodes,
|
|
@@ -11,6 +11,11 @@ export interface UseRowReorderProps<R extends GridRowModel = GridRowModel> {
|
|
|
11
11
|
*/
|
|
12
12
|
rows: R[];
|
|
13
13
|
getRowId?: (row: R) => GridRowId;
|
|
14
|
+
/**
|
|
15
|
+
* Row id → index in `rows`, when the caller already has it (the grid's row store does). Saves
|
|
16
|
+
* a pass over every row each time `rows` changes, such as after every cell edit.
|
|
17
|
+
*/
|
|
18
|
+
indexById?: ReadonlyMap<GridRowId, number>;
|
|
14
19
|
rowReordering?: boolean;
|
|
15
20
|
}
|
|
16
21
|
|
|
@@ -28,16 +33,17 @@ export interface UseRowReorderReturn {
|
|
|
28
33
|
export function useRowReorder<R extends GridRowModel = GridRowModel>(
|
|
29
34
|
props: UseRowReorderProps<R>
|
|
30
35
|
): UseRowReorderReturn {
|
|
31
|
-
const { onRowOrderChange, rows, getRowId, rowReordering = false } = props;
|
|
36
|
+
const { onRowOrderChange, rows, getRowId, indexById: givenIndexById, rowReordering = false } = props;
|
|
32
37
|
const [draggedRowId, setDraggedRowId] = useState<GridRowId | null>(null);
|
|
33
38
|
const [dragOverRowId, setDragOverRowId] = useState<GridRowId | null>(null);
|
|
34
39
|
const detachDragEndRef = useRef<() => void>(() => {});
|
|
35
40
|
|
|
36
|
-
const indexById = useMemo(() => {
|
|
41
|
+
const indexById = useMemo<ReadonlyMap<GridRowId, number>>(() => {
|
|
42
|
+
if (givenIndexById) return givenIndexById;
|
|
37
43
|
const map = new Map<GridRowId, number>();
|
|
38
44
|
rows.forEach((row, index) => map.set(getRowId ? getRowId(row) : row.id, index));
|
|
39
45
|
return map;
|
|
40
|
-
}, [rows, getRowId]);
|
|
46
|
+
}, [givenIndexById, rows, getRowId]);
|
|
41
47
|
|
|
42
48
|
const canReorderRow = useCallback((id: GridRowId) => indexById.has(id), [indexById]);
|
|
43
49
|
|
|
@@ -5,18 +5,17 @@
|
|
|
5
5
|
:where([class*="ogx"], [class*="ogx"] *)::after {
|
|
6
6
|
box-sizing: border-box;
|
|
7
7
|
}
|
|
8
|
-
|
|
9
|
-
user-select: none;
|
|
10
|
-
-webkit-user-select: none;
|
|
11
|
-
-moz-user-select: none;
|
|
12
|
-
-ms-user-select: none;
|
|
13
|
-
}
|
|
8
|
+
|
|
14
9
|
.ogx *:focus {
|
|
15
10
|
outline: none;
|
|
16
11
|
}
|
|
17
|
-
|
|
12
|
+
/* Background and border at zero specificity: a component's own button class (.ogx-button--contained,
|
|
13
|
+
the AI panel's suggestions, …) must win over this reset. Font and cursor keep their old weight. */
|
|
14
|
+
:where(.ogx button:not(.ogx-menu-item)) {
|
|
18
15
|
background: none;
|
|
19
16
|
border: none;
|
|
17
|
+
}
|
|
18
|
+
.ogx button:not(.ogx-menu-item) {
|
|
20
19
|
cursor: pointer;
|
|
21
20
|
font-family: inherit;
|
|
22
21
|
font-size: inherit;
|