@opencorestack/opengridx 1.0.6 → 1.0.7
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/dist/opengridx.css +1 -1
- package/dist/opengridx.es.js +2687 -2673
- package/dist/opengridx.es.js.map +1 -1
- package/dist/opengridx.umd.js +11 -11
- package/dist/opengridx.umd.js.map +1 -1
- package/lib/components/ColumnVisibilityPanel/ColumnVisibilityPanel.tsx +0 -2
- package/lib/components/DataGrid/DataGrid.css +36 -0
- package/lib/components/DataGrid/DataGrid.tsx +4 -2
- package/lib/components/DataGrid/GridAggregationFooter.tsx +47 -9
- package/lib/components/DataGrid/GridStandaloneColumnPanel.tsx +3 -1
- package/lib/components/FilterPanel/FilterPanel.tsx +5 -4
- package/lib/components/Header/Header.tsx +4 -3
- package/lib/components/Pagination/Pagination.tsx +4 -3
- package/lib/components/Row/Row.tsx +3 -2
- package/lib/components/Toolbar/GlobalSearch.tsx +0 -2
- package/lib/components/Toolbar/GridToolbar.tsx +0 -4
- package/package.json +1 -1
|
@@ -109,8 +109,6 @@ export function ColumnVisibilityPanel<R extends GridRowModel = GridRowModel>(
|
|
|
109
109
|
<div className="ogx-column-visibility-panel__search-container">
|
|
110
110
|
<SearchIcon />
|
|
111
111
|
<input
|
|
112
|
-
id="ogx-column-search"
|
|
113
|
-
name="ogx-column-search"
|
|
114
112
|
autoComplete="off"
|
|
115
113
|
className="ogx-column-visibility-panel__search-input"
|
|
116
114
|
placeholder="Search"
|
|
@@ -177,6 +177,42 @@
|
|
|
177
177
|
overflow: hidden;
|
|
178
178
|
box-sizing: border-box;
|
|
179
179
|
border-right: 1px solid var(--ogx-grid-border-color);
|
|
180
|
+
position: relative;
|
|
181
|
+
}
|
|
182
|
+
.ogx__aggregation-cell--pinned-left,
|
|
183
|
+
.ogx__aggregation-cell--pinned-right {
|
|
184
|
+
position: sticky;
|
|
185
|
+
z-index: 3;
|
|
186
|
+
background: var(--ogx-grid-header-background);
|
|
187
|
+
}
|
|
188
|
+
.ogx__aggregation-cell--pinned-right {
|
|
189
|
+
border-left: 1px solid var(--ogx-grid-border-color);
|
|
190
|
+
}
|
|
191
|
+
.ogx__aggregation-cell--pinned-left-last {
|
|
192
|
+
overflow: visible;
|
|
193
|
+
}
|
|
194
|
+
.ogx__aggregation-cell--pinned-left-last::after {
|
|
195
|
+
content: '';
|
|
196
|
+
position: absolute;
|
|
197
|
+
top: 0;
|
|
198
|
+
right: -10px;
|
|
199
|
+
bottom: 0;
|
|
200
|
+
width: 10px;
|
|
201
|
+
pointer-events: none;
|
|
202
|
+
box-shadow: inset 10px 0 8px -8px rgba(0, 0, 0, 0.1);
|
|
203
|
+
}
|
|
204
|
+
.ogx__aggregation-cell--pinned-right-first {
|
|
205
|
+
overflow: visible;
|
|
206
|
+
}
|
|
207
|
+
.ogx__aggregation-cell--pinned-right-first::before {
|
|
208
|
+
content: '';
|
|
209
|
+
position: absolute;
|
|
210
|
+
top: 0;
|
|
211
|
+
left: -10px;
|
|
212
|
+
bottom: 0;
|
|
213
|
+
width: 10px;
|
|
214
|
+
pointer-events: none;
|
|
215
|
+
box-shadow: inset -10px 0 8px -8px rgba(0, 0, 0, 0.1);
|
|
180
216
|
}
|
|
181
217
|
.ogx__aggregation-label {
|
|
182
218
|
font-size: 0.7em;
|
|
@@ -685,6 +685,7 @@ export function DataGrid<R extends GridRowModel = GridRowModel>(props: DataGridP
|
|
|
685
685
|
const hasRowSpanning = React.useMemo(() => effectiveColumns.some(c => !!c.rowSpan), [effectiveColumns]);
|
|
686
686
|
const [columnsPanelOpen, setColumnsPanelOpen] = React.useState(false);
|
|
687
687
|
const containerRef = React.useRef<HTMLDivElement>(null);
|
|
688
|
+
const standalonePanelRef = React.useRef<HTMLDivElement>(null);
|
|
688
689
|
|
|
689
690
|
const toolbarProps = React.useMemo(() => {
|
|
690
691
|
if (!slots?.toolbar) return null;
|
|
@@ -735,8 +736,7 @@ export function DataGrid<R extends GridRowModel = GridRowModel>(props: DataGridP
|
|
|
735
736
|
useEffect(() => {
|
|
736
737
|
if (!columnsPanelOpen || slots?.toolbar) return;
|
|
737
738
|
function handleClickOutside(e: MouseEvent) {
|
|
738
|
-
|
|
739
|
-
if (panel && !panel.contains(e.target as Node)) {
|
|
739
|
+
if (standalonePanelRef.current && !standalonePanelRef.current.contains(e.target as Node)) {
|
|
740
740
|
setColumnsPanelOpen(false);
|
|
741
741
|
}
|
|
742
742
|
}
|
|
@@ -762,6 +762,7 @@ export function DataGrid<R extends GridRowModel = GridRowModel>(props: DataGridP
|
|
|
762
762
|
<GridStandaloneColumnPanel<R>
|
|
763
763
|
isOpen={columnsPanelOpen}
|
|
764
764
|
containerRef={containerRef}
|
|
765
|
+
panelRef={standalonePanelRef}
|
|
765
766
|
effectiveColumns={effectiveColumns}
|
|
766
767
|
columnVisibilityModel={columnVisibilityModel}
|
|
767
768
|
effectiveColumnOrder={effectiveColumnOrder}
|
|
@@ -995,6 +996,7 @@ export function DataGrid<R extends GridRowModel = GridRowModel>(props: DataGridP
|
|
|
995
996
|
checkboxSelection={checkboxSelection}
|
|
996
997
|
hasDetailPanel={hasDetailPanel}
|
|
997
998
|
rowReordering={rowReordering}
|
|
999
|
+
pinnedColumns={pinnedColumns}
|
|
998
1000
|
/>
|
|
999
1001
|
)}
|
|
1000
1002
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import React from 'react';
|
|
1
|
+
import React, { useMemo } from 'react';
|
|
2
2
|
import { formatAggregationValue } from '../../hooks/features/useAggregation';
|
|
3
|
-
import type { GridColDef, GridAggregationModel, GridAggregationResult } from '../../types';
|
|
3
|
+
import type { GridColDef, GridAggregationModel, GridAggregationResult, GridColumnPinning } from '../../types';
|
|
4
|
+
import { calculatePinnedPositions, isColumnPinned } from '../../utils/pinning';
|
|
4
5
|
|
|
5
6
|
interface GridAggregationFooterProps {
|
|
6
7
|
columns: GridColDef[];
|
|
@@ -11,6 +12,7 @@ interface GridAggregationFooterProps {
|
|
|
11
12
|
checkboxSelection: boolean;
|
|
12
13
|
hasDetailPanel: boolean;
|
|
13
14
|
rowReordering: boolean;
|
|
15
|
+
pinnedColumns?: GridColumnPinning;
|
|
14
16
|
}
|
|
15
17
|
|
|
16
18
|
export function GridAggregationFooter({
|
|
@@ -22,7 +24,28 @@ export function GridAggregationFooter({
|
|
|
22
24
|
checkboxSelection,
|
|
23
25
|
hasDetailPanel,
|
|
24
26
|
rowReordering,
|
|
27
|
+
pinnedColumns,
|
|
25
28
|
}: GridAggregationFooterProps) {
|
|
29
|
+
// Compute sticky left/right pixel offsets for each pinned column.
|
|
30
|
+
// The footer always renders checkbox/detail/reorder spacers unconditionally,
|
|
31
|
+
// so treat pinCheckboxColumn and pinExpandColumn as always true.
|
|
32
|
+
const pinnedOffsets = useMemo(
|
|
33
|
+
() => calculatePinnedPositions(
|
|
34
|
+
columns,
|
|
35
|
+
columnWidths,
|
|
36
|
+
pinnedColumns,
|
|
37
|
+
checkboxSelection,
|
|
38
|
+
true,
|
|
39
|
+
hasDetailPanel,
|
|
40
|
+
true,
|
|
41
|
+
rowReordering,
|
|
42
|
+
),
|
|
43
|
+
[columns, columnWidths, pinnedColumns, checkboxSelection, hasDetailPanel, rowReordering]
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
const lastLeftField = pinnedColumns?.left?.[pinnedColumns.left.length - 1];
|
|
47
|
+
const firstRightField = pinnedColumns?.right?.[0];
|
|
48
|
+
|
|
26
49
|
return (
|
|
27
50
|
<div
|
|
28
51
|
className="ogx__aggregation-footer"
|
|
@@ -40,17 +63,32 @@ export function GridAggregationFooter({
|
|
|
40
63
|
const rawValue = aggregationResult[col.field];
|
|
41
64
|
const colWidth = columnWidths[col.field] ?? (typeof col.width === 'number' ? col.width : 120);
|
|
42
65
|
|
|
66
|
+
const pinnedPosition = isColumnPinned(col.field, pinnedColumns);
|
|
67
|
+
const pinnedOffset = pinnedPosition ? pinnedOffsets[col.field] : undefined;
|
|
68
|
+
|
|
69
|
+
const className = [
|
|
70
|
+
'ogx__aggregation-cell',
|
|
71
|
+
pinnedPosition === 'left' && 'ogx__aggregation-cell--pinned-left',
|
|
72
|
+
pinnedPosition === 'right' && 'ogx__aggregation-cell--pinned-right',
|
|
73
|
+
pinnedPosition === 'left' && col.field === lastLeftField && 'ogx__aggregation-cell--pinned-left-last',
|
|
74
|
+
pinnedPosition === 'right' && col.field === firstRightField && 'ogx__aggregation-cell--pinned-right-first',
|
|
75
|
+
].filter(Boolean).join(' ');
|
|
76
|
+
|
|
77
|
+
const style: React.CSSProperties = {
|
|
78
|
+
width: colWidth,
|
|
79
|
+
minWidth: colWidth,
|
|
80
|
+
maxWidth: colWidth,
|
|
81
|
+
textAlign: (col.align as React.CSSProperties['textAlign']) || 'left',
|
|
82
|
+
};
|
|
83
|
+
if (pinnedPosition === 'left' && pinnedOffset !== undefined) style.left = pinnedOffset;
|
|
84
|
+
if (pinnedPosition === 'right' && pinnedOffset !== undefined) style.right = pinnedOffset;
|
|
85
|
+
|
|
43
86
|
return (
|
|
44
87
|
<div
|
|
45
88
|
key={col.field}
|
|
46
|
-
className=
|
|
89
|
+
className={className}
|
|
47
90
|
role="gridcell"
|
|
48
|
-
style={
|
|
49
|
-
width: colWidth,
|
|
50
|
-
minWidth: colWidth,
|
|
51
|
-
maxWidth: colWidth,
|
|
52
|
-
textAlign: (col.align as React.CSSProperties['textAlign']) || 'left',
|
|
53
|
-
}}
|
|
91
|
+
style={style}
|
|
54
92
|
>
|
|
55
93
|
{fnName ? (
|
|
56
94
|
<>
|
|
@@ -6,6 +6,7 @@ import type { GridColDef, GridRowModel, GridColumnOrderChangeParams } from '../.
|
|
|
6
6
|
export interface GridStandaloneColumnPanelProps<R extends GridRowModel> {
|
|
7
7
|
isOpen: boolean;
|
|
8
8
|
containerRef: React.RefObject<HTMLDivElement | null>;
|
|
9
|
+
panelRef?: React.RefObject<HTMLDivElement | null>;
|
|
9
10
|
effectiveColumns: GridColDef<R>[];
|
|
10
11
|
columnVisibilityModel: Record<string, boolean>;
|
|
11
12
|
effectiveColumnOrder: string[];
|
|
@@ -20,6 +21,7 @@ export interface GridStandaloneColumnPanelProps<R extends GridRowModel> {
|
|
|
20
21
|
export function GridStandaloneColumnPanel<R extends GridRowModel>({
|
|
21
22
|
isOpen,
|
|
22
23
|
containerRef,
|
|
24
|
+
panelRef,
|
|
23
25
|
effectiveColumns,
|
|
24
26
|
columnVisibilityModel,
|
|
25
27
|
effectiveColumnOrder,
|
|
@@ -64,7 +66,7 @@ export function GridStandaloneColumnPanel<R extends GridRowModel>({
|
|
|
64
66
|
|
|
65
67
|
return ReactDOM.createPortal(
|
|
66
68
|
<div
|
|
67
|
-
|
|
69
|
+
ref={panelRef}
|
|
68
70
|
style={{
|
|
69
71
|
position: 'fixed',
|
|
70
72
|
top: panelTop,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react';
|
|
1
|
+
import React, { useCallback, useEffect, useId, useLayoutEffect, useMemo, useRef, useState } from 'react';
|
|
2
2
|
import type {
|
|
3
3
|
GridFilterModel,
|
|
4
4
|
GridFilterItem,
|
|
@@ -25,6 +25,7 @@ const FilterRow: React.FC<{
|
|
|
25
25
|
item: GridFilterItem | undefined;
|
|
26
26
|
onChange: (item: GridFilterItem | null) => void;
|
|
27
27
|
}> = ({ col, item, onChange }) => {
|
|
28
|
+
const filterId = useId();
|
|
28
29
|
const operators = getOperatorsForType(col.type);
|
|
29
30
|
const isActive = !!item;
|
|
30
31
|
const currentOperator = item?.operator ?? operators[0];
|
|
@@ -99,7 +100,7 @@ const FilterRow: React.FC<{
|
|
|
99
100
|
<div className="ogx-filter__controls">
|
|
100
101
|
{ }
|
|
101
102
|
<select
|
|
102
|
-
id={
|
|
103
|
+
id={`${filterId}-op`}
|
|
103
104
|
name={`filter-op-${col.field}`}
|
|
104
105
|
className={`ogx-filter__op-select${isActive ? ' ogx-filter__op-select--active' : ''}`}
|
|
105
106
|
value={String(currentOperator)}
|
|
@@ -114,7 +115,7 @@ const FilterRow: React.FC<{
|
|
|
114
115
|
{ }
|
|
115
116
|
{showValue && isBoolean && (
|
|
116
117
|
<select
|
|
117
|
-
id={
|
|
118
|
+
id={`${filterId}-val`}
|
|
118
119
|
name={`filter-val-${col.field}`}
|
|
119
120
|
className={`ogx-filter__value-input ogx-filter__value-select${isActive ? ' ogx-filter__value-input--active' : ''}`}
|
|
120
121
|
value={String(item?.value ?? 'true')}
|
|
@@ -136,7 +137,7 @@ const FilterRow: React.FC<{
|
|
|
136
137
|
|
|
137
138
|
{showValue && !isBoolean && (
|
|
138
139
|
<input
|
|
139
|
-
id={
|
|
140
|
+
id={`${filterId}-val`}
|
|
140
141
|
name={`filter-val-${col.field}`}
|
|
141
142
|
className={`ogx-filter__value-input${isActive ? ' ogx-filter__value-input--active' : ''}`}
|
|
142
143
|
type="text"
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React from 'react';
|
|
1
|
+
import React, { useId } from 'react';
|
|
2
2
|
import { Checkbox } from '../ui/Checkbox';
|
|
3
3
|
import { ColumnResizeHandle } from '../ColumnResizeHandle/ColumnResizeHandle';
|
|
4
4
|
import type { GridColDef, GridRowModel, GridSortDirection, GridColumnPinning, GridAggregationModel, GridColumnGroup, GridColumnGroupingModel } from '../../types';
|
|
@@ -171,6 +171,7 @@ export function Header<R extends GridRowModel = GridRowModel>(props: HeaderProps
|
|
|
171
171
|
onManageColumns,
|
|
172
172
|
} = props;
|
|
173
173
|
|
|
174
|
+
const selectAllId = useId();
|
|
174
175
|
const cellRefs = React.useRef<Record<string, HTMLElement | null>>({});
|
|
175
176
|
const [menuOpenParams, setMenuOpenParams] = React.useState<{ colDef: GridColDef<R>; anchorEl: HTMLElement } | null>(null);
|
|
176
177
|
|
|
@@ -340,8 +341,8 @@ export function Header<R extends GridRowModel = GridRowModel>(props: HeaderProps
|
|
|
340
341
|
}}
|
|
341
342
|
>
|
|
342
343
|
<Checkbox
|
|
343
|
-
id=
|
|
344
|
-
name=
|
|
344
|
+
id={selectAllId}
|
|
345
|
+
name={selectAllId}
|
|
345
346
|
checked={allSelected}
|
|
346
347
|
indeterminate={someSelected}
|
|
347
348
|
onChange={(e) => onSelectAll?.(e.target.checked)}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
|
|
2
|
-
import React from 'react';
|
|
2
|
+
import React, { useId } from 'react';
|
|
3
3
|
import { Button } from '../ui/Button';
|
|
4
4
|
|
|
5
5
|
export interface PaginationProps {
|
|
@@ -14,6 +14,7 @@ export interface PaginationProps {
|
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
export function Pagination(props: PaginationProps) {
|
|
17
|
+
const pageSizeId = useId();
|
|
17
18
|
const {
|
|
18
19
|
page,
|
|
19
20
|
pageSize,
|
|
@@ -61,11 +62,11 @@ export function Pagination(props: PaginationProps) {
|
|
|
61
62
|
<div className="ogx-pagination" role="navigation" aria-label="Pagination">
|
|
62
63
|
{ }
|
|
63
64
|
<div className="ogx-pagination__page-size">
|
|
64
|
-
<label htmlFor=
|
|
65
|
+
<label htmlFor={pageSizeId} className="ogx-pagination__label">
|
|
65
66
|
Rows per page:
|
|
66
67
|
</label>
|
|
67
68
|
<select
|
|
68
|
-
id=
|
|
69
|
+
id={pageSizeId}
|
|
69
70
|
className="ogx-pagination__select"
|
|
70
71
|
value={pageSize}
|
|
71
72
|
onChange={handlePageSizeChange}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
|
|
2
|
-
import React from 'react';
|
|
2
|
+
import React, { useId } from 'react';
|
|
3
3
|
import { Cell } from '../Cell/Cell';
|
|
4
4
|
import { Checkbox } from '../ui/Checkbox';
|
|
5
5
|
import { ExpandIcon } from '../ui/ExpandIcon';
|
|
@@ -94,6 +94,7 @@ export function Row<R extends GridRowModel = GridRowModel>(props: RowProps<R>) {
|
|
|
94
94
|
rowHeight = 52
|
|
95
95
|
} = props;
|
|
96
96
|
|
|
97
|
+
const checkboxId = useId();
|
|
97
98
|
const expandCellRef = React.useRef<HTMLDivElement>(null);
|
|
98
99
|
const checkboxCellRef = React.useRef<HTMLDivElement>(null);
|
|
99
100
|
|
|
@@ -291,7 +292,7 @@ export function Row<R extends GridRowModel = GridRowModel>(props: RowProps<R>) {
|
|
|
291
292
|
}}
|
|
292
293
|
>
|
|
293
294
|
<Checkbox
|
|
294
|
-
id={
|
|
295
|
+
id={checkboxId}
|
|
295
296
|
name={`ogx-select-row-${row.id}`}
|
|
296
297
|
checked={isSelected}
|
|
297
298
|
onChange={handleCheckboxChange}
|
|
@@ -146,8 +146,6 @@ export function GlobalSearch({ value = '', onChange, placeholder = 'Search...',
|
|
|
146
146
|
</div>
|
|
147
147
|
<input
|
|
148
148
|
ref={inputRef}
|
|
149
|
-
id="ogx-global-search-input"
|
|
150
|
-
name="ogx-global-search"
|
|
151
149
|
className="ogx-global-search__input"
|
|
152
150
|
value={localValue}
|
|
153
151
|
onChange={(e) => setLocalValue(e.target.value)}
|
|
@@ -684,11 +684,9 @@ export function GridToolbar({
|
|
|
684
684
|
<GridTooltip title="Pivot">
|
|
685
685
|
<button
|
|
686
686
|
ref={pivotButtonRef}
|
|
687
|
-
id="ogx-pivot-btn"
|
|
688
687
|
className={`ogx-toolbar__icon-btn${pivotOpen ? ' ogx-toolbar__icon-btn--active' : ''}`}
|
|
689
688
|
aria-label="Configure pivot"
|
|
690
689
|
aria-expanded={pivotOpen}
|
|
691
|
-
aria-controls="ogx-pivot-panel"
|
|
692
690
|
onClick={() => {
|
|
693
691
|
setPivotOpen(!pivotOpen);
|
|
694
692
|
setAggOpen(false);
|
|
@@ -727,11 +725,9 @@ export function GridToolbar({
|
|
|
727
725
|
<GridTooltip title="Summaries">
|
|
728
726
|
<button
|
|
729
727
|
ref={aggButtonRef}
|
|
730
|
-
id="ogx-aggregation-btn"
|
|
731
728
|
className={`ogx-toolbar__icon-btn${aggOpen ? ' ogx-toolbar__icon-btn--active' : ''}`}
|
|
732
729
|
aria-label="Configure summaries"
|
|
733
730
|
aria-expanded={aggOpen}
|
|
734
|
-
aria-controls="ogx-aggregation-panel"
|
|
735
731
|
onClick={() => {
|
|
736
732
|
setAggOpen(!aggOpen);
|
|
737
733
|
setPivotOpen(false);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@opencorestack/opengridx",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.7",
|
|
4
4
|
"description": "OpenGridX: High-performance React data infrastructure. Unlock advanced Row Grouping, Excel Export, and Column Pinning without the usual \"Pro\" gatekeeping. Built for speed, scale, and complete architectural freedom. Fully open, virtualization-ready, and feature-complete.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/opengridx.umd.js",
|