@opencorestack/opengridx 3.8.1 → 3.9.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 +41 -0
- package/README.md +11 -7
- package/dist/index.d.ts +340 -1
- package/dist/opengridx.css +1 -1
- package/dist/opengridx.es.js +7529 -7285
- 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 +93 -4
- package/docs/architecture/datagrid-orchestration.md +4 -2
- package/docs/architecture/use-grid-virtualization.md +2 -0
- package/docs/components/pagination.md +1 -1
- package/docs/customization/theming.md +11 -2
- package/docs/features/ai-toolkit.md +1 -1
- package/docs/features/rtl.md +1 -1
- package/docs/performance.md +19 -1
- package/docs/roadmap.md +26 -5
- package/lib/components/AiAssistant/GridAiAssistantPanel.tsx +16 -14
- package/lib/components/Cell/CellErrorBoundary.tsx +17 -10
- package/lib/components/CellSelectionStats/CellSelectionStats.tsx +5 -3
- package/lib/components/ColumnResizeHandle/ColumnResizeHandle.tsx +3 -1
- package/lib/components/ColumnVisibilityPanel/ColumnVisibilityPanel.tsx +6 -4
- package/lib/components/DataGrid/GridAggregationFooter.tsx +3 -1
- package/lib/components/DataGrid/GridErrorOverlay.tsx +4 -2
- package/lib/components/DataGrid/GridListView.tsx +7 -5
- package/lib/components/DataGrid/GridLiveRegion.tsx +5 -3
- package/lib/components/DataGrid/GridLoadingOverlay.tsx +3 -1
- package/lib/components/DataGrid/GridPinnedRows.tsx +1 -1
- package/lib/components/DataGrid/GridRoot.tsx +8 -1
- package/lib/components/DataGrid/GridStandaloneColumnPanel.tsx +3 -1
- package/lib/components/DataGrid/GridViewport.tsx +3 -1
- package/lib/components/DataGrid/GridVirtualRows.tsx +1 -1
- package/lib/components/DataGrid/gridLocaleContext.ts +13 -0
- package/lib/components/FilterPanel/FilterPanel.tsx +18 -17
- package/lib/components/Header/ColumnMenu.css +5 -5
- package/lib/components/Header/ColumnMenu.tsx +17 -15
- package/lib/components/Header/Header.tsx +8 -6
- package/lib/components/HeaderFilter/HeaderFilterRow.tsx +3 -1
- package/lib/components/ListView/ListViewRow.tsx +3 -1
- package/lib/components/Pagination/Pagination.tsx +15 -18
- package/lib/components/QuickFilter/QuickFilter.tsx +5 -2
- package/lib/components/Row/Row.tsx +5 -3
- package/lib/components/Toolbar/GlobalSearch.tsx +6 -3
- package/lib/components/Toolbar/GridToolbar.tsx +35 -29
- package/lib/components/Toolbar/PivotPanel.tsx +26 -24
- package/lib/components/ui/Checkbox.css +5 -5
- package/lib/components/ui/DragHandleIcon.tsx +3 -1
- package/lib/components/ui/ExpandIcon.css +3 -3
- package/lib/components/ui/ExpandIcon.tsx +3 -1
- package/lib/hooks/core/useGridRowInteractions.ts +13 -3
- package/lib/hooks/core/useGridToolbarStack.ts +2 -2
- package/lib/hooks/core/useGridVirtualization.ts +40 -25
- package/lib/hooks/core/useShallowStableValue.ts +29 -0
- package/lib/hooks/features/useGridAiAssistant.ts +52 -22
- package/lib/theme/DataGridThemeProvider.tsx +9 -0
- package/lib/theme/types.ts +18 -0
- package/lib/types/locale.ts +344 -0
- package/lib/utils/gridLocale.ts +241 -0
- package/llms.txt +3 -1
- package/package.json +4 -2
- package/lib/theme/__baseline__/default-theme.darwin.chromium.json +0 -1
- package/lib/theme/__baseline__/default-theme.darwin.firefox.json +0 -1
- package/lib/theme/__baseline__/default-theme.darwin.webkit.json +0 -1
|
@@ -3,6 +3,7 @@ import React, { useId } from 'react';
|
|
|
3
3
|
import { Button } from '../ui/Button';
|
|
4
4
|
import { normalizePageSize, getPageCount, clampPage } from '../../utils/pagination';
|
|
5
5
|
import type { GridLocaleText } from '../../types';
|
|
6
|
+
import { useGridLocale } from '../DataGrid/gridLocaleContext';
|
|
6
7
|
|
|
7
8
|
export interface PaginationProps {
|
|
8
9
|
page: number;
|
|
@@ -18,6 +19,7 @@ export interface PaginationProps {
|
|
|
18
19
|
|
|
19
20
|
export function Pagination(props: PaginationProps) {
|
|
20
21
|
const pageSizeId = useId();
|
|
22
|
+
const locale = useGridLocale();
|
|
21
23
|
const {
|
|
22
24
|
page,
|
|
23
25
|
pageSize,
|
|
@@ -43,15 +45,10 @@ export function Pagination(props: PaginationProps) {
|
|
|
43
45
|
|
|
44
46
|
const firstRowIndex = currentPage * safePageSize;
|
|
45
47
|
const lastRowIndex = Math.min(firstRowIndex + safePageSize, rowCount);
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
const
|
|
49
|
-
const
|
|
50
|
-
? localeText.paginationOf(rowCount === 0 ? 0 : firstRowIndex + 1, lastRowIndex, rowCount)
|
|
51
|
-
: `${displayedRows} of ${rowCount}`;
|
|
52
|
-
const pageLabel = localeText?.paginationPage
|
|
53
|
-
? localeText.paginationPage(currentPage + 1, pageCount)
|
|
54
|
-
: `Page ${currentPage + 1} of ${pageCount}`;
|
|
48
|
+
// An explicit `localeText` prop (standalone use) wins over the grid's strings.
|
|
49
|
+
const rowsPerPageLabel = localeText?.paginationRowsPerPage ?? locale.paginationRowsPerPage;
|
|
50
|
+
const ofLabel = (localeText?.paginationOf ?? locale.paginationOf)(rowCount === 0 ? 0 : firstRowIndex + 1, lastRowIndex, rowCount);
|
|
51
|
+
const pageLabel = (localeText?.paginationPage ?? locale.paginationPage)(currentPage + 1, pageCount);
|
|
55
52
|
|
|
56
53
|
const handleFirstPage = () => {
|
|
57
54
|
onPageChange(0);
|
|
@@ -79,7 +76,7 @@ export function Pagination(props: PaginationProps) {
|
|
|
79
76
|
const isLastPage = currentPage >= pageCount - 1;
|
|
80
77
|
|
|
81
78
|
return (
|
|
82
|
-
<div className="ogx-pagination" role="navigation" aria-label=
|
|
79
|
+
<div className="ogx-pagination" role="navigation" aria-label={locale.paginationLabel}>
|
|
83
80
|
{ }
|
|
84
81
|
<div className="ogx-pagination__page-size">
|
|
85
82
|
<label htmlFor={pageSizeId} className="ogx-pagination__label">
|
|
@@ -112,8 +109,8 @@ export function Pagination(props: PaginationProps) {
|
|
|
112
109
|
size="small"
|
|
113
110
|
onClick={handleFirstPage}
|
|
114
111
|
disabled={isFirstPage}
|
|
115
|
-
aria-label=
|
|
116
|
-
title=
|
|
112
|
+
aria-label={locale.paginationFirstPageLabel}
|
|
113
|
+
title={locale.paginationFirstPageTooltip}
|
|
117
114
|
>
|
|
118
115
|
<svg className="ogx-pagination__icon" width="20" height="20" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
|
|
119
116
|
<path d="M18.41 16.59L13.82 12l4.59-4.59L17 6l-6 6 6 6zM6 6h2v12H6z" />
|
|
@@ -126,8 +123,8 @@ export function Pagination(props: PaginationProps) {
|
|
|
126
123
|
size="small"
|
|
127
124
|
onClick={handlePreviousPage}
|
|
128
125
|
disabled={isFirstPage}
|
|
129
|
-
aria-label=
|
|
130
|
-
title=
|
|
126
|
+
aria-label={locale.paginationPreviousPageLabel}
|
|
127
|
+
title={locale.paginationPreviousPageTooltip}
|
|
131
128
|
>
|
|
132
129
|
<svg className="ogx-pagination__icon" width="20" height="20" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
|
|
133
130
|
<path d="M15.41 7.41L14 6l-6 6 6 6 1.41-1.41L10.83 12z" />
|
|
@@ -143,8 +140,8 @@ export function Pagination(props: PaginationProps) {
|
|
|
143
140
|
size="small"
|
|
144
141
|
onClick={handleNextPage}
|
|
145
142
|
disabled={isLastPage}
|
|
146
|
-
aria-label=
|
|
147
|
-
title=
|
|
143
|
+
aria-label={locale.paginationNextPageLabel}
|
|
144
|
+
title={locale.paginationNextPageTooltip}
|
|
148
145
|
>
|
|
149
146
|
<svg className="ogx-pagination__icon" width="20" height="20" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
|
|
150
147
|
<path d="M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z" />
|
|
@@ -157,8 +154,8 @@ export function Pagination(props: PaginationProps) {
|
|
|
157
154
|
size="small"
|
|
158
155
|
onClick={handleLastPage}
|
|
159
156
|
disabled={isLastPage}
|
|
160
|
-
aria-label=
|
|
161
|
-
title=
|
|
157
|
+
aria-label={locale.paginationLastPageLabel}
|
|
158
|
+
title={locale.paginationLastPageTooltip}
|
|
162
159
|
>
|
|
163
160
|
<svg className="ogx-pagination__icon" width="20" height="20" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
|
|
164
161
|
<path d="M5.59 7.41L10.18 12l-4.59 4.59L7 18l6-6-6-6zM16 6h2v12h-2z" />
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
|
|
2
2
|
import React, { useState, useCallback } from 'react';
|
|
3
3
|
import { Input } from '../ui/Input';
|
|
4
|
+
import { useGridLocale } from '../DataGrid/gridLocaleContext';
|
|
4
5
|
|
|
5
6
|
export interface QuickFilterProps {
|
|
6
7
|
value?: string;
|
|
@@ -13,9 +14,11 @@ export function QuickFilter(props: QuickFilterProps) {
|
|
|
13
14
|
const {
|
|
14
15
|
value = '',
|
|
15
16
|
onChange,
|
|
16
|
-
placeholder
|
|
17
|
+
placeholder: placeholderProp,
|
|
17
18
|
debounceMs = 300
|
|
18
19
|
} = props;
|
|
20
|
+
const locale = useGridLocale();
|
|
21
|
+
const placeholder = placeholderProp ?? locale.toolbarSearchPlaceholder;
|
|
19
22
|
|
|
20
23
|
const [localValue, setLocalValue] = useState(value);
|
|
21
24
|
const [timeoutId, setTimeoutId] = useState<NodeJS.Timeout | null>(null);
|
|
@@ -55,7 +58,7 @@ export function QuickFilter(props: QuickFilterProps) {
|
|
|
55
58
|
<button
|
|
56
59
|
className="ogx-quick-filter__clear"
|
|
57
60
|
onClick={handleClear}
|
|
58
|
-
aria-label=
|
|
61
|
+
aria-label={locale.toolbarSearchClear}
|
|
59
62
|
type="button"
|
|
60
63
|
>
|
|
61
64
|
<svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor">
|
|
@@ -5,6 +5,7 @@ import { Checkbox } from '../ui/Checkbox';
|
|
|
5
5
|
import { ExpandIcon } from '../ui/ExpandIcon';
|
|
6
6
|
import { DragHandleIcon } from '../ui/DragHandleIcon';
|
|
7
7
|
import { DetailPanel } from '../DetailPanel/DetailPanel';
|
|
8
|
+
import { useGridLocale } from '../DataGrid/gridLocaleContext';
|
|
8
9
|
import type { GridRowModel, GridColDef, GridRowId, GridColumnPinning, GridRowPinning, GridRowParams, GridCellParams, GridDetailPanelHeight, GridRowMeta } from '../../types';
|
|
9
10
|
import type { CellColSpanInfo, RowSpanningCaches } from '../../hooks/features/useGridSpanning';
|
|
10
11
|
import { isColumnPinned, calculatePinnedPositions, getPinnedEdgeFields, isRowPinned } from '../../utils/pinning';
|
|
@@ -108,6 +109,7 @@ export interface RowProps<R extends GridRowModel = GridRowModel> {
|
|
|
108
109
|
}
|
|
109
110
|
|
|
110
111
|
function RowImpl<R extends GridRowModel = GridRowModel>(props: RowProps<R>) {
|
|
112
|
+
const locale = useGridLocale();
|
|
111
113
|
const {
|
|
112
114
|
row,
|
|
113
115
|
skeleton = false,
|
|
@@ -297,7 +299,7 @@ function RowImpl<R extends GridRowModel = GridRowModel>(props: RowProps<R>) {
|
|
|
297
299
|
<div
|
|
298
300
|
className={`ogx__cell ogx__cell--drag-handle ${focusedCellField === '__reorder_col__' ? 'ogx__cell--focused' : ''} ${(focusedCellField === '__reorder_col__' && isFocusVisible) ? 'ogx__cell--focus-visible' : ''}`}
|
|
299
301
|
role="gridcell"
|
|
300
|
-
aria-label={onDragStart ?
|
|
302
|
+
aria-label={onDragStart ? locale.rowReorderLabel : undefined}
|
|
301
303
|
aria-colindex={reorderColIndex}
|
|
302
304
|
data-field="__reorder_col__"
|
|
303
305
|
tabIndex={-1}
|
|
@@ -322,7 +324,7 @@ function RowImpl<R extends GridRowModel = GridRowModel>(props: RowProps<R>) {
|
|
|
322
324
|
} ${focusedCellField === '__expand_col__' ? 'ogx__cell--focused' : ''} ${(focusedCellField === '__expand_col__' && isFocusVisible) ? 'ogx__cell--focus-visible' : ''
|
|
323
325
|
}`}
|
|
324
326
|
role="gridcell"
|
|
325
|
-
aria-label={showDetailPanel ? (isDetailPanelExpanded ?
|
|
327
|
+
aria-label={showDetailPanel ? (isDetailPanelExpanded ? locale.rowDetailPanelCollapseLabel : locale.rowDetailPanelExpandLabel) : undefined}
|
|
326
328
|
aria-expanded={showDetailPanel ? isDetailPanelExpanded : undefined}
|
|
327
329
|
aria-controls={showDetailPanel && isDetailPanelExpanded ? detailPanelId : undefined}
|
|
328
330
|
aria-colindex={expandColIndex}
|
|
@@ -390,7 +392,7 @@ function RowImpl<R extends GridRowModel = GridRowModel>(props: RowProps<R>) {
|
|
|
390
392
|
checked={isSelected}
|
|
391
393
|
onChange={handleCheckboxChange}
|
|
392
394
|
onClick={(e) => e.stopPropagation()}
|
|
393
|
-
aria-label={isSelected ?
|
|
395
|
+
aria-label={isSelected ? locale.rowDeselectLabel(id) : locale.rowSelectLabel(id)}
|
|
394
396
|
tabIndex={-1}
|
|
395
397
|
onMouseDown={(e) => e.preventDefault()}
|
|
396
398
|
/>
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import React, { useState, useEffect, useRef, useCallback, useLayoutEffect } from 'react';
|
|
2
2
|
import { Input } from '../ui/Input';
|
|
3
|
+
import { useGridLocale } from '../DataGrid/gridLocaleContext';
|
|
3
4
|
|
|
4
5
|
|
|
5
6
|
interface GlobalSearchProps {
|
|
@@ -32,7 +33,9 @@ function normalizeSearch(text: string): string {
|
|
|
32
33
|
return text.trim().split(/\s+/).filter(Boolean).join(' ');
|
|
33
34
|
}
|
|
34
35
|
|
|
35
|
-
export function GlobalSearch({ value = '', onChange, placeholder
|
|
36
|
+
export function GlobalSearch({ value = '', onChange, placeholder: placeholderProp, debounceMs = 250 }: GlobalSearchProps) {
|
|
37
|
+
const locale = useGridLocale();
|
|
38
|
+
const placeholder = placeholderProp ?? locale.toolbarSearchPlaceholder;
|
|
36
39
|
const [isExpanded, setIsExpanded] = useState(() => value.length > 0);
|
|
37
40
|
// Fully uncontrolled: localValue is the single source of truth.
|
|
38
41
|
const [localValue, setLocalValue] = useState(value);
|
|
@@ -152,11 +155,11 @@ export function GlobalSearch({ value = '', onChange, placeholder = 'Search...',
|
|
|
152
155
|
onFocus={handleFocus}
|
|
153
156
|
onBlur={handleBlur}
|
|
154
157
|
placeholder={placeholder}
|
|
155
|
-
aria-label=
|
|
158
|
+
aria-label={locale.toolbarSearchLabel}
|
|
156
159
|
autoComplete="off"
|
|
157
160
|
/>
|
|
158
161
|
{localValue && (
|
|
159
|
-
<button type="button" className="ogx-global-search__clear" onClick={handleClear} aria-label=
|
|
162
|
+
<button type="button" className="ogx-global-search__clear" onClick={handleClear} aria-label={locale.toolbarSearchClear}>
|
|
160
163
|
<CloseIcon />
|
|
161
164
|
</button>
|
|
162
165
|
)}
|
|
@@ -12,6 +12,7 @@ import { ColumnVisibilityPanel } from '../ColumnVisibilityPanel/ColumnVisibility
|
|
|
12
12
|
import { FilterPanel } from '../FilterPanel/FilterPanel';
|
|
13
13
|
import { GridTooltip } from '../Tooltip/Tooltip';
|
|
14
14
|
import { AiSparkleIcon } from '../AiAssistant/GridAiAssistantPanel';
|
|
15
|
+
import { useGridLocale } from '../DataGrid/gridLocaleContext';
|
|
15
16
|
|
|
16
17
|
/** Props passed to a custom toolbar button renderer. */
|
|
17
18
|
export interface ToolbarButtonRenderProps {
|
|
@@ -169,6 +170,7 @@ function AggregationPanel({
|
|
|
169
170
|
onClose: () => void;
|
|
170
171
|
activeCount: number;
|
|
171
172
|
}) {
|
|
173
|
+
const locale = useGridLocale();
|
|
172
174
|
const panelRef = useRef<HTMLDivElement>(null);
|
|
173
175
|
const [pos, setPos] = useState<PanelPosition>({ top: 0, right: 0 });
|
|
174
176
|
const [portalContainer, setPortalContainer] = useState<Element | null>(null);
|
|
@@ -214,22 +216,22 @@ function AggregationPanel({
|
|
|
214
216
|
ref={panelRef}
|
|
215
217
|
className="ogx-toolbar__panel"
|
|
216
218
|
role="dialog"
|
|
217
|
-
aria-label=
|
|
219
|
+
aria-label={locale.toolbarSummariesPanelLabel}
|
|
218
220
|
style={{ top: pos.top, right: pos.right, left: pos.left, width: 400, maxWidth: 'calc(100vw - 24px)' }}
|
|
219
221
|
>
|
|
220
222
|
<div className="ogx-toolbar__panel-header">
|
|
221
223
|
<span className="ogx-toolbar__panel-title">
|
|
222
224
|
<SigmaIcon />
|
|
223
|
-
|
|
225
|
+
{locale.toolbarSummaries}
|
|
224
226
|
</span>
|
|
225
227
|
{activeCount > 0 && (
|
|
226
228
|
<button
|
|
227
229
|
type="button"
|
|
228
230
|
className="ogx-toolbar__clear-btn"
|
|
229
231
|
onClick={onClearAll}
|
|
230
|
-
title=
|
|
232
|
+
title={locale.toolbarSummariesClearAllTooltip}
|
|
231
233
|
>
|
|
232
|
-
|
|
234
|
+
{locale.toolbarSummariesClearAll}
|
|
233
235
|
</button>
|
|
234
236
|
)}
|
|
235
237
|
</div>
|
|
@@ -237,8 +239,8 @@ function AggregationPanel({
|
|
|
237
239
|
<div className="ogx-toolbar__panel-body">
|
|
238
240
|
{aggregableColumns.length === 0 ? (
|
|
239
241
|
<p className="ogx-toolbar__empty">
|
|
240
|
-
|
|
241
|
-
|
|
242
|
+
{locale.toolbarSummariesEmpty}<br />
|
|
243
|
+
{`${locale.toolbarSummariesEmptyHintBefore} `}<code>aggregable: true</code>{` ${locale.toolbarSummariesEmptyHintAfter}`}
|
|
242
244
|
</p>
|
|
243
245
|
) : (
|
|
244
246
|
aggregableColumns.map((col) => {
|
|
@@ -257,7 +259,7 @@ function AggregationPanel({
|
|
|
257
259
|
onClick={() => onFunctionChange(col.field, fn)}
|
|
258
260
|
aria-pressed={currentFn === fn}
|
|
259
261
|
>
|
|
260
|
-
{fn}
|
|
262
|
+
{locale.toolbarAggregationFunction(fn)}
|
|
261
263
|
</button>
|
|
262
264
|
))}
|
|
263
265
|
</div>
|
|
@@ -295,6 +297,7 @@ function ColumnsPanelWrapper({
|
|
|
295
297
|
onClose: () => void;
|
|
296
298
|
showNonHideableColumns?: boolean;
|
|
297
299
|
}) {
|
|
300
|
+
const locale = useGridLocale();
|
|
298
301
|
const panelRef = useRef<HTMLDivElement>(null);
|
|
299
302
|
const [pos, setPos] = useState<PanelPosition>({ top: 0, right: 0 });
|
|
300
303
|
const [portalContainer, setPortalContainer] = useState<Element | null>(null);
|
|
@@ -340,7 +343,7 @@ function ColumnsPanelWrapper({
|
|
|
340
343
|
ref={panelRef}
|
|
341
344
|
className="ogx-toolbar__panel"
|
|
342
345
|
role="dialog"
|
|
343
|
-
aria-label=
|
|
346
|
+
aria-label={locale.toolbarColumnsPanelLabel}
|
|
344
347
|
style={{ top: pos.top, right: pos.right, left: pos.left, width: 280, padding: 0 }}
|
|
345
348
|
>
|
|
346
349
|
<ColumnVisibilityPanel
|
|
@@ -372,6 +375,7 @@ function FilterPanelWrapper({
|
|
|
372
375
|
onFilterModelChange: (model: GridFilterModel) => void;
|
|
373
376
|
onClose: () => void;
|
|
374
377
|
}) {
|
|
378
|
+
const locale = useGridLocale();
|
|
375
379
|
const panelRef = useRef<HTMLDivElement>(null);
|
|
376
380
|
const [pos, setPos] = useState<PanelPosition>({ top: 0, right: 0 });
|
|
377
381
|
// Stable ref for onClose — prevents the mousedown effect from
|
|
@@ -421,13 +425,13 @@ function FilterPanelWrapper({
|
|
|
421
425
|
ref={panelRef}
|
|
422
426
|
className="ogx-toolbar__panel ogx-toolbar__panel--filter"
|
|
423
427
|
role="dialog"
|
|
424
|
-
aria-label=
|
|
428
|
+
aria-label={locale.toolbarFiltersLabel}
|
|
425
429
|
style={{ top: pos.top, right: pos.right, left: pos.left, width: 540, maxWidth: 'calc(100vw - 24px)' }}
|
|
426
430
|
>
|
|
427
431
|
<div className="ogx-toolbar__panel-header">
|
|
428
432
|
<span className="ogx-toolbar__panel-title">
|
|
429
433
|
<FilterIcon />
|
|
430
|
-
|
|
434
|
+
{locale.toolbarFilters}
|
|
431
435
|
{activeFilterCount > 0 && (
|
|
432
436
|
<span className="ogx-toolbar__panel-count">{activeFilterCount}</span>
|
|
433
437
|
)}
|
|
@@ -438,19 +442,19 @@ function FilterPanelWrapper({
|
|
|
438
442
|
type="button"
|
|
439
443
|
className="ogx-toolbar__clear-btn"
|
|
440
444
|
onClick={handleClearAll}
|
|
441
|
-
title=
|
|
445
|
+
title={locale.toolbarFiltersClearAllTooltip}
|
|
442
446
|
>
|
|
443
|
-
|
|
447
|
+
{locale.toolbarFiltersClearAll}
|
|
444
448
|
</button>
|
|
445
449
|
)}
|
|
446
450
|
<button
|
|
447
451
|
type="button"
|
|
448
452
|
className="ogx-toolbar__close-btn"
|
|
449
453
|
onClick={() => onCloseRef.current()}
|
|
450
|
-
title=
|
|
451
|
-
aria-label=
|
|
454
|
+
title={locale.toolbarFiltersCloseTooltip}
|
|
455
|
+
aria-label={locale.toolbarFiltersCloseLabel}
|
|
452
456
|
>
|
|
453
|
-
|
|
457
|
+
{locale.toolbarFiltersClose}
|
|
454
458
|
</button>
|
|
455
459
|
</div>
|
|
456
460
|
</div>
|
|
@@ -501,8 +505,10 @@ export function GridToolbar({
|
|
|
501
505
|
showNonHideableColumns,
|
|
502
506
|
onAiAssistantToggle,
|
|
503
507
|
aiAssistantOpen = false,
|
|
504
|
-
aiAssistantLabel
|
|
508
|
+
aiAssistantLabel: aiAssistantLabelProp,
|
|
505
509
|
}: GridToolbarProps) {
|
|
510
|
+
const locale = useGridLocale();
|
|
511
|
+
const aiAssistantLabel = aiAssistantLabelProp ?? locale.aiAssistantButton;
|
|
506
512
|
// At most one panel is open. Every way the Columns panel closes (its button, a custom
|
|
507
513
|
// button, another panel opening, click-outside, Escape) reports onColumnsPanelClose, so the
|
|
508
514
|
// grid can ask for it again with forceColumnsOpen.
|
|
@@ -646,7 +652,7 @@ export function GridToolbar({
|
|
|
646
652
|
}, [columns, columnVisibilityModel, onColumnVisibilityModelChange]);
|
|
647
653
|
|
|
648
654
|
return (
|
|
649
|
-
<div className={['ogx-toolbar', className].filter(Boolean).join(' ')} role="toolbar" aria-label=
|
|
655
|
+
<div className={['ogx-toolbar', className].filter(Boolean).join(' ')} role="toolbar" aria-label={locale.toolbarLabel} style={style}>
|
|
650
656
|
{/* Left slot — custom content */}
|
|
651
657
|
{children && <div className="ogx-toolbar__left">{children}</div>}
|
|
652
658
|
|
|
@@ -681,7 +687,7 @@ export function GridToolbar({
|
|
|
681
687
|
<GlobalSearch
|
|
682
688
|
value={searchValue}
|
|
683
689
|
onChange={handleSearchChange}
|
|
684
|
-
placeholder=
|
|
690
|
+
placeholder={locale.toolbarSearchPlaceholder}
|
|
685
691
|
/>
|
|
686
692
|
)
|
|
687
693
|
}
|
|
@@ -698,12 +704,12 @@ export function GridToolbar({
|
|
|
698
704
|
activeCount: columns.length - visibleColumns.size,
|
|
699
705
|
})
|
|
700
706
|
: (
|
|
701
|
-
<GridTooltip title=
|
|
707
|
+
<GridTooltip title={locale.toolbarColumns}>
|
|
702
708
|
<button
|
|
703
709
|
type="button"
|
|
704
710
|
ref={colsButtonRef}
|
|
705
711
|
className={`ogx-toolbar__icon-btn${colsOpen ? ' ogx-toolbar__icon-btn--active' : ''}`}
|
|
706
|
-
aria-label=
|
|
712
|
+
aria-label={locale.toolbarColumnsLabel}
|
|
707
713
|
aria-haspopup="dialog"
|
|
708
714
|
aria-expanded={colsOpen}
|
|
709
715
|
onClick={() => togglePanel('columns')}
|
|
@@ -740,19 +746,19 @@ export function GridToolbar({
|
|
|
740
746
|
activeCount: activeFilterCount,
|
|
741
747
|
})
|
|
742
748
|
: (
|
|
743
|
-
<GridTooltip title=
|
|
749
|
+
<GridTooltip title={locale.toolbarFilters}>
|
|
744
750
|
<button
|
|
745
751
|
type="button"
|
|
746
752
|
ref={filterButtonRef}
|
|
747
753
|
className={`ogx-toolbar__icon-btn${filterOpen ? ' ogx-toolbar__icon-btn--active' : ''}`}
|
|
748
|
-
aria-label=
|
|
754
|
+
aria-label={locale.toolbarFiltersLabel}
|
|
749
755
|
aria-haspopup="dialog"
|
|
750
756
|
aria-expanded={filterOpen}
|
|
751
757
|
onClick={() => togglePanel('filters')}
|
|
752
758
|
>
|
|
753
759
|
<FilterIcon />
|
|
754
760
|
{activeFilterCount > 0 && (
|
|
755
|
-
<span className="ogx-toolbar__dot" aria-label=
|
|
761
|
+
<span className="ogx-toolbar__dot" aria-label={locale.toolbarFiltersActive} />
|
|
756
762
|
)}
|
|
757
763
|
</button>
|
|
758
764
|
</GridTooltip>
|
|
@@ -773,19 +779,19 @@ export function GridToolbar({
|
|
|
773
779
|
{/* Pivot button */}
|
|
774
780
|
{onPivotModelChange && (
|
|
775
781
|
<div className="ogx-toolbar__dropdown-wrapper" style={{ marginRight: 4 }}>
|
|
776
|
-
<GridTooltip title=
|
|
782
|
+
<GridTooltip title={locale.toolbarPivot}>
|
|
777
783
|
<button
|
|
778
784
|
type="button"
|
|
779
785
|
ref={pivotButtonRef}
|
|
780
786
|
className={`ogx-toolbar__icon-btn${pivotOpen ? ' ogx-toolbar__icon-btn--active' : ''}`}
|
|
781
|
-
aria-label=
|
|
787
|
+
aria-label={locale.toolbarPivotLabel}
|
|
782
788
|
aria-haspopup="dialog"
|
|
783
789
|
aria-expanded={pivotOpen}
|
|
784
790
|
onClick={() => togglePanel('pivot')}
|
|
785
791
|
>
|
|
786
792
|
<PivotIcon />
|
|
787
793
|
{pivotActive && (
|
|
788
|
-
<span className="ogx-toolbar__dot" aria-label=
|
|
794
|
+
<span className="ogx-toolbar__dot" aria-label={locale.toolbarPivotActive} />
|
|
789
795
|
)}
|
|
790
796
|
</button>
|
|
791
797
|
</GridTooltip>
|
|
@@ -812,19 +818,19 @@ export function GridToolbar({
|
|
|
812
818
|
activeCount,
|
|
813
819
|
})
|
|
814
820
|
: (
|
|
815
|
-
<GridTooltip title=
|
|
821
|
+
<GridTooltip title={locale.toolbarSummaries}>
|
|
816
822
|
<button
|
|
817
823
|
type="button"
|
|
818
824
|
ref={aggButtonRef}
|
|
819
825
|
className={`ogx-toolbar__icon-btn${aggOpen ? ' ogx-toolbar__icon-btn--active' : ''}`}
|
|
820
|
-
aria-label=
|
|
826
|
+
aria-label={locale.toolbarSummariesLabel}
|
|
821
827
|
aria-haspopup="dialog"
|
|
822
828
|
aria-expanded={aggOpen}
|
|
823
829
|
onClick={() => togglePanel('summaries')}
|
|
824
830
|
>
|
|
825
831
|
<SigmaIcon />
|
|
826
832
|
{activeCount > 0 && (
|
|
827
|
-
<span className="ogx-toolbar__dot" aria-label={
|
|
833
|
+
<span className="ogx-toolbar__dot" aria-label={locale.toolbarSummariesActive(activeCount)} />
|
|
828
834
|
)}
|
|
829
835
|
</button>
|
|
830
836
|
</GridTooltip>
|
|
@@ -4,6 +4,7 @@ import type { GridColDef } from '../../types';
|
|
|
4
4
|
import type { GridPivotModel, GridPivotAggFn, GridPivotValueField } from '../../types';
|
|
5
5
|
import { getViewportWidth } from '../../utils/viewport';
|
|
6
6
|
import { getPanelPosition, type GridPanelPosition } from '../../utils/direction';
|
|
7
|
+
import { useGridLocale } from '../DataGrid/gridLocaleContext';
|
|
7
8
|
|
|
8
9
|
const AGG_FNS: GridPivotAggFn[] = ['sum', 'avg', 'count', 'min', 'max'];
|
|
9
10
|
|
|
@@ -31,6 +32,7 @@ export interface PivotPanelProps {
|
|
|
31
32
|
}
|
|
32
33
|
|
|
33
34
|
export function PivotPanel({ anchorRef, columns, model, onChange, onClose }: PivotPanelProps) {
|
|
35
|
+
const locale = useGridLocale();
|
|
34
36
|
const panelRef = useRef<HTMLDivElement>(null);
|
|
35
37
|
const [pos, setPos] = useState<GridPanelPosition>({ top: 0, right: 0 });
|
|
36
38
|
const [portalContainer, setPortalContainer] = useState<Element | null>(null);
|
|
@@ -128,18 +130,18 @@ export function PivotPanel({ anchorRef, columns, model, onChange, onClose }: Piv
|
|
|
128
130
|
ref={panelRef}
|
|
129
131
|
className="ogx-pivot-panel"
|
|
130
132
|
role="dialog"
|
|
131
|
-
aria-label=
|
|
133
|
+
aria-label={locale.pivotPanelLabel}
|
|
132
134
|
style={{ top: pos.top, right: pos.right, left: pos.left }}
|
|
133
135
|
>
|
|
134
136
|
{ }
|
|
135
137
|
<div className="ogx-pivot-panel__header">
|
|
136
138
|
<span className="ogx-pivot-panel__title">
|
|
137
139
|
<PivotIcon />
|
|
138
|
-
|
|
140
|
+
{locale.pivotMode}
|
|
139
141
|
</span>
|
|
140
142
|
{totalActive > 0 && (
|
|
141
|
-
<button type="button" className="ogx-pivot-panel__reset-btn" onClick={reset} title=
|
|
142
|
-
|
|
143
|
+
<button type="button" className="ogx-pivot-panel__reset-btn" onClick={reset} title={locale.pivotResetTooltip}>
|
|
144
|
+
{locale.pivotReset}
|
|
143
145
|
</button>
|
|
144
146
|
)}
|
|
145
147
|
</div>
|
|
@@ -150,11 +152,11 @@ export function PivotPanel({ anchorRef, columns, model, onChange, onClose }: Piv
|
|
|
150
152
|
<div className="ogx-pivot-zone">
|
|
151
153
|
<span className="ogx-pivot-zone__label">
|
|
152
154
|
<span className="ogx-pivot-zone__dot ogx-pivot-zone__dot--row" />
|
|
153
|
-
|
|
155
|
+
{locale.pivotRowFields}
|
|
154
156
|
</span>
|
|
155
157
|
<div
|
|
156
158
|
className={`ogx-pivot-chips${model.rowFields.length === 0 ? ' ogx-pivot-chips--empty' : ''}`}
|
|
157
|
-
data-placeholder=
|
|
159
|
+
data-placeholder={locale.pivotRowFieldsPlaceholder}
|
|
158
160
|
>
|
|
159
161
|
{model.rowFields.map((f, i) => (
|
|
160
162
|
<span key={`${i}:${f}`} className="ogx-pivot-chip ogx-pivot-chip--row">
|
|
@@ -163,8 +165,8 @@ export function PivotPanel({ anchorRef, columns, model, onChange, onClose }: Piv
|
|
|
163
165
|
type="button"
|
|
164
166
|
className="ogx-pivot-chip__remove"
|
|
165
167
|
onClick={() => removeRow(i)}
|
|
166
|
-
title={
|
|
167
|
-
aria-label={
|
|
168
|
+
title={locale.pivotRemoveTooltip(colLabel(f))}
|
|
169
|
+
aria-label={locale.pivotRemoveRowLabel(colLabel(f))}
|
|
168
170
|
>×</button>
|
|
169
171
|
</span>
|
|
170
172
|
))}
|
|
@@ -175,11 +177,11 @@ export function PivotPanel({ anchorRef, columns, model, onChange, onClose }: Piv
|
|
|
175
177
|
<div className="ogx-pivot-zone">
|
|
176
178
|
<span className="ogx-pivot-zone__label">
|
|
177
179
|
<span className="ogx-pivot-zone__dot ogx-pivot-zone__dot--col" />
|
|
178
|
-
|
|
180
|
+
{locale.pivotColumnFields}
|
|
179
181
|
</span>
|
|
180
182
|
<div
|
|
181
183
|
className={`ogx-pivot-chips${model.columnFields.length === 0 ? ' ogx-pivot-chips--empty' : ''}`}
|
|
182
|
-
data-placeholder=
|
|
184
|
+
data-placeholder={locale.pivotColumnFieldsPlaceholder}
|
|
183
185
|
>
|
|
184
186
|
{model.columnFields.map((f, i) => (
|
|
185
187
|
<span key={`${i}:${f}`} className="ogx-pivot-chip ogx-pivot-chip--col">
|
|
@@ -188,8 +190,8 @@ export function PivotPanel({ anchorRef, columns, model, onChange, onClose }: Piv
|
|
|
188
190
|
type="button"
|
|
189
191
|
className="ogx-pivot-chip__remove"
|
|
190
192
|
onClick={() => removeCol(i)}
|
|
191
|
-
title={
|
|
192
|
-
aria-label={
|
|
193
|
+
title={locale.pivotRemoveTooltip(colLabel(f))}
|
|
194
|
+
aria-label={locale.pivotRemoveColumnLabel(colLabel(f))}
|
|
193
195
|
>×</button>
|
|
194
196
|
</span>
|
|
195
197
|
))}
|
|
@@ -200,11 +202,11 @@ export function PivotPanel({ anchorRef, columns, model, onChange, onClose }: Piv
|
|
|
200
202
|
<div className="ogx-pivot-zone">
|
|
201
203
|
<span className="ogx-pivot-zone__label">
|
|
202
204
|
<span className="ogx-pivot-zone__dot ogx-pivot-zone__dot--value" />
|
|
203
|
-
|
|
205
|
+
{locale.pivotValueFields}
|
|
204
206
|
</span>
|
|
205
207
|
<div
|
|
206
208
|
className={`ogx-pivot-chips${model.valueFields.length === 0 ? ' ogx-pivot-chips--empty' : ''}`}
|
|
207
|
-
data-placeholder=
|
|
209
|
+
data-placeholder={locale.pivotValueFieldsPlaceholder}
|
|
208
210
|
>
|
|
209
211
|
{model.valueFields.map((vf: GridPivotValueField, i) => {
|
|
210
212
|
const allowed = allowedFns(vf.field);
|
|
@@ -216,19 +218,19 @@ export function PivotPanel({ anchorRef, columns, model, onChange, onClose }: Piv
|
|
|
216
218
|
className="ogx-pivot-chip__fn-select"
|
|
217
219
|
value={vf.aggFn}
|
|
218
220
|
onChange={(e) => changeValueFn(i, e.target.value as GridPivotAggFn)}
|
|
219
|
-
title=
|
|
220
|
-
aria-label={
|
|
221
|
+
title={locale.pivotAggregationTooltip}
|
|
222
|
+
aria-label={locale.pivotAggregationLabel(colLabel(vf.field))}
|
|
221
223
|
>
|
|
222
224
|
{fnOptions.map((fn) => (
|
|
223
|
-
<option key={fn} value={fn}>{
|
|
225
|
+
<option key={fn} value={fn}>{locale.pivotAggregationFunction(fn)}</option>
|
|
224
226
|
))}
|
|
225
227
|
</select>
|
|
226
228
|
<button
|
|
227
229
|
type="button"
|
|
228
230
|
className="ogx-pivot-chip__remove"
|
|
229
231
|
onClick={() => removeValue(i)}
|
|
230
|
-
title={
|
|
231
|
-
aria-label={
|
|
232
|
+
title={locale.pivotRemoveTooltip(colLabel(vf.field))}
|
|
233
|
+
aria-label={locale.pivotRemoveValueLabel(colLabel(vf.field), locale.pivotAggregationFunction(vf.aggFn))}
|
|
232
234
|
>×</button>
|
|
233
235
|
</span>
|
|
234
236
|
);
|
|
@@ -240,7 +242,7 @@ export function PivotPanel({ anchorRef, columns, model, onChange, onClose }: Piv
|
|
|
240
242
|
|
|
241
243
|
{ }
|
|
242
244
|
<div>
|
|
243
|
-
<div className="ogx-pivot-panel__fields-label">
|
|
245
|
+
<div className="ogx-pivot-panel__fields-label">{locale.pivotAvailableFields}</div>
|
|
244
246
|
<div className="ogx-pivot-fields">
|
|
245
247
|
{availableColumns.map((col) => {
|
|
246
248
|
const isNum = canBeValue(col);
|
|
@@ -253,13 +255,13 @@ export function PivotPanel({ anchorRef, columns, model, onChange, onClose }: Piv
|
|
|
253
255
|
</span>
|
|
254
256
|
<div className="ogx-pivot-field-actions">
|
|
255
257
|
{isDimension && (
|
|
256
|
-
<button type="button" className="ogx-pivot-add-btn ogx-pivot-add-btn--row" onClick={() => addRow(col.field)} title=
|
|
258
|
+
<button type="button" className="ogx-pivot-add-btn ogx-pivot-add-btn--row" onClick={() => addRow(col.field)} title={locale.pivotAddRowTooltip} aria-label={locale.pivotAddRowLabel(label)}>{locale.pivotAddRow}</button>
|
|
257
259
|
)}
|
|
258
260
|
{isDimension && (
|
|
259
|
-
<button type="button" className="ogx-pivot-add-btn ogx-pivot-add-btn--col" onClick={() => addCol(col.field)} title=
|
|
261
|
+
<button type="button" className="ogx-pivot-add-btn ogx-pivot-add-btn--col" onClick={() => addCol(col.field)} title={locale.pivotAddColumnTooltip} aria-label={locale.pivotAddColumnLabel(label)}>{locale.pivotAddColumn}</button>
|
|
260
262
|
)}
|
|
261
263
|
{isNum && (
|
|
262
|
-
<button type="button" className="ogx-pivot-add-btn ogx-pivot-add-btn--value" onClick={() => addValue(col.field)} title=
|
|
264
|
+
<button type="button" className="ogx-pivot-add-btn ogx-pivot-add-btn--value" onClick={() => addValue(col.field)} title={locale.pivotAddValueTooltip} aria-label={locale.pivotAddValueLabel(label)}>{locale.pivotAddValue}</button>
|
|
263
265
|
)}
|
|
264
266
|
</div>
|
|
265
267
|
</div>
|
|
@@ -267,7 +269,7 @@ export function PivotPanel({ anchorRef, columns, model, onChange, onClose }: Piv
|
|
|
267
269
|
})}
|
|
268
270
|
{availableColumns.length === 0 && (
|
|
269
271
|
<span style={{ fontSize: 12, color: 'var(--ogx-color-gray-400, #94a3b8)' }}>
|
|
270
|
-
|
|
272
|
+
{locale.pivotAllFieldsInUse}
|
|
271
273
|
</span>
|
|
272
274
|
)}
|
|
273
275
|
</div>
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
border-radius: 4px;
|
|
27
27
|
background: var(--ogx-checkbox-bg, var(--ogx-color-white));
|
|
28
28
|
transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
|
|
29
|
-
color: white;
|
|
29
|
+
color: var(--ogx-checkbox-checkmark, white);
|
|
30
30
|
flex-shrink: 0;
|
|
31
31
|
}
|
|
32
32
|
.ogx-checkbox__icon {
|
|
@@ -43,15 +43,15 @@
|
|
|
43
43
|
line-height: 1;
|
|
44
44
|
}
|
|
45
45
|
.ogx-checkbox__input:checked + .ogx-checkbox__box {
|
|
46
|
-
background: var(--ogx-color-primary);
|
|
47
|
-
border-color: var(--ogx-color-primary);
|
|
46
|
+
background: var(--ogx-checkbox-checked-bg, var(--ogx-color-primary));
|
|
47
|
+
border-color: var(--ogx-checkbox-checked-bg, var(--ogx-color-primary));
|
|
48
48
|
}
|
|
49
49
|
.ogx-checkbox__input:checked + .ogx-checkbox__box .ogx-checkbox__icon {
|
|
50
50
|
transform: scale(1);
|
|
51
51
|
}
|
|
52
52
|
.ogx-checkbox__input:indeterminate + .ogx-checkbox__box {
|
|
53
|
-
background: var(--ogx-color-primary);
|
|
54
|
-
border-color: var(--ogx-color-primary);
|
|
53
|
+
background: var(--ogx-checkbox-checked-bg, var(--ogx-color-primary));
|
|
54
|
+
border-color: var(--ogx-checkbox-checked-bg, var(--ogx-color-primary));
|
|
55
55
|
}
|
|
56
56
|
.ogx-checkbox__input:indeterminate + .ogx-checkbox__box .ogx-checkbox__icon {
|
|
57
57
|
transform: scale(1);
|
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
+
import { useGridLocale } from '../DataGrid/gridLocaleContext';
|
|
2
3
|
|
|
3
4
|
export type DragHandleIconProps = React.ButtonHTMLAttributes<HTMLButtonElement>;
|
|
4
5
|
|
|
5
6
|
export const DragHandleIcon: React.FC<DragHandleIconProps> = ({ className = '', ...props }) => {
|
|
7
|
+
const locale = useGridLocale();
|
|
6
8
|
return (
|
|
7
9
|
<button
|
|
8
10
|
type="button"
|
|
9
11
|
className={`ogx-drag-handle ${className}`}
|
|
10
|
-
aria-label=
|
|
12
|
+
aria-label={locale.rowReorderLabel}
|
|
11
13
|
{...props}
|
|
12
14
|
>
|
|
13
15
|
<svg viewBox="0 0 24 24" width="20" height="20" fill="currentColor" aria-hidden="true">
|