@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.
Files changed (59) hide show
  1. package/CHANGELOG.md +41 -0
  2. package/dist/ai.cjs.map +1 -1
  3. package/dist/ai.es.js.map +1 -1
  4. package/dist/index.d.ts +2 -2
  5. package/dist/opengridx.css +1 -1
  6. package/dist/opengridx.es.js +6043 -5979
  7. package/dist/opengridx.es.js.map +1 -1
  8. package/dist/opengridx.umd.js +16 -16
  9. package/dist/opengridx.umd.js.map +1 -1
  10. package/docs/API_REFERENCE.md +28 -0
  11. package/docs/features/filtering.md +2 -0
  12. package/lib/components/AiAssistant/GridAiAssistantPanel.css +2 -2
  13. package/lib/components/Cell/Cell.css +1 -3
  14. package/lib/components/ColumnGroupHeader/ColumnGroupHeader.css +1 -2
  15. package/lib/components/DataGrid/DataGrid.css +1 -28
  16. package/lib/components/DataGrid/DataGrid.tsx +2 -4
  17. package/lib/components/DataGrid/GridListView.tsx +1 -1
  18. package/lib/components/DataGrid/GridPinnedRows.tsx +1 -1
  19. package/lib/components/DataGrid/GridVirtualRows.tsx +3 -2
  20. package/lib/components/FilterPanel/FilterPanel.tsx +7 -39
  21. package/lib/components/Header/Header.css +2 -11
  22. package/lib/components/HeaderFilter/HeaderFilterCell.tsx +6 -37
  23. package/lib/components/ListView/ListView.css +0 -16
  24. package/lib/components/Row/Row.tsx +4 -1
  25. package/lib/hooks/core/useDataGrid.ts +40 -35
  26. package/lib/hooks/core/useGridAggregationApi.ts +1 -1
  27. package/lib/hooks/core/useGridCellSelection.ts +3 -4
  28. package/lib/hooks/core/useGridClipboardPaste.ts +1 -1
  29. package/lib/hooks/core/useGridDetailPanel.ts +1 -1
  30. package/lib/hooks/core/useGridFillHandle.ts +1 -1
  31. package/lib/hooks/core/useGridHierarchy.ts +2 -2
  32. package/lib/hooks/core/useGridKeyboardNavigation.ts +1 -1
  33. package/lib/hooks/core/useGridLiveRowSelection.ts +9 -2
  34. package/lib/hooks/core/useGridRangeEditing.ts +1 -1
  35. package/lib/hooks/core/useGridRowInteractions.ts +3 -2
  36. package/lib/hooks/features/useGridSpanning.ts +4 -4
  37. package/lib/hooks/useRowGrouping.ts +19 -10
  38. package/lib/hooks/useRowReorder.ts +9 -3
  39. package/lib/styles/base/reset.css +6 -7
  40. package/lib/theme/__baseline__/default-theme.darwin.chromium.json +1 -1503
  41. package/lib/theme/__baseline__/default-theme.darwin.firefox.json +1 -1503
  42. package/lib/theme/__baseline__/default-theme.darwin.webkit.json +1 -1503
  43. package/lib/types/rows.ts +0 -2
  44. package/lib/types/state.ts +9 -0
  45. package/lib/utils/cellSelection.ts +0 -2
  46. package/lib/utils/editing/index.ts +1 -1
  47. package/lib/utils/editing/targets.ts +2 -3
  48. package/lib/utils/filtering/filterInput.ts +45 -0
  49. package/lib/utils/filtering/index.ts +0 -28
  50. package/lib/utils/gridApiRows.ts +1 -1
  51. package/lib/utils/grouping/groupedExportRows.ts +1 -1
  52. package/lib/utils/grouping/layeredMap.ts +52 -0
  53. package/lib/utils/pinning/index.ts +0 -107
  54. package/lib/utils/rowIndex.ts +67 -0
  55. package/lib/utils/sorting/index.ts +42 -33
  56. package/lib/utils/values.ts +5 -1
  57. package/llms.txt +7 -3
  58. package/package.json +1 -1
  59. 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: Map<GridRowId, GridRowMeta>;
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: Map<GridRowId, GridRowMeta> = new 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<Map<GridRowId, GridRowMeta>>(() => {
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?: Map<GridRowId, GridRowMeta>;
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 stale ? live : selectedRowIds;
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: Map<GridRowId, GridRowMeta>;
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: Map<GridRowId, GridRowMeta>;
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.map(getRowId);
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?: Map<GridRowId, GridRowMeta>;
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?: Map<GridRowId, GridRowMeta>;
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 || row._isSkeleton) continue;
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: Map<GridRowId, GridRowMeta> | undefined,
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
- const rowMetaMap = useMemo<Map<GridRowId, GridRowMeta>>(() => {
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
- const variants = groupRowVariants.get(id);
443
- if (!variants) {
444
- // One object per row (not shared per depth): `params.rowMeta` is public, and a consumer that
445
- // writes to it must not change other rows.
446
- map.set(id, { hasChildren: false, treeDepth: node.depth, isGroupRow: false });
447
- return;
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
- .ogx--dragging {
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
- .ogx button:not(.ogx-menu-item) {
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;