@opencorestack/opengridx 3.8.0 → 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 +23 -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 +5210 -5199
- 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/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/GridVirtualRows.tsx +2 -1
- 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/features/useGridSpanning.ts +1 -1
- 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/utils/cellSelection.ts +0 -2
- package/lib/utils/editing/index.ts +1 -1
- package/lib/utils/filtering/filterInput.ts +45 -0
- package/lib/utils/filtering/index.ts +0 -28
- package/lib/utils/pinning/index.ts +0 -107
- 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
package/docs/API_REFERENCE.md
CHANGED
|
@@ -546,6 +546,21 @@ In every exporter, a non-empty `selectedRows` takes precedence over `groupedRows
|
|
|
546
546
|
|
|
547
547
|
Every exporter takes `columns` typed for your rows (`GridColDef<R>[]`) or untyped (`GridColDef[]`), and its options also accept `getRowId?: (row: R) => GridRowId`. Pass the grid's `getRowId` when you use one: `selectedRows` holds those ids, and since v3.0 the grid no longer copies them onto `row.id`.
|
|
548
548
|
|
|
549
|
+
### Export option types
|
|
550
|
+
|
|
551
|
+
Each exporter's options are exported as a type. Fields common to all of them: `fileName`, `selectedRows`, `getRowId`, `aggregationResult`, `aggregationModel`, `groupedRows`.
|
|
552
|
+
|
|
553
|
+
| Type | For | Its own fields |
|
|
554
|
+
| :--- | :--- | :--- |
|
|
555
|
+
| `CsvExportOptions<R>` | `exportToCsv` | `includeHeaders`, `delimiter`, `escapeFormulas`, `bom` |
|
|
556
|
+
| `JsonExportOptions<R>` | `exportToJson` | `pretty` |
|
|
557
|
+
| `ExcelExportOptions<R>` | `exportToExcel` (HTML `.xls`) | `sheetName`, `includeHeaders`, `escapeFormulas` |
|
|
558
|
+
| `ExcelAdvancedExportOptions<R>` | `exportToExcelAdvanced` (`.xlsx`) | `sheets`, `columnStyles`, `headerFillColor`, `headerTextColor`, `headerFontSize`, `bodyFontSize` |
|
|
559
|
+
| `ExcelSheetDefinition` | one entry of `sheets` | `name`, `rows` (`'all'` / `'selected'`), `includeHeaders`, `includeSummary`, `autoFilter`, `frozenHeader`, `alternateRowColor`; `{ type: 'summary', name? }` adds a totals sheet |
|
|
560
|
+
| `ExcelColumnStyle` | a value of `columnStyles` (by field) | `numFmt` (e.g. `'$#,##0.00'`), `width`, `alignment`, `embedImage`, `imageWidth`, `imageHeight` |
|
|
561
|
+
|
|
562
|
+
`PdfExportOptions` and `PrintOptions` are described in [PDF Export](features/pdf-export.md) and the [Export Guide](features/export-guide.md).
|
|
563
|
+
|
|
549
564
|
---
|
|
550
565
|
|
|
551
566
|
## 🤖 AI Toolkit (`@opencorestack/opengridx/ai`)
|
|
@@ -652,6 +667,10 @@ Context provider for overriding the grid's visual system.
|
|
|
652
667
|
- **`overlays`**: colours (`background`, `text`, `border`, `shadow`, `item*`) and, v3.7, `fontFamily`, `fontWeight`, `fontSize` for menus and panels.
|
|
653
668
|
- **`toolbar`**, **`scrollbar`** (`thumbColor`, `trackColor`, `size`), **`skeleton`** (`baseColor`, `highlightColor`), **`transitions`**.
|
|
654
669
|
|
|
670
|
+
#### Theme types
|
|
671
|
+
|
|
672
|
+
`DataGridThemeProviderProps`: `theme: GridTheme`, `children`, `className?`, `style?` (both on the wrapper element). Each key of `GridTheme` has its own exported type, for building a theme in parts: `GridThemeColors` (with `GridThemeGrayScale` for `gray`), `GridThemeTypography`, `GridThemeSpacing`, `GridThemeBorders`, `GridThemeShadows`, `GridThemeGrid`, `GridThemeOverlays`, `GridThemeToolbar`, `GridThemeScrollbar`, `GridThemeSkeleton`, `GridThemeTransitions`. Every field is optional.
|
|
673
|
+
|
|
655
674
|
Presets: `darkTheme`, `roseTheme`, `emeraldTheme`, `amberTheme`, `compactTheme`. See [Theming](customization/theming.md).
|
|
656
675
|
|
|
657
676
|
---
|
|
@@ -836,6 +855,15 @@ If the key can change while the grid stays mounted, remount the grid with it (`<
|
|
|
836
855
|
|
|
837
856
|
## ⚙️ Interfaces & Types
|
|
838
857
|
|
|
858
|
+
### Small shared types
|
|
859
|
+
|
|
860
|
+
| Type | Definition | Used by |
|
|
861
|
+
| :--- | :--- | :--- |
|
|
862
|
+
| `GridAlignment` | `'left' \| 'center' \| 'right'` | `GridColDef.align`, `headerAlign` |
|
|
863
|
+
| `GridSortDirection` | `'asc' \| 'desc' \| null` | a column's sort state (`null`: unsorted) |
|
|
864
|
+
| `GridSortModel` | `{ field: string; sort: GridSortDirection }`: like `GridSortItem`, but `sort` may be `null` | a single column's sort state |
|
|
865
|
+
| `GridPinnedPosition` | `'left' \| 'right'` (logical: inline start / end in RTL) | column pinning |
|
|
866
|
+
|
|
839
867
|
### `GridSortItem`
|
|
840
868
|
```typescript
|
|
841
869
|
interface GridSortItem {
|
|
@@ -71,7 +71,7 @@
|
|
|
71
71
|
.ogx-ai-panel__suggestion {
|
|
72
72
|
padding: 3px 10px;
|
|
73
73
|
border: 1px solid var(--ogx-border-color, var(--ogx-grid-border-color));
|
|
74
|
-
border-radius:
|
|
74
|
+
border-radius: 999px;
|
|
75
75
|
background: var(--ogx-grid-header-background);
|
|
76
76
|
color: inherit;
|
|
77
77
|
font: inherit;
|
|
@@ -119,7 +119,7 @@
|
|
|
119
119
|
align-items: center;
|
|
120
120
|
gap: 4px;
|
|
121
121
|
padding: 2px 4px 2px 10px;
|
|
122
|
-
border-radius:
|
|
122
|
+
border-radius: 999px;
|
|
123
123
|
background: var(--ogx-color-primary-light, #e0e7ff);
|
|
124
124
|
color: var(--ogx-color-primary-dark, #1e3a8a);
|
|
125
125
|
}
|
|
@@ -27,9 +27,7 @@
|
|
|
27
27
|
outline: 2px solid var(--ogx-grid-cell-focus-border, var(--ogx-color-primary, #2563eb));
|
|
28
28
|
outline-offset: -2px;
|
|
29
29
|
}
|
|
30
|
-
|
|
31
|
-
cursor: text;
|
|
32
|
-
}
|
|
30
|
+
|
|
33
31
|
/*
|
|
34
32
|
* Cell range selection (cellSelection, v3.3). The tint is a background image, so it also covers
|
|
35
33
|
* pinned cells' own background; the outline is an inset box-shadow, so the layout never shifts.
|
|
@@ -67,7 +67,6 @@
|
|
|
67
67
|
}
|
|
68
68
|
|
|
69
69
|
/* ─── Dark-mode overrides ────────────────────────────────────────────────────*/
|
|
70
|
-
[data-theme="dark"] .ogx-col-group-cell__label
|
|
71
|
-
.ogx--dark .ogx-col-group-cell__label {
|
|
70
|
+
[data-theme="dark"] .ogx-col-group-cell__label {
|
|
72
71
|
color: var(--ogx-color-gray-300, #cbd5e1);
|
|
73
72
|
}
|
|
@@ -67,24 +67,7 @@
|
|
|
67
67
|
background: var(--ogx-grid-background);
|
|
68
68
|
box-shadow: 0 -4px 6px -1px rgba(0, 0, 0, 0.05);
|
|
69
69
|
}
|
|
70
|
-
|
|
71
|
-
opacity: 0.6;
|
|
72
|
-
pointer-events: none;
|
|
73
|
-
}
|
|
74
|
-
.ogx--loading::after {
|
|
75
|
-
content: '';
|
|
76
|
-
position: absolute;
|
|
77
|
-
top: 0;
|
|
78
|
-
left: 0;
|
|
79
|
-
right: 0;
|
|
80
|
-
bottom: 0;
|
|
81
|
-
background: color-mix(in srgb, var(--ogx-grid-background) 60%, transparent);
|
|
82
|
-
backdrop-filter: blur(2px);
|
|
83
|
-
display: flex;
|
|
84
|
-
align-items: center;
|
|
85
|
-
justify-content: center;
|
|
86
|
-
z-index: var(--ogx-z-index-modal);
|
|
87
|
-
}
|
|
70
|
+
|
|
88
71
|
/* No height prop: fill the container, as a block child of a sized box (height: 100%) or as a
|
|
89
72
|
flex item (flex: 1 with min-height: 0). In an auto-height parent 100% resolves to auto. */
|
|
90
73
|
.ogx--fill {
|
|
@@ -312,16 +295,6 @@
|
|
|
312
295
|
text-overflow: ellipsis;
|
|
313
296
|
}
|
|
314
297
|
|
|
315
|
-
.ogx--density-compact .ogx__cell,
|
|
316
|
-
.ogx--density-compact .ogx__header-cell {
|
|
317
|
-
padding: var(--ogx-spacing-xs) var(--ogx-spacing-sm);
|
|
318
|
-
font-size: var(--ogx-font-size-xs);
|
|
319
|
-
}
|
|
320
|
-
.ogx--density-comfortable .ogx__cell,
|
|
321
|
-
.ogx--density-comfortable .ogx__header-cell {
|
|
322
|
-
padding: var(--ogx-spacing-md) var(--ogx-spacing-lg);
|
|
323
|
-
font-size: var(--ogx-font-size-md);
|
|
324
|
-
}
|
|
325
298
|
.ogx__group-cell-content {
|
|
326
299
|
font-weight: 500;
|
|
327
300
|
text-align: start;
|
|
@@ -228,7 +228,8 @@ export function GridVirtualRows<R extends GridRowModel>({
|
|
|
228
228
|
{Array.from({ length: infiniteScrollSkeletonCount }).map((_, i) => (
|
|
229
229
|
<Row<R>
|
|
230
230
|
key={`__skeleton_${i}__`}
|
|
231
|
-
row={{ id: `__skeleton_${i}__
|
|
231
|
+
row={{ id: `__skeleton_${i}__` } as unknown as R}
|
|
232
|
+
skeleton
|
|
232
233
|
columns={virtualColumns}
|
|
233
234
|
rowIndex={unpinnedRowsLength + i}
|
|
234
235
|
rowHeight={rowHeight}
|
|
@@ -7,9 +7,9 @@ import type {
|
|
|
7
7
|
GridFilterOperator
|
|
8
8
|
} from '../../types';
|
|
9
9
|
import { filterValueToInputText, getOperatorsForType, NO_VALUE_OPERATORS } from '../../utils/filtering';
|
|
10
|
+
import { filterValueFromInputText, getFilterValueOptions, isSameFilterValue, matchesFilterValueOption } from '../../utils/filtering/filterInput';
|
|
10
11
|
import { Input } from '../ui/Input';
|
|
11
12
|
|
|
12
|
-
|
|
13
13
|
export interface FilterPanelProps {
|
|
14
14
|
filterModel: GridFilterModel;
|
|
15
15
|
columns: GridColDef[];
|
|
@@ -29,17 +29,10 @@ function getActiveFilter(items: readonly FilterEntry[], field: string): GridFilt
|
|
|
29
29
|
return items.find((entry): entry is GridFilterItem => isFilterItem(entry) && entry.field === field);
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
-
function sameValue(a: unknown, b: unknown): boolean {
|
|
33
|
-
if (Object.is(a, b)) return true;
|
|
34
|
-
if (a instanceof Date && b instanceof Date) return a.getTime() === b.getTime();
|
|
35
|
-
if (Array.isArray(a) && Array.isArray(b)) return a.length === b.length && a.every((v, i) => sameValue(v, b[i]));
|
|
36
|
-
return false;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
32
|
function sameEntry(a: FilterEntry, b: FilterEntry): boolean {
|
|
40
33
|
if (a === b) return true;
|
|
41
34
|
if (isFilterItem(a) && isFilterItem(b)) {
|
|
42
|
-
return a.id === b.id && a.field === b.field && a.operator === b.operator &&
|
|
35
|
+
return a.id === b.id && a.field === b.field && a.operator === b.operator && isSameFilterValue(a.value, b.value);
|
|
43
36
|
}
|
|
44
37
|
if (!isFilterItem(a) && !isFilterItem(b)) {
|
|
45
38
|
return a.id === b.id && a.logicOperator === b.logicOperator && sameEntries(a.items, b.items);
|
|
@@ -52,36 +45,11 @@ function sameEntries(a: readonly FilterEntry[], b: readonly FilterEntry[]): bool
|
|
|
52
45
|
return a.length === b.length && a.every((entry, i) => sameEntry(entry, b[i]));
|
|
53
46
|
}
|
|
54
47
|
|
|
55
|
-
interface ValueOption {
|
|
56
|
-
key: string;
|
|
57
|
-
value: unknown;
|
|
58
|
-
label: string;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
function getValueOptions(col: GridColDef): ValueOption[] {
|
|
62
|
-
return (col.valueOptions ?? []).map((option, index) => (
|
|
63
|
-
option !== null && typeof option === 'object'
|
|
64
|
-
? { key: String(index), value: option.value, label: option.label }
|
|
65
|
-
: { key: String(index), value: option, label: String(option) }
|
|
66
|
-
));
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
function matchesOption(option: ValueOption, value: unknown): boolean {
|
|
70
|
-
if (Object.is(option.value, value)) return true;
|
|
71
|
-
return value != null && String(option.value).toLowerCase() === String(value).toLowerCase();
|
|
72
|
-
}
|
|
73
|
-
|
|
74
48
|
/** Text shown in the value input for a committed value. Never used to emit anything. */
|
|
75
49
|
function toInputText(value: unknown, col: GridColDef): string {
|
|
76
50
|
return filterValueToInputText(value, col.type);
|
|
77
51
|
}
|
|
78
52
|
|
|
79
|
-
/** The item value for typed text. isAnyOf takes a comma-separated list. */
|
|
80
|
-
function fromInputText(text: string, operator: GridFilterOperator): unknown {
|
|
81
|
-
if (operator === 'isAnyOf') return text.split(',').map(part => part.trim()).filter(part => part !== '');
|
|
82
|
-
return text;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
53
|
/** Carry a value over to a newly picked operator: lists for isAnyOf, scalars for the rest. */
|
|
86
54
|
function convertValueForOperator(value: unknown, operator: GridFilterOperator): unknown {
|
|
87
55
|
if (NO_VALUE_OPERATORS.has(operator)) return undefined;
|
|
@@ -144,7 +112,7 @@ const FilterRow: React.FC<{
|
|
|
144
112
|
const unchanged = current ? text === toInputText(current.value, colRef.current) : text.trim() === '';
|
|
145
113
|
if (unchanged) return;
|
|
146
114
|
const operator = current?.operator ?? defaultOperator;
|
|
147
|
-
onChangeRef.current(buildItem(operator,
|
|
115
|
+
onChangeRef.current(buildItem(operator, filterValueFromInputText(text, operator)));
|
|
148
116
|
}, [defaultOperator, buildItem]);
|
|
149
117
|
|
|
150
118
|
// Text still waiting for the debounce; flushed when the panel closes (unmounts) meanwhile.
|
|
@@ -172,7 +140,7 @@ const FilterRow: React.FC<{
|
|
|
172
140
|
|
|
173
141
|
const handleOperatorChange = (op: GridFilterOperator) => {
|
|
174
142
|
// Keep what the user typed even if the debounce has not committed it yet.
|
|
175
|
-
const value = draft !== null ?
|
|
143
|
+
const value = draft !== null ? filterValueFromInputText(draft, op) : item?.value;
|
|
176
144
|
setDraft(null);
|
|
177
145
|
onChange(buildItem(op, convertValueForOperator(value, op)));
|
|
178
146
|
};
|
|
@@ -183,7 +151,7 @@ const FilterRow: React.FC<{
|
|
|
183
151
|
};
|
|
184
152
|
|
|
185
153
|
const showValue = !NO_VALUE_OPERATORS.has(currentOperator);
|
|
186
|
-
const valueOptions =
|
|
154
|
+
const valueOptions = getFilterValueOptions(col);
|
|
187
155
|
const valueClassName = `ogx-filter__value-input${isActive ? ' ogx-filter__value-input--active' : ''}`;
|
|
188
156
|
const valueLabel = `Filter value for ${label}`;
|
|
189
157
|
|
|
@@ -215,7 +183,7 @@ const FilterRow: React.FC<{
|
|
|
215
183
|
className={`${valueClassName} ogx-filter__value-multiselect`}
|
|
216
184
|
multiple
|
|
217
185
|
size={Math.min(valueOptions.length, 4)}
|
|
218
|
-
value={valueOptions.filter(o => selected.some(v =>
|
|
186
|
+
value={valueOptions.filter(o => selected.some(v => matchesFilterValueOption(o, v))).map(o => o.key)}
|
|
219
187
|
onChange={(e) => {
|
|
220
188
|
const keys = new Set(Array.from(e.target.selectedOptions, option => option.value));
|
|
221
189
|
onChange(buildItem('isAnyOf', valueOptions.filter(o => keys.has(o.key)).map(o => o.value)));
|
|
@@ -226,7 +194,7 @@ const FilterRow: React.FC<{
|
|
|
226
194
|
</select>
|
|
227
195
|
);
|
|
228
196
|
} else if (showValue && col.type === 'singleSelect' && valueOptions.length > 0 && (currentOperator === 'is' || currentOperator === 'not')) {
|
|
229
|
-
const current = valueOptions.find(o =>
|
|
197
|
+
const current = valueOptions.find(o => matchesFilterValueOption(o, item?.value));
|
|
230
198
|
valueControl = (
|
|
231
199
|
<select
|
|
232
200
|
id={`${filterId}-val`}
|
|
@@ -207,12 +207,7 @@
|
|
|
207
207
|
background-color: var(--ogx-grid-header-hover-background);
|
|
208
208
|
z-index: 6;
|
|
209
209
|
}
|
|
210
|
-
|
|
211
|
-
outline: 2px solid var(--ogx-grid-cell-focus-border, var(--ogx-color-primary));
|
|
212
|
-
outline-offset: -2px;
|
|
213
|
-
background-color: var(--ogx-grid-header-hover-background);
|
|
214
|
-
z-index: 6;
|
|
215
|
-
}
|
|
210
|
+
|
|
216
211
|
.ogx__header-cell:focus, .ogx__header-cell:focus-visible {
|
|
217
212
|
outline: none !important;
|
|
218
213
|
}
|
|
@@ -241,8 +236,4 @@
|
|
|
241
236
|
background: var(--ogx-grid-header-hover-background);
|
|
242
237
|
color: var(--ogx-grid-header-text);
|
|
243
238
|
}
|
|
244
|
-
|
|
245
|
-
opacity: 1;
|
|
246
|
-
background: var(--ogx-color-primary-light);
|
|
247
|
-
color: var(--ogx-color-primary);
|
|
248
|
-
}
|
|
239
|
+
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
getHeaderFilterOperators,
|
|
11
11
|
type GridHeaderFilterState,
|
|
12
12
|
} from '../../utils/headerFilters';
|
|
13
|
+
import { filterValueFromInputText, getFilterValueOptions, isSameFilterValue, matchesFilterValueOption } from '../../utils/filtering/filterInput';
|
|
13
14
|
import { Input } from '../ui/Input';
|
|
14
15
|
import { HeaderFilterOperatorMenu } from './HeaderFilterOperatorMenu';
|
|
15
16
|
import type { HeaderFilterLocale } from '../../utils/headerFilterLocale';
|
|
@@ -29,42 +30,10 @@ export interface HeaderFilterCellProps {
|
|
|
29
30
|
locale: HeaderFilterLocale;
|
|
30
31
|
}
|
|
31
32
|
|
|
32
|
-
interface ValueOption {
|
|
33
|
-
key: string;
|
|
34
|
-
value: unknown;
|
|
35
|
-
label: string;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
function getValueOptions(col: GridColDef): ValueOption[] {
|
|
39
|
-
return (col.valueOptions ?? []).map((option, index) => (
|
|
40
|
-
option !== null && typeof option === 'object'
|
|
41
|
-
? { key: String(index), value: option.value, label: option.label }
|
|
42
|
-
: { key: String(index), value: option, label: String(option) }
|
|
43
|
-
));
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
function matchesOption(option: ValueOption, value: unknown): boolean {
|
|
47
|
-
const single = Array.isArray(value) ? value[0] : value;
|
|
48
|
-
if (Object.is(option.value, single)) return true;
|
|
49
|
-
return single != null && String(option.value).toLowerCase() === String(single).toLowerCase();
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
/** The item value for typed text. isAnyOf takes a comma-separated list. */
|
|
53
|
-
function fromInputText(text: string, operator: GridFilterOperator): unknown {
|
|
54
|
-
if (operator === 'isAnyOf') return text.split(',').map(part => part.trim()).filter(part => part !== '');
|
|
55
|
-
return text;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
function sameValue(a: unknown, b: unknown): boolean {
|
|
59
|
-
if (Object.is(a, b)) return true;
|
|
60
|
-
if (Array.isArray(a) && Array.isArray(b)) return a.length === b.length && a.every((v, i) => sameValue(v, b[i]));
|
|
61
|
-
return false;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
33
|
function sameItem(a: GridFilterItem | undefined, b: GridFilterItem | undefined): boolean {
|
|
65
34
|
if (a === b) return true;
|
|
66
35
|
if (!a || !b) return false;
|
|
67
|
-
return a.operator === b.operator &&
|
|
36
|
+
return a.operator === b.operator && isSameFilterValue(a.value, b.value);
|
|
68
37
|
}
|
|
69
38
|
|
|
70
39
|
function ClearIcon() {
|
|
@@ -81,7 +50,7 @@ export function HeaderFilterCell({ colDef, state, style, className, ariaColIndex
|
|
|
81
50
|
const label = colDef.headerName || colDef.field;
|
|
82
51
|
const item = state.kind === 'editable' ? state.item : undefined;
|
|
83
52
|
const operator: GridFilterOperator = item?.operator ?? getHeaderFilterDefaultOperator(colDef);
|
|
84
|
-
const valueOptions =
|
|
53
|
+
const valueOptions = getFilterValueOptions(colDef);
|
|
85
54
|
const usesSelect = colDef.type === 'boolean' || (colDef.type === 'singleSelect' && valueOptions.length > 0);
|
|
86
55
|
const showsValue = !NO_VALUE_OPERATORS.has(operator);
|
|
87
56
|
|
|
@@ -117,7 +86,7 @@ export function HeaderFilterCell({ colDef, state, style, className, ariaColIndex
|
|
|
117
86
|
const unchanged = current ? text === filterValueToInputText(current.value, col.type) : text.trim() === '';
|
|
118
87
|
if (unchanged) return;
|
|
119
88
|
const op = current?.operator ?? getHeaderFilterDefaultOperator(col);
|
|
120
|
-
const next = buildHeaderFilterItem(col, op,
|
|
89
|
+
const next = buildHeaderFilterItem(col, op, filterValueFromInputText(text, op));
|
|
121
90
|
setLastEmitted(next);
|
|
122
91
|
emit(col.field, next);
|
|
123
92
|
}, []);
|
|
@@ -173,7 +142,7 @@ export function HeaderFilterCell({ colDef, state, style, className, ariaColIndex
|
|
|
173
142
|
|
|
174
143
|
const selectOperator = (op: GridFilterOperator) => {
|
|
175
144
|
// Keep what was typed even if the debounce has not committed it yet.
|
|
176
|
-
const value = draft !== null ?
|
|
145
|
+
const value = draft !== null ? filterValueFromInputText(draft, op) : item?.value;
|
|
177
146
|
setDraft(null);
|
|
178
147
|
setMenuAnchor(null);
|
|
179
148
|
report(buildHeaderFilterItem(colDef, op, convertHeaderFilterValue(value, op)));
|
|
@@ -271,7 +240,7 @@ export function HeaderFilterCell({ colDef, state, style, className, ariaColIndex
|
|
|
271
240
|
</select>
|
|
272
241
|
);
|
|
273
242
|
} else if (usesSelect) {
|
|
274
|
-
const selected = valueOptions.find(o =>
|
|
243
|
+
const selected = valueOptions.find(o => matchesFilterValueOption(o, item?.value));
|
|
275
244
|
valueControl = (
|
|
276
245
|
<select
|
|
277
246
|
ref={selectRef}
|
|
@@ -109,22 +109,6 @@
|
|
|
109
109
|
color: var(--ogx-text-secondary, #9ca3af);
|
|
110
110
|
}
|
|
111
111
|
|
|
112
|
-
/* ─── Responsive: auto switch at ≤768px when using ogx--list-auto class ─── */
|
|
113
|
-
@media (max-width: 768px) {
|
|
114
|
-
.ogx--list-auto .ogx__viewport {
|
|
115
|
-
display: none;
|
|
116
|
-
}
|
|
117
|
-
.ogx--list-auto .ogx-list-view {
|
|
118
|
-
display: flex;
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
@media (min-width: 769px) {
|
|
123
|
-
.ogx--list-auto .ogx-list-view {
|
|
124
|
-
display: none;
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
|
|
128
112
|
/* Loading with no rows yet: a progress bar, or slots.loadingOverlay centred. */
|
|
129
113
|
.ogx-list-view__loading {
|
|
130
114
|
position: relative;
|
|
@@ -46,6 +46,8 @@ function mergeColSpan<R extends GridRowModel>(
|
|
|
46
46
|
|
|
47
47
|
export interface RowProps<R extends GridRowModel = GridRowModel> {
|
|
48
48
|
row: R;
|
|
49
|
+
/** A shimmer placeholder shown while infinite scroll loads more rows (`row` is unused). */
|
|
50
|
+
skeleton?: boolean;
|
|
49
51
|
/** The row's id (from getRowId). Defaults to `row.id`; the grid always passes it. */
|
|
50
52
|
rowId?: GridRowId;
|
|
51
53
|
columns: GridColDef<R>[];
|
|
@@ -108,6 +110,7 @@ export interface RowProps<R extends GridRowModel = GridRowModel> {
|
|
|
108
110
|
function RowImpl<R extends GridRowModel = GridRowModel>(props: RowProps<R>) {
|
|
109
111
|
const {
|
|
110
112
|
row,
|
|
113
|
+
skeleton = false,
|
|
111
114
|
rowId: rowIdProp,
|
|
112
115
|
columns,
|
|
113
116
|
rowIndex,
|
|
@@ -197,7 +200,7 @@ function RowImpl<R extends GridRowModel = GridRowModel>(props: RowProps<R>) {
|
|
|
197
200
|
}, [onEditStop, id]);
|
|
198
201
|
|
|
199
202
|
// ── Skeleton row (shown during infinite-scroll fetch) ─────────────────────
|
|
200
|
-
if (
|
|
203
|
+
if (skeleton) {
|
|
201
204
|
// Render shimmer placeholders — one per visible column, matching row height
|
|
202
205
|
const cellCount = Math.max(columns.length, 5);
|
|
203
206
|
return (
|
|
@@ -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;
|
|
@@ -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;
|