@archbase/components 4.0.19 → 4.0.20
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/archbase-components-4.0.20.tgz +0 -0
- package/dist/datagrid/ag-grid/ag-grid-locale-ptbr.d.ts +7 -0
- package/dist/datagrid/ag-grid/ag-grid-mantine-theme.d.ts +24 -0
- package/dist/datagrid/ag-grid/archbase-data-grid-ag-formatters.d.ts +88 -0
- package/dist/datagrid/ag-grid/archbase-data-grid-ag-types.d.ts +329 -0
- package/dist/datagrid/ag-grid/archbase-data-grid-ag-utils.d.ts +71 -0
- package/dist/datagrid/ag-grid/archbase-data-grid-ag.d.ts +14 -0
- package/dist/datagrid/ag-grid/index.d.ts +14 -0
- package/dist/datagrid/index.d.ts +9 -1
- package/dist/index.js +11175 -10592
- package/package.json +6 -4
- package/src/datagrid/ag-grid/ag-grid-locale-ptbr.ts +432 -0
- package/src/datagrid/ag-grid/ag-grid-mantine-theme.ts +176 -0
- package/src/datagrid/ag-grid/archbase-data-grid-ag-formatters.tsx +470 -0
- package/src/datagrid/ag-grid/archbase-data-grid-ag-types.tsx +413 -0
- package/src/datagrid/ag-grid/archbase-data-grid-ag-utils.ts +394 -0
- package/src/datagrid/ag-grid/archbase-data-grid-ag.tsx +1241 -0
- package/src/datagrid/ag-grid/index.tsx +84 -0
- package/src/datagrid/index.tsx +59 -8
- package/src/datagrid/main/archbase-data-grid-pagination.tsx +1 -2
- package/src/datagrid/main/archbase-data-grid-toolbar.tsx +1 -2
- package/src/editors/ArchbaseAsyncMultiSelect.tsx +2 -1
- package/src/editors/ArchbaseAsyncSelect.tsx +2 -1
- package/src/editors/ArchbaseAvatarEdit.tsx +1 -0
- package/src/editors/ArchbaseCheckbox.tsx +1 -0
- package/src/editors/ArchbaseChip.tsx +1 -0
- package/src/editors/ArchbaseChipGroup.tsx +1 -0
- package/src/editors/ArchbaseColorGradientPicker.tsx +2 -1
- package/src/editors/ArchbaseDatePickerEdit.tsx +1 -0
- package/src/editors/ArchbaseDateTimePickerEdit.tsx +1 -0
- package/src/editors/ArchbaseDualListbox.tsx +1 -0
- package/src/editors/ArchbaseEdit.tsx +1 -0
- package/src/editors/ArchbaseImageEdit.tsx +1 -0
- package/src/editors/ArchbaseJsonEdit.tsx +1 -0
- package/src/editors/ArchbaseLookupEdit.tsx +2 -1
- package/src/editors/ArchbaseLookupNumber.tsx +2 -1
- package/src/editors/ArchbaseLookupSelect.tsx +2 -1
- package/src/editors/ArchbaseMarkdownEdit.tsx +1 -0
- package/src/editors/ArchbaseMaskEdit.tsx +1 -0
- package/src/editors/ArchbaseMentionInput.tsx +1 -0
- package/src/editors/ArchbaseMultiEmail.tsx +1 -0
- package/src/editors/ArchbaseMultiSelect.tsx +2 -1
- package/src/editors/ArchbaseNumberEdit.tsx +1 -0
- package/src/editors/ArchbaseNumberStepper.tsx +2 -1
- package/src/editors/ArchbasePasswordEdit.tsx +1 -0
- package/src/editors/ArchbaseRadioGroup.tsx +1 -0
- package/src/editors/ArchbaseRating.tsx +1 -0
- package/src/editors/ArchbaseRichTextEdit.tsx +1 -0
- package/src/editors/ArchbaseSelect.tsx +2 -1
- package/src/editors/ArchbaseSignaturePad.tsx +1 -0
- package/src/editors/ArchbaseSwitch.tsx +1 -0
- package/src/editors/ArchbaseTagInputEdit.tsx +1 -0
- package/src/editors/ArchbaseTextArea.tsx +1 -0
- package/src/editors/ArchbaseTimeEdit.tsx +1 -0
- package/dist/archbase-components-4.0.19.tgz +0 -0
|
@@ -0,0 +1,1241 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ArchbaseDataGridAG - AG Grid Implementation
|
|
3
|
+
*
|
|
4
|
+
* Grid component using AG Grid Community with Mantine theme integration.
|
|
5
|
+
* API compatible with the MUI DataGrid version for easy migration.
|
|
6
|
+
*/
|
|
7
|
+
import React, {
|
|
8
|
+
useState,
|
|
9
|
+
useEffect,
|
|
10
|
+
useRef,
|
|
11
|
+
useImperativeHandle,
|
|
12
|
+
useMemo,
|
|
13
|
+
useCallback,
|
|
14
|
+
Children,
|
|
15
|
+
isValidElement,
|
|
16
|
+
} from 'react';
|
|
17
|
+
import { AgGridReact } from 'ag-grid-react';
|
|
18
|
+
import {
|
|
19
|
+
AllCommunityModule,
|
|
20
|
+
ModuleRegistry,
|
|
21
|
+
type GridApi,
|
|
22
|
+
type ColDef,
|
|
23
|
+
type GridReadyEvent,
|
|
24
|
+
type SelectionChangedEvent,
|
|
25
|
+
type CellDoubleClickedEvent,
|
|
26
|
+
type SortChangedEvent,
|
|
27
|
+
type FilterChangedEvent,
|
|
28
|
+
type RowSelectionOptions,
|
|
29
|
+
type SortModelItem,
|
|
30
|
+
type GetRowIdParams,
|
|
31
|
+
} from 'ag-grid-community';
|
|
32
|
+
import { ActionIcon, Box, Group, Title, useMantineColorScheme } from '@mantine/core';
|
|
33
|
+
import { IconChevronDown, IconChevronRight, IconX } from '@tabler/icons-react';
|
|
34
|
+
|
|
35
|
+
// Import theme and locale
|
|
36
|
+
import { createArchbaseAgGridTheme, getAgGridMantineCssVars, getAgGridCustomStyles } from './ag-grid-mantine-theme';
|
|
37
|
+
import { AG_GRID_LOCALE_PTBR } from './ag-grid-locale-ptbr';
|
|
38
|
+
|
|
39
|
+
// Import types and utilities
|
|
40
|
+
import type {
|
|
41
|
+
ArchbaseDataGridAGProps,
|
|
42
|
+
ArchbaseDataGridAGRef,
|
|
43
|
+
ArchbaseDataGridAGColumnProps,
|
|
44
|
+
ExtendedColDef,
|
|
45
|
+
FieldDataType,
|
|
46
|
+
} from './archbase-data-grid-ag-types';
|
|
47
|
+
import { Columns } from './archbase-data-grid-ag-types';
|
|
48
|
+
import {
|
|
49
|
+
safeGetRowId,
|
|
50
|
+
isDataSourceV2,
|
|
51
|
+
getRecordsFromDataSource,
|
|
52
|
+
getDataSourceOptions,
|
|
53
|
+
getCurrentPageFromDataSource,
|
|
54
|
+
buildFilterExpression,
|
|
55
|
+
convertSortModelToDataSource,
|
|
56
|
+
getInitialSortModel,
|
|
57
|
+
convertActiveFiltersToAgGridModel,
|
|
58
|
+
convertColumnsToFilterDefinitions,
|
|
59
|
+
MAX_PAGE_SIZE,
|
|
60
|
+
hexToRgb,
|
|
61
|
+
} from './archbase-data-grid-ag-utils';
|
|
62
|
+
import {
|
|
63
|
+
getCellRendererByDataType,
|
|
64
|
+
getAlignmentByDataType,
|
|
65
|
+
createValueFormatter,
|
|
66
|
+
} from './archbase-data-grid-ag-formatters';
|
|
67
|
+
|
|
68
|
+
// Import shared components
|
|
69
|
+
import { ArchbaseDataGridToolbar } from '../main/archbase-data-grid-toolbar';
|
|
70
|
+
import { ArchbaseDataGridPagination } from '../main/archbase-data-grid-pagination';
|
|
71
|
+
import { ExportModal } from '../modals/export-modal';
|
|
72
|
+
import { PrintModal } from '../modals/print-modal';
|
|
73
|
+
import { exportData, ExportConfig } from '../modals/export-data';
|
|
74
|
+
import { printData, PrintConfig } from '../modals/print-data';
|
|
75
|
+
import {
|
|
76
|
+
ArchbaseDetailPanel,
|
|
77
|
+
ArchbaseDetailModal,
|
|
78
|
+
ArchbaseDetailDrawer,
|
|
79
|
+
} from '../main/archbase-detail-panel-component';
|
|
80
|
+
import {
|
|
81
|
+
useDetailPanels,
|
|
82
|
+
useDetailPanelAutoClose,
|
|
83
|
+
useAvailableSpace,
|
|
84
|
+
} from '../hooks/use-grid-details-panel';
|
|
85
|
+
|
|
86
|
+
import { useArchbaseTheme, useArchbaseAppContext } from '@archbase/core';
|
|
87
|
+
import { DataSourceEvent, DataSourceEventNames } from '@archbase/data';
|
|
88
|
+
|
|
89
|
+
// Register AG Grid modules
|
|
90
|
+
ModuleRegistry.registerModules([AllCommunityModule]);
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* ArchbaseDataGridAG Component
|
|
94
|
+
*
|
|
95
|
+
* A feature-rich data grid using AG Grid Community with:
|
|
96
|
+
* - Mantine theme integration
|
|
97
|
+
* - DataSource V1/V2 compatibility
|
|
98
|
+
* - Server-side pagination, sorting, filtering
|
|
99
|
+
* - Row selection
|
|
100
|
+
* - Detail panels
|
|
101
|
+
* - Export/Print functionality
|
|
102
|
+
*/
|
|
103
|
+
function ArchbaseDataGridAG<T extends object = any, ID = any>(
|
|
104
|
+
props: ArchbaseDataGridAGProps<T, ID>
|
|
105
|
+
) {
|
|
106
|
+
const {
|
|
107
|
+
dataSource,
|
|
108
|
+
getRowId,
|
|
109
|
+
resourceName,
|
|
110
|
+
resourceDescription,
|
|
111
|
+
columnSecurityOptions,
|
|
112
|
+
enableColumnResizing = true,
|
|
113
|
+
enableRowNumbers = false,
|
|
114
|
+
enableRowSelection = true,
|
|
115
|
+
enableRowActions = true,
|
|
116
|
+
enableColumnFilterModes = true,
|
|
117
|
+
enableGlobalFilter = true,
|
|
118
|
+
enableTopToolbar = true,
|
|
119
|
+
enableTopToolbarActions = true,
|
|
120
|
+
showPagination = true,
|
|
121
|
+
manualFiltering = true,
|
|
122
|
+
manualPagination = true,
|
|
123
|
+
manualSorting = true,
|
|
124
|
+
isLoading = false,
|
|
125
|
+
isError = false,
|
|
126
|
+
error,
|
|
127
|
+
height = '100%',
|
|
128
|
+
width = '100%',
|
|
129
|
+
pageSize = 15,
|
|
130
|
+
pageIndex = 0,
|
|
131
|
+
children,
|
|
132
|
+
renderRowActions,
|
|
133
|
+
renderToolbarActions,
|
|
134
|
+
renderToolbarInternalActions,
|
|
135
|
+
renderTopToolbar,
|
|
136
|
+
renderDetailPanel,
|
|
137
|
+
allowMultipleDetailPanels = false,
|
|
138
|
+
detailPanelMinHeight = 200,
|
|
139
|
+
detailPanelStyle,
|
|
140
|
+
detailPanelClassName,
|
|
141
|
+
onDetailPanelChange,
|
|
142
|
+
detailPanelDisplayMode = 'auto',
|
|
143
|
+
detailPanelTitle = 'Detalhes',
|
|
144
|
+
detailPanelPosition = 'right',
|
|
145
|
+
detailPanelSize = 'md',
|
|
146
|
+
allowColumnFilters = true,
|
|
147
|
+
allowExportData = true,
|
|
148
|
+
allowPrintData = true,
|
|
149
|
+
useCompositeFilters = false,
|
|
150
|
+
filterDefinitions: externalFilterDefinitions,
|
|
151
|
+
activeFilters: externalActiveFilters,
|
|
152
|
+
onFiltersChange: externalOnFiltersChange,
|
|
153
|
+
hideAgGridFilters = false,
|
|
154
|
+
withBorder = true,
|
|
155
|
+
withColumnBorders = true,
|
|
156
|
+
highlightOnHover = true,
|
|
157
|
+
striped = false,
|
|
158
|
+
className = '',
|
|
159
|
+
variant = 'filled',
|
|
160
|
+
fontSize,
|
|
161
|
+
cellPadding,
|
|
162
|
+
tableHeadCellPadding,
|
|
163
|
+
columnAutoWidth = false,
|
|
164
|
+
autoSizeStrategy,
|
|
165
|
+
skipHeaderOnAutoSize = false,
|
|
166
|
+
rowHeight = 40,
|
|
167
|
+
headerFontWeight = 600,
|
|
168
|
+
withToolbarBorder = true,
|
|
169
|
+
withPaginationBorder = true,
|
|
170
|
+
toolbarPadding,
|
|
171
|
+
paginationPadding,
|
|
172
|
+
printTitle = 'Data Grid Print',
|
|
173
|
+
logoPrint,
|
|
174
|
+
globalDateFormat = 'dd/MM/yyyy',
|
|
175
|
+
csvOptions,
|
|
176
|
+
toolbarAlignment = 'right',
|
|
177
|
+
positionActionsColumn = 'first',
|
|
178
|
+
actionsColumnWidth = 60,
|
|
179
|
+
toolbarLeftContent,
|
|
180
|
+
bottomToolbarMinHeight,
|
|
181
|
+
paginationLabels,
|
|
182
|
+
showProgressBars = true,
|
|
183
|
+
hideFooter = true,
|
|
184
|
+
onSelectedRowsChanged,
|
|
185
|
+
onCellDoubleClick,
|
|
186
|
+
onExport,
|
|
187
|
+
onPrint,
|
|
188
|
+
onFilterModelChange,
|
|
189
|
+
gridRef,
|
|
190
|
+
} = props;
|
|
191
|
+
|
|
192
|
+
const theme = useArchbaseTheme();
|
|
193
|
+
const { colorScheme: mantineColorScheme } = useMantineColorScheme();
|
|
194
|
+
// Normalizar colorScheme para "light" ou "dark" (tratar "auto" como "light")
|
|
195
|
+
const colorScheme: 'light' | 'dark' = mantineColorScheme === 'dark' ? 'dark' : 'light';
|
|
196
|
+
const appContext = useArchbaseAppContext();
|
|
197
|
+
|
|
198
|
+
// AG Grid API reference
|
|
199
|
+
const gridApiRef = useRef<GridApi | null>(null);
|
|
200
|
+
const gridContainerRef = useRef<HTMLDivElement>(null);
|
|
201
|
+
|
|
202
|
+
// Detect DataSource version
|
|
203
|
+
const isV2 = isDataSourceV2(dataSource);
|
|
204
|
+
|
|
205
|
+
// State
|
|
206
|
+
const [rows, setRows] = useState<T[]>(() => getRecordsFromDataSource<T>(dataSource));
|
|
207
|
+
const [isLoadingInternal, setIsLoadingInternal] = useState<boolean>(false);
|
|
208
|
+
const [selectedRows, setSelectedRows] = useState<T[]>([]);
|
|
209
|
+
const [totalRecords, setTotalRecords] = useState<number>(() => {
|
|
210
|
+
const total = dataSource.getGrandTotalRecords();
|
|
211
|
+
return Number.isFinite(total) ? Math.max(0, total) : 0;
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
// Pagination state
|
|
215
|
+
const [paginationModel, setPaginationModel] = useState({
|
|
216
|
+
page: Number.isFinite(getCurrentPageFromDataSource(dataSource))
|
|
217
|
+
? getCurrentPageFromDataSource(dataSource)
|
|
218
|
+
: pageIndex,
|
|
219
|
+
pageSize: Math.min(
|
|
220
|
+
Number.isFinite(dataSource?.getPageSize?.()) ? dataSource.getPageSize() : pageSize,
|
|
221
|
+
MAX_PAGE_SIZE
|
|
222
|
+
),
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
// Sort state
|
|
226
|
+
const [sortModel, setSortModel] = useState<SortModelItem[]>(() => getInitialSortModel(dataSource));
|
|
227
|
+
|
|
228
|
+
// Filter state (for toolbar compatibility)
|
|
229
|
+
const dsOptions = getDataSourceOptions(dataSource);
|
|
230
|
+
const [filterModel, setFilterModel] = useState({
|
|
231
|
+
items: dsOptions.originFilter || [],
|
|
232
|
+
quickFilterValues: dsOptions.originGlobalFilter ? [dsOptions.originGlobalFilter] : [],
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
// AG Grid filter model (internal)
|
|
236
|
+
const [agFilterModel, setAgFilterModel] = useState<Record<string, any>>({});
|
|
237
|
+
const [quickFilterText, setQuickFilterText] = useState<string>(dsOptions.originGlobalFilter || '');
|
|
238
|
+
|
|
239
|
+
// Internal active filters state
|
|
240
|
+
const [internalActiveFilters, setInternalActiveFilters] = useState<typeof externalActiveFilters>([]);
|
|
241
|
+
const activeFilters = externalActiveFilters !== undefined ? externalActiveFilters : internalActiveFilters;
|
|
242
|
+
|
|
243
|
+
// Export/Print modal state
|
|
244
|
+
const [exportModalOpen, setExportModalOpen] = useState<boolean>(false);
|
|
245
|
+
const [printModalOpen, setPrintModalOpen] = useState<boolean>(false);
|
|
246
|
+
|
|
247
|
+
// Sync control
|
|
248
|
+
const syncInProgress = useRef<boolean>(false);
|
|
249
|
+
|
|
250
|
+
// Detail panels
|
|
251
|
+
const {
|
|
252
|
+
expandedRowIds,
|
|
253
|
+
detailPanelHeight,
|
|
254
|
+
detailPanelRefs,
|
|
255
|
+
toggleExpand,
|
|
256
|
+
closeDetailPanel,
|
|
257
|
+
closeAllDetailPanels,
|
|
258
|
+
expandDetailPanel,
|
|
259
|
+
setExpandedRowIds,
|
|
260
|
+
} = useDetailPanels({
|
|
261
|
+
allowMultipleDetailPanels,
|
|
262
|
+
onDetailPanelChange: (ids) => {
|
|
263
|
+
if (onDetailPanelChange) {
|
|
264
|
+
onDetailPanelChange(Array.from(ids));
|
|
265
|
+
}
|
|
266
|
+
},
|
|
267
|
+
detailPanelMinHeight,
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
useDetailPanelAutoClose({
|
|
271
|
+
containerRef: gridContainerRef,
|
|
272
|
+
expandedRowIds,
|
|
273
|
+
detailPanelRefs,
|
|
274
|
+
closeAllDetailPanels,
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
const shouldUseModal = useAvailableSpace({
|
|
278
|
+
containerRef: gridContainerRef,
|
|
279
|
+
rows,
|
|
280
|
+
getRowId,
|
|
281
|
+
safeGetRowId,
|
|
282
|
+
rowHeight,
|
|
283
|
+
detailPanelMinHeight,
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
// Create AG Grid theme
|
|
287
|
+
const agGridTheme = useMemo(
|
|
288
|
+
() => createArchbaseAgGridTheme(theme, colorScheme, { headerFontWeight }),
|
|
289
|
+
[theme, colorScheme, headerFontWeight]
|
|
290
|
+
);
|
|
291
|
+
|
|
292
|
+
// Create column definitions from children
|
|
293
|
+
const columnDefs = useMemo((): ExtendedColDef<T>[] => {
|
|
294
|
+
const cols: ExtendedColDef<T>[] = [];
|
|
295
|
+
|
|
296
|
+
// Add expand column if detail panel is enabled
|
|
297
|
+
if (renderDetailPanel) {
|
|
298
|
+
cols.push({
|
|
299
|
+
field: '__expand__' as any,
|
|
300
|
+
headerName: '',
|
|
301
|
+
width: 50,
|
|
302
|
+
maxWidth: 50,
|
|
303
|
+
minWidth: 50,
|
|
304
|
+
sortable: false,
|
|
305
|
+
filter: false,
|
|
306
|
+
resizable: false,
|
|
307
|
+
suppressMovable: true,
|
|
308
|
+
cellRenderer: (params: any) => {
|
|
309
|
+
const rowId = safeGetRowId(params.data, getRowId);
|
|
310
|
+
if (rowId === undefined) return null;
|
|
311
|
+
const isExpanded = expandedRowIds.has(rowId);
|
|
312
|
+
return (
|
|
313
|
+
<ActionIcon
|
|
314
|
+
size="sm"
|
|
315
|
+
variant="subtle"
|
|
316
|
+
onClick={(e) => {
|
|
317
|
+
e.stopPropagation();
|
|
318
|
+
toggleExpand(rowId);
|
|
319
|
+
}}
|
|
320
|
+
>
|
|
321
|
+
{isExpanded ? <IconChevronDown size={16} /> : <IconChevronRight size={16} />}
|
|
322
|
+
</ActionIcon>
|
|
323
|
+
);
|
|
324
|
+
},
|
|
325
|
+
cellStyle: { display: 'flex', alignItems: 'center', justifyContent: 'center' },
|
|
326
|
+
});
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
// Add actions column if enabled
|
|
330
|
+
if (enableRowActions && renderRowActions) {
|
|
331
|
+
const actionsCol: ExtendedColDef<T> = {
|
|
332
|
+
field: '__actions__' as any,
|
|
333
|
+
headerName: 'Ações',
|
|
334
|
+
width: actionsColumnWidth,
|
|
335
|
+
sortable: false,
|
|
336
|
+
filter: false,
|
|
337
|
+
resizable: false,
|
|
338
|
+
suppressMovable: true,
|
|
339
|
+
cellRenderer: (params: any) => renderRowActions(params.data),
|
|
340
|
+
cellStyle: { display: 'flex', alignItems: 'center', justifyContent: 'center' },
|
|
341
|
+
enableGlobalFilter: false,
|
|
342
|
+
};
|
|
343
|
+
|
|
344
|
+
if (positionActionsColumn === 'first') {
|
|
345
|
+
cols.unshift(actionsCol);
|
|
346
|
+
} else {
|
|
347
|
+
cols.push(actionsCol);
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
// Extract column definitions from children
|
|
352
|
+
Children.forEach(children, (child) => {
|
|
353
|
+
if (isValidElement(child) && child.type === Columns) {
|
|
354
|
+
Children.forEach((child.props as any).children, (column) => {
|
|
355
|
+
if (isValidElement(column)) {
|
|
356
|
+
const columnProps = column.props as ArchbaseDataGridAGColumnProps<T>;
|
|
357
|
+
|
|
358
|
+
if (columnProps.visible === false) return;
|
|
359
|
+
|
|
360
|
+
const dataType = columnProps.dataType || 'text';
|
|
361
|
+
const alignment = getAlignmentByDataType(dataType, columnProps.align);
|
|
362
|
+
|
|
363
|
+
// Get cell renderer
|
|
364
|
+
const cellRenderer = getCellRendererByDataType(
|
|
365
|
+
dataType,
|
|
366
|
+
columnProps.render
|
|
367
|
+
? (params) =>
|
|
368
|
+
columnProps.render!({
|
|
369
|
+
getValue: () => params.value,
|
|
370
|
+
row: params.data,
|
|
371
|
+
})
|
|
372
|
+
: undefined,
|
|
373
|
+
{
|
|
374
|
+
maskOptions: columnProps.maskOptions,
|
|
375
|
+
dateFormat: appContext?.dateFormat || globalDateFormat,
|
|
376
|
+
dateTimeFormat: appContext?.dateTimeFormat,
|
|
377
|
+
timeFormat: 'HH:mm:ss',
|
|
378
|
+
enumValues: columnProps.enumValues,
|
|
379
|
+
decimalPlaces: 2,
|
|
380
|
+
}
|
|
381
|
+
);
|
|
382
|
+
|
|
383
|
+
// Create value getter for nested fields
|
|
384
|
+
const valueGetter = columnProps.dataField.includes('.')
|
|
385
|
+
? (params: any) => {
|
|
386
|
+
const parts = columnProps.dataField.split('.');
|
|
387
|
+
let result = params.data;
|
|
388
|
+
for (const part of parts) {
|
|
389
|
+
if (result && typeof result === 'object') {
|
|
390
|
+
result = result[part];
|
|
391
|
+
} else {
|
|
392
|
+
return undefined;
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
return result;
|
|
396
|
+
}
|
|
397
|
+
: undefined;
|
|
398
|
+
|
|
399
|
+
const colDef: ExtendedColDef<T> = {
|
|
400
|
+
field: columnProps.dataField as any,
|
|
401
|
+
headerName: columnProps.header,
|
|
402
|
+
width: columnProps.size || 150,
|
|
403
|
+
minWidth: columnProps.minSize,
|
|
404
|
+
maxWidth: columnProps.maxSize,
|
|
405
|
+
sortable: columnProps.enableSorting !== false,
|
|
406
|
+
filter: columnProps.enableColumnFilter !== false && allowColumnFilters,
|
|
407
|
+
resizable: enableColumnResizing,
|
|
408
|
+
flex: columnAutoWidth ? 1 : undefined,
|
|
409
|
+
cellRenderer,
|
|
410
|
+
valueGetter,
|
|
411
|
+
valueFormatter: createValueFormatter(dataType, {
|
|
412
|
+
dateFormat: appContext?.dateFormat || globalDateFormat,
|
|
413
|
+
dateTimeFormat: appContext?.dateTimeFormat,
|
|
414
|
+
enumValues: columnProps.enumValues,
|
|
415
|
+
}),
|
|
416
|
+
cellStyle: {
|
|
417
|
+
display: 'flex',
|
|
418
|
+
alignItems:
|
|
419
|
+
alignment === 'center'
|
|
420
|
+
? 'center'
|
|
421
|
+
: alignment === 'right'
|
|
422
|
+
? 'center'
|
|
423
|
+
: 'center',
|
|
424
|
+
justifyContent:
|
|
425
|
+
alignment === 'left'
|
|
426
|
+
? 'flex-start'
|
|
427
|
+
: alignment === 'right'
|
|
428
|
+
? 'flex-end'
|
|
429
|
+
: 'center',
|
|
430
|
+
},
|
|
431
|
+
// Custom properties
|
|
432
|
+
enableGlobalFilter: columnProps.enableGlobalFilter,
|
|
433
|
+
dataType: columnProps.dataType,
|
|
434
|
+
};
|
|
435
|
+
|
|
436
|
+
cols.push(colDef);
|
|
437
|
+
}
|
|
438
|
+
});
|
|
439
|
+
}
|
|
440
|
+
});
|
|
441
|
+
|
|
442
|
+
return cols;
|
|
443
|
+
}, [
|
|
444
|
+
children,
|
|
445
|
+
enableRowActions,
|
|
446
|
+
renderRowActions,
|
|
447
|
+
actionsColumnWidth,
|
|
448
|
+
positionActionsColumn,
|
|
449
|
+
renderDetailPanel,
|
|
450
|
+
expandedRowIds,
|
|
451
|
+
toggleExpand,
|
|
452
|
+
getRowId,
|
|
453
|
+
enableColumnResizing,
|
|
454
|
+
columnAutoWidth,
|
|
455
|
+
allowColumnFilters,
|
|
456
|
+
appContext?.dateFormat,
|
|
457
|
+
appContext?.dateTimeFormat,
|
|
458
|
+
globalDateFormat,
|
|
459
|
+
]);
|
|
460
|
+
|
|
461
|
+
// Auto-generate filter definitions
|
|
462
|
+
const autoGeneratedFilterDefinitions = useMemo(() => {
|
|
463
|
+
if (externalFilterDefinitions) {
|
|
464
|
+
return externalFilterDefinitions;
|
|
465
|
+
}
|
|
466
|
+
return convertColumnsToFilterDefinitions(columnDefs, {
|
|
467
|
+
excludeColumns: ['__actions__', '__expand__', 'id'],
|
|
468
|
+
onlyFilterable: true,
|
|
469
|
+
});
|
|
470
|
+
}, [columnDefs, externalFilterDefinitions]);
|
|
471
|
+
|
|
472
|
+
// Default column definition
|
|
473
|
+
const defaultColDef = useMemo(
|
|
474
|
+
(): ColDef => ({
|
|
475
|
+
resizable: enableColumnResizing,
|
|
476
|
+
sortable: true,
|
|
477
|
+
filter: allowColumnFilters,
|
|
478
|
+
filterParams: {
|
|
479
|
+
buttons: ['reset', 'apply'],
|
|
480
|
+
closeOnApply: true,
|
|
481
|
+
},
|
|
482
|
+
}),
|
|
483
|
+
[enableColumnResizing, allowColumnFilters]
|
|
484
|
+
);
|
|
485
|
+
|
|
486
|
+
// Row selection configuration
|
|
487
|
+
const rowSelection = useMemo((): RowSelectionOptions | undefined => {
|
|
488
|
+
if (!enableRowSelection) return undefined;
|
|
489
|
+
return {
|
|
490
|
+
mode: 'multiRow',
|
|
491
|
+
checkboxes: true,
|
|
492
|
+
headerCheckbox: true,
|
|
493
|
+
enableClickSelection: false,
|
|
494
|
+
};
|
|
495
|
+
}, [enableRowSelection]);
|
|
496
|
+
|
|
497
|
+
// Grid ready handler
|
|
498
|
+
const onGridReady = useCallback((event: GridReadyEvent) => {
|
|
499
|
+
gridApiRef.current = event.api;
|
|
500
|
+
|
|
501
|
+
// Apply initial sort
|
|
502
|
+
if (sortModel.length > 0) {
|
|
503
|
+
event.api.applyColumnState({
|
|
504
|
+
state: sortModel.map((s) => ({
|
|
505
|
+
colId: s.colId,
|
|
506
|
+
sort: s.sort,
|
|
507
|
+
})),
|
|
508
|
+
});
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
// Apply auto-size strategy after grid is ready
|
|
512
|
+
if (autoSizeStrategy) {
|
|
513
|
+
// Use setTimeout to ensure data is rendered before auto-sizing
|
|
514
|
+
setTimeout(() => {
|
|
515
|
+
if (autoSizeStrategy === 'fitCellContents') {
|
|
516
|
+
event.api.autoSizeAllColumns(skipHeaderOnAutoSize);
|
|
517
|
+
} else if (autoSizeStrategy === 'fitGridWidth') {
|
|
518
|
+
event.api.sizeColumnsToFit();
|
|
519
|
+
}
|
|
520
|
+
}, 100);
|
|
521
|
+
}
|
|
522
|
+
}, [sortModel, autoSizeStrategy, skipHeaderOnAutoSize]);
|
|
523
|
+
|
|
524
|
+
// Selection changed handler
|
|
525
|
+
const onSelectionChanged = useCallback(
|
|
526
|
+
(event: SelectionChangedEvent) => {
|
|
527
|
+
if (syncInProgress.current) return;
|
|
528
|
+
|
|
529
|
+
const selected = event.api.getSelectedRows() as T[];
|
|
530
|
+
setSelectedRows(selected);
|
|
531
|
+
|
|
532
|
+
if (onSelectedRowsChanged) {
|
|
533
|
+
onSelectedRowsChanged(selected);
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
// Sync single selection with DataSource
|
|
537
|
+
if (selected.length === 1 && dataSource.gotoRecordByData) {
|
|
538
|
+
syncInProgress.current = true;
|
|
539
|
+
try {
|
|
540
|
+
dataSource.gotoRecordByData(selected[0]);
|
|
541
|
+
} catch (error) {
|
|
542
|
+
console.error('[SELECTION] Error syncing with DataSource:', error);
|
|
543
|
+
}
|
|
544
|
+
setTimeout(() => {
|
|
545
|
+
syncInProgress.current = false;
|
|
546
|
+
}, 100);
|
|
547
|
+
}
|
|
548
|
+
},
|
|
549
|
+
[onSelectedRowsChanged, dataSource]
|
|
550
|
+
);
|
|
551
|
+
|
|
552
|
+
// Cell double click handler
|
|
553
|
+
const onCellDoubleClicked = useCallback(
|
|
554
|
+
(event: CellDoubleClickedEvent) => {
|
|
555
|
+
if (onCellDoubleClick && event.colDef.field) {
|
|
556
|
+
onCellDoubleClick({
|
|
557
|
+
id: safeGetRowId(event.data, getRowId) || '',
|
|
558
|
+
columnName: event.colDef.field,
|
|
559
|
+
rowData: event.data,
|
|
560
|
+
});
|
|
561
|
+
}
|
|
562
|
+
},
|
|
563
|
+
[onCellDoubleClick, getRowId]
|
|
564
|
+
);
|
|
565
|
+
|
|
566
|
+
// Sort changed handler
|
|
567
|
+
const onSortChanged = useCallback(
|
|
568
|
+
(event: SortChangedEvent) => {
|
|
569
|
+
const columnState = event.api.getColumnState();
|
|
570
|
+
const newSortModel: SortModelItem[] = columnState
|
|
571
|
+
.filter((col) => col.sort)
|
|
572
|
+
.map((col) => ({
|
|
573
|
+
colId: col.colId!,
|
|
574
|
+
sort: col.sort as 'asc' | 'desc',
|
|
575
|
+
}));
|
|
576
|
+
|
|
577
|
+
setSortModel(newSortModel);
|
|
578
|
+
|
|
579
|
+
if (manualSorting) {
|
|
580
|
+
const sortFields = convertSortModelToDataSource(newSortModel);
|
|
581
|
+
|
|
582
|
+
if (isV2) {
|
|
583
|
+
setIsLoadingInternal(true);
|
|
584
|
+
(dataSource as any).refreshData?.({
|
|
585
|
+
currentPage: paginationModel.page,
|
|
586
|
+
pageSize: paginationModel.pageSize,
|
|
587
|
+
sort: sortFields,
|
|
588
|
+
});
|
|
589
|
+
} else {
|
|
590
|
+
const options = getDataSourceOptions(dataSource);
|
|
591
|
+
options.sort = sortFields;
|
|
592
|
+
options.originSort = newSortModel;
|
|
593
|
+
setIsLoadingInternal(true);
|
|
594
|
+
(dataSource as any).refreshData?.(options);
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
closeAllDetailPanels();
|
|
598
|
+
}
|
|
599
|
+
},
|
|
600
|
+
[manualSorting, isV2, dataSource, paginationModel, closeAllDetailPanels]
|
|
601
|
+
);
|
|
602
|
+
|
|
603
|
+
// Filter changed handler (AG Grid internal filters)
|
|
604
|
+
const onFilterChanged = useCallback(
|
|
605
|
+
(event: FilterChangedEvent) => {
|
|
606
|
+
if (!manualFiltering || hideAgGridFilters) return;
|
|
607
|
+
|
|
608
|
+
const newFilterModel = event.api.getFilterModel();
|
|
609
|
+
setAgFilterModel(newFilterModel);
|
|
610
|
+
|
|
611
|
+
const rsql = buildFilterExpression(newFilterModel, columnDefs, quickFilterText);
|
|
612
|
+
|
|
613
|
+
if (isV2) {
|
|
614
|
+
setPaginationModel((prev) => ({ ...prev, page: 0 }));
|
|
615
|
+
setIsLoadingInternal(true);
|
|
616
|
+
(dataSource as any).refreshData?.({
|
|
617
|
+
currentPage: 0,
|
|
618
|
+
pageSize: paginationModel.pageSize,
|
|
619
|
+
filter: rsql,
|
|
620
|
+
});
|
|
621
|
+
} else {
|
|
622
|
+
const options = getDataSourceOptions(dataSource);
|
|
623
|
+
options.filter = rsql;
|
|
624
|
+
options.currentPage = 0;
|
|
625
|
+
setPaginationModel((prev) => ({ ...prev, page: 0 }));
|
|
626
|
+
setIsLoadingInternal(true);
|
|
627
|
+
(dataSource as any).refreshData?.(options);
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
closeAllDetailPanels();
|
|
631
|
+
},
|
|
632
|
+
[
|
|
633
|
+
manualFiltering,
|
|
634
|
+
hideAgGridFilters,
|
|
635
|
+
isV2,
|
|
636
|
+
dataSource,
|
|
637
|
+
paginationModel.pageSize,
|
|
638
|
+
columnDefs,
|
|
639
|
+
quickFilterText,
|
|
640
|
+
closeAllDetailPanels,
|
|
641
|
+
]
|
|
642
|
+
);
|
|
643
|
+
|
|
644
|
+
// Handle toolbar filter model change (for compatibility with existing toolbar)
|
|
645
|
+
const handleToolbarFilterModelChange = useCallback(
|
|
646
|
+
(newFilterModel: any) => {
|
|
647
|
+
setFilterModel(newFilterModel);
|
|
648
|
+
|
|
649
|
+
if (onFilterModelChange) {
|
|
650
|
+
onFilterModelChange(newFilterModel);
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
const quickFilterValue =
|
|
654
|
+
newFilterModel.quickFilterValues && newFilterModel.quickFilterValues.length > 0
|
|
655
|
+
? newFilterModel.quickFilterValues[0]
|
|
656
|
+
: '';
|
|
657
|
+
|
|
658
|
+
setQuickFilterText(quickFilterValue);
|
|
659
|
+
|
|
660
|
+
const rsql = buildFilterExpression(agFilterModel, columnDefs, quickFilterValue);
|
|
661
|
+
|
|
662
|
+
if (isV2) {
|
|
663
|
+
setPaginationModel((prev) => ({ ...prev, page: 0 }));
|
|
664
|
+
setIsLoadingInternal(true);
|
|
665
|
+
(dataSource as any).refreshData?.({
|
|
666
|
+
currentPage: 0,
|
|
667
|
+
pageSize: paginationModel.pageSize,
|
|
668
|
+
filter: rsql,
|
|
669
|
+
originGlobalFilter: quickFilterValue,
|
|
670
|
+
});
|
|
671
|
+
} else {
|
|
672
|
+
const options = getDataSourceOptions(dataSource);
|
|
673
|
+
options.filter = rsql;
|
|
674
|
+
options.originFilter = newFilterModel.items;
|
|
675
|
+
options.originGlobalFilter = quickFilterValue;
|
|
676
|
+
options.currentPage = 0;
|
|
677
|
+
setPaginationModel((prev) => ({ ...prev, page: 0 }));
|
|
678
|
+
setIsLoadingInternal(true);
|
|
679
|
+
(dataSource as any).refreshData?.(options);
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
closeAllDetailPanels();
|
|
683
|
+
},
|
|
684
|
+
[
|
|
685
|
+
onFilterModelChange,
|
|
686
|
+
agFilterModel,
|
|
687
|
+
columnDefs,
|
|
688
|
+
isV2,
|
|
689
|
+
dataSource,
|
|
690
|
+
paginationModel.pageSize,
|
|
691
|
+
closeAllDetailPanels,
|
|
692
|
+
]
|
|
693
|
+
);
|
|
694
|
+
|
|
695
|
+
// Handle composite filters change
|
|
696
|
+
const handleCompositeFiltersChange = useCallback(
|
|
697
|
+
(filters: any[], rsql?: string) => {
|
|
698
|
+
if (externalActiveFilters === undefined) {
|
|
699
|
+
setInternalActiveFilters(filters);
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
if (externalOnFiltersChange) {
|
|
703
|
+
externalOnFiltersChange(filters, rsql);
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
if (rsql || (filters && filters.length > 0)) {
|
|
707
|
+
const agModel = convertActiveFiltersToAgGridModel(filters || []);
|
|
708
|
+
setAgFilterModel(agModel);
|
|
709
|
+
setFilterModel({
|
|
710
|
+
items: filters.map((f) => ({ field: f.key, value: f.value, operator: f.operator })),
|
|
711
|
+
quickFilterValues: [],
|
|
712
|
+
});
|
|
713
|
+
|
|
714
|
+
if (isV2) {
|
|
715
|
+
setPaginationModel((prev) => ({ ...prev, page: 0 }));
|
|
716
|
+
setIsLoadingInternal(true);
|
|
717
|
+
(dataSource as any).refreshData?.({
|
|
718
|
+
currentPage: 0,
|
|
719
|
+
pageSize: paginationModel.pageSize,
|
|
720
|
+
filter: rsql,
|
|
721
|
+
});
|
|
722
|
+
} else {
|
|
723
|
+
const options = getDataSourceOptions(dataSource);
|
|
724
|
+
options.filter = rsql;
|
|
725
|
+
options.currentPage = 0;
|
|
726
|
+
setIsLoadingInternal(true);
|
|
727
|
+
(dataSource as any).refreshData?.(options);
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
closeAllDetailPanels();
|
|
731
|
+
} else {
|
|
732
|
+
setFilterModel({ items: [], quickFilterValues: [] });
|
|
733
|
+
setAgFilterModel({});
|
|
734
|
+
|
|
735
|
+
if (isV2) {
|
|
736
|
+
setIsLoadingInternal(true);
|
|
737
|
+
(dataSource as any).refreshData?.({
|
|
738
|
+
currentPage: 0,
|
|
739
|
+
pageSize: paginationModel.pageSize,
|
|
740
|
+
filter: undefined,
|
|
741
|
+
});
|
|
742
|
+
} else {
|
|
743
|
+
const options = getDataSourceOptions(dataSource);
|
|
744
|
+
options.filter = undefined;
|
|
745
|
+
options.originFilter = [];
|
|
746
|
+
setIsLoadingInternal(true);
|
|
747
|
+
(dataSource as any).refreshData?.(options);
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
closeAllDetailPanels();
|
|
751
|
+
}
|
|
752
|
+
},
|
|
753
|
+
[
|
|
754
|
+
externalActiveFilters,
|
|
755
|
+
externalOnFiltersChange,
|
|
756
|
+
isV2,
|
|
757
|
+
dataSource,
|
|
758
|
+
paginationModel.pageSize,
|
|
759
|
+
closeAllDetailPanels,
|
|
760
|
+
]
|
|
761
|
+
);
|
|
762
|
+
|
|
763
|
+
// Refresh handler
|
|
764
|
+
const handleRefresh = useCallback(() => {
|
|
765
|
+
const rsql = buildFilterExpression(agFilterModel, columnDefs, quickFilterText);
|
|
766
|
+
|
|
767
|
+
if (isV2) {
|
|
768
|
+
setIsLoadingInternal(true);
|
|
769
|
+
(dataSource as any).refreshData?.({
|
|
770
|
+
currentPage: paginationModel.page,
|
|
771
|
+
pageSize: paginationModel.pageSize,
|
|
772
|
+
filter: rsql,
|
|
773
|
+
originGlobalFilter: quickFilterText,
|
|
774
|
+
});
|
|
775
|
+
} else {
|
|
776
|
+
const options = getDataSourceOptions(dataSource);
|
|
777
|
+
options.filter = rsql;
|
|
778
|
+
options.originGlobalFilter = quickFilterText;
|
|
779
|
+
setIsLoadingInternal(true);
|
|
780
|
+
(dataSource as any).refreshData?.(options);
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
closeAllDetailPanels();
|
|
784
|
+
}, [
|
|
785
|
+
agFilterModel,
|
|
786
|
+
columnDefs,
|
|
787
|
+
quickFilterText,
|
|
788
|
+
isV2,
|
|
789
|
+
dataSource,
|
|
790
|
+
paginationModel,
|
|
791
|
+
closeAllDetailPanels,
|
|
792
|
+
]);
|
|
793
|
+
|
|
794
|
+
// Pagination handler
|
|
795
|
+
const handlePaginationChange = useCallback(
|
|
796
|
+
(newPaginationModel: { page: number; pageSize: number }) => {
|
|
797
|
+
const safePageSize = Math.min(newPaginationModel.pageSize, MAX_PAGE_SIZE);
|
|
798
|
+
|
|
799
|
+
setPaginationModel({
|
|
800
|
+
page: newPaginationModel.page,
|
|
801
|
+
pageSize: safePageSize,
|
|
802
|
+
});
|
|
803
|
+
|
|
804
|
+
if (manualPagination) {
|
|
805
|
+
const rsql = buildFilterExpression(agFilterModel, columnDefs, quickFilterText);
|
|
806
|
+
|
|
807
|
+
if (isV2) {
|
|
808
|
+
setIsLoadingInternal(true);
|
|
809
|
+
(dataSource as any).refreshData?.({
|
|
810
|
+
currentPage: newPaginationModel.page,
|
|
811
|
+
pageSize: safePageSize,
|
|
812
|
+
filter: rsql,
|
|
813
|
+
});
|
|
814
|
+
} else {
|
|
815
|
+
const options = getDataSourceOptions(dataSource);
|
|
816
|
+
options.currentPage = newPaginationModel.page;
|
|
817
|
+
options.pageSize = safePageSize;
|
|
818
|
+
setIsLoadingInternal(true);
|
|
819
|
+
(dataSource as any).refreshData?.(options);
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
closeAllDetailPanels();
|
|
823
|
+
}
|
|
824
|
+
},
|
|
825
|
+
[manualPagination, agFilterModel, columnDefs, quickFilterText, isV2, dataSource, closeAllDetailPanels]
|
|
826
|
+
);
|
|
827
|
+
|
|
828
|
+
// Export/Print handlers
|
|
829
|
+
const handleExportClick = useCallback(() => setExportModalOpen(true), []);
|
|
830
|
+
const handlePrintClick = useCallback(() => setPrintModalOpen(true), []);
|
|
831
|
+
|
|
832
|
+
const handleExportConfirm = useCallback(
|
|
833
|
+
(exportConfig: ExportConfig) => {
|
|
834
|
+
exportData(rows, columnDefs as any, exportConfig);
|
|
835
|
+
setExportModalOpen(false);
|
|
836
|
+
},
|
|
837
|
+
[rows, columnDefs]
|
|
838
|
+
);
|
|
839
|
+
|
|
840
|
+
const handlePrintConfirm = useCallback(
|
|
841
|
+
(printConfig: PrintConfig) => {
|
|
842
|
+
printData(rows, columnDefs as any, printConfig);
|
|
843
|
+
setPrintModalOpen(false);
|
|
844
|
+
},
|
|
845
|
+
[rows, columnDefs]
|
|
846
|
+
);
|
|
847
|
+
|
|
848
|
+
// Register export/print callbacks
|
|
849
|
+
useEffect(() => {
|
|
850
|
+
if (onExport) onExport(handleExportClick);
|
|
851
|
+
if (onPrint) onPrint(handlePrintClick);
|
|
852
|
+
}, [onExport, onPrint, handleExportClick, handlePrintClick]);
|
|
853
|
+
|
|
854
|
+
// Expose methods via ref
|
|
855
|
+
useImperativeHandle(
|
|
856
|
+
gridRef,
|
|
857
|
+
() => ({
|
|
858
|
+
refreshData: handleRefresh,
|
|
859
|
+
getSelectedRows: () => selectedRows,
|
|
860
|
+
clearSelection: () => {
|
|
861
|
+
gridApiRef.current?.deselectAll();
|
|
862
|
+
setSelectedRows([]);
|
|
863
|
+
},
|
|
864
|
+
exportData: handleExportClick,
|
|
865
|
+
printData: handlePrintClick,
|
|
866
|
+
expandRow: (rowId) => expandDetailPanel(rowId),
|
|
867
|
+
collapseRow: (rowId) => closeDetailPanel(rowId),
|
|
868
|
+
collapseAllRows: closeAllDetailPanels,
|
|
869
|
+
getExpandedRows: () => Array.from(expandedRowIds),
|
|
870
|
+
getFilterModel: () => filterModel,
|
|
871
|
+
getGridApi: () => gridApiRef.current,
|
|
872
|
+
autoSizeAllColumns: (skipHeader = false) => {
|
|
873
|
+
gridApiRef.current?.autoSizeAllColumns(skipHeader);
|
|
874
|
+
},
|
|
875
|
+
sizeColumnsToFit: () => {
|
|
876
|
+
gridApiRef.current?.sizeColumnsToFit();
|
|
877
|
+
},
|
|
878
|
+
}),
|
|
879
|
+
[
|
|
880
|
+
selectedRows,
|
|
881
|
+
expandedRowIds,
|
|
882
|
+
expandDetailPanel,
|
|
883
|
+
closeDetailPanel,
|
|
884
|
+
closeAllDetailPanels,
|
|
885
|
+
filterModel,
|
|
886
|
+
handleRefresh,
|
|
887
|
+
handleExportClick,
|
|
888
|
+
handlePrintClick,
|
|
889
|
+
]
|
|
890
|
+
);
|
|
891
|
+
|
|
892
|
+
// DataSource event listener
|
|
893
|
+
useEffect(() => {
|
|
894
|
+
const handleDataSourceEvent = (event: DataSourceEvent<T>) => {
|
|
895
|
+
if (
|
|
896
|
+
event.type === DataSourceEventNames.refreshData ||
|
|
897
|
+
event.type === DataSourceEventNames.dataChanged ||
|
|
898
|
+
event.type === DataSourceEventNames.afterRemove ||
|
|
899
|
+
event.type === DataSourceEventNames.afterSave ||
|
|
900
|
+
event.type === DataSourceEventNames.afterAppend ||
|
|
901
|
+
event.type === DataSourceEventNames.afterCancel
|
|
902
|
+
) {
|
|
903
|
+
setRows(getRecordsFromDataSource<T>(dataSource));
|
|
904
|
+
|
|
905
|
+
const currentPage = Number.isFinite(getCurrentPageFromDataSource(dataSource))
|
|
906
|
+
? getCurrentPageFromDataSource(dataSource)
|
|
907
|
+
: 0;
|
|
908
|
+
|
|
909
|
+
const pageSize = Number.isFinite(dataSource.getPageSize?.())
|
|
910
|
+
? Math.min(Math.max(1, dataSource.getPageSize()), MAX_PAGE_SIZE)
|
|
911
|
+
: 10;
|
|
912
|
+
|
|
913
|
+
setPaginationModel({ page: currentPage, pageSize });
|
|
914
|
+
|
|
915
|
+
const grandTotal = Number.isFinite(dataSource.getGrandTotalRecords?.())
|
|
916
|
+
? Math.max(0, dataSource.getGrandTotalRecords())
|
|
917
|
+
: 0;
|
|
918
|
+
|
|
919
|
+
setTotalRecords(grandTotal);
|
|
920
|
+
setIsLoadingInternal(false);
|
|
921
|
+
closeAllDetailPanels();
|
|
922
|
+
} else if (event.type === DataSourceEventNames.onError) {
|
|
923
|
+
// Reset loading state when an error occurs
|
|
924
|
+
setIsLoadingInternal(false);
|
|
925
|
+
} else if (event.type === DataSourceEventNames.afterScroll) {
|
|
926
|
+
if (syncInProgress.current) return;
|
|
927
|
+
|
|
928
|
+
const currentRecord = dataSource.getCurrentRecord();
|
|
929
|
+
if (currentRecord && gridApiRef.current) {
|
|
930
|
+
try {
|
|
931
|
+
const currentId = safeGetRowId(currentRecord, getRowId);
|
|
932
|
+
if (currentId !== undefined) {
|
|
933
|
+
syncInProgress.current = true;
|
|
934
|
+
|
|
935
|
+
gridApiRef.current.deselectAll();
|
|
936
|
+
gridApiRef.current.forEachNode((node) => {
|
|
937
|
+
const nodeId = safeGetRowId(node.data, getRowId);
|
|
938
|
+
if (String(nodeId) === String(currentId)) {
|
|
939
|
+
node.setSelected(true);
|
|
940
|
+
gridApiRef.current?.ensureNodeVisible(node);
|
|
941
|
+
}
|
|
942
|
+
});
|
|
943
|
+
|
|
944
|
+
setSelectedRows([currentRecord]);
|
|
945
|
+
if (onSelectedRowsChanged) {
|
|
946
|
+
onSelectedRowsChanged([currentRecord]);
|
|
947
|
+
}
|
|
948
|
+
|
|
949
|
+
setTimeout(() => {
|
|
950
|
+
syncInProgress.current = false;
|
|
951
|
+
}, 100);
|
|
952
|
+
}
|
|
953
|
+
} catch (error) {
|
|
954
|
+
console.error('[DATASOURCE] Error processing afterScroll:', error);
|
|
955
|
+
syncInProgress.current = false;
|
|
956
|
+
}
|
|
957
|
+
}
|
|
958
|
+
}
|
|
959
|
+
};
|
|
960
|
+
|
|
961
|
+
dataSource.addListener(handleDataSourceEvent);
|
|
962
|
+
return () => {
|
|
963
|
+
dataSource.removeListener(handleDataSourceEvent);
|
|
964
|
+
};
|
|
965
|
+
}, [dataSource, getRowId, onSelectedRowsChanged, closeAllDetailPanels]);
|
|
966
|
+
|
|
967
|
+
// Modal columns for export/print
|
|
968
|
+
const modalColumns = useMemo(() => {
|
|
969
|
+
return columnDefs
|
|
970
|
+
.filter((col) => col.field && !col.field.startsWith('__'))
|
|
971
|
+
.map((col) => ({
|
|
972
|
+
id: col.field!,
|
|
973
|
+
title: col.headerName || col.field!,
|
|
974
|
+
}));
|
|
975
|
+
}, [columnDefs]);
|
|
976
|
+
|
|
977
|
+
// Grid container styles
|
|
978
|
+
const containerStyle = useMemo(
|
|
979
|
+
() => ({
|
|
980
|
+
height,
|
|
981
|
+
width,
|
|
982
|
+
display: 'flex',
|
|
983
|
+
flexDirection: 'column' as const,
|
|
984
|
+
overflow: 'hidden',
|
|
985
|
+
border: withBorder
|
|
986
|
+
? `1px solid ${theme.colors.gray[colorScheme === 'dark' ? 8 : 3]}`
|
|
987
|
+
: 'none',
|
|
988
|
+
borderRadius: theme.radius.sm,
|
|
989
|
+
backgroundColor: colorScheme === 'dark' ? theme.colors.dark[6] : theme.white,
|
|
990
|
+
}),
|
|
991
|
+
[height, width, withBorder, theme, colorScheme]
|
|
992
|
+
);
|
|
993
|
+
|
|
994
|
+
// CSS vars for additional styling
|
|
995
|
+
const cssVars = useMemo(
|
|
996
|
+
() => getAgGridMantineCssVars(theme, colorScheme),
|
|
997
|
+
[theme, colorScheme]
|
|
998
|
+
);
|
|
999
|
+
|
|
1000
|
+
// Custom CSS for cell focus styling
|
|
1001
|
+
const customStyles = useMemo(
|
|
1002
|
+
() => getAgGridCustomStyles(theme, colorScheme),
|
|
1003
|
+
[theme, colorScheme]
|
|
1004
|
+
);
|
|
1005
|
+
|
|
1006
|
+
// Render fixed detail panel (inline mode)
|
|
1007
|
+
const renderFixedDetailPanel = () => {
|
|
1008
|
+
if (!renderDetailPanel || expandedRowIds.size === 0) return null;
|
|
1009
|
+
|
|
1010
|
+
const rowId = Array.from(expandedRowIds)[0];
|
|
1011
|
+
const rowData = rows.find((row) => String(safeGetRowId(row, getRowId)) === String(rowId));
|
|
1012
|
+
|
|
1013
|
+
if (!rowData) return null;
|
|
1014
|
+
|
|
1015
|
+
const panelTitle =
|
|
1016
|
+
typeof detailPanelTitle === 'function' ? detailPanelTitle(rowId, rowData) : detailPanelTitle;
|
|
1017
|
+
|
|
1018
|
+
const detailContent = renderDetailPanel({ row: rowData });
|
|
1019
|
+
|
|
1020
|
+
return (
|
|
1021
|
+
<div
|
|
1022
|
+
key={`fixed-detail-panel-${rowId}`}
|
|
1023
|
+
ref={(el) => {
|
|
1024
|
+
if (el) detailPanelRefs.current.set(rowId, el);
|
|
1025
|
+
}}
|
|
1026
|
+
style={{
|
|
1027
|
+
width: '100%',
|
|
1028
|
+
borderBottom: `1px solid ${theme.colors.gray[colorScheme === 'dark' ? 7 : 3]}`,
|
|
1029
|
+
marginBottom: '8px',
|
|
1030
|
+
zIndex: 2,
|
|
1031
|
+
}}
|
|
1032
|
+
>
|
|
1033
|
+
<Box
|
|
1034
|
+
className={`detail-panel-container ${detailPanelClassName || ''}`}
|
|
1035
|
+
style={{
|
|
1036
|
+
padding: 8,
|
|
1037
|
+
backgroundColor: colorScheme === 'dark' ? theme.colors.dark[5] : theme.colors.gray[0],
|
|
1038
|
+
borderTop: `1px solid ${theme.colors.gray[colorScheme === 'dark' ? 7 : 3]}`,
|
|
1039
|
+
width: '100%',
|
|
1040
|
+
...(detailPanelStyle || {}),
|
|
1041
|
+
}}
|
|
1042
|
+
>
|
|
1043
|
+
<Group justify="apart" mb="xs">
|
|
1044
|
+
<Title order={4} style={{ fontSize: '1rem', fontWeight: 500 }}>
|
|
1045
|
+
{panelTitle}
|
|
1046
|
+
</Title>
|
|
1047
|
+
<ActionIcon onClick={() => closeDetailPanel(rowId)} size="sm" color="gray">
|
|
1048
|
+
<IconX size={16} />
|
|
1049
|
+
</ActionIcon>
|
|
1050
|
+
</Group>
|
|
1051
|
+
<Box>{detailContent}</Box>
|
|
1052
|
+
</Box>
|
|
1053
|
+
</div>
|
|
1054
|
+
);
|
|
1055
|
+
};
|
|
1056
|
+
|
|
1057
|
+
// Render floating detail panels (modal/drawer mode)
|
|
1058
|
+
const renderFloatingDetailPanels = () => {
|
|
1059
|
+
if (!renderDetailPanel || expandedRowIds.size === 0) return null;
|
|
1060
|
+
|
|
1061
|
+
return Array.from(expandedRowIds).map((rowId) => {
|
|
1062
|
+
const rowData = rows.find((row) => String(safeGetRowId(row, getRowId)) === String(rowId));
|
|
1063
|
+
if (!rowData) return null;
|
|
1064
|
+
|
|
1065
|
+
const panelTitle =
|
|
1066
|
+
typeof detailPanelTitle === 'function' ? detailPanelTitle(rowId, rowData) : detailPanelTitle;
|
|
1067
|
+
|
|
1068
|
+
if (detailPanelDisplayMode === 'modal') {
|
|
1069
|
+
return (
|
|
1070
|
+
<ArchbaseDetailModal
|
|
1071
|
+
key={`detail-modal-${rowId}`}
|
|
1072
|
+
rowId={rowId}
|
|
1073
|
+
rowData={rowData}
|
|
1074
|
+
renderDetailPanel={renderDetailPanel}
|
|
1075
|
+
onClose={closeDetailPanel}
|
|
1076
|
+
theme={theme}
|
|
1077
|
+
className={detailPanelClassName}
|
|
1078
|
+
style={detailPanelStyle}
|
|
1079
|
+
opened={expandedRowIds.has(rowId)}
|
|
1080
|
+
title={panelTitle}
|
|
1081
|
+
/>
|
|
1082
|
+
);
|
|
1083
|
+
} else if (detailPanelDisplayMode === 'drawer') {
|
|
1084
|
+
return (
|
|
1085
|
+
<ArchbaseDetailDrawer
|
|
1086
|
+
key={`detail-drawer-${rowId}`}
|
|
1087
|
+
rowId={rowId}
|
|
1088
|
+
rowData={rowData}
|
|
1089
|
+
renderDetailPanel={renderDetailPanel}
|
|
1090
|
+
onClose={closeDetailPanel}
|
|
1091
|
+
theme={theme}
|
|
1092
|
+
className={detailPanelClassName}
|
|
1093
|
+
style={detailPanelStyle}
|
|
1094
|
+
opened={expandedRowIds.has(rowId)}
|
|
1095
|
+
title={panelTitle}
|
|
1096
|
+
position={detailPanelPosition}
|
|
1097
|
+
size={detailPanelSize}
|
|
1098
|
+
/>
|
|
1099
|
+
);
|
|
1100
|
+
} else if (detailPanelDisplayMode === 'auto' && shouldUseModal) {
|
|
1101
|
+
return (
|
|
1102
|
+
<ArchbaseDetailModal
|
|
1103
|
+
key={`detail-modal-${rowId}`}
|
|
1104
|
+
rowId={rowId}
|
|
1105
|
+
rowData={rowData}
|
|
1106
|
+
renderDetailPanel={renderDetailPanel}
|
|
1107
|
+
onClose={closeDetailPanel}
|
|
1108
|
+
theme={theme}
|
|
1109
|
+
className={detailPanelClassName}
|
|
1110
|
+
style={detailPanelStyle}
|
|
1111
|
+
opened={expandedRowIds.has(rowId)}
|
|
1112
|
+
title={panelTitle}
|
|
1113
|
+
/>
|
|
1114
|
+
);
|
|
1115
|
+
}
|
|
1116
|
+
|
|
1117
|
+
return null;
|
|
1118
|
+
});
|
|
1119
|
+
};
|
|
1120
|
+
|
|
1121
|
+
return (
|
|
1122
|
+
<Box
|
|
1123
|
+
className={`archbase-data-grid-ag ${className}`}
|
|
1124
|
+
ref={gridContainerRef}
|
|
1125
|
+
style={containerStyle}
|
|
1126
|
+
>
|
|
1127
|
+
{/* Custom styles for cell focus */}
|
|
1128
|
+
<style dangerouslySetInnerHTML={{ __html: customStyles }} />
|
|
1129
|
+
|
|
1130
|
+
{/* Toolbar */}
|
|
1131
|
+
{enableTopToolbar && (
|
|
1132
|
+
<ArchbaseDataGridToolbar
|
|
1133
|
+
dataSource={dataSource}
|
|
1134
|
+
filterModel={filterModel}
|
|
1135
|
+
enableGlobalFilter={enableGlobalFilter}
|
|
1136
|
+
enableTopToolbarActions={enableTopToolbarActions}
|
|
1137
|
+
allowExportData={allowExportData}
|
|
1138
|
+
allowPrintData={allowPrintData}
|
|
1139
|
+
toolbarAlignment={toolbarAlignment}
|
|
1140
|
+
toolbarLeftContent={toolbarLeftContent}
|
|
1141
|
+
renderToolbarActions={renderToolbarActions}
|
|
1142
|
+
renderToolbarInternalActions={renderToolbarInternalActions}
|
|
1143
|
+
theme={theme}
|
|
1144
|
+
onFilterModelChange={handleToolbarFilterModelChange}
|
|
1145
|
+
onRefresh={handleRefresh}
|
|
1146
|
+
onExport={handleExportClick}
|
|
1147
|
+
onPrint={handlePrintClick}
|
|
1148
|
+
apiRef={{ current: gridApiRef.current }}
|
|
1149
|
+
children={children}
|
|
1150
|
+
useCompositeFilters={useCompositeFilters}
|
|
1151
|
+
filterDefinitions={autoGeneratedFilterDefinitions}
|
|
1152
|
+
activeFilters={activeFilters}
|
|
1153
|
+
onFiltersChange={handleCompositeFiltersChange}
|
|
1154
|
+
hideMuiFilters={hideAgGridFilters}
|
|
1155
|
+
withBorder={withToolbarBorder}
|
|
1156
|
+
padding={toolbarPadding}
|
|
1157
|
+
/>
|
|
1158
|
+
)}
|
|
1159
|
+
|
|
1160
|
+
{/* Inline Detail Panel */}
|
|
1161
|
+
{(detailPanelDisplayMode === 'inline' ||
|
|
1162
|
+
(detailPanelDisplayMode === 'auto' && !shouldUseModal)) &&
|
|
1163
|
+
renderFixedDetailPanel()}
|
|
1164
|
+
|
|
1165
|
+
{/* AG Grid */}
|
|
1166
|
+
<Box style={{ flex: 1, overflow: 'hidden', position: 'relative', minHeight: 0, ...cssVars }}>
|
|
1167
|
+
<AgGridReact
|
|
1168
|
+
theme={agGridTheme}
|
|
1169
|
+
rowData={rows}
|
|
1170
|
+
columnDefs={columnDefs}
|
|
1171
|
+
defaultColDef={defaultColDef}
|
|
1172
|
+
rowSelection={rowSelection}
|
|
1173
|
+
onGridReady={onGridReady}
|
|
1174
|
+
onSelectionChanged={onSelectionChanged}
|
|
1175
|
+
onCellDoubleClicked={onCellDoubleClicked}
|
|
1176
|
+
onSortChanged={onSortChanged}
|
|
1177
|
+
onFilterChanged={onFilterChanged}
|
|
1178
|
+
getRowId={(params: GetRowIdParams<T>) => {
|
|
1179
|
+
const id = safeGetRowId(params.data, getRowId);
|
|
1180
|
+
return id !== undefined ? String(id) : '';
|
|
1181
|
+
}}
|
|
1182
|
+
rowHeight={rowHeight}
|
|
1183
|
+
headerHeight={40}
|
|
1184
|
+
loading={isLoadingInternal || isLoading}
|
|
1185
|
+
suppressCellFocus={false}
|
|
1186
|
+
suppressRowClickSelection={true}
|
|
1187
|
+
animateRows={false}
|
|
1188
|
+
localeText={AG_GRID_LOCALE_PTBR}
|
|
1189
|
+
suppressPaginationPanel={true}
|
|
1190
|
+
suppressScrollOnNewData={true}
|
|
1191
|
+
enableCellTextSelection={true}
|
|
1192
|
+
ensureDomOrder={true}
|
|
1193
|
+
/>
|
|
1194
|
+
</Box>
|
|
1195
|
+
|
|
1196
|
+
{/* Pagination */}
|
|
1197
|
+
{showPagination && (
|
|
1198
|
+
<ArchbaseDataGridPagination
|
|
1199
|
+
paginationModel={paginationModel}
|
|
1200
|
+
totalRecords={totalRecords}
|
|
1201
|
+
onPaginationModelChange={handlePaginationChange}
|
|
1202
|
+
paginationLabels={paginationLabels}
|
|
1203
|
+
bottomToolbarMinHeight={bottomToolbarMinHeight}
|
|
1204
|
+
theme={theme}
|
|
1205
|
+
withBorder={withPaginationBorder}
|
|
1206
|
+
padding={paginationPadding}
|
|
1207
|
+
/>
|
|
1208
|
+
)}
|
|
1209
|
+
|
|
1210
|
+
{/* Floating Detail Panels */}
|
|
1211
|
+
{renderFloatingDetailPanels()}
|
|
1212
|
+
|
|
1213
|
+
{/* Export Modal */}
|
|
1214
|
+
<ExportModal
|
|
1215
|
+
opened={exportModalOpen}
|
|
1216
|
+
onClose={() => setExportModalOpen(false)}
|
|
1217
|
+
onExport={handleExportConfirm}
|
|
1218
|
+
columns={modalColumns}
|
|
1219
|
+
defaultConfig={{
|
|
1220
|
+
filename: printTitle || 'export',
|
|
1221
|
+
dateFormat: globalDateFormat,
|
|
1222
|
+
}}
|
|
1223
|
+
/>
|
|
1224
|
+
|
|
1225
|
+
{/* Print Modal */}
|
|
1226
|
+
<PrintModal
|
|
1227
|
+
opened={printModalOpen}
|
|
1228
|
+
onClose={() => setPrintModalOpen(false)}
|
|
1229
|
+
onPrint={handlePrintConfirm}
|
|
1230
|
+
columns={modalColumns}
|
|
1231
|
+
defaultConfig={{
|
|
1232
|
+
title: printTitle,
|
|
1233
|
+
orientation: 'landscape',
|
|
1234
|
+
pageSize: 'A4',
|
|
1235
|
+
}}
|
|
1236
|
+
/>
|
|
1237
|
+
</Box>
|
|
1238
|
+
);
|
|
1239
|
+
}
|
|
1240
|
+
|
|
1241
|
+
export default ArchbaseDataGridAG;
|