react-kd-grid 2.2.0 → 2.2.2

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 (142) hide show
  1. package/dist/hooks/useAdvancedFiltering.d.ts +3 -1
  2. package/dist/icons/index.d.ts +48 -0
  3. package/dist/index.esm.js +1 -1
  4. package/dist/index.esm.js.map +1 -1
  5. package/dist/index.js +1 -1
  6. package/dist/index.js.map +1 -1
  7. package/package.json +1 -2
  8. package/dist/cjs/CustomGrid.d.ts +0 -3
  9. package/dist/cjs/components/ColumnFilterSelector.d.ts +0 -18
  10. package/dist/cjs/components/CustomSelect.d.ts +0 -14
  11. package/dist/cjs/components/FooterAggregate.d.ts +0 -16
  12. package/dist/cjs/components/GridHeader.d.ts +0 -33
  13. package/dist/cjs/components/GridRows.d.ts +0 -49
  14. package/dist/cjs/components/GroupBar.d.ts +0 -12
  15. package/dist/cjs/components/GroupHeader.d.ts +0 -29
  16. package/dist/cjs/components/NoDataMessage.d.ts +0 -7
  17. package/dist/cjs/components/PaginationControls.d.ts +0 -15
  18. package/dist/cjs/components/Popover.d.ts +0 -17
  19. package/dist/cjs/components/RowContextMenu.d.ts +0 -22
  20. package/dist/cjs/components/SearchToolbar.d.ts +0 -79
  21. package/dist/cjs/components/filters/BooleanFilter.d.ts +0 -7
  22. package/dist/cjs/components/filters/DateFilter.d.ts +0 -9
  23. package/dist/cjs/components/filters/FilterContent.d.ts +0 -9
  24. package/dist/cjs/components/filters/FilterPopup.d.ts +0 -2
  25. package/dist/cjs/components/filters/MultiselectFilter.d.ts +0 -10
  26. package/dist/cjs/components/filters/NumberFilter.d.ts +0 -9
  27. package/dist/cjs/components/filters/TextFilter.d.ts +0 -8
  28. package/dist/cjs/components/filters/index.d.ts +0 -6
  29. package/dist/cjs/components/ui/DatePicker.d.ts +0 -10
  30. package/dist/cjs/constants.d.ts +0 -1
  31. package/dist/cjs/hooks/useAdvancedFiltering.d.ts +0 -16
  32. package/dist/cjs/hooks/useDataWorker.d.ts +0 -10
  33. package/dist/cjs/hooks/useExport.d.ts +0 -15
  34. package/dist/cjs/hooks/useFilteringAndSorting.d.ts +0 -16
  35. package/dist/cjs/hooks/useGrouping.d.ts +0 -28
  36. package/dist/cjs/hooks/usePagination.d.ts +0 -28
  37. package/dist/cjs/hooks/useSelection.d.ts +0 -13
  38. package/dist/cjs/hooks/useVirtualization.d.ts +0 -17
  39. package/dist/cjs/index.d.ts +0 -11
  40. package/dist/cjs/index.js +0 -2
  41. package/dist/cjs/index.js.map +0 -1
  42. package/dist/cjs/types.d.ts +0 -421
  43. package/dist/cjs/utils/highlightText.d.ts +0 -15
  44. package/dist/cjs/workers/dataWorker.d.ts +0 -16
  45. package/dist/core/DataGrid/DataGrid.d.ts +0 -3
  46. package/dist/esm/CustomGrid.d.ts +0 -3
  47. package/dist/esm/CustomGrid.js +0 -2
  48. package/dist/esm/CustomGrid.js.map +0 -1
  49. package/dist/esm/components/ColumnFilterSelector.d.ts +0 -18
  50. package/dist/esm/components/ColumnFilterSelector.js +0 -2
  51. package/dist/esm/components/ColumnFilterSelector.js.map +0 -1
  52. package/dist/esm/components/CustomSelect.d.ts +0 -14
  53. package/dist/esm/components/CustomSelect.js +0 -2
  54. package/dist/esm/components/CustomSelect.js.map +0 -1
  55. package/dist/esm/components/FooterAggregate.d.ts +0 -16
  56. package/dist/esm/components/FooterAggregate.js +0 -2
  57. package/dist/esm/components/FooterAggregate.js.map +0 -1
  58. package/dist/esm/components/GridHeader.d.ts +0 -33
  59. package/dist/esm/components/GridHeader.js +0 -2
  60. package/dist/esm/components/GridHeader.js.map +0 -1
  61. package/dist/esm/components/GridRows.d.ts +0 -49
  62. package/dist/esm/components/GridRows.js +0 -2
  63. package/dist/esm/components/GridRows.js.map +0 -1
  64. package/dist/esm/components/GroupBar.d.ts +0 -12
  65. package/dist/esm/components/GroupBar.js +0 -2
  66. package/dist/esm/components/GroupBar.js.map +0 -1
  67. package/dist/esm/components/GroupHeader.d.ts +0 -29
  68. package/dist/esm/components/GroupHeader.js +0 -2
  69. package/dist/esm/components/GroupHeader.js.map +0 -1
  70. package/dist/esm/components/NoDataMessage.d.ts +0 -7
  71. package/dist/esm/components/NoDataMessage.js +0 -2
  72. package/dist/esm/components/NoDataMessage.js.map +0 -1
  73. package/dist/esm/components/PaginationControls.d.ts +0 -15
  74. package/dist/esm/components/PaginationControls.js +0 -2
  75. package/dist/esm/components/PaginationControls.js.map +0 -1
  76. package/dist/esm/components/Popover.d.ts +0 -17
  77. package/dist/esm/components/Popover.js +0 -2
  78. package/dist/esm/components/Popover.js.map +0 -1
  79. package/dist/esm/components/RowContextMenu.d.ts +0 -22
  80. package/dist/esm/components/SearchToolbar.d.ts +0 -79
  81. package/dist/esm/components/SearchToolbar.js +0 -2
  82. package/dist/esm/components/SearchToolbar.js.map +0 -1
  83. package/dist/esm/components/filters/BooleanFilter.d.ts +0 -7
  84. package/dist/esm/components/filters/BooleanFilter.js +0 -2
  85. package/dist/esm/components/filters/BooleanFilter.js.map +0 -1
  86. package/dist/esm/components/filters/DateFilter.d.ts +0 -9
  87. package/dist/esm/components/filters/DateFilter.js +0 -2
  88. package/dist/esm/components/filters/DateFilter.js.map +0 -1
  89. package/dist/esm/components/filters/FilterContent.d.ts +0 -9
  90. package/dist/esm/components/filters/FilterContent.js +0 -2
  91. package/dist/esm/components/filters/FilterContent.js.map +0 -1
  92. package/dist/esm/components/filters/FilterPopup.d.ts +0 -2
  93. package/dist/esm/components/filters/MultiselectFilter.d.ts +0 -10
  94. package/dist/esm/components/filters/MultiselectFilter.js +0 -2
  95. package/dist/esm/components/filters/MultiselectFilter.js.map +0 -1
  96. package/dist/esm/components/filters/NumberFilter.d.ts +0 -9
  97. package/dist/esm/components/filters/NumberFilter.js +0 -2
  98. package/dist/esm/components/filters/NumberFilter.js.map +0 -1
  99. package/dist/esm/components/filters/TextFilter.d.ts +0 -8
  100. package/dist/esm/components/filters/TextFilter.js +0 -2
  101. package/dist/esm/components/filters/TextFilter.js.map +0 -1
  102. package/dist/esm/components/filters/index.d.ts +0 -6
  103. package/dist/esm/components/ui/DatePicker.d.ts +0 -10
  104. package/dist/esm/components/ui/DatePicker.js +0 -2
  105. package/dist/esm/components/ui/DatePicker.js.map +0 -1
  106. package/dist/esm/constants.d.ts +0 -1
  107. package/dist/esm/constants.js +0 -2
  108. package/dist/esm/constants.js.map +0 -1
  109. package/dist/esm/hooks/useAdvancedFiltering.d.ts +0 -16
  110. package/dist/esm/hooks/useAdvancedFiltering.js +0 -2
  111. package/dist/esm/hooks/useAdvancedFiltering.js.map +0 -1
  112. package/dist/esm/hooks/useDataWorker.d.ts +0 -10
  113. package/dist/esm/hooks/useDataWorker.js +0 -2
  114. package/dist/esm/hooks/useDataWorker.js.map +0 -1
  115. package/dist/esm/hooks/useExport.d.ts +0 -15
  116. package/dist/esm/hooks/useExport.js +0 -2
  117. package/dist/esm/hooks/useExport.js.map +0 -1
  118. package/dist/esm/hooks/useFilteringAndSorting.d.ts +0 -16
  119. package/dist/esm/hooks/useFilteringAndSorting.js +0 -2
  120. package/dist/esm/hooks/useFilteringAndSorting.js.map +0 -1
  121. package/dist/esm/hooks/useGrouping.d.ts +0 -28
  122. package/dist/esm/hooks/useGrouping.js +0 -2
  123. package/dist/esm/hooks/useGrouping.js.map +0 -1
  124. package/dist/esm/hooks/usePagination.d.ts +0 -28
  125. package/dist/esm/hooks/usePagination.js +0 -2
  126. package/dist/esm/hooks/usePagination.js.map +0 -1
  127. package/dist/esm/hooks/useSelection.d.ts +0 -13
  128. package/dist/esm/hooks/useSelection.js +0 -2
  129. package/dist/esm/hooks/useSelection.js.map +0 -1
  130. package/dist/esm/hooks/useVirtualization.d.ts +0 -17
  131. package/dist/esm/hooks/useVirtualization.js +0 -2
  132. package/dist/esm/hooks/useVirtualization.js.map +0 -1
  133. package/dist/esm/index.d.ts +0 -11
  134. package/dist/esm/index.js +0 -2
  135. package/dist/esm/index.js.map +0 -1
  136. package/dist/esm/types.d.ts +0 -421
  137. package/dist/esm/utils/highlightText.d.ts +0 -15
  138. package/dist/esm/utils/highlightText.js +0 -2
  139. package/dist/esm/utils/highlightText.js.map +0 -1
  140. package/dist/esm/workers/dataWorker.d.ts +0 -16
  141. package/dist/hooks/useFilteringAndSorting.d.ts +0 -16
  142. package/dist/workers/dataWorker.d.ts +0 -16
