@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.
Files changed (55) hide show
  1. package/dist/archbase-components-4.0.20.tgz +0 -0
  2. package/dist/datagrid/ag-grid/ag-grid-locale-ptbr.d.ts +7 -0
  3. package/dist/datagrid/ag-grid/ag-grid-mantine-theme.d.ts +24 -0
  4. package/dist/datagrid/ag-grid/archbase-data-grid-ag-formatters.d.ts +88 -0
  5. package/dist/datagrid/ag-grid/archbase-data-grid-ag-types.d.ts +329 -0
  6. package/dist/datagrid/ag-grid/archbase-data-grid-ag-utils.d.ts +71 -0
  7. package/dist/datagrid/ag-grid/archbase-data-grid-ag.d.ts +14 -0
  8. package/dist/datagrid/ag-grid/index.d.ts +14 -0
  9. package/dist/datagrid/index.d.ts +9 -1
  10. package/dist/index.js +11175 -10592
  11. package/package.json +6 -4
  12. package/src/datagrid/ag-grid/ag-grid-locale-ptbr.ts +432 -0
  13. package/src/datagrid/ag-grid/ag-grid-mantine-theme.ts +176 -0
  14. package/src/datagrid/ag-grid/archbase-data-grid-ag-formatters.tsx +470 -0
  15. package/src/datagrid/ag-grid/archbase-data-grid-ag-types.tsx +413 -0
  16. package/src/datagrid/ag-grid/archbase-data-grid-ag-utils.ts +394 -0
  17. package/src/datagrid/ag-grid/archbase-data-grid-ag.tsx +1241 -0
  18. package/src/datagrid/ag-grid/index.tsx +84 -0
  19. package/src/datagrid/index.tsx +59 -8
  20. package/src/datagrid/main/archbase-data-grid-pagination.tsx +1 -2
  21. package/src/datagrid/main/archbase-data-grid-toolbar.tsx +1 -2
  22. package/src/editors/ArchbaseAsyncMultiSelect.tsx +2 -1
  23. package/src/editors/ArchbaseAsyncSelect.tsx +2 -1
  24. package/src/editors/ArchbaseAvatarEdit.tsx +1 -0
  25. package/src/editors/ArchbaseCheckbox.tsx +1 -0
  26. package/src/editors/ArchbaseChip.tsx +1 -0
  27. package/src/editors/ArchbaseChipGroup.tsx +1 -0
  28. package/src/editors/ArchbaseColorGradientPicker.tsx +2 -1
  29. package/src/editors/ArchbaseDatePickerEdit.tsx +1 -0
  30. package/src/editors/ArchbaseDateTimePickerEdit.tsx +1 -0
  31. package/src/editors/ArchbaseDualListbox.tsx +1 -0
  32. package/src/editors/ArchbaseEdit.tsx +1 -0
  33. package/src/editors/ArchbaseImageEdit.tsx +1 -0
  34. package/src/editors/ArchbaseJsonEdit.tsx +1 -0
  35. package/src/editors/ArchbaseLookupEdit.tsx +2 -1
  36. package/src/editors/ArchbaseLookupNumber.tsx +2 -1
  37. package/src/editors/ArchbaseLookupSelect.tsx +2 -1
  38. package/src/editors/ArchbaseMarkdownEdit.tsx +1 -0
  39. package/src/editors/ArchbaseMaskEdit.tsx +1 -0
  40. package/src/editors/ArchbaseMentionInput.tsx +1 -0
  41. package/src/editors/ArchbaseMultiEmail.tsx +1 -0
  42. package/src/editors/ArchbaseMultiSelect.tsx +2 -1
  43. package/src/editors/ArchbaseNumberEdit.tsx +1 -0
  44. package/src/editors/ArchbaseNumberStepper.tsx +2 -1
  45. package/src/editors/ArchbasePasswordEdit.tsx +1 -0
  46. package/src/editors/ArchbaseRadioGroup.tsx +1 -0
  47. package/src/editors/ArchbaseRating.tsx +1 -0
  48. package/src/editors/ArchbaseRichTextEdit.tsx +1 -0
  49. package/src/editors/ArchbaseSelect.tsx +2 -1
  50. package/src/editors/ArchbaseSignaturePad.tsx +1 -0
  51. package/src/editors/ArchbaseSwitch.tsx +1 -0
  52. package/src/editors/ArchbaseTagInputEdit.tsx +1 -0
  53. package/src/editors/ArchbaseTextArea.tsx +1 -0
  54. package/src/editors/ArchbaseTimeEdit.tsx +1 -0
  55. 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;