@alaarab/ogrid-core 1.8.2 → 2.0.0-beta
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/README.md +28 -31
- package/dist/esm/constants.js +11 -0
- package/dist/esm/index.js +6 -11
- package/dist/esm/types/index.js +2 -1
- package/dist/esm/utils/clientSideData.js +25 -12
- package/dist/esm/utils/columnUtils.js +6 -0
- package/dist/esm/utils/gridRowComparator.js +78 -0
- package/dist/esm/utils/index.js +1 -1
- package/dist/esm/utils/ogridHelpers.js +2 -1
- package/dist/esm/utils/paginationHelpers.js +7 -1
- package/dist/types/constants.d.ts +11 -0
- package/dist/types/index.d.ts +3 -16
- package/dist/types/types/columnTypes.d.ts +5 -9
- package/dist/types/types/dataGridTypes.d.ts +12 -133
- package/dist/types/types/index.d.ts +3 -3
- package/dist/types/utils/gridRowComparator.d.ts +57 -0
- package/dist/types/utils/index.d.ts +2 -2
- package/package.json +6 -24
- package/dist/esm/components/GridContextMenu.js +0 -34
- package/dist/esm/components/MarchingAntsOverlay.js +0 -109
- package/dist/esm/components/OGridLayout.js +0 -90
- package/dist/esm/components/SideBar.js +0 -100
- package/dist/esm/components/StatusBar.js +0 -6
- package/dist/esm/hooks/index.js +0 -19
- package/dist/esm/hooks/useActiveCell.js +0 -46
- package/dist/esm/hooks/useCellEditing.js +0 -11
- package/dist/esm/hooks/useCellSelection.js +0 -318
- package/dist/esm/hooks/useClipboard.js +0 -162
- package/dist/esm/hooks/useColumnChooserState.js +0 -62
- package/dist/esm/hooks/useColumnHeaderFilterState.js +0 -228
- package/dist/esm/hooks/useColumnResize.js +0 -69
- package/dist/esm/hooks/useContextMenu.js +0 -17
- package/dist/esm/hooks/useDataGridState.js +0 -366
- package/dist/esm/hooks/useDebounce.js +0 -35
- package/dist/esm/hooks/useFillHandle.js +0 -191
- package/dist/esm/hooks/useFilterOptions.js +0 -40
- package/dist/esm/hooks/useInlineCellEditorState.js +0 -44
- package/dist/esm/hooks/useKeyboardNavigation.js +0 -436
- package/dist/esm/hooks/useOGrid.js +0 -414
- package/dist/esm/hooks/useRichSelectState.js +0 -53
- package/dist/esm/hooks/useRowSelection.js +0 -68
- package/dist/esm/hooks/useSideBarState.js +0 -34
- package/dist/esm/hooks/useUndoRedo.js +0 -82
- package/dist/esm/storybook/index.js +0 -1
- package/dist/esm/storybook/mockData.js +0 -73
- package/dist/esm/utils/dataGridViewModel.js +0 -233
- package/dist/types/components/GridContextMenu.d.ts +0 -18
- package/dist/types/components/MarchingAntsOverlay.d.ts +0 -15
- package/dist/types/components/OGridLayout.d.ts +0 -37
- package/dist/types/components/SideBar.d.ts +0 -30
- package/dist/types/components/StatusBar.d.ts +0 -24
- package/dist/types/hooks/index.d.ts +0 -37
- package/dist/types/hooks/useActiveCell.d.ts +0 -13
- package/dist/types/hooks/useCellEditing.d.ts +0 -12
- package/dist/types/hooks/useCellSelection.d.ts +0 -17
- package/dist/types/hooks/useClipboard.d.ts +0 -25
- package/dist/types/hooks/useColumnChooserState.d.ts +0 -27
- package/dist/types/hooks/useColumnHeaderFilterState.d.ts +0 -72
- package/dist/types/hooks/useColumnResize.d.ts +0 -18
- package/dist/types/hooks/useContextMenu.d.ts +0 -15
- package/dist/types/hooks/useDataGridState.d.ts +0 -136
- package/dist/types/hooks/useDebounce.d.ts +0 -9
- package/dist/types/hooks/useFillHandle.d.ts +0 -28
- package/dist/types/hooks/useFilterOptions.d.ts +0 -16
- package/dist/types/hooks/useInlineCellEditorState.d.ts +0 -24
- package/dist/types/hooks/useKeyboardNavigation.d.ts +0 -34
- package/dist/types/hooks/useOGrid.d.ts +0 -31
- package/dist/types/hooks/useRichSelectState.d.ts +0 -17
- package/dist/types/hooks/useRowSelection.d.ts +0 -17
- package/dist/types/hooks/useSideBarState.d.ts +0 -15
- package/dist/types/hooks/useUndoRedo.d.ts +0 -21
- package/dist/types/storybook/index.d.ts +0 -2
- package/dist/types/storybook/mockData.d.ts +0 -37
- package/dist/types/utils/dataGridViewModel.d.ts +0 -169
package/README.md
CHANGED
|
@@ -1,48 +1,45 @@
|
|
|
1
|
-
|
|
1
|
+
<p align="center">
|
|
2
|
+
<strong>OGrid Core</strong> — Pure TypeScript types, algorithms, and utilities for OGrid data grids.
|
|
3
|
+
</p>
|
|
2
4
|
|
|
3
|
-
|
|
5
|
+
<p align="center">
|
|
6
|
+
<a href="https://www.npmjs.com/package/@alaarab/ogrid-core"><img src="https://img.shields.io/npm/v/@alaarab/ogrid-core?color=%23217346&label=npm" alt="npm version" /></a>
|
|
7
|
+
<a href="https://github.com/alaarab/ogrid/blob/main/LICENSE"><img src="https://img.shields.io/badge/license-MIT-green" alt="MIT License" /></a>
|
|
8
|
+
<img src="https://img.shields.io/badge/TypeScript-strict-blue" alt="TypeScript strict" />
|
|
9
|
+
</p>
|
|
4
10
|
|
|
5
|
-
|
|
11
|
+
<p align="center">
|
|
12
|
+
<a href="https://alaarab.github.io/ogrid/">Documentation</a> · <a href="https://alaarab.github.io/ogrid/docs/getting-started/overview">Getting Started</a> · <a href="https://alaarab.github.io/ogrid/docs/api/ogrid-props">API Reference</a>
|
|
13
|
+
</p>
|
|
6
14
|
|
|
7
|
-
|
|
15
|
+
---
|
|
8
16
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
17
|
+
Framework-agnostic foundation for [OGrid](https://github.com/alaarab/ogrid) data grids. **Zero dependencies** — pure TypeScript types and utilities shared by [`@alaarab/ogrid-react`](https://www.npmjs.com/package/@alaarab/ogrid-react) and [`@alaarab/ogrid-js`](https://www.npmjs.com/package/@alaarab/ogrid-js).
|
|
18
|
+
|
|
19
|
+
You typically don't need to install this directly — the UI packages re-export everything from core.
|
|
12
20
|
|
|
13
|
-
## What's
|
|
21
|
+
## What's Inside
|
|
14
22
|
|
|
15
23
|
### Types
|
|
16
24
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
- `IFilters` -- Unified filter values (text, multi-select, people)
|
|
21
|
-
- `UserLike` -- Minimal user shape for people picker
|
|
22
|
-
- `IColumnFilterDef`, `IColumnMeta`, `IPageResult`, `ColumnFilterType`
|
|
25
|
+
`IColumnDef<T>` · `IColumnGroupDef` · `IDataSource<T>` · `IFilters` · `FilterValue` · `IDateFilterValue` · `UserLike` · `IOGridApi<T>` · `IOGridProps<T>` · `IOGridDataGridProps<T>` · `ICellEditorProps<T>` · `IGridColumnState` · `ISideBarDef` · `ColumnFilterType` · `IColumnMeta`
|
|
26
|
+
|
|
27
|
+
### Utilities
|
|
23
28
|
|
|
24
|
-
|
|
29
|
+
`getCellValue` · `buildHeaderRows` · `parseValue` · `normalizeSelectionRange` · `isInSelectionRange` · `toUserLike`
|
|
25
30
|
|
|
26
|
-
|
|
27
|
-
- `useDataGridState({ props, wrapperRef })` -- Orchestrator for grid state, selection, editing, clipboard, keyboard, fill handle, status bar
|
|
28
|
-
- `useFilterOptions(dataSource, fields)` -- Loads filter options for multi-select columns
|
|
29
|
-
- `useColumnHeaderFilterState(params)` -- Headless filter popover state (open, temp values, apply/clear, people search)
|
|
30
|
-
- `useColumnChooserState({ columns, visibleColumns, onVisibilityChange })` -- Column visibility dropdown (open, Escape, select all/clear)
|
|
31
|
-
- `useInlineCellEditorState({ value, editorType, onCommit, onCancel })` -- Inline cell editor (localValue, keydown, blur/commit)
|
|
31
|
+
## Install
|
|
32
32
|
|
|
33
|
-
|
|
33
|
+
```bash
|
|
34
|
+
npm install @alaarab/ogrid-core
|
|
35
|
+
```
|
|
34
36
|
|
|
35
|
-
|
|
37
|
+
No peer dependencies. No framework dependencies.
|
|
36
38
|
|
|
37
|
-
|
|
39
|
+
## Documentation
|
|
38
40
|
|
|
39
|
-
|
|
40
|
-
- `getHeaderFilterConfig(col, input)` -- ColumnHeaderFilter props from column + filter/sort state
|
|
41
|
-
- `getCellRenderDescriptor(item, col, rowIndex, colIdx, input)` -- Cell mode (editing-inline / editing-popover / display) and flags for DataGridTable
|
|
42
|
-
- `toUserLike(user)` -- Converts a user-like object to `UserLike`
|
|
43
|
-
- `exportToCsv(items, columns, getValue, filename)` -- Full CSV export
|
|
44
|
-
- `buildCsvHeader`, `buildCsvRows`, `triggerCsvDownload`, `escapeCsvValue` -- Low-level CSV helpers
|
|
41
|
+
Full docs at **[alaarab.github.io/ogrid](https://alaarab.github.io/ogrid/)**.
|
|
45
42
|
|
|
46
43
|
## License
|
|
47
44
|
|
|
48
|
-
MIT
|
|
45
|
+
MIT — Free forever.
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Core OGrid constants — magic numbers centralized for consistency and maintainability.
|
|
3
|
+
*/
|
|
4
|
+
/** Width of the row selection checkbox column in pixels. */
|
|
5
|
+
export const CHECKBOX_COLUMN_WIDTH = 48;
|
|
6
|
+
/** Default minimum width for resizable columns in pixels. */
|
|
7
|
+
export const DEFAULT_MIN_COLUMN_WIDTH = 80;
|
|
8
|
+
/** Horizontal padding inside cells, used for width calculations. */
|
|
9
|
+
export const CELL_PADDING = 16;
|
|
10
|
+
/** Border radius for the grid container in pixels. */
|
|
11
|
+
export const GRID_BORDER_RADIUS = 6;
|
package/dist/esm/index.js
CHANGED
|
@@ -1,11 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
export
|
|
7
|
-
export { GridContextMenu } from './components/GridContextMenu';
|
|
8
|
-
export { MarchingAntsOverlay } from './components/MarchingAntsOverlay';
|
|
9
|
-
export { SideBar } from './components/SideBar';
|
|
10
|
-
// Utilities
|
|
11
|
-
export { escapeCsvValue, buildCsvHeader, buildCsvRows, exportToCsv, triggerCsvDownload, getCellValue, flattenColumns, buildHeaderRows, getFilterField, mergeFilter, deriveFilterOptionsFromData, getMultiSelectFilterFields, getStatusBarParts, getDataGridStatusBarConfig, GRID_CONTEXT_MENU_ITEMS, getContextMenuHandlers, formatShortcut, getPaginationViewModel, PAGE_SIZE_OPTIONS, MAX_PAGE_BUTTONS, getHeaderFilterConfig, getCellRenderDescriptor, isRowInRange, resolveCellDisplayContent, resolveCellStyle, buildInlineEditorProps, buildPopoverEditorProps, getCellInteractionProps, parseValue, numberParser, currencyParser, dateParser, emailParser, booleanParser, computeAggregations, processClientSideData, } from './utils';
|
|
1
|
+
// Types
|
|
2
|
+
export * from './types';
|
|
3
|
+
// Utils
|
|
4
|
+
export * from './utils';
|
|
5
|
+
// Constants
|
|
6
|
+
export * from './constants';
|
package/dist/esm/types/index.js
CHANGED
|
@@ -1 +1,2 @@
|
|
|
1
|
-
|
|
1
|
+
// Utility functions
|
|
2
|
+
export { toUserLike, isInSelectionRange, normalizeSelectionRange, } from './dataGridTypes';
|
|
@@ -12,33 +12,37 @@ import { getFilterField } from './ogridHelpers';
|
|
|
12
12
|
* @returns Filtered and sorted array
|
|
13
13
|
*/
|
|
14
14
|
export function processClientSideData(data, columns, filters, sortBy, sortDirection) {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
columns.
|
|
15
|
+
// --- Filtering (single-pass: build predicates, then one .filter()) ---
|
|
16
|
+
const predicates = [];
|
|
17
|
+
for (let i = 0; i < columns.length; i++) {
|
|
18
|
+
const col = columns[i];
|
|
18
19
|
const filterKey = getFilterField(col);
|
|
19
20
|
const val = filters[filterKey];
|
|
20
21
|
if (!val)
|
|
21
|
-
|
|
22
|
+
continue;
|
|
22
23
|
switch (val.type) {
|
|
23
24
|
case 'multiSelect':
|
|
24
25
|
if (val.value.length > 0) {
|
|
25
|
-
|
|
26
|
+
const allowedSet = new Set(val.value);
|
|
27
|
+
predicates.push((r) => allowedSet.has(String(getCellValue(r, col))));
|
|
26
28
|
}
|
|
27
29
|
break;
|
|
28
|
-
case 'text':
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
30
|
+
case 'text': {
|
|
31
|
+
const trimmed = val.value.trim();
|
|
32
|
+
if (trimmed) {
|
|
33
|
+
const lower = trimmed.toLowerCase();
|
|
34
|
+
predicates.push((r) => String(getCellValue(r, col) ?? '').toLowerCase().includes(lower));
|
|
32
35
|
}
|
|
33
36
|
break;
|
|
37
|
+
}
|
|
34
38
|
case 'people': {
|
|
35
39
|
const email = val.value.email.toLowerCase();
|
|
36
|
-
|
|
40
|
+
predicates.push((r) => String(getCellValue(r, col) ?? '').toLowerCase() === email);
|
|
37
41
|
break;
|
|
38
42
|
}
|
|
39
43
|
case 'date': {
|
|
40
44
|
const dv = val.value;
|
|
41
|
-
|
|
45
|
+
predicates.push((r) => {
|
|
42
46
|
const cellVal = getCellValue(r, col);
|
|
43
47
|
if (cellVal == null)
|
|
44
48
|
return false;
|
|
@@ -55,7 +59,16 @@ export function processClientSideData(data, columns, filters, sortBy, sortDirect
|
|
|
55
59
|
break;
|
|
56
60
|
}
|
|
57
61
|
}
|
|
58
|
-
}
|
|
62
|
+
}
|
|
63
|
+
const rows = predicates.length > 0
|
|
64
|
+
? data.filter((row) => {
|
|
65
|
+
for (let i = 0; i < predicates.length; i++) {
|
|
66
|
+
if (!predicates[i](row))
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
69
|
+
return true;
|
|
70
|
+
})
|
|
71
|
+
: data.slice();
|
|
59
72
|
// --- Sorting ---
|
|
60
73
|
if (sortBy) {
|
|
61
74
|
const sortCol = columns.find((c) => c.columnId === sortBy);
|
|
@@ -66,7 +66,12 @@ export function buildHeaderRows(columns, visibleColumns) {
|
|
|
66
66
|
const totalRows = maxDepth + 1;
|
|
67
67
|
const rows = Array.from({ length: totalRows }, () => []);
|
|
68
68
|
// Step 3: Walk the tree and place cells
|
|
69
|
+
// Cache leaf counts by children array ref to avoid O(n²) repeated traversals
|
|
70
|
+
const leafCountCache = new Map();
|
|
69
71
|
function countVisibleLeaves(cols) {
|
|
72
|
+
const cached = leafCountCache.get(cols);
|
|
73
|
+
if (cached !== undefined)
|
|
74
|
+
return cached;
|
|
70
75
|
let count = 0;
|
|
71
76
|
for (const c of cols) {
|
|
72
77
|
if (isColumnGroupDef(c)) {
|
|
@@ -78,6 +83,7 @@ export function buildHeaderRows(columns, visibleColumns) {
|
|
|
78
83
|
}
|
|
79
84
|
}
|
|
80
85
|
}
|
|
86
|
+
leafCountCache.set(cols, count);
|
|
81
87
|
return count;
|
|
82
88
|
}
|
|
83
89
|
function walk(cols, depth) {
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Checks whether a given row index falls within a selection range.
|
|
3
|
+
* O(1) — used by React.memo comparators to skip unchanged rows.
|
|
4
|
+
*/
|
|
5
|
+
export function isRowInRange(range, rowIndex) {
|
|
6
|
+
if (!range)
|
|
7
|
+
return false;
|
|
8
|
+
const minR = Math.min(range.startRow, range.endRow);
|
|
9
|
+
const maxR = Math.max(range.startRow, range.endRow);
|
|
10
|
+
return rowIndex >= minR && rowIndex <= maxR;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Shared React.memo comparator for GridRow components across all 3 UI packages.
|
|
14
|
+
* Skips re-render for rows unaffected by selection/editing/interaction changes.
|
|
15
|
+
*
|
|
16
|
+
* Used by:
|
|
17
|
+
* - packages/radix/src/DataGridTable/DataGridTable.tsx
|
|
18
|
+
* - packages/fluent/src/DataGridTable/DataGridTable.tsx
|
|
19
|
+
* - packages/material/src/DataGridTable/DataGridTable.tsx
|
|
20
|
+
*/
|
|
21
|
+
export function areGridRowPropsEqual(prev, next) {
|
|
22
|
+
// Data / structure changes — always re-render
|
|
23
|
+
if (prev.item !== next.item)
|
|
24
|
+
return false;
|
|
25
|
+
if (prev.isSelected !== next.isSelected)
|
|
26
|
+
return false;
|
|
27
|
+
if (prev.hasCheckboxCol !== next.hasCheckboxCol)
|
|
28
|
+
return false;
|
|
29
|
+
// Framework-specific structure props (compared by identity)
|
|
30
|
+
if (prev.visibleCols !== next.visibleCols)
|
|
31
|
+
return false;
|
|
32
|
+
if (prev.columnMeta !== next.columnMeta)
|
|
33
|
+
return false;
|
|
34
|
+
if (prev.cellClassMap !== next.cellClassMap)
|
|
35
|
+
return false;
|
|
36
|
+
if (prev.columnLayouts !== next.columnLayouts)
|
|
37
|
+
return false;
|
|
38
|
+
const ri = prev.rowIndex;
|
|
39
|
+
// Editing cell in this row?
|
|
40
|
+
if (prev.editingRowId !== next.editingRowId) {
|
|
41
|
+
if (prev.editingRowId === prev.rowId || next.editingRowId === next.rowId)
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
// Active cell in this row?
|
|
45
|
+
const prevActive = prev.activeCell?.rowIndex === ri;
|
|
46
|
+
const nextActive = next.activeCell?.rowIndex === ri;
|
|
47
|
+
if (prevActive !== nextActive)
|
|
48
|
+
return false;
|
|
49
|
+
if (prevActive && nextActive && prev.activeCell.columnIndex !== next.activeCell.columnIndex)
|
|
50
|
+
return false;
|
|
51
|
+
// Selection range touches this row?
|
|
52
|
+
const prevInSel = isRowInRange(prev.selectionRange, ri);
|
|
53
|
+
const nextInSel = isRowInRange(next.selectionRange, ri);
|
|
54
|
+
if (prevInSel !== nextInSel)
|
|
55
|
+
return false;
|
|
56
|
+
if (prevInSel && nextInSel) {
|
|
57
|
+
if (prev.selectionRange.startCol !== next.selectionRange.startCol ||
|
|
58
|
+
prev.selectionRange.endCol !== next.selectionRange.endCol)
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
// Fill handle (selection end row) + isDragging
|
|
62
|
+
const prevIsEnd = prev.selectionRange?.endRow === ri;
|
|
63
|
+
const nextIsEnd = next.selectionRange?.endRow === ri;
|
|
64
|
+
if (prevIsEnd !== nextIsEnd)
|
|
65
|
+
return false;
|
|
66
|
+
if ((prevIsEnd || nextIsEnd) && prev.isDragging !== next.isDragging)
|
|
67
|
+
return false;
|
|
68
|
+
// Cut/copy ranges touch this row?
|
|
69
|
+
if (prev.cutRange !== next.cutRange) {
|
|
70
|
+
if (isRowInRange(prev.cutRange, ri) || isRowInRange(next.cutRange, ri))
|
|
71
|
+
return false;
|
|
72
|
+
}
|
|
73
|
+
if (prev.copyRange !== next.copyRange) {
|
|
74
|
+
if (isRowInRange(prev.copyRange, ri) || isRowInRange(next.copyRange, ri))
|
|
75
|
+
return false;
|
|
76
|
+
}
|
|
77
|
+
return true;
|
|
78
|
+
}
|
package/dist/esm/utils/index.js
CHANGED
|
@@ -6,7 +6,7 @@ export { getStatusBarParts } from './statusBarHelpers';
|
|
|
6
6
|
export { getDataGridStatusBarConfig } from './dataGridStatusBar';
|
|
7
7
|
export { getPaginationViewModel, PAGE_SIZE_OPTIONS, MAX_PAGE_BUTTONS, } from './paginationHelpers';
|
|
8
8
|
export { GRID_CONTEXT_MENU_ITEMS, getContextMenuHandlers, formatShortcut } from './gridContextMenuHelpers';
|
|
9
|
-
export { getHeaderFilterConfig, getCellRenderDescriptor, isRowInRange, resolveCellDisplayContent, resolveCellStyle, buildInlineEditorProps, buildPopoverEditorProps, getCellInteractionProps, } from './dataGridViewModel';
|
|
10
9
|
export { parseValue, numberParser, currencyParser, dateParser, emailParser, booleanParser, } from './valueParsers';
|
|
11
10
|
export { computeAggregations } from './aggregationUtils';
|
|
12
11
|
export { processClientSideData } from './clientSideData';
|
|
12
|
+
export { areGridRowPropsEqual, isRowInRange } from './gridRowComparator';
|
|
@@ -10,7 +10,8 @@ export function mergeFilter(prev, key, value) {
|
|
|
10
10
|
const isEmpty = value === undefined ||
|
|
11
11
|
(value.type === 'text' && value.value.trim() === '') ||
|
|
12
12
|
(value.type === 'multiSelect' && value.value.length === 0) ||
|
|
13
|
-
(value.type === 'date' && !value.value.from && !value.value.to)
|
|
13
|
+
(value.type === 'date' && !value.value.from && !value.value.to) ||
|
|
14
|
+
(value.type === 'people' && !value.value);
|
|
14
15
|
if (isEmpty) {
|
|
15
16
|
delete next[key];
|
|
16
17
|
}
|
|
@@ -3,6 +3,12 @@
|
|
|
3
3
|
* UI packages use this and render only presentation.
|
|
4
4
|
*/
|
|
5
5
|
export const PAGE_SIZE_OPTIONS = [10, 25, 50, 100];
|
|
6
|
+
/** Ensures the active pageSize is included in the options list, inserting it in sorted order if missing. */
|
|
7
|
+
function ensurePageSizeInOptions(pageSize, options) {
|
|
8
|
+
if (options.includes(pageSize))
|
|
9
|
+
return options;
|
|
10
|
+
return [...options, pageSize].sort((a, b) => a - b);
|
|
11
|
+
}
|
|
6
12
|
export const MAX_PAGE_BUTTONS = 5;
|
|
7
13
|
/**
|
|
8
14
|
* Returns a view model for pagination UI. Use in Fluent/Material/Radix PaginationControls
|
|
@@ -47,6 +53,6 @@ export function getPaginationViewModel(currentPage, pageSize, totalCount, option
|
|
|
47
53
|
showEndEllipsis,
|
|
48
54
|
startItem,
|
|
49
55
|
endItem,
|
|
50
|
-
pageSizeOptions: options?.pageSizeOptions ?? PAGE_SIZE_OPTIONS,
|
|
56
|
+
pageSizeOptions: ensurePageSizeInOptions(pageSize, options?.pageSizeOptions ?? PAGE_SIZE_OPTIONS),
|
|
51
57
|
};
|
|
52
58
|
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Core OGrid constants — magic numbers centralized for consistency and maintainability.
|
|
3
|
+
*/
|
|
4
|
+
/** Width of the row selection checkbox column in pixels. */
|
|
5
|
+
export declare const CHECKBOX_COLUMN_WIDTH = 48;
|
|
6
|
+
/** Default minimum width for resizable columns in pixels. */
|
|
7
|
+
export declare const DEFAULT_MIN_COLUMN_WIDTH = 80;
|
|
8
|
+
/** Horizontal padding inside cells, used for width calculations. */
|
|
9
|
+
export declare const CELL_PADDING = 16;
|
|
10
|
+
/** Border radius for the grid container in pixels. */
|
|
11
|
+
export declare const GRID_BORDER_RADIUS = 6;
|
package/dist/types/index.d.ts
CHANGED
|
@@ -1,16 +1,3 @@
|
|
|
1
|
-
export
|
|
2
|
-
export
|
|
3
|
-
export
|
|
4
|
-
export type { UseFilterOptionsResult, UseOGridResult, ColumnChooserPlacement, UseActiveCellResult, UseCellEditingResult, EditingCell, UseContextMenuResult, ContextMenuPosition, UseCellSelectionResult, UseCellSelectionParams, UseClipboardResult, UseClipboardParams, UseRowSelectionResult, UseRowSelectionParams, UseKeyboardNavigationResult, UseKeyboardNavigationParams, UseUndoRedoResult, UseUndoRedoParams, UseFillHandleResult, UseFillHandleParams, UseDataGridStateParams, UseDataGridStateResult, DataGridLayoutState, DataGridRowSelectionState, DataGridEditingState, DataGridCellInteractionState, DataGridContextMenuState, DataGridViewModelState, UseColumnHeaderFilterStateParams, UseColumnHeaderFilterStateResult, UseColumnChooserStateParams, UseColumnChooserStateResult, UseInlineCellEditorStateParams, UseInlineCellEditorStateResult, InlineCellEditorType, UseColumnResizeParams, UseColumnResizeResult, UseRichSelectStateParams, UseRichSelectStateResult, UseSideBarStateParams, UseSideBarStateResult, } from './hooks';
|
|
5
|
-
export { OGridLayout } from './components/OGridLayout';
|
|
6
|
-
export type { OGridLayoutProps } from './components/OGridLayout';
|
|
7
|
-
export { StatusBar } from './components/StatusBar';
|
|
8
|
-
export type { StatusBarProps, StatusBarClassNames } from './components/StatusBar';
|
|
9
|
-
export { GridContextMenu } from './components/GridContextMenu';
|
|
10
|
-
export type { GridContextMenuProps, GridContextMenuClassNames } from './components/GridContextMenu';
|
|
11
|
-
export { MarchingAntsOverlay } from './components/MarchingAntsOverlay';
|
|
12
|
-
export type { MarchingAntsOverlayProps } from './components/MarchingAntsOverlay';
|
|
13
|
-
export { SideBar } from './components/SideBar';
|
|
14
|
-
export type { SideBarProps, SideBarFilterColumn } from './components/SideBar';
|
|
15
|
-
export { escapeCsvValue, buildCsvHeader, buildCsvRows, exportToCsv, triggerCsvDownload, getCellValue, flattenColumns, buildHeaderRows, getFilterField, mergeFilter, deriveFilterOptionsFromData, getMultiSelectFilterFields, getStatusBarParts, getDataGridStatusBarConfig, GRID_CONTEXT_MENU_ITEMS, getContextMenuHandlers, formatShortcut, getPaginationViewModel, PAGE_SIZE_OPTIONS, MAX_PAGE_BUTTONS, getHeaderFilterConfig, getCellRenderDescriptor, isRowInRange, resolveCellDisplayContent, resolveCellStyle, buildInlineEditorProps, buildPopoverEditorProps, getCellInteractionProps, parseValue, numberParser, currencyParser, dateParser, emailParser, booleanParser, computeAggregations, processClientSideData, } from './utils';
|
|
16
|
-
export type { CsvColumn, StatusBarPart, StatusBarPartsInput, GridContextMenuItem, GridContextMenuHandlerProps, PaginationViewModel, HeaderFilterConfigInput, HeaderFilterConfig, CellRenderDescriptorInput, CellRenderDescriptor, CellRenderMode, CellInteractionHandlers, ParseValueResult, AggregationResult, } from './utils';
|
|
1
|
+
export * from './types';
|
|
2
|
+
export * from './utils';
|
|
3
|
+
export * from './constants';
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import * as React from 'react';
|
|
2
1
|
export type ColumnFilterType = 'none' | 'text' | 'multiSelect' | 'people' | 'date';
|
|
3
2
|
/** Date range filter value (ISO YYYY-MM-DD strings). Both fields optional for open-ended ranges. */
|
|
4
3
|
export interface IDateFilterValue {
|
|
@@ -40,7 +39,6 @@ export interface IValueParserParams<T = unknown> {
|
|
|
40
39
|
column: IColumnDef<T>;
|
|
41
40
|
}
|
|
42
41
|
export interface IColumnDef<T = unknown> extends IColumnMeta {
|
|
43
|
-
renderCell?: (item: T) => React.ReactNode;
|
|
44
42
|
compare?: (a: T, b: T) => number;
|
|
45
43
|
/** Compute cell value from row data (used for filtering, sorting, display when no renderCell). */
|
|
46
44
|
valueGetter?: (item: T) => unknown;
|
|
@@ -52,12 +50,11 @@ export interface IColumnDef<T = unknown> extends IColumnMeta {
|
|
|
52
50
|
* Return the parsed value to use, or `undefined` to reject (skip) the change.
|
|
53
51
|
*/
|
|
54
52
|
valueParser?: (params: IValueParserParams<T>) => unknown;
|
|
55
|
-
/** Static or per-row cell inline styles. */
|
|
56
|
-
cellStyle?: React.CSSProperties | ((item: T) => React.CSSProperties);
|
|
57
53
|
/** Whether the cell is editable (per-column or per-row). */
|
|
58
54
|
editable?: boolean | ((item: T) => boolean);
|
|
59
|
-
/** Built-in editor type or custom React component.
|
|
60
|
-
|
|
55
|
+
/** Built-in editor type or framework-specific custom editor (e.g. React component).
|
|
56
|
+
* Core utilities never inspect this value — framework packages narrow the type. */
|
|
57
|
+
cellEditor?: unknown;
|
|
61
58
|
/** When true, custom cell editor is rendered in a popover/popper instead of inline. */
|
|
62
59
|
cellEditorPopup?: boolean;
|
|
63
60
|
/** Params passed to the cell editor (e.g. { values: string[] } for select). */
|
|
@@ -67,7 +64,6 @@ export interface IColumnDef<T = unknown> extends IColumnMeta {
|
|
|
67
64
|
export interface ICellValueChangedEvent<T> {
|
|
68
65
|
item: T;
|
|
69
66
|
columnId: string;
|
|
70
|
-
field: string;
|
|
71
67
|
oldValue: unknown;
|
|
72
68
|
newValue: unknown;
|
|
73
69
|
rowIndex: number;
|
|
@@ -82,12 +78,12 @@ export interface ICellEditorProps<T> {
|
|
|
82
78
|
column: IColumnDef<T>;
|
|
83
79
|
cellEditorParams?: CellEditorParams;
|
|
84
80
|
}
|
|
85
|
-
/** Params for built-in cell editors (e.g. select: { values: string[] }).
|
|
81
|
+
/** Params for built-in cell editors (e.g. select: { values: string[] }). */
|
|
86
82
|
export interface CellEditorParams {
|
|
83
|
+
/** Array of allowed values for select/richSelect editors. */
|
|
87
84
|
values?: unknown[];
|
|
88
85
|
/** Format a value for display in rich select editor. */
|
|
89
86
|
formatValue?: (value: unknown) => string;
|
|
90
|
-
[key: string]: unknown;
|
|
91
87
|
}
|
|
92
88
|
/** Column group for multi-row header (has children, no columnId for data). */
|
|
93
89
|
export interface IColumnGroupDef<T = unknown> {
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import type { IColumnDef, IColumnGroupDef, ICellValueChangedEvent, IDateFilterValue } from './columnTypes';
|
|
1
|
+
import type { IDateFilterValue } from './columnTypes';
|
|
3
2
|
/** Row identifier type — grids accept string or number IDs. */
|
|
4
3
|
export type RowId = string | number;
|
|
5
4
|
export interface UserLike {
|
|
@@ -149,136 +148,16 @@ export interface IOGridApi<T> {
|
|
|
149
148
|
selectAll: () => void;
|
|
150
149
|
/** Deselect all rows. */
|
|
151
150
|
deselectAll: () => void;
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
page?: number;
|
|
160
|
-
pageSize?: number;
|
|
161
|
-
sort?: {
|
|
162
|
-
field: string;
|
|
163
|
-
direction: 'asc' | 'desc';
|
|
164
|
-
};
|
|
165
|
-
filters?: IFilters;
|
|
166
|
-
visibleColumns?: Set<string>;
|
|
167
|
-
isLoading?: boolean;
|
|
168
|
-
onPageChange?: (page: number) => void;
|
|
169
|
-
onPageSizeChange?: (size: number) => void;
|
|
170
|
-
onSortChange?: (sort: {
|
|
171
|
-
field: string;
|
|
172
|
-
direction: 'asc' | 'desc';
|
|
151
|
+
/** Clear all filters (shorthand for setFilterModel({})). */
|
|
152
|
+
clearFilters: () => void;
|
|
153
|
+
/** Reset sort to the default (first column, ascending). */
|
|
154
|
+
clearSort: () => void;
|
|
155
|
+
/** Reset all grid state (filters, sort, selection). */
|
|
156
|
+
resetGridState: (options?: {
|
|
157
|
+
keepSelection?: boolean;
|
|
173
158
|
}) => void;
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
/** Called when a column is resized by the user. */
|
|
179
|
-
onColumnResized?: (columnId: string, width: number) => void;
|
|
180
|
-
/** Called when a column is pinned or unpinned. */
|
|
181
|
-
onColumnPinned?: (columnId: string, pinned: 'left' | 'right' | null) => void;
|
|
182
|
-
freezeRows?: number;
|
|
183
|
-
freezeCols?: number;
|
|
184
|
-
editable?: boolean;
|
|
185
|
-
/** Enable spreadsheet-like cell selection (active cell, range, fill handle, clipboard, context menu). Default: true. */
|
|
186
|
-
cellSelection?: boolean;
|
|
187
|
-
onCellValueChanged?: (event: ICellValueChangedEvent<T>) => void;
|
|
188
|
-
onUndo?: () => void;
|
|
189
|
-
onRedo?: () => void;
|
|
190
|
-
canUndo?: boolean;
|
|
191
|
-
canRedo?: boolean;
|
|
192
|
-
rowSelection?: RowSelectionMode;
|
|
193
|
-
selectedRows?: Set<RowId>;
|
|
194
|
-
onSelectionChange?: (event: IRowSelectionChangeEvent<T>) => void;
|
|
195
|
-
statusBar?: boolean | IStatusBarProps;
|
|
196
|
-
defaultPageSize?: number;
|
|
197
|
-
defaultSortBy?: string;
|
|
198
|
-
defaultSortDirection?: 'asc' | 'desc';
|
|
199
|
-
toolbar?: ReactNode;
|
|
200
|
-
/** Secondary toolbar row rendered below the primary toolbar (e.g. active filter chips). */
|
|
201
|
-
toolbarBelow?: ReactNode;
|
|
202
|
-
emptyState?: {
|
|
203
|
-
message?: ReactNode;
|
|
204
|
-
render?: () => ReactNode;
|
|
205
|
-
};
|
|
206
|
-
entityLabelPlural?: string;
|
|
207
|
-
className?: string;
|
|
208
|
-
/** Where the column chooser renders.
|
|
209
|
-
* - `true` or `'toolbar'` (default): column chooser button in the toolbar strip.
|
|
210
|
-
* - `'sidebar'`: column chooser only available via the sidebar columns panel.
|
|
211
|
-
* - `false`: column chooser hidden entirely. */
|
|
212
|
-
columnChooser?: boolean | 'toolbar' | 'sidebar';
|
|
213
|
-
layoutMode?: 'content' | 'fill';
|
|
214
|
-
/** When true, horizontal scrolling is suppressed (overflow-x hidden). */
|
|
215
|
-
suppressHorizontalScroll?: boolean;
|
|
216
|
-
/** Side bar configuration. `true` shows default panels (columns + filters). Pass ISideBarDef for options. */
|
|
217
|
-
sideBar?: boolean | ISideBarDef;
|
|
218
|
-
/** Page size options shown in the pagination dropdown. Default: [10, 20, 50, 100]. */
|
|
219
|
-
pageSizeOptions?: number[];
|
|
220
|
-
/** Fires once when the grid first renders with data (useful for restoring column state). */
|
|
221
|
-
onFirstDataRendered?: () => void;
|
|
222
|
-
/** Called when server-side fetchPage fails. */
|
|
223
|
-
onError?: (error: unknown) => void;
|
|
224
|
-
'aria-label'?: string;
|
|
225
|
-
'aria-labelledby'?: string;
|
|
226
|
-
}
|
|
227
|
-
/** Props passed from useOGrid to the framework-specific DataGridTable. */
|
|
228
|
-
export interface IOGridDataGridProps<T> {
|
|
229
|
-
items: T[];
|
|
230
|
-
columns: (IColumnDef<T> | IColumnGroupDef<T>)[];
|
|
231
|
-
getRowId: (item: T) => RowId;
|
|
232
|
-
sortBy?: string;
|
|
233
|
-
sortDirection: 'asc' | 'desc';
|
|
234
|
-
onColumnSort: (columnKey: string) => void;
|
|
235
|
-
visibleColumns: Set<string>;
|
|
236
|
-
/** Optional column display order (column ids). When set, visible columns are ordered by this array. */
|
|
237
|
-
columnOrder?: string[];
|
|
238
|
-
onColumnOrderChange?: (order: string[]) => void;
|
|
239
|
-
/** Called when a column is resized by the user. */
|
|
240
|
-
onColumnResized?: (columnId: string, width: number) => void;
|
|
241
|
-
/** Called when a column is pinned or unpinned. */
|
|
242
|
-
onColumnPinned?: (columnId: string, pinned: 'left' | 'right' | null) => void;
|
|
243
|
-
/** Runtime pin overrides (from restored state or programmatic changes). */
|
|
244
|
-
pinnedColumns?: Record<string, 'left' | 'right'>;
|
|
245
|
-
/** Initial column width overrides (from restored state). */
|
|
246
|
-
initialColumnWidths?: Record<string, number>;
|
|
247
|
-
/** Number of rows to freeze (sticky), e.g. 1 = header row. */
|
|
248
|
-
freezeRows?: number;
|
|
249
|
-
/** Number of data columns to freeze (sticky left). */
|
|
250
|
-
freezeCols?: number;
|
|
251
|
-
layoutMode?: 'content' | 'fill';
|
|
252
|
-
/** When true, horizontal scrolling is suppressed (overflow-x hidden). */
|
|
253
|
-
suppressHorizontalScroll?: boolean;
|
|
254
|
-
isLoading?: boolean;
|
|
255
|
-
loadingMessage?: string;
|
|
256
|
-
editable?: boolean;
|
|
257
|
-
/** Enable spreadsheet-like cell selection. Default: true. */
|
|
258
|
-
cellSelection?: boolean;
|
|
259
|
-
onCellValueChanged?: (event: ICellValueChangedEvent<T>) => void;
|
|
260
|
-
onUndo?: () => void;
|
|
261
|
-
onRedo?: () => void;
|
|
262
|
-
canUndo?: boolean;
|
|
263
|
-
canRedo?: boolean;
|
|
264
|
-
rowSelection?: RowSelectionMode;
|
|
265
|
-
selectedRows?: Set<RowId>;
|
|
266
|
-
onSelectionChange?: (event: IRowSelectionChangeEvent<T>) => void;
|
|
267
|
-
statusBar?: IStatusBarProps;
|
|
268
|
-
/** Unified filter model (discriminated union values). */
|
|
269
|
-
filters: IFilters;
|
|
270
|
-
/** Single callback for all filter changes. Pass undefined to clear. */
|
|
271
|
-
onFilterChange: (key: string, value: FilterValue | undefined) => void;
|
|
272
|
-
filterOptions: Record<string, string[]>;
|
|
273
|
-
loadingFilterOptions: Record<string, boolean>;
|
|
274
|
-
peopleSearch?: (query: string) => Promise<UserLike[]>;
|
|
275
|
-
getUserByEmail?: (email: string) => Promise<UserLike | undefined>;
|
|
276
|
-
emptyState?: {
|
|
277
|
-
onClearAll: () => void;
|
|
278
|
-
hasActiveFilters: boolean;
|
|
279
|
-
message?: ReactNode;
|
|
280
|
-
render?: () => ReactNode;
|
|
281
|
-
};
|
|
282
|
-
'aria-label'?: string;
|
|
283
|
-
'aria-labelledby'?: string;
|
|
159
|
+
/** Get the currently displayed (paginated) rows. */
|
|
160
|
+
getDisplayedRows: () => T[];
|
|
161
|
+
/** Re-trigger a data fetch (server-side only; no-op for client-side). */
|
|
162
|
+
refreshData: () => void;
|
|
284
163
|
}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export type { ColumnFilterType, IColumnFilterDef, IColumnMeta,
|
|
2
|
-
export type { RowId, UserLike, UserLikeInput, FilterValue, IFilters, IFetchParams, IPageResult, IDataSource, IGridColumnState,
|
|
3
|
-
export { toUserLike, isInSelectionRange, normalizeSelectionRange } from './dataGridTypes';
|
|
1
|
+
export type { ColumnFilterType, IDateFilterValue, IColumnFilterDef, IColumnMeta, IValueParserParams, IColumnDef, ICellValueChangedEvent, ICellEditorProps, CellEditorParams, IColumnGroupDef, HeaderCell, HeaderRow, IColumnDefinition, } from './columnTypes';
|
|
2
|
+
export type { RowId, UserLike, UserLikeInput, FilterValue, IFilters, IFetchParams, IPageResult, IDataSource, IGridColumnState, RowSelectionMode, IRowSelectionChangeEvent, StatusBarPanel, IStatusBarProps, IActiveCell, ISelectionRange, SideBarPanelId, ISideBarDef, IOGridApi, } from './dataGridTypes';
|
|
3
|
+
export { toUserLike, isInSelectionRange, normalizeSelectionRange, } from './dataGridTypes';
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Checks whether a given row index falls within a selection range.
|
|
3
|
+
* O(1) — used by React.memo comparators to skip unchanged rows.
|
|
4
|
+
*/
|
|
5
|
+
export declare function isRowInRange(range: {
|
|
6
|
+
startRow: number;
|
|
7
|
+
endRow: number;
|
|
8
|
+
} | null, rowIndex: number): boolean;
|
|
9
|
+
/**
|
|
10
|
+
* Props for GridRow comparator (generic to work with all 3 UI frameworks).
|
|
11
|
+
* Includes both render props and comparator-only props used to decide re-renders.
|
|
12
|
+
*/
|
|
13
|
+
export interface GridRowComparatorProps {
|
|
14
|
+
item: unknown;
|
|
15
|
+
rowIndex: number;
|
|
16
|
+
rowId: string | number;
|
|
17
|
+
isSelected: boolean;
|
|
18
|
+
hasCheckboxCol: boolean;
|
|
19
|
+
selectionRange: {
|
|
20
|
+
startRow: number;
|
|
21
|
+
endRow: number;
|
|
22
|
+
startCol: number;
|
|
23
|
+
endCol: number;
|
|
24
|
+
} | null;
|
|
25
|
+
activeCell: {
|
|
26
|
+
rowIndex: number;
|
|
27
|
+
columnIndex: number;
|
|
28
|
+
} | null;
|
|
29
|
+
cutRange: {
|
|
30
|
+
startRow: number;
|
|
31
|
+
endRow: number;
|
|
32
|
+
startCol: number;
|
|
33
|
+
endCol: number;
|
|
34
|
+
} | null;
|
|
35
|
+
copyRange: {
|
|
36
|
+
startRow: number;
|
|
37
|
+
endRow: number;
|
|
38
|
+
startCol: number;
|
|
39
|
+
endCol: number;
|
|
40
|
+
} | null;
|
|
41
|
+
isDragging: boolean;
|
|
42
|
+
editingRowId: string | number | null;
|
|
43
|
+
visibleCols?: unknown;
|
|
44
|
+
columnMeta?: unknown;
|
|
45
|
+
cellClassMap?: unknown;
|
|
46
|
+
columnLayouts?: unknown;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Shared React.memo comparator for GridRow components across all 3 UI packages.
|
|
50
|
+
* Skips re-render for rows unaffected by selection/editing/interaction changes.
|
|
51
|
+
*
|
|
52
|
+
* Used by:
|
|
53
|
+
* - packages/radix/src/DataGridTable/DataGridTable.tsx
|
|
54
|
+
* - packages/fluent/src/DataGridTable/DataGridTable.tsx
|
|
55
|
+
* - packages/material/src/DataGridTable/DataGridTable.tsx
|
|
56
|
+
*/
|
|
57
|
+
export declare function areGridRowPropsEqual(prev: GridRowComparatorProps, next: GridRowComparatorProps): boolean;
|