@@ -1 +0,0 @@
1
- {"version":3,"file":"CustomGrid.js","sources":["../../../../CustomGrid.tsx"],"sourcesContent":["import {\n ChangeEvent,\n forwardRef,\n useCallback,\n useEffect,\n useImperativeHandle,\n useMemo,\n useRef,\n useState,\n} from \"react\";\nimport { FooterAggregate } from \"./components/FooterAggregate\";\nimport { GridHeader } from \"./components/GridHeader\";\nimport { GridRows } from \"./components/GridRows\";\nimport { GroupBar } from \"./components/GroupBar\";\nimport { GroupHeader } from \"./components/GroupHeader\";\nimport { NoDataMessage } from \"./components/NoDataMessage\";\nimport { PaginationControls } from \"./components/PaginationControls\";\nimport { SearchToolbar } from \"./components/SearchToolbar\";\nimport { SELECT_COL_WIDTH } from \"./constants\";\nimport { useAdvancedFiltering } from \"./hooks/useAdvancedFiltering\";\nimport { useDataWorker } from \"./hooks/useDataWorker\";\nimport { useExport } from \"./hooks/useExport\";\nimport { useGrouping } from \"./hooks/useGrouping\";\nimport { usePagination } from \"./hooks/usePagination\";\nimport { useSelection } from \"./hooks/useSelection\";\nimport { useVirtualization } from \"./hooks/useVirtualization\";\nimport { CustomDataGridProps, CustomDataGridRef, Density } from \"./types\";\n\nexport const CustomDataGrid = forwardRef<\n CustomDataGridRef,\n CustomDataGridProps\n>(function CustomDataGrid(\n {\n data,\n columns,\n getRowId,\n height,\n density: densityProp = \"md\",\n headerDensity,\n headerHeight: headerHeightProp,\n onDensityChange,\n showDensityControl = false,\n onRowSelect,\n onSelectedRowsChange,\n selectable = false,\n isRowSelectable,\n showToolbar = true,\n showExport = false,\n filterable = true,\n groupable = false,\n virtualized = true,\n rowHeight: rowHeightProp,\n overscan = 6,\n rowStyle,\n onContextMenu,\n onRowDoubleClick,\n onRowClick,\n onCellClick,\n onCellContextMenu,\n cellFocusEnabled = false,\n onCellFocusChange,\n cellSelectionEnabled = false,\n canSelectCell = () => true,\n onCellSelectionChange,\n pagination = { enabled: false, mode: \"client\", pageSize: 50 },\n virtualizationThreshold = 300,\n isLoading = false,\n onFilterChange,\n onSort,\n onColumnConfigChange,\n initialColumnConfig,\n toolbarLeft,\n toolbarRight,\n groupBarVisibility = \"auto\",\n groupFooterVariant = \"chips\",\n onCopyCells,\n selectedRowIds,\n performanceConfig = {},\n exportOptions: exportOptionsProp,\n renderGroupActions,\n }: CustomDataGridProps,\n ref,\n) {\n // ==== Container width tracking (for column flex support) ====\n const containerRef = useRef<HTMLDivElement>(null);\n const [containerWidth, setContainerWidth] = useState<number>(0);\n useEffect(() => {\n const el = containerRef.current;\n if (!el) return;\n const update = () => setContainerWidth(el.clientWidth || 0);\n update();\n const ro = new ResizeObserver(() => update());\n ro.observe(el);\n window.addEventListener(\"resize\", update);\n return () => {\n ro.disconnect();\n window.removeEventListener(\"resize\", update);\n };\n }, []);\n // ===== Cell focus & rectangular selection state =====\n const [focusedCell, setFocusedCell] = useState<{\n rowId: string | number;\n columnKey: string;\n } | null>(null);\n const [selectionAnchor, setSelectionAnchor] = useState<{\n rowIndex: number;\n colIndex: number;\n } | null>(null);\n const [selectionEnd, setSelectionEnd] = useState<{\n rowIndex: number;\n colIndex: number;\n } | null>(null);\n const isMouseDownRef = useRef(false);\n const lastMousePosRef = useRef<{ x: number; y: number } | null>(null);\n const autoScrollRafRef = useRef<number | null>(null);\n const [dragOverlay, setDragOverlay] = useState<{\n x: number;\n y: number;\n rows: number;\n cols: number;\n } | null>(null);\n\n // ===== Density management and effective row height =====\n const [density, setDensity] = useState<Density>(densityProp);\n useEffect(() => {\n // keep in sync if parent changes controlled prop\n setDensity(densityProp);\n }, [densityProp]);\n\n const densityMap: Record<Density, number> = useMemo(\n () => ({ sm: 28, md: 40, lg: 56 }),\n [],\n );\n const resolvedRowHeight = useMemo(\n () => (rowHeightProp != null ? rowHeightProp : densityMap[density]),\n [rowHeightProp, density, densityMap],\n );\n // Header height: explicit headerHeight > headerDensity > default md\n const resolvedHeaderHeight = useMemo(() => {\n if (headerHeightProp != null) return headerHeightProp;\n const d = headerDensity ?? \"md\";\n return densityMap[d];\n }, [headerHeightProp, headerDensity, densityMap]);\n // Use custom hooks for different concerns - OPTIMIZED\n const {\n globalFilter,\n columnFilters,\n filteredData,\n setGlobalFilter,\n setColumnFilter,\n clearAllFilters,\n } = useAdvancedFiltering({ data, columns });\n\n // Sorting state (tri-state: asc -> desc -> none)\n const [sortConfig, setSortConfig] = useState<{\n key: string | null;\n direction: \"asc\" | \"desc\" | null;\n }>({\n key: null,\n direction: null,\n });\n\n // Column widths state\n const [columnWidths, setColumnWidths] = useState<Record<string, number>>(\n () => {\n const initialWidths: Record<string, number> = {};\n columns.forEach((col) => {\n initialWidths[col.key] = col.width || 100;\n });\n return initialWidths;\n },\n );\n\n // Keep an immutable snapshot of initial widths for reset\n const initialWidthsRef = useRef<Record<string, number>>(\n columns.reduce(\n (acc, c) => {\n acc[c.key] = c.width || 100;\n return acc;\n },\n {} as Record<string, number>,\n ),\n );\n\n // Column order state + initial snapshot\n const initialOrderRef = useRef<string[]>(columns.map((c) => c.key));\n const [columnOrder, setColumnOrder] = useState<string[]>(() =>\n initialOrderRef.current.slice(),\n );\n\n // Column pinning state\n const [pinnedColumns, setPinnedColumns] = useState<Set<string>>(new Set());\n\n // Column visibility state\n const [columnVisibility, setColumnVisibility] = useState<\n Record<string, boolean>\n >(() => {\n const initialVisibility: Record<string, boolean> = {};\n columns.forEach((col) => {\n initialVisibility[col.key] = true;\n });\n return initialVisibility;\n });\n\n const handleSort = useCallback(\n (key: string) => {\n let nextKey: string | null = key;\n let nextDir: \"asc\" | \"desc\" | null = \"asc\";\n\n if (sortConfig.key !== key) {\n nextKey = key;\n nextDir = \"asc\";\n } else if (sortConfig.direction === \"asc\") {\n nextDir = \"desc\";\n } else if (sortConfig.direction === \"desc\") {\n // clear sorting\n nextKey = null;\n nextDir = null;\n } else {\n nextDir = \"asc\";\n }\n\n setSortConfig({ key: nextKey, direction: nextDir });\n\n // Server-side sorting: only call when a direction is active\n if (onSort && pagination?.mode === \"server\" && nextKey && nextDir) {\n onSort(nextKey, nextDir);\n }\n },\n [onSort, pagination?.mode, sortConfig.key, sortConfig.direction],\n );\n\n const handleColumnResize = useCallback((columnKey: string, width: number) => {\n setColumnWidths((prev) => ({\n ...prev,\n [columnKey]: width,\n }));\n }, []);\n\n // Heuristic text width calc using character count; padding accounts for cell padding/icons\n const estimateWidthForText = useCallback((text: any) => {\n if (text == null) return 0;\n const s = typeof text === \"string\" ? text : String(text);\n const avgCharPx = 8; // conservative average for grid font\n return s.length * avgCharPx;\n }, []);\n\n const autosizeColumn = useCallback(\n (columnKey: string) => {\n const col = columns.find((c) => c.key === columnKey);\n if (!col) return;\n const headerWidth = estimateWidthForText(col.header);\n const sample = data.slice(0, 500); // limit for perf\n let maxCell = 0;\n for (const row of sample) {\n const value = row[columnKey];\n maxCell = Math.max(maxCell, estimateWidthForText(value));\n }\n const padding = 32; // cell padding + sort/menu affordance\n const computed = Math.max(\n 60,\n Math.min(500, Math.max(headerWidth, maxCell) + padding),\n );\n setColumnWidths((prev) => ({ ...prev, [columnKey]: computed }));\n },\n [columns, data, estimateWidthForText],\n );\n\n const autosizeAllColumns = useCallback(() => {\n columns.forEach((c) => autosizeColumn(c.key));\n }, [columns, autosizeColumn]);\n\n const resetColumns = useCallback(() => {\n // reset widths\n setColumnWidths({ ...initialWidthsRef.current });\n // clear pinning\n setPinnedColumns(new Set());\n // show all columns\n setColumnVisibility((prev) => {\n const next: Record<string, boolean> = {};\n Object.keys(prev).forEach((k) => (next[k] = true));\n return next;\n });\n // reset order\n setColumnOrder(initialOrderRef.current.slice());\n }, []);\n\n const headerScrollRef = useRef<HTMLDivElement>(null);\n const footerScrollRef = useRef<HTMLDivElement>(null);\n\n const handleColumnPin = useCallback((columnKey: string, pinned: boolean) => {\n setPinnedColumns((prev) => {\n const newSet = new Set(prev);\n if (pinned) {\n newSet.add(columnKey);\n } else {\n newSet.delete(columnKey);\n }\n return newSet;\n });\n }, []);\n\n const handleColumnVisibilityChange = useCallback(\n (columnKey: string, visible: boolean) => {\n setColumnVisibility((prev) => ({\n ...prev,\n [columnKey]: visible,\n }));\n },\n [],\n );\n\n // Apply sorting, with optional worker offload for large datasets\n const { process: workerProcess } = useDataWorker({ enabled: true });\n const [sortedData, setSortedData] = useState<any[]>(filteredData);\n\n // Worker threshold: large datasets offloaded to background\n const workerThreshold =\n (performanceConfig as any)?.sortWorkerThreshold ?? 5000;\n\n useEffect(() => {\n let cancelled = false;\n if (!sortConfig.key) {\n setSortedData(filteredData);\n return;\n }\n const rows = filteredData;\n if (rows.length < workerThreshold) {\n // inline sort for small lists\n const dir = sortConfig.direction === \"asc\" ? 1 : -1;\n const out = [...rows].sort((a, b) => {\n const av = a?.[sortConfig.key!];\n const bv = b?.[sortConfig.key!];\n if (av == null && bv == null) return 0;\n if (av == null) return -1 * dir;\n if (bv == null) return 1 * dir;\n if (typeof av === \"number\" && typeof bv === \"number\")\n return (av - bv) * dir;\n return String(av).localeCompare(String(bv)) * dir;\n });\n setSortedData(out);\n return;\n }\n workerProcess(rows, {\n key: sortConfig.key!,\n direction: sortConfig.direction as any,\n }).then((out) => {\n if (!cancelled) setSortedData(out);\n });\n return () => {\n cancelled = true;\n };\n }, [filteredData, sortConfig, workerProcess, workerThreshold]);\n\n const {\n paginationConfig,\n paginatedData,\n isServerLoading,\n handlePageChange,\n handlePageSizeChange,\n resetPage,\n currentPage,\n } = usePagination({ data: sortedData, pagination });\n\n // Unified loading state for header loader with smooth finish animation\n const loadingOverall = isLoading || isServerLoading;\n const [showLoader, setShowLoader] = useState<boolean>(loadingOverall);\n const [finishingLoader, setFinishingLoader] = useState<boolean>(false);\n const [loaderTop, setLoaderTop] = useState<number>(0);\n useEffect(() => {\n if (loadingOverall) {\n setShowLoader(true);\n setFinishingLoader(false);\n return;\n }\n if (showLoader) {\n setFinishingLoader(true);\n const t = setTimeout(() => {\n setShowLoader(false);\n setFinishingLoader(false);\n }, 200);\n return () => clearTimeout(t);\n }\n }, [loadingOverall]);\n\n const handleGlobalFilterChange = useCallback(\n (e: ChangeEvent<HTMLInputElement>) => {\n setGlobalFilter(e.target.value);\n },\n [setGlobalFilter],\n );\n\n // Grouping functionality\n const {\n groupConfig,\n displayData: groupedDisplayData,\n handleGroupBy,\n addGroupKey,\n removeGroupKey,\n setGroupKeys,\n toggleGroupExpansion,\n isGrouped,\n expandAllGroups,\n collapseAllGroups,\n } = useGrouping({ data: sortedData, columns });\n\n // Expose imperative grouping controls to parent components\n useImperativeHandle(\n ref,\n () => ({\n setGroupKeys,\n addGroupKey,\n removeGroupKey,\n expandAllGroups,\n collapseAllGroups,\n toggleGroupExpansion,\n getGroupConfig: () => groupConfig,\n // Filter methods\n setColumnFilter: (columnKey: string, filter: any) => {\n setColumnFilter(columnKey, filter);\n },\n clearColumnFilter: (columnKey: string) => {\n setColumnFilter(columnKey, null);\n },\n clearAllFilters: () => {\n clearAllFilters();\n },\n setGlobalFilter: (value: string) => {\n setGlobalFilter(value);\n },\n }),\n [groupConfig, setColumnFilter, clearAllFilters, setGlobalFilter],\n );\n\n // Do NOT pin grouped columns; only respect explicit pins.\n const effectivePinnedColumns = useMemo(() => {\n return new Set(pinnedColumns);\n }, [pinnedColumns]);\n\n const finalDisplayData = pagination?.enabled\n ? pagination.mode === \"server\"\n ? data\n : paginatedData\n : sortedData;\n\n const dataToDisplay = isGrouped ? groupedDisplayData : finalDisplayData;\n\n const { selectedRows, handleRowSelect, handleSelectAll } = useSelection({\n data: dataToDisplay,\n onRowSelect,\n selectedRowIds,\n getRowId,\n });\n\n // Keep stable ref for onSelectedRowsChange to avoid effect re-running on every parent render\n const onSelectedRowsChangeRef = useRef(onSelectedRowsChange);\n useEffect(() => {\n onSelectedRowsChangeRef.current = onSelectedRowsChange;\n }, [onSelectedRowsChange]);\n\n useEffect(() => {\n if (onSelectedRowsChangeRef.current) {\n onSelectedRowsChangeRef.current(selectedRows);\n }\n }, [selectedRows]);\n\n // Update export hook with selected rows\n const { exportData: exportDataWithSelection } = useExport({\n // Export the currently rendered dataset. In server mode this is the fetched page.\n data: finalDisplayData,\n columns,\n selectedRows,\n filename: (exportOptionsProp && exportOptionsProp.filename) || \"grid-data\",\n columnVisibility,\n });\n\n // Decide how to export: client (default) vs server callback\n const handleToolbarExport = useCallback(\n (format: any, exportSelected: boolean) => {\n const selectedIds = Array.from(selectedRows || []);\n if (\n exportOptionsProp?.exportMode === \"server\" &&\n typeof exportOptionsProp?.onServerExport === \"function\"\n ) {\n exportOptionsProp.onServerExport(format, exportSelected, selectedIds);\n } else {\n // Client mode: export only the data available in memory (filtered/paginated set)\n exportDataWithSelection(format, exportSelected);\n }\n },\n [\n exportOptionsProp?.exportMode,\n exportOptionsProp?.onServerExport,\n exportDataWithSelection,\n selectedRows,\n ],\n );\n\n const handleClearFilters = useCallback(() => {\n clearAllFilters();\n }, [clearAllFilters]);\n\n const handleSetDensity = useCallback(\n (d: Density) => {\n setDensity(d);\n if (onDensityChange) onDensityChange(d);\n },\n [onDensityChange],\n );\n\n const effectiveVirtualized = useMemo(() => {\n if (!virtualized) return false;\n if (dataToDisplay.length <= virtualizationThreshold) return false;\n return true;\n }, [\n virtualized,\n dataToDisplay.length,\n virtualizationThreshold,\n isGrouped,\n cellSelectionEnabled,\n ]);\n\n // Performance configuration with defaults\n const perfConfig = useMemo(\n () => ({\n enableHorizontalVirtualization: true,\n horizontalVirtualizationThreshold: 50,\n sortWorkerThreshold: 5000,\n maxFilterCacheSize: 5000,\n enableAggressiveMemoization: true,\n ...performanceConfig,\n }),\n [performanceConfig],\n );\n\n const { scrollRef, virtualizedRange, visibleData, handleScroll } =\n useVirtualization({\n data: dataToDisplay,\n virtualized: effectiveVirtualized,\n rowHeight: resolvedRowHeight,\n overscan,\n height: height || 400,\n });\n\n // Position the loader bar just below the sticky header, regardless of horizontal scroll\n useEffect(() => {\n const update = () => {\n const el = scrollRef.current;\n if (!el) return;\n // offsetTop is relative to the closest positioned ancestor (the relative grid container)\n const top = (el.offsetTop || 0) + (resolvedHeaderHeight || 0) - 1;\n setLoaderTop(top);\n };\n update();\n const ro = new ResizeObserver(update);\n if (containerRef.current) ro.observe(containerRef.current);\n if (scrollRef.current) ro.observe(scrollRef.current);\n window.addEventListener(\"resize\", update);\n return () => {\n ro.disconnect();\n window.removeEventListener(\"resize\", update);\n };\n }, [resolvedHeaderHeight]);\n\n // Horizontal virtualization state\n const [hScrollLeft, setHScrollLeft] = useState(0);\n const hScrollLeftRef = useRef(0);\n const hScrollRafRef = useRef<number | null>(null);\n const scheduleHScrollUpdate = useCallback((value: number) => {\n hScrollLeftRef.current = value;\n if (hScrollRafRef.current != null) return;\n hScrollRafRef.current = requestAnimationFrame(() => {\n hScrollRafRef.current = null;\n setHScrollLeft(hScrollLeftRef.current);\n });\n }, []);\n const [viewportWidth, setViewportWidth] = useState(0);\n useEffect(() => {\n const el = scrollRef.current;\n if (!el) return;\n const update = () => setViewportWidth(el.clientWidth || 0);\n update();\n const ro = new ResizeObserver(update);\n ro.observe(el);\n return () => ro.disconnect();\n }, [scrollRef]);\n\n // Notify parent of filter changes in server-side mode\n useEffect(() => {\n if (!onFilterChange) return;\n if (!(pagination?.enabled && pagination.mode === \"server\")) return;\n\n const filterableKeys = columns\n .filter((c) => !!c.filterable)\n .map((c) => c.key);\n\n onFilterChange({\n globalFilter,\n columnFilters,\n filterableKeys,\n });\n }, [globalFilter, columnFilters]);\n\n // Hydrate from initialColumnConfig on mount\n useEffect(() => {\n if (!initialColumnConfig) return;\n const existingKeys = new Set(columns.map((c) => c.key));\n\n if (initialColumnConfig.columnWidths) {\n const next = Object.fromEntries(\n Object.entries(initialColumnConfig.columnWidths).filter(([k]) =>\n existingKeys.has(k),\n ),\n );\n setColumnWidths((prev) => ({ ...prev, ...next }));\n }\n\n if (initialColumnConfig.columnVisibility) {\n const next = Object.fromEntries(\n Object.entries(initialColumnConfig.columnVisibility).filter(([k]) =>\n existingKeys.has(k),\n ),\n );\n setColumnVisibility((prev) => ({ ...prev, ...next }));\n }\n\n if (initialColumnConfig.pinnedColumns) {\n const pins = initialColumnConfig.pinnedColumns.filter((k) =>\n existingKeys.has(k),\n );\n setPinnedColumns(new Set(pins));\n }\n\n if (initialColumnConfig.sortConfig) {\n const dir =\n initialColumnConfig.sortConfig.direction === \"asc\" ||\n initialColumnConfig.sortConfig.direction === \"desc\"\n ? initialColumnConfig.sortConfig.direction\n : null;\n const key = dir ? initialColumnConfig.sortConfig.key : null;\n setSortConfig({ key, direction: dir });\n }\n\n if (initialColumnConfig.filters) {\n setGlobalFilter(initialColumnConfig.filters.globalFilter || \"\");\n const entries = Object.entries(\n initialColumnConfig.filters.columnFilters || {},\n ).filter(([k]) => existingKeys.has(k));\n for (const [k, v] of entries) setColumnFilter(k, v as any);\n }\n\n if (\n initialColumnConfig.columnOrder &&\n initialColumnConfig.columnOrder.length\n ) {\n const filtered = initialColumnConfig.columnOrder.filter((k) =>\n existingKeys.has(k),\n );\n // append any new columns not in saved order at the end, by original order\n const remaining = columns\n .map((c) => c.key)\n .filter((k) => !filtered.includes(k));\n setColumnOrder([...filtered, ...remaining]);\n }\n\n if (\n initialColumnConfig.groupConfig?.columnKeys &&\n initialColumnConfig.groupConfig.columnKeys.length\n ) {\n setGroupKeys(initialColumnConfig.groupConfig.columnKeys);\n } else if (initialColumnConfig.groupConfig?.columnKey) {\n handleGroupBy(initialColumnConfig.groupConfig.columnKey);\n }\n if (initialColumnConfig.density) {\n setDensity(initialColumnConfig.density as Density);\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n // Emit onColumnConfigChange when relevant state updates (debounced)\n useEffect(() => {\n if (!onColumnConfigChange) return;\n const timer = setTimeout(() => {\n onColumnConfigChange({\n columnWidths,\n columnVisibility,\n pinnedColumns: Array.from(pinnedColumns),\n sortConfig,\n groupConfig: { columnKeys: groupConfig.columnKeys },\n filters: { globalFilter, columnFilters },\n columnOrder,\n density,\n });\n }, 200);\n return () => clearTimeout(timer);\n }, [\n onColumnConfigChange,\n columnWidths,\n columnVisibility,\n pinnedColumns,\n sortConfig,\n groupConfig,\n globalFilter,\n columnFilters,\n columnOrder,\n density,\n ]);\n\n // Sync horizontal scroll between header, body, and footer\n // (moved scroll sync effect below after columnsWithWidths is defined)\n\n // Create columns with updated widths and handle pinning order - OPTIMIZED\n const columnsWithWidths = useMemo(() => {\n // Early return if no columns\n if (columns.length === 0) return [];\n\n // order columns by columnOrder first\n const orderIndex = new Map<string, number>(\n columnOrder.map((k, i) => [k, i] as const),\n );\n\n // Batch process columns more efficiently\n const visibleColumns = columns.filter(\n (col) => columnVisibility[col.key] !== false,\n );\n\n // Pre-sort and process in one pass\n const base = visibleColumns\n .sort(\n (a, b) => (orderIndex.get(a.key) || 0) - (orderIndex.get(b.key) || 0),\n )\n .map((col) => {\n const candidate = columnWidths[col.key] ?? col.width;\n const width = Number.isFinite(Number(candidate))\n ? Number(candidate)\n : 100;\n // Enforce default sortable=true unless explicitly false\n const sortable = col.sortable !== false;\n return { ...col, width, sortable } as typeof col & {\n width: number;\n sortable: boolean;\n };\n });\n\n // Optimized flex calculation - only if needed\n const viewportW = Math.max(\n 0,\n containerWidth - (selectable ? SELECT_COL_WIDTH : 0),\n );\n const baseTotal = base.reduce((sum, c) => sum + c.width, 0);\n\n if (viewportW > baseTotal) {\n const flexCols = base.filter((c) => c.flex);\n if (flexCols.length > 0) {\n const extra = viewportW - baseTotal;\n const totalWeight = flexCols.reduce((acc, c) => {\n const weight =\n c.flex === true ? 1 : typeof c.flex === \"number\" ? c.flex : 1;\n return acc + Math.max(0, weight);\n }, 0);\n\n if (totalWeight > 0) {\n // Batch update flex columns\n for (const col of flexCols) {\n const weight =\n col.flex === true\n ? 1\n : typeof col.flex === \"number\"\n ? col.flex\n : 1;\n const increment = (extra * weight) / totalWeight;\n const minW = col.minWidth ?? 0;\n const maxW = col.maxWidth ?? Number.POSITIVE_INFINITY;\n col.width = Math.max(minW, Math.min(maxW, col.width + increment));\n }\n }\n }\n }\n\n // Separate pinned and unpinned columns efficiently\n const pinned: typeof base = [];\n const unpinned: typeof base = [];\n\n for (const col of base) {\n if (effectivePinnedColumns.has(col.key)) {\n pinned.push(col);\n } else {\n unpinned.push(col);\n }\n }\n\n // Sort unpinned columns by grouping if needed\n if (groupConfig.columnKeys && groupConfig.columnKeys.length > 0) {\n const groupIndex = new Map(groupConfig.columnKeys.map((k, i) => [k, i]));\n unpinned.sort((a, b) => {\n const aIdx = groupIndex.get(a.key) ?? Number.POSITIVE_INFINITY;\n const bIdx = groupIndex.get(b.key) ?? Number.POSITIVE_INFINITY;\n return aIdx - bIdx;\n });\n }\n\n return [...pinned, ...unpinned];\n }, [\n columns,\n columnWidths,\n effectivePinnedColumns,\n columnVisibility,\n columnOrder,\n groupConfig.columnKeys,\n containerWidth,\n selectable,\n ]);\n\n // Horizontal virtualization for large column sets\n const needsHorizontalVirtualization = useMemo(() => {\n return (\n perfConfig.enableHorizontalVirtualization &&\n columnsWithWidths.length > perfConfig.horizontalVirtualizationThreshold\n );\n }, [\n columnsWithWidths.length,\n perfConfig.enableHorizontalVirtualization,\n perfConfig.horizontalVirtualizationThreshold,\n ]);\n\n // Pinned/unpinned memo + prefix sums for binary search slicing\n const pinnedAll = useMemo(\n () => columnsWithWidths.filter((c) => effectivePinnedColumns.has(c.key)),\n [columnsWithWidths, effectivePinnedColumns],\n );\n const unpinnedAll = useMemo(\n () => columnsWithWidths.filter((c) => !effectivePinnedColumns.has(c.key)),\n [columnsWithWidths, effectivePinnedColumns],\n );\n const unpinnedMeta = useMemo(() => {\n const widths = unpinnedAll.map((c) => (c.width as number) || 100);\n const prefix = new Array(widths.length + 1).fill(0) as number[];\n for (let i = 0; i < widths.length; i++)\n prefix[i + 1] = prefix[i] + widths[i];\n const totalWidth = prefix[widths.length] || 0;\n return { widths, prefix, totalWidth };\n }, [unpinnedAll]);\n\n const visibleColumns = useMemo(() => {\n if (!needsHorizontalVirtualization) return columnsWithWidths;\n\n const leftPad = selectable ? SELECT_COL_WIDTH : 0;\n const pinnedTotal = pinnedAll.reduce(\n (sum, c) => sum + ((c.width as number) || 100),\n 0,\n );\n\n const targetLeft = Math.max(0, hScrollLeft - 200);\n const targetRight = hScrollLeft + viewportWidth + 200;\n\n // Map into unpinned local coordinates\n const base = leftPad + pinnedTotal;\n const localLeft = Math.max(0, targetLeft - base);\n const localRight = Math.max(\n 0,\n Math.min(unpinnedMeta.totalWidth, targetRight - base),\n );\n\n // Binary search over prefix sums\n const firstIndexWithRightGte = (x: number): number => {\n if (!unpinnedMeta.widths.length) return 0;\n let lo = 0,\n hi = unpinnedMeta.widths.length - 1,\n ans = 0;\n while (lo <= hi) {\n const mid = (lo + hi) >> 1;\n const right = unpinnedMeta.prefix[mid + 1];\n if (right >= x) {\n ans = mid;\n hi = mid - 1;\n } else {\n lo = mid + 1;\n }\n }\n return Math.max(0, Math.min(ans, unpinnedMeta.widths.length - 1));\n };\n const lastIndexWithLeftLte = (x: number): number => {\n if (!unpinnedMeta.widths.length) return -1;\n let lo = 0,\n hi = unpinnedMeta.widths.length - 1,\n ans = unpinnedMeta.widths.length - 1;\n while (lo <= hi) {\n const mid = (lo + hi) >> 1;\n const left = unpinnedMeta.prefix[mid];\n if (left <= x) {\n ans = mid;\n lo = mid + 1;\n } else {\n hi = mid - 1;\n }\n }\n return Math.max(0, Math.min(ans, unpinnedMeta.widths.length - 1));\n };\n\n const startIdx = firstIndexWithRightGte(localLeft);\n const endIdx = lastIndexWithLeftLte(localRight);\n const sliced =\n endIdx >= startIdx && endIdx >= 0\n ? unpinnedAll.slice(startIdx, endIdx + 1)\n : [];\n return [...pinnedAll, ...sliced];\n }, [\n needsHorizontalVirtualization,\n columnsWithWidths,\n hScrollLeft,\n viewportWidth,\n selectable,\n pinnedAll,\n unpinnedAll,\n unpinnedMeta,\n ]);\n\n // Memoized hvPadLeft/Right for header and rows\n const unpinnedIndexByKey = useMemo(() => {\n const m = new Map<string, number>();\n unpinnedAll.forEach((c, i) => m.set(c.key, i));\n return m;\n }, [unpinnedAll]);\n const { hvPadLeftMemo, hvPadRightMemo } = useMemo(() => {\n if (!needsHorizontalVirtualization)\n return { hvPadLeftMemo: 0, hvPadRightMemo: 0 } as const;\n const visibleUnpinned = visibleColumns.filter(\n (c) => !effectivePinnedColumns.has(c.key),\n );\n if (!visibleUnpinned.length)\n return { hvPadLeftMemo: 0, hvPadRightMemo: 0 } as const;\n const firstIdx = unpinnedIndexByKey.get(visibleUnpinned[0].key);\n const lastIdx = unpinnedIndexByKey.get(\n visibleUnpinned[visibleUnpinned.length - 1].key,\n );\n if (firstIdx == null || lastIdx == null)\n return { hvPadLeftMemo: 0, hvPadRightMemo: 0 } as const;\n const left = unpinnedMeta.prefix[firstIdx];\n const right = unpinnedMeta.totalWidth - unpinnedMeta.prefix[lastIdx + 1];\n return { hvPadLeftMemo: left, hvPadRightMemo: right } as const;\n }, [\n needsHorizontalVirtualization,\n visibleColumns,\n effectivePinnedColumns,\n unpinnedIndexByKey,\n unpinnedMeta,\n ]);\n\n // Determine footer aggregate presence and keep scroll areas in sync (header/body/footer)\n const hasFooterAggregate = useMemo(\n () => columnsWithWidths.some((c) => c.footer_aggregate),\n [columnsWithWidths],\n );\n\n useEffect(() => {\n const bodyElement = scrollRef.current;\n if (!bodyElement) return;\n\n let isBodyScrolling = false;\n\n const syncFromBody = () => {\n if (isBodyScrolling) return;\n isBodyScrolling = true;\n const footerEl = footerScrollRef.current;\n if (footerEl && footerEl.scrollLeft !== bodyElement.scrollLeft) {\n footerEl.scrollLeft = bodyElement.scrollLeft;\n }\n scheduleHScrollUpdate(bodyElement.scrollLeft);\n requestAnimationFrame(() => {\n isBodyScrolling = false;\n });\n };\n\n bodyElement.addEventListener(\"scroll\", syncFromBody, { passive: true });\n // align footer on mount\n if (footerScrollRef.current) {\n footerScrollRef.current.scrollLeft = bodyElement.scrollLeft;\n }\n\n return () => {\n bodyElement.removeEventListener(\"scroll\", syncFromBody);\n };\n }, [scrollRef, hasFooterAggregate]);\n\n // Column index lookup in current render order (pinned first)\n const colIndexByKey = useMemo(() => {\n const m = new Map<string, number>();\n columnsWithWidths.forEach((c, i) => m.set(c.key, i));\n return m;\n }, [columnsWithWidths]);\n\n const colByKey = useMemo(() => {\n const m = new Map<string, any>();\n columnsWithWidths.forEach((c) => m.set(c.key, c));\n return m;\n }, [columnsWithWidths]);\n\n // Flat list of data rows (exclude group headers) for selection math\n const flatRows = useMemo(\n () =>\n dataToDisplay.filter((r: any) => !r._isGroupHeader && !r._isGroupFooter),\n [dataToDisplay],\n );\n const rowIndexById = useMemo(() => {\n const m = new Map<string | number, number>();\n flatRows.forEach((r, i) => m.set(r.id, i));\n return m;\n }, [flatRows]);\n\n const rowById = useMemo(() => {\n const m = new Map<string | number, any>();\n flatRows.forEach((r) => m.set(r.id, r));\n return m;\n }, [flatRows]);\n\n // Keep a stable reference to the latest onCellSelectionChange and a signature of last emitted selection\n const prevSelectionSignatureRef = useRef<string | null>(null);\n const onCellSelectionChangeRef = useRef(onCellSelectionChange);\n const onCopyCellsRef = useRef(onCopyCells);\n useEffect(() => {\n onCopyCellsRef.current = onCopyCells;\n }, [onCopyCells]);\n useEffect(() => {\n onCellSelectionChangeRef.current = onCellSelectionChange;\n }, [onCellSelectionChange]);\n // (onCopyCells handled separately above)\n\n // Derive a stable dependency for columns (by keys) to avoid re-running effects on width changes\n const columnKeys = useMemo(\n () => columnsWithWidths.map((c) => c.key),\n [columnsWithWidths],\n );\n\n // Selection predicates\n const isCellSelected = useMemo(\n () => (rowId: string | number, columnKey: string) => {\n if (!cellSelectionEnabled) return false;\n if (!selectionAnchor || !selectionEnd) return false;\n const r = rowIndexById.get(rowId);\n const c = colIndexByKey.get(columnKey);\n if (r == null || c == null) return false;\n const rowObj = rowById.get(rowId);\n const colObj = colByKey.get(columnKey);\n if (rowObj && colObj && !canSelectCell(rowObj, colObj)) return false;\n const rMin = Math.min(selectionAnchor.rowIndex, selectionEnd.rowIndex);\n const rMax = Math.max(selectionAnchor.rowIndex, selectionEnd.rowIndex);\n const cMin = Math.min(selectionAnchor.colIndex, selectionEnd.colIndex);\n const cMax = Math.max(selectionAnchor.colIndex, selectionEnd.colIndex);\n return r >= rMin && r <= rMax && c >= cMin && c <= cMax;\n },\n [\n cellSelectionEnabled,\n selectionAnchor,\n selectionEnd,\n rowIndexById,\n colIndexByKey,\n rowById,\n colByKey,\n canSelectCell,\n ],\n );\n\n const isCellFocused = useMemo(\n () => (rowId: string | number, columnKey: string) =>\n focusedCell?.rowId === rowId && focusedCell?.columnKey === columnKey,\n [focusedCell],\n );\n\n // Forward horizontal wheel/trackpad scrolls from header/footer to body to keep a single scroll source\n const forwardWheelToBody = useCallback(\n (e: React.WheelEvent<HTMLDivElement>) => {\n const bodyEl = scrollRef.current;\n if (!bodyEl) return;\n const primarilyHorizontal = Math.abs(e.deltaX) >= Math.abs(e.deltaY);\n const dx = primarilyHorizontal ? e.deltaX : e.deltaY;\n if (dx !== 0) {\n e.preventDefault();\n bodyEl.scrollBy({ left: dx, top: 0, behavior: \"auto\" });\n }\n },\n [],\n );\n\n // Mouse handlers for drag selection\n const handleCellMouseDown = useCallback(\n ({ row, column, event }: { row: any; column: any; event: any }) => {\n const rIndex = rowIndexById.get(row.id);\n const cIndex = colIndexByKey.get(column.key);\n\n // Always set focused cell when focus mode is enabled\n if (cellFocusEnabled && rIndex != null && cIndex != null) {\n // Don't update focus if modal is open\n if (!document.body.hasAttribute(\"data-modal-open\")) {\n setFocusedCell({ rowId: row.id, columnKey: column.key });\n if (onCellFocusChange) {\n onCellFocusChange({\n row,\n column,\n value: row[column.key],\n rowIndex: rIndex,\n colIndex: cIndex,\n });\n }\n }\n }\n\n // Handle rectangular selection only when enabled and left button\n if (!cellSelectionEnabled) return;\n if (!canSelectCell(row, column)) return;\n if (event.button !== 0) return; // left button only\n isMouseDownRef.current = true;\n lastMousePosRef.current = { x: event.clientX, y: event.clientY };\n if (rIndex == null || cIndex == null) return;\n setFocusedCell({ rowId: row.id, columnKey: column.key });\n setSelectionAnchor({ rowIndex: rIndex, colIndex: cIndex });\n setSelectionEnd({ rowIndex: rIndex, colIndex: cIndex });\n // Initialize overlay\n setDragOverlay({\n x: event.clientX,\n y: event.clientY,\n rows: 1,\n cols: 1,\n });\n },\n [\n cellFocusEnabled,\n onCellFocusChange,\n cellSelectionEnabled,\n canSelectCell,\n rowIndexById,\n colIndexByKey,\n ],\n );\n\n const handleCellMouseEnter = useCallback(\n ({ row, column, event }: { row: any; column: any; event: any }) => {\n if (!cellSelectionEnabled) return;\n if (!isMouseDownRef.current || !selectionAnchor) return;\n if (!canSelectCell(row, column)) return;\n const rIndex = rowIndexById.get(row.id);\n const cIndex = colIndexByKey.get(column.key);\n if (rIndex == null || cIndex == null) return;\n setSelectionEnd({ rowIndex: rIndex, colIndex: cIndex });\n // Update last mouse and overlay\n lastMousePosRef.current = { x: event.clientX, y: event.clientY };\n const rMin = Math.min(selectionAnchor.rowIndex, rIndex);\n const rMax = Math.max(selectionAnchor.rowIndex, rIndex);\n const cMin = Math.min(selectionAnchor.colIndex, cIndex);\n const cMax = Math.max(selectionAnchor.colIndex, cIndex);\n setDragOverlay({\n x: event.clientX,\n y: event.clientY,\n rows: rMax - rMin + 1,\n cols: cMax - cMin + 1,\n });\n },\n [\n cellSelectionEnabled,\n canSelectCell,\n selectionAnchor,\n rowIndexById,\n colIndexByKey,\n ],\n );\n\n useEffect(() => {\n const onUp = () => {\n isMouseDownRef.current = false;\n setDragOverlay(null);\n if (autoScrollRafRef.current) {\n cancelAnimationFrame(autoScrollRafRef.current);\n autoScrollRafRef.current = null;\n }\n };\n window.addEventListener(\"mouseup\", onUp);\n return () => window.removeEventListener(\"mouseup\", onUp);\n }, []);\n\n // Global mousemove to keep tracking position while dragging (even outside cells)\n useEffect(() => {\n const onMove = (e: MouseEvent) => {\n if (!isMouseDownRef.current) return;\n lastMousePosRef.current = { x: e.clientX, y: e.clientY };\n };\n window.addEventListener(\"mousemove\", onMove as any);\n return () => window.removeEventListener(\"mousemove\", onMove as any);\n }, []);\n\n // Auto-scroll while dragging near edges of the scroll container\n const ensureAutoScrollLoop = useCallback(() => {\n if (autoScrollRafRef.current != null) return;\n const step = () => {\n autoScrollRafRef.current = null;\n if (!isMouseDownRef.current) return;\n const el = scrollRef.current;\n const mp = lastMousePosRef.current;\n if (el && mp) {\n const rect = el.getBoundingClientRect();\n const margin = 24; // px\n const maxSpeed = 24; // px per frame approx\n let dx = 0;\n let dy = 0;\n if (mp.x < rect.left + margin) dx = -(rect.left + margin - mp.x);\n else if (mp.x > rect.right - margin) dx = mp.x - (rect.right - margin);\n if (mp.y < rect.top + margin) dy = -(rect.top + margin - mp.y);\n else if (mp.y > rect.bottom - margin)\n dy = mp.y - (rect.bottom - margin);\n // Normalize to maxSpeed\n const clamp = (v: number) => Math.max(-maxSpeed, Math.min(maxSpeed, v));\n dx = clamp(dx);\n dy = clamp(dy);\n if (dx !== 0 || dy !== 0) {\n el.scrollBy({ left: dx, top: dy, behavior: \"auto\" });\n }\n }\n autoScrollRafRef.current = requestAnimationFrame(step);\n };\n autoScrollRafRef.current = requestAnimationFrame(step);\n }, []);\n\n // Kick the auto-scroll loop when we start a drag\n useEffect(() => {\n if (isMouseDownRef.current) {\n ensureAutoScrollLoop();\n }\n }, [ensureAutoScrollLoop, selectionAnchor]);\n\n // Keyboard: ESC clears selection; Shift+Arrows expands selection\n useEffect(() => {\n const onKey = (e: KeyboardEvent) => {\n if (e.key === \"Escape\") {\n setSelectionAnchor(null);\n setSelectionEnd(null);\n setFocusedCell(null);\n setDragOverlay(null);\n return;\n }\n const isShift = e.shiftKey;\n const arrow = [\n \"ArrowUp\",\n \"ArrowDown\",\n \"ArrowLeft\",\n \"ArrowRight\",\n ].includes(e.key);\n if (!isShift || !arrow || !cellSelectionEnabled) return;\n e.preventDefault();\n // Determine base cell\n let base = selectionEnd;\n if (!base) {\n if (focusedCell) {\n const r = rowIndexById.get(focusedCell.rowId);\n const c = colIndexByKey.get(focusedCell.columnKey);\n if (r != null && c != null) {\n setSelectionAnchor({ rowIndex: r, colIndex: c });\n base = { rowIndex: r, colIndex: c };\n } else {\n return;\n }\n } else {\n return;\n }\n }\n const maxRow = flatRows.length - 1;\n const maxCol = columnsWithWidths.length - 1;\n let nr = base.rowIndex;\n let nc = base.colIndex;\n if (e.key === \"ArrowUp\") nr = Math.max(0, nr - 1);\n if (e.key === \"ArrowDown\") nr = Math.min(maxRow, nr + 1);\n if (e.key === \"ArrowLeft\") nc = Math.max(0, nc - 1);\n if (e.key === \"ArrowRight\") nc = Math.min(maxCol, nc + 1);\n setSelectionEnd({ rowIndex: nr, colIndex: nc });\n // Update overlay near the scroll container center\n const el = scrollRef.current;\n if (el) {\n const rect = el.getBoundingClientRect();\n setDragOverlay({\n x: rect.right - 16,\n y: rect.bottom - 16,\n rows: Math.abs((selectionAnchor?.rowIndex ?? nr) - nr) + 1,\n cols: Math.abs((selectionAnchor?.colIndex ?? nc) - nc) + 1,\n });\n }\n };\n window.addEventListener(\"keydown\", onKey);\n return () => window.removeEventListener(\"keydown\", onKey);\n }, [\n cellSelectionEnabled,\n focusedCell,\n selectionAnchor,\n selectionEnd,\n rowIndexById,\n colIndexByKey,\n flatRows.length,\n columnsWithWidths.length,\n ]);\n\n // Keyboard navigation for focused cell\n useEffect(() => {\n if (!cellFocusEnabled) return;\n const bodyEl = scrollRef.current;\n const headerEl = headerScrollRef.current;\n\n const ensureVisible = (rowIndex: number, colIndex: number) => {\n if (!bodyEl) return;\n // Vertical\n const viewportH = bodyEl.clientHeight;\n const targetTop = rowIndex * resolvedRowHeight;\n const targetBottom = targetTop + resolvedRowHeight;\n if (targetTop < bodyEl.scrollTop) bodyEl.scrollTop = targetTop;\n else if (targetBottom > bodyEl.scrollTop + viewportH)\n bodyEl.scrollTop = targetBottom - viewportH;\n\n // Horizontal\n const colWidth = (columnsWithWidths[colIndex]?.width as number) || 100;\n let left = selectable ? SELECT_COL_WIDTH : 0;\n for (let i = 0; i < colIndex; i++) {\n left += (columnsWithWidths[i]?.width as number) || 100;\n }\n const right = left + colWidth;\n const viewportW = bodyEl.clientWidth;\n if (left < bodyEl.scrollLeft) {\n bodyEl.scrollLeft = left;\n } else if (right > bodyEl.scrollLeft + viewportW) {\n bodyEl.scrollLeft = right - viewportW;\n }\n if (headerEl) headerEl.scrollLeft = bodyEl.scrollLeft;\n };\n\n const handler = (e: KeyboardEvent) => {\n if (!focusedCell) return;\n // Don't handle keyboard navigation if modal is open\n if (document.body.hasAttribute(\"data-modal-open\")) return;\n const { key } = e;\n if (\n key !== \"ArrowLeft\" &&\n key !== \"ArrowRight\" &&\n key !== \"ArrowUp\" &&\n key !== \"ArrowDown\"\n )\n return;\n e.preventDefault();\n const currentRowIdx = rowIndexById.get(focusedCell.rowId);\n const currentColIdx = colIndexByKey.get(focusedCell.columnKey);\n if (currentRowIdx == null || currentColIdx == null) return;\n let nextRow = currentRowIdx;\n let nextCol = currentColIdx;\n if (e.key === \"ArrowLeft\") nextCol = Math.max(0, currentColIdx - 1);\n if (e.key === \"ArrowRight\")\n nextCol = Math.min(columnsWithWidths.length - 1, currentColIdx + 1);\n if (e.key === \"ArrowUp\") nextRow = Math.max(0, currentRowIdx - 1);\n if (e.key === \"ArrowDown\")\n nextRow = Math.min(flatRows.length - 1, currentRowIdx + 1);\n\n const row = flatRows[nextRow];\n const column = columnsWithWidths[nextCol];\n if (!row || !column) return;\n\n setFocusedCell({ rowId: row.id, columnKey: column.key });\n if (onCellFocusChange) {\n onCellFocusChange({\n row,\n column,\n value: row[column.key],\n rowIndex: nextRow,\n colIndex: nextCol,\n });\n }\n ensureVisible(nextRow, nextCol);\n };\n window.addEventListener(\"keydown\", handler);\n return () => window.removeEventListener(\"keydown\", handler);\n }, [\n cellFocusEnabled,\n focusedCell,\n flatRows,\n columnsWithWidths,\n resolvedRowHeight,\n selectable,\n rowIndexById,\n colIndexByKey,\n onCellFocusChange,\n ]);\n\n // Notify parent about selection rectangle (guarded to avoid redundant callbacks)\n useEffect(() => {\n if (!cellSelectionEnabled || !selectionAnchor || !selectionEnd) {\n const payload = { bounds: null as any, cells: [] as any[] };\n const sig = \"none\";\n if (sig !== prevSelectionSignatureRef.current) {\n prevSelectionSignatureRef.current = sig;\n onCellSelectionChangeRef.current?.(payload);\n }\n return;\n }\n\n const rowStart = Math.min(selectionAnchor.rowIndex, selectionEnd.rowIndex);\n const rowEnd = Math.max(selectionAnchor.rowIndex, selectionEnd.rowIndex);\n const colStart = Math.min(selectionAnchor.colIndex, selectionEnd.colIndex);\n const colEnd = Math.max(selectionAnchor.colIndex, selectionEnd.colIndex);\n\n const cells: Array<{\n row: any;\n column: any;\n value: any;\n rowIndex: number;\n colIndex: number;\n }> = [];\n for (let r = rowStart; r <= rowEnd; r++) {\n const row = flatRows[r];\n if (!row) continue;\n for (let c = colStart; c <= colEnd; c++) {\n const column = columnsWithWidths[c];\n if (!column) continue;\n if (!canSelectCell(row, column)) continue;\n cells.push({\n row,\n column,\n value: row[column.key],\n rowIndex: r,\n colIndex: c,\n });\n }\n }\n\n const payload = {\n bounds: { rowStart, rowEnd, colStart, colEnd },\n cells,\n };\n const sig = `${rowStart}-${rowEnd}-${colStart}-${colEnd}|len:${cells.length}`;\n if (sig === prevSelectionSignatureRef.current) return;\n prevSelectionSignatureRef.current = sig;\n onCellSelectionChangeRef.current?.(payload);\n }, [\n cellSelectionEnabled,\n selectionAnchor,\n selectionEnd,\n flatRows,\n columnKeys,\n canSelectCell,\n ]);\n\n // ================= COPY HANDLING (Ctrl/Cmd + C) =================\n useEffect(() => {\n // Enable custom copy when either rectangular selection or cell focus mode is active.\n if (!cellSelectionEnabled && !cellFocusEnabled) return;\n const onKeyDown = async (e: KeyboardEvent) => {\n const meta = e.metaKey || e.ctrlKey;\n if (!meta || e.key.toLowerCase() !== \"c\") return;\n const includeHeaders = !!e.shiftKey; // Hold Shift to include header row\n // Determine context\n const hasRect = !!(selectionAnchor && selectionEnd);\n const hasFocus = !!(cellFocusEnabled && focusedCell);\n if (!hasRect && !hasFocus) return; // nothing to do\n\n // If only focused cell (no rectangle) and user has an actual text selection or editing input, allow default behavior\n if (!hasRect && hasFocus) {\n const sel = window.getSelection();\n if (sel && sel.toString().length > 0) return; // user selected text manually\n const active = document.activeElement as HTMLElement | null;\n if (\n active &&\n (active.tagName === \"INPUT\" ||\n active.tagName === \"TEXTAREA\" ||\n active.isContentEditable)\n ) {\n return; // let native copy proceed inside editable fields\n }\n }\n\n // We'll handle copy ourselves now\n e.preventDefault();\n\n const collectCells = (): Array<{\n row: any;\n column: any;\n value: any;\n rowIndex: number;\n colIndex: number;\n }> => {\n const cells: Array<{\n row: any;\n column: any;\n value: any;\n rowIndex: number;\n colIndex: number;\n }> = [];\n if (hasRect && selectionAnchor && selectionEnd) {\n const rowStart = Math.min(\n selectionAnchor.rowIndex,\n selectionEnd.rowIndex,\n );\n const rowEnd = Math.max(\n selectionAnchor.rowIndex,\n selectionEnd.rowIndex,\n );\n const colStart = Math.min(\n selectionAnchor.colIndex,\n selectionEnd.colIndex,\n );\n const colEnd = Math.max(\n selectionAnchor.colIndex,\n selectionEnd.colIndex,\n );\n for (let r = rowStart; r <= rowEnd; r++) {\n const row = flatRows[r];\n if (!row) continue;\n for (let c = colStart; c <= colEnd; c++) {\n const column = columnsWithWidths[c];\n if (!column) continue;\n if (!canSelectCell(row, column)) continue;\n cells.push({\n row,\n column,\n value: row[column.key],\n rowIndex: r,\n colIndex: c,\n });\n }\n }\n } else if (hasFocus && focusedCell) {\n const rIndex = rowIndexById.get(focusedCell.rowId);\n const cIndex = colIndexByKey.get(focusedCell.columnKey);\n if (rIndex != null && cIndex != null) {\n const row = flatRows[rIndex];\n const column = columnsWithWidths[cIndex];\n if (row && column) {\n cells.push({\n row,\n column,\n value: row[column.key],\n rowIndex: rIndex,\n colIndex: cIndex,\n });\n }\n }\n }\n return cells;\n };\n\n const cells = collectCells();\n if (!cells.length) return;\n\n const escapeCell = (val: any): string => {\n const s = val == null ? \"\" : String(val);\n if (\n s.includes('\"') ||\n s.includes(\"\\t\") ||\n s.includes(\"\\n\") ||\n s.includes(\"\\r\")\n ) {\n return '\"' + s.replace(/\"/g, '\"\"') + '\"';\n }\n return s;\n };\n\n // Build tab / newline delimited text (Excel friendly)\n let text = \"\";\n if (hasRect && selectionAnchor && selectionEnd) {\n const rowStart = Math.min(\n selectionAnchor.rowIndex,\n selectionEnd.rowIndex,\n );\n const rowEnd = Math.max(\n selectionAnchor.rowIndex,\n selectionEnd.rowIndex,\n );\n const colStart = Math.min(\n selectionAnchor.colIndex,\n selectionEnd.colIndex,\n );\n const colEnd = Math.max(\n selectionAnchor.colIndex,\n selectionEnd.colIndex,\n );\n if (includeHeaders) {\n const headers: string[] = [];\n for (let c = colStart; c <= colEnd; c++) {\n const column = columnsWithWidths[c];\n if (!column) continue;\n headers.push(escapeCell((column as any).header ?? column.key));\n }\n text += headers.join(\"\\t\") + \"\\n\";\n }\n for (let r = rowStart; r <= rowEnd; r++) {\n const row = flatRows[r];\n if (!row) continue;\n const line: string[] = [];\n for (let c = colStart; c <= colEnd; c++) {\n const column = columnsWithWidths[c];\n if (!column) continue;\n if (!canSelectCell(row, column)) {\n line.push(\"\");\n continue;\n }\n const raw = row[column.key];\n line.push(escapeCell(raw));\n }\n text += line.join(\"\\t\");\n if (r < rowEnd) text += \"\\n\";\n }\n } else {\n // Single focused cell\n const cell = cells[0];\n text = escapeCell(cell.value);\n }\n\n try {\n await navigator.clipboard.writeText(text);\n } catch {\n console.warn(\"Failed to write to clipboard\");\n }\n\n // Fire optional callback if provided\n if (typeof onCopyCellsRef.current === \"function\") {\n const bounds =\n hasRect && selectionAnchor && selectionEnd\n ? {\n rowStart: Math.min(\n selectionAnchor.rowIndex,\n selectionEnd.rowIndex,\n ),\n rowEnd: Math.max(\n selectionAnchor.rowIndex,\n selectionEnd.rowIndex,\n ),\n colStart: Math.min(\n selectionAnchor.colIndex,\n selectionEnd.colIndex,\n ),\n colEnd: Math.max(\n selectionAnchor.colIndex,\n selectionEnd.colIndex,\n ),\n }\n : null;\n try {\n onCopyCellsRef.current({\n text,\n bounds,\n cells,\n isRectangular: !!bounds,\n isFocusedCell: !bounds,\n });\n } catch {\n /* swallow */\n }\n }\n };\n window.addEventListener(\"keydown\", onKeyDown);\n return () => window.removeEventListener(\"keydown\", onKeyDown);\n }, [\n cellSelectionEnabled,\n cellFocusEnabled,\n selectionAnchor,\n selectionEnd,\n focusedCell,\n flatRows,\n columnsWithWidths,\n canSelectCell,\n rowIndexById,\n colIndexByKey,\n ref,\n ]);\n\n const totalWidth = columnsWithWidths.reduce(\n (sum, col) => sum + (col.width || 100),\n 0,\n );\n\n useEffect(() => {\n if (currentPage !== 1) {\n // When filters change, reset pagination to the first page.\n // In server mode, also trigger a server fetch for page 1.\n if (pagination?.enabled && pagination.mode === \"server\") {\n handlePageChange(1);\n } else {\n resetPage();\n }\n }\n // Note: do not depend on currentPage or pagination props here,\n // to avoid resetting when the user simply navigates pages.\n // This effect should only respond to filter changes.\n }, [globalFilter, columnFilters]);\n\n return (\n <div\n ref={containerRef}\n className=\"border border-gray-200 dark:border-gray-700 rounded-lg overflow-hidden bg-white dark:bg-gray-900 h-full flex flex-col\"\n data-grid-container\n >\n {/* Search Toolbar */}\n {showToolbar && filterable && (\n <SearchToolbar\n globalFilter={globalFilter}\n filteredDataLength={filteredData.length}\n totalDataLength={paginationConfig.totalRows}\n paginationMode={pagination?.mode}\n selectedRowsCount={selectedRows.size}\n showExport={showExport}\n columns={columns}\n columnFilters={columnFilters}\n columnVisibility={columnVisibility}\n onResetColumns={resetColumns}\n exportOptions={{\n enabled: (exportOptionsProp?.enabled ?? true) && showExport,\n formats: exportOptionsProp?.formats ?? [\"xlsx\"],\n filename: exportOptionsProp?.filename ?? \"grid-data\",\n onExport: handleToolbarExport,\n }}\n customLeftContent={toolbarLeft}\n customRightContent={toolbarRight}\n onGlobalFilterChange={handleGlobalFilterChange}\n onClearFilters={handleClearFilters}\n onColumnFilter={setColumnFilter}\n onColumnVisibilityChange={handleColumnVisibilityChange}\n columnOrder={columnOrder}\n onColumnOrderChange={setColumnOrder}\n pinnedColumns={pinnedColumns}\n onScrollToColumn={(columnKey) => {\n const bodyEl = scrollRef.current;\n const headerEl = headerScrollRef.current;\n if (!bodyEl) return;\n // Find column index in current render order\n const idx = colIndexByKey.get(columnKey);\n if (idx == null) return;\n // Compute left position of the column by summing widths of previous columns\n const colWidth = (columnsWithWidths[idx]?.width as number) || 100;\n let left = selectable ? SELECT_COL_WIDTH : 0;\n for (let i = 0; i < idx; i++) {\n left += (columnsWithWidths[i]?.width as number) || 100;\n }\n const right = left + colWidth;\n const viewportW = bodyEl.clientWidth;\n // Scroll behavior: if already in view, minimal movement; otherwise align left\n if (left < bodyEl.scrollLeft) {\n bodyEl.scrollTo({ left, behavior: \"smooth\" });\n } else if (right > bodyEl.scrollLeft + viewportW) {\n bodyEl.scrollTo({ left: right - viewportW, behavior: \"smooth\" });\n } else {\n // Nudge slightly to center it\n const center = Math.max(\n 0,\n left - Math.max(0, (viewportW - colWidth) / 2),\n );\n bodyEl.scrollTo({ left: center, behavior: \"smooth\" });\n }\n if (headerEl) headerEl.scrollLeft = bodyEl.scrollLeft;\n }}\n density={density}\n onDensityChange={handleSetDensity}\n showDensityControl={showDensityControl}\n />\n )}\n\n {/* Grid Container */}\n <div className=\"flex flex-col relative flex-1 min-h-50\">\n {/* Group Bar (drag columns here to group) */}\n {groupable &&\n (groupBarVisibility === \"visible\" ||\n (groupBarVisibility === \"auto\" &&\n groupConfig.columnKeys.length > 0)) && (\n <GroupBar\n columns={columns}\n groupedKeys={groupConfig.columnKeys}\n onRemove={removeGroupKey}\n onReorder={setGroupKeys}\n onDropColumnKey={addGroupKey}\n isAnyExpanded={groupConfig.expanded.size > 0}\n onToggleExpandAll={() => {\n if (groupConfig.expanded.size > 0) {\n collapseAllGroups();\n } else {\n expandAllGroups();\n }\n }}\n />\n )}\n {/* Header moved inside body scroll area as sticky for perfect sync */}\n\n {/* Body */}\n <div\n ref={scrollRef}\n className={`flex-1 overflow-auto relative [&::-webkit-scrollbar]:thin ${\n cellSelectionEnabled ? \"select-none\" : \"\"\n }`}\n onScroll={handleScroll}\n >\n {/* Sticky header inside the same scroll container */}\n <div className=\"sticky top-0 z-3\">\n <div\n style={{\n minWidth: `${\n totalWidth + (selectable ? SELECT_COL_WIDTH : 0)\n }px`,\n }}\n >\n {(() => {\n const headerPinned = pinnedAll;\n const headerUnpinned = visibleColumns.filter(\n (c) => !effectivePinnedColumns.has(c.key),\n );\n\n return (\n <GridHeader\n pinnedColumns={headerPinned}\n unpinnedColumns={headerUnpinned}\n hvPadLeft={hvPadLeftMemo}\n hvPadRight={hvPadRightMemo}\n headerHeight={resolvedHeaderHeight}\n sortConfig={sortConfig}\n columnFilters={columnFilters}\n selectable={selectable}\n selectedRows={selectedRows}\n totalRows={dataToDisplay.length}\n data={data}\n onSort={handleSort}\n onColumnFilter={setColumnFilter}\n onSelectAll={() =>\n handleSelectAll(\n selectedRows.size !== dataToDisplay.length,\n )\n }\n onColumnResize={handleColumnResize}\n pinnedKeySet={effectivePinnedColumns}\n onColumnPin={handleColumnPin}\n groupable={groupable}\n groupedByColumn={groupConfig.columnKeys[0] || null}\n groupedByColumns={groupConfig.columnKeys}\n onGroupToggle={(key, next) =>\n next ? addGroupKey(key) : removeGroupKey(key)\n }\n onGroupBy={handleGroupBy}\n onAutosizeColumn={autosizeColumn}\n onAutosizeAllColumns={autosizeAllColumns}\n onResetColumns={resetColumns}\n columnOrder={columnOrder}\n onColumnOrderChange={setColumnOrder}\n paginationMode={\n pagination?.enabled ? pagination.mode : null\n }\n />\n );\n })()}\n </div>\n </div>\n\n <div\n style={{\n minWidth: `${totalWidth + (selectable ? SELECT_COL_WIDTH : 0)}px`,\n height: effectiveVirtualized\n ? `${dataToDisplay.length * resolvedRowHeight}px`\n : \"auto\",\n }}\n >\n {/* Loading bar is rendered at the bottom of the sticky header above */}\n {/* Empty state: content area renders nothing; overlay is placed outside scrollRef */}\n {!isLoading && dataToDisplay.length === 0 ? (\n <></>\n ) : (\n <div\n style={{\n transform: `translateY(${\n effectiveVirtualized ? virtualizedRange.offsetY : 0\n }px)`,\n willChange: \"transform\",\n contain: \"content\",\n }}\n >\n {(() => {\n const nodes: React.ReactNode[] = [];\n let chunk: any[] = [];\n const flush = () => {\n if (!chunk.length) return;\n const key = `chunk-${chunk[0]?.id}-${chunk.length}-${virtualizedRange.offsetY}`;\n nodes.push(\n <div key={key} style={{ opacity: isLoading ? 0.5 : 1 }}>\n <GridRows\n data={chunk}\n columns={visibleColumns}\n selectedRows={selectedRows}\n virtualized={false}\n virtualizedRange={{\n startIndex: 0,\n endIndex: chunk.length - 1,\n offsetY: 0,\n }}\n rowHeight={resolvedRowHeight}\n selectable={selectable}\n isRowSelectable={isRowSelectable}\n onRowSelect={handleRowSelect}\n pinnedColumns={effectivePinnedColumns}\n hvPadLeft={hvPadLeftMemo}\n hvPadRight={hvPadRightMemo}\n rowStyle={rowStyle}\n globalFilter={globalFilter}\n onContextMenu={onContextMenu}\n onRowDoubleClick={onRowDoubleClick}\n onRowClick={onRowClick}\n onCellClick={onCellClick}\n onCellContextMenu={onCellContextMenu}\n isCellFocused={isCellFocused}\n isCellSelected={isCellSelected}\n onCellMouseDown={handleCellMouseDown}\n onCellMouseEnter={handleCellMouseEnter}\n getRowId={getRowId}\n />\n </div>,\n );\n chunk = [];\n };\n\n for (const row of visibleData) {\n if ((row as any)._isGroupHeader) {\n flush();\n nodes.push(\n <GroupHeader\n key={row.id}\n row={row}\n isExpanded={groupConfig.expanded.has(\n row._groupKey || \"\",\n )}\n onToggle={toggleGroupExpansion}\n viewportWidth={containerWidth}\n selectable={selectable}\n rowHeight={resolvedRowHeight}\n getHeaderLabel={(colKey) =>\n columns.find((c) => c.key === colKey)?.header ||\n colKey\n }\n getValueLabel={(colKey, raw) => {\n const col = columns.find((c) => c.key === colKey);\n if (col?.formatter) {\n try {\n return col.formatter(raw);\n } catch (_) {\n return String(raw ?? \"\");\n }\n }\n return String(raw ?? \"\");\n }}\n renderGroupActions={renderGroupActions}\n />,\n );\n } else if ((row as any)._isGroupFooter) {\n flush();\n // Use a consistent light blue theme for aggregate footers (with dark mode support)\n if (groupFooterVariant === \"chips\") {\n // Build chips for columns that requested aggregation and have a value\n const chips = columnsWithWidths\n .filter(\n (c) =>\n !!c.aggregate &&\n (row as any)._groupAgg &&\n (row as any)._groupAgg[c.key] !== undefined,\n )\n .map((c) => {\n const rawVal = (row as any)._groupAgg[c.key];\n const fmt = c.aggregateFormatter || c.formatter;\n const display = fmt\n ? (() => {\n try {\n return fmt(rawVal);\n } catch {\n return String(rawVal);\n }\n })()\n : String(rawVal);\n return (\n <span\n key={c.key}\n className={\n // Use static Tailwind classes to avoid JIT purge issues\n \"px-2.5 py-1 rounded-md bg-blue-50 dark:bg-blue-900/40 text-blue-700 dark:text-blue-200 text-xs font-medium\"\n }\n title={`${c.header}`}\n >\n {c.header}: {display}\n </span>\n );\n });\n\n nodes.push(\n <div\n key={row.id}\n className=\"sticky left-0 z-1\"\n style={{\n minWidth: `${\n totalWidth + (selectable ? SELECT_COL_WIDTH : 0)\n }px`,\n height: `${resolvedRowHeight}px`,\n }}\n >\n <div\n className={\n // Static Tailwind classes for 'blue' theme\n \"flex items-center justify-between h-full border-t border-blue-200 dark:border-blue-800 bg-blue-50/60 dark:bg-blue-900/20 px-4\"\n }\n role=\"contentinfo\"\n >\n <div className=\"flex items-center gap-2 flex-wrap overflow-hidden\">\n {chips.length ? (\n chips\n ) : (\n <span className=\"text-xs text-gray-500\">\n No aggregates\n </span>\n )}\n </div>\n <span className=\"text-[10px] uppercase tracking-wide text-blue-700 dark:text-blue-300\">\n Group Summary\n </span>\n </div>\n </div>,\n );\n } else {\n // columns variant: show aggregate values in-place under their respective columns\n nodes.push(\n <div\n key={row.id}\n className=\"sticky left-0 z-1 bg-blue-100 dark:bg-gray-900\"\n style={{\n minWidth: `${\n totalWidth + (selectable ? SELECT_COL_WIDTH : 0)\n }px`,\n height: `${resolvedRowHeight}px`,\n }}\n >\n <div\n className={\n // Static Tailwind classes matching the 'chips' variant theme\n \"flex h-full border-t border-blue-200 dark:border-blue-800 bg-blue-50/60 dark:bg-blue-900/20\"\n }\n role=\"contentinfo\"\n >\n {selectable && (\n <div\n style={{ width: SELECT_COL_WIDTH }}\n className=\"shrink-0\"\n />\n )}\n {columnsWithWidths.map((col) => {\n const width = (col.width as number) || 100;\n const rawVal = (row as any)._groupAgg\n ? (row as any)._groupAgg[col.key]\n : undefined;\n const fmt =\n col.aggregateFormatter || col.formatter;\n const raw =\n rawVal === undefined\n ? \"\"\n : fmt\n ? (() => {\n try {\n return fmt(rawVal);\n } catch {\n return rawVal;\n }\n })()\n : rawVal;\n\n // ensure final format is 2 decimals only for decimal numbers\n let display;\n if (typeof raw === \"number\") {\n display = Number.isInteger(raw)\n ? String(raw)\n : raw.toFixed(2);\n } else if (\n !isNaN(raw) &&\n raw !== \"\" &&\n raw !== null\n ) {\n // also handles when formatter returned string numbers\n const num = Number(raw);\n display = Number.isInteger(num)\n ? String(num)\n : num.toFixed(2);\n } else {\n display = String(raw ?? \"\");\n }\n return (\n <div\n key={`agg-cell-${(row as any)._groupKey}-${\n col.key\n }`}\n className=\"px-2 text-xs font-medium text-blue-800 dark:text-blue-200 flex items-center overflow-hidden\"\n style={{\n width,\n minWidth: width,\n maxWidth: width,\n }}\n title={\n display\n ? `${col.header}: ${display}`\n : undefined\n }\n >\n <span className=\"truncate w-full\">\n {display}\n </span>\n </div>\n );\n })}\n </div>\n </div>,\n );\n }\n } else {\n chunk.push(row);\n }\n }\n flush();\n return nodes;\n })()}\n </div>\n )}\n </div>\n </div>\n {/* Loading bar overlay (outside scroll area, like NoDataMessage) */}\n {showLoader && (\n <div\n className={`pointer-events-none absolute left-0 right-0 h-1 bg-gray-200 overflow-hidden z-30 ${\n finishingLoader\n ? \"opacity-0 transition-opacity duration-200\"\n : \"opacity-100\"\n }`}\n style={{ top: loaderTop }}\n >\n <div className=\"h-full w-1/3 bg-blue-500 animate-[slide_1.2s_linear_infinite]\" />\n </div>\n )}\n {/* Viewport overlay for NoDataMessage that does not scroll horizontally/vertically */}\n {!isLoading && dataToDisplay.length === 0 && (\n <div className=\"pointer-events-none absolute inset-0 z-0 flex items-center justify-center\">\n <div className=\"pointer-events-auto\">\n <NoDataMessage\n hasFilters={\n !!globalFilter || Object.keys(columnFilters).length > 0\n }\n hasData={data.length > 0}\n />\n </div>\n </div>\n )}\n </div>\n\n {/* Footer Aggregate */}\n {columnsWithWidths.some((col) => col.footer_aggregate) && (\n <div\n ref={footerScrollRef}\n className=\"shrink-0 overflow-x-hidden overflow-y-visible [&::-webkit-scrollbar]:hidden\"\n style={{ scrollbarWidth: \"none\", msOverflowStyle: \"none\" }}\n onWheel={forwardWheelToBody}\n >\n <div\n className=\"bg-gray-50 dark:bg-gray-800 border-t border-gray-200 dark:border-gray-700\"\n style={{\n minWidth: `${totalWidth + (selectable ? SELECT_COL_WIDTH : 0)}px`,\n height: `${resolvedRowHeight}px`,\n }}\n >\n <FooterAggregate\n columns={columnsWithWidths}\n data={flatRows}\n selectable={selectable}\n rowHeight={resolvedRowHeight}\n pinnedColumns={effectivePinnedColumns}\n />\n </div>\n </div>\n )}\n\n {/* Pagination Footer */}\n\n <PaginationControls\n paginationConfig={paginationConfig}\n currentPage={paginationConfig.currentPage}\n isServerLoading={isServerLoading}\n selectedRowsCount={selectedRows.size}\n totalDataLength={data.length}\n filteredDataLength={\n pagination?.mode === \"server\"\n ? undefined // Don't pass filtered data length for server mode\n : filteredData.length\n }\n paginationMode={pagination.mode}\n onPageChange={handlePageChange}\n onPageSizeChange={handlePageSizeChange}\n />\n\n {/* Drag selection overlay (fixed near cursor) */}\n {dragOverlay && (\n <div\n className=\"fixed z-50 pointer-events-none select-none\"\n style={{ top: dragOverlay.y + 12, left: dragOverlay.x + 12 }}\n >\n <div className=\"px-2 py-1 rounded bg-gray-800 text-white text-xs shadow-lg opacity-90\">\n {dragOverlay.rows} × {dragOverlay.cols}\n </div>\n </div>\n )}\n </div>\n );\n});\n\nexport default CustomDataGrid;\n"],"names":["CustomDataGrid","forwardRef","data","columns","getRowId","height","density","densityProp","headerDensity","headerHeight","headerHeightProp","onDensityChange","showDensityControl","onRowSelect","onSelectedRowsChange","selectable","isRowSelectable","showToolbar","showExport","filterable","groupable","virtualized","rowHeight","rowHeightProp","overscan","rowStyle","onContextMenu","onRowDoubleClick","onRowClick","onCellClick","onCellContextMenu","cellFocusEnabled","onCellFocusChange","cellSelectionEnabled","canSelectCell","onCellSelectionChange","pagination","enabled","mode","pageSize","virtualizationThreshold","isLoading","onFilterChange","onSort","onColumnConfigChange","initialColumnConfig","toolbarLeft","toolbarRight","groupBarVisibility","groupFooterVariant","onCopyCells","selectedRowIds","performanceConfig","exportOptions","exportOptionsProp","renderGroupActions","ref","containerRef","useRef","containerWidth","setContainerWidth","useState","useEffect","el","current","update","clientWidth","ro","ResizeObserver","observe","window","addEventListener","disconnect","removeEventListener","focusedCell","setFocusedCell","selectionAnchor","setSelectionAnchor","selectionEnd","setSelectionEnd","isMouseDownRef","lastMousePosRef","autoScrollRafRef","dragOverlay","setDragOverlay","setDensity","densityMap","useMemo","sm","md","lg","resolvedRowHeight","resolvedHeaderHeight","globalFilter","columnFilters","filteredData","setGlobalFilter","setColumnFilter","clearAllFilters","useAdvancedFiltering","sortConfig","setSortConfig","key","direction","columnWidths","setColumnWidths","initialWidths","forEach","col","width","initialWidthsRef","reduce","acc","c","initialOrderRef","map","columnOrder","setColumnOrder","slice","pinnedColumns","setPinnedColumns","Set","columnVisibility","setColumnVisibility","initialVisibility","handleSort","useCallback","nextKey","nextDir","handleColumnResize","columnKey","prev","estimateWidthForText","text","String","length","autosizeColumn","find","headerWidth","header","sample","maxCell","row","value","Math","max","computed","min","autosizeAllColumns","resetColumns","next","Object","keys","k","headerScrollRef","footerScrollRef","handleColumnPin","pinned","newSet","add","delete","handleColumnVisibilityChange","visible","process","workerProcess","useDataWorker","sortedData","setSortedData","workerThreshold","sortWorkerThreshold","cancelled","rows","dir","out","sort","a","b","av","bv","localeCompare","then","paginationConfig","paginatedData","isServerLoading","handlePageChange","handlePageSizeChange","resetPage","currentPage","usePagination","loadingOverall","showLoader","setShowLoader","finishingLoader","setFinishingLoader","loaderTop","setLoaderTop","t","setTimeout","clearTimeout","handleGlobalFilterChange","e","target","groupConfig","displayData","groupedDisplayData","handleGroupBy","addGroupKey","removeGroupKey","setGroupKeys","toggleGroupExpansion","isGrouped","expandAllGroups","collapseAllGroups","useGrouping","useImperativeHandle","getGroupConfig","filter","clearColumnFilter","effectivePinnedColumns","finalDisplayData","dataToDisplay","selectedRows","handleRowSelect","handleSelectAll","useSelection","onSelectedRowsChangeRef","exportData","exportDataWithSelection","useExport","filename","handleToolbarExport","format","exportSelected","selectedIds","Array","from","exportMode","onServerExport","handleClearFilters","handleSetDensity","d","effectiveVirtualized","perfConfig","enableHorizontalVirtualization","horizontalVirtualizationThreshold","maxFilterCacheSize","enableAggressiveMemoization","scrollRef","virtualizedRange","visibleData","handleScroll","useVirtualization","top","offsetTop","hScrollLeft","setHScrollLeft","hScrollLeftRef","hScrollRafRef","scheduleHScrollUpdate","requestAnimationFrame","viewportWidth","setViewportWidth","filterableKeys","existingKeys","fromEntries","entries","has","pins","filters","v","filtered","remaining","includes","columnKeys","timer","columnsWithWidths","orderIndex","Map","i","base","get","candidate","Number","isFinite","sortable","viewportW","SELECT_COL_WIDTH","baseTotal","sum","flexCols","flex","extra","totalWeight","weight","increment","minW","minWidth","maxW","maxWidth","POSITIVE_INFINITY","unpinned","push","groupIndex","needsHorizontalVirtualization","pinnedAll","unpinnedAll","unpinnedMeta","widths","prefix","fill","totalWidth","visibleColumns","leftPad","pinnedTotal","targetLeft","targetRight","localLeft","localRight","startIdx","x","lo","hi","ans","mid","firstIndexWithRightGte","endIdx","lastIndexWithLeftLte","sliced","unpinnedIndexByKey","m","set","hvPadLeftMemo","hvPadRightMemo","visibleUnpinned","firstIdx","lastIdx","hasFooterAggregate","some","footer_aggregate","bodyElement","isBodyScrolling","syncFromBody","footerEl","scrollLeft","passive","colIndexByKey","colByKey","flatRows","r","_isGroupHeader","_isGroupFooter","rowIndexById","id","rowById","prevSelectionSignatureRef","onCellSelectionChangeRef","onCopyCellsRef","isCellSelected","rowId","rowObj","colObj","rMin","rowIndex","rMax","cMin","colIndex","cMax","isCellFocused","forwardWheelToBody","bodyEl","dx","abs","deltaX","deltaY","preventDefault","scrollBy","left","behavior","handleCellMouseDown","column","event","rIndex","cIndex","document","body","hasAttribute","button","clientX","y","clientY","cols","handleCellMouseEnter","onUp","cancelAnimationFrame","onMove","ensureAutoScrollLoop","step","mp","rect","getBoundingClientRect","margin","maxSpeed","dy","right","bottom","clamp","onKey","isShift","shiftKey","arrow","maxRow","maxCol","nr","nc","headerEl","handler","currentRowIdx","currentColIdx","nextRow","nextCol","viewportH","clientHeight","targetTop","targetBottom","scrollTop","colWidth","ensureVisible","payload","bounds","cells","sig","rowStart","rowEnd","colStart","colEnd","onKeyDown","async","metaKey","ctrlKey","toLowerCase","includeHeaders","hasRect","hasFocus","sel","getSelection","toString","active","activeElement","tagName","isContentEditable","collectCells","escapeCell","val","s","replace","headers","join","line","raw","navigator","clipboard","writeText","console","warn","isRectangular","isFocusedCell","_jsxs","className","children","_jsx","SearchToolbar","filteredDataLength","totalDataLength","totalRows","paginationMode","selectedRowsCount","size","onResetColumns","formats","onExport","customLeftContent","customRightContent","onGlobalFilterChange","onClearFilters","onColumnFilter","onColumnVisibilityChange","onColumnOrderChange","onScrollToColumn","idx","scrollTo","center","GroupBar","groupedKeys","onRemove","onReorder","onDropColumnKey","isAnyExpanded","expanded","onToggleExpandAll","onScroll","style","headerPinned","headerUnpinned","GridHeader","unpinnedColumns","hvPadLeft","hvPadRight","onSelectAll","onColumnResize","pinnedKeySet","onColumnPin","groupedByColumn","groupedByColumns","onGroupToggle","onGroupBy","onAutosizeColumn","onAutosizeAllColumns","transform","offsetY","willChange","contain","nodes","chunk","flush","opacity","GridRows","startIndex","endIndex","onCellMouseDown","onCellMouseEnter","GroupHeader","isExpanded","_groupKey","onToggle","getHeaderLabel","colKey","getValueLabel","formatter","_","chips","aggregate","_groupAgg","undefined","rawVal","fmt","aggregateFormatter","display","title","role","isInteger","toFixed","isNaN","num","_Fragment","NoDataMessage","hasFilters","hasData","scrollbarWidth","msOverflowStyle","onWheel","FooterAggregate","PaginationControls","onPageChange","onPageSizeChange"],"mappings":"4mCA4Ba,MAAAA,EAAiBC,EAG5B,UACAC,KACEA,EAAIC,QACJA,EAAOC,SACPA,EAAQC,OACRA,EACAC,QAASC,EAAc,KAAIC,cAC3BA,EACAC,aAAcC,EAAgBC,gBAC9BA,EAAeC,mBACfA,GAAqB,EAAKC,YAC1BA,EAAWC,qBACXA,EAAoBC,WACpBA,GAAa,EAAKC,gBAClBA,EAAeC,YACfA,GAAc,EAAIC,WAClBA,GAAa,EAAKC,WAClBA,GAAa,EAAIC,UACjBA,GAAY,EAAKC,YACjBA,GAAc,EACdC,UAAWC,EAAaC,SACxBA,EAAW,EAACC,SACZA,EAAQC,cACRA,EAAaC,iBACbA,EAAgBC,WAChBA,EAAUC,YACVA,EAAWC,kBACXA,EAAiBC,iBACjBA,GAAmB,EAAKC,kBACxBA,EAAiBC,qBACjBA,GAAuB,EAAKC,cAC5BA,GAAgB,KAAM,EAAIC,sBAC1BA,GAAqBC,WACrBA,GAAa,CAAEC,SAAS,EAAOC,KAAM,SAAUC,SAAU,IAAIC,wBAC7DA,GAA0B,IAAGC,UAC7BA,IAAY,EAAKC,eACjBA,GAAcC,OACdA,GAAMC,qBACNA,GAAoBC,oBACpBA,GAAmBC,YACnBA,GAAWC,aACXA,GAAYC,mBACZA,GAAqB,OAAMC,mBAC3BA,GAAqB,QAAOC,YAC5BA,GAAWC,eACXA,GAAcC,kBACdA,GAAoB,CAAA,EACpBC,cAAeC,GAAiBC,mBAChCA,IAEFC,IAGA,MAAMC,GAAeC,EAAuB,OACrCC,GAAgBC,IAAqBC,EAAiB,GAC7DC,EAAU,KACR,MAAMC,EAAKN,GAAaO,QACxB,IAAKD,EAAI,OACT,MAAME,EAAS,IAAML,GAAkBG,EAAGG,aAAe,GACzDD,IACA,MAAME,EAAK,IAAIC,eAAe,IAAMH,KAGpC,OAFAE,EAAGE,QAAQN,GACXO,OAAOC,iBAAiB,SAAUN,GAC3B,KACLE,EAAGK,aACHF,OAAOG,oBAAoB,SAAUR,KAEtC,IAEH,MAAOS,GAAaC,IAAkBd,EAG5B,OACHe,GAAiBC,IAAsBhB,EAGpC,OACHiB,GAAcC,IAAmBlB,EAG9B,MACJmB,GAAiBtB,GAAO,GACxBuB,GAAkBvB,EAAwC,MAC1DwB,GAAmBxB,EAAsB,OACxCyB,GAAaC,IAAkBvB,EAK5B,OAGHvD,GAAS+E,IAAcxB,EAAkBtD,GAChDuD,EAAU,KAERuB,GAAW9E,IACV,CAACA,IAEJ,MAAM+E,GAAsCC,EAC1C,KAAO,CAAEC,GAAI,GAAIC,GAAI,GAAIC,GAAI,KAC7B,IAEIC,GAAoBJ,EACxB,IAAwB,MAAjBhE,EAAwBA,EAAgB+D,GAAWhF,IAC1D,CAACiB,EAAejB,GAASgF,KAGrBM,GAAuBL,EAAQ,KACnC,GAAwB,MAApB7E,EAA0B,OAAOA,EAErC,OAAO4E,GADG9E,GAAiB,OAE1B,CAACE,EAAkBF,EAAe8E,MAE/BO,aACJA,GAAYC,cACZA,GAAaC,aACbA,GAAYC,gBACZA,GAAeC,gBACfA,GAAeC,gBACfA,IACEC,EAAqB,CAAEjG,OAAMC,aAG1BiG,GAAYC,IAAiBxC,EAGjC,CACDyC,IAAK,KACLC,UAAW,QAINC,GAAcC,IAAmB5C,EACtC,KACE,MAAM6C,EAAwC,CAAA,EAI9C,OAHAvG,EAAQwG,QAASC,IACfF,EAAcE,EAAIN,KAAOM,EAAIC,OAAS,MAEjCH,IAKLI,GAAmBpD,EACvBvD,EAAQ4G,OACN,CAACC,EAAKC,KACJD,EAAIC,EAAEX,KAAOW,EAAEJ,OAAS,IACjBG,GAET,CAAA,IAKEE,GAAkBxD,EAAiBvD,EAAQgH,IAAKF,GAAMA,EAAEX,OACvDc,GAAaC,IAAkBxD,EAAmB,IACvDqD,GAAgBlD,QAAQsD,UAInBC,GAAeC,IAAoB3D,EAAsB,IAAI4D,MAG7DC,GAAkBC,IAAuB9D,EAE9C,KACA,MAAM+D,EAA6C,CAAA,EAInD,OAHAzH,EAAQwG,QAASC,IACfgB,EAAkBhB,EAAIN,MAAO,IAExBsB,IAGHC,GAAaC,EAChBxB,IACC,IAAIyB,EAAyBzB,EACzB0B,EAAiC,MAEjC5B,GAAWE,MAAQA,GACrByB,EAAUzB,EACV0B,EAAU,OACwB,QAAzB5B,GAAWG,UACpByB,EAAU,OACwB,SAAzB5B,GAAWG,WAEpBwB,EAAU,KACVC,EAAU,MAEVA,EAAU,MAGZ3B,GAAc,CAAEC,IAAKyB,EAASxB,UAAWyB,IAGrCrF,IAA+B,WAArBP,IAAYE,MAAqByF,GAAWC,GACxDrF,GAAOoF,EAASC,IAGpB,CAACrF,GAAQP,IAAYE,KAAM8D,GAAWE,IAAKF,GAAWG,YAGlD0B,GAAqBH,EAAY,CAACI,EAAmBrB,KACzDJ,GAAiB0B,IAAU,IACtBA,EACHD,CAACA,GAAYrB,MAEd,IAGGuB,GAAuBN,EAAaO,IACxC,GAAY,MAARA,EAAc,OAAO,EAGzB,OADkB,GADQ,iBAATA,EAAoBA,EAAOC,OAAOD,IAE1CE,QACR,IAEGC,GAAiBV,EACpBI,IACC,MAAMtB,EAAMzG,EAAQsI,KAAMxB,GAAMA,EAAEX,MAAQ4B,GAC1C,IAAKtB,EAAK,OACV,MAAM8B,EAAcN,GAAqBxB,EAAI+B,QACvCC,EAAS1I,EAAKoH,MAAM,EAAG,KAC7B,IAAIuB,EAAU,EACd,IAAK,MAAMC,KAAOF,EAAQ,CACxB,MAAMG,EAAQD,EAAIZ,GAClBW,EAAUG,KAAKC,IAAIJ,EAAST,GAAqBW,GAClD,CACD,MACMG,EAAWF,KAAKC,IACpB,GACAD,KAAKG,IAAI,IAAKH,KAAKC,IAAIP,EAAaG,GAHtB,KAKhBpC,GAAiB0B,IAAI,IAAWA,EAAMD,CAACA,GAAYgB,MAErD,CAAC/I,EAASD,EAAMkI,KAGZgB,GAAqBtB,EAAY,KACrC3H,EAAQwG,QAASM,GAAMuB,GAAevB,EAAEX,OACvC,CAACnG,EAASqI,KAEPa,GAAevB,EAAY,KAE/BrB,GAAgB,IAAKK,GAAiB9C,UAEtCwD,GAAiB,IAAIC,KAErBE,GAAqBQ,IACnB,MAAMmB,EAAgC,CAAA,EAEtC,OADAC,OAAOC,KAAKrB,GAAMxB,QAAS8C,GAAOH,EAAKG,IAAK,GACrCH,IAGTjC,GAAeH,GAAgBlD,QAAQsD,UACtC,IAEGoC,GAAkBhG,EAAuB,MACzCiG,GAAkBjG,EAAuB,MAEzCkG,GAAkB9B,EAAY,CAACI,EAAmB2B,KACtDrC,GAAkBW,IAChB,MAAM2B,EAAS,IAAIrC,IAAIU,GAMvB,OALI0B,EACFC,EAAOC,IAAI7B,GAEX4B,EAAOE,OAAO9B,GAET4B,KAER,IAEGG,GAA+BnC,EACnC,CAACI,EAAmBgC,KAClBvC,GAAqBQ,IAAU,IAC1BA,EACHD,CAACA,GAAYgC,MAGjB,KAIMC,QAASC,IAAkBC,EAAc,CAAEhI,SAAS,KACrDiI,GAAYC,IAAiB1G,EAAgBkC,IAG9CyE,GACHpH,IAA2BqH,qBAAuB,IAErD3G,EAAU,KACR,IAAI4G,GAAY,EAChB,IAAKtE,GAAWE,IAEd,YADAiE,GAAcxE,IAGhB,MAAM4E,EAAO5E,GACb,GAAI4E,EAAKpC,OAASiC,GAAiB,CAEjC,MAAMI,EAA+B,QAAzBxE,GAAWG,UAAsB,GAAK,EAC5CsE,EAAM,IAAIF,GAAMG,KAAK,CAACC,EAAGC,KAC7B,MAAMC,EAAKF,IAAI3E,GAAWE,KACpB4E,EAAKF,IAAI5E,GAAWE,KAC1B,OAAU,MAAN2E,GAAoB,MAANC,EAAmB,EAC3B,MAAND,GAAoB,EAAIL,EAClB,MAANM,EAAmB,EAAIN,EACT,iBAAPK,GAAiC,iBAAPC,GAC3BD,EAAKC,GAAMN,EACdtC,OAAO2C,GAAIE,cAAc7C,OAAO4C,IAAON,IAGhD,YADAL,GAAcM,EAEf,CAOD,OANAT,GAAcO,EAAM,CAClBrE,IAAKF,GAAWE,IAChBC,UAAWH,GAAWG,YACrB6E,KAAMP,IACFH,GAAWH,GAAcM,KAEzB,KACLH,GAAY,IAEb,CAAC3E,GAAcK,GAAYgE,GAAeI,KAE7C,MAAMa,iBACJA,GAAgBC,cAChBA,GAAaC,gBACbA,GAAeC,iBACfA,GAAgBC,qBAChBA,GAAoBC,UACpBA,GAASC,YACTA,IACEC,EAAc,CAAE1L,KAAMoK,GAAYlI,gBAGhCyJ,GAAiBpJ,IAAa8I,IAC7BO,GAAYC,IAAiBlI,EAAkBgI,KAC/CG,GAAiBC,IAAsBpI,GAAkB,IACzDqI,GAAWC,IAAgBtI,EAAiB,GACnDC,EAAU,KACR,GAAI+H,GAGF,OAFAE,IAAc,QACdE,IAAmB,GAGrB,GAAIH,GAAY,CACdG,IAAmB,GACnB,MAAMG,EAAIC,WAAW,KACnBN,IAAc,GACdE,IAAmB,IAClB,KACH,MAAO,IAAMK,aAAaF,EAC3B,GACA,CAACP,KAEJ,MAAMU,GAA2BzE,EAC9B0E,IACCxG,GAAgBwG,EAAEC,OAAO1D,QAE3B,CAAC/C,MAIG0G,YACJA,GACAC,YAAaC,GAAkBC,cAC/BA,GAAaC,YACbA,GAAWC,eACXA,GAAcC,aACdA,GAAYC,qBACZA,GAAoBC,UACpBA,GAASC,gBACTA,GAAeC,kBACfA,IACEC,EAAY,CAAEnN,KAAMoK,GAAYnK,YAGpCmN,EACE9J,GACA,KAAO,CACLwJ,gBACAF,eACAC,kBACAI,mBACAC,qBACAH,wBACAM,eAAgB,IAAMb,GAEtBzG,gBAAiB,CAACiC,EAAmBsF,KACnCvH,GAAgBiC,EAAWsF,IAE7BC,kBAAoBvF,IAClBjC,GAAgBiC,EAAW,OAE7BhC,gBAAiB,KACfA,MAEFF,gBAAkB+C,IAChB/C,GAAgB+C,MAGpB,CAAC2D,GAAazG,GAAiBC,GAAiBF,KAIlD,MAAM0H,GAAyBnI,EAAQ,IAC9B,IAAIkC,IAAIF,IACd,CAACA,KAEEoG,GAAmBvL,IAAYC,QACb,WAApBD,GAAWE,KACTpC,EACAoL,GACFhB,GAEEsD,GAAgBV,GAAYN,GAAqBe,IAEjDE,aAAEA,GAAYC,gBAAEA,GAAeC,gBAAEA,IAAoBC,EAAa,CACtE9N,KAAM0N,GACN/M,cACAsC,kBACA/C,aAII6N,GAA0BvK,EAAO5C,GACvCgD,EAAU,KACRmK,GAAwBjK,QAAUlD,GACjC,CAACA,IAEJgD,EAAU,KACJmK,GAAwBjK,SAC1BiK,GAAwBjK,QAAQ6J,KAEjC,CAACA,KAGJ,MAAQK,WAAYC,IAA4BC,EAAU,CAExDlO,KAAMyN,GACNxN,UACA0N,gBACAQ,SAAW/K,IAAqBA,GAAkB+K,UAAa,YAC/D3G,sBAII4G,GAAsBxG,EAC1B,CAACyG,EAAaC,KACZ,MAAMC,EAAcC,MAAMC,KAAKd,IAAgB,IAEX,WAAlCvK,IAAmBsL,YAC0B,mBAAtCtL,IAAmBuL,eAE1BvL,GAAkBuL,eAAeN,EAAQC,EAAgBC,GAGzDN,GAAwBI,EAAQC,IAGpC,CACElL,IAAmBsL,WACnBtL,IAAmBuL,eACnBV,GACAN,KAIEiB,GAAqBhH,EAAY,KACrC5B,MACC,CAACA,KAEE6I,GAAmBjH,EACtBkH,IACC3J,GAAW2J,GACPrO,GAAiBA,EAAgBqO,IAEvC,CAACrO,IAGGsO,GAAuB1J,EAAQ,MAC9BlE,KACDuM,GAAcrF,QAAU/F,IAE3B,CACDnB,EACAuM,GAAcrF,OACd/F,GACA0K,GACAjL,IAIIiN,GAAa3J,EACjB,KAAO,CACL4J,gCAAgC,EAChCC,kCAAmC,GACnC3E,oBAAqB,IACrB4E,mBAAoB,IACpBC,6BAA6B,KAC1BlM,KAEL,CAACA,MAGGmM,UAAEA,GAASC,iBAAEA,GAAgBC,YAAEA,GAAWC,aAAEA,IAChDC,EAAkB,CAChBzP,KAAM0N,GACNvM,YAAa4N,GACb3N,UAAWqE,GACXnE,WACAnB,OAAQA,GAAU,MAItByD,EAAU,KACR,MAAMG,EAAS,KACb,MAAMF,EAAKwL,GAAUvL,QACrB,IAAKD,EAAI,OAET,MAAM6L,GAAO7L,EAAG8L,WAAa,IAAMjK,IAAwB,GAAK,EAChEuG,GAAayD,IAEf3L,IACA,MAAME,EAAK,IAAIC,eAAeH,GAI9B,OAHIR,GAAaO,SAASG,EAAGE,QAAQZ,GAAaO,SAC9CuL,GAAUvL,SAASG,EAAGE,QAAQkL,GAAUvL,SAC5CM,OAAOC,iBAAiB,SAAUN,GAC3B,KACLE,EAAGK,aACHF,OAAOG,oBAAoB,SAAUR,KAEtC,CAAC2B,KAGJ,MAAOkK,GAAaC,IAAkBlM,EAAS,GACzCmM,GAAiBtM,EAAO,GACxBuM,GAAgBvM,EAAsB,MACtCwM,GAAwBpI,EAAaiB,IACzCiH,GAAehM,QAAU+E,EACI,MAAzBkH,GAAcjM,UAClBiM,GAAcjM,QAAUmM,sBAAsB,KAC5CF,GAAcjM,QAAU,KACxB+L,GAAeC,GAAehM,aAE/B,KACIoM,GAAeC,IAAoBxM,EAAS,GACnDC,EAAU,KACR,MAAMC,EAAKwL,GAAUvL,QACrB,IAAKD,EAAI,OACT,MAAME,EAAS,IAAMoM,GAAiBtM,EAAGG,aAAe,GACxDD,IACA,MAAME,EAAK,IAAIC,eAAeH,GAE9B,OADAE,EAAGE,QAAQN,GACJ,IAAMI,EAAGK,cACf,CAAC+K,KAGJzL,EAAU,KACR,IAAKpB,GAAgB,OACrB,IAAMN,IAAYC,SAA+B,WAApBD,GAAWE,KAAoB,OAE5D,MAAMgO,EAAiBnQ,EACpBqN,OAAQvG,KAAQA,EAAE9F,YAClBgG,IAAKF,GAAMA,EAAEX,KAEhB5D,GAAe,CACbmD,gBACAC,iBACAwK,oBAED,CAACzK,GAAcC,KAGlBhC,EAAU,KACR,IAAKjB,GAAqB,OAC1B,MAAM0N,EAAe,IAAI9I,IAAItH,EAAQgH,IAAKF,GAAMA,EAAEX,MAElD,GAAIzD,GAAoB2D,aAAc,CACpC,MAAM8C,EAAOC,OAAOiH,YAClBjH,OAAOkH,QAAQ5N,GAAoB2D,cAAcgH,OAAO,EAAE/D,KACxD8G,EAAaG,IAAIjH,KAGrBhD,GAAiB0B,QAAeA,KAASmB,IAC1C,CAED,GAAIzG,GAAoB6E,iBAAkB,CACxC,MAAM4B,EAAOC,OAAOiH,YAClBjH,OAAOkH,QAAQ5N,GAAoB6E,kBAAkB8F,OAAO,EAAE/D,KAC5D8G,EAAaG,IAAIjH,KAGrB9B,GAAqBQ,QAAeA,KAASmB,IAC9C,CAED,GAAIzG,GAAoB0E,cAAe,CACrC,MAAMoJ,EAAO9N,GAAoB0E,cAAciG,OAAQ/D,GACrD8G,EAAaG,IAAIjH,IAEnBjC,GAAiB,IAAIC,IAAIkJ,GAC1B,CAED,GAAI9N,GAAoBuD,WAAY,CAClC,MAAMwE,EACyC,QAA7C/H,GAAoBuD,WAAWG,WACc,SAA7C1D,GAAoBuD,WAAWG,UAC3B1D,GAAoBuD,WAAWG,UAC/B,KACAD,EAAMsE,EAAM/H,GAAoBuD,WAAWE,IAAM,KACvDD,GAAc,CAAEC,MAAKC,UAAWqE,GACjC,CAED,GAAI/H,GAAoB+N,QAAS,CAC/B5K,GAAgBnD,GAAoB+N,QAAQ/K,cAAgB,IAC5D,MAAM4K,EAAUlH,OAAOkH,QACrB5N,GAAoB+N,QAAQ9K,eAAiB,CAAE,GAC/C0H,OAAO,EAAE/D,KAAO8G,EAAaG,IAAIjH,IACnC,IAAK,MAAOA,EAAGoH,KAAMJ,EAASxK,GAAgBwD,EAAGoH,EAClD,CAED,GACEhO,GAAoBuE,aACpBvE,GAAoBuE,YAAYmB,OAChC,CACA,MAAMuI,EAAWjO,GAAoBuE,YAAYoG,OAAQ/D,GACvD8G,EAAaG,IAAIjH,IAGbsH,EAAY5Q,EACfgH,IAAKF,GAAMA,EAAEX,KACbkH,OAAQ/D,IAAOqH,EAASE,SAASvH,IACpCpC,GAAe,IAAIyJ,KAAaC,GACjC,CAGClO,GAAoB6J,aAAauE,YACjCpO,GAAoB6J,YAAYuE,WAAW1I,OAE3CyE,GAAanK,GAAoB6J,YAAYuE,YACpCpO,GAAoB6J,aAAaxE,WAC1C2E,GAAchK,GAAoB6J,YAAYxE,WAE5CrF,GAAoBvC,SACtB+E,GAAWxC,GAAoBvC,UAGhC,IAGHwD,EAAU,KACR,IAAKlB,GAAsB,OAC3B,MAAMsO,EAAQ7E,WAAW,KACvBzJ,GAAqB,CACnB4D,gBACAkB,oBACAH,cAAemH,MAAMC,KAAKpH,IAC1BnB,cACAsG,YAAa,CAAEuE,WAAYvE,GAAYuE,YACvCL,QAAS,CAAE/K,gBAAcC,kBACzBsB,eACA9G,cAED,KACH,MAAO,IAAMgM,aAAa4E,IACzB,CACDtO,GACA4D,GACAkB,GACAH,GACAnB,GACAsG,GACA7G,GACAC,GACAsB,GACA9G,KAOF,MAAM6Q,GAAoB5L,EAAQ,KAEhC,GAAuB,IAAnBpF,EAAQoI,OAAc,MAAO,GAGjC,MAAM6I,EAAa,IAAIC,IACrBjK,GAAYD,IAAI,CAACsC,EAAG6H,IAAM,CAAC7H,EAAG6H,KAS1BC,EALiBpR,EAAQqN,OAC5B5G,IAAsC,IAA9Bc,GAAiBd,EAAIN,MAK7BwE,KACC,CAACC,EAAGC,KAAOoG,EAAWI,IAAIzG,EAAEzE,MAAQ,IAAM8K,EAAWI,IAAIxG,EAAE1E,MAAQ,IAEpEa,IAAKP,IACJ,MAAM6K,EAAYjL,GAAaI,EAAIN,MAAQM,EAAIC,MACzCA,EAAQ6K,OAAOC,SAASD,OAAOD,IACjCC,OAAOD,GACP,IAEEG,GAA4B,IAAjBhL,EAAIgL,SACrB,MAAO,IAAKhL,EAAKC,QAAO+K,cAOtBC,EAAY7I,KAAKC,IACrB,EACAtF,IAAkB5C,EAAa+Q,EAAmB,IAE9CC,EAAYR,EAAKxK,OAAO,CAACiL,EAAK/K,IAAM+K,EAAM/K,EAAEJ,MAAO,GAEzD,GAAIgL,EAAYE,EAAW,CACzB,MAAME,EAAWV,EAAK/D,OAAQvG,GAAMA,EAAEiL,MACtC,GAAID,EAAS1J,OAAS,EAAG,CACvB,MAAM4J,EAAQN,EAAYE,EACpBK,EAAcH,EAASlL,OAAO,CAACC,EAAKC,KACxC,MAAMoL,GACO,IAAXpL,EAAEiL,KAAgB,EAAsB,iBAAXjL,EAAEiL,KAAoBjL,EAAEiL,KAAO,EAC9D,OAAOlL,EAAMgC,KAAKC,IAAI,EAAGoJ,IACxB,GAEH,GAAID,EAAc,EAEhB,IAAK,MAAMxL,KAAOqL,EAAU,CAC1B,MAMMK,EAAaH,IALJ,IAAbvL,EAAIsL,KACA,EACoB,iBAAbtL,EAAIsL,KACTtL,EAAIsL,KACJ,GAC6BE,EAC/BG,EAAO3L,EAAI4L,UAAY,EACvBC,EAAO7L,EAAI8L,UAAYhB,OAAOiB,kBACpC/L,EAAIC,MAAQmC,KAAKC,IAAIsJ,EAAMvJ,KAAKG,IAAIsJ,EAAM7L,EAAIC,MAAQyL,GACvD,CAEJ,CACF,CAGD,MAAMzI,EAAsB,GACtB+I,EAAwB,GAE9B,IAAK,MAAMhM,KAAO2K,EACZ7D,GAAuBgD,IAAI9J,EAAIN,KACjCuD,EAAOgJ,KAAKjM,GAEZgM,EAASC,KAAKjM,GAKlB,GAAI8F,GAAYuE,YAAcvE,GAAYuE,WAAW1I,OAAS,EAAG,CAC/D,MAAMuK,EAAa,IAAIzB,IAAI3E,GAAYuE,WAAW9J,IAAI,CAACsC,EAAG6H,IAAM,CAAC7H,EAAG6H,KACpEsB,EAAS9H,KAAK,CAACC,EAAGC,KACH8H,EAAWtB,IAAIzG,EAAEzE,MAAQoL,OAAOiB,oBAChCG,EAAWtB,IAAIxG,EAAE1E,MAAQoL,OAAOiB,mBAGhD,CAED,MAAO,IAAI9I,KAAW+I,IACrB,CACDzS,EACAqG,GACAkH,GACAhG,GACAN,GACAsF,GAAYuE,WACZtN,GACA5C,IAIIgS,GAAgCxN,EAAQ,IAE1C2J,GAAWC,gCACXgC,GAAkB5I,OAAS2G,GAAWE,kCAEvC,CACD+B,GAAkB5I,OAClB2G,GAAWC,+BACXD,GAAWE,oCAIP4D,GAAYzN,EAChB,IAAM4L,GAAkB3D,OAAQvG,GAAMyG,GAAuBgD,IAAIzJ,EAAEX,MACnE,CAAC6K,GAAmBzD,KAEhBuF,GAAc1N,EAClB,IAAM4L,GAAkB3D,OAAQvG,IAAOyG,GAAuBgD,IAAIzJ,EAAEX,MACpE,CAAC6K,GAAmBzD,KAEhBwF,GAAe3N,EAAQ,KAC3B,MAAM4N,EAASF,GAAY9L,IAAKF,GAAOA,EAAEJ,OAAoB,KACvDuM,EAAS,IAAI1E,MAAMyE,EAAO5K,OAAS,GAAG8K,KAAK,GACjD,IAAK,IAAI/B,EAAI,EAAGA,EAAI6B,EAAO5K,OAAQ+I,IACjC8B,EAAO9B,EAAI,GAAK8B,EAAO9B,GAAK6B,EAAO7B,GAErC,MAAO,CAAE6B,SAAQC,SAAQE,WADNF,EAAOD,EAAO5K,SAAW,IAE3C,CAAC0K,KAEEM,GAAiBhO,EAAQ,KAC7B,IAAKwN,GAA+B,OAAO5B,GAE3C,MAAMqC,EAAUzS,EAAa+Q,EAAmB,EAC1C2B,EAAcT,GAAUjM,OAC5B,CAACiL,EAAK/K,IAAM+K,GAAQ/K,EAAEJ,OAAoB,KAC1C,GAGI6M,EAAa1K,KAAKC,IAAI,EAAG6G,GAAc,KACvC6D,EAAc7D,GAAcM,GAAgB,IAG5CmB,EAAOiC,EAAUC,EACjBG,EAAY5K,KAAKC,IAAI,EAAGyK,EAAanC,GACrCsC,EAAa7K,KAAKC,IACtB,EACAD,KAAKG,IAAI+J,GAAaI,WAAYK,EAAcpC,IAuC5CuC,EAnCyB,CAACC,IAC9B,IAAKb,GAAaC,OAAO5K,OAAQ,OAAO,EACxC,IAAIyL,EAAK,EACPC,EAAKf,GAAaC,OAAO5K,OAAS,EAClC2L,EAAM,EACR,KAAOF,GAAMC,GAAI,CACf,MAAME,EAAOH,EAAKC,GAAO,EACXf,GAAaE,OAAOe,EAAM,IAC3BJ,GACXG,EAAMC,EACNF,EAAKE,EAAM,GAEXH,EAAKG,EAAM,CAEd,CACD,OAAOnL,KAAKC,IAAI,EAAGD,KAAKG,IAAI+K,EAAKhB,GAAaC,OAAO5K,OAAS,KAoB/C6L,CAAuBR,GAClCS,EAnBuB,CAACN,IAC5B,IAAKb,GAAaC,OAAO5K,OAAQ,OAAQ,EACzC,IAAIyL,EAAK,EACPC,EAAKf,GAAaC,OAAO5K,OAAS,EAClC2L,EAAMhB,GAAaC,OAAO5K,OAAS,EACrC,KAAOyL,GAAMC,GAAI,CACf,MAAME,EAAOH,EAAKC,GAAO,EACZf,GAAaE,OAAOe,IACrBJ,GACVG,EAAMC,EACNH,EAAKG,EAAM,GAEXF,EAAKE,EAAM,CAEd,CACD,OAAOnL,KAAKC,IAAI,EAAGD,KAAKG,IAAI+K,EAAKhB,GAAaC,OAAO5K,OAAS,KAIjD+L,CAAqBT,GAC9BU,EACJF,GAAUP,GAAYO,GAAU,EAC5BpB,GAAY3L,MAAMwM,EAAUO,EAAS,GACrC,GACN,MAAO,IAAIrB,MAAcuB,IACxB,CACDxB,GACA5B,GACArB,GACAM,GACArP,EACAiS,GACAC,GACAC,KAIIsB,GAAqBjP,EAAQ,KACjC,MAAMkP,EAAI,IAAIpD,IAEd,OADA4B,GAAYtM,QAAQ,CAACM,EAAGqK,IAAMmD,EAAEC,IAAIzN,EAAEX,IAAKgL,IACpCmD,GACN,CAACxB,MACE0B,cAAEA,GAAaC,eAAEA,IAAmBrP,EAAQ,KAChD,IAAKwN,GACH,MAAO,CAAE4B,cAAe,EAAGC,eAAgB,GAC7C,MAAMC,EAAkBtB,GAAe/F,OACpCvG,IAAOyG,GAAuBgD,IAAIzJ,EAAEX,MAEvC,IAAKuO,EAAgBtM,OACnB,MAAO,CAAEoM,cAAe,EAAGC,eAAgB,GAC7C,MAAME,EAAWN,GAAmBhD,IAAIqD,EAAgB,GAAGvO,KACrDyO,EAAUP,GAAmBhD,IACjCqD,EAAgBA,EAAgBtM,OAAS,GAAGjC,KAE9C,GAAgB,MAAZwO,GAA+B,MAAXC,EACtB,MAAO,CAAEJ,cAAe,EAAGC,eAAgB,GAG7C,MAAO,CAAED,cAFIzB,GAAaE,OAAO0B,GAEHF,eADhB1B,GAAaI,WAAaJ,GAAaE,OAAO2B,EAAU,KAErE,CACDhC,GACAQ,GACA7F,GACA8G,GACAtB,KAII8B,GAAqBzP,EACzB,IAAM4L,GAAkB8D,KAAMhO,GAAMA,EAAEiO,kBACtC,CAAC/D,KAGHrN,EAAU,KACR,MAAMqR,EAAc5F,GAAUvL,QAC9B,IAAKmR,EAAa,OAElB,IAAIC,GAAkB,EAEtB,MAAMC,EAAe,KACnB,GAAID,EAAiB,OACrBA,GAAkB,EAClB,MAAME,EAAW3L,GAAgB3F,QAC7BsR,GAAYA,EAASC,aAAeJ,EAAYI,aAClDD,EAASC,WAAaJ,EAAYI,YAEpCrF,GAAsBiF,EAAYI,YAClCpF,sBAAsB,KACpBiF,GAAkB,KAUtB,OANAD,EAAY5Q,iBAAiB,SAAU8Q,EAAc,CAAEG,SAAS,IAE5D7L,GAAgB3F,UAClB2F,GAAgB3F,QAAQuR,WAAaJ,EAAYI,YAG5C,KACLJ,EAAY1Q,oBAAoB,SAAU4Q,KAE3C,CAAC9F,GAAWyF,KAGf,MAAMS,GAAgBlQ,EAAQ,KAC5B,MAAMkP,EAAI,IAAIpD,IAEd,OADAF,GAAkBxK,QAAQ,CAACM,EAAGqK,IAAMmD,EAAEC,IAAIzN,EAAEX,IAAKgL,IAC1CmD,GACN,CAACtD,KAEEuE,GAAWnQ,EAAQ,KACvB,MAAMkP,EAAI,IAAIpD,IAEd,OADAF,GAAkBxK,QAASM,GAAMwN,EAAEC,IAAIzN,EAAEX,IAAKW,IACvCwN,GACN,CAACtD,KAGEwE,GAAWpQ,EACf,IACEqI,GAAcJ,OAAQoI,IAAYA,EAAEC,iBAAmBD,EAAEE,gBAC3D,CAAClI,KAEGmI,GAAexQ,EAAQ,KAC3B,MAAMkP,EAAI,IAAIpD,IAEd,OADAsE,GAAShP,QAAQ,CAACiP,EAAGtE,IAAMmD,EAAEC,IAAIkB,EAAEI,GAAI1E,IAChCmD,GACN,CAACkB,KAEEM,GAAU1Q,EAAQ,KACtB,MAAMkP,EAAI,IAAIpD,IAEd,OADAsE,GAAShP,QAASiP,GAAMnB,EAAEC,IAAIkB,EAAEI,GAAIJ,IAC7BnB,GACN,CAACkB,KAGEO,GAA4BxS,EAAsB,MAClDyS,GAA2BzS,EAAOvB,IAClCiU,GAAiB1S,EAAOR,IAC9BY,EAAU,KACRsS,GAAepS,QAAUd,IACxB,CAACA,KACJY,EAAU,KACRqS,GAAyBnS,QAAU7B,IAClC,CAACA,KAIJ,MAAM8O,GAAa1L,EACjB,IAAM4L,GAAkBhK,IAAKF,GAAMA,EAAEX,KACrC,CAAC6K,KAIGkF,GAAiB9Q,EACrB,IAAM,CAAC+Q,EAAwBpO,KAC7B,IAAKjG,EAAsB,OAAO,EAClC,IAAK2C,KAAoBE,GAAc,OAAO,EAC9C,MAAM8Q,EAAIG,GAAavE,IAAI8E,GACrBrP,EAAIwO,GAAcjE,IAAItJ,GAC5B,GAAS,MAAL0N,GAAkB,MAAL3O,EAAW,OAAO,EACnC,MAAMsP,EAASN,GAAQzE,IAAI8E,GACrBE,EAASd,GAASlE,IAAItJ,GAC5B,GAAIqO,GAAUC,IAAWtU,GAAcqU,EAAQC,GAAS,OAAO,EAC/D,MAAMC,EAAOzN,KAAKG,IAAIvE,GAAgB8R,SAAU5R,GAAa4R,UACvDC,EAAO3N,KAAKC,IAAIrE,GAAgB8R,SAAU5R,GAAa4R,UACvDE,EAAO5N,KAAKG,IAAIvE,GAAgBiS,SAAU/R,GAAa+R,UACvDC,EAAO9N,KAAKC,IAAIrE,GAAgBiS,SAAU/R,GAAa+R,UAC7D,OAAOjB,GAAKa,GAAQb,GAAKe,GAAQ1P,GAAK2P,GAAQ3P,GAAK6P,GAErD,CACE7U,EACA2C,GACAE,GACAiR,GACAN,GACAQ,GACAP,GACAxT,KAIE6U,GAAgBxR,EACpB,IAAM,CAAC+Q,EAAwBpO,IAC7BxD,IAAa4R,QAAUA,GAAS5R,IAAawD,YAAcA,EAC7D,CAACxD,KAIGsS,GAAqBlP,EACxB0E,IACC,MAAMyK,EAAS1H,GAAUvL,QACzB,IAAKiT,EAAQ,OACb,MACMC,EADsBlO,KAAKmO,IAAI3K,EAAE4K,SAAWpO,KAAKmO,IAAI3K,EAAE6K,QAC5B7K,EAAE4K,OAAS5K,EAAE6K,OACnC,IAAPH,IACF1K,EAAE8K,iBACFL,EAAOM,SAAS,CAAEC,KAAMN,EAAItH,IAAK,EAAG6H,SAAU,WAGlD,IAIIC,GAAsB5P,EAC1B,EAAGgB,MAAK6O,SAAQC,YACd,MAAMC,EAAS9B,GAAavE,IAAI1I,EAAIkN,IAC9B8B,EAASrC,GAAcjE,IAAImG,EAAOrR,KAGpCvE,GAA8B,MAAV8V,GAA4B,MAAVC,IAEnCC,SAASC,KAAKC,aAAa,qBAC9BtT,GAAe,CAAE2R,MAAOxN,EAAIkN,GAAI9N,UAAWyP,EAAOrR,MAC9CtE,GACFA,EAAkB,CAChB8G,MACA6O,SACA5O,MAAOD,EAAI6O,EAAOrR,KAClBoQ,SAAUmB,EACVhB,SAAUiB,MAOb7V,GACAC,GAAc4G,EAAK6O,IACH,IAAjBC,EAAMM,SACVlT,GAAehB,SAAU,EACzBiB,GAAgBjB,QAAU,CAAE+P,EAAG6D,EAAMO,QAASC,EAAGR,EAAMS,SACzC,MAAVR,GAA4B,MAAVC,IACtBnT,GAAe,CAAE2R,MAAOxN,EAAIkN,GAAI9N,UAAWyP,EAAOrR,MAClDzB,GAAmB,CAAE6R,SAAUmB,EAAQhB,SAAUiB,IACjD/S,GAAgB,CAAE2R,SAAUmB,EAAQhB,SAAUiB,IAE9C1S,GAAe,CACb2O,EAAG6D,EAAMO,QACTC,EAAGR,EAAMS,QACT1N,KAAM,EACN2N,KAAM,OAGV,CACEvW,EACAC,EACAC,EACAC,GACA6T,GACAN,KAIE8C,GAAuBzQ,EAC3B,EAAGgB,MAAK6O,SAAQC,YACd,IAAK3V,EAAsB,OAC3B,IAAK+C,GAAehB,UAAYY,GAAiB,OACjD,IAAK1C,GAAc4G,EAAK6O,GAAS,OACjC,MAAME,EAAS9B,GAAavE,IAAI1I,EAAIkN,IAC9B8B,EAASrC,GAAcjE,IAAImG,EAAOrR,KACxC,GAAc,MAAVuR,GAA4B,MAAVC,EAAgB,OACtC/S,GAAgB,CAAE2R,SAAUmB,EAAQhB,SAAUiB,IAE9C7S,GAAgBjB,QAAU,CAAE+P,EAAG6D,EAAMO,QAASC,EAAGR,EAAMS,SACvD,MAAM5B,EAAOzN,KAAKG,IAAIvE,GAAgB8R,SAAUmB,GAC1ClB,EAAO3N,KAAKC,IAAIrE,GAAgB8R,SAAUmB,GAC1CjB,EAAO5N,KAAKG,IAAIvE,GAAgBiS,SAAUiB,GAC1ChB,EAAO9N,KAAKC,IAAIrE,GAAgBiS,SAAUiB,GAChD1S,GAAe,CACb2O,EAAG6D,EAAMO,QACTC,EAAGR,EAAMS,QACT1N,KAAMgM,EAAOF,EAAO,EACpB6B,KAAMxB,EAAOF,EAAO,KAGxB,CACE3U,EACAC,GACA0C,GACAmR,GACAN,KAIJ3R,EAAU,KACR,MAAM0U,EAAO,KACXxT,GAAehB,SAAU,EACzBoB,GAAe,MACXF,GAAiBlB,UACnByU,qBAAqBvT,GAAiBlB,SACtCkB,GAAiBlB,QAAU,OAI/B,OADAM,OAAOC,iBAAiB,UAAWiU,GAC5B,IAAMlU,OAAOG,oBAAoB,UAAW+T,IAClD,IAGH1U,EAAU,KACR,MAAM4U,EAAUlM,IACTxH,GAAehB,UACpBiB,GAAgBjB,QAAU,CAAE+P,EAAGvH,EAAE2L,QAASC,EAAG5L,EAAE6L,WAGjD,OADA/T,OAAOC,iBAAiB,YAAamU,GAC9B,IAAMpU,OAAOG,oBAAoB,YAAaiU,IACpD,IAGH,MAAMC,GAAuB7Q,EAAY,KACvC,GAAgC,MAA5B5C,GAAiBlB,QAAiB,OACtC,MAAM4U,EAAO,KAEX,GADA1T,GAAiBlB,QAAU,MACtBgB,GAAehB,QAAS,OAC7B,MAAMD,EAAKwL,GAAUvL,QACf6U,EAAK5T,GAAgBjB,QAC3B,GAAID,GAAM8U,EAAI,CACZ,MAAMC,EAAO/U,EAAGgV,wBACVC,EAAS,GACTC,EAAW,GACjB,IAAI/B,EAAK,EACLgC,EAAK,EACLL,EAAG9E,EAAI+E,EAAKtB,KAAOwB,EAAQ9B,IAAO4B,EAAKtB,KAAOwB,EAASH,EAAG9E,GACrD8E,EAAG9E,EAAI+E,EAAKK,MAAQH,IAAQ9B,EAAK2B,EAAG9E,GAAK+E,EAAKK,MAAQH,IAC3DH,EAAGT,EAAIU,EAAKlJ,IAAMoJ,EAAQE,IAAOJ,EAAKlJ,IAAMoJ,EAASH,EAAGT,GACnDS,EAAGT,EAAIU,EAAKM,OAASJ,IAC5BE,EAAKL,EAAGT,GAAKU,EAAKM,OAASJ,IAE7B,MAAMK,EAASxI,GAAc7H,KAAKC,KAAKgQ,EAAUjQ,KAAKG,IAAI8P,EAAUpI,IACpEqG,EAAKmC,EAAMnC,GACXgC,EAAKG,EAAMH,GACA,IAAPhC,GAAmB,IAAPgC,GACdnV,EAAGwT,SAAS,CAAEC,KAAMN,EAAItH,IAAKsJ,EAAIzB,SAAU,QAE9C,CACDvS,GAAiBlB,QAAUmM,sBAAsByI,IAEnD1T,GAAiBlB,QAAUmM,sBAAsByI,IAChD,IAGH9U,EAAU,KACJkB,GAAehB,SACjB2U,MAED,CAACA,GAAsB/T,KAG1Bd,EAAU,KACR,MAAMwV,EAAS9M,IACb,GAAc,WAAVA,EAAElG,IAKJ,OAJAzB,GAAmB,MACnBE,GAAgB,MAChBJ,GAAe,WACfS,GAAe,MAGjB,MAAMmU,EAAU/M,EAAEgN,SACZC,EAAQ,CACZ,UACA,YACA,YACA,cACAzI,SAASxE,EAAElG,KACb,IAAKiT,IAAYE,IAAUxX,EAAsB,OACjDuK,EAAE8K,iBAEF,IAAI/F,EAAOzM,GACX,IAAKyM,EAAM,CACT,IAAI7M,GAUF,OAVe,CACf,MAAMkR,EAAIG,GAAavE,IAAI9M,GAAY4R,OACjCrP,EAAIwO,GAAcjE,IAAI9M,GAAYwD,WACxC,GAAS,MAAL0N,GAAkB,MAAL3O,EAIf,OAHApC,GAAmB,CAAE6R,SAAUd,EAAGiB,SAAU5P,IAC5CsK,EAAO,CAAEmF,SAAUd,EAAGiB,SAAU5P,EAInC,CAGF,CACD,MAAMyS,EAAS/D,GAASpN,OAAS,EAC3BoR,EAASxI,GAAkB5I,OAAS,EAC1C,IAAIqR,EAAKrI,EAAKmF,SACVmD,EAAKtI,EAAKsF,SACA,YAAVrK,EAAElG,MAAmBsT,EAAK5Q,KAAKC,IAAI,EAAG2Q,EAAK,IACjC,cAAVpN,EAAElG,MAAqBsT,EAAK5Q,KAAKG,IAAIuQ,EAAQE,EAAK,IACxC,cAAVpN,EAAElG,MAAqBuT,EAAK7Q,KAAKC,IAAI,EAAG4Q,EAAK,IACnC,eAAVrN,EAAElG,MAAsBuT,EAAK7Q,KAAKG,IAAIwQ,EAAQE,EAAK,IACvD9U,GAAgB,CAAE2R,SAAUkD,EAAI/C,SAAUgD,IAE1C,MAAM9V,EAAKwL,GAAUvL,QACrB,GAAID,EAAI,CACN,MAAM+U,EAAO/U,EAAGgV,wBAChB3T,GAAe,CACb2O,EAAG+E,EAAKK,MAAQ,GAChBf,EAAGU,EAAKM,OAAS,GACjBzO,KAAM3B,KAAKmO,KAAKvS,IAAiB8R,UAAYkD,GAAMA,GAAM,EACzDtB,KAAMtP,KAAKmO,KAAKvS,IAAiBiS,UAAYgD,GAAMA,GAAM,GAE5D,GAGH,OADAvV,OAAOC,iBAAiB,UAAW+U,GAC5B,IAAMhV,OAAOG,oBAAoB,UAAW6U,IAClD,CACDrX,EACAyC,GACAE,GACAE,GACAiR,GACAN,GACAE,GAASpN,OACT4I,GAAkB5I,SAIpBzE,EAAU,KACR,IAAK/B,EAAkB,OACvB,MAAMkV,EAAS1H,GAAUvL,QACnB8V,EAAWpQ,GAAgB1F,QA4B3B+V,EAAWvN,IACf,IAAK9H,GAAa,OAElB,GAAIqT,SAASC,KAAKC,aAAa,mBAAoB,OACnD,MAAM3R,IAAEA,GAAQkG,EAChB,GACU,cAARlG,GACQ,eAARA,GACQ,YAARA,GACQ,cAARA,EAEA,OACFkG,EAAE8K,iBACF,MAAM0C,EAAgBjE,GAAavE,IAAI9M,GAAY4R,OAC7C2D,EAAgBxE,GAAcjE,IAAI9M,GAAYwD,WACpD,GAAqB,MAAjB8R,GAA0C,MAAjBC,EAAuB,OACpD,IAAIC,EAAUF,EACVG,EAAUF,EACA,cAAVzN,EAAElG,MAAqB6T,EAAUnR,KAAKC,IAAI,EAAGgR,EAAgB,IACnD,eAAVzN,EAAElG,MACJ6T,EAAUnR,KAAKG,IAAIgI,GAAkB5I,OAAS,EAAG0R,EAAgB,IACrD,YAAVzN,EAAElG,MAAmB4T,EAAUlR,KAAKC,IAAI,EAAG+Q,EAAgB,IACjD,cAAVxN,EAAElG,MACJ4T,EAAUlR,KAAKG,IAAIwM,GAASpN,OAAS,EAAGyR,EAAgB,IAE1D,MAAMlR,EAAM6M,GAASuE,GACfvC,EAASxG,GAAkBgJ,GAC5BrR,GAAQ6O,IAEbhT,GAAe,CAAE2R,MAAOxN,EAAIkN,GAAI9N,UAAWyP,EAAOrR,MAC9CtE,GACFA,EAAkB,CAChB8G,MACA6O,SACA5O,MAAOD,EAAI6O,EAAOrR,KAClBoQ,SAAUwD,EACVrD,SAAUsD,IA9DM,EAACzD,EAAkBG,KACvC,IAAKI,EAAQ,OAEb,MAAMmD,EAAYnD,EAAOoD,aACnBC,EAAY5D,EAAW/Q,GACvB4U,EAAeD,EAAY3U,GAC7B2U,EAAYrD,EAAOuD,UAAWvD,EAAOuD,UAAYF,EAC5CC,EAAetD,EAAOuD,UAAYJ,IACzCnD,EAAOuD,UAAYD,EAAeH,GAGpC,MAAMK,EAAYtJ,GAAkB0F,IAAWhQ,OAAoB,IACnE,IAAI2Q,EAAOzW,EAAa+Q,EAAmB,EAC3C,IAAK,IAAIR,EAAI,EAAGA,EAAIuF,EAAUvF,IAC5BkG,GAASrG,GAAkBG,IAAIzK,OAAoB,IAErD,MAAMsS,EAAQ3B,EAAOiD,EACf5I,EAAYoF,EAAO/S,YACrBsT,EAAOP,EAAO1B,WAChB0B,EAAO1B,WAAaiC,EACX2B,EAAQlC,EAAO1B,WAAa1D,IACrCoF,EAAO1B,WAAa4D,EAAQtH,GAE1BiI,IAAUA,EAASvE,WAAa0B,EAAO1B,aA0C3CmF,CAAcR,EAASC,KAGzB,OADA7V,OAAOC,iBAAiB,UAAWwV,GAC5B,IAAMzV,OAAOG,oBAAoB,UAAWsV,IAClD,CACDhY,EACA2C,GACAiR,GACAxE,GACAxL,GACA5E,EACAgV,GACAN,GACAzT,IAIF8B,EAAU,KACR,IAAK7B,IAAyB2C,KAAoBE,GAAc,CAC9D,MAAM6V,EAAU,CAAEC,OAAQ,KAAaC,MAAO,IACxCC,EAAM,OAKZ,YAJIA,IAAQ5E,GAA0BlS,UACpCkS,GAA0BlS,QAAU8W,EACpC3E,GAAyBnS,UAAU2W,IAGtC,CAED,MAAMI,EAAW/R,KAAKG,IAAIvE,GAAgB8R,SAAU5R,GAAa4R,UAC3DsE,EAAShS,KAAKC,IAAIrE,GAAgB8R,SAAU5R,GAAa4R,UACzDuE,EAAWjS,KAAKG,IAAIvE,GAAgBiS,SAAU/R,GAAa+R,UAC3DqE,EAASlS,KAAKC,IAAIrE,GAAgBiS,SAAU/R,GAAa+R,UAEzDgE,EAMD,GACL,IAAK,IAAIjF,EAAImF,EAAUnF,GAAKoF,EAAQpF,IAAK,CACvC,MAAM9M,EAAM6M,GAASC,GACrB,GAAK9M,EACL,IAAK,IAAI7B,EAAIgU,EAAUhU,GAAKiU,EAAQjU,IAAK,CACvC,MAAM0Q,EAASxG,GAAkBlK,GAC5B0Q,IACAzV,GAAc4G,EAAK6O,IACxBkD,EAAMhI,KAAK,CACT/J,MACA6O,SACA5O,MAAOD,EAAI6O,EAAOrR,KAClBoQ,SAAUd,EACViB,SAAU5P,IAEb,CACF,CAED,MAAM0T,EAAU,CACdC,OAAQ,CAAEG,WAAUC,SAAQC,WAAUC,UACtCL,SAEIC,EAAM,GAAGC,KAAYC,KAAUC,KAAYC,SAAcL,EAAMtS,SACjEuS,IAAQ5E,GAA0BlS,UACtCkS,GAA0BlS,QAAU8W,EACpC3E,GAAyBnS,UAAU2W,KAClC,CACD1Y,EACA2C,GACAE,GACA6Q,GACA1E,GACA/O,KAIF4B,EAAU,KAER,IAAK7B,IAAyBF,EAAkB,OAChD,MAAMoZ,EAAYC,MAAO5O,IAEvB,KADaA,EAAE6O,SAAW7O,EAAE8O,UACS,MAAxB9O,EAAElG,IAAIiV,cAAuB,OAC1C,MAAMC,IAAmBhP,EAAEgN,SAErBiC,KAAa7W,KAAmBE,IAChC4W,KAAc3Z,IAAoB2C,IACxC,IAAK+W,IAAYC,EAAU,OAG3B,IAAKD,GAAWC,EAAU,CACxB,MAAMC,EAAMrX,OAAOsX,eACnB,GAAID,GAAOA,EAAIE,WAAWtT,OAAS,EAAG,OACtC,MAAMuT,EAAS/D,SAASgE,cACxB,GACED,IACoB,UAAnBA,EAAOE,SACa,aAAnBF,EAAOE,SACPF,EAAOG,mBAET,MAEH,CAGDzP,EAAE8K,iBAEF,MAmEMuD,EAnEe,MAOnB,MAAMA,EAMD,GACL,GAAIY,GAAW7W,IAAmBE,GAAc,CAC9C,MAAMiW,EAAW/R,KAAKG,IACpBvE,GAAgB8R,SAChB5R,GAAa4R,UAETsE,EAAShS,KAAKC,IAClBrE,GAAgB8R,SAChB5R,GAAa4R,UAETuE,EAAWjS,KAAKG,IACpBvE,GAAgBiS,SAChB/R,GAAa+R,UAETqE,EAASlS,KAAKC,IAClBrE,GAAgBiS,SAChB/R,GAAa+R,UAEf,IAAK,IAAIjB,EAAImF,EAAUnF,GAAKoF,EAAQpF,IAAK,CACvC,MAAM9M,EAAM6M,GAASC,GACrB,GAAK9M,EACL,IAAK,IAAI7B,EAAIgU,EAAUhU,GAAKiU,EAAQjU,IAAK,CACvC,MAAM0Q,EAASxG,GAAkBlK,GAC5B0Q,IACAzV,GAAc4G,EAAK6O,IACxBkD,EAAMhI,KAAK,CACT/J,MACA6O,SACA5O,MAAOD,EAAI6O,EAAOrR,KAClBoQ,SAAUd,EACViB,SAAU5P,IAEb,CACF,CACF,MAAM,GAAIyU,GAAYhX,GAAa,CAClC,MAAMmT,EAAS9B,GAAavE,IAAI9M,GAAY4R,OACtCwB,EAASrC,GAAcjE,IAAI9M,GAAYwD,WAC7C,GAAc,MAAV2P,GAA4B,MAAVC,EAAgB,CACpC,MAAMhP,EAAM6M,GAASkC,GACfF,EAASxG,GAAkB2G,GAC7BhP,GAAO6O,GACTkD,EAAMhI,KAAK,CACT/J,MACA6O,SACA5O,MAAOD,EAAI6O,EAAOrR,KAClBoQ,SAAUmB,EACVhB,SAAUiB,GAGf,CACF,CACD,OAAO+C,GAGKqB,GACd,IAAKrB,EAAMtS,OAAQ,OAEnB,MAAM4T,EAAcC,IAClB,MAAMC,EAAW,MAAPD,EAAc,GAAK9T,OAAO8T,GACpC,OACEC,EAAErL,SAAS,MACXqL,EAAErL,SAAS,OACXqL,EAAErL,SAAS,OACXqL,EAAErL,SAAS,MAEJ,IAAMqL,EAAEC,QAAQ,KAAM,MAAQ,IAEhCD,GAIT,IAAIhU,EAAO,GACX,GAAIoT,GAAW7W,IAAmBE,GAAc,CAC9C,MAAMiW,EAAW/R,KAAKG,IACpBvE,GAAgB8R,SAChB5R,GAAa4R,UAETsE,EAAShS,KAAKC,IAClBrE,GAAgB8R,SAChB5R,GAAa4R,UAETuE,EAAWjS,KAAKG,IACpBvE,GAAgBiS,SAChB/R,GAAa+R,UAETqE,EAASlS,KAAKC,IAClBrE,GAAgBiS,SAChB/R,GAAa+R,UAEf,GAAI2E,EAAgB,CAClB,MAAMe,EAAoB,GAC1B,IAAK,IAAItV,EAAIgU,EAAUhU,GAAKiU,EAAQjU,IAAK,CACvC,MAAM0Q,EAASxG,GAAkBlK,GAC5B0Q,GACL4E,EAAQ1J,KAAKsJ,EAAYxE,EAAehP,QAAUgP,EAAOrR,KAC1D,CACD+B,GAAQkU,EAAQC,KAAK,MAAQ,IAC9B,CACD,IAAK,IAAI5G,EAAImF,EAAUnF,GAAKoF,EAAQpF,IAAK,CACvC,MAAM9M,EAAM6M,GAASC,GACrB,IAAK9M,EAAK,SACV,MAAM2T,EAAiB,GACvB,IAAK,IAAIxV,EAAIgU,EAAUhU,GAAKiU,EAAQjU,IAAK,CACvC,MAAM0Q,EAASxG,GAAkBlK,GACjC,IAAK0Q,EAAQ,SACb,IAAKzV,GAAc4G,EAAK6O,GAAS,CAC/B8E,EAAK5J,KAAK,IACV,QACD,CACD,MAAM6J,EAAM5T,EAAI6O,EAAOrR,KACvBmW,EAAK5J,KAAKsJ,EAAWO,GACtB,CACDrU,GAAQoU,EAAKD,KAAK,MACd5G,EAAIoF,IAAQ3S,GAAQ,KACzB,CACF,KAAM,CAGLA,EAAO8T,EADMtB,EAAM,GACI9R,MACxB,CAED,UACQ4T,UAAUC,UAAUC,UAAUxU,EACrC,CAAC,MACAyU,QAAQC,KAAK,+BACd,CAGD,GAAsC,mBAA3B3G,GAAepS,QAAwB,CAChD,MAAM4W,EACJa,GAAW7W,IAAmBE,GAC1B,CACEiW,SAAU/R,KAAKG,IACbvE,GAAgB8R,SAChB5R,GAAa4R,UAEfsE,OAAQhS,KAAKC,IACXrE,GAAgB8R,SAChB5R,GAAa4R,UAEfuE,SAAUjS,KAAKG,IACbvE,GAAgBiS,SAChB/R,GAAa+R,UAEfqE,OAAQlS,KAAKC,IACXrE,GAAgBiS,SAChB/R,GAAa+R,WAGjB,KACN,IACET,GAAepS,QAAQ,CACrBqE,OACAuS,SACAC,QACAmC,gBAAiBpC,EACjBqC,eAAgBrC,GAEnB,CAAC,MAED,CACF,GAGH,OADAtW,OAAOC,iBAAiB,UAAW4W,GAC5B,IAAM7W,OAAOG,oBAAoB,UAAW0W,IAClD,CACDlZ,EACAF,EACA6C,GACAE,GACAJ,GACAiR,GACAxE,GACAjP,GACA6T,GACAN,GACAjS,KAGF,MAAM8P,GAAanC,GAAkBpK,OACnC,CAACiL,EAAKpL,IAAQoL,GAAOpL,EAAIC,OAAS,KAClC,GAkBF,OAfA/C,EAAU,KACY,IAAhB6H,KAGEvJ,IAAYC,SAA+B,WAApBD,GAAWE,KACpCkJ,GAAiB,GAEjBE,OAMH,CAAC7F,GAAcC,KAGhBoX,EAAA,MAAA,CACE1Z,IAAKC,GACL0Z,UAAU,wHAAuH,uBAAA,EAAAC,SAAA,CAIhInc,GAAeE,GACdkc,EAACC,EACC,CAAAzX,aAAcA,GACd0X,mBAAoBxX,GAAawC,OACjCiV,gBAAiBnS,GAAiBoS,UAClCC,eAAgBtb,IAAYE,KAC5Bqb,kBAAmB9P,GAAa+P,KAChC1c,WAAYA,EACZf,QAASA,EACT2F,cAAeA,GACf4B,iBAAkBA,GAClBmW,eAAgBxU,GAChBhG,cAAe,CACbhB,SAAUiB,IAAmBjB,UAAW,IAASnB,EACjD4c,QAASxa,IAAmBwa,SAAW,CAAC,QACxCzP,SAAU/K,IAAmB+K,UAAY,YACzC0P,SAAUzP,IAEZ0P,kBAAmBlb,GACnBmb,mBAAoBlb,GACpBmb,qBAAsB3R,GACtB4R,eAAgBrP,GAChBsP,eAAgBnY,GAChBoY,yBAA0BpU,GAC1B7C,YAAaA,GACbkX,oBAAqBjX,GACrBE,cAAeA,GACfgX,iBAAmBrW,IACjB,MAAM+O,EAAS1H,GAAUvL,QACnB8V,EAAWpQ,GAAgB1F,QACjC,IAAKiT,EAAQ,OAEb,MAAMuH,EAAM/I,GAAcjE,IAAItJ,GAC9B,GAAW,MAAPsW,EAAa,OAEjB,MAAM/D,EAAYtJ,GAAkBqN,IAAM3X,OAAoB,IAC9D,IAAI2Q,EAAOzW,EAAa+Q,EAAmB,EAC3C,IAAK,IAAIR,EAAI,EAAGA,EAAIkN,EAAKlN,IACvBkG,GAASrG,GAAkBG,IAAIzK,OAAoB,IAErD,MAAMsS,EAAQ3B,EAAOiD,EACf5I,EAAYoF,EAAO/S,YAEzB,GAAIsT,EAAOP,EAAO1B,WAChB0B,EAAOwH,SAAS,CAAEjH,OAAMC,SAAU,gBAC7B,GAAI0B,EAAQlC,EAAO1B,WAAa1D,EACrCoF,EAAOwH,SAAS,CAAEjH,KAAM2B,EAAQtH,EAAW4F,SAAU,eAChD,CAEL,MAAMiH,EAAS1V,KAAKC,IAClB,EACAuO,EAAOxO,KAAKC,IAAI,GAAI4I,EAAY4I,GAAY,IAE9CxD,EAAOwH,SAAS,CAAEjH,KAAMkH,EAAQjH,SAAU,UAC3C,CACGqC,IAAUA,EAASvE,WAAa0B,EAAO1B,aAE7CjV,QAASA,GACTK,gBAAiBoO,GACjBnO,mBAAoBA,IAKxBsc,EAAA,MAAA,CAAKC,UAAU,mDAEZ/b,IACyB,YAAvB4B,IACyB,SAAvBA,IACC0J,GAAYuE,WAAW1I,OAAS,IAClC8U,EAACsB,EACC,CAAAxe,QAASA,EACTye,YAAalS,GAAYuE,WACzB4N,SAAU9R,GACV+R,UAAW9R,GACX+R,gBAAiBjS,GACjBkS,cAAetS,GAAYuS,SAASrB,KAAO,EAC3CsB,kBAAmB,KACbxS,GAAYuS,SAASrB,KAAO,EAC9BxQ,KAEAD,QAQV+P,EAAA,MAAA,CACE1Z,IAAK+L,GACL4N,UAAW,8DACTlb,EAAuB,cAAgB,IAEzCkd,SAAUzP,GAGV0N,SAAA,CAAAC,EAAA,MAAA,CAAKF,UAAU,mBACbC,SAAAC,EAAA,MAAA,CACE+B,MAAO,CACL5M,SAAU,GACRc,IAAcvS,EAAa+Q,EAAmB,QAIjDsL,SAAA,MACC,MAAMiC,EAAerM,GACfsM,EAAiB/L,GAAe/F,OACnCvG,IAAOyG,GAAuBgD,IAAIzJ,EAAEX,MAGvC,OACE+W,EAACkC,GACChY,cAAe8X,EACfG,gBAAiBF,EACjBG,UAAW9K,GACX+K,WAAY9K,GACZnU,aAAcmF,GACdQ,WAAYA,GACZN,cAAeA,GACf/E,WAAYA,EACZ8M,aAAcA,GACd4P,UAAW7P,GAAcrF,OACzBrI,KAAMA,EACNyC,OAAQkF,GACRuW,eAAgBnY,GAChB0Z,YAAa,IACX5R,GACEF,GAAa+P,OAAShQ,GAAcrF,QAGxCqX,eAAgB3X,GAChB4X,aAAcnS,GACdoS,YAAalW,GACbxI,UAAWA,EACX2e,gBAAiBrT,GAAYuE,WAAW,IAAM,KAC9C+O,iBAAkBtT,GAAYuE,WAC9BgP,cAAe,CAAC3Z,EAAKgD,IACnBA,EAAOwD,GAAYxG,GAAOyG,GAAezG,GAE3C4Z,UAAWrT,GACXsT,iBAAkB3X,GAClB4X,qBAAsBhX,GACtByU,eAAgBxU,GAChBjC,YAAaA,GACbkX,oBAAqBjX,GACrBqW,eACEtb,IAAYC,QAAUD,GAAWE,KAAO,MAI/C,EA9CA,OAkDL+a,EAAA,MAAA,CACE+B,MAAO,CACL5M,SAAU,GAAGc,IAAcvS,EAAa+Q,EAAmB,OAC3DzR,OAAQ4O,GACDrB,GAAcrF,OAAS5C,GAA1B,KACA,QAKLyX,SAAC3a,IAAsC,IAAzBmL,GAAcrF,OAG3B8U,EAAA,MAAA,CACE+B,MAAO,CACLiB,UAAW,cACTpR,GAAuBO,GAAiB8Q,QAAU,OAEpDC,WAAY,YACZC,QAAS,WAGVpD,SAAA,MACC,MAAMqD,EAA2B,GACjC,IAAIC,EAAe,GACnB,MAAMC,EAAQ,KACZ,IAAKD,EAAMnY,OAAQ,OACnB,MAAMjC,EAAM,SAASoa,EAAM,IAAI1K,MAAM0K,EAAMnY,UAAUiH,GAAiB8Q,UACtEG,EAAM5N,KACJwK,SAAe+B,MAAO,CAAEwB,QAASne,GAAY,GAAM,GAAG2a,SACpDC,EAACwD,GACC3gB,KAAMwgB,EACNvgB,QAASoT,GACT1F,aAAcA,GACdxM,aAAa,EACbmO,iBAAkB,CAChBsR,WAAY,EACZC,SAAUL,EAAMnY,OAAS,EACzB+X,QAAS,GAEXhf,UAAWqE,GACX5E,WAAYA,EACZC,gBAAiBA,EACjBH,YAAaiN,GACbvG,cAAemG,GACf+R,UAAW9K,GACX+K,WAAY9K,GACZnT,SAAUA,EACVoE,aAAcA,GACdnE,cAAeA,EACfC,iBAAkBA,EAClBC,WAAYA,EACZC,YAAaA,EACbC,kBAAmBA,EACnBiV,cAAeA,GACfV,eAAgBA,GAChB2K,gBAAiBtJ,GACjBuJ,iBAAkB1I,GAClBnY,SAAUA,KA7BJkG,IAiCZoa,EAAQ,IAGV,IAAK,MAAM5X,KAAO2G,GAChB,GAAK3G,EAAY+M,eACf8K,IACAF,EAAM5N,KACJwK,EAAC6D,EAAW,CAEVpY,IAAKA,EACLqY,WAAYzU,GAAYuS,SAASvO,IAC/B5H,EAAIsY,WAAa,IAEnBC,SAAUpU,GACVmD,cAAezM,GACf5C,WAAYA,EACZO,UAAWqE,GACX2b,eAAiBC,GACfphB,EAAQsI,KAAMxB,GAAMA,EAAEX,MAAQib,IAAS5Y,QACvC4Y,EAEFC,cAAe,CAACD,EAAQ7E,KACtB,MAAM9V,EAAMzG,EAAQsI,KAAMxB,GAAMA,EAAEX,MAAQib,GAC1C,GAAI3a,GAAK6a,UACP,IACE,OAAO7a,EAAI6a,UAAU/E,EACtB,CAAC,MAAOgF,GACP,OAAOpZ,OAAOoU,GAAO,GACtB,CAEH,OAAOpU,OAAOoU,GAAO,KAEvBnZ,mBAAoBA,IAxBfuF,EAAIkN,UA2BR,GAAKlN,EAAYgN,eAGtB,GAFA6K,IAE2B,UAAvB1d,GAAgC,CAElC,MAAM0e,EAAQxQ,GACX3D,OACEvG,KACGA,EAAE2a,WACH9Y,EAAY+Y,gBACqBC,IAAjChZ,EAAY+Y,UAAU5a,EAAEX,MAE5Ba,IAAKF,IACJ,MAAM8a,EAAUjZ,EAAY+Y,UAAU5a,EAAEX,KAClC0b,EAAM/a,EAAEgb,oBAAsBhb,EAAEwa,UAChCS,EAAUF,EACZ,MACE,IACE,OAAOA,EAAID,EACZ,CAAC,MACA,OAAOzZ,OAAOyZ,EACf,CACF,EAND,GAOAzZ,OAAOyZ,GACX,OACE7E,UAEEC,UAEE,6GAEFgF,MAAO,GAAGlb,EAAE0B,SAEXyU,SAAA,CAAAnW,EAAE0B,OAAM,KAAIuZ,IAPRjb,EAAEX,OAYfma,EAAM5N,KACJwK,EAAA,MAAA,CAEEF,UAAU,oBACViC,MAAO,CACL5M,SAAU,GACRc,IAAcvS,EAAa+Q,EAAmB,OAEhDzR,OAAQ,GAAGsF,QACZyX,SAEDF,SACEC,UAEE,gIAEFiF,KAAK,cAELhF,SAAA,CAAAC,EAAA,MAAA,CAAKF,UAAU,6DACZwE,EAAMpZ,OACL,EAEA8U,EAAA,OAAA,CAAMF,UAAU,qDAKpBE,EAAM,OAAA,CAAAF,UAAU,uEAAsEC,SAAA,sBAzBnFtU,EAAIkN,IA+Bd,MAECyK,EAAM5N,KACJwK,EAAA,MAAA,CAEEF,UAAU,iDACViC,MAAO,CACL5M,SAAU,GACRc,IAAcvS,EAAa+Q,EAAmB,OAEhDzR,OAAQ,GAAGsF,QACZyX,SAEDF,SACEC,UAEE,8FAEFiF,KAAK,cAEJhF,SAAA,CAAArc,GACCsc,EACE,MAAA,CAAA+B,MAAO,CAAEvY,MAAOiL,GAChBqL,UAAU,aAGbhM,GAAkBhK,IAAKP,IACtB,MAAMC,EAASD,EAAIC,OAAoB,IACjCkb,EAAUjZ,EAAY+Y,UACvB/Y,EAAY+Y,UAAUjb,EAAIN,UAC3Bwb,EACEE,EACJpb,EAAIqb,oBAAsBrb,EAAI6a,UAC1B/E,OACOoF,IAAXC,EACI,GACAC,EACE,MACE,IACE,OAAOA,EAAID,EACZ,CAAC,MACA,OAAOA,CACR,CACF,EAND,GAOAA,EAGR,IAAIG,EACJ,GAAmB,iBAARxF,EACTwF,EAAUxQ,OAAO2Q,UAAU3F,GACvBpU,OAAOoU,GACPA,EAAI4F,QAAQ,QACX,GACJC,MAAM7F,IACC,KAARA,GACQ,OAARA,EAQAwF,EAAU5Z,OAAOoU,GAAO,QAPxB,CAEA,MAAM8F,EAAM9Q,OAAOgL,GACnBwF,EAAUxQ,OAAO2Q,UAAUG,GACvBla,OAAOka,GACPA,EAAIF,QAAQ,EACjB,CAGD,OACEjF,EAIE,MAAA,CAAAF,UAAU,8FACViC,MAAO,CACLvY,QACA2L,SAAU3L,EACV6L,SAAU7L,GAEZsb,MACED,EACI,GAAGtb,EAAI+B,WAAWuZ,SAClBJ,EAGN1E,SAAAC,EAAA,OAAA,CAAMF,UAAU,kBACbC,SAAA8E,KAhBE,YAAapZ,EAAYsY,aAC5Bxa,EAAIN,aAhETwC,EAAIkN,UAyFf0K,EAAM7N,KAAK/J,GAIf,OADA6X,IACOF,CACR,EApPA,KAXHpD,EAAAoF,EAAA,CAAA,QAqQL3W,IACCuR,EAAA,MAAA,CACEF,UAAW,qFACTnR,GACI,4CACA,eAENoT,MAAO,CAAExP,IAAK1D,IAEdkR,SAAAC,EAAA,MAAA,CAAKF,UAAU,qEAIjB1a,IAAsC,IAAzBmL,GAAcrF,QAC3B8U,EAAA,MAAA,CAAKF,UAAU,4EAA2EC,SACxFC,EAAK,MAAA,CAAAF,UAAU,sBACbC,SAAAC,EAACqF,EAAa,CACZC,aACI9c,IAAgB0D,OAAOC,KAAK1D,IAAeyC,OAAS,EAExDqa,QAAS1iB,EAAKqI,OAAS,WAQhC4I,GAAkB8D,KAAMrO,GAAQA,EAAIsO,mBACnCmI,EACE,MAAA,CAAA7Z,IAAKmG,GACLwT,UAAU,8EACViC,MAAO,CAAEyD,eAAgB,OAAQC,gBAAiB,QAClDC,QAAS/L,GAAkBoG,SAE3BC,EACE,MAAA,CAAAF,UAAU,4EACViC,MAAO,CACL5M,SAAU,GAAGc,IAAcvS,EAAa+Q,EAAmB,OAC3DzR,OAAQ,GAAGsF,QACZyX,SAEDC,EAAC2F,EACC,CAAA7iB,QAASgR,GACTjR,KAAMyV,GACN5U,WAAYA,EACZO,UAAWqE,GACX4B,cAAemG,SAQvB2P,EAAC4F,EACC,CAAA5X,iBAAkBA,GAClBM,YAAaN,GAAiBM,YAC9BJ,gBAAiBA,GACjBoS,kBAAmB9P,GAAa+P,KAChCJ,gBAAiBtd,EAAKqI,OACtBgV,mBACuB,WAArBnb,IAAYE,UACRwf,EACA/b,GAAawC,OAEnBmV,eAAgBtb,GAAWE,KAC3B4gB,aAAc1X,GACd2X,iBAAkB1X,KAInBtG,IACCkY,EACE,MAAA,CAAAF,UAAU,6CACViC,MAAO,CAAExP,IAAKzK,GAAYiT,EAAI,GAAIZ,KAAMrS,GAAY4O,EAAI,IAExDqJ,SAAAF,EAAA,MAAA,CAAKC,UAAU,wEAAuEC,SAAA,CACnFjY,GAAYwF,KAAS,MAAAxF,GAAYmT,YAM9C"}
@@ -1,18 +0,0 @@
1
- interface ColumnFilterSelectorProps {
2
- columns: Array<{
3
- key: string;
4
- header: string;
5
- filterable?: {
6
- type?: string;
7
- options?: Array<{
8
- label: string;
9
- value: string | number;
10
- }>;
11
- placeholder?: string;
12
- };
13
- }>;
14
- columnFilters: Record<string, any>;
15
- onColumnFilter?: (columnKey: string, filter: any) => void;
16
- }
17
- export declare const ColumnFilterSelector: ({ columns, columnFilters, onColumnFilter, }: ColumnFilterSelectorProps) => import("react/jsx-runtime").JSX.Element;
18
- export {};
@@ -1,2 +0,0 @@
1
- import{jsxs as e,jsx as t,Fragment as l}from"react/jsx-runtime";import{useState as a,useEffect as r}from"react";import{X as n,Search as s}from"lucide-react";import{CustomSelect as o}from"./CustomSelect.js";import{DatePicker as i}from"./ui/DatePicker.js";import{format as c,parse as u}from"date-fns";const d=({columns:d,columnFilters:m,onColumnFilter:y})=>{const b=d.filter(e=>e.filterable),[h,p]=a(b.length>0?b[0].key:""),[f,v]=a(""),g=b.map(e=>({label:e.header,value:e.key})),N=b.find(e=>e.key===h);r(()=>{v("")},[h]);return e("div",{className:"space-y-4",children:[e("div",{children:[t("label",{className:"text-sm font-medium text-gray-700 mb-2 block",children:"Select Column to Filter"}),t(o,{options:g,value:h,placeholder:"Choose a column...",onChange:e=>{p(e)},searchable:!0})]}),h&&(()=>{if(!N)return null;const a=N.filterable?.type||"text",r=m[h];if("multiselect"===a&&N.filterable?.options){const l=f.toLowerCase(),a=l?N.filterable.options.filter(e=>e.label.toLowerCase().includes(l)||String(e.value).toLowerCase().includes(l)):N.filterable.options;return e("div",{className:"mt-3",children:[e("label",{className:"text-sm font-medium text-gray-700 mb-2 block",children:["Filter by ",N.header]}),e("div",{className:"relative mb-2",children:[t(s,{className:"w-4 h-4 text-gray-400 absolute left-2 top-1/2 -translate-y-1/2"}),t("input",{type:"text",value:f,onChange:e=>v(e.target.value),placeholder:"Search options...",className:"w-full pl-8 pr-2 py-1.5 border border-gray-300 rounded-md text-sm focus:ring-2 focus:ring-blue-500 focus:border-blue-500"}),f&&t("button",{type:"button",onClick:()=>v(""),className:"absolute right-2 top-1/2 -translate-y-1/2 text-gray-400 hover:text-gray-600",title:"Clear search",children:t(n,{className:"w-3 h-3"})})]}),e("div",{className:"max-h-32 overflow-y-auto border border-gray-300 rounded-md p-2 bg-white",children:[0===a.length&&t("div",{className:"text-xs text-gray-500 px-1 py-1",children:"No matches"}),a.map(l=>{const a=r?.value||[],n=Array.isArray(a)&&a.includes(l.value);return e("label",{className:"flex items-center gap-2 py-1 hover:bg-gray-50 cursor-pointer",children:[t("input",{type:"checkbox",className:"rounded border-gray-300 text-blue-600 focus:ring-blue-500",checked:n,onChange:e=>{const t=r?.value||[];let a;a=e.target.checked?[...t,l.value]:t.filter(e=>e!==l.value),a.length>0?y?.(h,{type:"multiselect",value:a,operator:"in"}):y?.(h,null)}}),t("span",{className:"text-sm",children:l.label})]},l.value)})]})]})}if("select"===a&&N.filterable?.options){const l=[{label:`All ${N.header}`,value:""},...N.filterable.options.map(e=>({label:e.label,value:String(e.value)}))];return e("div",{className:"mt-3",children:[e("label",{className:"text-sm font-medium text-gray-700 mb-2 block",children:["Filter by ",N.header]}),t(o,{options:l,value:r?.value||"",placeholder:`Select ${N.header.toLowerCase()}...`,onChange:e=>{y?.(h,e?{type:"select",value:e,operator:"between"}:null)},searchable:!0})]})}if("date"===a){const l=e=>{if(!e)return null;if(e instanceof Date&&!isNaN(e.getTime()))return e;const t=u(String(e),"yyyy-MM-dd",new Date);if(!isNaN(t.getTime()))return t;const l=new Date(e);return isNaN(l.getTime())?null:l},a=l(r?.value),n=l(r?.secondValue),s=r?.operator||"between";return e("div",{className:"mt-3",children:[e("label",{className:"text-sm font-medium text-gray-700 mb-2 block",children:["Filter by ",N.header]}),t("div",{className:"mb-3",children:t(o,{options:[{label:"Between Dates",value:"between"},{label:"On Date",value:"equals"}],value:s,placeholder:"Select date filter...",onChange:e=>{y?.(h,{type:"date",value:r?.value||"",operator:e,secondValue:r?.secondValue||""})},searchable:!1},`date-operator-${h}-${s}`)}),e("div",{className:"flex items-end gap-2",children:[t("div",{className:"flex-1 min-w-37.5",children:t(i,{name:"column_filter_start",label:"between"===s?"Start Date":"Date",value:a?c(a,"yyyy-MM-dd"):null,onChange:e=>{if(e){const t=c(u(e,"yyyy-MM-dd",new Date),"yyyy-MM-dd");y?.(h,{type:"date",value:t,operator:s,secondValue:r?.secondValue||null})}else y?.(h,null)},className:"w-full"})}),"between"===s&&t("div",{className:"flex-1 min-w-37.5",children:t(i,{name:"column_filter_end",label:"End Date",value:n?c(n,"yyyy-MM-dd"):null,onChange:e=>{if(e&&r){const t=c(u(e,"yyyy-MM-dd",new Date),"yyyy-MM-dd");y?.(h,{...r,secondValue:t})}},className:"w-full"})})]})]})}return e("div",{className:"mt-3",children:[e("label",{className:"text-sm font-medium text-gray-700 mb-2 block",children:["Filter by ",N.header]}),e(l,{children:[t("input",{type:"text",placeholder:N.filterable?.placeholder||`Filter ${N.header.toLowerCase()}...`,className:"w-full px-3 py-2.5 border border-gray-300 rounded-lg text-sm focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-all duration-200",value:r?.value||"",onChange:e=>{e.target.value?y?.(h,{type:"text",value:e.target.value,operator:"contains"}):y?.(h,null)}}),r&&t("div",{className:"mt-2",children:t(o,{options:[{label:"Contains",value:"contains"},{label:"Equals",value:"equals"},{label:"Starts with",value:"startsWith"},{label:"Ends with",value:"endsWith"}],value:r?.operator||"contains",placeholder:"Select operator...",onChange:e=>{r&&y?.(h,{...r,operator:e})},searchable:!1})})]})]})})(),Object.keys(m).length>0&&e("div",{className:"mt-4",children:[t("label",{className:"text-sm font-medium text-gray-700 mb-2 block",children:"Active Filters"}),t("div",{className:"space-y-2",children:Object.entries(m).map(([l,a])=>{const r=b.find(e=>e.key===l);return r?e("div",{className:"flex items-center justify-between bg-blue-50 border border-blue-200 rounded-md px-3 py-2 cursor-pointer hover:bg-blue-100",role:"button",title:`Edit filter for ${r.header}`,onClick:()=>(e=>{p(e)})(l),children:[e("div",{className:"flex-1",children:[e("span",{className:"text-sm font-medium text-blue-900",children:[r.header,":"]}),t("span",{className:"text-sm text-blue-700 ml-1",children:(()=>{if(Array.isArray(a.value))return`${a.value.length} selected`;if("date"===a.type){const e=e=>{try{if(!e)return"";if(e instanceof Date&&!isNaN(e.getTime()))return c(e,"dd-MM-yyyy");if("string"==typeof e){const t=u(e,"yyyy-MM-dd",new Date);if(!isNaN(t.getTime()))return c(t,"dd-MM-yyyy");const l=new Date(e);if(!isNaN(l.getTime()))return c(l,"dd-MM-yyyy")}return String(e)}catch{return String(e??"")}},t={equals:"On",gt:"After",gte:"On or After",lt:"Before",lte:"On or Before",between:"Between"}[a.operator]||"On";return"between"===a.operator&&a.secondValue?`${t} ${e(a.value)} - ${e(a.secondValue)}`:`${t} ${e(a.value)}`}return a.value})()})]}),t("button",{onClick:e=>{e.stopPropagation(),y?.(l,null)},className:"text-blue-400 hover:text-red-500 transition-colors",title:"Clear filter",children:t(n,{className:"w-4 h-4"})})]},l):null})})]})]})};export{d as ColumnFilterSelector};
2
- //# sourceMappingURL=ColumnFilterSelector.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"ColumnFilterSelector.js","sources":["../../../../../components/ColumnFilterSelector.tsx"],"sourcesContent":["import { useState, useEffect } from \"react\";\nimport { X, Search } from \"lucide-react\";\nimport { CustomSelect } from \"./CustomSelect\";\nimport { DatePicker } from \"./ui/DatePicker\";\nimport { format, parse } from \"date-fns\";\n\ninterface ColumnFilterSelectorProps {\n columns: Array<{\n key: string;\n header: string;\n filterable?: {\n type?: string;\n options?: Array<{ label: string; value: string | number }>;\n placeholder?: string;\n };\n }>;\n columnFilters: Record<string, any>;\n onColumnFilter?: (columnKey: string, filter: any) => void;\n}\n\nexport const ColumnFilterSelector = ({\n columns,\n columnFilters,\n onColumnFilter,\n}: ColumnFilterSelectorProps) => {\n // Filter out columns that don't have filterable property\n const filterableColumns = columns.filter((col) => col.filterable);\n\n const [selectedColumn, setSelectedColumn] = useState<string>(\n filterableColumns.length > 0 ? filterableColumns[0].key : \"\",\n );\n // Multiselect search text (shared; reset on column change)\n const [multiSearch, setMultiSearch] = useState(\"\");\n const handleColumnSelect = (columnKey: string) => {\n setSelectedColumn(columnKey);\n };\n\n const handleActiveFilterClick = (columnKey: string) => {\n setSelectedColumn(columnKey);\n // Optionally, could add focus management here if the select is focusable via ref\n };\n\n // Convert columns to options for CustomSelect\n const columnOptions = filterableColumns.map((col) => ({\n label: col.header,\n value: col.key,\n }));\n\n const selectedColumnData = filterableColumns.find(\n (col) => col.key === selectedColumn,\n );\n\n // Reset multiselect search whenever the selected column changes\n useEffect(() => {\n setMultiSearch(\"\");\n }, [selectedColumn]);\n\n const renderFilterInput = () => {\n if (!selectedColumnData) return null;\n\n const filterType = selectedColumnData.filterable?.type || \"text\";\n const currentFilter = columnFilters[selectedColumn];\n\n if (\n filterType === \"multiselect\" &&\n selectedColumnData.filterable?.options\n ) {\n const lowered = multiSearch.toLowerCase();\n const filteredOptions = lowered\n ? selectedColumnData.filterable.options.filter(\n (opt) =>\n opt.label.toLowerCase().includes(lowered) ||\n String(opt.value).toLowerCase().includes(lowered),\n )\n : selectedColumnData.filterable.options;\n return (\n <div className=\"mt-3\">\n <label className=\"text-sm font-medium text-gray-700 mb-2 block\">\n Filter by {selectedColumnData.header}\n </label>\n <div className=\"relative mb-2\">\n <Search className=\"w-4 h-4 text-gray-400 absolute left-2 top-1/2 -translate-y-1/2\" />\n <input\n type=\"text\"\n value={multiSearch}\n onChange={(e) => setMultiSearch(e.target.value)}\n placeholder=\"Search options...\"\n className=\"w-full pl-8 pr-2 py-1.5 border border-gray-300 rounded-md text-sm focus:ring-2 focus:ring-blue-500 focus:border-blue-500\"\n />\n {multiSearch && (\n <button\n type=\"button\"\n onClick={() => setMultiSearch(\"\")}\n className=\"absolute right-2 top-1/2 -translate-y-1/2 text-gray-400 hover:text-gray-600\"\n title=\"Clear search\"\n >\n <X className=\"w-3 h-3\" />\n </button>\n )}\n </div>\n <div className=\"max-h-32 overflow-y-auto border border-gray-300 rounded-md p-2 bg-white\">\n {filteredOptions.length === 0 && (\n <div className=\"text-xs text-gray-500 px-1 py-1\">No matches</div>\n )}\n {filteredOptions.map((option) => {\n const selectedValues = currentFilter?.value || [];\n const isSelected =\n Array.isArray(selectedValues) &&\n selectedValues.includes(option.value);\n return (\n <label\n key={option.value}\n className=\"flex items-center gap-2 py-1 hover:bg-gray-50 cursor-pointer\"\n >\n <input\n type=\"checkbox\"\n className=\"rounded border-gray-300 text-blue-600 focus:ring-blue-500\"\n checked={isSelected}\n onChange={(e) => {\n const currentValues = currentFilter?.value || [];\n let newValues;\n if (e.target.checked) {\n newValues = [...currentValues, option.value];\n } else {\n newValues = currentValues.filter(\n (v: any) => v !== option.value,\n );\n }\n if (newValues.length > 0) {\n onColumnFilter?.(selectedColumn, {\n type: \"multiselect\",\n value: newValues,\n operator: \"in\",\n });\n } else {\n onColumnFilter?.(selectedColumn, null);\n }\n }}\n />\n <span className=\"text-sm\">{option.label}</span>\n </label>\n );\n })}\n </div>\n </div>\n );\n }\n\n if (filterType === \"select\" && selectedColumnData.filterable?.options) {\n const selectOptions = [\n { label: `All ${selectedColumnData.header}`, value: \"\" },\n ...selectedColumnData.filterable.options.map((opt) => ({\n label: opt.label,\n value: String(opt.value),\n })),\n ];\n\n return (\n <div className=\"mt-3\">\n <label className=\"text-sm font-medium text-gray-700 mb-2 block\">\n Filter by {selectedColumnData.header}\n </label>\n <CustomSelect\n options={selectOptions}\n value={currentFilter?.value || \"\"}\n placeholder={`Select ${selectedColumnData.header.toLowerCase()}...`}\n onChange={(value) => {\n if (value) {\n onColumnFilter?.(selectedColumn, {\n type: \"select\",\n value: value,\n operator: \"between\",\n });\n } else {\n onColumnFilter?.(selectedColumn, null);\n }\n }}\n searchable={true}\n />\n </div>\n );\n }\n\n if (filterType === \"date\") {\n const parseInput = (val: any) => {\n if (!val) return null;\n if (val instanceof Date && !isNaN(val.getTime())) return val;\n // Accept both stored yyyy-MM-dd and other ISO/date strings\n const direct = parse(String(val), \"yyyy-MM-dd\", new Date());\n if (!isNaN(direct.getTime())) return direct;\n const asDate = new Date(val);\n return isNaN(asDate.getTime()) ? null : asDate;\n };\n\n const primaryDate = parseInput(currentFilter?.value);\n const secondDate = parseInput(currentFilter?.secondValue);\n const effectiveOperator = currentFilter?.operator || \"between\";\n\n return (\n <div className=\"mt-3\">\n <label className=\"text-sm font-medium text-gray-700 mb-2 block\">\n Filter by {selectedColumnData.header}\n </label>\n <div className=\"mb-3\">\n <CustomSelect\n key={`date-operator-${selectedColumn}-${effectiveOperator}`}\n options={[\n { label: \"Between Dates\", value: \"between\" },\n { label: \"On Date\", value: \"equals\" },\n ]}\n value={effectiveOperator}\n placeholder=\"Select date filter...\"\n onChange={(operator) => {\n onColumnFilter?.(selectedColumn, {\n type: \"date\",\n value: currentFilter?.value || \"\",\n operator: operator as any,\n secondValue: currentFilter?.secondValue || \"\",\n });\n }}\n searchable={false}\n />\n </div>\n <div className=\"flex items-end gap-2\">\n <div className=\"flex-1 min-w-37.5\">\n <DatePicker\n name=\"column_filter_start\"\n label={effectiveOperator === \"between\" ? \"Start Date\" : \"Date\"}\n value={primaryDate ? format(primaryDate, \"yyyy-MM-dd\") : null}\n onChange={(newValue) => {\n if (newValue) {\n const stored = format(\n parse(newValue, \"yyyy-MM-dd\", new Date()),\n \"yyyy-MM-dd\",\n );\n onColumnFilter?.(selectedColumn, {\n type: \"date\",\n value: stored,\n operator: effectiveOperator,\n secondValue: currentFilter?.secondValue || null,\n });\n } else {\n onColumnFilter?.(selectedColumn, null);\n }\n }}\n className=\"w-full\"\n />\n </div>\n {effectiveOperator === \"between\" && (\n <div className=\"flex-1 min-w-37.5\">\n <DatePicker\n name=\"column_filter_end\"\n label=\"End Date\"\n value={secondDate ? format(secondDate, \"yyyy-MM-dd\") : null}\n onChange={(newValue) => {\n if (newValue && currentFilter) {\n const stored = format(\n parse(newValue, \"yyyy-MM-dd\", new Date()),\n \"yyyy-MM-dd\",\n );\n onColumnFilter?.(selectedColumn, {\n ...currentFilter,\n secondValue: stored,\n });\n }\n }}\n className=\"w-full\"\n />\n </div>\n )}\n </div>\n </div>\n );\n }\n\n // Default text input\n return (\n <div className=\"mt-3\">\n <label className=\"text-sm font-medium text-gray-700 mb-2 block\">\n Filter by {selectedColumnData.header}\n </label>\n <>\n <input\n type=\"text\"\n placeholder={\n selectedColumnData.filterable?.placeholder ||\n `Filter ${selectedColumnData.header.toLowerCase()}...`\n }\n className=\"w-full px-3 py-2.5 border border-gray-300 rounded-lg text-sm focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-all duration-200\"\n value={currentFilter?.value || \"\"}\n onChange={(e) => {\n if (e.target.value) {\n onColumnFilter?.(selectedColumn, {\n type: \"text\",\n value: e.target.value,\n operator: \"contains\",\n });\n } else {\n onColumnFilter?.(selectedColumn, null);\n }\n }}\n />\n\n {/* Filter Operator Selector for text filters */}\n {currentFilter && (\n <div className=\"mt-2\">\n <CustomSelect\n options={[\n { label: \"Contains\", value: \"contains\" },\n { label: \"Equals\", value: \"equals\" },\n { label: \"Starts with\", value: \"startsWith\" },\n { label: \"Ends with\", value: \"endsWith\" },\n ]}\n value={currentFilter?.operator || \"contains\"}\n placeholder=\"Select operator...\"\n onChange={(operator) => {\n if (currentFilter) {\n onColumnFilter?.(selectedColumn, {\n ...currentFilter,\n operator: operator as any,\n });\n }\n }}\n searchable={false}\n />\n </div>\n )}\n </>\n </div>\n );\n };\n\n return (\n <div className=\"space-y-4\">\n {/* Column Selection Dropdown */}\n <div>\n <label className=\"text-sm font-medium text-gray-700 mb-2 block\">\n Select Column to Filter\n </label>\n <CustomSelect\n options={columnOptions}\n value={selectedColumn}\n placeholder=\"Choose a column...\"\n onChange={handleColumnSelect}\n searchable={true}\n />\n </div>\n\n {/* Filter Input for Selected Column */}\n {selectedColumn && renderFilterInput()}\n\n {/* Active Filters Display */}\n {Object.keys(columnFilters).length > 0 && (\n <div className=\"mt-4\">\n <label className=\"text-sm font-medium text-gray-700 mb-2 block\">\n Active Filters\n </label>\n <div className=\"space-y-2\">\n {Object.entries(columnFilters).map(([columnKey, filter]) => {\n const column = filterableColumns.find(\n (col) => col.key === columnKey,\n );\n if (!column) return null;\n\n return (\n <div\n key={columnKey}\n className=\"flex items-center justify-between bg-blue-50 border border-blue-200 rounded-md px-3 py-2 cursor-pointer hover:bg-blue-100\"\n role=\"button\"\n title={`Edit filter for ${column.header}`}\n onClick={() => handleActiveFilterClick(columnKey)}\n >\n <div className=\"flex-1\">\n <span className=\"text-sm font-medium text-blue-900\">\n {column.header}:\n </span>\n <span className=\"text-sm text-blue-700 ml-1\">\n {(() => {\n if (Array.isArray(filter.value)) {\n return `${filter.value.length} selected`;\n }\n\n // Handle date filters with operators\n if (filter.type === \"date\") {\n const formatDate = (dateValue: any) => {\n try {\n if (!dateValue) return \"\";\n if (\n dateValue instanceof Date &&\n !isNaN(dateValue.getTime())\n ) {\n return format(dateValue, \"dd-MM-yyyy\");\n }\n if (typeof dateValue === \"string\") {\n // Prefer our stored format first\n const parsed = parse(\n dateValue,\n \"yyyy-MM-dd\",\n new Date(),\n );\n if (!isNaN(parsed.getTime())) {\n return format(parsed, \"dd-MM-yyyy\");\n }\n const asDate = new Date(dateValue);\n if (!isNaN(asDate.getTime())) {\n return format(asDate, \"dd-MM-yyyy\");\n }\n }\n return String(dateValue);\n } catch {\n return String(dateValue ?? \"\");\n }\n };\n\n const operatorLabels: Record<string, string> = {\n equals: \"On\",\n gt: \"After\",\n gte: \"On or After\",\n lt: \"Before\",\n lte: \"On or Before\",\n between: \"Between\",\n };\n\n const operatorLabel =\n operatorLabels[filter.operator as string] || \"On\";\n\n if (\n filter.operator === \"between\" &&\n filter.secondValue\n ) {\n return `${operatorLabel} ${formatDate(\n filter.value,\n )} - ${formatDate(filter.secondValue)}`;\n }\n\n return `${operatorLabel} ${formatDate(filter.value)}`;\n }\n\n return filter.value;\n })()}\n </span>\n </div>\n <button\n onClick={(e) => {\n e.stopPropagation();\n onColumnFilter?.(columnKey, null);\n }}\n className=\"text-blue-400 hover:text-red-500 transition-colors\"\n title=\"Clear filter\"\n >\n <X className=\"w-4 h-4\" />\n </button>\n </div>\n );\n })}\n </div>\n </div>\n )}\n </div>\n );\n};\n"],"names":["ColumnFilterSelector","columns","columnFilters","onColumnFilter","filterableColumns","filter","col","filterable","selectedColumn","setSelectedColumn","useState","length","key","multiSearch","setMultiSearch","columnOptions","map","label","header","value","selectedColumnData","find","useEffect","_jsxs","className","children","_jsx","CustomSelect","options","placeholder","onChange","columnKey","searchable","filterType","type","currentFilter","lowered","toLowerCase","filteredOptions","opt","includes","String","Search","e","target","onClick","title","X","option","selectedValues","isSelected","Array","isArray","checked","currentValues","newValues","v","operator","selectOptions","parseInput","val","Date","isNaN","getTime","direct","parse","asDate","primaryDate","secondDate","secondValue","effectiveOperator","DatePicker","name","format","newValue","stored","_Fragment","renderFilterInput","Object","keys","entries","column","role","handleActiveFilterClick","formatDate","dateValue","parsed","operatorLabel","equals","gt","gte","lt","lte","between","stopPropagation"],"mappings":"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"}
@@ -1,14 +0,0 @@
1
- interface Option {
2
- label: string;
3
- value: string;
4
- }
5
- interface CustomSelectProps {
6
- options: Option[];
7
- value?: string;
8
- placeholder?: string;
9
- onChange: (value: string) => void;
10
- searchable?: boolean;
11
- className?: string;
12
- }
13
- export declare const CustomSelect: ({ options, value, placeholder, onChange, searchable, className, }: CustomSelectProps) => import("react/jsx-runtime").JSX.Element;
14
- export {};
@@ -1,2 +0,0 @@
1
- import{jsxs as e,jsx as t}from"react/jsx-runtime";import{useState as r,useRef as n,useEffect as a}from"react";import{ChevronDown as o,Check as l}from"lucide-react";const s=({options:s,value:i,placeholder:c="Select an option...",onChange:u,searchable:d=!0,className:m=""})=>{const[h,p]=r(!1),[b,f]=r(""),[g,v]=r(-1),w=n(null),x=n(null),y=n(null),N=s.find(e=>e.value===i),k=d?s.filter(e=>e.label?.toLowerCase()?.includes(b?.toLowerCase())):s;a(()=>{const e=e=>{w.current&&!w.current.contains(e.target)&&(p(!1),f(""),v(-1))};return document.addEventListener("mousedown",e),()=>document.removeEventListener("mousedown",e)},[]),a(()=>{const e=e=>{if(h)switch(e.key){case"ArrowDown":e.preventDefault(),v(e=>e<k.length-1?e+1:0);break;case"ArrowUp":e.preventDefault(),v(e=>e>0?e-1:k.length-1);break;case"Enter":e.preventDefault(),g>=0&&k[g]&&C(k[g].value);break;case"Escape":p(!1),f(""),v(-1)}};if(h)return document.addEventListener("keydown",e),()=>document.removeEventListener("keydown",e)},[h,g,k]),a(()=>{if(g>=0&&y.current){const e=y.current.children[g];e&&e.scrollIntoView({block:"nearest",behavior:"smooth"})}},[g]);const C=e=>{u(e),p(!1),f(""),v(-1)};return e("div",{className:`relative ${m}`,ref:w,children:[t("div",{className:"relative w-full cursor-pointer border border-gray-300 rounded-lg bg-white transition-all duration-200 "+(h?"ring-2 ring-blue-500 border-blue-500":"hover:border-gray-400 focus-within:ring-2 focus-within:ring-blue-500 focus-within:border-blue-500"),onClick:()=>{p(!h),d&&!h&&setTimeout(()=>x.current?.focus(),0)},children:e("div",{className:"flex items-center justify-between px-3 py-2.5",children:[d&&h?t("input",{ref:x,type:"text",className:"flex-1 outline-none text-sm bg-transparent",placeholder:`Search ${c?.toLowerCase()}...`,value:b,onChange:e=>{f(e.target.value),v(-1),h||p(!0)},onClick:e=>e.stopPropagation()}):t("span",{className:"flex-1 text-sm truncate "+(N?"text-gray-900":"text-gray-500"),children:N?N.label:c}),t(o,{className:"w-4 h-4 text-gray-400 transition-transform duration-200 "+(h?"rotate-180":"")})]})}),h&&t("div",{className:"absolute top-full left-0 right-0 mt-1 bg-white border border-gray-200 rounded-lg shadow-lg z-99999 max-h-80 overflow-hidden",children:k.length>0?t("ul",{ref:y,className:"py-1 overflow-y-auto max-h-80",children:k.map((r,n)=>e("li",{className:"px-3 py-2 text-sm cursor-pointer flex items-center justify-between transition-colors "+(n===g?"bg-blue-50 text-blue-700":"text-gray-700 hover:bg-gray-50"),onClick:()=>C(r.value),onMouseEnter:()=>v(n),children:[t("span",{className:"truncate",children:r.label}),N?.value===r.value&&t(l,{className:"w-4 h-4 text-blue-600 shrink-0 ml-2"})]},r.value))}):t("div",{className:"px-3 py-2 text-sm text-gray-500 text-center",children:"No options found"})})]})};export{s as CustomSelect};
2
- //# sourceMappingURL=CustomSelect.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"CustomSelect.js","sources":["../../../../../components/CustomSelect.tsx"],"sourcesContent":["import { useState, useRef, useEffect, ChangeEvent } from \"react\";\nimport { ChevronDown, Check } from \"lucide-react\";\n\ninterface Option {\n label: string;\n value: string;\n}\n\ninterface CustomSelectProps {\n options: Option[];\n value?: string;\n placeholder?: string;\n onChange: (value: string) => void;\n searchable?: boolean;\n className?: string;\n}\n\nexport const CustomSelect = ({\n options,\n value,\n placeholder = \"Select an option...\",\n onChange,\n searchable = true,\n className = \"\",\n}: CustomSelectProps) => {\n const [isOpen, setIsOpen] = useState(false);\n const [searchTerm, setSearchTerm] = useState(\"\");\n const [highlightedIndex, setHighlightedIndex] = useState(-1);\n\n const containerRef = useRef<HTMLDivElement>(null);\n const inputRef = useRef<HTMLInputElement>(null);\n const listRef = useRef<HTMLUListElement>(null);\n\n const selectedOption = options.find((option) => option.value === value);\n\n const filteredOptions = searchable\n ? options.filter((option) =>\n option.label?.toLowerCase()?.includes(searchTerm?.toLowerCase())\n )\n : options;\n\n // Close dropdown when clicking outside\n useEffect(() => {\n const handleClickOutside = (event: MouseEvent) => {\n if (\n containerRef.current &&\n !containerRef.current.contains(event.target as Node)\n ) {\n setIsOpen(false);\n setSearchTerm(\"\");\n setHighlightedIndex(-1);\n }\n };\n\n document.addEventListener(\"mousedown\", handleClickOutside);\n return () => document.removeEventListener(\"mousedown\", handleClickOutside);\n }, []);\n\n // Handle keyboard navigation\n useEffect(() => {\n const handleKeyDown = (event: KeyboardEvent) => {\n if (!isOpen) return;\n\n switch (event.key) {\n case \"ArrowDown\":\n event.preventDefault();\n setHighlightedIndex((prev) =>\n prev < filteredOptions.length - 1 ? prev + 1 : 0\n );\n break;\n case \"ArrowUp\":\n event.preventDefault();\n setHighlightedIndex((prev) =>\n prev > 0 ? prev - 1 : filteredOptions.length - 1\n );\n break;\n case \"Enter\":\n event.preventDefault();\n if (highlightedIndex >= 0 && filteredOptions[highlightedIndex]) {\n handleSelect(filteredOptions[highlightedIndex].value);\n }\n break;\n case \"Escape\":\n setIsOpen(false);\n setSearchTerm(\"\");\n setHighlightedIndex(-1);\n break;\n }\n };\n\n if (isOpen) {\n document.addEventListener(\"keydown\", handleKeyDown);\n return () => document.removeEventListener(\"keydown\", handleKeyDown);\n }\n }, [isOpen, highlightedIndex, filteredOptions]);\n\n // Scroll highlighted option into view\n useEffect(() => {\n if (highlightedIndex >= 0 && listRef.current) {\n const highlightedElement = listRef.current.children[\n highlightedIndex\n ] as HTMLElement;\n if (highlightedElement) {\n highlightedElement.scrollIntoView({\n block: \"nearest\",\n behavior: \"smooth\",\n });\n }\n }\n }, [highlightedIndex]);\n\n const handleSelect = (optionValue: string) => {\n onChange(optionValue);\n setIsOpen(false);\n setSearchTerm(\"\");\n setHighlightedIndex(-1);\n };\n\n const handleInputClick = () => {\n setIsOpen(!isOpen);\n if (searchable && !isOpen) {\n setTimeout(() => inputRef.current?.focus(), 0);\n }\n };\n\n const handleInputChange = (e: ChangeEvent<HTMLInputElement>) => {\n setSearchTerm(e.target.value);\n setHighlightedIndex(-1);\n if (!isOpen) setIsOpen(true);\n };\n\n return (\n <div className={`relative ${className}`} ref={containerRef}>\n <div\n className={`relative w-full cursor-pointer border border-gray-300 rounded-lg bg-white transition-all duration-200 ${\n isOpen\n ? \"ring-2 ring-blue-500 border-blue-500\"\n : \"hover:border-gray-400 focus-within:ring-2 focus-within:ring-blue-500 focus-within:border-blue-500\"\n }`}\n onClick={handleInputClick}\n >\n <div className=\"flex items-center justify-between px-3 py-2.5\">\n {searchable && isOpen ? (\n <input\n ref={inputRef}\n type=\"text\"\n className=\"flex-1 outline-none text-sm bg-transparent\"\n placeholder={`Search ${placeholder?.toLowerCase()}...`}\n value={searchTerm}\n onChange={handleInputChange}\n onClick={(e) => e.stopPropagation()}\n />\n ) : (\n <span\n className={`flex-1 text-sm truncate ${\n selectedOption ? \"text-gray-900\" : \"text-gray-500\"\n }`}\n >\n {selectedOption ? selectedOption.label : placeholder}\n </span>\n )}\n\n <ChevronDown\n className={`w-4 h-4 text-gray-400 transition-transform duration-200 ${\n isOpen ? \"rotate-180\" : \"\"\n }`}\n />\n </div>\n </div>\n\n {/* Dropdown List */}\n {isOpen && (\n <div className=\"absolute top-full left-0 right-0 mt-1 bg-white border border-gray-200 rounded-lg shadow-lg z-99999 max-h-80 overflow-hidden\">\n {filteredOptions.length > 0 ? (\n <ul ref={listRef} className=\"py-1 overflow-y-auto max-h-80\">\n {filteredOptions.map((option, index) => (\n <li\n key={option.value}\n className={`px-3 py-2 text-sm cursor-pointer flex items-center justify-between transition-colors ${\n index === highlightedIndex\n ? \"bg-blue-50 text-blue-700\"\n : \"text-gray-700 hover:bg-gray-50\"\n }`}\n onClick={() => handleSelect(option.value)}\n onMouseEnter={() => setHighlightedIndex(index)}\n >\n <span className=\"truncate\">{option.label}</span>\n {selectedOption?.value === option.value && (\n <Check className=\"w-4 h-4 text-blue-600 shrink-0 ml-2\" />\n )}\n </li>\n ))}\n </ul>\n ) : (\n <div className=\"px-3 py-2 text-sm text-gray-500 text-center\">\n No options found\n </div>\n )}\n </div>\n )}\n </div>\n );\n};\n"],"names":["CustomSelect","options","value","placeholder","onChange","searchable","className","isOpen","setIsOpen","useState","searchTerm","setSearchTerm","highlightedIndex","setHighlightedIndex","containerRef","useRef","inputRef","listRef","selectedOption","find","option","filteredOptions","filter","label","toLowerCase","includes","useEffect","handleClickOutside","event","current","contains","target","document","addEventListener","removeEventListener","handleKeyDown","key","preventDefault","prev","length","handleSelect","highlightedElement","children","scrollIntoView","block","behavior","optionValue","_jsxs","ref","_jsx","onClick","setTimeout","focus","type","e","stopPropagation","ChevronDown","map","index","onMouseEnter","Check"],"mappings":"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"}
@@ -1,16 +0,0 @@
1
- import React from "react";
2
- import { GridColumn, GridRow } from "../types";
3
- interface FooterAggregateProps {
4
- columns: (GridColumn & {
5
- width: number;
6
- })[];
7
- data: GridRow[];
8
- selectable: boolean;
9
- /** Resolved row height so footer matches density */
10
- rowHeight: number;
11
- /** Pinned columns (same semantics as grid body) */
12
- pinnedColumns?: Set<string>;
13
- getRowId?: (row: GridRow) => string | number;
14
- }
15
- export declare const FooterAggregate: React.NamedExoticComponent<FooterAggregateProps>;
16
- export {};
@@ -1,2 +0,0 @@
1
- import{jsxs as e,jsx as t}from"react/jsx-runtime";import{memo as r,useMemo as n}from"react";import{SELECT_COL_WIDTH as a}from"../constants.js";const i=r(({columns:r,data:i,selectable:o,rowHeight:l,pinnedColumns:s=new Set,getRowId:g})=>{const d=n(()=>{const e={},t=r.filter(e=>e.footer_aggregate);return 0===t.length||t.forEach(t=>{const r=i.map(e=>e[t.key]);let n=null;if("function"==typeof t.footer_aggregate)try{n=t.footer_aggregate(r)}catch(e){console.warn(`Error computing custom footer aggregate for column ${t.key}:`,e),n=null}else{const e=e=>!!/^\d{4}-\d{2}-\d{2}$/.test(e)||(!!/^\d{2}-\d{2}-\d{4}$/.test(e)||(!!/^\d{4}\/\d{2}\/\d{2}$/.test(e)||(!!/^\d{2}\/\d{2}\/\d{4}$/.test(e)||!(!e.includes("-")&&!e.includes("/")||!Number.isFinite(Date.parse(e)))))),a=r.map(t=>{if("number"==typeof t)return t;if(null==t)return NaN;const r=String(t);if(e(r))return NaN;const n=parseFloat(r.replace(/,/g,""));return Number.isFinite(n)?n:NaN}).filter(e=>Number.isFinite(e)),o=()=>r.map(e=>{if(e instanceof Date)return e.getTime();if("string"==typeof e||"number"==typeof e){const t=Date.parse(String(e));return Number.isFinite(t)?t:NaN}return NaN}).filter(e=>Number.isFinite(e));switch(t.footer_aggregate){case"count":n=i.length;break;case"sum":a.length&&(n=a.reduce((e,t)=>e+t,0));break;case"avg":if(a.length){n=a.reduce((e,t)=>e+t,0)/a.length}break;case"min":if(a.length)n=Math.min(...a);else{const e=o();e.length&&(n=new Date(Math.min(...e)))}break;case"max":if(a.length)n=Math.max(...a);else{const e=o();e.length&&(n=new Date(Math.max(...e)))}break;default:n=null}}null!=n&&(e[t.key]=n)}),e},[r,i]);if(!r.some(e=>e.footer_aggregate))return null;const c=r.reduce((e,t)=>e+t.width,0),f=n(()=>{const e=new Map,t=r.filter(e=>s.has(e.key)).map(e=>e.key);let n=o?a:0;for(const a of t){e.set(a,n);n+=r.find(e=>e.key===a)?.width||100}return e},[r,s,o]);return e("div",{className:"flex h-full",style:{minWidth:`${c+(o?a:0)}px`,height:`${l}px`},children:[o&&t("div",{className:"w-12 p-3 border-r border-gray-200 dark:border-gray-700 flex items-center justify-center sticky left-0 bg-white dark:bg-gray-900 z-30",style:{height:`${l}px`},children:t("span",{className:"text-xs font-medium text-gray-600 dark:text-gray-300",children:"Total"})},"select-footer-cell"),r.map(e=>{const r=e.width,n=d[e.key],a=e.footer_aggregate&&void 0!==n;let i="";if(a){const t=e.footerAggregateFormatter||e.formatter;if(t)try{i=t(n)}catch{i=String(n)}else i="number"==typeof n?n%1==0?n.toString():n.toFixed(2):String(n?.toFixed)}const o=s.has(e.key),g=o&&f.get(e.key)||0,c="center"===e.align?"text-center":"right"===e.align?"text-right":"text-left",u="center"===e.align?"justify-center":"right"===e.align?"justify-end":"justify-start",h=e.noPadding?"p-0":"p-3";return t("div",{className:`border-r border-gray-200 dark:border-gray-700 last:border-r-0 ${h} text-sm flex items-center font-semibold text-gray-700 dark:text-gray-200 overflow-hidden ${u} ${c} ${o?"sticky bg-white dark:bg-gray-900 z-20":""}`,style:{width:`${r}px`,minWidth:`${r}px`,maxWidth:`${r}px`,left:o?`${g}px`:"auto",height:`${l}px`},title:a?`${e.header}: ${i}`:void 0,children:a&&t("span",{className:"truncate w-full",children:i})},e.key)})]})},(e,t)=>{if(e.selectable!==t.selectable)return!1;if(e.rowHeight!==t.rowHeight)return!1;if(e.getRowId!==t.getRowId)return!1;if(e.columns.length!==t.columns.length)return!1;for(let r=0;r<e.columns.length;r++){const n=e.columns[r],a=t.columns[r];if(n.key!==a.key||n.width!==a.width||n.footer_aggregate!==a.footer_aggregate)return!1}if(e.data.length!==t.data.length)return!1;const r=(e,t,r)=>r?r(e):void 0!==e.id?e.id:t,n=e.data.length>0?r(e.data[0],0,e.getRowId):void 0,a=t.data.length>0?r(t.data[0],0,t.getRowId):void 0,i=e.data.length-1,o=t.data.length-1,l=e.data.length>0?r(e.data[i],i,e.getRowId):void 0,s=t.data.length>0?r(t.data[o],o,t.getRowId):void 0;return n===a&&l===s&&e.pinnedColumns?.size===t.pinnedColumns?.size});export{i as FooterAggregate};
2
- //# sourceMappingURL=FooterAggregate.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"FooterAggregate.js","sources":["../../../../../components/FooterAggregate.tsx"],"sourcesContent":["import React, { useMemo, memo } from \"react\";\nimport { GridColumn, GridRow } from \"../types\";\nimport { SELECT_COL_WIDTH } from \"../constants\";\n\ninterface FooterAggregateProps {\n columns: (GridColumn & { width: number })[];\n data: GridRow[];\n selectable: boolean;\n /** Resolved row height so footer matches density */\n rowHeight: number;\n /** Pinned columns (same semantics as grid body) */\n pinnedColumns?: Set<string>;\n getRowId?: (row: GridRow) => string | number;\n}\n\nconst FooterAggregateComponent: React.FC<FooterAggregateProps> = ({\n columns,\n data,\n selectable,\n rowHeight,\n pinnedColumns = new Set(),\n getRowId,\n}) => {\n const aggregateValues = useMemo(() => {\n const result: Record<string, any> = {};\n\n // Only compute aggregates for columns that have footer_aggregate defined\n const columnsWithFooterAgg = columns.filter((col) => col.footer_aggregate);\n\n if (columnsWithFooterAgg.length === 0) {\n return result;\n }\n\n columnsWithFooterAgg.forEach((col) => {\n const values = data.map((row) => row[col.key]);\n let aggValue: any = null;\n\n if (typeof col.footer_aggregate === \"function\") {\n try {\n aggValue = col.footer_aggregate(values);\n } catch (error) {\n console.warn(\n `Error computing custom footer aggregate for column ${col.key}:`,\n error,\n );\n aggValue = null;\n }\n } else {\n const isDateLike = (s: string) => {\n // Common date formats: YYYY-MM-DD, DD-MM-YYYY, with '/' too\n if (/^\\d{4}-\\d{2}-\\d{2}$/.test(s)) return true;\n if (/^\\d{2}-\\d{2}-\\d{4}$/.test(s)) return true;\n if (/^\\d{4}\\/\\d{2}\\/\\d{2}$/.test(s)) return true;\n if (/^\\d{2}\\/\\d{2}\\/\\d{4}$/.test(s)) return true;\n // Fallback: if Date.parse works and the string contains '-' or '/'\n if (\n (s.includes(\"-\") || s.includes(\"/\")) &&\n Number.isFinite(Date.parse(s))\n )\n return true;\n return false;\n };\n\n const nums = values\n .map((v) => {\n if (typeof v === \"number\") return v;\n if (v == null) return NaN;\n const sv = String(v);\n // Prevent treating date-like strings as numbers (e.g., \"2025-01-01\" -> 2025)\n if (isDateLike(sv)) return NaN;\n const n = parseFloat(sv.replace(/,/g, \"\"));\n return Number.isFinite(n) ? n : NaN;\n })\n .filter((n) => Number.isFinite(n)) as number[];\n\n const tryDateNumbers = () => {\n // Convert date-like values to timestamps for min/max\n const dateNums = values\n .map((v) => {\n if (v instanceof Date) return v.getTime();\n if (typeof v === \"string\" || typeof v === \"number\") {\n const t = Date.parse(String(v));\n return Number.isFinite(t) ? t : NaN;\n }\n return NaN;\n })\n .filter((t) => Number.isFinite(t)) as number[];\n return dateNums;\n };\n\n switch (col.footer_aggregate) {\n case \"count\":\n aggValue = data.length;\n break;\n case \"sum\":\n if (nums.length) {\n aggValue = nums.reduce((a, b) => a + b, 0);\n }\n break;\n case \"avg\":\n if (nums.length) {\n const sum = nums.reduce((a, b) => a + b, 0);\n aggValue = sum / nums.length;\n }\n break;\n case \"min\":\n if (nums.length) {\n aggValue = Math.min(...nums);\n } else {\n const dateNums = tryDateNumbers();\n if (dateNums.length) {\n aggValue = new Date(Math.min(...dateNums));\n }\n }\n break;\n case \"max\":\n if (nums.length) {\n aggValue = Math.max(...nums);\n } else {\n const dateNums = tryDateNumbers();\n if (dateNums.length) {\n aggValue = new Date(Math.max(...dateNums));\n }\n }\n break;\n default:\n aggValue = null;\n }\n }\n\n if (aggValue !== null && aggValue !== undefined) {\n result[col.key] = aggValue;\n }\n });\n\n return result;\n }, [columns, data]);\n\n // Don't render if no columns have footer aggregates\n const hasFooterAggregates = columns.some((col) => col.footer_aggregate);\n if (!hasFooterAggregates) {\n return null;\n }\n\n const totalWidth = columns.reduce((sum, col) => sum + col.width, 0);\n\n // Precompute left offsets for pinned columns (mirrors GridRows logic)\n const leftOffsetByPinnedKey = useMemo(() => {\n const map = new Map<string, number>();\n const pinnedKeysInOrder = columns\n .filter((c) => pinnedColumns.has(c.key))\n .map((c) => c.key);\n let acc = selectable ? SELECT_COL_WIDTH : 0;\n for (const key of pinnedKeysInOrder) {\n map.set(key, acc);\n const w = columns.find((c) => c.key === key)?.width || 100;\n acc += w;\n }\n return map;\n }, [columns, pinnedColumns, selectable]);\n\n return (\n <div\n className=\"flex h-full\"\n style={{\n minWidth: `${totalWidth + (selectable ? SELECT_COL_WIDTH : 0)}px`,\n height: `${rowHeight}px`,\n }}\n >\n {selectable && (\n <div\n key=\"select-footer-cell\"\n className=\"w-12 p-3 border-r border-gray-200 dark:border-gray-700 flex items-center justify-center sticky left-0 bg-white dark:bg-gray-900 z-30\"\n style={{ height: `${rowHeight}px` }}\n >\n <span className=\"text-xs font-medium text-gray-600 dark:text-gray-300\">\n Total\n </span>\n </div>\n )}\n {columns.map((col) => {\n const width = col.width;\n const rawValue = aggregateValues[col.key];\n const hasAggregate = col.footer_aggregate && rawValue !== undefined;\n\n // Format value\n let displayValue = \"\";\n if (hasAggregate) {\n const formatter = col.footerAggregateFormatter || col.formatter;\n if (formatter) {\n try {\n displayValue = formatter(rawValue);\n } catch {\n displayValue = String(rawValue);\n }\n } else {\n if (typeof rawValue === \"number\") {\n displayValue =\n rawValue % 1 === 0 ? rawValue.toString() : rawValue.toFixed(2);\n } else {\n displayValue = String(rawValue?.toFixed);\n }\n }\n }\n\n const isPinned = pinnedColumns.has(col.key);\n const leftOffset = isPinned\n ? leftOffsetByPinnedKey.get(col.key) || 0\n : 0;\n const alignClass =\n col.align === \"center\"\n ? \"text-center\"\n : col.align === \"right\"\n ? \"text-right\"\n : \"text-left\";\n const justifyClass =\n col.align === \"center\"\n ? \"justify-center\"\n : col.align === \"right\"\n ? \"justify-end\"\n : \"justify-start\";\n const paddingClass = col.noPadding ? \"p-0\" : \"p-3\";\n\n return (\n <div\n key={col.key}\n className={`border-r border-gray-200 dark:border-gray-700 last:border-r-0 ${paddingClass} text-sm flex items-center font-semibold text-gray-700 dark:text-gray-200 overflow-hidden ${justifyClass} ${alignClass} ${\n isPinned ? \"sticky bg-white dark:bg-gray-900 z-20\" : \"\"\n }`}\n style={{\n width: `${width}px`,\n minWidth: `${width}px`,\n maxWidth: `${width}px`,\n left: isPinned ? `${leftOffset}px` : \"auto\",\n height: `${rowHeight}px`,\n }}\n title={hasAggregate ? `${col.header}: ${displayValue}` : undefined}\n >\n {hasAggregate && (\n <span className=\"truncate w-full\">{displayValue}</span>\n )}\n </div>\n );\n })}\n </div>\n );\n};\n\n// Memoize footer to avoid recomputing aggregates unless relevant inputs change\nexport const FooterAggregate = memo(FooterAggregateComponent, (prev, next) => {\n if (prev.selectable !== next.selectable) return false;\n if (prev.rowHeight !== next.rowHeight) return false;\n if (prev.getRowId !== next.getRowId) return false;\n if (prev.columns.length !== next.columns.length) return false;\n // Compare columns by key + width + aggregate spec reference\n for (let i = 0; i < prev.columns.length; i++) {\n const pc = prev.columns[i];\n const nc = next.columns[i];\n if (\n pc.key !== nc.key ||\n pc.width !== nc.width ||\n pc.footer_aggregate !== nc.footer_aggregate\n )\n return false;\n }\n // If data length changed or first/last row id changed, recompute\n if (prev.data.length !== next.data.length) return false;\n\n const extractId = (\n row: any,\n idx: number,\n getRowId?: (r: any) => string | number,\n ) => {\n if (getRowId) return getRowId(row);\n if (row.id !== undefined) return row.id;\n return idx;\n };\n\n const prevFirst =\n prev.data.length > 0\n ? extractId(prev.data[0], 0, prev.getRowId)\n : undefined;\n const nextFirst =\n next.data.length > 0\n ? extractId(next.data[0], 0, next.getRowId)\n : undefined;\n const prevLastIdx = prev.data.length - 1;\n const nextLastIdx = next.data.length - 1;\n const prevLast =\n prev.data.length > 0\n ? extractId(prev.data[prevLastIdx], prevLastIdx, prev.getRowId)\n : undefined;\n const nextLast =\n next.data.length > 0\n ? extractId(next.data[nextLastIdx], nextLastIdx, next.getRowId)\n : undefined;\n if (prevFirst !== nextFirst || prevLast !== nextLast) return false;\n if (prev.pinnedColumns?.size !== next.pinnedColumns?.size) return false;\n return true;\n});\n"],"names":["FooterAggregate","memo","columns","data","selectable","rowHeight","pinnedColumns","Set","getRowId","aggregateValues","useMemo","result","columnsWithFooterAgg","filter","col","footer_aggregate","length","forEach","values","map","row","key","aggValue","error","console","warn","isDateLike","s","test","includes","Number","isFinite","Date","parse","nums","v","NaN","sv","String","n","parseFloat","replace","tryDateNumbers","getTime","t","reduce","a","b","Math","min","dateNums","max","some","totalWidth","sum","width","leftOffsetByPinnedKey","Map","pinnedKeysInOrder","c","has","acc","SELECT_COL_WIDTH","set","find","_jsxs","className","style","minWidth","height","children","_jsx","rawValue","hasAggregate","undefined","displayValue","formatter","footerAggregateFormatter","toString","toFixed","isPinned","leftOffset","get","alignClass","align","justifyClass","paddingClass","noPadding","maxWidth","left","title","header","prev","next","i","pc","nc","extractId","idx","id","prevFirst","nextFirst","prevLastIdx","nextLastIdx","prevLast","nextLast","size"],"mappings":"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"}
@@ -1,33 +0,0 @@
1
- import { GridColumn, SortConfig, ActiveFilters, ColumnFilterValue, GridRow } from "../types";
2
- interface GridHeaderProps {
3
- pinnedColumns: GridColumn[];
4
- unpinnedColumns: GridColumn[];
5
- hvPadLeft?: number;
6
- hvPadRight?: number;
7
- headerHeight: number;
8
- sortConfig: SortConfig;
9
- columnFilters: ActiveFilters;
10
- selectable: boolean;
11
- selectedRows: Set<string | number>;
12
- totalRows: number;
13
- data: GridRow[];
14
- onSort: (key: string) => void;
15
- onColumnFilter: (key: string, filter: ColumnFilterValue | null) => void;
16
- onSelectAll: () => void;
17
- onColumnResize?: (columnKey: string, width: number) => void;
18
- pinnedKeySet?: Set<string>;
19
- onColumnPin?: (columnKey: string, pinned: boolean) => void;
20
- groupable?: boolean;
21
- groupedByColumn?: string | null;
22
- onGroupBy?: (columnKey: string | null) => void;
23
- groupedByColumns?: string[];
24
- onGroupToggle?: (columnKey: string, nextGrouped: boolean) => void;
25
- onAutosizeColumn?: (columnKey: string) => void;
26
- onAutosizeAllColumns?: () => void;
27
- onResetColumns?: () => void;
28
- columnOrder: string[];
29
- onColumnOrderChange: (order: string[]) => void;
30
- paginationMode?: "client" | "server" | null;
31
- }
32
- export declare const GridHeader: ({ pinnedColumns, unpinnedColumns, hvPadLeft, hvPadRight, headerHeight, sortConfig, columnFilters, selectable, selectedRows, totalRows, data, onSort, onColumnFilter, onSelectAll, onColumnResize, pinnedKeySet, onColumnPin, groupable, groupedByColumn, onGroupBy, groupedByColumns, onGroupToggle, onAutosizeColumn, onAutosizeAllColumns, onResetColumns, columnOrder, onColumnOrderChange, paginationMode, }: GridHeaderProps) => import("react/jsx-runtime").JSX.Element;
33
- export {};