@opencorestack/opengridx 3.3.0 → 3.5.0
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 +64 -0
- package/README.md +10 -1
- package/dist/ai.cjs +2 -0
- package/dist/ai.cjs.map +1 -0
- package/dist/ai.d.ts +366 -0
- package/dist/ai.es.js +2 -0
- package/dist/ai.es.js.map +1 -0
- package/dist/index.d.ts +396 -6
- package/dist/opengridx.css +1 -1
- package/dist/opengridx.es.js +8029 -6090
- package/dist/opengridx.es.js.map +1 -1
- package/dist/opengridx.umd.js +16 -14
- package/dist/opengridx.umd.js.map +1 -1
- package/docs/API_REFERENCE.md +182 -4
- package/docs/README.md +5 -1
- package/docs/architecture/datagrid-orchestration.md +19 -5
- package/docs/components/filter-panel.md +1 -0
- package/docs/components/header.md +4 -1
- package/docs/components/toolbar.md +7 -0
- package/docs/customization/theming.md +12 -1
- package/docs/features/ai-toolkit.md +213 -0
- package/docs/features/cell-selection.md +8 -2
- package/docs/features/clipboard.md +88 -0
- package/docs/features/editing-reordering.md +50 -0
- package/docs/features/fill-handle.md +125 -0
- package/docs/features/filtering.md +3 -0
- package/docs/features/header-filters.md +138 -0
- package/docs/features/keyboard-navigation.md +31 -2
- package/docs/features/undo-redo.md +79 -0
- package/docs/roadmap.md +3 -4
- package/lib/ai/agentTools.ts +160 -0
- package/lib/ai/coerce.ts +90 -0
- package/lib/ai/columns.ts +90 -0
- package/lib/ai/index.ts +33 -0
- package/lib/ai/promptHandler.ts +109 -0
- package/lib/ai/schema.ts +214 -0
- package/lib/ai/types.ts +152 -0
- package/lib/ai/validate.ts +296 -0
- package/lib/components/AiAssistant/GridAiAssistantArea.tsx +26 -0
- package/lib/components/AiAssistant/GridAiAssistantPanel.css +171 -0
- package/lib/components/AiAssistant/GridAiAssistantPanel.tsx +145 -0
- package/lib/components/AiAssistant/useSpeechInput.ts +74 -0
- package/lib/components/Cell/Cell.css +33 -0
- package/lib/components/Cell/Cell.tsx +6 -2
- package/lib/components/Cell/GridEditInputCell.css +12 -4
- package/lib/components/Cell/GridEditInputCell.tsx +19 -12
- package/lib/components/ColumnVisibilityPanel/ColumnVisibilityPanel.css +14 -0
- package/lib/components/ColumnVisibilityPanel/ColumnVisibilityPanel.tsx +3 -2
- package/lib/components/DataGrid/DataGrid.tsx +138 -5
- package/lib/components/DataGrid/GridLiveRegion.tsx +5 -2
- package/lib/components/DataGrid/GridPinnedRows.tsx +5 -1
- package/lib/components/DataGrid/GridVirtualRows.tsx +5 -1
- package/lib/components/FilterPanel/FilterPanel.css +14 -0
- package/lib/components/FilterPanel/FilterPanel.tsx +5 -11
- package/lib/components/Header/Header.tsx +44 -23
- package/lib/components/Header/headerColumnStyle.ts +28 -0
- package/lib/components/HeaderFilter/HeaderFilter.css +160 -0
- package/lib/components/HeaderFilter/HeaderFilterCell.tsx +370 -0
- package/lib/components/HeaderFilter/HeaderFilterOperatorMenu.tsx +124 -0
- package/lib/components/HeaderFilter/HeaderFilterRow.tsx +157 -0
- package/lib/components/ListView/ListView.css +2 -4
- package/lib/components/ListView/ListViewRow.tsx +2 -2
- package/lib/components/Toolbar/GlobalSearch.css +16 -0
- package/lib/components/Toolbar/GlobalSearch.tsx +3 -2
- package/lib/components/Toolbar/GridToolbar.tsx +51 -0
- package/lib/components/ui/Checkbox.tsx +16 -5
- package/lib/components/ui/Input.css +43 -0
- package/lib/components/ui/Input.tsx +24 -5
- package/lib/hooks/core/gridApiPlaceholder.ts +16 -1
- package/lib/hooks/core/gridToolbarHostContext.ts +5 -0
- package/lib/hooks/core/useDataGrid.ts +27 -0
- package/lib/hooks/core/useGridApiMethods.ts +39 -0
- package/lib/hooks/core/useGridAriaRows.ts +5 -3
- package/lib/hooks/core/useGridCellSelection.ts +107 -13
- package/lib/hooks/core/useGridClipboardPaste.ts +283 -0
- package/lib/hooks/core/useGridColumnsPanel.ts +24 -1
- package/lib/hooks/core/useGridControlledState.ts +27 -0
- package/lib/hooks/core/useGridFillHandle.ts +323 -0
- package/lib/hooks/core/useGridKeyboardNavigation.ts +29 -10
- package/lib/hooks/core/useGridToolbarProps.ts +19 -0
- package/lib/hooks/core/useGridUndoRedo.ts +220 -0
- package/lib/hooks/features/useGridAiAssistant.ts +284 -0
- package/lib/hooks/features/useGridBatchEdit.ts +217 -0
- package/lib/hooks/features/useGridEditing.ts +24 -45
- package/lib/index.ts +10 -0
- package/lib/styles/opengridx.css +4 -1
- package/lib/theme/DataGridThemeProvider.tsx +2 -0
- package/lib/theme/types.ts +2 -0
- package/lib/types/index.ts +343 -1
- package/lib/utils/aiAssistant.ts +205 -0
- package/lib/utils/aiBrand.ts +17 -0
- package/lib/utils/cellSelection.ts +25 -5
- package/lib/utils/editing/commit.ts +69 -0
- package/lib/utils/editing/fill.ts +238 -0
- package/lib/utils/editing/targets.ts +109 -0
- package/lib/utils/filtering/index.ts +14 -0
- package/lib/utils/focus/index.ts +9 -3
- package/lib/utils/headerFilters.ts +189 -0
- package/lib/utils/parsing.ts +252 -0
- package/llms.txt +97 -3
- package/package.json +8 -2
|
@@ -17,6 +17,7 @@ import type {
|
|
|
17
17
|
import type { CellColSpanInfo, RowSpanningCaches } from '../../hooks/features/useGridSpanning';
|
|
18
18
|
import type { GridDisplayCellRange } from '../../hooks/core/useGridCellSelection';
|
|
19
19
|
import { getRowCellRange } from '../../utils/cellSelection';
|
|
20
|
+
import type { GridFillHandlePosition } from '../../utils/cellSelection';
|
|
20
21
|
import type { GridEditingState } from '../../hooks/features/useGridEditing';
|
|
21
22
|
|
|
22
23
|
interface PinnedRowEditingHandlers {
|
|
@@ -64,6 +65,8 @@ export interface GridPinnedRowsProps<R extends GridRowModel> {
|
|
|
64
65
|
cellRange?: GridDisplayCellRange | null;
|
|
65
66
|
/** The grid has row spans: rows tell their cells how far the range bottom is. */
|
|
66
67
|
cellRangeHasRowSpan?: boolean;
|
|
68
|
+
/** Where the fill handle is drawn (v3.5), or null. */
|
|
69
|
+
fillHandle?: GridFillHandlePosition | null;
|
|
67
70
|
/** Pinned rows are edited like any other row. */
|
|
68
71
|
editingHandlers?: PinnedRowEditingHandlers;
|
|
69
72
|
isCellEditable?: (params: GridCellParams<R>) => boolean;
|
|
@@ -111,6 +114,7 @@ export function GridPinnedRows<R extends GridRowModel>({
|
|
|
111
114
|
rowReordering = false,
|
|
112
115
|
cellRange,
|
|
113
116
|
cellRangeHasRowSpan = false,
|
|
117
|
+
fillHandle,
|
|
114
118
|
}: GridPinnedRowsProps<R>) {
|
|
115
119
|
if (rows.length === 0) return null;
|
|
116
120
|
|
|
@@ -156,7 +160,7 @@ export function GridPinnedRows<R extends GridRowModel>({
|
|
|
156
160
|
onEditCellValueChange={editingHandlers?.setEditCellValue}
|
|
157
161
|
isCellEditable={isCellEditable}
|
|
158
162
|
rowReordering={rowReordering}
|
|
159
|
-
cellRange={getRowCellRange(cellRange, rowIndex, cellRangeHasRowSpan)}
|
|
163
|
+
cellRange={getRowCellRange(cellRange, rowIndex, cellRangeHasRowSpan, fillHandle)}
|
|
160
164
|
/>
|
|
161
165
|
);
|
|
162
166
|
})}
|
|
@@ -20,6 +20,7 @@ import type { GridEditingState } from '../../hooks/features/useGridEditing';
|
|
|
20
20
|
import type { CellColSpanInfo, RowSpanningCaches } from '../../hooks/features/useGridSpanning';
|
|
21
21
|
import type { GridDisplayCellRange } from '../../hooks/core/useGridCellSelection';
|
|
22
22
|
import { getRowCellRange } from '../../utils/cellSelection';
|
|
23
|
+
import type { GridFillHandlePosition } from '../../utils/cellSelection';
|
|
23
24
|
|
|
24
25
|
interface RowEditingHandlers {
|
|
25
26
|
editingCell: GridEditingState['editingCell'];
|
|
@@ -77,6 +78,8 @@ export interface GridVirtualRowsProps<R extends GridRowModel> {
|
|
|
77
78
|
cellRange?: GridDisplayCellRange | null;
|
|
78
79
|
/** The grid has row spans: rows tell their cells how far the range bottom is. */
|
|
79
80
|
cellRangeHasRowSpan?: boolean;
|
|
81
|
+
/** Where the fill handle is drawn (v3.5), or null. */
|
|
82
|
+
fillHandle?: GridFillHandlePosition | null;
|
|
80
83
|
}
|
|
81
84
|
|
|
82
85
|
export function GridVirtualRows<R extends GridRowModel>({
|
|
@@ -123,6 +126,7 @@ export function GridVirtualRows<R extends GridRowModel>({
|
|
|
123
126
|
columnIndexMap,
|
|
124
127
|
cellRange,
|
|
125
128
|
cellRangeHasRowSpan = false,
|
|
129
|
+
fillHandle,
|
|
126
130
|
}: GridVirtualRowsProps<R>) {
|
|
127
131
|
const skeletonColumns: GridColDef<R>[] = baseColumns.length > 0
|
|
128
132
|
? baseColumns
|
|
@@ -214,7 +218,7 @@ export function GridVirtualRows<R extends GridRowModel>({
|
|
|
214
218
|
rowSpanningCaches={rowSpanningCaches}
|
|
215
219
|
rowHeight={rowHeight}
|
|
216
220
|
rowMeta={rowMetaMap.get(id)}
|
|
217
|
-
cellRange={getRowCellRange(cellRange, actualIndex, cellRangeHasRowSpan)}
|
|
221
|
+
cellRange={getRowCellRange(cellRange, actualIndex, cellRangeHasRowSpan, fillHandle)}
|
|
218
222
|
/>
|
|
219
223
|
))
|
|
220
224
|
)}
|
|
@@ -115,11 +115,25 @@
|
|
|
115
115
|
border-radius: var(--ogx-border-radius-sm);
|
|
116
116
|
font-family: var(--ogx-font-family);
|
|
117
117
|
font-size: var(--ogx-font-size-xs);
|
|
118
|
+
line-height: revert;
|
|
118
119
|
background: var(--ogx-grid-background);
|
|
119
120
|
color: var(--ogx-grid-row-text);
|
|
120
121
|
outline: none;
|
|
121
122
|
transition: border-color 0.15s, box-shadow 0.15s;
|
|
122
123
|
}
|
|
124
|
+
/* The text/date value box renders through the shared Input: the <input> keeps its own border,
|
|
125
|
+
so the wrapper draws nothing and only takes the input's place in the row. */
|
|
126
|
+
.ogx-filter__controls .ogx-input-wrapper {
|
|
127
|
+
flex: 1;
|
|
128
|
+
min-width: 60px;
|
|
129
|
+
border: none;
|
|
130
|
+
border-radius: 0;
|
|
131
|
+
background: transparent;
|
|
132
|
+
transition: none;
|
|
133
|
+
}
|
|
134
|
+
.ogx-filter__controls .ogx-input-wrapper:focus-within {
|
|
135
|
+
box-shadow: none;
|
|
136
|
+
}
|
|
123
137
|
.ogx-filter__value-input::placeholder {
|
|
124
138
|
color: var(--ogx-overlay-text, var(--ogx-color-gray-400));
|
|
125
139
|
opacity: 0.6;
|
|
@@ -6,8 +6,8 @@ import type {
|
|
|
6
6
|
GridColDef,
|
|
7
7
|
GridFilterOperator
|
|
8
8
|
} from '../../types';
|
|
9
|
-
import { getOperatorsForType, NO_VALUE_OPERATORS } from '../../utils/filtering';
|
|
10
|
-
import {
|
|
9
|
+
import { filterValueToInputText, getOperatorsForType, NO_VALUE_OPERATORS } from '../../utils/filtering';
|
|
10
|
+
import { Input } from '../ui/Input';
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
export interface FilterPanelProps {
|
|
@@ -73,13 +73,7 @@ function matchesOption(option: ValueOption, value: unknown): boolean {
|
|
|
73
73
|
|
|
74
74
|
/** Text shown in the value input for a committed value. Never used to emit anything. */
|
|
75
75
|
function toInputText(value: unknown, col: GridColDef): string {
|
|
76
|
-
|
|
77
|
-
if (col.type === 'date' || value instanceof Date) {
|
|
78
|
-
const day = toLocalDateString(value);
|
|
79
|
-
return day || (typeof value === 'string' ? value : '');
|
|
80
|
-
}
|
|
81
|
-
if (Array.isArray(value)) return value.map(v => String(v)).join(', ');
|
|
82
|
-
return String(value);
|
|
76
|
+
return filterValueToInputText(value, col.type);
|
|
83
77
|
}
|
|
84
78
|
|
|
85
79
|
/** The item value for typed text. isAnyOf takes a comma-separated list. */
|
|
@@ -251,10 +245,10 @@ const FilterRow: React.FC<{
|
|
|
251
245
|
);
|
|
252
246
|
} else if (showValue) {
|
|
253
247
|
valueControl = (
|
|
254
|
-
<
|
|
248
|
+
<Input
|
|
255
249
|
id={`${filterId}-val`}
|
|
256
250
|
name={`filter-val-${col.field}`}
|
|
257
|
-
|
|
251
|
+
inputClassName={valueClassName}
|
|
258
252
|
type={col.type === 'date' ? 'date' : 'text'}
|
|
259
253
|
value={draft ?? toInputText(item?.value, col)}
|
|
260
254
|
onChange={(e) => setDraft(e.target.value)}
|
|
@@ -2,12 +2,14 @@ import React, { useId } from 'react';
|
|
|
2
2
|
import { Checkbox } from '../ui/Checkbox';
|
|
3
3
|
import { ColumnResizeHandle } from '../ColumnResizeHandle/ColumnResizeHandle';
|
|
4
4
|
import { clampResizeWidth } from '../ColumnResizeHandle/clampResizeWidth';
|
|
5
|
-
import type { GridColDef, GridRowModel, GridSortDirection, GridColumnPinning, GridAggregationModel, GridColumnGroupingModel } from '../../types';
|
|
5
|
+
import type { GridColDef, GridRowModel, GridSortDirection, GridColumnPinning, GridAggregationModel, GridColumnGroupingModel, GridFilterModel } from '../../types';
|
|
6
6
|
import { isColumnPinned, calculatePinnedPositions, getPinnedEdgeFields } from '../../utils/pinning';
|
|
7
7
|
import { getRenderedColumnWidth } from '../../utils/columnWidth';
|
|
8
8
|
import { buildColumnGroupRow, getColumnGroupDepth, getColumnGroupPaths } from '../../utils/columnGroups';
|
|
9
9
|
import { ColumnMenu } from './ColumnMenu';
|
|
10
10
|
import { getHeaderLineClamp, shouldWrapHeaderText } from '../../utils/headerWrap';
|
|
11
|
+
import { getHeaderColumnStyle } from './headerColumnStyle';
|
|
12
|
+
import { HeaderFilterRow } from '../HeaderFilter/HeaderFilterRow';
|
|
11
13
|
|
|
12
14
|
|
|
13
15
|
function MenuIcon() {
|
|
@@ -60,8 +62,11 @@ export interface HeaderProps<R extends GridRowModel = GridRowModel> {
|
|
|
60
62
|
onDragOver?: (field: string) => (event: React.DragEvent) => void;
|
|
61
63
|
onDragEnd?: () => void;
|
|
62
64
|
onDrop?: (targetField: string) => (event: React.DragEvent) => void;
|
|
63
|
-
/**
|
|
64
|
-
|
|
65
|
+
/**
|
|
66
|
+
* The grid's focus position; `id: null` means a header cell is focused, and `headerFilter: true`
|
|
67
|
+
* a cell of the header filter row.
|
|
68
|
+
*/
|
|
69
|
+
focusedCell?: { id: string | number | null; field: string; headerFilter?: boolean } | null;
|
|
65
70
|
onHeaderClick?: (field: string) => void;
|
|
66
71
|
onSortAdd?: (field: string, direction: GridSortDirection) => void;
|
|
67
72
|
multiSort?: boolean;
|
|
@@ -74,6 +79,14 @@ export interface HeaderProps<R extends GridRowModel = GridRowModel> {
|
|
|
74
79
|
wrapHeaderText?: boolean;
|
|
75
80
|
/** Header row height in pixels; sets how many lines a wrapped title may take. Defaults to 56. */
|
|
76
81
|
headerHeight?: number;
|
|
82
|
+
/** Renders the header filter row under the column headers (`DataGridProps.headerFilters`). */
|
|
83
|
+
headerFilters?: boolean;
|
|
84
|
+
/** The filter model the header filter row reads. */
|
|
85
|
+
filterModel?: GridFilterModel;
|
|
86
|
+
/** Receives the filter model after a header filter changes. */
|
|
87
|
+
onFilterModelChange?: (model: GridFilterModel) => void;
|
|
88
|
+
/** Opens the filter panel; a header filter cell showing "Custom filter" calls it. */
|
|
89
|
+
onOpenFilterPanel?: () => void;
|
|
77
90
|
}
|
|
78
91
|
|
|
79
92
|
export function Header<R extends GridRowModel = GridRowModel>(props: HeaderProps<R>) {
|
|
@@ -112,6 +125,10 @@ export function Header<R extends GridRowModel = GridRowModel>(props: HeaderProps
|
|
|
112
125
|
columnIndexMap,
|
|
113
126
|
wrapHeaderText = false,
|
|
114
127
|
headerHeight = 56,
|
|
128
|
+
headerFilters = false,
|
|
129
|
+
filterModel,
|
|
130
|
+
onFilterModelChange,
|
|
131
|
+
onOpenFilterPanel,
|
|
115
132
|
} = props;
|
|
116
133
|
|
|
117
134
|
const selectAllId = useId();
|
|
@@ -148,7 +165,7 @@ export function Header<R extends GridRowModel = GridRowModel>(props: HeaderProps
|
|
|
148
165
|
setMenuOpenParams({ colDef, anchorEl: anchor });
|
|
149
166
|
};
|
|
150
167
|
|
|
151
|
-
const isHeaderFocused = (field: string) => focusedCell?.id === null && focusedCell.field === field;
|
|
168
|
+
const isHeaderFocused = (field: string) => focusedCell?.id === null && !focusedCell.headerFilter && focusedCell.field === field;
|
|
152
169
|
|
|
153
170
|
// aria-colindex: system columns first (reorder, expand, checkbox), then the data columns.
|
|
154
171
|
const reorderColIndex = rowReordering ? 1 : 0;
|
|
@@ -437,30 +454,12 @@ export function Header<R extends GridRowModel = GridRowModel>(props: HeaderProps
|
|
|
437
454
|
colDef.headerClassName
|
|
438
455
|
].filter(Boolean).join(' ');
|
|
439
456
|
|
|
440
|
-
const effectiveWidth = columnWidths[colDef.field] ?? colDef.width ?? 100;
|
|
441
|
-
|
|
442
457
|
const style: React.CSSProperties = {
|
|
443
|
-
|
|
444
|
-
minWidth: colDef.minWidth,
|
|
445
|
-
maxWidth: colDef.maxWidth,
|
|
446
|
-
flexGrow: colDef.flex ?? 0,
|
|
447
|
-
flexShrink: 0,
|
|
448
|
-
flexBasis: colDef.flex ? 'auto' : 'auto',
|
|
449
|
-
boxSizing: 'border-box',
|
|
450
|
-
position: isPinned ? 'sticky' : 'relative',
|
|
458
|
+
...getHeaderColumnStyle(colDef, columnWidths, pinnedPosition, pinnedPosition ? pinnedPositions[colDef.field] : undefined),
|
|
451
459
|
zIndex: colDef.zIndex,
|
|
452
460
|
opacity: isDragging ? 0.5 : 1
|
|
453
461
|
};
|
|
454
462
|
|
|
455
|
-
if (isPinned && pinnedPosition) {
|
|
456
|
-
const offset = pinnedPositions[colDef.field];
|
|
457
|
-
if (pinnedPosition === 'left') {
|
|
458
|
-
style.left = offset;
|
|
459
|
-
} else {
|
|
460
|
-
style.right = offset;
|
|
461
|
-
}
|
|
462
|
-
}
|
|
463
|
-
|
|
464
463
|
const headerContent = colDef.renderHeader
|
|
465
464
|
? colDef.renderHeader({ field: colDef.field, colDef: colDef as unknown as GridColDef, colIndex })
|
|
466
465
|
: colDef.headerName || colDef.field;
|
|
@@ -565,6 +564,28 @@ export function Header<R extends GridRowModel = GridRowModel>(props: HeaderProps
|
|
|
565
564
|
/>
|
|
566
565
|
)}
|
|
567
566
|
</div>{/* end .ogx__header */}
|
|
567
|
+
|
|
568
|
+
{/* ── Header filter row ── */}
|
|
569
|
+
{headerFilters && (
|
|
570
|
+
<HeaderFilterRow
|
|
571
|
+
columns={columns as unknown as GridColDef[]}
|
|
572
|
+
columnWidths={columnWidths}
|
|
573
|
+
pinnedColumns={pinnedColumns}
|
|
574
|
+
pinnedPositions={pinnedPositions}
|
|
575
|
+
pinnedEdges={pinnedEdges}
|
|
576
|
+
rowReordering={rowReordering}
|
|
577
|
+
hasDetailPanel={hasDetailPanel}
|
|
578
|
+
checkboxSelection={checkboxSelection}
|
|
579
|
+
pinExpandColumn={pinExpandColumn}
|
|
580
|
+
pinCheckboxColumn={pinCheckboxColumn}
|
|
581
|
+
columnIndexMap={columnIndexMap}
|
|
582
|
+
ariaRowIndex={groupDepth + 2}
|
|
583
|
+
filterModel={filterModel}
|
|
584
|
+
onFilterModelChange={onFilterModelChange}
|
|
585
|
+
onOpenFilterPanel={onOpenFilterPanel}
|
|
586
|
+
focusedField={focusedCell?.id === null && focusedCell.headerFilter ? focusedCell.field : null}
|
|
587
|
+
/>
|
|
588
|
+
)}
|
|
568
589
|
</div>
|
|
569
590
|
);
|
|
570
591
|
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type React from 'react';
|
|
2
|
+
import type { GridColDef, GridRowModel } from '../../types';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Width, flex and sticky offset of a data column's cell in a header row. The column header row
|
|
6
|
+
* and the header filter row both use it, so their cells line up whatever the pinning and widths.
|
|
7
|
+
*/
|
|
8
|
+
export function getHeaderColumnStyle<R extends GridRowModel>(
|
|
9
|
+
colDef: GridColDef<R>,
|
|
10
|
+
columnWidths: Record<string, number>,
|
|
11
|
+
pinnedPosition: 'left' | 'right' | null,
|
|
12
|
+
pinnedOffset: number | undefined,
|
|
13
|
+
): React.CSSProperties {
|
|
14
|
+
const style: React.CSSProperties = {
|
|
15
|
+
width: columnWidths[colDef.field] ?? colDef.width ?? 100,
|
|
16
|
+
minWidth: colDef.minWidth,
|
|
17
|
+
maxWidth: colDef.maxWidth,
|
|
18
|
+
flexGrow: colDef.flex ?? 0,
|
|
19
|
+
flexShrink: 0,
|
|
20
|
+
flexBasis: 'auto',
|
|
21
|
+
boxSizing: 'border-box',
|
|
22
|
+
position: pinnedPosition ? 'sticky' : 'relative',
|
|
23
|
+
};
|
|
24
|
+
if (pinnedPosition === 'left') style.left = pinnedOffset;
|
|
25
|
+
else if (pinnedPosition === 'right') style.right = pinnedOffset;
|
|
26
|
+
return style;
|
|
27
|
+
}
|
|
28
|
+
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
/* Header filter row (headerFilters): inside .ogx__header-wrap, under the column header row, so it
|
|
2
|
+
sticks with the headers. Its height (--ogx-header-filter-height) is set by the grid from
|
|
3
|
+
headerFilterHeight. */
|
|
4
|
+
.ogx__header-filter-row {
|
|
5
|
+
display: flex;
|
|
6
|
+
align-items: stretch;
|
|
7
|
+
height: var(--ogx-header-filter-height, 40px);
|
|
8
|
+
min-height: var(--ogx-header-filter-height, 40px);
|
|
9
|
+
background: var(--ogx-grid-header-background);
|
|
10
|
+
border-bottom: 1px solid var(--ogx-grid-border-color);
|
|
11
|
+
}
|
|
12
|
+
.ogx__header-filter-cell {
|
|
13
|
+
display: flex;
|
|
14
|
+
align-items: center;
|
|
15
|
+
gap: 2px;
|
|
16
|
+
padding: 4px 6px;
|
|
17
|
+
box-sizing: border-box;
|
|
18
|
+
flex-shrink: 0;
|
|
19
|
+
overflow: hidden;
|
|
20
|
+
position: relative;
|
|
21
|
+
background: var(--ogx-grid-header-background);
|
|
22
|
+
font-family: var(--ogx-font-family);
|
|
23
|
+
font-size: var(--ogx-font-size-sm, 0.8125rem);
|
|
24
|
+
color: var(--ogx-grid-header-text);
|
|
25
|
+
}
|
|
26
|
+
.ogx__header-filter-cell:focus,
|
|
27
|
+
.ogx__header-filter-cell:focus-visible {
|
|
28
|
+
outline: none;
|
|
29
|
+
}
|
|
30
|
+
.ogx--kb .ogx__header-filter-cell--focused {
|
|
31
|
+
outline: 2px solid var(--ogx-grid-cell-focus-border, var(--ogx-color-primary, #2563eb));
|
|
32
|
+
outline-offset: -2px;
|
|
33
|
+
z-index: 6;
|
|
34
|
+
}
|
|
35
|
+
.ogx__header-filter-cell--pinned {
|
|
36
|
+
z-index: 12;
|
|
37
|
+
}
|
|
38
|
+
.ogx__header-filter-cell--pinned-left-last {
|
|
39
|
+
box-shadow: var(--ogx-grid-pinned-left-shadow, 4px 0 12px -4px rgba(0, 0, 0, 0.05));
|
|
40
|
+
}
|
|
41
|
+
.ogx__header-filter-cell--pinned-right-first {
|
|
42
|
+
margin-left: auto;
|
|
43
|
+
box-shadow: var(--ogx-grid-pinned-right-shadow, -4px 0 12px -4px rgba(0, 0, 0, 0.05));
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/* Operator button: a compact symbol for the current operator; opens the operator menu. */
|
|
47
|
+
.ogx__header-filter-operator {
|
|
48
|
+
display: inline-flex;
|
|
49
|
+
align-items: center;
|
|
50
|
+
justify-content: center;
|
|
51
|
+
flex-shrink: 0;
|
|
52
|
+
min-width: 22px;
|
|
53
|
+
height: 24px;
|
|
54
|
+
padding: 0 3px;
|
|
55
|
+
border: none;
|
|
56
|
+
border-radius: var(--ogx-border-radius-sm, 4px);
|
|
57
|
+
background: transparent;
|
|
58
|
+
color: var(--ogx-color-gray-500);
|
|
59
|
+
font: inherit;
|
|
60
|
+
font-size: 12px;
|
|
61
|
+
line-height: 1;
|
|
62
|
+
cursor: pointer;
|
|
63
|
+
}
|
|
64
|
+
.ogx__header-filter-operator:hover,
|
|
65
|
+
.ogx__header-filter-operator--open {
|
|
66
|
+
background: var(--ogx-grid-header-hover-background);
|
|
67
|
+
color: var(--ogx-grid-header-text);
|
|
68
|
+
}
|
|
69
|
+
.ogx__header-filter-cell--active .ogx__header-filter-operator {
|
|
70
|
+
color: var(--ogx-color-primary);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/* Value controls: the shared Input, and native selects styled to match it. */
|
|
74
|
+
.ogx__header-filter-input-wrapper {
|
|
75
|
+
flex: 1 1 auto;
|
|
76
|
+
min-width: 0;
|
|
77
|
+
height: 28px;
|
|
78
|
+
}
|
|
79
|
+
.ogx-input.ogx__header-filter-input {
|
|
80
|
+
width: 100%;
|
|
81
|
+
min-width: 0;
|
|
82
|
+
height: 100%;
|
|
83
|
+
box-sizing: border-box;
|
|
84
|
+
padding: 0 6px;
|
|
85
|
+
font-size: inherit;
|
|
86
|
+
}
|
|
87
|
+
.ogx__header-filter-select {
|
|
88
|
+
flex: 1 1 auto;
|
|
89
|
+
min-width: 0;
|
|
90
|
+
height: 28px;
|
|
91
|
+
padding: 0 4px;
|
|
92
|
+
box-sizing: border-box;
|
|
93
|
+
border: var(--ogx-border-width-thin, 1px) solid var(--ogx-border-color);
|
|
94
|
+
border-radius: var(--ogx-border-radius-md);
|
|
95
|
+
background: var(--ogx-color-white);
|
|
96
|
+
color: var(--ogx-color-gray-900);
|
|
97
|
+
font-family: var(--ogx-font-family);
|
|
98
|
+
font-size: inherit;
|
|
99
|
+
outline: none;
|
|
100
|
+
cursor: pointer;
|
|
101
|
+
}
|
|
102
|
+
.ogx__header-filter-select:hover {
|
|
103
|
+
border-color: var(--ogx-border-color-hover);
|
|
104
|
+
}
|
|
105
|
+
.ogx__header-filter-select:focus {
|
|
106
|
+
border-color: var(--ogx-color-primary);
|
|
107
|
+
box-shadow: 0 0 0 1px var(--ogx-color-primary);
|
|
108
|
+
}
|
|
109
|
+
.ogx__header-filter-cell--active .ogx-input-wrapper,
|
|
110
|
+
.ogx__header-filter-cell--active .ogx__header-filter-select {
|
|
111
|
+
border-color: var(--ogx-color-primary);
|
|
112
|
+
}
|
|
113
|
+
.ogx__header-filter-operator-label {
|
|
114
|
+
flex: 1 1 auto;
|
|
115
|
+
min-width: 0;
|
|
116
|
+
overflow: hidden;
|
|
117
|
+
text-overflow: ellipsis;
|
|
118
|
+
white-space: nowrap;
|
|
119
|
+
color: var(--ogx-color-gray-600);
|
|
120
|
+
font-style: italic;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
.ogx__header-filter-clear {
|
|
124
|
+
display: inline-flex;
|
|
125
|
+
align-items: center;
|
|
126
|
+
justify-content: center;
|
|
127
|
+
flex-shrink: 0;
|
|
128
|
+
width: 22px;
|
|
129
|
+
height: 22px;
|
|
130
|
+
padding: 0;
|
|
131
|
+
border: none;
|
|
132
|
+
border-radius: var(--ogx-border-radius-sm, 4px);
|
|
133
|
+
background: transparent;
|
|
134
|
+
color: var(--ogx-color-gray-500);
|
|
135
|
+
cursor: pointer;
|
|
136
|
+
}
|
|
137
|
+
.ogx__header-filter-clear:hover {
|
|
138
|
+
background: var(--ogx-grid-header-hover-background);
|
|
139
|
+
color: var(--ogx-grid-header-text);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/* Read-only label for a filter the row cannot show (OR logic, nested groups, other conditions). */
|
|
143
|
+
.ogx__header-filter-custom {
|
|
144
|
+
flex: 1 1 auto;
|
|
145
|
+
min-width: 0;
|
|
146
|
+
overflow: hidden;
|
|
147
|
+
text-overflow: ellipsis;
|
|
148
|
+
white-space: nowrap;
|
|
149
|
+
padding: 0 4px;
|
|
150
|
+
border: none;
|
|
151
|
+
background: transparent;
|
|
152
|
+
color: var(--ogx-color-primary);
|
|
153
|
+
font: inherit;
|
|
154
|
+
font-style: italic;
|
|
155
|
+
text-align: left;
|
|
156
|
+
}
|
|
157
|
+
button.ogx__header-filter-custom {
|
|
158
|
+
cursor: pointer;
|
|
159
|
+
text-decoration: underline dotted;
|
|
160
|
+
}
|