@opencorestack/opengridx 2.1.0 → 3.0.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 +399 -0
- package/README.md +95 -72
- package/dist/index.d.ts +590 -131
- package/dist/opengridx.css +1 -1
- package/dist/opengridx.es.js +7891 -5631
- package/dist/opengridx.es.js.map +1 -1
- package/dist/opengridx.umd.js +17 -23
- package/dist/opengridx.umd.js.map +1 -1
- package/docs/API_REFERENCE.md +348 -166
- package/docs/README.md +6 -1
- package/docs/architecture/datagrid-orchestration.md +81 -0
- package/docs/architecture/grid-row-meta.md +102 -0
- package/docs/architecture/use-grid-columns.md +128 -0
- package/docs/architecture/use-grid-controlled-state.md +157 -0
- package/docs/architecture/use-grid-row-pipeline.md +120 -0
- package/docs/architecture/use-grid-scroll-sync.md +111 -0
- package/docs/architecture/use-grid-state-snapshot.md +90 -0
- package/docs/architecture/use-grid-virtualization.md +115 -0
- package/docs/architecture/use-grid-visible-rows.md +85 -0
- package/docs/assets/banner-alt.png +0 -0
- package/docs/assets/banner.png +0 -0
- package/docs/assets/hero.png +0 -0
- package/docs/assets/logo.png +0 -0
- package/docs/components/aggregation-footer.md +15 -10
- package/docs/components/cell.md +16 -6
- package/docs/components/column-group-header.md +55 -28
- package/docs/components/column-resize.md +17 -13
- package/docs/components/column-visibility.md +6 -7
- package/docs/components/datagrid.md +15 -13
- package/docs/components/empty-state.md +2 -1
- package/docs/components/error-overlay.md +12 -7
- package/docs/components/filter-panel.md +28 -8
- package/docs/components/header.md +28 -9
- package/docs/components/pagination.md +14 -8
- package/docs/components/row.md +21 -5
- package/docs/components/toolbar.md +27 -9
- package/docs/components/tooltip.md +16 -7
- package/docs/customization/slots-api.md +90 -38
- package/docs/customization/theming.md +66 -18
- package/docs/features/aggregation-pivot.md +75 -9
- package/docs/features/cell-spanning.md +80 -125
- package/docs/features/clipboard.md +41 -16
- package/docs/features/custom-pagination.md +38 -18
- package/docs/features/data-source.md +36 -13
- package/docs/features/editing-reordering.md +125 -16
- package/docs/features/export-guide.md +126 -39
- package/docs/features/filtering.md +44 -6
- package/docs/features/infinite-scroll.md +54 -46
- package/docs/features/keyboard-navigation.md +83 -0
- package/docs/features/list-view.md +58 -5
- package/docs/features/loading-states.md +18 -20
- package/docs/features/master-detail.md +18 -5
- package/docs/features/pdf-export.md +72 -19
- package/docs/features/pinning.md +24 -5
- package/docs/features/selection.md +50 -10
- package/docs/features/sorting-pagination.md +64 -14
- package/docs/features/state-persistence.md +78 -10
- package/docs/features/toolbar-customization.md +26 -17
- package/docs/features/tree-data-grouping.md +36 -8
- package/docs/features/virtualization.md +37 -14
- package/docs/migration/v1-to-v2.md +115 -0
- package/docs/migration/v2-to-v3.md +1233 -0
- package/docs/performance.md +124 -0
- package/docs/roadmap.md +5 -5
- package/lib/components/Cell/Cell.css +17 -1
- package/lib/components/Cell/Cell.tsx +129 -57
- package/lib/components/Cell/CellErrorBoundary.tsx +12 -6
- package/lib/components/Cell/GridEditInputCell.tsx +114 -41
- package/lib/components/ColumnGroupHeader/ColumnGroupHeader.css +7 -1
- package/lib/components/ColumnResizeHandle/ColumnResizeHandle.css +15 -2
- package/lib/components/ColumnResizeHandle/ColumnResizeHandle.tsx +104 -38
- package/lib/components/ColumnResizeHandle/clampResizeWidth.ts +13 -0
- package/lib/components/ColumnVisibilityPanel/ColumnVisibilityPanel.css +6 -0
- package/lib/components/ColumnVisibilityPanel/ColumnVisibilityPanel.tsx +21 -13
- package/lib/components/DataGrid/DataGrid.css +85 -12
- package/lib/components/DataGrid/DataGrid.tsx +582 -876
- package/lib/components/DataGrid/GridAggregationFooter.tsx +53 -17
- package/lib/components/DataGrid/GridErrorOverlay.tsx +15 -12
- package/lib/components/DataGrid/GridListView.tsx +165 -24
- package/lib/components/DataGrid/GridLiveRegion.tsx +31 -0
- package/lib/components/DataGrid/GridLoadingOverlay.tsx +27 -0
- package/lib/components/DataGrid/GridPaginationArea.tsx +44 -0
- package/lib/components/DataGrid/GridPinnedRows.tsx +62 -11
- package/lib/components/DataGrid/GridStandaloneColumnPanel.tsx +40 -38
- package/lib/components/DataGrid/GridToolbarSlot.tsx +51 -0
- package/lib/components/DataGrid/GridVirtualRows.tsx +40 -24
- package/lib/components/DetailPanel/DetailPanel.css +0 -6
- package/lib/components/DetailPanel/DetailPanel.tsx +34 -5
- package/lib/components/DetailPanel/DetailPanelContent.tsx +17 -0
- package/lib/components/FilterPanel/FilterPanel.css +13 -2
- package/lib/components/FilterPanel/FilterPanel.tsx +272 -99
- package/lib/components/Header/ColumnMenu.tsx +91 -39
- package/lib/components/Header/Header.css +6 -3
- package/lib/components/Header/Header.tsx +181 -160
- package/lib/components/ListView/ListView.css +25 -0
- package/lib/components/ListView/ListViewRow.tsx +76 -10
- package/lib/components/Pagination/Pagination.tsx +15 -7
- package/lib/components/Row/Row.tsx +177 -77
- package/lib/components/Toolbar/GlobalSearch.css +6 -3
- package/lib/components/Toolbar/GlobalSearch.tsx +26 -27
- package/lib/components/Toolbar/GridToolbar.css +12 -11
- package/lib/components/Toolbar/GridToolbar.tsx +133 -89
- package/lib/components/Toolbar/PivotPanel.tsx +89 -49
- package/lib/components/Tooltip/Tooltip.css +20 -0
- package/lib/components/Tooltip/Tooltip.tsx +68 -35
- package/lib/components/ui/Button.tsx +3 -0
- package/lib/components/ui/Checkbox.tsx +2 -1
- package/lib/components/ui/ExpandIcon.css +0 -9
- package/lib/components/ui/ExpandIcon.tsx +4 -1
- package/lib/hooks/core/gridApiPlaceholder.ts +34 -0
- package/lib/hooks/core/gridToolbarHostContext.ts +14 -0
- package/lib/hooks/core/useDataGrid.ts +115 -173
- package/lib/hooks/core/useGridAggregationApi.ts +72 -0
- package/lib/hooks/core/useGridApiMethods.ts +115 -0
- package/lib/hooks/core/useGridApiRef.ts +19 -4
- package/lib/hooks/core/useGridApiRefBinding.ts +18 -0
- package/lib/hooks/core/useGridAriaRows.ts +38 -0
- package/lib/hooks/core/useGridClipboardApi.ts +38 -0
- package/lib/hooks/core/useGridColumnLookup.ts +30 -0
- package/lib/hooks/core/useGridColumns.tsx +279 -76
- package/lib/hooks/core/useGridColumnsPanel.ts +77 -0
- package/lib/hooks/core/useGridControlledState.ts +99 -6
- package/lib/hooks/core/useGridDetailPanel.ts +51 -0
- package/lib/hooks/core/useGridDevWarnings.ts +104 -5
- package/lib/hooks/core/useGridFocusHandlers.ts +50 -0
- package/lib/hooks/core/useGridGroupingColumn.ts +62 -0
- package/lib/hooks/core/useGridHeaderHandlers.ts +92 -0
- package/lib/hooks/core/useGridHierarchy.ts +81 -0
- package/lib/hooks/core/useGridKeyboardNavigation.ts +447 -243
- package/lib/hooks/core/useGridLiveRowSelection.ts +67 -0
- package/lib/hooks/core/useGridPageCorrection.ts +55 -0
- package/lib/hooks/core/useGridRowIdOf.ts +32 -0
- package/lib/hooks/core/useGridRowInteractions.ts +81 -0
- package/lib/hooks/core/useGridRowPipeline.ts +69 -39
- package/lib/hooks/core/useGridRowSelection.ts +91 -0
- package/lib/hooks/core/useGridScrollSync.ts +80 -32
- package/lib/hooks/core/useGridScrollToIndexesApi.ts +45 -0
- package/lib/hooks/core/useGridStateSnapshot.ts +40 -5
- package/lib/hooks/core/useGridThemeDimensions.ts +49 -0
- package/lib/hooks/core/useGridToolbarProps.ts +97 -0
- package/lib/hooks/core/useGridViewport.ts +38 -0
- package/lib/hooks/core/useGridViewportSize.ts +26 -0
- package/lib/hooks/core/useGridVisibleRows.ts +17 -5
- package/lib/hooks/core/useLayout.ts +215 -195
- package/lib/hooks/core/useRowsScrollEndLatch.ts +41 -0
- package/lib/hooks/core/useStableColumns.ts +34 -0
- package/lib/hooks/features/useAggregation.ts +82 -51
- package/lib/hooks/features/useColumnGroupReorderGuard.ts +34 -0
- package/lib/hooks/features/useDetailPanelHeights.ts +29 -0
- package/lib/hooks/features/useGridClipboard.ts +133 -73
- package/lib/hooks/features/useGridDataSource.ts +354 -128
- package/lib/hooks/features/useGridEditing.ts +211 -51
- package/lib/hooks/features/useGridPivot.ts +92 -0
- package/lib/hooks/features/useGridSpanRenderWindow.ts +104 -0
- package/lib/hooks/features/useGridSpanning.ts +410 -306
- package/lib/hooks/features/usePivot.ts +0 -0
- package/lib/hooks/features/useServerTreeChildren.ts +50 -0
- package/lib/hooks/useColumnReorder.ts +38 -44
- package/lib/hooks/useRowGrouping.ts +298 -160
- package/lib/hooks/useRowReorder.ts +62 -31
- package/lib/hooks/useTreeData.ts +287 -220
- package/lib/index.ts +28 -2
- package/lib/state/index.ts +1 -0
- package/lib/state/types.ts +7 -1
- package/lib/state/useGridStateStorage.ts +88 -29
- package/lib/theme/DataGridThemeProvider.tsx +143 -38
- package/lib/theme/gridThemeContext.ts +5 -0
- package/lib/theme/index.ts +5 -0
- package/lib/theme/presets.ts +5 -4
- package/lib/theme/types.ts +40 -2
- package/lib/types/index.ts +313 -116
- package/lib/utils/aggregation/index.ts +119 -24
- package/lib/utils/aria/index.ts +40 -0
- package/lib/utils/columnGroups.ts +110 -0
- package/lib/utils/columnLookup.ts +33 -0
- package/lib/utils/columnWidth.ts +22 -0
- package/lib/utils/dataSource/index.ts +46 -0
- package/lib/utils/detailPanel.ts +66 -0
- package/lib/utils/dragData.ts +44 -0
- package/lib/utils/editing/index.ts +30 -0
- package/lib/utils/export/exportShared.ts +197 -0
- package/lib/utils/export/exportToExcelAdvanced.ts +278 -139
- package/lib/utils/export/exportToPdf.ts +143 -92
- package/lib/utils/export/groupLabel.ts +14 -0
- package/lib/utils/export/index.ts +278 -272
- package/lib/utils/export/pdfText.ts +50 -0
- package/lib/utils/filtering/index.ts +277 -148
- package/lib/utils/focus/index.ts +115 -0
- package/lib/utils/gridApiRows.ts +35 -0
- package/lib/utils/gridModes.ts +77 -0
- package/lib/utils/grouping/groupedExportRows.ts +91 -0
- package/lib/utils/pagination/index.ts +56 -0
- package/lib/utils/pinning/index.ts +77 -33
- package/lib/utils/pivot/index.ts +368 -0
- package/lib/utils/scroll/index.ts +55 -5
- package/lib/utils/scroll/scrollEnd.ts +18 -0
- package/lib/utils/sorting/index.ts +154 -30
- package/lib/utils/syntheticRows.ts +12 -0
- package/lib/utils/values.ts +206 -0
- package/lib/utils/viewport.ts +14 -0
- package/llms.txt +261 -164
- package/package.json +13 -5
- package/lib/components/ColumnGroupHeader/ColumnGroupHeader.tsx +0 -220
- package/lib/components/ColumnVisibilityPanel/ColumnVisibilityPanel.test.tsx +0 -85
- package/lib/components/DataGrid/DataGrid.defects.test.tsx +0 -103
- package/lib/components/DataGrid/DataGrid.regression.test.tsx +0 -132
- package/lib/components/DataGrid/DataGrid.smoke.test.tsx +0 -58
- package/lib/components/DataGrid/DataGrid.v2.test.tsx +0 -127
- package/lib/components/DataGrid/DataGrid.virtualization.browser.test.tsx +0 -183
- package/lib/components/Header/Header.test.tsx +0 -135
- package/lib/components/Pagination/Pagination.test.tsx +0 -64
- package/lib/hooks/core/useDataGrid.regression.test.ts +0 -66
- package/lib/hooks/core/useGridControlledState.test.ts +0 -86
- package/lib/hooks/core/useGridKeyboardNavigation.test.ts +0 -176
- package/lib/hooks/core/useGridRowPipeline.test.ts +0 -117
- package/lib/hooks/features/useAggregation.test.ts +0 -175
- package/lib/hooks/features/usePivot.test.ts +0 -202
- package/lib/hooks/useRowGrouping.test.ts +0 -298
- package/lib/hooks/useTreeData.test.ts +0 -83
- package/lib/utils/aggregation/aggregation.test.ts +0 -16
- package/lib/utils/export/exportToExcelAdvanced.test.ts +0 -109
- package/lib/utils/export/exportToPdf.test.ts +0 -120
- package/lib/utils/export/groupedExport.test.ts +0 -144
- package/lib/utils/filtering/filtering.test.ts +0 -178
- package/lib/utils/sorting/sorting.test.ts +0 -88
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { GridRowId, GridRowMeta } from '../types';
|
|
2
|
+
import { PIVOT_GRAND_TOTAL_ID } from './pivot';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* True for a row the grid made rather than one of the consumer's rows: a row-grouping header or
|
|
6
|
+
* subtotal, an auto-created tree-data parent (`rowMeta.isGroupRow`), or the pivot Grand Total.
|
|
7
|
+
* Such rows are never selected (checkbox, click, keyboard, `apiRef`) and never start or carry a
|
|
8
|
+
* row span.
|
|
9
|
+
*/
|
|
10
|
+
export function isSyntheticRowId(id: GridRowId, rowMetaMap?: ReadonlyMap<GridRowId, GridRowMeta>): boolean {
|
|
11
|
+
return id === PIVOT_GRAND_TOTAL_ID || rowMetaMap?.get(id)?.isGroupRow === true;
|
|
12
|
+
}
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
import type { GridColDef, GridRowModel } from '../types';
|
|
2
|
+
|
|
3
|
+
const warnedCallbacks = new Set<string>();
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Warn once per key (callback kind and column), in development only, that a consumer callback
|
|
7
|
+
* threw and the grid fell back to a default instead of crashing.
|
|
8
|
+
*/
|
|
9
|
+
export function warnCallbackFailure(key: string, message: string, error: unknown): void {
|
|
10
|
+
if (process.env.NODE_ENV === 'production' || warnedCallbacks.has(key)) return;
|
|
11
|
+
warnedCallbacks.add(key);
|
|
12
|
+
console.warn(`[OpenGridX] ${message}.`, error);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Call a consumer callback; when it throws, warn once per `key` (dev only) and return `fallback`.
|
|
17
|
+
* Used for callbacks whose failure has an obvious default (a detail-panel height, a group label).
|
|
18
|
+
*/
|
|
19
|
+
export function callSafely<T>(fn: () => T, fallback: T, key: string, message: string): T {
|
|
20
|
+
try {
|
|
21
|
+
return fn();
|
|
22
|
+
} catch (error) {
|
|
23
|
+
warnCallbackFailure(key, message, error);
|
|
24
|
+
return fallback;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
type ValueGetterColumn<R extends GridRowModel> = Pick<GridColDef<R>, 'valueGetter'>;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Read a cell through the column's `valueGetter` (or `row[field]`), returning a throw as `error`
|
|
32
|
+
* instead of swallowing it. The row renderer uses this to show the error in that one cell.
|
|
33
|
+
*/
|
|
34
|
+
export function tryGetCellValue<R extends GridRowModel>(
|
|
35
|
+
row: R,
|
|
36
|
+
field: string,
|
|
37
|
+
colDef?: ValueGetterColumn<R>
|
|
38
|
+
): { value: unknown; error?: unknown } {
|
|
39
|
+
const raw = row[field];
|
|
40
|
+
if (!colDef?.valueGetter) return { value: raw };
|
|
41
|
+
try {
|
|
42
|
+
return { value: colDef.valueGetter({ row, field, value: raw }) };
|
|
43
|
+
} catch (error) {
|
|
44
|
+
return { value: undefined, error: error ?? new Error('valueGetter failed') };
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* The value a cell holds before formatting: the column's `valueGetter` result when it has one,
|
|
50
|
+
* otherwise `row[field]`. Filtering, sorting, the quick filter, aggregation, row grouping, pivot,
|
|
51
|
+
* list view, clipboard and editing all read cells through this, so a computed column behaves the
|
|
52
|
+
* same everywhere. A `valueGetter` that throws reads as `undefined` (with a one-time dev warning
|
|
53
|
+
* per column), so one bad record cannot take the whole grid down.
|
|
54
|
+
*/
|
|
55
|
+
export function getCellValue<R extends GridRowModel>(row: R, field: string, colDef?: ValueGetterColumn<R>): unknown {
|
|
56
|
+
const { value, error } = tryGetCellValue(row, field, colDef);
|
|
57
|
+
if (error !== undefined) {
|
|
58
|
+
warnCallbackFailure(`valueGetter:${field}`, `valueGetter for column "${field}" threw; the value is read as undefined`, error);
|
|
59
|
+
}
|
|
60
|
+
return value;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* The column's `valueFormatter` text for `value`, or undefined when the column has no formatter
|
|
65
|
+
* or the formatter throws (with a one-time dev warning per column).
|
|
66
|
+
*/
|
|
67
|
+
export function getFormattedValue<R extends GridRowModel>(
|
|
68
|
+
row: R,
|
|
69
|
+
field: string,
|
|
70
|
+
value: unknown,
|
|
71
|
+
colDef?: Pick<GridColDef<R>, 'valueFormatter'>
|
|
72
|
+
): string | undefined {
|
|
73
|
+
const formatter = colDef?.valueFormatter;
|
|
74
|
+
if (!formatter) return undefined;
|
|
75
|
+
return callSafely(
|
|
76
|
+
() => formatter({ value, row, field }),
|
|
77
|
+
undefined,
|
|
78
|
+
`valueFormatter:${field}`,
|
|
79
|
+
`valueFormatter for column "${field}" threw; the value is shown unformatted`,
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Coerce a cell or filter value to a number. Returns null for null/undefined, blank strings,
|
|
85
|
+
* NaN and anything that does not parse, so an empty cell is never read as 0.
|
|
86
|
+
*/
|
|
87
|
+
export function toNumber(value: unknown): number | null {
|
|
88
|
+
if (value == null) return null;
|
|
89
|
+
if (typeof value === 'number') return Number.isNaN(value) ? null : value;
|
|
90
|
+
if (typeof value === 'string') {
|
|
91
|
+
const trimmed = value.trim();
|
|
92
|
+
if (trimmed === '') return null;
|
|
93
|
+
const n = Number(trimmed);
|
|
94
|
+
return Number.isNaN(n) ? null : n;
|
|
95
|
+
}
|
|
96
|
+
if (typeof value === 'boolean') return null;
|
|
97
|
+
if (value instanceof Date) {
|
|
98
|
+
const t = value.getTime();
|
|
99
|
+
return Number.isNaN(t) ? null : t;
|
|
100
|
+
}
|
|
101
|
+
if (typeof value === 'bigint') return Number(value);
|
|
102
|
+
return null;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const DATE_ONLY = /^(\d{4})-(\d{2})-(\d{2})$/;
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Parse a Date object, epoch milliseconds or a date string. A plain `YYYY-MM-DD` string is read
|
|
109
|
+
* as a local calendar date: `new Date('2024-01-10')` is UTC midnight, which is the previous day
|
|
110
|
+
* west of UTC. Returns null for empty, unparsable or invalid values.
|
|
111
|
+
*/
|
|
112
|
+
export function toDate(value: unknown): Date | null {
|
|
113
|
+
if (value == null) return null;
|
|
114
|
+
let date: Date;
|
|
115
|
+
if (value instanceof Date) {
|
|
116
|
+
date = value;
|
|
117
|
+
} else if (typeof value === 'number') {
|
|
118
|
+
date = new Date(value);
|
|
119
|
+
} else if (typeof value === 'string') {
|
|
120
|
+
const trimmed = value.trim();
|
|
121
|
+
if (trimmed === '') return null;
|
|
122
|
+
const match = DATE_ONLY.exec(trimmed);
|
|
123
|
+
if (match) {
|
|
124
|
+
const year = Number(match[1]);
|
|
125
|
+
const month = Number(match[2]) - 1;
|
|
126
|
+
const day = Number(match[3]);
|
|
127
|
+
date = new Date(year, month, day);
|
|
128
|
+
// Reject roll-overs such as 2024-02-31.
|
|
129
|
+
if (date.getMonth() !== month || date.getDate() !== day) return null;
|
|
130
|
+
} else {
|
|
131
|
+
date = new Date(trimmed);
|
|
132
|
+
}
|
|
133
|
+
} else {
|
|
134
|
+
return null;
|
|
135
|
+
}
|
|
136
|
+
return Number.isNaN(date.getTime()) ? null : date;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/** A sortable integer for the local calendar day of a date (YYYYMMDD), or null when unparsable. */
|
|
140
|
+
export function toLocalDayKey(value: unknown): number | null {
|
|
141
|
+
const date = toDate(value);
|
|
142
|
+
if (!date) return null;
|
|
143
|
+
return date.getFullYear() * 10000 + (date.getMonth() + 1) * 100 + date.getDate();
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/** The local calendar day of a date as `YYYY-MM-DD`, or '' when unparsable. */
|
|
147
|
+
export function toLocalDateString(value: unknown): string {
|
|
148
|
+
const date = toDate(value);
|
|
149
|
+
if (!date) return '';
|
|
150
|
+
const y = String(date.getFullYear()).padStart(4, '0');
|
|
151
|
+
const m = String(date.getMonth() + 1).padStart(2, '0');
|
|
152
|
+
const d = String(date.getDate()).padStart(2, '0');
|
|
153
|
+
return `${y}-${m}-${d}`;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
let localDateFormat: Intl.DateTimeFormat | null = null;
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* `date.toLocaleDateString()` (the default locale's numeric date), with the formatter created
|
|
160
|
+
* once: building a new one per call is several times slower on large grids.
|
|
161
|
+
*/
|
|
162
|
+
export function formatLocalDate(date: Date): string {
|
|
163
|
+
localDateFormat ??= new Intl.DateTimeFormat();
|
|
164
|
+
return localDateFormat.format(date);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
type ValueOption = string | number | { value: unknown; label: string };
|
|
168
|
+
|
|
169
|
+
/** The label of the `valueOptions` entry for `value`, or undefined when no option matches. */
|
|
170
|
+
export function getValueOptionLabel(options: readonly ValueOption[] | undefined, value: unknown): string | undefined {
|
|
171
|
+
if (!options) return undefined;
|
|
172
|
+
const optionValue = (option: ValueOption): unknown => (typeof option === 'object' ? option.value : option);
|
|
173
|
+
const optionLabel = (option: ValueOption): string => (typeof option === 'object' ? option.label : String(option));
|
|
174
|
+
// Exact match first; then by text, so a numeric option still labels a value read back as a string.
|
|
175
|
+
const match = options.find(option => Object.is(optionValue(option), value))
|
|
176
|
+
?? options.find(option => String(optionValue(option)) === String(value));
|
|
177
|
+
return match === undefined ? undefined : optionLabel(match);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Default display text for a cell whose column has no `valueFormatter`, by column `type`:
|
|
182
|
+
* - `date`: the local calendar date (`toLocaleDateString()`) of a Date, timestamp or date string;
|
|
183
|
+
* text that does not parse as a date is shown as is.
|
|
184
|
+
* - `boolean`: `Yes` / `No` for `true` / `false`.
|
|
185
|
+
* - `singleSelect`: the matching `valueOptions` label; a value with no option is shown as is.
|
|
186
|
+
* - anything else (and `image`, whose cell renders an image of this URL): `String(value)`.
|
|
187
|
+
* null and undefined are ''. The grid cells and the text exporters (CSV, print, PDF) use this, so
|
|
188
|
+
* a file shows what the grid shows.
|
|
189
|
+
*/
|
|
190
|
+
export function formatValueByType(value: unknown, colDef: Pick<GridColDef, 'type' | 'valueOptions'> | undefined): string {
|
|
191
|
+
if (value === null || value === undefined) return '';
|
|
192
|
+
switch (colDef?.type) {
|
|
193
|
+
case 'date': {
|
|
194
|
+
const date = toDate(value);
|
|
195
|
+
return date ? formatLocalDate(date) : String(value);
|
|
196
|
+
}
|
|
197
|
+
case 'boolean':
|
|
198
|
+
if (value === true) return 'Yes';
|
|
199
|
+
if (value === false) return 'No';
|
|
200
|
+
return String(value);
|
|
201
|
+
case 'singleSelect':
|
|
202
|
+
return getValueOptionLabel(colDef.valueOptions, value) ?? String(value);
|
|
203
|
+
default:
|
|
204
|
+
return String(value);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Size of the layout viewport that `position: fixed` offsets are measured from. Unlike
|
|
3
|
+
* `window.innerWidth` / `innerHeight` it excludes a classic (non-overlay) page scrollbar, so a
|
|
4
|
+
* panel placed with `right: getViewportWidth() - anchorRect.right` lines up with its anchor on
|
|
5
|
+
* Windows and Linux desktops too. Falls back to the window size where the document element
|
|
6
|
+
* reports no size (jsdom).
|
|
7
|
+
*/
|
|
8
|
+
export function getViewportWidth(): number {
|
|
9
|
+
return document.documentElement.clientWidth || window.innerWidth;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function getViewportHeight(): number {
|
|
13
|
+
return document.documentElement.clientHeight || window.innerHeight;
|
|
14
|
+
}
|