react-kd-grid 2.1.2 → 2.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (141) hide show
  1. package/dist/hooks/useAdvancedFiltering.d.ts +3 -1
  2. package/dist/index.esm.js +1 -1
  3. package/dist/index.esm.js.map +1 -1
  4. package/dist/index.js +1 -1
  5. package/dist/index.js.map +1 -1
  6. package/package.json +2 -4
  7. package/dist/cjs/CustomGrid.d.ts +0 -3
  8. package/dist/cjs/components/ColumnFilterSelector.d.ts +0 -18
  9. package/dist/cjs/components/CustomSelect.d.ts +0 -14
  10. package/dist/cjs/components/FooterAggregate.d.ts +0 -16
  11. package/dist/cjs/components/GridHeader.d.ts +0 -33
  12. package/dist/cjs/components/GridRows.d.ts +0 -49
  13. package/dist/cjs/components/GroupBar.d.ts +0 -12
  14. package/dist/cjs/components/GroupHeader.d.ts +0 -29
  15. package/dist/cjs/components/NoDataMessage.d.ts +0 -7
  16. package/dist/cjs/components/PaginationControls.d.ts +0 -15
  17. package/dist/cjs/components/Popover.d.ts +0 -17
  18. package/dist/cjs/components/RowContextMenu.d.ts +0 -22
  19. package/dist/cjs/components/SearchToolbar.d.ts +0 -79
  20. package/dist/cjs/components/filters/BooleanFilter.d.ts +0 -7
  21. package/dist/cjs/components/filters/DateFilter.d.ts +0 -9
  22. package/dist/cjs/components/filters/FilterContent.d.ts +0 -9
  23. package/dist/cjs/components/filters/FilterPopup.d.ts +0 -2
  24. package/dist/cjs/components/filters/MultiselectFilter.d.ts +0 -10
  25. package/dist/cjs/components/filters/NumberFilter.d.ts +0 -9
  26. package/dist/cjs/components/filters/TextFilter.d.ts +0 -8
  27. package/dist/cjs/components/filters/index.d.ts +0 -6
  28. package/dist/cjs/components/ui/DatePicker.d.ts +0 -10
  29. package/dist/cjs/constants.d.ts +0 -1
  30. package/dist/cjs/hooks/useAdvancedFiltering.d.ts +0 -16
  31. package/dist/cjs/hooks/useDataWorker.d.ts +0 -10
  32. package/dist/cjs/hooks/useExport.d.ts +0 -15
  33. package/dist/cjs/hooks/useFilteringAndSorting.d.ts +0 -16
  34. package/dist/cjs/hooks/useGrouping.d.ts +0 -28
  35. package/dist/cjs/hooks/usePagination.d.ts +0 -28
  36. package/dist/cjs/hooks/useSelection.d.ts +0 -13
  37. package/dist/cjs/hooks/useVirtualization.d.ts +0 -17
  38. package/dist/cjs/index.d.ts +0 -11
  39. package/dist/cjs/index.js +0 -2
  40. package/dist/cjs/index.js.map +0 -1
  41. package/dist/cjs/types.d.ts +0 -421
  42. package/dist/cjs/utils/highlightText.d.ts +0 -15
  43. package/dist/cjs/workers/dataWorker.d.ts +0 -16
  44. package/dist/core/DataGrid/DataGrid.d.ts +0 -3
  45. package/dist/esm/CustomGrid.d.ts +0 -3
  46. package/dist/esm/CustomGrid.js +0 -2
  47. package/dist/esm/CustomGrid.js.map +0 -1
  48. package/dist/esm/components/ColumnFilterSelector.d.ts +0 -18
  49. package/dist/esm/components/ColumnFilterSelector.js +0 -2
  50. package/dist/esm/components/ColumnFilterSelector.js.map +0 -1
  51. package/dist/esm/components/CustomSelect.d.ts +0 -14
  52. package/dist/esm/components/CustomSelect.js +0 -2
  53. package/dist/esm/components/CustomSelect.js.map +0 -1
  54. package/dist/esm/components/FooterAggregate.d.ts +0 -16
  55. package/dist/esm/components/FooterAggregate.js +0 -2
  56. package/dist/esm/components/FooterAggregate.js.map +0 -1
  57. package/dist/esm/components/GridHeader.d.ts +0 -33
  58. package/dist/esm/components/GridHeader.js +0 -2
  59. package/dist/esm/components/GridHeader.js.map +0 -1
  60. package/dist/esm/components/GridRows.d.ts +0 -49
  61. package/dist/esm/components/GridRows.js +0 -2
  62. package/dist/esm/components/GridRows.js.map +0 -1
  63. package/dist/esm/components/GroupBar.d.ts +0 -12
  64. package/dist/esm/components/GroupBar.js +0 -2
  65. package/dist/esm/components/GroupBar.js.map +0 -1
  66. package/dist/esm/components/GroupHeader.d.ts +0 -29
  67. package/dist/esm/components/GroupHeader.js +0 -2
  68. package/dist/esm/components/GroupHeader.js.map +0 -1
  69. package/dist/esm/components/NoDataMessage.d.ts +0 -7
  70. package/dist/esm/components/NoDataMessage.js +0 -2
  71. package/dist/esm/components/NoDataMessage.js.map +0 -1
  72. package/dist/esm/components/PaginationControls.d.ts +0 -15
  73. package/dist/esm/components/PaginationControls.js +0 -2
  74. package/dist/esm/components/PaginationControls.js.map +0 -1
  75. package/dist/esm/components/Popover.d.ts +0 -17
  76. package/dist/esm/components/Popover.js +0 -2
  77. package/dist/esm/components/Popover.js.map +0 -1
  78. package/dist/esm/components/RowContextMenu.d.ts +0 -22
  79. package/dist/esm/components/SearchToolbar.d.ts +0 -79
  80. package/dist/esm/components/SearchToolbar.js +0 -2
  81. package/dist/esm/components/SearchToolbar.js.map +0 -1
  82. package/dist/esm/components/filters/BooleanFilter.d.ts +0 -7
  83. package/dist/esm/components/filters/BooleanFilter.js +0 -2
  84. package/dist/esm/components/filters/BooleanFilter.js.map +0 -1
  85. package/dist/esm/components/filters/DateFilter.d.ts +0 -9
  86. package/dist/esm/components/filters/DateFilter.js +0 -2
  87. package/dist/esm/components/filters/DateFilter.js.map +0 -1
  88. package/dist/esm/components/filters/FilterContent.d.ts +0 -9
  89. package/dist/esm/components/filters/FilterContent.js +0 -2
  90. package/dist/esm/components/filters/FilterContent.js.map +0 -1
  91. package/dist/esm/components/filters/FilterPopup.d.ts +0 -2
  92. package/dist/esm/components/filters/MultiselectFilter.d.ts +0 -10
  93. package/dist/esm/components/filters/MultiselectFilter.js +0 -2
  94. package/dist/esm/components/filters/MultiselectFilter.js.map +0 -1
  95. package/dist/esm/components/filters/NumberFilter.d.ts +0 -9
  96. package/dist/esm/components/filters/NumberFilter.js +0 -2
  97. package/dist/esm/components/filters/NumberFilter.js.map +0 -1
  98. package/dist/esm/components/filters/TextFilter.d.ts +0 -8
  99. package/dist/esm/components/filters/TextFilter.js +0 -2
  100. package/dist/esm/components/filters/TextFilter.js.map +0 -1
  101. package/dist/esm/components/filters/index.d.ts +0 -6
  102. package/dist/esm/components/ui/DatePicker.d.ts +0 -10
  103. package/dist/esm/components/ui/DatePicker.js +0 -2
  104. package/dist/esm/components/ui/DatePicker.js.map +0 -1
  105. package/dist/esm/constants.d.ts +0 -1
  106. package/dist/esm/constants.js +0 -2
  107. package/dist/esm/constants.js.map +0 -1
  108. package/dist/esm/hooks/useAdvancedFiltering.d.ts +0 -16
  109. package/dist/esm/hooks/useAdvancedFiltering.js +0 -2
  110. package/dist/esm/hooks/useAdvancedFiltering.js.map +0 -1
  111. package/dist/esm/hooks/useDataWorker.d.ts +0 -10
  112. package/dist/esm/hooks/useDataWorker.js +0 -2
  113. package/dist/esm/hooks/useDataWorker.js.map +0 -1
  114. package/dist/esm/hooks/useExport.d.ts +0 -15
  115. package/dist/esm/hooks/useExport.js +0 -2
  116. package/dist/esm/hooks/useExport.js.map +0 -1
  117. package/dist/esm/hooks/useFilteringAndSorting.d.ts +0 -16
  118. package/dist/esm/hooks/useFilteringAndSorting.js +0 -2
  119. package/dist/esm/hooks/useFilteringAndSorting.js.map +0 -1
  120. package/dist/esm/hooks/useGrouping.d.ts +0 -28
  121. package/dist/esm/hooks/useGrouping.js +0 -2
  122. package/dist/esm/hooks/useGrouping.js.map +0 -1
  123. package/dist/esm/hooks/usePagination.d.ts +0 -28
  124. package/dist/esm/hooks/usePagination.js +0 -2
  125. package/dist/esm/hooks/usePagination.js.map +0 -1
  126. package/dist/esm/hooks/useSelection.d.ts +0 -13
  127. package/dist/esm/hooks/useSelection.js +0 -2
  128. package/dist/esm/hooks/useSelection.js.map +0 -1
  129. package/dist/esm/hooks/useVirtualization.d.ts +0 -17
  130. package/dist/esm/hooks/useVirtualization.js +0 -2
  131. package/dist/esm/hooks/useVirtualization.js.map +0 -1
  132. package/dist/esm/index.d.ts +0 -11
  133. package/dist/esm/index.js +0 -2
  134. package/dist/esm/index.js.map +0 -1
  135. package/dist/esm/types.d.ts +0 -421
  136. package/dist/esm/utils/highlightText.d.ts +0 -15
  137. package/dist/esm/utils/highlightText.js +0 -2
  138. package/dist/esm/utils/highlightText.js.map +0 -1
  139. package/dist/esm/workers/dataWorker.d.ts +0 -16
  140. package/dist/hooks/useFilteringAndSorting.d.ts +0 -16
  141. package/dist/workers/dataWorker.d.ts +0 -16
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.js","sources":["../../../../constants.ts","../../../../components/FooterAggregate.tsx","../../../../components/filters/TextFilter.tsx","../../../../components/filters/NumberFilter.tsx","../../../../components/ui/DatePicker.tsx","../../../../components/filters/DateFilter.tsx","../../../../components/filters/MultiselectFilter.tsx","../../../../components/filters/BooleanFilter.tsx","../../../../components/filters/FilterContent.tsx","../../../../components/GridHeader.tsx","../../../../components/GridRows.tsx","../../../../utils/highlightText.tsx","../../../../components/GroupBar.tsx","../../../../components/GroupHeader.tsx","../../../../components/NoDataMessage.tsx","../../../../components/PaginationControls.tsx","../../../../components/CustomSelect.tsx","../../../../components/ColumnFilterSelector.tsx","../../../../components/Popover.tsx","../../../../components/SearchToolbar.tsx","../../../../hooks/useDataWorker.ts","../../../../hooks/useExport.ts","../../../../hooks/usePagination.ts","../../../../hooks/useSelection.ts","../../../../hooks/useVirtualization.ts","../../../../CustomGrid.tsx","../../../../hooks/useAdvancedFiltering.ts","../../../../hooks/useGrouping.ts","../../../../hooks/useFilteringAndSorting.ts"],"sourcesContent":["export const SELECT_COL_WIDTH = 48; // px\n","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","import { useState } from \"react\";\nimport { ColumnFilterValue } from \"../../types\";\n\ninterface TextFilterProps {\n value: ColumnFilterValue | null;\n onChange: (value: ColumnFilterValue | null) => void;\n placeholder?: string;\n}\n\nexport const TextFilter = ({\n value,\n onChange,\n placeholder = \"Enter text to filter...\",\n}: TextFilterProps) => {\n const [textValue, setTextValue] = useState(value?.value || \"\");\n const [operator, setOperator] = useState<\n \"contains\" | \"equals\" | \"startsWith\" | \"endsWith\"\n >((value?.operator as any) || \"contains\");\n\n const handleTextChange = (newText: string) => {\n setTextValue(newText);\n if (newText.trim()) {\n onChange({\n type: \"text\",\n value: newText,\n operator: operator as any,\n });\n } else {\n onChange(null);\n }\n };\n\n const handleOperatorChange = (newOperator: string) => {\n const op = newOperator as \"contains\" | \"equals\" | \"startsWith\" | \"endsWith\";\n setOperator(op);\n if (textValue.trim()) {\n onChange({\n type: \"text\",\n value: textValue,\n operator: op,\n });\n }\n };\n\n return (\n <div className=\"space-y-3\">\n <div>\n <label className=\"block text-sm font-medium text-gray-700 mb-1\">\n Filter Type\n </label>\n <select\n value={operator}\n onChange={(e) => handleOperatorChange(e.target.value)}\n className=\"w-full px-3 py-2 border border-gray-300 rounded-md focus:ring-2 focus:ring-blue-500 focus:border-blue-500\"\n >\n <option value=\"contains\">Contains</option>\n <option value=\"equals\">Equals</option>\n <option value=\"startsWith\">Starts with</option>\n <option value=\"endsWith\">Ends with</option>\n </select>\n </div>\n\n <div>\n <label className=\"block text-sm font-medium text-gray-700 mb-1\">\n Text Value\n </label>\n <input\n type=\"text\"\n value={textValue}\n onChange={(e) => handleTextChange(e.target.value)}\n placeholder={placeholder}\n className=\"w-full px-3 py-2 border border-gray-300 rounded-md focus:ring-2 focus:ring-blue-500 focus:border-blue-500\"\n />\n </div>\n </div>\n );\n};\n","import { useState } from \"react\";\nimport { ColumnFilterValue } from \"../../types\";\n\ninterface NumberFilterProps {\n value: ColumnFilterValue | null;\n onChange: (value: ColumnFilterValue | null) => void;\n min?: number;\n max?: number;\n}\n\nexport const NumberFilter = ({\n value,\n onChange,\n min,\n max,\n}: NumberFilterProps) => {\n const [numberValue, setNumberValue] = useState(value?.value || \"\");\n const [secondValue, setSecondValue] = useState(value?.secondValue || \"\");\n const [operator, setOperator] = useState<\n \"equals\" | \"gt\" | \"gte\" | \"lt\" | \"lte\" | \"between\"\n >((value?.operator as any) || \"equals\");\n\n const handleValueChange = (newValue: string, isSecond = false) => {\n if (isSecond) {\n setSecondValue(newValue);\n } else {\n setNumberValue(newValue);\n }\n\n const numValue = parseFloat(newValue);\n const numSecondValue = parseFloat(secondValue);\n\n if (operator === \"between\") {\n if (!isNaN(numValue) && !isNaN(numSecondValue)) {\n onChange({\n type: \"number\",\n value: Math.min(numValue, numSecondValue),\n secondValue: Math.max(numValue, numSecondValue),\n operator: \"between\",\n });\n } else {\n onChange(null);\n }\n } else {\n if (!isNaN(numValue)) {\n onChange({\n type: \"number\",\n value: numValue,\n operator: operator as any,\n });\n } else {\n onChange(null);\n }\n }\n };\n\n const handleOperatorChange = (newOperator: string) => {\n const op = newOperator as\n | \"equals\"\n | \"gt\"\n | \"gte\"\n | \"lt\"\n | \"lte\"\n | \"between\";\n setOperator(op);\n\n if (op === \"between\") {\n const numValue = parseFloat(numberValue);\n const numSecondValue = parseFloat(secondValue);\n if (!isNaN(numValue) && !isNaN(numSecondValue)) {\n onChange({\n type: \"number\",\n value: Math.min(numValue, numSecondValue),\n secondValue: Math.max(numValue, numSecondValue),\n operator: \"between\",\n });\n }\n } else {\n const numValue = parseFloat(numberValue);\n if (!isNaN(numValue)) {\n onChange({\n type: \"number\",\n value: numValue,\n operator: op,\n });\n }\n }\n };\n\n return (\n <div className=\"space-y-3\">\n <div>\n <label className=\"block text-sm font-medium text-gray-700 mb-1\">\n Filter Type\n </label>\n <select\n value={operator}\n onChange={(e) => handleOperatorChange(e.target.value)}\n className=\"w-full px-3 py-2 border border-gray-300 rounded-md focus:ring-2 focus:ring-blue-500 focus:border-blue-500\"\n >\n <option value=\"equals\">Equals</option>\n <option value=\"gt\">Greater than</option>\n <option value=\"gte\">Greater than or equal</option>\n <option value=\"lt\">Less than</option>\n <option value=\"lte\">Less than or equal</option>\n <option value=\"between\">Between</option>\n </select>\n </div>\n\n <div>\n <label className=\"block text-sm font-medium text-gray-700 mb-1\">\n {operator === \"between\" ? \"From Value\" : \"Number Value\"}\n </label>\n <input\n type=\"number\"\n value={numberValue}\n onChange={(e) => handleValueChange(e.target.value)}\n min={min}\n max={max}\n className=\"w-full px-3 py-2 border border-gray-300 rounded-md focus:ring-2 focus:ring-blue-500 focus:border-blue-500\"\n />\n </div>\n\n {operator === \"between\" && (\n <div>\n <label className=\"block text-sm font-medium text-gray-700 mb-1\">\n To Value\n </label>\n <input\n type=\"number\"\n value={secondValue}\n onChange={(e) => handleValueChange(e.target.value, true)}\n min={min}\n max={max}\n className=\"w-full px-3 py-2 border border-gray-300 rounded-md focus:ring-2 focus:ring-blue-500 focus:border-blue-500\"\n />\n </div>\n )}\n </div>\n );\n};\n","import React from 'react';\n\ninterface DatePickerProps {\n name?: string;\n label?: string;\n value: string | null;\n onChange: (value: string | null) => void;\n className?: string;\n}\n\nexport const DatePicker: React.FC<DatePickerProps> = ({\n name,\n label,\n value,\n onChange,\n className = '',\n}) => {\n return (\n <div className={`space-y-1 ${className}`}>\n {label && (\n <label className=\"text-sm font-medium text-gray-700\">\n {label}\n </label>\n )}\n <input\n type=\"date\"\n name={name}\n value={value || ''}\n onChange={(e) => onChange(e.target.value || null)}\n className=\"w-full px-3 py-2 border border-gray-300 rounded-md text-sm focus:ring-2 focus:ring-blue-500 focus:border-blue-500\"\n />\n </div>\n );\n};","import { useState, useEffect, useMemo } from \"react\";\nimport { ColumnFilterValue } from \"../../types\";\nimport { DatePicker } from \"../ui/DatePicker\";\nimport { format, parse } from \"date-fns\";\n\ninterface DateFilterProps {\n value: ColumnFilterValue | null;\n onChange: (value: ColumnFilterValue | null) => void;\n min?: number;\n max?: number;\n}\n\nexport const DateFilter = ({ value, onChange, min, max }: DateFilterProps) => {\n // Local UI state uses Date objects; stored values use \"yyyy-MM-dd\"\n const toDateObj = (v?: string | Date | null): Date | null => {\n if (!v) return null;\n if (v instanceof Date) return isNaN(v.getTime()) ? null : v;\n // Try parse yyyy-MM-dd first\n try {\n const d = parse(String(v), \"yyyy-MM-dd\", new Date());\n return isNaN(d.getTime()) ? null : d;\n } catch (e) {\n const d = new Date(v);\n return isNaN(d.getTime()) ? null : d;\n }\n };\n const toStored = (d: Date | null): string =>\n d ? format(d, \"yyyy-MM-dd\") : \"\";\n\n const [dateValue, setDateValue] = useState<Date | null>(\n toDateObj(value?.value as any),\n );\n const [secondDateValue, setSecondDateValue] = useState<Date | null>(\n value?.operator === \"between\"\n ? toDateObj((value?.secondValue as any) ?? (value?.value as any))\n : null,\n );\n const [operator, setOperator] = useState<\n \"equals\" | \"gt\" | \"gte\" | \"lt\" | \"lte\" | \"between\"\n >((value?.operator as any) || \"between\");\n\n // Sync local state when parent value changes (e.g., clear/apply from outside)\n useEffect(() => {\n setOperator((value?.operator as any) || \"between\");\n setDateValue(toDateObj(value?.value as any));\n setSecondDateValue(\n value?.operator === \"between\"\n ? toDateObj((value?.secondValue as any) ?? (value?.value as any))\n : null,\n );\n }, [value?.value, value?.secondValue, value?.operator]);\n\n // Handle operator changes\n useEffect(() => {\n if (operator === \"between\" && !secondDateValue && dateValue) {\n // If switching to between mode and second date is not set, initialize it with first date\n setSecondDateValue(dateValue);\n }\n }, [operator, dateValue, secondDateValue]);\n\n const clampDate = (date: Date | null, minDate?: Date, maxDate?: Date) => {\n if (!date) return null;\n if (minDate && date < minDate) return null;\n if (maxDate && date > maxDate) return null;\n return date;\n };\n\n const handleDateChange = (newDate: Date | null, isSecond = false) => {\n const nextFirst = isSecond ? dateValue : newDate;\n const nextSecond = isSecond ? newDate : secondDateValue;\n\n const maxForFirst =\n operator === \"between\" && secondDateValue ? secondDateValue : maxDateObj;\n const minForSecond = dateValue ?? minDateObj ?? undefined;\n\n const clampedDate = isSecond\n ? clampDate(newDate, minForSecond, maxDateObj)\n : clampDate(newDate, minDateObj, maxForFirst);\n\n if (isSecond) setSecondDateValue(clampedDate);\n else setDateValue(clampedDate);\n\n if (operator === \"between\") {\n if (nextFirst && nextSecond) {\n const start = nextFirst <= nextSecond ? nextFirst : nextSecond;\n const end = nextFirst >= nextSecond ? nextFirst : nextSecond;\n onChange({\n type: \"date\",\n value: toStored(start),\n secondValue: toStored(end),\n operator: \"between\",\n });\n } else {\n onChange(null);\n }\n } else {\n if (clampedDate) {\n onChange({\n type: \"date\",\n value: toStored(clampedDate),\n operator: operator as any,\n });\n } else {\n onChange(null);\n }\n }\n };\n\n const handleOperatorChange = (newOperator: string) => {\n const op = newOperator as\n | \"equals\"\n | \"gt\"\n | \"gte\"\n | \"lt\"\n | \"lte\"\n | \"between\";\n setOperator(op);\n\n if (op === \"between\") {\n if (dateValue && secondDateValue) {\n const start =\n dateValue <= secondDateValue ? dateValue : secondDateValue;\n const end = dateValue >= secondDateValue ? dateValue : secondDateValue;\n onChange({\n type: \"date\",\n value: start,\n secondValue: end,\n operator: \"between\",\n });\n }\n } else {\n if (dateValue) {\n onChange({\n type: \"date\",\n value: dateValue,\n operator: op,\n });\n }\n }\n };\n\n const minDateObj = useMemo(() => (min ? new Date(min) : undefined), [min]);\n const maxDateObj = useMemo(() => (max ? new Date(max) : undefined), [max]);\n\n return (\n <div className=\"space-y-3\">\n <div>\n <label className=\"block text-sm font-medium text-gray-700 mb-1\">\n Filter Type\n </label>\n <select\n value={operator}\n onChange={(e) => handleOperatorChange(e.target.value)}\n className=\"w-full px-3 py-2 border border-gray-300 rounded-md focus:ring-2 focus:ring-blue-500 focus:border-blue-500\"\n >\n <option value=\"equals\">On Date</option>\n <option value=\"gt\">After</option>\n <option value=\"gte\">On or After</option>\n <option value=\"lt\">Before</option>\n <option value=\"lte\">On or Before</option>\n <option value=\"between\">Between Dates</option>\n </select>\n </div>\n\n <div>\n <label className=\"block text-sm font-medium text-gray-700 mb-1\">\n {operator === \"between\" ? \"From Date\" : \"Date\"}\n </label>\n <DatePicker\n name=\"date_filter_start\"\n value={dateValue ? format(dateValue, \"yyyy-MM-dd\") : null}\n onChange={(d) =>\n handleDateChange(\n d ? parse(d, \"yyyy-MM-dd\", new Date()) : null,\n false,\n )\n }\n className=\"w-full\"\n />\n </div>\n\n {operator === \"between\" && (\n <div>\n <label className=\"block text-sm font-medium text-gray-700 mb-1\">\n To Date\n </label>\n <DatePicker\n name=\"date_filter_end\"\n value={\n secondDateValue ? format(secondDateValue, \"yyyy-MM-dd\") : null\n }\n onChange={(d) =>\n handleDateChange(\n d ? parse(d, \"yyyy-MM-dd\", new Date()) : null,\n true,\n )\n }\n className=\"w-full\"\n />\n </div>\n )}\n </div>\n );\n};\n","import { useState, useMemo, useEffect } from \"react\";\nimport { Search, Check } from \"lucide-react\";\nimport { ColumnFilterValue, FilterOption, GridRow } from \"../../types\";\n\ninterface MultiselectFilterProps {\n value: ColumnFilterValue | null;\n onChange: (value: ColumnFilterValue | null) => void;\n options?: FilterOption[];\n data: GridRow[];\n columnKey: string;\n}\n\nexport const MultiselectFilter = ({\n value,\n onChange,\n options,\n data,\n columnKey,\n}: MultiselectFilterProps) => {\n const [searchText, setSearchText] = useState(\"\");\n const [selectedValues, setSelectedValues] = useState<Set<any>>(\n new Set(value?.value || [])\n );\n\n // Sync selectedValues when value prop changes\n useEffect(() => {\n setSelectedValues(new Set(value?.value || []));\n }, [value]);\n\n // Generate options from data if not provided\n const availableOptions = useMemo(() => {\n let opts: FilterOption[] = [];\n\n // Helper to check if a value is blank\n const isBlankValue = (val: any): boolean => {\n return (\n val === null ||\n val === undefined ||\n val === \"\" ||\n (typeof val === \"string\" && val.trim() === \"\")\n );\n };\n\n if (options && options.length > 0) {\n opts = options;\n } else {\n // Normalize blank values to null and collect unique values\n const normalizedValues = data.map((row) => {\n const val = row[columnKey];\n return isBlankValue(val) ? null : val;\n });\n\n const uniqueValues = [...new Set(normalizedValues)];\n\n opts = uniqueValues.map((val) => ({\n label: val === null ? \"Blank\" : String(val),\n value: val,\n }));\n }\n\n // Transform blank/null/empty values to display as \"Blank\"\n // and sort them to the end of the list\n const processedOptions = opts.map((opt) => {\n const isBlank = isBlankValue(opt.value);\n\n return {\n ...opt,\n label: isBlank ? \"Blank\" : opt.label,\n value: isBlank ? null : opt.value, // Normalize all blank values to null\n isBlank,\n };\n });\n\n // Remove duplicates after normalization (in case options had multiple blank types)\n const uniqueProcessed = Array.from(\n new Map(\n processedOptions.map((opt) => [\n opt.isBlank ? \"__blank__\" : opt.value,\n opt,\n ])\n ).values()\n );\n\n // Sort: non-blank items first (alphabetically), then blank items at the end\n return uniqueProcessed.sort((a, b) => {\n if (a.isBlank && !b.isBlank) return 1;\n if (!a.isBlank && b.isBlank) return -1;\n return String(a.label).localeCompare(String(b.label));\n });\n }, [options, data, columnKey]);\n\n // Filter options based on search\n const filteredOptions = useMemo(() => {\n if (!searchText) return availableOptions;\n const searchLower = searchText.toLowerCase();\n return availableOptions.filter(\n (option) =>\n option.label.toLowerCase().includes(searchLower) ||\n String(option.value).toLowerCase().includes(searchLower) ||\n (searchLower === \"blank\" && (option as any).isBlank)\n );\n }, [availableOptions, searchText]);\n\n const handleToggleValue = (optionValue: any) => {\n const newSelected = new Set(selectedValues);\n\n if (newSelected.has(optionValue)) {\n newSelected.delete(optionValue);\n } else {\n newSelected.add(optionValue);\n }\n\n setSelectedValues(newSelected);\n\n // Update the temp filter state but don't apply immediately\n if (newSelected.size > 0) {\n onChange({\n type: \"multiselect\",\n value: Array.from(newSelected),\n operator: \"equals\",\n });\n } else {\n onChange(null);\n }\n };\n\n const handleSelectAll = () => {\n const allValues = new Set(filteredOptions.map((opt) => opt.value));\n setSelectedValues(allValues);\n onChange({\n type: \"multiselect\",\n value: Array.from(allValues),\n operator: \"equals\",\n });\n };\n\n const handleClearAll = () => {\n setSelectedValues(new Set());\n onChange(null);\n };\n\n return (\n <div className=\"space-y-3\">\n {/* Search */}\n <div className=\"relative\">\n <Search className=\"absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400 w-4 h-4\" />\n <input\n type=\"text\"\n value={searchText}\n onChange={(e) => setSearchText(e.target.value)}\n placeholder=\"Search options...\"\n className=\"w-full pl-10 pr-4 py-2 border border-gray-300 rounded-md focus:ring-2 focus:ring-blue-500 focus:border-blue-500\"\n />\n </div>\n\n {/* Actions */}\n <div className=\"flex justify-between items-center text-sm\">\n <span className=\"text-gray-600\">\n {selectedValues.size} of {availableOptions.length} selected\n </span>\n <div className=\"flex gap-2\">\n <button\n onClick={handleSelectAll}\n className=\"text-blue-600 hover:text-blue-800 transition-colors\"\n >\n Select All\n </button>\n <button\n onClick={handleClearAll}\n className=\"text-gray-600 hover:text-gray-800 transition-colors\"\n >\n Clear All\n </button>\n </div>\n </div>\n\n {/* Options List */}\n <div className=\"max-h-48 overflow-y-auto border border-gray-200 rounded-md\">\n {filteredOptions.length === 0 ? (\n <div className=\"p-3 text-sm text-gray-500 text-center\">\n No options found\n </div>\n ) : (\n filteredOptions.map((option, index) => {\n const isSelected = selectedValues.has(option.value);\n return (\n <div\n key={index}\n onClick={() => handleToggleValue(option.value)}\n className={`flex items-center gap-3 p-2 cursor-pointer hover:bg-gray-50 transition-colors ${\n isSelected ? \"bg-blue-50\" : \"\"\n }`}\n >\n <div\n className={`w-4 h-4 border-2 rounded flex items-center justify-center ${\n isSelected\n ? \"bg-blue-600 border-blue-600 text-white\"\n : \"border-gray-300\"\n }`}\n >\n {isSelected && <Check className=\"w-3 h-3\" />}\n </div>\n <span\n className={`text-sm ${\n isSelected ? \"text-blue-900 font-medium\" : \"text-gray-700\"\n } ${(option as any).isBlank ? \"italic text-gray-500\" : \"\"}`}\n >\n {option.label}\n </span>\n </div>\n );\n })\n )}\n </div>\n </div>\n );\n};\n","import { useState } from \"react\";\nimport { ColumnFilterValue } from \"../../types\";\n\ninterface BooleanFilterProps {\n value: ColumnFilterValue | null;\n onChange: (value: ColumnFilterValue | null) => void;\n}\n\nexport const BooleanFilter = ({ value, onChange }: BooleanFilterProps) => {\n const [booleanValue, setBooleanValue] = useState<string>(\n value?.value !== undefined ? String(value.value) : \"\"\n );\n\n const handleValueChange = (newValue: string) => {\n setBooleanValue(newValue);\n\n if (newValue === \"\") {\n onChange(null);\n } else {\n onChange({\n type: \"boolean\",\n value: newValue === \"true\",\n operator: \"equals\",\n });\n }\n };\n\n return (\n <div className=\"space-y-3\">\n <div>\n <label className=\"block text-sm font-medium text-gray-700 mb-1\">\n Boolean Value\n </label>\n <select\n value={booleanValue}\n onChange={(e) => handleValueChange(e.target.value)}\n className=\"w-full px-3 py-2 border border-gray-300 rounded-md focus:ring-2 focus:ring-blue-500 focus:border-blue-500\"\n >\n <option value=\"\">All values</option>\n <option value=\"true\">True</option>\n <option value=\"false\">False</option>\n </select>\n </div>\n </div>\n );\n};\n","import { useMemo } from \"react\";\nimport { ColumnFilterValue, ColumnFilter, GridColumn, GridRow } from \"../../types\";\nimport { TextFilter } from \"./TextFilter\";\nimport { NumberFilter } from \"./NumberFilter\";\nimport { DateFilter } from \"./DateFilter\";\nimport { MultiselectFilter } from \"./MultiselectFilter\";\nimport { BooleanFilter } from \"./BooleanFilter\";\n\ninterface FilterContentProps {\n column: GridColumn;\n data: GridRow[];\n value: ColumnFilterValue | null;\n onChange: (val: ColumnFilterValue | null) => void;\n}\n\nexport const FilterContent = ({ column, data, value, onChange }: FilterContentProps) => {\n const filterConfig: ColumnFilter = useMemo(() => {\n if (typeof column.filterable === \"object\") return column.filterable;\n\n // Auto-detect filter type based on data\n const sampleValues = data\n .slice(0, 100)\n .map((row) => row[column.key])\n .filter((val) => val != null);\n\n if (sampleValues.length === 0) return { type: \"text\" };\n\n const firstValue = sampleValues[0];\n if (typeof firstValue === \"boolean\") return { type: \"boolean\" };\n if (typeof firstValue === \"number\") return { type: \"number\" };\n if (\n firstValue instanceof Date ||\n (typeof firstValue === \"string\" && !isNaN(Date.parse(firstValue)))\n ) {\n return { type: \"date\" };\n }\n\n const uniqueValues = [...new Set(sampleValues)];\n if (uniqueValues.length <= 20) {\n return {\n type: \"multiselect\",\n options: uniqueValues.map((val) => ({ label: String(val), value: val })),\n };\n }\n\n return { type: \"text\" };\n }, [column.filterable, column.key, data]);\n\n switch (filterConfig.type) {\n case \"text\":\n return (\n <TextFilter value={value} onChange={onChange} placeholder={filterConfig.placeholder} />\n );\n case \"number\":\n return (\n <NumberFilter value={value} onChange={onChange} min={filterConfig.min} max={filterConfig.max} />\n );\n case \"date\":\n return (\n <DateFilter value={value} onChange={onChange} min={filterConfig.min} max={filterConfig.max} />\n );\n case \"multiselect\":\n return (\n <MultiselectFilter\n value={value}\n onChange={onChange}\n options={filterConfig.options || []}\n data={data}\n columnKey={column.key}\n />\n );\n case \"boolean\":\n return <BooleanFilter value={value} onChange={onChange} />;\n default:\n return null;\n }\n};\n","import { useEffect, useState } from \"react\";\nimport type { MouseEvent as ReactMouseEvent } from \"react\";\nimport {\n Filter,\n FilterX,\n Menu,\n Pin,\n PinOff,\n Group,\n Ungroup,\n ArrowUp,\n ArrowDown,\n ArrowUpDown,\n} from \"lucide-react\";\nimport {\n GridColumn,\n SortConfig,\n ActiveFilters,\n ColumnFilterValue,\n GridRow,\n} from \"../types\";\nimport { SELECT_COL_WIDTH } from \"../constants\";\nimport { FilterContent } from \"./filters/FilterContent\";\n\ninterface GridHeaderProps {\n pinnedColumns: GridColumn[];\n unpinnedColumns: GridColumn[];\n hvPadLeft?: number;\n hvPadRight?: number;\n // Fixed header height in px (use density-controlled row height)\n headerHeight: number;\n sortConfig: SortConfig;\n columnFilters: ActiveFilters;\n selectable: boolean;\n selectedRows: Set<string | number>;\n totalRows: number;\n data: GridRow[];\n onSort: (key: string) => void;\n onColumnFilter: (key: string, filter: ColumnFilterValue | null) => void;\n onSelectAll: () => void;\n onColumnResize?: (columnKey: string, width: number) => void;\n pinnedKeySet?: Set<string>;\n onColumnPin?: (columnKey: string, pinned: boolean) => void;\n groupable?: boolean;\n // Back-compat single grouped column\n groupedByColumn?: string | null;\n onGroupBy?: (columnKey: string | null) => void;\n // New multi-group API\n groupedByColumns?: string[];\n onGroupToggle?: (columnKey: string, nextGrouped: boolean) => void;\n onAutosizeColumn?: (columnKey: string) => void;\n onAutosizeAllColumns?: () => void;\n onResetColumns?: () => void;\n // Reorder props\n columnOrder: string[];\n onColumnOrderChange: (order: string[]) => void;\n // Pagination mode for auto-apply filtering\n paginationMode?: \"client\" | \"server\" | null;\n}\n\nexport const GridHeader = ({\n pinnedColumns,\n unpinnedColumns,\n hvPadLeft = 0,\n hvPadRight = 0,\n headerHeight,\n sortConfig,\n columnFilters,\n selectable,\n selectedRows,\n totalRows,\n data,\n onSort,\n onColumnFilter,\n onSelectAll,\n onColumnResize,\n pinnedKeySet = new Set<string>(),\n onColumnPin,\n groupable = false,\n groupedByColumn,\n onGroupBy,\n groupedByColumns = [],\n onGroupToggle,\n onAutosizeColumn,\n onAutosizeAllColumns,\n onResetColumns,\n columnOrder,\n onColumnOrderChange,\n paginationMode = \"client\",\n}: GridHeaderProps) => {\n const allColumns = [...pinnedColumns, ...unpinnedColumns];\n const [activeColumnMenu, setActiveColumnMenu] = useState<string | null>(null);\n const [menuPosition, setMenuPosition] = useState<{\n top: number;\n left: number;\n containerRect: DOMRect;\n } | null>(null);\n const [isResizing, setIsResizing] = useState<string | null>(null);\n const [startX, setStartX] = useState<number>(0);\n const [startWidth, setStartWidth] = useState<number>(0);\n const [activeTab, setActiveTab] = useState<\"menu\" | \"filter\" | \"reorder\">(\n \"menu\"\n );\n const [tempFilter, setTempFilter] = useState<ColumnFilterValue | null>(null);\n const [isTouchDevice, setIsTouchDevice] = useState(false);\n\n useEffect(() => {\n setIsTouchDevice(\n \"ontouchstart\" in window ||\n navigator.maxTouchPoints > 0 ||\n window.matchMedia(\"(pointer: coarse)\").matches\n );\n }, []);\n\n // Reorder-in-menu state\n const [menuReorderDraggingKey, setMenuReorderDraggingKey] = useState<\n string | null\n >(null);\n const [menuReorderDragOver, setMenuReorderDragOver] = useState<{\n key: string;\n position: \"before\" | \"after\";\n } | null>(null);\n\n const isAllSelected = selectedRows.size === totalRows && totalRows > 0;\n const isIndeterminate =\n selectedRows.size > 0 && selectedRows.size < totalRows;\n\n const handleColumnMenuClick = (\n columnKey: string,\n event: ReactMouseEvent<HTMLButtonElement>\n ) => {\n event.stopPropagation();\n if (activeColumnMenu === columnKey) {\n setActiveColumnMenu(null);\n } else {\n setActiveColumnMenu(columnKey);\n setActiveTab(\"menu\");\n setTempFilter(columnFilters[columnKey] || null);\n\n // Store button position and clamp to grid container bounds\n const rect = event.currentTarget.getBoundingClientRect();\n // Find the nearest grid container for boundary clamping\n const containerEl = event.currentTarget.closest(\n \"[data-grid-container]\"\n ) as HTMLElement | null;\n const containerRect = containerEl\n ? containerEl.getBoundingClientRect()\n : new DOMRect(0, 0, window.innerWidth, window.innerHeight);\n\n const MENU_WIDTH = 320; // matches dropdown width below\n const PADDING = 10;\n\n // Clamp left so the menu stays inside the grid container horizontally\n const minLeft = containerRect.left + PADDING;\n const maxLeft = containerRect.right - MENU_WIDTH - PADDING;\n const clampedLeft = Math.max(minLeft, Math.min(rect.left, maxLeft));\n\n setMenuPosition({\n top: rect.bottom + 4,\n left: clampedLeft,\n containerRect,\n });\n }\n };\n\n const handleApplyFilter = (columnKey: string) => {\n onColumnFilter(columnKey, tempFilter || null);\n setActiveColumnMenu(null);\n };\n\n const hasActiveFilter = (columnKey: string) => {\n return columnFilters[columnKey] != null;\n };\n\n const handleResizeStart = (columnKey: string, event: ReactMouseEvent) => {\n event.preventDefault();\n setIsResizing(columnKey);\n setStartX(event.clientX);\n const column = [...pinnedColumns, ...unpinnedColumns].find(\n (col) => col.key === columnKey\n );\n setStartWidth(column?.width || 100);\n };\n\n const handleResizeMove = (event: globalThis.MouseEvent) => {\n if (!isResizing || !onColumnResize) return;\n\n const deltaX = event.clientX - startX;\n const newWidth = Math.max(50, startWidth + deltaX); // Minimum width of 50px\n onColumnResize(isResizing, newWidth);\n };\n\n const handleResizeEnd = () => {\n setIsResizing(null);\n setStartX(0);\n setStartWidth(0);\n };\n\n // Drag & Drop state for reordering\n const [draggingKey, setDraggingKey] = useState<string | null>(null);\n const [dragOver, setDragOver] = useState<{\n key: string;\n position: \"before\" | \"after\";\n } | null>(null);\n\n // (no-op helper removed; ordering is computed inline in handleDrop)\n\n const handleDragStart = (\n columnKey: string,\n e: React.DragEvent<HTMLDivElement>\n ) => {\n setDraggingKey(columnKey);\n e.dataTransfer.effectAllowed = \"move\";\n e.dataTransfer.setData(\"text/plain\", columnKey);\n };\n\n const handleDragOver = (\n targetKey: string,\n e: React.DragEvent<HTMLDivElement>\n ) => {\n // Allow drop\n e.preventDefault();\n e.dataTransfer.dropEffect = \"move\";\n const rect = (e.currentTarget as HTMLDivElement).getBoundingClientRect();\n const midpoint = rect.left + rect.width / 2;\n const position = e.clientX < midpoint ? \"before\" : \"after\";\n setDragOver({ key: targetKey, position });\n };\n\n const handleDrop = (targetKey: string) => {\n const sourceKey = draggingKey;\n if (!sourceKey || sourceKey === targetKey) return;\n\n // Prevent cross pinned/unpinned moves for clearer UX\n const sourcePinned = pinnedKeySet.has(sourceKey);\n const targetPinned = pinnedKeySet.has(targetKey);\n if (sourcePinned !== targetPinned) {\n setDraggingKey(null);\n setDragOver(null);\n return;\n }\n\n const before =\n dragOver?.key === targetKey ? dragOver?.position === \"before\" : true;\n const base = columnOrder.filter((k) => k !== sourceKey);\n let idx = base.indexOf(targetKey);\n if (idx === -1) {\n onColumnOrderChange(base);\n setDraggingKey(null);\n setDragOver(null);\n return;\n }\n if (!before) idx += 1;\n base.splice(idx, 0, sourceKey);\n const next = base;\n onColumnOrderChange(next);\n setDraggingKey(null);\n setDragOver(null);\n };\n\n const handleDragEnd = () => {\n setDraggingKey(null);\n setDragOver(null);\n };\n\n // --- Reorder handlers for the popup list ---\n const handleMenuReorderDragStart = (\n columnKey: string,\n e: React.DragEvent<HTMLDivElement>\n ) => {\n setMenuReorderDraggingKey(columnKey);\n e.dataTransfer.effectAllowed = \"move\";\n e.dataTransfer.setData(\"text/plain\", columnKey);\n };\n\n const handleMenuReorderDragOver = (\n targetKey: string,\n e: React.DragEvent<HTMLDivElement>\n ) => {\n e.preventDefault();\n e.dataTransfer.dropEffect = \"move\";\n const rect = (e.currentTarget as HTMLDivElement).getBoundingClientRect();\n const midpoint = rect.top + rect.height / 2;\n const position = e.clientY < midpoint ? \"before\" : \"after\";\n setMenuReorderDragOver({ key: targetKey, position });\n };\n\n const handleMenuReorderDrop = (targetKey: string) => {\n const sourceKey = menuReorderDraggingKey;\n if (!sourceKey || sourceKey === targetKey) return;\n\n // Prevent cross pinned/unpinned moves\n const sourcePinned = pinnedKeySet.has(sourceKey);\n const targetPinned = pinnedKeySet.has(targetKey);\n if (sourcePinned !== targetPinned) {\n setMenuReorderDraggingKey(null);\n setMenuReorderDragOver(null);\n return;\n }\n\n const before =\n menuReorderDragOver?.key === targetKey\n ? menuReorderDragOver?.position === \"before\"\n : true;\n const base = columnOrder.filter((k) => k !== sourceKey);\n let idx = base.indexOf(targetKey);\n if (idx === -1) {\n onColumnOrderChange(base);\n setMenuReorderDraggingKey(null);\n setMenuReorderDragOver(null);\n return;\n }\n if (!before) idx += 1;\n base.splice(idx, 0, sourceKey);\n onColumnOrderChange(base);\n setMenuReorderDraggingKey(null);\n setMenuReorderDragOver(null);\n };\n\n const handleMenuReorderDragEnd = () => {\n setMenuReorderDraggingKey(null);\n setMenuReorderDragOver(null);\n };\n\n // Add global mouse event listeners for resizing\n useEffect(() => {\n if (isResizing) {\n document.addEventListener(\"mousemove\", handleResizeMove);\n document.addEventListener(\"mouseup\", handleResizeEnd);\n document.body.style.cursor = \"col-resize\";\n document.body.style.userSelect = \"none\";\n\n return () => {\n document.removeEventListener(\"mousemove\", handleResizeMove);\n document.removeEventListener(\"mouseup\", handleResizeEnd);\n document.body.style.cursor = \"\";\n document.body.style.userSelect = \"\";\n };\n }\n }, [isResizing, startX, startWidth, onColumnResize]);\n\n // Close menu on outside click\n useEffect(() => {\n const handleClickOutside = (event: MouseEvent) => {\n const target = event.target as Element;\n if (activeColumnMenu && !target.closest(\"[data-column-menu]\")) {\n setActiveColumnMenu(null);\n }\n };\n\n if (activeColumnMenu) {\n document.addEventListener(\"mousedown\", handleClickOutside);\n return () =>\n document.removeEventListener(\"mousedown\", handleClickOutside);\n }\n }, [activeColumnMenu]);\n\n return (\n <>\n <div\n className=\"bg-blue-50 dark:bg-gray-800 border-b border-gray-200 dark:border-gray-700\"\n style={{ height: headerHeight }}\n role=\"row\"\n aria-label=\"Column headers\"\n >\n <div className=\"flex h-full\">\n {selectable && (\n <div\n className=\"px-3 border-r border-gray-200 dark:border-gray-700 flex items-center justify-center bg-blue-50 dark:bg-gray-800 sticky left-0 z-3 h-full\"\n style={{ width: SELECT_COL_WIDTH, height: headerHeight }}\n role=\"columnheader\"\n aria-label=\"Select all rows\"\n >\n <input\n type=\"checkbox\"\n checked={isAllSelected}\n ref={(el) => {\n if (el) el.indeterminate = isIndeterminate;\n }}\n onChange={onSelectAll}\n onKeyDown={(e) => {\n if (e.key === \" \" || e.key === \"Enter\") {\n e.preventDefault();\n onSelectAll();\n }\n }}\n className=\"rounded border-gray-300 dark:border-gray-600 text-blue-600 cursor-pointer disabled:cursor-not-allowed disabled:opacity-50 h-4 w-4 transition-all duration-150\"\n aria-label={\n isAllSelected\n ? \"Deselect all rows\"\n : isIndeterminate\n ? `${selectedRows.size} rows selected. Click to select all`\n : \"Select all rows\"\n }\n title={\n isAllSelected\n ? `Deselect all ${totalRows} rows`\n : isIndeterminate\n ? `${selectedRows.size} of ${totalRows} rows selected`\n : `Select all ${totalRows} rows`\n }\n />\n </div>\n )}\n\n {/* Pinned columns */}\n {pinnedColumns.map((column, index) => {\n const isPinned = pinnedKeySet.has(column.key);\n const pinnedKeysInOrder = pinnedColumns\n .filter((c) => pinnedKeySet.has(c.key))\n .map((c) => c.key);\n const pinnedIndex = pinnedKeysInOrder.indexOf(column.key);\n const leftOffset = isPinned\n ? pinnedKeysInOrder.slice(0, pinnedIndex).reduce(\n (sum, key) => {\n const col = pinnedColumns.find((c) => c.key === key);\n return sum + (col?.width || 100);\n },\n selectable ? SELECT_COL_WIDTH : 0\n )\n : 0;\n\n const headerJustify =\n column.headerAlign === \"center\"\n ? \"justify-center\"\n : column.headerAlign === \"right\"\n ? \"justify-end\"\n : \"justify-start\";\n\n // Determine effective sortable (default true unless explicitly false)\n const colSortable = column.sortable !== false;\n return (\n <div\n key={column.key + index}\n className={`flex-none border-r border-gray-200 dark:border-gray-700 last:border-r-0 relative ${\n isPinned\n ? \"bg-blue-50 dark:bg-blue-900/20 border-blue-200 dark:border-blue-700 sticky z-2\"\n : \"\"\n } `}\n style={{\n width: column.width || 100,\n left: isPinned ? `${leftOffset}px` : \"auto\",\n height: headerHeight,\n }}\n data-column-key={column.key}\n draggable\n onDragStart={(e) => handleDragStart(column.key, e)}\n onDragOver={(e) => handleDragOver(column.key, e)}\n onDrop={() => handleDrop(column.key)}\n onDragEnd={handleDragEnd}\n >\n {/* Drop indicator */}\n {dragOver?.key === column.key && (\n <div\n className=\"absolute top-0 bottom-0 w-0.5 bg-blue-600 z-4\"\n style={{\n left: dragOver?.position === \"before\" ? 0 : undefined,\n right: dragOver?.position === \"after\" ? 0 : undefined,\n }}\n />\n )}\n <div className=\"px-3 h-full\">\n {/* Header with sort */}\n <div className=\"flex items-center justify-between gap-1 group h-full\">\n <div\n className={`flex items-center min-w-0 flex-1 ${headerJustify} ${\n colSortable ? \"cursor-pointer hover:text-blue-600\" : \"\"\n }`}\n onClick={() => colSortable && onSort(column.key)}\n >\n <div className=\"flex items-center gap-2 min-w-0\">\n {/* Filter indicator */}\n {hasActiveFilter(column.key) && (\n <div\n className=\"w-2 h-2 bg-red-500 rounded-full shrink-0\"\n title=\"Filter applied\"\n />\n )}\n <span className=\"font-medium text-gray-900 dark:text-gray-100 text-sm truncate min-w-0\">\n {column.header}\n </span>\n </div>\n {colSortable && sortConfig.key === column.key && (\n <div className=\"ml-1 shrink-0 pointer-events-none select-none\">\n {sortConfig.direction === \"asc\" ? (\n <ArrowUp className=\"w-3 h-3 text-blue-600 dark:text-blue-400\" />\n ) : (\n <ArrowDown className=\"w-3 h-3 text-blue-600 dark:text-blue-400\" />\n )}\n </div>\n )}\n </div>\n\n {/* Three-dot menu */}\n {(column.filterable || onColumnPin || groupable) && (\n <div\n className={`relative ${\n activeColumnMenu === column.key || isTouchDevice\n ? \"block\"\n : \"hidden group-hover:block\"\n }`}\n data-column-menu\n >\n <button\n className=\"p-1 hover:bg-gray-200 dark:hover:bg-gray-700 rounded shrink-0\"\n onClick={(e) => handleColumnMenuClick(column.key, e)}\n data-menu-trigger\n >\n <Menu className=\"w-4 h-4 text-gray-500 dark:text-gray-400\" />\n </button>\n\n {/* Dropdown Menu with Tabs */}\n {activeColumnMenu === column.key && menuPosition && (\n <div\n className=\"fixed bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-md shadow-lg z-1000 w-[320px] overflow-auto\"\n style={{\n top: `${menuPosition.top}px`,\n left: `${menuPosition.left}px`,\n // Ensure the menu height never exceeds available space within the grid container\n maxHeight: `${Math.max(\n 160,\n menuPosition.containerRect.bottom -\n 12 -\n menuPosition.top\n )}px`,\n }}\n data-column-menu\n >\n {/* Tabs */}\n <div className=\"flex items-center border-b border-gray-200 dark:border-gray-700\">\n <button\n className={`px-3 py-2 text-sm flex items-center gap-2 ${\n activeTab === \"menu\"\n ? \"border-b-2 border-blue-600 text-blue-600\"\n : \"text-gray-600\"\n }`}\n onClick={() => setActiveTab(\"menu\")}\n >\n <Menu className=\"w-4 h-4\" />\n </button>\n {column.filterable && (\n <button\n className={`px-3 py-2 text-sm flex items-center gap-2 ${\n activeTab === \"filter\"\n ? \"border-b-2 border-blue-600 text-blue-600\"\n : \"text-gray-600\"\n }`}\n onClick={() => setActiveTab(\"filter\")}\n >\n <Filter className=\"w-4 h-4\" />\n </button>\n )}\n <button\n className={`px-3 py-2 text-sm flex items-center gap-2 ${\n activeTab === \"reorder\"\n ? \"border-b-2 border-blue-600 text-blue-600\"\n : \"text-gray-600\"\n }`}\n onClick={() => setActiveTab(\"reorder\")}\n >\n <ArrowUpDown className=\"w-4 h-4\" />\n </button>\n </div>\n {/* Tab Content */}\n {activeTab === \"menu\" && (\n <div className=\"py-1\">\n {onColumnPin &&\n (groupedByColumn === column.key ? (\n <div className=\"w-full px-3 py-2 text-left text-sm flex items-center gap-2 text-blue-600\">\n <Pin className=\"w-4 h-4\" />\n Pinned by grouping\n </div>\n ) : (\n <button\n className=\"w-full px-3 py-2 text-left text-sm hover:bg-gray-50 flex items-center gap-2\"\n onClick={(e) => {\n e.stopPropagation();\n setActiveColumnMenu(null);\n onColumnPin?.(\n column.key,\n !pinnedKeySet.has(column.key)\n );\n }}\n >\n {pinnedKeySet.has(column.key) ? (\n <>\n <PinOff className=\"w-4 h-4\" />\n Unpin Column\n </>\n ) : (\n <>\n <Pin className=\"w-4 h-4\" />\n Pin Column\n </>\n )}\n </button>\n ))}\n\n {onAutosizeColumn && (\n <button\n className=\"w-full px-3 py-2 text-left text-sm hover:bg-gray-50\"\n onClick={() => {\n onAutosizeColumn?.(column.key);\n setActiveColumnMenu(null);\n }}\n >\n Autosize This Column\n </button>\n )}\n {onAutosizeAllColumns && (\n <button\n className=\"w-full px-3 py-2 text-left text-sm hover:bg-gray-50\"\n onClick={() => {\n onAutosizeAllColumns?.();\n setActiveColumnMenu(null);\n }}\n >\n Autosize All Columns\n </button>\n )}\n\n {groupable && (\n <button\n className=\"w-full px-3 py-2 text-left text-sm hover:bg-gray-50 flex items-center gap-2\"\n onClick={(e) => {\n e.stopPropagation();\n setActiveColumnMenu(null);\n const isGrouped =\n groupedByColumns?.includes(\n column.key\n ) || groupedByColumn === column.key;\n if (onGroupToggle)\n onGroupToggle(column.key, !isGrouped);\n else\n onGroupBy?.(\n isGrouped ? null : column.key\n );\n }}\n >\n {groupedByColumn === column.key ||\n groupedByColumns?.includes(column.key) ? (\n <>\n <Ungroup className=\"w-4 h-4\" />\n Ungroup\n </>\n ) : (\n <>\n <Group className=\"w-4 h-4\" />\n Group by {column.header}\n </>\n )}\n </button>\n )}\n\n {onResetColumns && (\n <button\n className=\"w-full px-3 py-2 text-left text-sm hover:bg-gray-50\"\n onClick={() => {\n onResetColumns?.();\n setActiveColumnMenu(null);\n }}\n >\n Reset Columns\n </button>\n )}\n </div>\n )}\n {activeTab === \"filter\" && (\n <div className=\"p-3 w-full\">\n {/* Filter Tab */}\n {hasActiveFilter(column.key) && (\n <button\n className=\"mb-2 text-xs text-red-600 hover:underline flex items-center gap-1\"\n onClick={() => {\n setTempFilter(null);\n onColumnFilter(column.key, null);\n setActiveColumnMenu(null);\n }}\n >\n <FilterX className=\"w-3 h-3\" /> Clear Filter\n </button>\n )}\n <div className=\"max-h-75 overflow-auto pr-1\">\n <FilterContent\n column={column}\n data={data}\n value={tempFilter}\n onChange={(val) => {\n setTempFilter(val);\n // Auto-apply for client-side or no pagination\n if (paginationMode !== \"server\") {\n onColumnFilter(column.key, val);\n }\n }}\n />\n </div>\n {paginationMode === \"server\" ? (\n <div className=\"flex justify-end gap-2 mt-3\">\n <button\n className=\"px-3 py-1.5 text-sm bg-gray-100 text-gray-700 rounded-md hover:bg-gray-200\"\n onClick={() => setActiveColumnMenu(null)}\n >\n Cancel\n </button>\n <button\n className=\"px-3 py-1.5 text-sm bg-blue-600 text-white rounded-md hover:bg-blue-700\"\n onClick={() =>\n handleApplyFilter(column.key)\n }\n >\n Apply\n </button>\n </div>\n ) : (\n <div className=\"flex justify-end mt-3\">\n <button\n className=\"px-3 py-1.5 text-sm bg-blue-600 text-white rounded-md hover:bg-blue-700\"\n onClick={() => setActiveColumnMenu(null)}\n >\n Close\n </button>\n </div>\n )}\n </div>\n )}\n {activeTab === \"reorder\" && (\n <div className=\"px-3 py-2\">\n <div className=\"text-xs font-medium text-gray-500 mb-1\">\n Reorder Columns\n </div>\n <div className=\"max-h-60 overflow-auto border border-gray-200 rounded-md\">\n {(() => {\n // Build ordered array by columnOrder using allColumns\n const ordered = [\n ...columnOrder\n .map((k) =>\n allColumns.find((c) => c.key === k)\n )\n .filter(Boolean),\n // add any missing\n ...allColumns.filter(\n (c) => !columnOrder.includes(c.key)\n ),\n ] as GridColumn[];\n\n return ordered.map((c) => {\n const isPinnedItem = pinnedKeySet.has(\n c.key\n );\n return (\n <div\n key={c.key}\n className={`flex items-center justify-between px-2 py-1 text-sm bg-white hover:bg-gray-50 border-b last:border-b-0 relative ${\n menuReorderDraggingKey === c.key\n ? \"opacity-60\"\n : \"\"\n } ${\n isPinnedItem ? \"bg-blue-50/40\" : \"\"\n }`}\n draggable\n onDragStart={(e) =>\n handleMenuReorderDragStart(c.key, e)\n }\n onDragOver={(e) =>\n handleMenuReorderDragOver(c.key, e)\n }\n onDrop={() =>\n handleMenuReorderDrop(c.key)\n }\n onDragEnd={handleMenuReorderDragEnd}\n >\n {/* drop indicator */}\n {menuReorderDragOver?.key ===\n c.key && (\n <div\n className=\"absolute left-0 right-0 h-0.5 bg-blue-600\"\n style={{\n top:\n menuReorderDragOver?.position ===\n \"before\"\n ? 0\n : undefined,\n bottom:\n menuReorderDragOver?.position ===\n \"after\"\n ? 0\n : undefined,\n }}\n />\n )}\n <div className=\"flex items-center gap-2\">\n <span className=\"text-gray-400 select-none\">\n ⋮⋮\n </span>\n <span className=\"text-gray-800 truncate max-w-45\">\n {c.header}\n {isPinnedItem && (\n <span className=\"ml-1 text-[10px] text-blue-600\">\n (pinned)\n </span>\n )}\n </span>\n </div>\n </div>\n );\n });\n })()}\n </div>\n <div className=\"text-[10px] text-gray-400 mt-1\">\n Tip: You can only reorder within pinned or\n unpinned groups.\n </div>\n </div>\n )}\n </div>\n )}\n </div>\n )}\n </div>\n </div>\n\n {/* Resize handle */}\n {index < pinnedColumns.length - 1 && onColumnResize && (\n <div\n className=\"absolute top-0 right-0 w-1 h-full cursor-col-resize hover:bg-blue-300 transition-colors group\"\n onMouseDown={(e) => handleResizeStart(column.key, e)}\n onDoubleClick={(e) => {\n e.preventDefault();\n e.stopPropagation();\n onAutosizeColumn?.(column.key);\n }}\n title=\"Drag to resize • Double‑click to autosize\"\n >\n <div className=\"w-full h-full group-hover:bg-blue-400\" />\n </div>\n )}\n </div>\n );\n })}\n\n {/* Spacer before first visible unpinned col */}\n {hvPadLeft > 0 && (\n <div\n style={{ width: hvPadLeft, height: headerHeight }}\n className=\"flex-none\"\n />\n )}\n\n {/* Unpinned visible columns */}\n {unpinnedColumns.map((column, index) => {\n // unpinned visible segment\n const headerJustify =\n column.headerAlign === \"center\"\n ? \"justify-center\"\n : column.headerAlign === \"right\"\n ? \"justify-end\"\n : \"justify-start\";\n\n const colSortable = column.sortable !== false;\n return (\n <div\n key={column.key + index}\n className={`flex-none border-r border-gray-200 dark:border-gray-700 last:border-r-0 relative`}\n style={{ width: column.width || 100, height: headerHeight }}\n data-column-key={column.key}\n draggable\n onDragStart={(e) => handleDragStart(column.key, e)}\n onDragOver={(e) => handleDragOver(column.key, e)}\n onDrop={() => handleDrop(column.key)}\n onDragEnd={handleDragEnd}\n >\n {/* Drop indicator */}\n {dragOver?.key === column.key && (\n <div\n className=\"absolute top-0 bottom-0 w-0.5 bg-blue-600 z-4\"\n style={{\n left: dragOver?.position === \"before\" ? 0 : undefined,\n right: dragOver?.position === \"after\" ? 0 : undefined,\n }}\n />\n )}\n <div className=\"px-3 h-full\">\n {/* Header with sort */}\n <div className=\"flex items-center justify-between gap-1 group h-full\">\n <div\n className={`flex items-center min-w-0 flex-1 ${headerJustify} ${\n colSortable ? \"cursor-pointer hover:text-blue-600\" : \"\"\n }`}\n onClick={() => colSortable && onSort(column.key)}\n >\n <div className=\"flex items-center gap-2 min-w-0\">\n {/* Filter indicator */}\n {hasActiveFilter(column.key) && (\n <div\n className=\"w-2 h-2 bg-red-500 rounded-full shrink-0\"\n title=\"Filter applied\"\n />\n )}\n <span className=\"font-medium text-gray-900 dark:text-gray-100 text-sm truncate min-w-0\">\n {column.header}\n </span>\n </div>\n {colSortable && sortConfig.key === column.key && (\n <div className=\"ml-1 shrink-0 pointer-events-none select-none\">\n {sortConfig.direction === \"asc\" ? (\n <ArrowUp className=\"w-3 h-3 text-blue-600 dark:text-blue-400\" />\n ) : (\n <ArrowDown className=\"w-3 h-3 text-blue-600 dark:text-blue-400\" />\n )}\n </div>\n )}\n </div>\n\n {/* Three-dot menu */}\n {(column.filterable || onColumnPin || groupable) && (\n <div\n className={`relative ${\n activeColumnMenu === column.key || isTouchDevice\n ? \"block\"\n : \"hidden group-hover:block\"\n }`}\n data-column-menu\n >\n <button\n className=\"p-1 hover:bg-gray-200 dark:hover:bg-gray-700 rounded shrink-0\"\n onClick={(e) => handleColumnMenuClick(column.key, e)}\n data-menu-trigger\n >\n <Menu className=\"w-4 h-4 text-gray-500 dark:text-gray-400\" />\n </button>\n\n {/* Dropdown Menu with Tabs */}\n {activeColumnMenu === column.key && menuPosition && (\n <div\n className=\"fixed bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-md shadow-lg z-10 w-[320px] overflow-auto\"\n style={{\n top: `${menuPosition.top}px`,\n left: `${menuPosition.left}px`,\n // Ensure the menu height never exceeds available space within the grid container\n maxHeight: `${Math.max(\n 160,\n menuPosition.containerRect.bottom -\n 12 -\n menuPosition.top\n )}px`,\n }}\n data-column-menu\n >\n {/* Tabs */}\n <div className=\"flex items-center border-b border-gray-200 dark:border-gray-700\">\n <button\n className={`px-3 py-2 text-sm flex items-center gap-2 ${\n activeTab === \"menu\"\n ? \"border-b-2 border-blue-600 text-blue-600\"\n : \"text-gray-600\"\n }`}\n onClick={() => setActiveTab(\"menu\")}\n >\n <Menu className=\"w-4 h-4\" />\n </button>\n {column.filterable && (\n <button\n className={`px-3 py-2 text-sm flex items-center gap-2 ${\n activeTab === \"filter\"\n ? \"border-b-2 border-blue-600 text-blue-600\"\n : \"text-gray-600\"\n }`}\n onClick={() => setActiveTab(\"filter\")}\n >\n <Filter className=\"w-4 h-4\" />\n </button>\n )}\n <button\n className={`px-3 py-2 text-sm flex items-center gap-2 ${\n activeTab === \"reorder\"\n ? \"border-b-2 border-blue-600 text-blue-600\"\n : \"text-gray-600\"\n }`}\n onClick={() => setActiveTab(\"reorder\")}\n >\n <ArrowUpDown className=\"w-4 h-4\" />\n </button>\n </div>\n {/* Tab Content (reusing existing code paths) */}\n {activeTab === \"menu\" && (\n <div className=\"py-1\">\n {onColumnPin && (\n <button\n className=\"w-full px-3 py-2 text-left text-sm hover:bg-gray-50 flex items-center gap-2\"\n onClick={(e) => {\n e.stopPropagation();\n setActiveColumnMenu(null);\n const nextPinned = !pinnedKeySet.has(\n column.key\n );\n onColumnPin?.(column.key, nextPinned);\n }}\n >\n {pinnedKeySet.has(column.key) ? (\n <>\n <PinOff className=\"w-4 h-4\" />\n Unpin Column\n </>\n ) : (\n <>\n <Pin className=\"w-4 h-4\" />\n Pin Column\n </>\n )}\n </button>\n )}\n {onAutosizeColumn && (\n <button\n className=\"w-full px-3 py-2 text-left text-sm hover:bg-gray-50\"\n onClick={() => {\n onAutosizeColumn?.(column.key);\n setActiveColumnMenu(null);\n }}\n >\n Autosize This Column\n </button>\n )}\n {onAutosizeAllColumns && (\n <button\n className=\"w-full px-3 py-2 text-left text-sm hover:bg-gray-50\"\n onClick={() => {\n onAutosizeAllColumns?.();\n setActiveColumnMenu(null);\n }}\n >\n Autosize All Columns\n </button>\n )}\n\n {groupable && (\n <button\n className=\"w-full px-3 py-2 text-left text-sm hover:bg-gray-50 flex items-center gap-2\"\n onClick={(e) => {\n e.stopPropagation();\n setActiveColumnMenu(null);\n const isGrouped =\n groupedByColumns?.includes(\n column.key\n ) || groupedByColumn === column.key;\n if (onGroupToggle)\n onGroupToggle(column.key, !isGrouped);\n else\n onGroupBy?.(\n isGrouped ? null : column.key\n );\n }}\n >\n {groupedByColumn === column.key ||\n groupedByColumns?.includes(column.key) ? (\n <>\n <Ungroup className=\"w-4 h-4\" />\n Ungroup\n </>\n ) : (\n <>\n <Group className=\"w-4 h-4\" />\n Group by {column.header}\n </>\n )}\n </button>\n )}\n\n {onResetColumns && (\n <button\n className=\"w-full px-3 py-2 text-left text-sm hover:bg-gray-50\"\n onClick={() => {\n onResetColumns?.();\n setActiveColumnMenu(null);\n }}\n >\n Reset Columns\n </button>\n )}\n </div>\n )}\n {activeTab === \"filter\" && (\n <div className=\"p-3 w-full\">\n <div className=\"max-h-75 overflow-auto pr-1\">\n <FilterContent\n column={column}\n data={data}\n value={tempFilter}\n onChange={(val) => {\n setTempFilter(val);\n // Auto-apply for client-side or no pagination\n if (paginationMode !== \"server\") {\n onColumnFilter(column.key, val);\n }\n }}\n />\n </div>\n <div className=\"flex justify-end gap-2 mt-3\">\n {hasActiveFilter(column.key) && (\n <button\n className=\"px-3 py-1.5 text-sm bg-red-500 text-white rounded-md hover:bg-red-600 flex items-center gap-2 cursor-pointer\"\n onClick={() => {\n setTempFilter(null);\n onColumnFilter(column.key, null);\n setActiveColumnMenu(null);\n }}\n >\n <FilterX className=\"w-3 h-3\" /> Clear\n Filter\n </button>\n )}\n <button\n className=\"px-3 py-1.5 text-sm bg-green-100 text-green-700 rounded-md hover:bg-green-200 cursor-pointer\"\n onClick={() => setActiveColumnMenu(null)}\n >\n Apply\n </button>\n {paginationMode === \"server\" && (\n <button\n className=\"px-3 py-1.5 text-sm bg-blue-600 text-white rounded-md hover:bg-blue-700\"\n onClick={() =>\n handleApplyFilter(column.key)\n }\n >\n Apply\n </button>\n )}\n </div>\n </div>\n )}\n {activeTab === \"reorder\" && (\n <div className=\"px-3 py-2\">\n <div className=\"text-xs font-medium text-gray-500 mb-1\">\n Reorder Columns\n </div>\n <div className=\"max-h-60 overflow-auto border border-gray-200 rounded-md\">\n {(() => {\n const ordered = [\n ...columnOrder\n .map((k) =>\n allColumns.find((c) => c.key === k)\n )\n .filter(Boolean),\n ...allColumns.filter(\n (c) => !columnOrder.includes(c.key)\n ),\n ] as GridColumn[];\n\n return ordered.map((c) => {\n const isPinnedItem = pinnedKeySet.has(\n c.key\n );\n return (\n <div\n key={c.key}\n className={`flex items-center justify-between px-2 py-1 text-sm bg-white hover:bg-gray-50 border-b last:border-b-0 relative ${\n menuReorderDraggingKey === c.key\n ? \"opacity-60\"\n : \"\"\n } ${\n isPinnedItem ? \"bg-blue-50/40\" : \"\"\n }`}\n draggable\n onDragStart={(e) =>\n handleMenuReorderDragStart(c.key, e)\n }\n onDragOver={(e) =>\n handleMenuReorderDragOver(c.key, e)\n }\n onDrop={() =>\n handleMenuReorderDrop(c.key)\n }\n onDragEnd={handleMenuReorderDragEnd}\n >\n {menuReorderDragOver?.key ===\n c.key && (\n <div\n className=\"absolute left-0 right-0 h-0.5 bg-blue-600\"\n style={{\n top:\n menuReorderDragOver?.position ===\n \"before\"\n ? 0\n : undefined,\n bottom:\n menuReorderDragOver?.position ===\n \"after\"\n ? 0\n : undefined,\n }}\n />\n )}\n <div className=\"flex items-center gap-2\">\n <span className=\"text-gray-400 select-none\">\n ⋮⋮\n </span>\n <span className=\"text-gray-800 truncate max-w-45\">\n {c.header}\n {isPinnedItem && (\n <span className=\"ml-1 text-[10px] text-blue-600\">\n (pinned)\n </span>\n )}\n </span>\n </div>\n </div>\n );\n });\n })()}\n </div>\n <div className=\"text-[10px] text-gray-400 mt-1\">\n Tip: You can only reorder within pinned or\n unpinned groups.\n </div>\n </div>\n )}\n </div>\n )}\n </div>\n )}\n </div>\n </div>\n\n {/* Resize handle */}\n {index < unpinnedColumns.length - 1 && onColumnResize && (\n <div\n className=\"absolute top-0 right-0 w-1 h-full cursor-col-resize hover:bg-blue-300 transition-colors group\"\n onMouseDown={(e) => handleResizeStart(column.key, e)}\n onDoubleClick={(e) => {\n e.preventDefault();\n e.stopPropagation();\n onAutosizeColumn?.(column.key);\n }}\n title=\"Drag to resize • Double‑click to autosize\"\n >\n <div className=\"w-full h-full group-hover:bg-blue-400\" />\n </div>\n )}\n </div>\n );\n })}\n\n {/* Spacer after last visible unpinned col */}\n {hvPadRight > 0 && (\n <div\n style={{ width: hvPadRight, height: headerHeight }}\n className=\"flex-none\"\n />\n )}\n </div>\n </div>\n\n {/* Inline filter handled in dropdown tabs */}\n </>\n );\n};\n","import { CSSProperties, MouseEvent, memo, useMemo } from \"react\";\nimport { GridRow, GridColumn, VirtualizedRange } from \"../types\";\nimport { SELECT_COL_WIDTH } from \"../constants\";\nimport { highlightText, containsSearchQuery } from \"../utils/highlightText\";\n\ninterface GridRowsProps {\n data: GridRow[];\n columns: GridColumn[];\n selectedRows: Set<string | number>;\n virtualized: boolean;\n virtualizedRange: VirtualizedRange;\n rowHeight: number;\n selectable: boolean;\n isRowSelectable?: (row: GridRow) => boolean;\n onRowSelect: (rowId: string | number, isSelected: boolean) => void;\n pinnedColumns?: Set<string>;\n hvPadLeft?: number;\n hvPadRight?: number;\n rowStyle?: (row: GridRow) => CSSProperties | undefined;\n globalFilter?: string;\n onContextMenu?: (row: GridRow, event: MouseEvent) => void;\n onRowDoubleClick?: (row: GridRow, event: MouseEvent) => void;\n onRowClick?: (row: GridRow, event: MouseEvent) => void;\n onCellClick?: (args: {\n row: GridRow;\n column: GridColumn;\n value: any;\n event: MouseEvent;\n }) => void;\n // Cell focus & selection\n isCellFocused?: (rowId: string | number, columnKey: string) => boolean;\n isCellSelected?: (rowId: string | number, columnKey: string) => boolean;\n // Cell-level context menu (right-click)\n onCellContextMenu?: (args: {\n row: GridRow;\n column: GridColumn;\n value: any;\n displayValue: string;\n event: MouseEvent;\n }) => void;\n onCellMouseDown?: (args: {\n row: GridRow;\n column: GridColumn;\n event: MouseEvent;\n }) => void;\n onCellMouseEnter?: (args: {\n row: GridRow;\n column: GridColumn;\n event: MouseEvent;\n }) => void;\n getRowId?: (row: GridRow) => string | number;\n}\n\nconst GridRowsComponent = ({\n data,\n columns,\n selectedRows,\n virtualized,\n virtualizedRange,\n rowHeight,\n selectable,\n isRowSelectable,\n onRowSelect,\n pinnedColumns = new Set(),\n hvPadLeft = 0,\n hvPadRight = 0,\n rowStyle,\n globalFilter,\n onContextMenu,\n onRowDoubleClick,\n onRowClick,\n onCellClick,\n isCellFocused,\n isCellSelected,\n onCellContextMenu,\n onCellMouseDown,\n onCellMouseEnter,\n getRowId,\n}: GridRowsProps) => {\n // Helper to extract row ID\n const extractRowId = (row: GridRow, rowIndex?: number): string | number => {\n if (getRowId) {\n return getRowId(row);\n }\n if (row.id !== undefined) {\n return row.id;\n }\n // Fallback: use row index\n if (process.env.NODE_ENV === \"development\" && rowIndex === 0) {\n console.warn(\n \"[CustomDataGrid] No row IDs found. Either:\\n\" +\n \" 1. Add 'id' property to each row object, or\\n\" +\n \" 2. Provide 'getRowId' prop to extract a unique identifier.\\n\" +\n \" Using row index as fallback ID, but this may cause issues with pagination/filtering.\",\n );\n }\n return rowIndex ?? 0;\n };\n const totalHeight = useMemo(\n () => data.length * rowHeight,\n [data.length, rowHeight],\n );\n\n // Precompute pinned metadata for left offsets efficiently - OPTIMIZED\n const { leftOffsetByPinnedKey } = useMemo(() => {\n // Early return if no pinned columns\n if (pinnedColumns.size === 0) {\n return { leftOffsetByPinnedKey: new Map<string, number>() };\n }\n\n const pinnedKeysInOrder = columns\n .filter((c) => pinnedColumns.has(c.key))\n .map((c) => c.key);\n\n // Use direct property access instead of Map for better performance\n const widthByKey = new Map<string, number>();\n for (const col of columns) {\n widthByKey.set(col.key, col.width || 100);\n }\n\n const leftOffsetByPinnedKey = new Map<string, number>();\n let acc = selectable ? SELECT_COL_WIDTH : 0;\n\n for (const key of pinnedKeysInOrder) {\n leftOffsetByPinnedKey.set(key, acc);\n acc += widthByKey.get(key) || 100;\n }\n\n return { leftOffsetByPinnedKey };\n }, [columns, pinnedColumns, selectable]);\n\n // Count how many columns are pinned in the provided visible columns list\n const pinnedCount = useMemo(\n () => columns.reduce((n, c) => n + (pinnedColumns.has(c.key) ? 1 : 0), 0),\n [columns, pinnedColumns],\n );\n\n const handleContextMenu = (e: MouseEvent, row: GridRow) => {\n e.preventDefault();\n onContextMenu?.(row, e);\n };\n\n const handleDoubleClick = (e: MouseEvent, row: GridRow) => {\n onRowDoubleClick?.(row, e);\n };\n\n const handleRowClick = (e: MouseEvent, row: GridRow) => {\n onRowClick?.(row, e);\n };\n\n const handleCellClick = (\n e: MouseEvent,\n row: GridRow,\n column: GridColumn,\n value: any,\n ) => {\n onCellClick?.({ row, column, value, event: e });\n };\n\n return (\n <div\n style={{\n height: virtualized ? `${totalHeight}px` : \"auto\",\n position: \"relative\",\n }}\n >\n <div\n style={{\n transform: virtualized\n ? `translate3d(0, ${virtualizedRange.offsetY}px, 0)`\n : \"none\",\n position: virtualized ? \"absolute\" : \"static\",\n top: 0,\n left: 0,\n right: 0,\n }}\n >\n {data.map((row, index) => {\n const actualIndex = virtualized\n ? virtualizedRange.startIndex + index\n : index;\n const customRowStyles = rowStyle?.(row);\n const rowId = extractRowId(row, actualIndex);\n const defaultRowColor =\n actualIndex % 2 === 0\n ? \"bg-white dark:bg-gray-900\"\n : \"bg-gray-25 dark:bg-gray-800\";\n const selectedRowColor = selectedRows.has(rowId)\n ? \"bg-blue-50 dark:bg-blue-900/20\"\n : \"\";\n\n return (\n <div\n key={`${rowId}-${actualIndex}`}\n className={`flex border-b border-gray-100 dark:border-gray-700 hover:bg-gray-50 dark:hover:bg-gray-700 ${\n selectedRowColor || (customRowStyles ? \"\" : defaultRowColor)\n }`}\n style={{\n height: `${rowHeight}px`,\n ...customRowStyles,\n }}\n onContextMenu={(e) => handleContextMenu(e, row)}\n onDoubleClick={(e) => handleDoubleClick(e, row)}\n onClick={(e) => handleRowClick(e, row)}\n >\n {selectable && (\n <div\n className=\"flex items-center justify-center sticky left-0 bg-white dark:bg-gray-900 z-2 border-r border-gray-200 dark:border-gray-700\"\n style={{ width: SELECT_COL_WIDTH }}\n >\n <input\n type=\"checkbox\"\n checked={selectedRows.has(rowId)}\n disabled={isRowSelectable ? !isRowSelectable(row) : false}\n onChange={(e) => onRowSelect(rowId, e.target.checked)}\n onKeyDown={(e) => {\n if (e.key === \" \" || e.key === \"Enter\") {\n e.preventDefault();\n onRowSelect(rowId, !selectedRows.has(rowId));\n }\n }}\n className=\"rounded border-gray-300 dark:border-gray-600 text-blue-600 cursor-pointer disabled:cursor-not-allowed disabled:opacity-50 h-4 w-4 transition-all duration-150\"\n aria-label={`Select row ${rowId}`}\n tabIndex={0}\n />\n </div>\n )}\n\n {columns.map((column, colIndex) => {\n const isPinned = pinnedColumns.has(column.key);\n const leftOffset = isPinned\n ? leftOffsetByPinnedKey.get(column.key) || 0\n : 0;\n\n // Optimized cell value processing\n const rawCell = row[column.key];\n let renderedValue: React.ReactNode;\n let displayValue: string | undefined;\n\n // Optimize cell renderer calls\n if (column.cellRenderer) {\n renderedValue = column.cellRenderer(rawCell, row);\n // Only compute display value if needed for tooltip\n if (\n typeof rawCell === \"string\" ||\n typeof rawCell === \"number\" ||\n typeof rawCell === \"boolean\"\n ) {\n displayValue = String(rawCell);\n }\n } else if (column.formatter) {\n renderedValue = column.formatter(rawCell);\n displayValue = String(renderedValue);\n } else {\n renderedValue = rawCell ?? \"\";\n displayValue = renderedValue\n ? String(renderedValue)\n : undefined;\n }\n\n // Apply highlighting for global filter if present and no custom renderer\n if (\n globalFilter &&\n globalFilter.trim() &&\n !column.cellRenderer &&\n renderedValue\n ) {\n const textValue = String(renderedValue);\n if (containsSearchQuery(textValue, globalFilter)) {\n renderedValue = highlightText(textValue, globalFilter);\n }\n }\n\n // Clean up empty display values\n if (displayValue && displayValue.trim().length === 0) {\n displayValue = undefined;\n }\n\n // Pre-compute CSS classes for better performance\n const alignClass =\n column.align === \"center\"\n ? \"text-center\"\n : column.align === \"right\"\n ? \"text-right\"\n : \"text-left\";\n const justifyClass =\n column.align === \"center\"\n ? \"justify-center\"\n : column.align === \"right\"\n ? \"justify-end\"\n : \"justify-start\";\n const paddingClass = column.noPadding ? \"p-0\" : \"p-3\";\n\n // Optimize dynamic class/style computation\n const extraCellClass =\n typeof column.className === \"function\"\n ? column.className(rawCell, row)\n : column.className || \"\";\n const extraCellStyle =\n typeof column.cellStyle === \"function\"\n ? column.cellStyle(rawCell, row)\n : column.cellStyle || undefined;\n\n const cell = (\n <div\n key={`${column.key}-${colIndex}`}\n className={`border-r border-gray-200 last:border-r-0 ${paddingClass} text-sm flex items-center ${\n isCellSelected?.(rowId, column.key)\n ? \"bg-blue-100/60 dark:bg-blue-900/30\"\n : \"\"\n } ${\n isPinned\n ? isCellFocused?.(rowId, column.key)\n ? \"sticky z-2 bg-inherit\"\n : \"sticky z-1 bg-inherit\"\n : \"\"\n } ${\n isCellFocused?.(rowId, column.key) ? \"relative\" : \"\"\n } ${alignClass} ${justifyClass} ${extraCellClass}`}\n style={{\n width: `${column.width || 100}px`,\n left: isPinned ? `${leftOffset}px` : \"auto\",\n ...extraCellStyle,\n }}\n // Show native tooltip only when a meaningful value exists (non-empty string after trimming)\n title={displayValue}\n onClick={(e) =>\n handleCellClick(e, row, column, row[column.key])\n }\n onContextMenu={(e) => {\n // Only handle for real data rows\n if (\n (row as any)._isGroupHeader ||\n (row as any)._isGroupFooter\n )\n return;\n if (onCellContextMenu) {\n e.preventDefault();\n e.stopPropagation();\n onCellContextMenu({\n row,\n column,\n value: (row as any)[column.key],\n displayValue: displayValue ?? \"\",\n event: e as any,\n });\n }\n // If no onCellContextMenu prop, fall back to row-level handler (do not stop propagation)\n }}\n onMouseDown={(e) =>\n onCellMouseDown?.({ row, column, event: e })\n }\n onMouseEnter={(e) =>\n onCellMouseEnter?.({ row, column, event: e })\n }\n >\n {isCellFocused?.(rowId, column.key) && (\n <div className=\"pointer-events-none absolute inset-0 border border-blue-400\" />\n )}\n {column.wrapCellContent === false ? (\n <>{renderedValue}</>\n ) : (\n <div className=\"truncate w-full\">{renderedValue}</div>\n )}\n </div>\n );\n // Insert left spacer once before the first unpinned visible column\n if (hvPadLeft > 0 && colIndex === pinnedCount) {\n return (\n <>\n <div style={{ width: hvPadLeft }} className=\"flex-none\" />\n {cell}\n </>\n );\n }\n\n return cell;\n })}\n {/* Spacer after last visible unpinned col */}\n {hvPadRight > 0 && (\n <div style={{ width: hvPadRight }} className=\"flex-none\" />\n )}\n </div>\n );\n })}\n </div>\n </div>\n );\n};\n\nconst areEqual = (prev: GridRowsProps, next: GridRowsProps) => {\n if (prev.rowHeight !== next.rowHeight) return false;\n if (prev.selectable !== next.selectable) return false;\n if (prev.virtualized !== next.virtualized) return false;\n if (prev.globalFilter !== next.globalFilter) return false;\n if (prev.virtualizedRange.startIndex !== next.virtualizedRange.startIndex)\n return false;\n if (prev.virtualizedRange.endIndex !== next.virtualizedRange.endIndex)\n return false;\n if (prev.virtualizedRange.offsetY !== next.virtualizedRange.offsetY)\n return false;\n // Re-render when focus/selection functions change identity (they depend on selection & focus states)\n if (prev.isCellSelected !== next.isCellSelected) return false;\n if (prev.isCellFocused !== next.isCellFocused) return false;\n // Re-render when drag-selection handlers change identity (closure depends on selectionAnchor/end)\n if (prev.onCellMouseDown !== next.onCellMouseDown) return false;\n if (prev.onCellMouseEnter !== next.onCellMouseEnter) return false;\n if (prev.getRowId !== next.getRowId) return false;\n if (prev.selectedRows.size !== next.selectedRows.size) return false;\n if (prev.pinnedColumns?.size !== next.pinnedColumns?.size) return false;\n const prevCols = prev.columns.map((c) => c.key).join(\"|\");\n const nextCols = next.columns.map((c) => c.key).join(\"|\");\n if (prevCols !== nextCols) return false;\n if (prev.data.length !== next.data.length) return false;\n\n // Helper to extract IDs for comparison\n const extractId = (\n row: GridRow,\n idx: number,\n getRowId?: (r: GridRow) => string | number,\n ) => {\n if (getRowId) {\n return getRowId(row);\n }\n if (row.id !== undefined) {\n return row.id;\n }\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 return true;\n};\n\nexport const GridRows = memo(GridRowsComponent, areEqual);\n","import React from \"react\";\n\n/**\n * Highlights matching text within a string based on a search query\n * @param text - The text to highlight\n * @param searchQuery - The search query to match against\n * @returns JSX element with highlighted matches\n */\nexport const highlightText = (\n text: string,\n searchQuery: string\n): React.ReactNode => {\n if (!searchQuery || !text) {\n return text;\n }\n\n const query = searchQuery.toLowerCase().trim();\n const lowerText = text.toLowerCase();\n\n if (!query || !lowerText.includes(query)) {\n return text;\n }\n\n const parts: React.ReactNode[] = [];\n let lastIndex = 0;\n let searchIndex = 0;\n let keyCounter = 0; // Use counter for unique keys\n\n while ((searchIndex = lowerText.indexOf(query, lastIndex)) !== -1) {\n // Add text before the match\n if (searchIndex > lastIndex) {\n parts.push(text.slice(lastIndex, searchIndex));\n }\n\n // Add the highlighted match\n parts.push(\n <mark\n key={`highlight-${keyCounter++}`}\n className=\"bg-yellow-200 dark:bg-yellow-600/40 text-current font-medium px-0.5 py-0 rounded-sm\"\n style={{ margin: 0 }} // Ensure no unwanted margins\n >\n {text.slice(searchIndex, searchIndex + query.length)}\n </mark>\n );\n\n lastIndex = searchIndex + query.length;\n }\n\n // Add remaining text after the last match\n if (lastIndex < text.length) {\n parts.push(text.slice(lastIndex));\n }\n\n return <>{parts}</>;\n};\n\n/**\n * Checks if a value contains the search query (case-insensitive)\n * @param value - The value to check\n * @param searchQuery - The search query\n * @returns true if the value contains the search query\n */\nexport const containsSearchQuery = (\n value: any,\n searchQuery: string\n): boolean => {\n if (!searchQuery || value == null) {\n return false;\n }\n\n const query = searchQuery.toLowerCase().trim();\n if (!query) {\n return false;\n }\n\n const text = String(value).toLowerCase();\n return text.includes(query);\n};\n","import { useState } from \"react\";\nimport type { GridColumn } from \"../types\";\n\ninterface GroupBarProps {\n columns: GridColumn[];\n groupedKeys: string[];\n onRemove: (columnKey: string) => void;\n onReorder: (newOrder: string[]) => void;\n onDropColumnKey: (columnKey: string) => void;\n onToggleExpandAll?: () => void;\n isAnyExpanded?: boolean;\n}\n\nexport const GroupBar = ({\n columns,\n groupedKeys,\n onRemove,\n onReorder,\n onDropColumnKey,\n onToggleExpandAll,\n isAnyExpanded = false,\n}: GroupBarProps) => {\n const [dragKey, setDragKey] = useState<string | null>(null);\n const [dragOverKey, setDragOverKey] = useState<string | null>(null);\n const [dragOverBefore, setDragOverBefore] = useState<boolean>(true);\n\n const labelFor = (key: string) => columns.find((c) => c.key === key)?.header || key;\n\n const handleDropFromHeader: React.DragEventHandler<HTMLDivElement> = (e) => {\n e.preventDefault();\n const key = e.dataTransfer.getData(\"text/plain\");\n if (!key) return;\n if (!groupedKeys.includes(key)) onDropColumnKey(key);\n };\n\n const onDragOverBar: React.DragEventHandler<HTMLDivElement> = (e) => {\n e.preventDefault();\n };\n\n const handleChipDragStart = (key: string): React.DragEventHandler<HTMLDivElement> => (e) => {\n setDragKey(key);\n e.dataTransfer.effectAllowed = \"move\";\n e.dataTransfer.setData(\"application/x-group-key\", key);\n };\n\n const handleChipDragOver = (key: string): React.DragEventHandler<HTMLDivElement> => (e) => {\n e.preventDefault();\n const rect = (e.currentTarget as HTMLDivElement).getBoundingClientRect();\n const before = e.clientX < rect.left + rect.width / 2;\n setDragOverKey(key);\n setDragOverBefore(before);\n };\n\n const handleChipDrop = (targetKey: string): React.DragEventHandler<HTMLDivElement> => () => {\n if (!dragKey || dragKey === targetKey) {\n setDragKey(null);\n setDragOverKey(null);\n return;\n }\n const order = groupedKeys.filter((k) => k !== dragKey);\n const idx = order.indexOf(targetKey);\n if (idx === -1) return;\n if (!dragOverBefore) {\n order.splice(idx + 1, 0, dragKey);\n } else {\n order.splice(idx, 0, dragKey);\n }\n onReorder(order);\n setDragKey(null);\n setDragOverKey(null);\n };\n\n const clearDnD = () => {\n setDragKey(null);\n setDragOverKey(null);\n };\n\n return (\n <div\n className=\"flex items-center gap-2 px-3 py-2 bg-gray-50 border-b border-gray-200 text-sm\"\n onDrop={handleDropFromHeader}\n onDragOver={onDragOverBar}\n onDragEnd={clearDnD}\n >\n <span className=\"text-gray-600 whitespace-nowrap\">Group by:</span>\n {groupedKeys.length > 0 && (\n <button\n type=\"button\"\n className=\"ml-1 px-2 py-1 text-xs border border-gray-300 rounded hover:bg-gray-100\"\n title={isAnyExpanded ? \"Collapse all groups\" : \"Expand all groups\"}\n onClick={(e) => {\n e.preventDefault();\n e.stopPropagation();\n onToggleExpandAll?.();\n }}\n >\n {isAnyExpanded ? \"Collapse All\" : \"Expand All\"}\n </button>\n )}\n {groupedKeys.length === 0 && (\n <span className=\"text-gray-400\">Drag a column here</span>\n )}\n <div className=\"flex items-center gap-2 flex-wrap\">\n {groupedKeys.map((key) => (\n <div\n key={key}\n className=\"inline-flex items-center gap-2 bg-blue-100 text-blue-800 px-2 py-1 rounded-md border border-blue-200 cursor-move relative\"\n draggable\n onDragStart={handleChipDragStart(key)}\n onDragOver={handleChipDragOver(key)}\n onDrop={handleChipDrop(key)}\n title={labelFor(key)}\n >\n <span className=\"text-xs font-medium truncate max-w-[160px]\">{labelFor(key)}</span>\n <button\n className=\"text-blue-700 hover:text-blue-900\"\n onClick={() => onRemove(key)}\n aria-label={`Remove grouping ${labelFor(key)}`}\n >\n ×\n </button>\n {dragOverKey === key && (\n <span\n className=\"absolute top-0 bottom-0 w-0.5 bg-blue-600 dark:bg-blue-400 animate-pulse\"\n style={{ left: dragOverBefore ? 0 : undefined, right: dragOverBefore ? undefined : 0 }}\n aria-hidden=\"true\"\n />\n )}\n </div>\n ))}\n </div>\n </div>\n );\n};\n","import { memo } from \"react\";\nimport { ChevronDown, ChevronRight } from \"lucide-react\";\nimport { GridRow, CustomDataGridProps } from \"../types\";\nimport { SELECT_COL_WIDTH } from \"../constants\";\n\ninterface GroupHeaderProps {\n row: GridRow & {\n _isGroupHeader?: boolean;\n _groupKey?: string;\n _groupCount?: number;\n };\n isExpanded: boolean;\n onToggle: (groupKey: string) => void;\n viewportWidth: number;\n selectable: boolean;\n rowHeight?: number;\n /**\n * Optional function to map a column key to its display label (header).\n * If not provided, the raw column key will be used as a fallback.\n */\n getHeaderLabel?: (columnKey: string) => string;\n /**\n * Optional function to map a column key and raw group value to a display label.\n * Useful to apply the column's formatter for group headers.\n */\n getValueLabel?: (columnKey: string, rawValue: any) => string;\n /**\n * Optional custom renderer for actions on grouped rows.\n */\n renderGroupActions?: CustomDataGridProps[\"renderGroupActions\"];\n}\n\nexport const GroupHeader = memo(\n ({\n row,\n isExpanded,\n onToggle,\n viewportWidth,\n selectable,\n rowHeight = 40,\n getHeaderLabel,\n getValueLabel,\n renderGroupActions,\n }: GroupHeaderProps) => {\n const handleToggle = () => {\n if (row._groupKey) {\n onToggle(row._groupKey);\n }\n };\n\n return (\n <div\n className=\"flex items-center bg-blue-50 dark:bg-blue-900/20 border-b border-gray-200 dark:border-gray-800 hover:bg-blue-100/70 cursor-pointer sticky left-0 z-20 overflow-hidden\"\n style={{\n minWidth: `${viewportWidth + (selectable ? SELECT_COL_WIDTH : 0)}px`,\n height: `${rowHeight}px`,\n }}\n onClick={handleToggle}\n role=\"button\"\n tabIndex={0}\n aria-expanded={isExpanded}\n onKeyDown={(e) => {\n if (e.key === \"Enter\" || e.key === \" \") {\n e.preventDefault();\n handleToggle();\n }\n }}\n >\n {selectable && (\n <div className=\"w-12 flex items-center justify-center\">\n {/* Empty space for checkbox column */}\n </div>\n )}\n\n <div className=\"flex items-center px-4 py-2 flex-1\">\n <div\n className=\"flex items-center mr-3\"\n style={{\n paddingLeft: `${(row as any)._groupLevel ? ((row as any)._groupLevel as number) * 16 : 0}px`,\n }}\n >\n {isExpanded ? (\n <ChevronDown className=\"w-4 h-4 text-gray-600\" />\n ) : (\n <ChevronRight className=\"w-4 h-4 text-gray-600\" />\n )}\n </div>\n\n <div className=\"flex items-center gap-2\">\n <span className=\"font-medium text-gray-900\">\n {(row as any)._groupColumnKey\n ? `${getHeaderLabel ? getHeaderLabel((row as any)._groupColumnKey) : (row as any)._groupColumnKey}: `\n : \"\"}\n {getValueLabel && (row as any)._groupColumnKey\n ? getValueLabel(\n (row as any)._groupColumnKey,\n (row as any)._groupValue,\n )\n : ((row as any)._groupValue ?? (row._groupKey || \"Unknown\"))}\n </span>\n <span className=\"text-sm text-gray-600 bg-gray-200/70 px-2 py-1 rounded-full\">\n {row._groupCount} {row._groupCount === 1 ? \"item\" : \"items\"}\n </span>\n </div>\n\n {renderGroupActions && (\n <div\n className=\"flex items-center px-4\"\n onClick={(e) => e.stopPropagation()}\n >\n {renderGroupActions({\n groupKey: row._groupKey || \"\",\n columnKey: (row as any)._groupColumnKey || \"\",\n groupValue: (row as any)._groupValue,\n rows: (row as any)._groupRows || [],\n count: row._groupCount || 0,\n level: (row as any)._groupLevel || 0,\n })}\n </div>\n )}\n </div>\n </div>\n );\n },\n);\n","import { Search, Database } from \"lucide-react\";\n\ninterface NoDataMessageProps {\n hasFilters?: boolean;\n hasData?: boolean;\n message?: string;\n}\n\nexport const NoDataMessage = ({\n hasFilters = false,\n hasData = true,\n message,\n}: NoDataMessageProps) => {\n const getMessage = () => {\n if (message) {\n return {\n title: message,\n description: \"\",\n icon: Database,\n };\n }\n\n if (!hasData) {\n return {\n title: \"No Data Available\",\n description: \"There is no data to display in this table.\",\n icon: Database,\n };\n }\n\n if (hasFilters) {\n return {\n title: \"No Results Found\",\n description:\n \"No data matches your current search criteria. Try adjusting your filters.\",\n icon: Search,\n };\n }\n\n return {\n title: \"No Data to Display\",\n description: \"There are no items to show at the moment.\",\n icon: Database,\n };\n };\n\n const { title, description, icon: Icon } = getMessage();\n\n return (\n <div className=\"flex flex-col items-center justify-center pt-16 pb-10 px-4 text-center\">\n <div className=\"w-16 h-16 bg-gray-100 dark:bg-gray-800 rounded-full flex items-center justify-center mb-4\">\n <Icon className=\"w-8 h-8 text-gray-400 dark:text-gray-500\" />\n </div>\n <h3 className=\"text-lg font-medium text-gray-900 dark:text-gray-100 mb-2\">\n {title}\n </h3>\n <p className=\"text-sm text-gray-500 dark:text-gray-400 max-w-md\">\n {description}\n </p>\n </div>\n );\n};\n","import {\n ChevronLeft,\n ChevronRight,\n ChevronsLeft,\n ChevronsRight,\n Loader2,\n Users,\n Eye,\n Database,\n} from \"lucide-react\";\nimport { PaginationConfig } from \"../types\";\n\ninterface PaginationControlsProps {\n paginationConfig: PaginationConfig;\n currentPage: number;\n isServerLoading: boolean;\n selectedRowsCount: number;\n totalDataLength: number;\n filteredDataLength?: number;\n paginationMode?: \"client\" | \"server\";\n onPageChange: (page: number, pageSize: number) => void;\n onPageSizeChange: (pageSize: number) => void;\n showNoDataMessage?: boolean;\n}\n\nexport const PaginationControls = ({\n paginationConfig,\n currentPage,\n isServerLoading,\n selectedRowsCount,\n totalDataLength,\n filteredDataLength,\n paginationMode = \"client\",\n onPageChange,\n onPageSizeChange,\n}: PaginationControlsProps) => {\n // Helper function to get the effective total count\n const getEffectiveTotalCount = () => {\n if (paginationMode === \"server\") {\n return paginationConfig.totalRows || totalDataLength;\n }\n return filteredDataLength || totalDataLength;\n };\n\n // Helper function to get the current page range\n const getCurrentPageRange = () => {\n const effectiveTotal = paginationConfig.enabled\n ? paginationConfig.totalRows || totalDataLength\n : getEffectiveTotalCount();\n\n const start = paginationConfig.enabled\n ? Math.min(\n (currentPage - 1) * paginationConfig.pageSize + 1,\n effectiveTotal\n )\n : 1;\n\n const end = paginationConfig.enabled\n ? Math.min(currentPage * paginationConfig.pageSize, effectiveTotal)\n : effectiveTotal;\n\n return { start, end, total: effectiveTotal };\n };\n\n const { start, end, total } = getCurrentPageRange();\n const isFiltered =\n paginationMode === \"client\" &&\n filteredDataLength &&\n filteredDataLength !== totalDataLength;\n\n return (\n <div className=\"px-3 sm:px-6 py-2 border-t border-gray-200 dark:border-gray-700 bg-linear-to-r from-white via-gray-50 to-white dark:from-gray-900 dark:via-gray-800 dark:to-gray-900\">\n <div className=\"flex flex-col xl:flex-row gap-4 xl:gap-6 xl:justify-between xl:items-center\">\n {/* Left Section: Stats and Page Size */}\n <div className=\"flex flex-col sm:flex-row gap-3 sm:gap-4 items-start sm:items-center\">\n {/* Stats Section */}\n <div className=\"flex flex-wrap items-center gap-2 sm:gap-3\">\n {/* Total Count Badge - Only when pagination is disabled */}\n {!paginationConfig.enabled && (\n <div className=\"inline-flex items-center gap-1.5 px-3 py-1.5 rounded-full text-xs font-semibold bg-linear-to-r from-blue-500 to-blue-600 text-white shadow-lg ring-2 ring-blue-500/20\">\n <Database className=\"w-3 h-3\" />\n <span className=\"font-bold\">\n {total.toLocaleString()} {total === 1 ? \"Record\" : \"Records\"}\n </span>\n {isFiltered && (\n <span className=\"text-blue-100 font-normal\">\n (of {totalDataLength.toLocaleString()})\n </span>\n )}\n </div>\n )}\n\n {/* Selected Count Badge */}\n {selectedRowsCount > 0 && (\n <div className=\"inline-flex items-center gap-1.5 px-3 py-1.5 rounded-full text-xs font-semibold bg-linear-to-r from-primary to-primary text-white shadow-lg ring-2 ring-primary/20\">\n <Users className=\"w-3 h-3\" />\n <span>{selectedRowsCount.toLocaleString()} selected</span>\n </div>\n )}\n\n {/* Filtered Indicator */}\n {isFiltered && (\n <div className=\"inline-flex items-center gap-1.5 px-2.5 py-1 rounded-full text-xs font-medium bg-linear-to-r from-warning/10 to-warning/5 text-warning border border-warning/20\">\n <Eye className=\"w-3 h-3\" />\n <span>Filtered</span>\n </div>\n )}\n\n {/* Page Range Info (Desktop) */}\n {paginationConfig.enabled && (\n <div className=\"hidden sm:block text-sm text-gray-600 dark:text-gray-400 font-medium\">\n <span className=\"inline-flex items-center gap-1\">\n <span>Showing</span>\n <span className=\"px-2 py-0.5 bg-gray-100 dark:bg-gray-700 rounded-md font-bold text-gray-900 dark:text-gray-100\">\n {start.toLocaleString()}\n </span>\n <span>-</span>\n <span className=\"px-2 py-0.5 bg-gray-100 dark:bg-gray-700 rounded-md font-bold text-gray-900 dark:text-gray-100\">\n {end.toLocaleString()}\n </span>\n <span>of {total.toLocaleString()}</span>\n </span>\n </div>\n )}\n </div>\n\n {/* Page Size Selector */}\n {paginationConfig.enabled &&\n paginationConfig.showPageSizeSelector && (\n <div className=\"flex items-center gap-2\">\n <span className=\"text-sm font-medium text-gray-700 dark:text-gray-300 whitespace-nowrap\">\n Rows per page:\n </span>\n <select\n value={paginationConfig.pageSize}\n onChange={(e) => onPageSizeChange(Number(e.target.value))}\n className=\"border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100 rounded-xl px-3 py-1 text-sm font-medium focus:ring-2 focus:ring-primary focus:border-primary disabled:opacity-50 disabled:cursor-not-allowed shadow-sm transition-all duration-200 hover:border-gray-400 dark:hover:border-gray-500\"\n disabled={isServerLoading}\n >\n {paginationConfig.pageSizeOptions?.map((size) => (\n <option key={size} value={size}>\n {size}\n </option>\n ))}\n </select>\n </div>\n )}\n </div>\n\n {/* Right Section: Pagination Controls */}\n {paginationConfig.enabled && (\n <div className=\"flex flex-col sm:flex-row items-start sm:items-center gap-3 sm:gap-4\">\n {/* Loading Indicator */}\n {isServerLoading && (\n <div className=\"flex items-center gap-2 px-3 py-1.5 bg-blue-50 dark:bg-blue-900/20 rounded-full border border-blue-200 dark:border-blue-800\">\n <Loader2 className=\"w-4 h-4 animate-spin text-primary\" />\n <span className=\"text-sm font-medium text-primary\">\n Loading...\n </span>\n </div>\n )}\n\n {/* Mobile Page Info */}\n <div className=\"sm:hidden text-sm text-gray-600 dark:text-gray-400 font-medium\">\n <div className=\"flex flex-col gap-1\">\n <span className=\"inline-flex items-center gap-1\">\n <span>Page</span>\n <span className=\"px-2 py-0.5 bg-gray-100 dark:bg-gray-700 rounded-md font-bold text-gray-900 dark:text-gray-100\">\n {currentPage.toLocaleString()}\n </span>\n <span>of</span>\n <span className=\"px-2 py-0.5 bg-gray-100 dark:bg-gray-700 rounded-md font-bold text-gray-900 dark:text-gray-100\">\n {paginationConfig.totalPages.toLocaleString()}\n </span>\n </span>\n <span className=\"text-xs text-gray-500 dark:text-gray-500\">\n {start.toLocaleString()} - {end.toLocaleString()} of{\" \"}\n {total.toLocaleString()}\n </span>\n </div>\n </div>\n\n {/* Pagination Navigation */}\n <div className=\"flex items-center gap-1 sm:gap-2\">\n {/* First Page Button */}\n <button\n onClick={() => onPageChange(1, paginationConfig.pageSize)}\n disabled={currentPage === 1 || isServerLoading}\n className=\"inline-flex items-center justify-center w-8 h-8 rounded-full border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-800 hover:bg-gray-50 dark:hover:bg-gray-700 disabled:opacity-40 disabled:cursor-not-allowed cursor-pointer text-gray-600 dark:text-gray-300 hover:text-gray-900 dark:hover:text-gray-100 transition-all duration-200 hover:border-gray-400 dark:hover:border-gray-500 hover:shadow-md focus:ring-2 focus:ring-primary/20 focus:border-primary\"\n title=\"First page\"\n aria-label=\"Go to first page\"\n >\n <ChevronsLeft className=\"w-3.5 h-3.5\" />\n </button>\n\n {/* Previous Page Button */}\n <button\n onClick={() =>\n onPageChange(currentPage - 1, paginationConfig.pageSize)\n }\n disabled={currentPage === 1 || isServerLoading}\n className=\"inline-flex items-center justify-center w-8 h-8 rounded-full border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-800 hover:bg-gray-50 dark:hover:bg-gray-700 disabled:opacity-40 disabled:cursor-not-allowed cursor-pointer text-gray-600 dark:text-gray-300 hover:text-gray-900 dark:hover:text-gray-100 transition-all duration-200 hover:border-gray-400 dark:hover:border-gray-500 hover:shadow-md focus:ring-2 focus:ring-primary/20 focus:border-primary\"\n title=\"Previous page\"\n aria-label=\"Go to previous page\"\n >\n <ChevronLeft className=\"w-3.5 h-3.5\" />\n </button>\n\n {/* Mobile Page Selector */}\n <div className=\"sm:hidden\">\n <select\n value={currentPage}\n onChange={(e) =>\n onPageChange(\n Number(e.target.value),\n paginationConfig.pageSize\n )\n }\n className=\"border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100 rounded-xl px-3 py-2 text-sm font-medium focus:ring-2 focus:ring-primary focus:border-primary disabled:opacity-50 disabled:cursor-not-allowed shadow-sm min-w-[80px]\"\n disabled={isServerLoading}\n aria-label=\"Select page\"\n >\n {Array.from(\n { length: paginationConfig.totalPages },\n (_, i) => i + 1\n ).map((page) => (\n <option key={page} value={page}>\n {page}\n </option>\n ))}\n </select>\n </div>\n\n {/* Desktop Page Numbers */}\n <div className=\"hidden sm:flex items-center gap-1 mx-2\">\n {(() => {\n const pages = [];\n const totalPages = paginationConfig.totalPages;\n const current = currentPage;\n const maxVisible =\n typeof window !== \"undefined\" && window.innerWidth >= 768\n ? 7\n : 5;\n\n if (totalPages <= maxVisible) {\n for (let i = 1; i <= totalPages; i++) {\n pages.push(i);\n }\n } else {\n const delta = Math.floor(maxVisible / 2) - 1;\n if (current <= delta + 1) {\n for (let i = 1; i <= maxVisible - 2; i++) pages.push(i);\n pages.push(\"ellipsis1\");\n pages.push(totalPages);\n } else if (current >= totalPages - delta) {\n pages.push(1);\n pages.push(\"ellipsis1\");\n for (\n let i = totalPages - (maxVisible - 3);\n i <= totalPages;\n i++\n )\n pages.push(i);\n } else {\n pages.push(1);\n pages.push(\"ellipsis1\");\n for (let i = current - delta; i <= current + delta; i++)\n pages.push(i);\n pages.push(\"ellipsis2\");\n pages.push(totalPages);\n }\n }\n\n return pages.map((page) => {\n if (typeof page === \"string\") {\n return (\n <div\n key={page}\n className=\"flex items-center justify-center w-8 h-8 text-gray-400 dark:text-gray-500 select-none\"\n aria-hidden=\"true\"\n >\n <span className=\"text-lg leading-none\">⋯</span>\n </div>\n );\n }\n\n const isActive = page === current;\n return (\n <button\n key={page}\n onClick={() =>\n onPageChange(page, paginationConfig.pageSize)\n }\n disabled={isServerLoading}\n className={`inline-flex items-center justify-center w-8 h-8 rounded-full text-sm font-semibold transition-all duration-200 focus:ring-2 focus:ring-primary/20 focus:outline-none cursor-pointer ${\n isActive\n ? \"bg-linear-to-r from-primary to-primary text-white shadow-lg ring-2 ring-primary/20 transform scale-105 z-10 relative\"\n : \"border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-800 text-gray-700 dark:text-gray-300 hover:bg-gray-50 dark:hover:bg-gray-700 hover:border-gray-400 dark:hover:border-gray-500 hover:shadow-md disabled:opacity-40 disabled:cursor-not-allowed\"\n }`}\n aria-label={`Go to page ${page}`}\n aria-current={isActive ? \"page\" : undefined}\n >\n {page}\n </button>\n );\n });\n })()}\n </div>\n\n {/* Next Page Button */}\n <button\n onClick={() =>\n onPageChange(currentPage + 1, paginationConfig.pageSize)\n }\n disabled={\n currentPage === paginationConfig.totalPages || isServerLoading\n }\n className=\"inline-flex items-center justify-center w-8 h-8 rounded-full border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-800 hover:bg-gray-50 dark:hover:bg-gray-700 disabled:opacity-40 disabled:cursor-not-allowed cursor-pointer text-gray-600 dark:text-gray-300 hover:text-gray-900 dark:hover:text-gray-100 transition-all duration-200 hover:border-gray-400 dark:hover:border-gray-500 hover:shadow-md focus:ring-2 focus:ring-primary/20 focus:border-primary\"\n title=\"Next page\"\n aria-label=\"Go to next page\"\n >\n <ChevronRight className=\"w-3.5 h-3.5\" />\n </button>\n\n {/* Last Page Button */}\n <button\n onClick={() =>\n onPageChange(\n paginationConfig.totalPages,\n paginationConfig.pageSize\n )\n }\n disabled={\n currentPage === paginationConfig.totalPages || isServerLoading\n }\n className=\"inline-flex items-center justify-center w-8 h-8 rounded-full border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-800 hover:bg-gray-50 dark:hover:bg-gray-700 disabled:opacity-40 disabled:cursor-not-allowed cursor-pointer text-gray-600 dark:text-gray-300 hover:text-gray-900 dark:hover:text-gray-100 transition-all duration-200 hover:border-gray-400 dark:hover:border-gray-500 hover:shadow-md focus:ring-2 focus:ring-primary/20 focus:border-primary\"\n title=\"Last page\"\n aria-label=\"Go to last page\"\n >\n <ChevronsRight className=\"w-3.5 h-3.5\" />\n </button>\n </div>\n </div>\n )}\n </div>\n </div>\n );\n};\n","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","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","import React, { useEffect } from \"react\";\nimport { createPortal } from \"react-dom\";\nimport {\n autoUpdate,\n flip,\n offset as fOffset,\n shift,\n size,\n useFloating,\n type Placement,\n} from \"@floating-ui/react\";\n\ninterface PopoverProps {\n open: boolean;\n anchorEl: HTMLElement | null;\n onClose: () => void;\n placement?: Placement;\n offset?: number;\n sameWidth?: boolean;\n maxHeight?: number | string;\n zIndex?: number;\n className?: string;\n children: React.ReactNode;\n shouldIgnoreClose?: (target: Node) => boolean;\n}\n\nexport const Popover: React.FC<PopoverProps> = ({\n open,\n anchorEl,\n onClose,\n placement = \"bottom-start\",\n offset = 8,\n sameWidth = false,\n maxHeight,\n zIndex = 1000,\n className,\n children,\n shouldIgnoreClose,\n}) => {\n const { refs, floatingStyles, update } = useFloating({\n placement,\n whileElementsMounted: autoUpdate,\n middleware: [\n fOffset(offset),\n flip({ padding: 8 }),\n shift({ padding: 8 }),\n size({\n apply({ rects, elements, availableHeight, availableWidth }) {\n const el = elements.floating as HTMLElement;\n if (!el) return;\n if (sameWidth) {\n el.style.width = `${rects.reference.width}px`;\n }\n if (maxHeight != null) {\n el.style.maxHeight =\n typeof maxHeight === \"number\"\n ? `${maxHeight}px`\n : String(maxHeight);\n } else {\n el.style.maxHeight = `${availableHeight}px`;\n }\n if (availableWidth != null) {\n el.style.maxWidth = `${availableWidth}px`;\n }\n },\n }),\n ],\n });\n\n // Attach the reference programmatically\n useEffect(() => {\n if (anchorEl) refs.setReference(anchorEl);\n // Force an update when anchor changes\n if (open && update) update();\n }, [anchorEl, open, refs, update]);\n\n // Close on outside click and Escape\n useEffect(() => {\n if (!open) return;\n const onMouseDown = (e: MouseEvent) => {\n const t = e.target as Node;\n if (shouldIgnoreClose && shouldIgnoreClose(t)) return;\n const floating = refs.floating.current;\n if (!floating) return;\n if (\n (anchorEl && anchorEl.contains(t)) ||\n (floating && floating.contains(t))\n ) {\n return; // inside\n }\n onClose();\n };\n const onKeyDown = (e: KeyboardEvent) => {\n if (e.key === \"Escape\") onClose();\n };\n document.addEventListener(\"mousedown\", onMouseDown);\n document.addEventListener(\"keydown\", onKeyDown);\n return () => {\n document.removeEventListener(\"mousedown\", onMouseDown);\n document.removeEventListener(\"keydown\", onKeyDown);\n };\n }, [open, onClose, refs.floating, anchorEl]);\n\n if (!open || !anchorEl) return null;\n\n return createPortal(\n <div\n ref={refs.setFloating}\n data-popover-root=\"true\"\n style={{\n ...floatingStyles,\n zIndex,\n position: floatingStyles.position as any,\n overflowX: \"hidden\",\n }}\n className={className}\n >\n {children}\n </div>,\n document.body\n );\n};\n\nexport default Popover;\n","import {\n useState,\n useRef,\n useEffect,\n ReactNode,\n ChangeEvent,\n useMemo,\n} from \"react\";\nimport {\n Search,\n Download,\n Filter,\n X,\n FileText,\n Database,\n Table,\n Settings,\n Eye,\n EyeOff,\n MoreHorizontal,\n Trash2,\n Copy,\n Edit,\n Archive,\n LocateFixed,\n} from \"lucide-react\";\nimport { ColumnFilterSelector } from \"./ColumnFilterSelector\";\nimport Popover from \"./Popover\";\nimport type { Density } from \"../types\";\n\nexport type ExportFormat = \"csv\" | \"json\" | \"xlsx\";\n// SearchMode removed along with UI\nexport type BulkAction = \"delete\" | \"archive\" | \"copy\" | \"edit\" | \"export\";\n\nexport interface BulkActionOption {\n id: BulkAction;\n label: string;\n icon: ReactNode;\n onClick: (selectedRows: Set<string | number>) => void;\n disabled?: boolean;\n destructive?: boolean;\n}\n\ninterface ColumnVisibility {\n [columnKey: string]: boolean;\n}\n\ninterface SavedFilter {\n id: string;\n name: string;\n globalFilter: string;\n columnFilters: Record<string, any>;\n}\n\ninterface SearchToolbarProps {\n globalFilter: string;\n filteredDataLength: number;\n totalDataLength: number;\n paginationMode?: \"client\" | \"server\";\n totalRows?: number;\n selectedRowsCount?: number;\n selectedRows?: Set<string | number>;\n showExport?: boolean;\n showRefresh?: boolean;\n showColumnToggle?: boolean;\n showBulkActions?: boolean;\n showSavedFilters?: boolean;\n columns?: Array<{\n key: string;\n header: string;\n visible?: boolean;\n filterOptions?: Array<{ label: string; value: string | number }>;\n filterType?: \"text\" | \"select\" | \"multiselect\";\n filterable?: any;\n }>;\n columnVisibility?: ColumnVisibility;\n columnFilters?: Record<string, any>;\n savedFilters?: SavedFilter[];\n bulkActions?: BulkActionOption[];\n isLoading?: boolean;\n exportOptions?: {\n enabled: boolean;\n formats?: ExportFormat[];\n filename?: string;\n onExport?: (format: ExportFormat, exportSelected: boolean) => void;\n };\n onGlobalFilterChange: (e: ChangeEvent<HTMLInputElement>) => void;\n onClearFilters?: () => void;\n onRefresh?: () => void;\n onColumnVisibilityChange?: (columnKey: string, visible: boolean) => void;\n onColumnFilter?: (columnKey: string, filter: any) => void;\n onSaveFilter?: (filter: Omit<SavedFilter, \"id\">) => void;\n onLoadFilter?: (filter: SavedFilter) => void;\n onDeleteFilter?: (filterId: string) => void;\n onRowSelect?: (selectedRows: number[]) => void;\n onToggleFilters?: (show: boolean) => void;\n onResetColumns?: () => void;\n // New: reorder support\n columnOrder?: string[];\n onColumnOrderChange?: (order: string[]) => void;\n pinnedColumns?: Set<string>;\n // Scroll to a specific column in the grid\n onScrollToColumn?: (columnKey: string) => void;\n // Density controls\n density?: Density;\n onDensityChange?: (density: Density) => void;\n showDensityControl?: boolean;\n // Custom content slots\n customLeftContent?: ReactNode;\n customRightContent?: ReactNode;\n}\n\nexport const SearchToolbar = ({\n globalFilter,\n filteredDataLength,\n selectedRowsCount = 0,\n selectedRows = new Set(),\n showExport = true,\n showColumnToggle = true,\n showBulkActions = true,\n columns = [],\n columnVisibility = {},\n columnFilters = {},\n bulkActions = [],\n exportOptions,\n onGlobalFilterChange,\n onClearFilters,\n onColumnVisibilityChange,\n onColumnFilter,\n onResetColumns,\n columnOrder = [],\n onColumnOrderChange,\n pinnedColumns,\n onScrollToColumn,\n density = \"md\",\n onDensityChange,\n showDensityControl = false,\n customLeftContent,\n customRightContent,\n}: SearchToolbarProps) => {\n const [showExportMenu, setShowExportMenu] = useState(false);\n const [showFilters, setShowFilters] = useState(false);\n const [showBulkActionsMenu, setShowBulkActionsMenu] = useState(false);\n const [showColumnMenu, setShowColumnMenu] = useState(false);\n const [columnSearch, setColumnSearch] = useState(\"\");\n // DnD state for in-menu reordering\n const [draggingKey, setDraggingKey] = useState<string | null>(null);\n const [dragOver, setDragOver] = useState<{\n key: string;\n position: \"before\" | \"after\";\n } | null>(null);\n\n const searchInputRef = useRef<HTMLInputElement>(null);\n const exportMenuRef = useRef<HTMLDivElement>(null);\n const bulkActionsRef = useRef<HTMLDivElement>(null);\n const columnMenuRef = useRef<HTMLDivElement>(null);\n const filtersMenuRef = useRef<HTMLDivElement>(null);\n const filtersAnchorRef = useRef<HTMLButtonElement | null>(null);\n const exportAnchorRef = useRef<HTMLButtonElement | null>(null);\n const bulkActionsAnchorRef = useRef<HTMLButtonElement | null>(null);\n const columnAnchorRef = useRef<HTMLButtonElement | null>(null);\n\n // Close menus when clicking outside\n useEffect(() => {\n const handleClickOutside = (event: MouseEvent) => {\n // If clicking inside any portal popover root, do nothing here.\n const isInAnyPopover = (target: any): boolean => {\n let node: any = target;\n while (node) {\n if (\n (node.getAttribute &&\n node.getAttribute(\"data-popover-root\") === \"true\") ||\n (node.dataset && (node.dataset as any).popoverRoot === \"true\")\n )\n return true;\n node = node.parentNode;\n }\n return false;\n };\n\n if (isInAnyPopover(event.target)) return;\n // Helper: detect if click occurred inside a MUI Date Picker popup so we don't treat it as outside\n const isInMuiDatePicker = (target: any): boolean => {\n let node: any = target;\n while (node) {\n if (node.classList) {\n const cls = Array.from(node.classList).join(\" \");\n if (/MuiPickers|MuiDatePicker|MuiPickersPopper/.test(cls))\n return true;\n }\n // Some pickers add role=\"dialog\"\n if (node.getAttribute && node.getAttribute(\"role\") === \"dialog\") {\n const cls = node.className?.toString() || \"\";\n if (/MuiPickers|MuiDatePicker/.test(cls)) return true;\n }\n node = node.parentNode;\n }\n return false;\n };\n\n if (\n exportMenuRef.current &&\n !exportMenuRef.current.contains(event.target as Node)\n ) {\n setShowExportMenu(false);\n }\n if (\n bulkActionsRef.current &&\n !bulkActionsRef.current.contains(event.target as Node)\n ) {\n setShowBulkActionsMenu(false);\n }\n if (\n columnMenuRef.current &&\n !columnMenuRef.current.contains(event.target as Node)\n ) {\n setShowColumnMenu(false);\n }\n if (\n filtersMenuRef.current &&\n !filtersMenuRef.current.contains(event.target as Node) &&\n !isInMuiDatePicker(event.target)\n ) {\n setShowFilters(false);\n }\n };\n\n document.addEventListener(\"mousedown\", handleClickOutside);\n return () => document.removeEventListener(\"mousedown\", handleClickOutside);\n }, []);\n\n const exportFormats = exportOptions?.formats || [\"xlsx\"];\n const hasActiveFilters =\n (globalFilter?.length ?? 0) > 0 ||\n Object.keys(columnFilters || {}).length > 0;\n const hasSelectedRows = selectedRowsCount > 0;\n const visibleColumnsCount = columns.filter(\n (col) => columnVisibility[col.key] !== false\n ).length;\n\n // Build ordered list based on columnOrder, append any missing, then filter by search\n const orderedAll = (() => {\n const fromOrder = columnOrder\n .map((k) => columns.find((c) => c.key === k))\n .filter(Boolean) as typeof columns;\n const missing = columns.filter((c) => !columnOrder.includes(c.key));\n return [...fromOrder, ...missing];\n })();\n\n const filteredColumns = orderedAll.filter((c) =>\n c.header?.toLowerCase()?.includes(columnSearch?.toLowerCase())\n );\n\n // Search mode UI removed per request; default behavior remains in parent handlers.\n\n const getExportIcon = (format: ExportFormat) => {\n switch (format) {\n case \"csv\":\n return <Table size={18} />;\n case \"json\":\n return <Database size={18} />;\n case \"xlsx\":\n return <FileText size={18} />;\n default:\n return <Download size={18} />;\n }\n };\n\n const getBulkActionIcon = (action: BulkAction) => {\n switch (action) {\n case \"delete\":\n return <Trash2 size={18} />;\n case \"archive\":\n return <Archive size={18} />;\n case \"copy\":\n return <Copy size={18} />;\n case \"edit\":\n return <Edit size={18} />;\n case \"export\":\n return <Download size={18} />;\n default:\n return <MoreHorizontal size={18} />;\n }\n };\n\n const getFormatLabel = (format: ExportFormat) => {\n switch (format) {\n case \"csv\":\n return \"CSV\";\n case \"json\":\n return \"JSON\";\n case \"xlsx\":\n return \"Excel\";\n default:\n return format;\n }\n };\n\n const handleExport = (\n format: ExportFormat,\n exportSelected: boolean = false\n ) => {\n exportOptions?.onExport?.(format, exportSelected);\n setShowExportMenu(false);\n };\n\n const handleBulkAction = (action: BulkActionOption) => {\n if (!action.disabled) {\n action.onClick(selectedRows);\n setShowBulkActionsMenu(false);\n }\n };\n\n const handleColumnToggle = (columnKey: string) => {\n const isVisible = columnVisibility[columnKey] !== false;\n onColumnVisibilityChange?.(columnKey, !isVisible);\n };\n\n // DnD handlers for reordering within menu\n const handleDragStart = (\n columnKey: string,\n e: React.DragEvent<HTMLLabelElement>\n ) => {\n setDraggingKey(columnKey);\n e.dataTransfer.effectAllowed = \"move\";\n e.dataTransfer.setData(\"text/plain\", columnKey);\n };\n\n const handleDragOver = (\n targetKey: string,\n e: React.DragEvent<HTMLLabelElement>\n ) => {\n e.preventDefault();\n e.dataTransfer.dropEffect = \"move\";\n const rect = (e.currentTarget as HTMLLabelElement).getBoundingClientRect();\n const midpoint = rect.top + rect.height / 2;\n const position = e.clientY < midpoint ? \"before\" : \"after\";\n setDragOver({ key: targetKey, position });\n };\n\n const handleDrop = (targetKey: string) => {\n const sourceKey = draggingKey;\n if (!sourceKey || sourceKey === targetKey) return;\n\n // respect pinned vs unpinned if provided\n if (pinnedColumns) {\n const sPinned = pinnedColumns.has(sourceKey);\n const tPinned = pinnedColumns.has(targetKey);\n if (sPinned !== tPinned) {\n setDraggingKey(null);\n setDragOver(null);\n return;\n }\n }\n\n const before =\n dragOver?.key === targetKey ? dragOver.position === \"before\" : true;\n const base = (\n columnOrder && columnOrder.length\n ? columnOrder\n : columns.map((c) => c.key)\n ).filter((k) => k !== sourceKey);\n let idx = base.indexOf(targetKey);\n if (idx === -1) {\n onColumnOrderChange?.(base);\n setDraggingKey(null);\n setDragOver(null);\n return;\n }\n if (!before) idx += 1;\n base.splice(idx, 0, sourceKey);\n onColumnOrderChange?.(base);\n setDraggingKey(null);\n setDragOver(null);\n };\n\n const handleDragEnd = () => {\n setDraggingKey(null);\n setDragOver(null);\n };\n\n // Search mode handler removed with UI\n\n const handleResetColumns = () => {\n columns.forEach((column) => {\n onColumnVisibilityChange?.(column.key, column.visible !== false);\n });\n onColumnOrderChange?.(columns.map((column) => column.key));\n onResetColumns?.();\n };\n\n const hasFilterableColumns = useMemo(\n () => columns.some((col) => col.filterable),\n [columns]\n );\n\n return (\n <div\n className=\"bg-white dark:bg-gray-900 border-b border-gray-200 dark:border-gray-700\"\n role=\"toolbar\"\n aria-label=\"Data grid toolbar\"\n >\n {/* Screen reader status region */}\n <div\n id=\"search-results-status\"\n className=\"sr-only\"\n role=\"status\"\n aria-live=\"polite\"\n aria-atomic=\"true\"\n >\n {hasActiveFilters\n ? `Showing ${filteredDataLength} of ${filteredDataLength} rows`\n : `${filteredDataLength} total rows`}\n {hasSelectedRows &&\n `, ${selectedRowsCount} row${\n selectedRowsCount !== 1 ? \"s\" : \"\"\n } selected`}\n </div>\n\n {/* Main Toolbar */}\n <div className=\"px-3 py-2\">\n <div className=\"flex flex-wrap items-center justify-between gap-2 sm:gap-4\">\n {/* Left Section - Actions */}\n <div className=\"order-1 flex items-center flex-wrap gap-2 sm:gap-3\">\n {/* Filter Toggle */}\n <div className=\"relative\" ref={filtersMenuRef}>\n {hasFilterableColumns && (\n <button\n ref={(el) => {\n filtersAnchorRef.current = el;\n }}\n onClick={() => setShowFilters(!showFilters)}\n className={`flex cursor-pointer items-center gap-2 px-3 py-2 border rounded-lg transition-colors ${\n showFilters\n ? \"bg-blue-50 dark:bg-blue-900/40 border-blue-200 dark:border-blue-600 text-blue-700 dark:text-blue-300\"\n : \"border-gray-300 dark:border-gray-600 text-gray-600 dark:text-gray-400 hover:bg-gray-50 dark:hover:bg-gray-800\"\n }`}\n title=\"Toggle column filters (Ctrl+K)\"\n aria-label=\"Toggle column filters\"\n aria-haspopup=\"menu\"\n aria-expanded={showFilters}\n aria-controls=\"filters-menu\"\n aria-keyshortcuts=\"Control+K\"\n >\n <Filter className=\"w-4 h-4\" />\n <span className=\"hidden sm:inline\">Filters</span>\n {Object.keys(columnFilters).length > 0 && (\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n onClearFilters?.();\n }}\n className=\"bg-red-500 cursor-pointer hover:bg-red-600 text-white text-xs px-1.5 py-0.5 rounded-full min-w-5 h-5 flex items-center justify-center transition-all group relative\"\n title=\"Clear all filters\"\n >\n <span className=\"group-hover:hidden\">\n {Object.keys(columnFilters).length}\n </span>\n <X className=\"w-3 h-3 hidden group-hover:block absolute\" />\n </button>\n )}\n </button>\n )}\n <Popover\n open={showFilters}\n anchorEl={filtersAnchorRef.current}\n onClose={() => setShowFilters(false)}\n placement=\"bottom-start\"\n offset={8}\n zIndex={1000}\n maxHeight={520}\n className=\"w-88 sm:w-88 max-w-[90vw] bg-white border border-gray-200 rounded-lg shadow-xl overflow-y-auto min-h-100\"\n shouldIgnoreClose={(target) => {\n // Ignore clicks inside MUI pickers (often portal-rooted)\n let node: any = target;\n while (node) {\n if (node.classList) {\n const cls = Array.from(node.classList).join(\" \");\n if (/MuiPickers|MuiDatePicker|MuiPickersPopper/.test(cls))\n return true;\n }\n if (\n node.getAttribute &&\n node.getAttribute(\"role\") === \"dialog\"\n ) {\n const cls = node.className?.toString() || \"\";\n if (/MuiPickers|MuiDatePicker/.test(cls)) return true;\n }\n node = node.parentNode;\n }\n return false;\n }}\n >\n <div className=\"flex items-center justify-between px-2\">\n <div className=\"p-3 border-b border-gray-100\">\n <h3 className=\"font-medium text-gray-900\">\n Column Filters\n </h3>\n <p className=\"text-xs text-gray-500 mt-1\">\n Filter data by column values\n </p>\n </div>\n <button\n onClick={onClearFilters}\n className=\"px-3 py-1.5 text-sm text-gray-600 border border-gray-300 rounded-md hover:bg-gray-50 transition-colors\"\n >\n Clear All\n </button>\n </div>\n <div className=\"p-3 space-y-3\">\n <ColumnFilterSelector\n columns={columns}\n columnFilters={columnFilters}\n onColumnFilter={onColumnFilter}\n />\n </div>\n <div className=\"flex items-center justify-between p-3 border-t border-gray-200\">\n <div className=\"text-sm text-gray-600\">\n {Object.keys(columnFilters).length > 0 && (\n <span>\n {Object.keys(columnFilters).length} filter\n {Object.keys(columnFilters).length !== 1\n ? \"s\"\n : \"\"}{\" \"}\n applied\n </span>\n )}\n </div>\n </div>\n </Popover>\n </div>\n\n {/* Custom Left Content Slot */}\n {customLeftContent}\n </div>\n\n {/* Right Section - Actions */}\n <div className=\"order-2 flex items-center gap-2 sm:gap-3 flex-wrap sm:flex-nowrap\">\n {/* Custom Right Content Slot */}\n {customRightContent}\n {/* Search mode option removed */}\n\n {/* Bulk Actions */}\n {showBulkActions && hasSelectedRows && bulkActions.length > 0 && (\n <div className=\"relative\" ref={bulkActionsRef}>\n <button\n ref={(el) => {\n bulkActionsAnchorRef.current = el;\n }}\n onClick={() => setShowBulkActionsMenu(!showBulkActionsMenu)}\n className=\"flex items-center gap-2 px-3 py-2.5 bg-orange-600 text-white rounded-lg hover:bg-orange-700 transition-colors\"\n title=\"Bulk actions\"\n >\n <MoreHorizontal className=\"w-4 h-4\" />\n <span className=\"hidden sm:inline text-sm font-medium\">\n Actions ({selectedRowsCount})\n </span>\n </button>\n <Popover\n open={showBulkActionsMenu}\n anchorEl={bulkActionsAnchorRef.current}\n onClose={() => setShowBulkActionsMenu(false)}\n placement=\"bottom-end\"\n offset={8}\n zIndex={1000}\n className=\"w-56 bg-white border border-gray-200 rounded-lg shadow-xl\"\n >\n <div className=\"p-3 border-b border-gray-100\">\n <h3 className=\"font-medium text-gray-900\">Bulk Actions</h3>\n <p className=\"text-xs text-gray-500 mt-1\">\n {selectedRowsCount} row\n {selectedRowsCount !== 1 ? \"s\" : \"\"} selected\n </p>\n </div>\n <div className=\"p-2\">\n {bulkActions.map((action) => (\n <button\n key={action.id}\n onClick={() => handleBulkAction(action)}\n disabled={action.disabled}\n className={`w-full flex items-center gap-3 px-3 py-2 text-sm rounded-md transition-colors ${\n action.disabled\n ? \"text-gray-400 cursor-not-allowed\"\n : action.destructive\n ? \"text-red-700 hover:bg-red-50\"\n : \"text-gray-700 hover:bg-gray-50\"\n }`}\n >\n {getBulkActionIcon(action.id)}\n {action.label}\n </button>\n ))}\n </div>\n </Popover>\n </div>\n )}\n\n {/* Column Visibility Toggle */}\n {showColumnToggle && columns.length > 0 && (\n <div className=\"relative\" ref={columnMenuRef}>\n <button\n ref={(el) => {\n columnAnchorRef.current = el;\n }}\n onClick={() => setShowColumnMenu(!showColumnMenu)}\n className=\"flex items-center gap-2 px-3 py-2.5 border border-gray-300 dark:border-gray-600 rounded-lg hover:bg-gray-50 dark:hover:bg-gray-800 transition-colors\"\n title=\"Column visibility\"\n aria-label={`Column visibility. ${visibleColumnsCount} of ${columns.length} columns visible`}\n aria-haspopup=\"menu\"\n aria-expanded={showColumnMenu}\n aria-controls=\"column-visibility-menu\"\n >\n <Settings className=\"w-4 h-4\" />\n <span className=\"hidden sm:inline text-sm font-medium\">\n Columns\n </span>\n <span className=\"hidden sm:inline text-xs text-gray-500\">\n ({visibleColumnsCount}/{columns.length})\n </span>\n </button>\n\n {/* Column Visibility Dropdown */}\n <Popover\n open={showColumnMenu}\n anchorEl={columnAnchorRef.current}\n onClose={() => setShowColumnMenu(false)}\n placement=\"bottom-end\"\n offset={8}\n zIndex={1000}\n className=\"w-[90vw] sm:w-72 max-w-[90vw] bg-white border border-gray-200 rounded-lg shadow-xl\"\n >\n <div className=\"p-3 border-b border-gray-100\">\n <h3 className=\"font-medium text-gray-900\">\n Show/Hide Columns\n </h3>\n <div className=\"relative mt-2\">\n <Search className=\"absolute left-2 top-1/2 -translate-y-1/2 text-gray-400 w-4 h-4\" />\n <input\n type=\"text\"\n placeholder=\"Search columns...\"\n className=\"w-full pl-8 pr-2 py-1.5 border border-gray-300 rounded-md focus:ring-2 focus:ring-blue-500 focus:border-blue-500 text-sm\"\n value={columnSearch}\n onChange={(e) => setColumnSearch(e.target.value)}\n />\n </div>\n </div>\n <div className=\"max-h-60 overflow-y-auto p-2\">\n {filteredColumns.map((column) => {\n const isVisible = columnVisibility[column.key] !== false;\n return (\n <label\n key={column.key}\n className={`flex items-center gap-3 p-2 hover:bg-gray-50 rounded-md cursor-grab relative ${\n draggingKey === column.key ? \"opacity-60\" : \"\"\n }`}\n draggable\n onDragStart={(e) => handleDragStart(column.key, e)}\n onDragOver={(e) => handleDragOver(column.key, e)}\n onDrop={() => handleDrop(column.key)}\n onDragEnd={handleDragEnd}\n >\n {dragOver?.key === column.key && (\n <span\n className=\"absolute left-2 right-2 h-0.5 bg-blue-600\"\n style={{\n top:\n dragOver.position === \"before\"\n ? 0\n : undefined,\n bottom:\n dragOver.position === \"after\" ? 0 : undefined,\n }}\n />\n )}\n <input\n type=\"checkbox\"\n checked={isVisible}\n onChange={() => handleColumnToggle(column.key)}\n className=\"rounded border-gray-300 text-blue-600 focus:ring-blue-500\"\n />\n {isVisible ? (\n <Eye className=\"w-4 h-4 text-green-600\" />\n ) : (\n <EyeOff className=\"w-4 h-4 text-gray-400\" />\n )}\n <span className=\"text-sm text-gray-700\">\n {column.header}\n </span>\n <button\n type=\"button\"\n onClick={(e) => {\n e.preventDefault();\n e.stopPropagation();\n // Ensure column is visible before scrolling\n if (!isVisible) {\n onColumnVisibilityChange?.(column.key, true);\n // Wait for visibility update to render before scrolling\n requestAnimationFrame(() =>\n onScrollToColumn?.(column.key)\n );\n } else {\n onScrollToColumn?.(column.key);\n }\n }}\n className=\"ml-auto p-1 text-gray-400 hover:text-blue-600\"\n title=\"Scroll to this column\"\n aria-label={`Scroll to column ${column.header}`}\n >\n <LocateFixed className=\"w-4 h-4\" />\n </button>\n </label>\n );\n })}\n </div>\n <div className=\"p-2 border-t border-gray-100\">\n <div className=\"flex items-center justify-between\">\n <label className=\"flex items-center gap-2 text-sm text-gray-600 hover:text-gray-900 cursor-pointer hover:bg-gray-50 px-3 py-1.5 rounded transition-colors min-h-9\">\n <input\n type=\"checkbox\"\n checked={filteredColumns.every(\n (col) => columnVisibility[col.key] !== false\n )}\n onChange={(e) => {\n filteredColumns.forEach((column) => {\n onColumnVisibilityChange?.(\n column.key,\n e.target.checked\n );\n });\n }}\n className=\"rounded border-gray-300 text-blue-600 focus:ring-blue-500\"\n />\n <span>Select All</span>\n </label>\n <button\n className=\"text-sm text-gray-600 hover:text-gray-900 hover:bg-gray-50 px-3 py-1.5 rounded transition-colors min-h-9\"\n onClick={() => {\n handleResetColumns();\n setShowColumnMenu(false);\n }}\n >\n Reset Columns\n </button>\n </div>\n {columnSearch && (\n <button\n className=\"text-xs text-blue-600 hover:underline mt-2\"\n onClick={() => setColumnSearch(\"\")}\n >\n Clear Search\n </button>\n )}\n </div>\n </Popover>\n </div>\n )}\n\n {/* Density Selector */}\n {showDensityControl && (\n <div className=\"flex items-center\">\n <div\n className=\"flex items-center gap-1 border border-gray-300 rounded-lg p-1\"\n title=\"Row density\"\n >\n {([\"sm\", \"md\", \"lg\"] as Density[]).map((d) => (\n <button\n key={d}\n className={`px-2 py-1 text-xs rounded-md ${\n density === d\n ? \"bg-blue-600 text-white\"\n : \"text-gray-700 hover:bg-gray-100\"\n }`}\n onClick={() => onDensityChange?.(d)}\n >\n {d.toUpperCase()}\n </button>\n ))}\n </div>\n </div>\n )}\n\n {/* Enhanced Search Panel - Right aligned */}\n <div className=\"flex items-center gap-2 order-3 w-full sm:order-0 sm:w-auto flex-1 min-w-0\">\n {/* Search Input with integrated match type */}\n <div className=\"relative\">\n <Search\n className=\"absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400 w-4 h-4\"\n aria-hidden=\"true\"\n />\n <input\n ref={searchInputRef}\n type=\"text\"\n placeholder={`Search...`}\n className=\"w-full sm:w-64 pl-10 pr-10 py-2.5 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-800 dark:text-gray-200 transition-colors\"\n value={globalFilter}\n onChange={onGlobalFilterChange}\n aria-label=\"Search all columns\"\n aria-keyshortcuts=\"Control+F\"\n aria-describedby=\"search-results-status\"\n />\n {globalFilter && (\n <button\n onClick={() =>\n onGlobalFilterChange({\n target: { value: \"\" },\n } as ChangeEvent<HTMLInputElement>)\n }\n className=\"absolute right-2 top-1/2 transform -translate-y-1/2 text-gray-400 hover:text-gray-600 transition-colors\"\n title=\"Clear search\"\n >\n <X className=\"w-4 h-4\" />\n </button>\n )}\n </div>\n </div>\n\n {/* Export Icon Button */}\n {showExport && exportOptions?.enabled && (\n <div className=\"relative\" ref={exportMenuRef}>\n <button\n ref={(el) => {\n exportAnchorRef.current = el;\n }}\n onClick={() => setShowExportMenu(!showExportMenu)}\n className=\"p-2.5 cursor-pointer border border-gray-300 dark:border-gray-600 rounded-lg hover:bg-gray-50 dark:hover:bg-gray-800 text-gray-700 dark:text-gray-300 transition-colors\"\n aria-label={`Export data${\n hasSelectedRows\n ? ` (${selectedRowsCount} rows selected)`\n : ` (${filteredDataLength} rows)`\n }`}\n aria-haspopup=\"menu\"\n aria-expanded={showExportMenu}\n aria-controls=\"export-menu\"\n aria-keyshortcuts=\"Control+E\"\n title=\"Export data (Ctrl+E)\"\n >\n <Download className=\"w-4 h-4\" />\n </button>\n <Popover\n open={showExportMenu}\n anchorEl={exportAnchorRef.current}\n onClose={() => setShowExportMenu(false)}\n placement=\"bottom-end\"\n offset={8}\n zIndex={1000}\n className=\"w-[90vw] sm:w-64 max-w-[90vw] bg-white border border-gray-200 rounded-lg shadow-xl\"\n >\n <div className=\"p-3 border-b border-gray-100\">\n <h3 className=\"font-medium text-gray-900\">Export Data</h3>\n <p className=\"text-xs text-gray-500 mt-1\">\n Choose format and data scope\n </p>\n </div>\n\n <div className=\"p-2\">\n {/* Export All */}\n <div className=\"mb-3\">\n <p className=\"text-xs font-medium text-gray-700 mb-2 px-2\">\n Export All Data\n </p>\n <div className=\"space-y-1\">\n {exportFormats.map((format) => (\n <button\n key={`all-${format}`}\n onClick={() => handleExport(format, false)}\n className=\" cursor-pointer w-full flex items-center gap-3 px-3 py-2 text-sm text-gray-700 hover:bg-gray-50 rounded-md transition-colors\"\n >\n {getExportIcon(format)}\n <span>Export as {getFormatLabel(format)}</span>\n <span className=\"ml-auto text-xs text-gray-500\">\n {filteredDataLength.toLocaleString()} rows\n </span>\n </button>\n ))}\n </div>\n </div>\n\n {/* Export Selected */}\n {hasSelectedRows && (\n <div>\n <p className=\"text-xs font-medium text-gray-700 mb-2 px-2\">\n Export Selected\n </p>\n <div className=\"space-y-1\">\n {exportFormats.map((format) => (\n <button\n key={`selected-${format}`}\n onClick={() => handleExport(format, true)}\n className=\"w-full flex items-center gap-3 px-3 py-2 text-sm text-blue-700 hover:bg-blue-50 rounded-md transition-colors\"\n >\n {getExportIcon(format)}\n <span>Export as {getFormatLabel(format)}</span>\n <span className=\"ml-auto text-xs text-blue-500\">\n {selectedRowsCount} rows\n </span>\n </button>\n ))}\n </div>\n </div>\n )}\n </div>\n </Popover>\n </div>\n )}\n </div>\n </div>\n </div>\n </div>\n );\n};\n","import { useEffect, useMemo, useRef } from \"react\";\n\ntype SortCfg = { key: string; direction: \"asc\" | \"desc\" } | null;\n\nexport function useDataWorker(options?: { enabled?: boolean }) {\n const enabled = options?.enabled ?? true;\n const workerRef = useRef<Worker | null>(null);\n const resolverRef = useRef<((rows: any[]) => void) | null>(null);\n\n useEffect(() => {\n if (!enabled) return;\n // Use Blob-based worker to avoid path resolution issues in consuming projects\n try {\n const code = `(${workerFactory.toString()})()`;\n const blob = new Blob([code], { type: \"application/javascript\" });\n workerRef.current = new Worker(URL.createObjectURL(blob));\n } catch (err) {\n console.warn(\n \"Failed to initialize data worker, grid will run in main thread mode.\",\n err,\n );\n workerRef.current = null;\n }\n const w = workerRef.current;\n w!.onmessage = (e: MessageEvent) => {\n const data = e.data;\n if (data && data.type === \"result\") {\n resolverRef.current?.(data.payload.rows);\n resolverRef.current = null;\n }\n };\n return () => {\n w?.terminate();\n workerRef.current = null;\n resolverRef.current = null;\n };\n }, [enabled]);\n\n const process = useMemo(() => {\n return (rows: any[], sort: SortCfg) => {\n if (!enabled || !workerRef.current) {\n // Inline fallback\n if (!rows || rows.length === 0 || !sort?.key)\n return Promise.resolve(rows);\n const dir = sort.direction === \"asc\" ? 1 : -1;\n const out = [...rows].sort((a, b) => {\n const av = a?.[sort.key!];\n const bv = b?.[sort.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 return Promise.resolve(out);\n }\n return new Promise<any[]>((resolve) => {\n resolverRef.current = resolve;\n workerRef.current!.postMessage({\n type: \"process\",\n payload: { rows, sort },\n });\n });\n };\n }, [enabled]);\n\n return { process };\n}\n\n// Inlined worker code for Blob fallback\nfunction workerFactory() {\n const ctx = self as any;\n const processData = (\n rows: any[],\n sort?: { key: string; direction: \"asc\" | \"desc\" } | null,\n ) => {\n if (!rows || rows.length === 0) return rows;\n let out = rows;\n if (sort && sort.key) {\n const { key, direction } = sort;\n const dir = direction === \"asc\" ? 1 : -1;\n out = [...rows].sort((a, b) => {\n const av = a?.[key];\n const bv = b?.[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 const as = String(av);\n const bs = String(bv);\n return as.localeCompare(bs) * dir;\n });\n }\n return out;\n };\n (ctx as any).onmessage = (e: MessageEvent) => {\n const msg = e.data;\n if (!msg || msg.type !== \"process\") return;\n try {\n const { rows, sort } = msg.payload;\n const result = processData(rows, sort ?? null);\n (ctx as any).postMessage({ type: \"result\", payload: { rows: result } });\n } catch (err) {\n (ctx as any).postMessage({\n type: \"result\",\n payload: { rows: e.data?.payload?.rows || [] },\n });\n }\n };\n}\n","import { useCallback } from \"react\";\nimport { GridRow, GridColumn } from \"../types\";\n\nexport type ExportFormat = \"csv\" | \"json\" | \"xlsx\";\n\ninterface UseExportProps {\n data: GridRow[];\n columns: GridColumn[];\n selectedRows?: Set<string | number>;\n filename?: string;\n columnVisibility?: Record<string, boolean>;\n}\n\nexport const useExport = ({\n data,\n columns,\n selectedRows,\n filename = \"data\",\n columnVisibility,\n}: UseExportProps) => {\n // Filter only visible columns\n const visibleColumns = columnVisibility\n ? columns.filter((col) => columnVisibility[col.key] !== false)\n : columns;\n const downloadFile = useCallback(\n (content: string, fileName: string, mimeType: string) => {\n const blob = new Blob([content], { type: mimeType });\n const url = URL.createObjectURL(blob);\n const link = document.createElement(\"a\");\n link.href = url;\n link.download = fileName;\n document.body.appendChild(link);\n link.click();\n document.body.removeChild(link);\n URL.revokeObjectURL(url);\n },\n [],\n );\n\n const exportToCSV = useCallback(\n (exportData: GridRow[], exportFilename: string) => {\n const headers = visibleColumns.map((col) => col.header).join(\",\");\n const rows = exportData.map((row) =>\n visibleColumns\n .map((col) => {\n const value = row[col.key];\n // Escape commas and quotes in CSV\n const stringValue = String(value || \"\");\n return stringValue.includes(\",\") || stringValue.includes('\"')\n ? `\"${stringValue.replace(/\"/g, '\"\"')}\"`\n : stringValue;\n })\n .join(\",\"),\n );\n\n const csvContent = [headers, ...rows].join(\"\\n\");\n downloadFile(csvContent, `${exportFilename}.csv`, \"text/csv\");\n },\n [visibleColumns, downloadFile],\n );\n\n const exportToJSON = useCallback(\n (exportData: GridRow[], exportFilename: string) => {\n const jsonData = exportData.map((row) => {\n const cleanRow: Record<string, any> = {};\n visibleColumns.forEach((col) => {\n cleanRow[col.header] = row[col.key];\n });\n return cleanRow;\n });\n\n const jsonContent = JSON.stringify(jsonData, null, 2);\n downloadFile(jsonContent, `${exportFilename}.json`, \"application/json\");\n },\n [visibleColumns, downloadFile],\n );\n\n const exportToXLSX = useCallback(\n async (exportData: GridRow[], exportFilename: string) => {\n // Dynamic import to keep main bundle light\n const [ExcelJS, { saveAs }] = await Promise.all([\n import(\"exceljs\"),\n import(\"file-saver\"),\n ]);\n\n const workbook = new (ExcelJS.default || ExcelJS).Workbook();\n const sheet = workbook.addWorksheet(\"Data\");\n\n // Define columns with headers and keys; approximate widths by sampling\n const SAMPLE_COUNT = 50; // limit for width estimation\n const sampled = exportData.slice(0, SAMPLE_COUNT);\n\n const coerceValue = (v: any) => {\n if (v == null) return \"\";\n if (typeof v === \"number\") return v;\n if (typeof v === \"boolean\") return v;\n if (v instanceof Date) return v;\n const s = String(v);\n\n // If contains letters, keep as text (prevents CR-154, INV-123 conversion)\n if (/[a-zA-Z]/.test(s)) return s;\n\n // Only convert to number if purely numeric\n if (/^-?[\\d,]+\\.?\\d*$/.test(s.trim())) {\n const n = Number(s.replace(/,/g, \"\"));\n if (Number.isFinite(n)) return n;\n }\n\n return s;\n };\n\n // Compute widths in \"characters\" based on header and sample values\n const columnDefs = visibleColumns.map((col) => {\n const header = col.header || col.key;\n let maxLen = header?.length || 10;\n for (const row of sampled) {\n const raw = row[col.key];\n const s = raw == null ? \"\" : String(raw);\n if (s.length > maxLen) maxLen = s.length;\n }\n // ExcelJS width is approx in characters; clamp for readability\n const width = Math.max(8, Math.min(50, Math.ceil(maxLen * 1.1)));\n return { header, key: col.key, width };\n });\n\n sheet.columns = columnDefs as any;\n\n // Add rows\n for (const row of exportData) {\n const obj: Record<string, any> = {};\n for (const col of visibleColumns) {\n obj[col.key] = coerceValue(row[col.key]);\n }\n sheet.addRow(obj);\n }\n\n // Format header row bold\n const headerRow = sheet.getRow(1);\n headerRow.font = { bold: true };\n\n // Auto-filter\n sheet.autoFilter = {\n from: { row: 1, column: 1 },\n to: { row: Math.max(1, sheet.rowCount), column: visibleColumns.length },\n };\n\n const buffer = await workbook.xlsx.writeBuffer();\n const blob = new Blob([buffer], {\n type: \"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet\",\n });\n saveAs(blob, `${exportFilename}.xlsx`);\n },\n [visibleColumns],\n );\n\n const exportData = useCallback(\n (format: ExportFormat, exportSelected: boolean = false) => {\n const exportData =\n exportSelected && selectedRows && selectedRows.size > 0\n ? data.filter((row) => selectedRows.has(row.id))\n : data;\n\n const exportFilename =\n exportSelected && selectedRows && selectedRows.size > 0\n ? `${filename}_selected_${selectedRows.size}_rows`\n : filename;\n\n switch (format) {\n case \"csv\":\n exportToCSV(exportData, exportFilename);\n break;\n case \"json\":\n exportToJSON(exportData, exportFilename);\n break;\n case \"xlsx\":\n // Fire and forget\n void exportToXLSX(exportData, exportFilename);\n break;\n default:\n console.warn(`Unsupported export format: ${format}`);\n }\n },\n [data, selectedRows, filename, exportToCSV, exportToJSON, exportToXLSX],\n );\n\n const canExportSelected = selectedRows && selectedRows.size > 0;\n\n return {\n exportData,\n canExportSelected,\n selectedCount: selectedRows?.size || 0,\n };\n};\n","import { useState, useMemo, useCallback } from \"react\";\nimport { PaginationConfig, GridRow } from \"../types\";\n\ninterface UsePaginationProps {\n data: GridRow[];\n pagination?: {\n enabled?: boolean;\n mode?: \"client\" | \"server\";\n pageSize?: number;\n showPageSizeSelector?: boolean;\n pageSizeOptions?: number[];\n serverConfig?: {\n enabled: boolean;\n onPageChange: (page: number, pageSize: number) => Promise<void> | void;\n onPageSizeChange?: (pageSize: number) => Promise<void> | void;\n loading?: boolean;\n totalRows: number;\n };\n };\n}\n\nexport const usePagination = ({ data, pagination }: UsePaginationProps) => {\n const [currentPage, setCurrentPage] = useState(1);\n const [pageSize, setPageSize] = useState(pagination?.pageSize || 50);\n const [isServerLoading, setIsServerLoading] = useState(false);\n\n const resetPage = useCallback(() => {\n setCurrentPage(1);\n }, []);\n\n const paginationConfig: PaginationConfig = useMemo(() => {\n const isEnabled = pagination?.enabled !== false;\n const mode = pagination?.mode || \"client\";\n const totalRows =\n mode === \"server\"\n ? pagination?.serverConfig?.totalRows || 0\n : data.length;\n const totalPages = Math.ceil(totalRows / pageSize);\n\n return {\n enabled: isEnabled,\n pageSize,\n currentPage,\n totalPages,\n totalRows,\n showPageSizeSelector: pagination?.showPageSizeSelector !== false,\n pageSizeOptions: pagination?.pageSizeOptions || [25, 50, 100, 200, 500],\n };\n }, [pagination, pageSize, currentPage, data.length]);\n\n const paginatedData = useMemo(() => {\n if (!paginationConfig.enabled || pagination?.mode === \"server\") {\n return data;\n }\n\n const startIndex = (currentPage - 1) * pageSize;\n const endIndex = startIndex + pageSize;\n return data.slice(startIndex, endIndex);\n }, [data, currentPage, pageSize, paginationConfig.enabled, pagination?.mode]);\n\n const handlePageChange = useCallback(\n async (page: number) => {\n if (\n pagination?.mode === \"server\" &&\n pagination?.serverConfig?.onPageChange\n ) {\n setIsServerLoading(true);\n try {\n await pagination.serverConfig.onPageChange(page, pageSize);\n } finally {\n setIsServerLoading(false);\n }\n }\n setCurrentPage(page);\n },\n [pagination, pageSize]\n );\n\n const handlePageSizeChange = useCallback(\n async (newPageSize: number) => {\n if (\n pagination?.mode === \"server\" &&\n pagination?.serverConfig?.onPageSizeChange\n ) {\n setIsServerLoading(true);\n try {\n await pagination.serverConfig.onPageSizeChange(newPageSize);\n } finally {\n setIsServerLoading(false);\n }\n }\n setPageSize(newPageSize);\n setCurrentPage(1);\n },\n [pagination]\n );\n\n return {\n currentPage,\n resetPage,\n paginationConfig,\n paginatedData,\n isServerLoading,\n handlePageChange,\n handlePageSizeChange,\n };\n};\n","import { useState, useCallback, useEffect } from \"react\";\nimport { GridRow } from \"../types\";\n\ninterface UseSelectionProps {\n data: GridRow[];\n selectedRowIds?: Iterable<string | number> | null;\n onRowSelect?: (row: GridRow) => void;\n getRowId?: (row: GridRow) => string | number;\n}\n\nconst areSetsEqual = (a: Set<string | number>, b: Set<string | number>) => {\n if (a.size !== b.size) return false;\n for (const value of a) {\n if (!b.has(value)) return false;\n }\n return true;\n};\n\nexport const useSelection = ({\n data,\n selectedRowIds,\n onRowSelect,\n getRowId,\n}: UseSelectionProps) => {\n // Extract row IDs based on getRowId or fallback to row.id\n const extractRowId = useCallback(\n (row: GridRow, rowIndex?: number): string | number => {\n if (getRowId) {\n return getRowId(row);\n }\n if (row.id !== undefined) {\n return row.id;\n }\n // Fallback: use row index\n if (process.env.NODE_ENV === \"development\" && rowIndex === 0) {\n console.warn(\n \"[CustomDataGrid] No row IDs found. Either:\\n\" +\n \" 1. Add 'id' property to each row object, or\\n\" +\n \" 2. Provide 'getRowId' prop to extract a unique identifier.\\n\" +\n \" Using row index as fallback ID, but this may cause issues with pagination/filtering.\"\n );\n }\n return rowIndex ?? 0;\n },\n [getRowId]\n );\n\n const [selectedRows, setSelectedRows] = useState<Set<string | number>>(\n () => new Set(selectedRowIds ? Array.from(selectedRowIds) : [])\n );\n\n useEffect(() => {\n if (!selectedRowIds) return;\n setSelectedRows((prev) => {\n const next = new Set(Array.from(selectedRowIds));\n if (areSetsEqual(prev, next)) return prev;\n return next;\n });\n }, [selectedRowIds]);\n\n useEffect(() => {\n const validIds = new Set(data.map((row, idx) => extractRowId(row, idx)));\n setSelectedRows((prev) => {\n const next = new Set<string | number>();\n prev.forEach((id) => {\n if (validIds.has(id)) next.add(id);\n });\n if (areSetsEqual(prev, next)) return prev;\n return next;\n });\n }, [data, extractRowId]);\n\n const handleRowSelect = useCallback(\n (rowId: string | number, isSelected: boolean) => {\n setSelectedRows((prev) => {\n const newSet = new Set(prev);\n if (isSelected) {\n newSet.add(rowId);\n } else {\n newSet.delete(rowId);\n }\n return newSet;\n });\n\n if (onRowSelect) {\n const row = data.find((item, idx) => extractRowId(item, idx) === rowId);\n if (row) onRowSelect(row);\n }\n },\n [data, onRowSelect, extractRowId]\n );\n\n const handleSelectAll = useCallback(\n (isSelected: boolean) => {\n if (isSelected) {\n setSelectedRows(new Set(data.map((row, idx) => extractRowId(row, idx))));\n } else {\n setSelectedRows(new Set());\n }\n },\n [data, extractRowId]\n );\n\n return {\n selectedRows,\n handleRowSelect,\n handleSelectAll,\n };\n};\n","import {\n useState,\n useMemo,\n useCallback,\n useEffect,\n useRef,\n UIEvent,\n} from \"react\";\nimport { VirtualizedRange, GridRow } from \"../types\";\n\ninterface UseVirtualizationProps {\n data: GridRow[];\n virtualized?: boolean;\n rowHeight?: number;\n overscan?: number;\n height: number;\n}\n\nexport const useVirtualization = ({\n data,\n virtualized = true,\n rowHeight = 40,\n overscan = 10,\n height,\n}: UseVirtualizationProps) => {\n const scrollRef = useRef<HTMLDivElement>(null);\n const [scrollTop, setScrollTop] = useState(0);\n const rafIdRef = useRef<number | null>(null);\n const pendingScrollTopRef = useRef<number>(0);\n const [containerHeight, setContainerHeight] = useState(height);\n\n const virtualizedRange = useMemo((): VirtualizedRange => {\n if (!virtualized) {\n return {\n startIndex: 0,\n endIndex: data.length - 1,\n offsetY: 0,\n };\n }\n\n // Dynamic overscan scaled by viewport height (rows in view)\n const rowsInView = Math.max(1, Math.ceil(containerHeight / rowHeight));\n const dynamicOverscan = Math.max(\n overscan,\n Math.max(8, Math.min(rowsInView * 2, 50))\n );\n\n const startIndex = Math.max(\n 0,\n Math.floor(scrollTop / rowHeight) - dynamicOverscan\n );\n const visibleCount = Math.ceil(containerHeight / rowHeight);\n const endIndex = Math.min(\n data.length - 1,\n startIndex + visibleCount + dynamicOverscan * 2\n );\n\n const offsetY = startIndex * rowHeight;\n return { startIndex, endIndex, offsetY };\n }, [\n scrollTop,\n containerHeight,\n data.length,\n rowHeight,\n overscan,\n virtualized,\n ]);\n\n const visibleData = useMemo(() => {\n if (!virtualized) return data;\n return data.slice(\n virtualizedRange.startIndex,\n virtualizedRange.endIndex + 1\n );\n }, [data, virtualizedRange, virtualized]);\n\n const totalHeight = data.length * rowHeight;\n\n const handleScroll = useCallback((e: UIEvent<HTMLDivElement>) => {\n const target = e.target as HTMLDivElement;\n const nextTop = target.scrollTop;\n pendingScrollTopRef.current = nextTop;\n if (rafIdRef.current == null) {\n rafIdRef.current = requestAnimationFrame(() => {\n rafIdRef.current = null;\n setScrollTop(pendingScrollTopRef.current);\n });\n }\n }, []);\n\n useEffect(() => {\n return () => {\n if (rafIdRef.current != null) cancelAnimationFrame(rafIdRef.current);\n };\n }, []);\n\n useEffect(() => {\n setContainerHeight(height);\n }, [height]);\n\n return {\n scrollRef,\n virtualizedRange,\n visibleData,\n totalHeight,\n handleScroll,\n };\n};\n","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","import {\n useState,\n useMemo,\n useCallback,\n useDeferredValue,\n useRef,\n} from \"react\";\nimport {\n GridRow,\n GridColumn,\n ActiveFilters,\n ColumnFilterValue,\n} from \"../types\";\n\ninterface UseAdvancedFilteringProps {\n data: GridRow[];\n columns: GridColumn[];\n}\n\nexport const useAdvancedFiltering = ({\n data,\n columns,\n}: UseAdvancedFilteringProps) => {\n const [globalFilter, setGlobalFilter] = useState<string>(\"\");\n const [columnFilters, setColumnFilters] = useState<ActiveFilters>({});\n\n // Use deferred values for better performance with large datasets\n const deferredGlobalFilter = useDeferredValue(globalFilter);\n const deferredColumnFilters = useDeferredValue(columnFilters);\n\n const applyColumnFilter = useCallback(\n (row: GridRow, columnKey: string, filter: ColumnFilterValue): boolean => {\n const value = row[columnKey];\n\n switch (filter.type) {\n case \"text\":\n const textValue = String(value || \"\").toLowerCase();\n const filterText = String(filter.value || \"\").toLowerCase();\n\n switch (filter.operator) {\n case \"contains\":\n return textValue.includes(filterText);\n case \"equals\":\n return textValue === filterText;\n case \"startsWith\":\n return textValue.startsWith(filterText);\n case \"endsWith\":\n return textValue.endsWith(filterText);\n default:\n return textValue.includes(filterText);\n }\n\n case \"number\":\n const numValue = parseFloat(value);\n const filterNum = parseFloat(filter.value);\n\n if (isNaN(numValue) || isNaN(filterNum)) return false;\n\n switch (filter.operator) {\n case \"equals\":\n return numValue === filterNum;\n case \"gt\":\n return numValue > filterNum;\n case \"gte\":\n return numValue >= filterNum;\n case \"lt\":\n return numValue < filterNum;\n case \"lte\":\n return numValue <= filterNum;\n case \"between\":\n const secondNum = parseFloat(filter.secondValue);\n return (\n !isNaN(secondNum) &&\n numValue >= filterNum &&\n numValue <= secondNum\n );\n default:\n return numValue === filterNum;\n }\n\n case \"date\":\n const dateValue = new Date(value);\n const filterDate = new Date(filter.value);\n\n if (isNaN(dateValue.getTime()) || isNaN(filterDate.getTime()))\n return false;\n\n // Compare dates without time\n const dateOnly = new Date(\n dateValue.getFullYear(),\n dateValue.getMonth(),\n dateValue.getDate()\n );\n const filterDateOnly = new Date(\n filterDate.getFullYear(),\n filterDate.getMonth(),\n filterDate.getDate()\n );\n\n switch (filter.operator) {\n case \"equals\":\n return dateOnly.getTime() === filterDateOnly.getTime();\n case \"gt\":\n return dateOnly.getTime() > filterDateOnly.getTime();\n case \"gte\":\n return dateOnly.getTime() >= filterDateOnly.getTime();\n case \"lt\":\n return dateOnly.getTime() < filterDateOnly.getTime();\n case \"lte\":\n return dateOnly.getTime() <= filterDateOnly.getTime();\n case \"between\":\n const secondDate = new Date(filter.secondValue);\n if (isNaN(secondDate.getTime())) return false;\n const secondDateOnly = new Date(\n secondDate.getFullYear(),\n secondDate.getMonth(),\n secondDate.getDate()\n );\n return (\n dateOnly.getTime() >= filterDateOnly.getTime() &&\n dateOnly.getTime() <= secondDateOnly.getTime()\n );\n default:\n return dateOnly.getTime() === filterDateOnly.getTime();\n }\n\n case \"boolean\":\n return Boolean(value) === Boolean(filter.value);\n\n case \"multiselect\":\n const filterValues = Array.isArray(filter.value)\n ? filter.value\n : [filter.value];\n\n // Normalize blank values for comparison\n const normalizeBlank = (val: any) => {\n if (\n val === null ||\n val === undefined ||\n val === \"\" ||\n (typeof val === \"string\" && val.trim() === \"\")\n ) {\n return null;\n }\n return val;\n };\n\n const normalizedValue = normalizeBlank(value);\n const normalizedFilterValues = filterValues.map(normalizeBlank);\n\n return normalizedFilterValues.includes(normalizedValue);\n\n default:\n return true;\n }\n },\n []\n );\n\n // Optimized search cache with size limits for large datasets\n const rowSearchCacheRef = useRef<WeakMap<GridRow, string>>(new WeakMap());\n const cacheSizeRef = useRef(0);\n const maxCacheSize = 5000; // Limit cache size for memory management\n\n // Clear cache when it gets too large\n const clearCacheIfNeeded = useCallback(() => {\n if (cacheSizeRef.current > maxCacheSize) {\n rowSearchCacheRef.current = new WeakMap();\n cacheSizeRef.current = 0;\n }\n }, []);\n\n const filteredData = useMemo(() => {\n clearCacheIfNeeded();\n\n // Short-circuit quickly when nothing active\n const hasColumnFilters = Object.keys(deferredColumnFilters).length > 0;\n const hasGlobal = !!deferredGlobalFilter;\n if (!hasColumnFilters && !hasGlobal) return data;\n\n const lowerGlobal = deferredGlobalFilter.toLowerCase();\n const cache = rowSearchCacheRef.current!;\n let working = data;\n\n // Apply column filters first (usually more selective)\n if (hasColumnFilters) {\n for (const [columnKey, filter] of Object.entries(deferredColumnFilters)) {\n if (!filter) continue;\n working = working.filter((row) =>\n applyColumnFilter(row, columnKey, filter)\n );\n // Early exit if no results\n if (working.length === 0) break;\n }\n }\n\n // Apply global filter with optimized search\n if (hasGlobal && working.length > 0) {\n // Only search visible columns for better performance\n const visibleColumns = columns.filter((col) => col.visible !== false);\n\n working = working.filter((row) => {\n let packed = cache.get(row);\n if (packed == null) {\n packed = visibleColumns\n .map((c) => {\n const v = row[c.key];\n if (v == null) return \"\";\n if (typeof v === \"string\") return v.toLowerCase();\n if (typeof v === \"number\" || typeof v === \"boolean\")\n return String(v).toLowerCase();\n // Limit object stringification to prevent performance issues\n const s = String(v);\n return s.length > 100 ? s.slice(0, 100) : s;\n })\n .join(\"|\");\n\n // Cache with size limit\n if (cacheSizeRef.current < maxCacheSize) {\n cache.set(row, packed);\n cacheSizeRef.current++;\n }\n }\n return packed.includes(lowerGlobal);\n });\n }\n\n return working;\n }, [\n data,\n deferredColumnFilters,\n deferredGlobalFilter,\n columns,\n applyColumnFilter,\n clearCacheIfNeeded,\n ]);\n\n const setColumnFilter = useCallback(\n (columnKey: string, filter: ColumnFilterValue | null) => {\n setColumnFilters((prev) => {\n const newFilters = { ...prev };\n if (filter) {\n newFilters[columnKey] = filter;\n } else {\n delete newFilters[columnKey];\n }\n return newFilters;\n });\n },\n []\n );\n\n const clearColumnFilter = useCallback(\n (columnKey: string) => {\n setColumnFilter(columnKey, null);\n },\n [setColumnFilter]\n );\n\n const clearAllFilters = useCallback(() => {\n setColumnFilters({});\n setGlobalFilter(\"\");\n // Clear cache when filters are cleared\n rowSearchCacheRef.current = new WeakMap();\n cacheSizeRef.current = 0;\n }, []);\n\n const getActiveFilterCount = useCallback(() => {\n let count = 0;\n if (deferredGlobalFilter) count++;\n count += Object.keys(deferredColumnFilters).length;\n return count;\n }, [deferredGlobalFilter, deferredColumnFilters]);\n\n return {\n globalFilter,\n setGlobalFilter,\n columnFilters: deferredColumnFilters,\n filteredData,\n setColumnFilter,\n clearColumnFilter,\n clearAllFilters,\n getActiveFilterCount,\n };\n};\n","import { useState, useMemo, useEffect } from \"react\";\nimport { GridRow, GroupConfig, GridColumn } from \"../types\";\n\ninterface UseGroupingProps {\n data: GridRow[];\n columns: GridColumn[];\n}\n\nexport const useGrouping = ({ data, columns }: UseGroupingProps) => {\n const [groupConfig, setGroupConfig] = useState<GroupConfig>({\n columnKeys: [],\n expanded: new Set<string>(),\n });\n\n type FlatRow = GridRow & {\n _isGroupHeader?: boolean;\n _groupKey?: string; // path string e.g. City=SF|Status=Open\n _groupCount?: number;\n _groupLevel?: number; // 0-based\n _groupColumnKey?: string;\n _groupValue?: string;\n _groupRows?: GridRow[];\n _isGroupFooter?: boolean;\n _groupAgg?: Record<string, any>;\n };\n\n const UNKNOWN = \"Unknown\";\n const buildPath = (parts: string[]) => parts.join(\"|\");\n\n // Build flattened rows with nested group headers based on columnKeys\n const displayData = useMemo<FlatRow[]>(() => {\n const keys = groupConfig.columnKeys;\n if (!keys.length) return data as FlatRow[];\n\n const flat: FlatRow[] = [];\n\n const computeAggregates = (rows: GridRow[]) => {\n const result: Record<string, any> = {};\n for (const col of columns) {\n if (!col.aggregate) continue;\n const values = rows.map((r) => (r as any)[col.key]);\n let aggVal: any = null;\n if (typeof col.aggregate === \"function\") {\n try {\n aggVal = col.aggregate(values);\n } catch (_) {\n aggVal = null;\n }\n } else {\n // Try numeric aggregation first\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 if (col.aggregate === \"sum\" || col.aggregate === \"avg\") {\n if (nums.length) {\n if (col.aggregate === \"sum\")\n aggVal = nums.reduce((a, b) => a + b, 0);\n else {\n const s = nums.reduce((a, b) => a + b, 0);\n aggVal = s / nums.length;\n }\n } else {\n // Non-numeric data: skip sum/avg\n aggVal = null;\n }\n } else if (col.aggregate === \"min\" || col.aggregate === \"max\") {\n if (nums.length) {\n aggVal =\n col.aggregate === \"min\" ? Math.min(...nums) : Math.max(...nums);\n } else {\n const dateNums = tryDateNumbers();\n if (dateNums.length) {\n const picked =\n col.aggregate === \"min\"\n ? Math.min(...dateNums)\n : Math.max(...dateNums);\n aggVal = new Date(picked);\n } else {\n aggVal = null;\n }\n }\n }\n }\n if (aggVal !== null && aggVal !== undefined) {\n result[col.key] = aggVal;\n }\n }\n return result;\n };\n\n const recurse = (rows: GridRow[], level: number, pathParts: string[]) => {\n const colKey = keys[level];\n const map = new Map<string, GridRow[]>();\n for (const r of rows) {\n const str = String((r as any)[colKey] ?? UNKNOWN);\n const arr = map.get(str);\n if (arr) arr.push(r);\n else map.set(str, [r]);\n }\n const entries = Array.from(map.entries()).sort(([a], [b]) =>\n a.localeCompare(b),\n );\n for (const [value, groupRows] of entries) {\n const seg = `${colKey}=${value}`;\n const nextPath = [...pathParts, seg];\n const pathStr = buildPath(nextPath);\n flat.push({\n id: `group-${pathStr}`,\n _isGroupHeader: true,\n _groupKey: pathStr,\n _groupCount: groupRows.length,\n _groupLevel: level,\n _groupColumnKey: colKey,\n _groupValue: value,\n _groupRows: groupRows,\n } as FlatRow);\n\n const isExpanded = groupConfig.expanded.has(pathStr);\n if (!isExpanded) continue;\n if (level < keys.length - 1) {\n recurse(groupRows, level + 1, nextPath);\n } else {\n for (const r of groupRows) flat.push(r);\n }\n\n // Append footer with aggregates for this group\n const agg = computeAggregates(groupRows);\n flat.push({\n id: `group-footer-${pathStr}`,\n _isGroupFooter: true,\n _groupKey: pathStr,\n _groupLevel: level,\n _groupAgg: agg,\n } as FlatRow);\n }\n };\n\n recurse(data, 0, []);\n return flat;\n }, [data, groupConfig.columnKeys, groupConfig.expanded]);\n\n // Always keep groups expanded by default when grouping is active.\n useEffect(() => {\n if (!groupConfig.columnKeys.length) return;\n // Build full set of all possible group paths for current data + keys\n const keys = groupConfig.columnKeys;\n const allPaths = new Set<string>();\n const gather = (rows: GridRow[], level: number, parts: string[]) => {\n const colKey = keys[level];\n const map = new Map<string, GridRow[]>();\n for (const r of rows) {\n const str = String((r as any)[colKey] ?? UNKNOWN);\n const arr = map.get(str);\n if (arr) arr.push(r);\n else map.set(str, [r]);\n }\n for (const [value, groupRows] of map.entries()) {\n const seg = `${colKey}=${value}`;\n const next = [...parts, seg];\n const pathStr = buildPath(next);\n allPaths.add(pathStr);\n if (level < keys.length - 1) gather(groupRows, level + 1, next);\n }\n };\n gather(data, 0, []);\n // If expanded differs from allPaths, update\n let differs = false;\n if (allPaths.size !== groupConfig.expanded.size) differs = true;\n else {\n for (const p of allPaths)\n if (!groupConfig.expanded.has(p)) {\n differs = true;\n break;\n }\n }\n if (differs) {\n setGroupConfig((prev) => ({ ...prev, expanded: allPaths }));\n }\n }, [groupConfig.columnKeys, data]);\n\n // Single-key convenience API (back-compat)\n const handleGroupBy = (columnKey: string | null) => {\n setGroupConfig({\n columnKeys: columnKey ? [columnKey] : [],\n expanded: new Set(),\n });\n };\n\n // Multi-key operations\n const addGroupKey = (columnKey: string) => {\n setGroupConfig((prev) =>\n prev.columnKeys.includes(columnKey)\n ? prev\n : { ...prev, columnKeys: [...prev.columnKeys, columnKey] },\n );\n };\n\n const removeGroupKey = (columnKey: string) => {\n setGroupConfig((prev) => {\n const nextKeys = prev.columnKeys.filter((k) => k !== columnKey);\n const nextExpanded = new Set<string>();\n for (const p of prev.expanded)\n if (!p.includes(`${columnKey}=`)) nextExpanded.add(p);\n return { columnKeys: nextKeys, expanded: nextExpanded };\n });\n };\n\n const setGroupKeys = (columnKeys: string[]) => {\n setGroupConfig({ columnKeys: [...columnKeys], expanded: new Set() });\n };\n\n const toggleGroupExpansion = (groupPath: string) => {\n setGroupConfig((prev) => {\n const next = new Set(prev.expanded);\n if (next.has(groupPath)) next.delete(groupPath);\n else next.add(groupPath);\n return { ...prev, expanded: next };\n });\n };\n\n // Expand/collapse helpers\n const expandAllGroups = () => {\n const keys = groupConfig.columnKeys;\n if (!keys.length) return;\n // gather all group paths\n const gatherAllPaths = (\n rows: GridRow[],\n level: number,\n parts: string[],\n acc: Set<string>,\n ) => {\n const colKey = keys[level];\n const map = new Map<string, GridRow[]>();\n for (const r of rows) {\n const str = String((r as any)[colKey] ?? UNKNOWN);\n const arr = map.get(str);\n if (arr) arr.push(r);\n else map.set(str, [r]);\n }\n for (const [value, groupRows] of map.entries()) {\n const seg = `${colKey}=${value}`;\n const next = [...parts, seg];\n const pathStr = buildPath(next);\n acc.add(pathStr);\n if (level < keys.length - 1)\n gatherAllPaths(groupRows, level + 1, next, acc);\n }\n };\n const acc = new Set<string>();\n gatherAllPaths(data, 0, [], acc);\n setGroupConfig((prev) => ({ ...prev, expanded: acc }));\n };\n\n const collapseAllGroups = () => {\n setGroupConfig((prev) => ({ ...prev, expanded: new Set<string>() }));\n };\n\n return {\n groupConfig,\n displayData,\n handleGroupBy,\n addGroupKey,\n removeGroupKey,\n setGroupKeys,\n toggleGroupExpansion,\n expandAllGroups,\n collapseAllGroups,\n isGrouped: groupConfig.columnKeys.length > 0,\n };\n};\n","import {\n useState,\n useMemo,\n useCallback,\n useDeferredValue,\n ChangeEvent,\n} from \"react\";\nimport { SortConfig, GridRow, GridColumn } from \"../types\";\n\n// Debounce utility for performance optimization\nfunction debounce<T extends (...args: any[]) => any>(func: T, wait: number): T {\n let timeout: NodeJS.Timeout;\n return ((...args: any[]) => {\n clearTimeout(timeout);\n timeout = setTimeout(() => func(...args), wait);\n }) as T;\n}\n\ninterface UseFilteringAndSortingProps {\n data: GridRow[];\n columns: GridColumn[];\n}\n\nexport const useFilteringAndSorting = ({\n data,\n columns,\n}: UseFilteringAndSortingProps) => {\n const [sortConfig, setSortConfig] = useState<SortConfig>({\n key: null,\n direction: null,\n });\n const [filters, setFilters] = useState<Record<string, string>>({});\n const [globalFilter, setGlobalFilter] = useState<string>(\"\");\n\n // Use deferred values for better performance with large datasets\n const deferredGlobalFilter = useDeferredValue(globalFilter);\n const deferredFilters = useDeferredValue(filters);\n\n // Sorting logic\n const sortedData = useMemo(() => {\n if (!sortConfig.key) return data;\n\n return [...data].sort((a, b) => {\n const aValue = a[sortConfig.key || \"\"];\n const bValue = b[sortConfig.key || \"\"];\n\n if (aValue == null && bValue == null) return 0;\n if (aValue == null) return sortConfig.direction === \"asc\" ? -1 : 1;\n if (bValue == null) return sortConfig.direction === \"asc\" ? 1 : -1;\n\n if (typeof aValue === \"string\" && typeof bValue === \"string\") {\n return sortConfig.direction === \"asc\"\n ? aValue.localeCompare(bValue)\n : bValue.localeCompare(aValue);\n }\n\n if (typeof aValue === \"number\" && typeof bValue === \"number\") {\n return sortConfig.direction === \"asc\"\n ? aValue - bValue\n : bValue - aValue;\n }\n\n // Convert to strings for comparison if types don't match\n const aStr = String(aValue);\n const bStr = String(bValue);\n return sortConfig.direction === \"asc\"\n ? aStr.localeCompare(bStr)\n : bStr.localeCompare(aStr);\n });\n }, [data, sortConfig]);\n\n // Optimized filtering logic with deferred values\n const filteredData = useMemo(() => {\n let result = sortedData;\n\n // Column-specific filters\n Object.entries(deferredFilters).forEach(([key, value]) => {\n if (value) {\n const lowerValue = value.toLowerCase();\n result = result.filter((row) => {\n const cellValue = row[key];\n return (\n cellValue != null &&\n String(cellValue).toLowerCase().includes(lowerValue)\n );\n });\n }\n });\n\n // Global filter with optimized search\n if (deferredGlobalFilter) {\n const lowerGlobalFilter = deferredGlobalFilter.toLowerCase();\n result = result.filter((row) => {\n // Only search in visible columns for better performance\n return columns.some((column) => {\n const value = row[column.key];\n return (\n value != null &&\n String(value).toLowerCase().includes(lowerGlobalFilter)\n );\n });\n });\n }\n\n return result;\n }, [sortedData, deferredFilters, deferredGlobalFilter, columns]);\n\n // Handlers\n const handleSort = useCallback((key: string) => {\n setSortConfig((prev) => ({\n key,\n direction: prev.key === key && prev.direction === \"asc\" ? \"desc\" : \"asc\",\n }));\n }, []);\n\n const handleFilter = useCallback((key: string, value: string) => {\n setFilters((prev) => ({ ...prev, [key]: value }));\n }, []);\n\n // Debounced filter handler for better performance\n const debouncedSetGlobalFilter = useMemo(\n () => debounce((value: string) => setGlobalFilter(value), 300),\n []\n );\n\n const handleGlobalFilterChange = useCallback(\n (e: ChangeEvent<HTMLInputElement>) => {\n debouncedSetGlobalFilter(e.target.value);\n },\n [debouncedSetGlobalFilter]\n );\n\n return {\n sortConfig,\n filters,\n globalFilter,\n filteredData,\n handleSort,\n handleFilter,\n handleGlobalFilterChange,\n };\n};\n"],"names":["SELECT_COL_WIDTH","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","set","find","_jsxs","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","jsx","prev","next","i","pc","nc","extractId","idx","id","prevFirst","nextFirst","prevLastIdx","nextLastIdx","prevLast","nextLast","size","TextFilter","value","onChange","placeholder","textValue","setTextValue","useState","operator","setOperator","e","newOperator","op","trim","type","handleOperatorChange","target","handleTextChange","newText","NumberFilter","numberValue","setNumberValue","secondValue","setSecondValue","handleValueChange","newValue","isSecond","numValue","numSecondValue","isNaN","DatePicker","name","label","DateFilter","toDateObj","d","toStored","format","dateValue","setDateValue","secondDateValue","setSecondDateValue","useEffect","clampDate","date","minDate","maxDate","handleDateChange","newDate","nextSecond","maxForFirst","maxDateObj","clampedDate","minDateObj","end","MultiselectFilter","options","columnKey","searchText","setSearchText","selectedValues","setSelectedValues","availableOptions","opts","isBlankValue","val","normalizedValues","processedOptions","opt","isBlank","Array","from","sort","localeCompare","filteredOptions","searchLower","toLowerCase","option","Search","onClick","allValues","index","isSelected","optionValue","newSelected","delete","add","handleToggleValue","Check","BooleanFilter","booleanValue","setBooleanValue","FilterContent","column","filterConfig","filterable","sampleValues","slice","firstValue","uniqueValues","GridHeader","unpinnedColumns","hvPadLeft","hvPadRight","headerHeight","sortConfig","columnFilters","selectedRows","totalRows","onSort","onColumnFilter","onSelectAll","onColumnResize","pinnedKeySet","onColumnPin","groupable","groupedByColumn","onGroupBy","groupedByColumns","onGroupToggle","onAutosizeColumn","onAutosizeAllColumns","onResetColumns","columnOrder","onColumnOrderChange","paginationMode","allColumns","activeColumnMenu","setActiveColumnMenu","menuPosition","setMenuPosition","isResizing","setIsResizing","startX","setStartX","startWidth","setStartWidth","activeTab","setActiveTab","tempFilter","setTempFilter","isTouchDevice","setIsTouchDevice","window","navigator","maxTouchPoints","matchMedia","matches","menuReorderDraggingKey","setMenuReorderDraggingKey","menuReorderDragOver","setMenuReorderDragOver","isAllSelected","isIndeterminate","handleColumnMenuClick","event","stopPropagation","rect","currentTarget","getBoundingClientRect","containerEl","closest","containerRect","DOMRect","innerWidth","innerHeight","MENU_WIDTH","PADDING","minLeft","maxLeft","right","clampedLeft","top","bottom","handleApplyFilter","hasActiveFilter","handleResizeStart","preventDefault","clientX","handleResizeMove","deltaX","newWidth","handleResizeEnd","draggingKey","setDraggingKey","dragOver","setDragOver","handleDragStart","dataTransfer","effectAllowed","setData","handleDragOver","targetKey","dropEffect","midpoint","position","handleDrop","sourceKey","before","base","k","indexOf","splice","handleDragEnd","handleMenuReorderDragStart","handleMenuReorderDragOver","clientY","handleMenuReorderDrop","handleMenuReorderDragEnd","document","addEventListener","body","cursor","userSelect","removeEventListener","handleClickOutside","_Fragment","role","checked","ref","el","indeterminate","onKeyDown","pinnedIndex","headerJustify","headerAlign","colSortable","sortable","draggable","onDragStart","onDragOver","onDrop","onDragEnd","direction","ArrowUp","ArrowDown","Menu","maxHeight","Filter","ArrowUpDown","Pin","PinOff","isGrouped","Fragment","Ungroup","Group","FilterX","Boolean","isPinnedItem","onMouseDown","onDoubleClick","nextPinned","GridRows","virtualized","virtualizedRange","isRowSelectable","onRowSelect","rowStyle","globalFilter","onContextMenu","onRowDoubleClick","onRowClick","onCellClick","isCellFocused","isCellSelected","onCellContextMenu","onCellMouseDown","onCellMouseEnter","totalHeight","widthByKey","pinnedCount","transform","offsetY","actualIndex","startIndex","customRowStyles","rowId","rowIndex","process","env","NODE_ENV","extractRowId","defaultRowColor","selectedRowColor","handleContextMenu","handleDoubleClick","handleRowClick","disabled","tabIndex","colIndex","rawCell","renderedValue","cellRenderer","searchQuery","query","containsSearchQuery","text","lowerText","parts","lastIndex","searchIndex","keyCounter","push","margin","highlightText","extraCellClass","extraCellStyle","cellStyle","cell","handleCellClick","_isGroupHeader","_isGroupFooter","onMouseEnter","wrapCellContent","endIndex","join","GroupBar","groupedKeys","onRemove","onReorder","onDropColumnKey","onToggleExpandAll","isAnyExpanded","dragKey","setDragKey","dragOverKey","setDragOverKey","dragOverBefore","setDragOverBefore","labelFor","handleChipDragStart","handleChipDragOver","getData","order","GroupHeader","isExpanded","onToggle","viewportWidth","getHeaderLabel","getValueLabel","renderGroupActions","handleToggle","_groupKey","paddingLeft","_groupLevel","ChevronDown","ChevronRight","_groupColumnKey","_groupValue","_groupCount","groupKey","groupValue","rows","_groupRows","count","level","NoDataMessage","hasFilters","hasData","message","description","icon","Icon","Database","PaginationControls","paginationConfig","currentPage","isServerLoading","selectedRowsCount","totalDataLength","filteredDataLength","onPageChange","onPageSizeChange","start","total","effectiveTotal","enabled","pageSize","getCurrentPageRange","isFiltered","toLocaleString","Users","Eye","showPageSizeSelector","pageSizeOptions","Loader2","totalPages","ChevronsLeft","ChevronLeft","_","page","pages","current","maxVisible","delta","floor","isActive","ChevronsRight","CustomSelect","searchable","isOpen","setIsOpen","searchTerm","setSearchTerm","highlightedIndex","setHighlightedIndex","containerRef","useRef","inputRef","listRef","selectedOption","contains","handleKeyDown","handleSelect","highlightedElement","scrollIntoView","block","behavior","setTimeout","focus","ColumnFilterSelector","filterableColumns","selectedColumn","setSelectedColumn","multiSearch","setMultiSearch","columnOptions","selectedColumnData","filterType","currentFilter","lowered","X","isArray","currentValues","newValues","selectOptions","parseInput","direct","asDate","primaryDate","secondDate","effectiveOperator","stored","renderFilterInput","Object","keys","entries","handleActiveFilterClick","formatDate","parsed","operatorLabel","equals","gt","gte","lt","lte","between","Popover","open","anchorEl","onClose","placement","offset","sameWidth","zIndex","shouldIgnoreClose","refs","floatingStyles","update","useFloating","whileElementsMounted","autoUpdate","middleware","fOffset","flip","padding","shift","apply","rects","elements","availableHeight","availableWidth","floating","reference","setReference","createPortal","setFloating","overflowX","SearchToolbar","showExport","showColumnToggle","showBulkActions","columnVisibility","bulkActions","exportOptions","onGlobalFilterChange","onClearFilters","onColumnVisibilityChange","onScrollToColumn","density","onDensityChange","showDensityControl","customLeftContent","customRightContent","showExportMenu","setShowExportMenu","showFilters","setShowFilters","showBulkActionsMenu","setShowBulkActionsMenu","showColumnMenu","setShowColumnMenu","columnSearch","setColumnSearch","searchInputRef","exportMenuRef","bulkActionsRef","columnMenuRef","filtersMenuRef","filtersAnchorRef","exportAnchorRef","bulkActionsAnchorRef","columnAnchorRef","node","getAttribute","dataset","popoverRoot","parentNode","isInAnyPopover","classList","cls","isInMuiDatePicker","exportFormats","formats","hasActiveFilters","hasSelectedRows","visibleColumnsCount","filteredColumns","getExportIcon","Table","FileText","Download","getBulkActionIcon","action","Trash2","Archive","Copy","Edit","MoreHorizontal","getFormatLabel","handleExport","exportSelected","onExport","hasFilterableColumns","handleBulkAction","destructive","Settings","isVisible","handleColumnToggle","EyeOff","requestAnimationFrame","LocateFixed","every","visible","toUpperCase","useDataWorker","workerRef","resolverRef","code","workerFactory","blob","Blob","Worker","URL","createObjectURL","err","w","onmessage","payload","terminate","Promise","resolve","dir","out","av","bv","postMessage","ctx","self","msg","as","bs","processData","usePagination","pagination","setCurrentPage","setPageSize","setIsServerLoading","resetPage","useCallback","isEnabled","mode","serverConfig","ceil","paginatedData","handlePageChange","async","handlePageSizeChange","newPageSize","areSetsEqual","useSelection","selectedRowIds","setSelectedRows","validIds","handleRowSelect","newSet","item","handleSelectAll","useVirtualization","overscan","scrollRef","scrollTop","setScrollTop","rafIdRef","pendingScrollTopRef","containerHeight","setContainerHeight","rowsInView","dynamicOverscan","visibleCount","visibleData","handleScroll","nextTop","cancelAnimationFrame","CustomDataGrid","forwardRef","densityProp","headerDensity","headerHeightProp","onSelectedRowsChange","showToolbar","rowHeightProp","cellFocusEnabled","onCellFocusChange","cellSelectionEnabled","canSelectCell","onCellSelectionChange","virtualizationThreshold","isLoading","onFilterChange","onColumnConfigChange","initialColumnConfig","toolbarLeft","toolbarRight","groupBarVisibility","groupFooterVariant","onCopyCells","performanceConfig","exportOptionsProp","containerWidth","setContainerWidth","clientWidth","ro","ResizeObserver","observe","disconnect","focusedCell","setFocusedCell","selectionAnchor","setSelectionAnchor","selectionEnd","setSelectionEnd","isMouseDownRef","lastMousePosRef","autoScrollRafRef","dragOverlay","setDragOverlay","setDensity","densityMap","sm","md","lg","resolvedRowHeight","resolvedHeaderHeight","filteredData","setGlobalFilter","setColumnFilter","clearAllFilters","setColumnFilters","deferredGlobalFilter","useDeferredValue","deferredColumnFilters","applyColumnFilter","filterText","startsWith","endsWith","filterNum","secondNum","filterDate","dateOnly","getFullYear","getMonth","getDate","filterDateOnly","secondDateOnly","filterValues","normalizeBlank","normalizedValue","rowSearchCacheRef","WeakMap","cacheSizeRef","clearCacheIfNeeded","hasColumnFilters","hasGlobal","lowerGlobal","cache","working","visibleColumns","packed","newFilters","clearColumnFilter","getActiveFilterCount","useAdvancedFiltering","setSortConfig","columnWidths","setColumnWidths","initialWidths","initialWidthsRef","initialOrderRef","setColumnOrder","setPinnedColumns","setColumnVisibility","initialVisibility","handleSort","nextKey","nextDir","handleColumnResize","estimateWidthForText","autosizeColumn","headerWidth","sample","maxCell","computed","autosizeAllColumns","resetColumns","headerScrollRef","footerScrollRef","handleColumnPin","pinned","handleColumnVisibilityChange","workerProcess","sortedData","setSortedData","workerThreshold","sortWorkerThreshold","cancelled","then","loadingOverall","showLoader","setShowLoader","finishingLoader","setFinishingLoader","loaderTop","setLoaderTop","clearTimeout","handleGlobalFilterChange","groupConfig","displayData","groupedDisplayData","handleGroupBy","addGroupKey","removeGroupKey","setGroupKeys","toggleGroupExpansion","expandAllGroups","collapseAllGroups","setGroupConfig","columnKeys","expanded","UNKNOWN","buildPath","flat","computeAggregates","aggregate","r","aggVal","picked","recurse","pathParts","colKey","str","arr","groupRows","nextPath","pathStr","agg","_groupAgg","allPaths","gather","differs","p","nextKeys","nextExpanded","groupPath","gatherAllPaths","useGrouping","useImperativeHandle","getGroupConfig","effectivePinnedColumns","finalDisplayData","dataToDisplay","onSelectedRowsChangeRef","exportData","exportDataWithSelection","filename","downloadFile","content","fileName","mimeType","url","link","createElement","href","download","appendChild","click","removeChild","revokeObjectURL","exportToCSV","exportFilename","csvContent","stringValue","exportToJSON","jsonData","cleanRow","jsonContent","JSON","stringify","exportToXLSX","ExcelJS","saveAs","all","import","workbook","default","Workbook","sheet","addWorksheet","sampled","coerceValue","columnDefs","maxLen","raw","obj","addRow","getRow","font","bold","autoFilter","to","rowCount","buffer","xlsx","writeBuffer","canExportSelected","selectedCount","useExport","handleToolbarExport","selectedIds","exportMode","onServerExport","handleClearFilters","handleSetDensity","effectiveVirtualized","perfConfig","enableHorizontalVirtualization","horizontalVirtualizationThreshold","maxFilterCacheSize","enableAggressiveMemoization","offsetTop","hScrollLeft","setHScrollLeft","hScrollLeftRef","hScrollRafRef","scheduleHScrollUpdate","setViewportWidth","filterableKeys","existingKeys","fromEntries","pins","filters","filtered","remaining","timer","columnsWithWidths","orderIndex","candidate","viewportW","baseTotal","flexCols","flex","extra","totalWeight","weight","increment","minW","maxW","POSITIVE_INFINITY","unpinned","groupIndex","needsHorizontalVirtualization","pinnedAll","unpinnedAll","unpinnedMeta","widths","prefix","fill","leftPad","pinnedTotal","targetLeft","targetRight","localLeft","localRight","startIdx","x","lo","hi","ans","mid","firstIndexWithRightGte","endIdx","lastIndexWithLeftLte","sliced","unpinnedIndexByKey","m","hvPadLeftMemo","hvPadRightMemo","visibleUnpinned","firstIdx","lastIdx","hasFooterAggregate","bodyElement","isBodyScrolling","syncFromBody","footerEl","scrollLeft","passive","colIndexByKey","colByKey","flatRows","rowIndexById","rowById","prevSelectionSignatureRef","onCellSelectionChangeRef","onCopyCellsRef","rowObj","colObj","rMin","rMax","cMin","cMax","forwardWheelToBody","bodyEl","dx","abs","deltaY","scrollBy","handleCellMouseDown","rIndex","cIndex","hasAttribute","button","y","cols","handleCellMouseEnter","onUp","onMove","ensureAutoScrollLoop","step","mp","maxSpeed","dy","clamp","onKey","isShift","shiftKey","arrow","maxRow","maxCol","nr","headerEl","handler","currentRowIdx","currentColIdx","nextRow","nextCol","viewportH","clientHeight","targetTop","targetBottom","colWidth","ensureVisible","bounds","cells","sig","rowStart","rowEnd","colStart","colEnd","metaKey","ctrlKey","includeHeaders","hasRect","hasFocus","sel","getSelection","active","activeElement","tagName","isContentEditable","collectCells","escapeCell","headers","line","clipboard","writeText","isRectangular","isFocusedCell","scrollTo","center","onScroll","headerPinned","headerUnpinned","willChange","contain","nodes","chunk","flush","opacity","chips","rawVal","fmt","aggregateFormatter","display","isInteger","num","scrollbarWidth","msOverflowStyle","onWheel","setFilters","deferredFilters","aValue","bValue","aStr","bStr","lowerValue","cellValue","lowerGlobalFilter","handleFilter","debouncedSetGlobalFilter","func","wait","timeout","args","debounce"],"mappings":"0KAAO,MAAMA,EAAmB,GCyPnBC,EAAkBC,EAAAA,KA1OkC,EAC/DC,UACAC,OACAC,aACAC,YACAC,gBAAgB,IAAIC,IACpBC,eAEA,MAAMC,EAAkBC,EAAAA,QAAQ,KAC9B,MAAMC,EAA8B,CAAA,EAG9BC,EAAuBV,EAAQW,OAAQC,GAAQA,EAAIC,kBAEzD,OAAoC,IAAhCH,EAAqBI,QAIzBJ,EAAqBK,QAASH,IAC5B,MAAMI,EAASf,EAAKgB,IAAKC,GAAQA,EAAIN,EAAIO,MACzC,IAAIC,EAAgB,KAEpB,GAAoC,mBAAzBR,EAAIC,iBACb,IACEO,EAAWR,EAAIC,iBAAiBG,EACjC,CAAC,MAAOK,GACPC,QAAQC,KACN,sDAAsDX,EAAIO,OAC1DE,GAEFD,EAAW,IACZ,KACI,CACL,MAAMI,EAAcC,KAEd,sBAAsBC,KAAKD,OAC3B,sBAAsBC,KAAKD,OAC3B,wBAAwBC,KAAKD,OAC7B,wBAAwBC,KAAKD,OAG9BA,EAAEE,SAAS,OAAQF,EAAEE,SAAS,OAC/BC,OAAOC,SAASC,KAAKC,MAAMN,QAMzBO,EAAOhB,EACVC,IAAKgB,IACJ,GAAiB,iBAANA,EAAgB,OAAOA,EAClC,GAAS,MAALA,EAAW,OAAOC,IACtB,MAAMC,EAAKC,OAAOH,GAElB,GAAIT,EAAWW,GAAK,OAAOD,IAC3B,MAAMG,EAAIC,WAAWH,EAAGI,QAAQ,KAAM,KACtC,OAAOX,OAAOC,SAASQ,GAAKA,EAAIH,MAEjCvB,OAAQ0B,GAAMT,OAAOC,SAASQ,IAE3BG,EAAiB,IAEJxB,EACdC,IAAKgB,IACJ,GAAIA,aAAaH,KAAM,OAAOG,EAAEQ,UAChC,GAAiB,iBAANR,GAA+B,iBAANA,EAAgB,CAClD,MAAMS,EAAIZ,KAAKC,MAAMK,OAAOH,IAC5B,OAAOL,OAAOC,SAASa,GAAKA,EAAIR,GACjC,CACD,OAAOA,MAERvB,OAAQ+B,GAAMd,OAAOC,SAASa,IAInC,OAAQ9B,EAAIC,kBACV,IAAK,QACHO,EAAWnB,EAAKa,OAChB,MACF,IAAK,MACCkB,EAAKlB,SACPM,EAAWY,EAAKW,OAAO,CAACC,EAAGC,IAAMD,EAAIC,EAAG,IAE1C,MACF,IAAK,MACH,GAAIb,EAAKlB,OAAQ,CAEfM,EADYY,EAAKW,OAAO,CAACC,EAAGC,IAAMD,EAAIC,EAAG,GACxBb,EAAKlB,MACvB,CACD,MACF,IAAK,MACH,GAAIkB,EAAKlB,OACPM,EAAW0B,KAAKC,OAAOf,OAClB,CACL,MAAMgB,EAAWR,IACbQ,EAASlC,SACXM,EAAW,IAAIU,KAAKgB,KAAKC,OAAOC,IAEnC,CACD,MACF,IAAK,MACH,GAAIhB,EAAKlB,OACPM,EAAW0B,KAAKG,OAAOjB,OAClB,CACL,MAAMgB,EAAWR,IACbQ,EAASlC,SACXM,EAAW,IAAIU,KAAKgB,KAAKG,OAAOD,IAEnC,CACD,MACF,QACE5B,EAAW,KAEhB,CAEGA,UACFX,EAAOG,EAAIO,KAAOC,KArGbX,GA0GR,CAACT,EAASC,IAIb,IAD4BD,EAAQkD,KAAMtC,GAAQA,EAAIC,kBAEpD,OAAO,KAGT,MAAMsC,EAAanD,EAAQ2C,OAAO,CAACS,EAAKxC,IAAQwC,EAAMxC,EAAIyC,MAAO,GAG3DC,EAAwB9C,EAAAA,QAAQ,KACpC,MAAMS,EAAM,IAAIsC,IACVC,EAAoBxD,EACvBW,OAAQ8C,GAAMrD,EAAcsD,IAAID,EAAEtC,MAClCF,IAAKwC,GAAMA,EAAEtC,KAChB,IAAIwC,EAAMzD,EAAaL,EAAmB,EAC1C,IAAK,MAAMsB,KAAOqC,EAAmB,CACnCvC,EAAI2C,IAAIzC,EAAKwC,GAEbA,GADU3D,EAAQ6D,KAAMJ,GAAMA,EAAEtC,MAAQA,IAAMkC,OAAS,GAExD,CACD,OAAOpC,GACN,CAACjB,EAASI,EAAeF,IAE5B,OACE4D,EACEC,KAAA,MAAA,CAAAC,UAAU,cACVC,MAAO,CACLC,SAAU,GAAGf,GAAcjD,EAAaL,EAAmB,OAC3DsE,OAAQ,GAAGhE,OACZiE,SAAA,CAEAlE,GACCmE,EAAAA,IAAA,MAAA,CAEEL,UAAU,uIACVC,MAAO,CAAEE,OAAQ,GAAGhE,OAEpBiE,SAAAC,EAAAA,IAAA,OAAA,CAAML,UAAU,uDAETI,SAAA,WANH,sBASPpE,EAAQiB,IAAKL,IACZ,MAAMyC,EAAQzC,EAAIyC,MACZiB,EAAW/D,EAAgBK,EAAIO,KAC/BoD,EAAe3D,EAAIC,uBAAiC2D,IAAbF,EAG7C,IAAIG,EAAe,GACnB,GAAIF,EAAc,CAChB,MAAMG,EAAY9D,EAAI+D,0BAA4B/D,EAAI8D,UACtD,GAAIA,EACF,IACED,EAAeC,EAAUJ,EAC1B,CAAC,MACAG,EAAerC,OAAOkC,EACvB,MAGCG,EADsB,iBAAbH,EAEPA,EAAW,GAAM,EAAIA,EAASM,WAAaN,EAASO,QAAQ,GAE/CzC,OAAOkC,GAAUO,QAGrC,CAED,MAAMC,EAAW1E,EAAcsD,IAAI9C,EAAIO,KACjC4D,EAAaD,GACfxB,EAAsB0B,IAAIpE,EAAIO,MAC9B,EACE8D,EACU,WAAdrE,EAAIsE,MACA,cACc,UAAdtE,EAAIsE,MACF,aACA,YACFC,EACU,WAAdvE,EAAIsE,MACA,iBACc,UAAdtE,EAAIsE,MACF,cACA,gBACFE,EAAexE,EAAIyE,UAAY,MAAQ,MAE7C,OACEhB,EAAAA,WAEEL,UAAW,iEAAiEoB,8FAAyGD,KAAgBF,KACnMH,EAAW,wCAA0C,KAEvDb,MAAO,CACLZ,MAAO,GAAGA,MACVa,SAAU,GAAGb,MACbiC,SAAU,GAAGjC,MACbkC,KAAMT,EAAW,GAAGC,MAAiB,OACrCZ,OAAQ,GAAGhE,OAEbqF,MAAOjB,EAAe,GAAG3D,EAAI6E,WAAWhB,SAAiBD,EAExDJ,SAAAG,GACCF,EAAAqB,IAAA,OAAA,CAAM1B,UAAU,kBAAmBI,SAAAK,KAdhC7D,EAAIO,WAwByC,CAACwE,EAAMC,KACnE,GAAID,EAAKzF,aAAe0F,EAAK1F,WAAY,OAAO,EAChD,GAAIyF,EAAKxF,YAAcyF,EAAKzF,UAAW,OAAO,EAC9C,GAAIwF,EAAKrF,WAAasF,EAAKtF,SAAU,OAAO,EAC5C,GAAIqF,EAAK3F,QAAQc,SAAW8E,EAAK5F,QAAQc,OAAQ,OAAO,EAExD,IAAK,IAAI+E,EAAI,EAAGA,EAAIF,EAAK3F,QAAQc,OAAQ+E,IAAK,CAC5C,MAAMC,EAAKH,EAAK3F,QAAQ6F,GAClBE,EAAKH,EAAK5F,QAAQ6F,GACxB,GACEC,EAAG3E,MAAQ4E,EAAG5E,KACd2E,EAAGzC,QAAU0C,EAAG1C,OAChByC,EAAGjF,mBAAqBkF,EAAGlF,iBAE3B,OAAO,CACV,CAED,GAAI8E,EAAK1F,KAAKa,SAAW8E,EAAK3F,KAAKa,OAAQ,OAAO,EAElD,MAAMkF,EAAY,CAChB9E,EACA+E,EACA3F,IAEIA,EAAiBA,EAASY,QACfsD,IAAXtD,EAAIgF,GAAyBhF,EAAIgF,GAC9BD,EAGHE,EACJR,EAAK1F,KAAKa,OAAS,EACfkF,EAAUL,EAAK1F,KAAK,GAAI,EAAG0F,EAAKrF,eAChCkE,EACA4B,EACJR,EAAK3F,KAAKa,OAAS,EACfkF,EAAUJ,EAAK3F,KAAK,GAAI,EAAG2F,EAAKtF,eAChCkE,EACA6B,EAAcV,EAAK1F,KAAKa,OAAS,EACjCwF,EAAcV,EAAK3F,KAAKa,OAAS,EACjCyF,EACJZ,EAAK1F,KAAKa,OAAS,EACfkF,EAAUL,EAAK1F,KAAKoG,GAAcA,EAAaV,EAAKrF,eACpDkE,EACAgC,EACJZ,EAAK3F,KAAKa,OAAS,EACfkF,EAAUJ,EAAK3F,KAAKqG,GAAcA,EAAaV,EAAKtF,eACpDkE,EACN,OAAI2B,IAAcC,GAAaG,IAAaC,GACxCb,EAAKvF,eAAeqG,OAASb,EAAKxF,eAAeqG,OChS1CC,EAAa,EACxBC,QACAC,WACAC,cAAc,8BAEd,MAAOC,EAAWC,GAAgBC,EAAQA,SAACL,GAAOA,OAAS,KACpDM,EAAUC,GAAeF,EAAQA,SAErCL,GAAOM,UAAoB,YA2B9B,OACEnD,cAAKE,UAAU,YACbI,SAAA,CAAAN,OAAA,MAAA,CAAAM,SAAA,CACEC,EAAOqB,IAAA,QAAA,CAAA1B,UAAU,wEAGjBF,EAAAC,KAAA,SAAA,CACE4C,MAAOM,EACPL,SAAWO,GApBU,CAACC,IAC5B,MAAMC,EAAKD,EACXF,EAAYG,GACRP,EAAUQ,QACZV,EAAS,CACPW,KAAM,OACNZ,MAAOG,EACPG,SAAUI,KAaSG,CAAqBL,EAAEM,OAAOd,OAC/C3C,UAAU,4GAEVI,SAAA,CAAAC,MAAA,SAAA,CAAQsC,MAAM,WAA4BvC,SAAA,aAC1CC,EAAAA,IAAQ,SAAA,CAAAsC,MAAM,SAAQvC,SAAA,WACtBC,EAAAqB,IAAA,SAAA,CAAQiB,MAAM,aAAiCvC,SAAA,gBAC/CC,EAAQqB,IAAA,SAAA,CAAAiB,MAAM,wCAIlB7C,OAAA,MAAA,CAAAM,SAAA,CACEC,EAAAA,IAAO,QAAA,CAAAL,UAAU,+CAA8CI,SAAA,eAG/DC,EAAAqB,IAAA,QAAA,CACE6B,KAAK,OACLZ,MAAOG,EACPF,SAAWO,IAAMO,OAlDCC,EAkDgBR,EAAEM,OAAOd,MAjDjDI,EAAaY,QACTA,EAAQL,OACVV,EAAS,CACPW,KAAM,OACNZ,MAAOgB,EACPV,SAAUA,IAGZL,EAAS,OATY,IAACe,GAmDlBd,YAAaA,EACb7C,UAAU,qHC7DP4D,EAAe,EAC1BjB,QACAC,WACA7D,MACAE,UAEA,MAAO4E,EAAaC,GAAkBd,EAAQA,SAACL,GAAOA,OAAS,KACxDoB,EAAaC,GAAkBhB,EAAQA,SAACL,GAAOoB,aAAe,KAC9Dd,EAAUC,GAAeF,EAAQA,SAErCL,GAAOM,UAAoB,UAExBgB,EAAoB,CAACC,EAAkBC,GAAW,KAClDA,EACFH,EAAeE,GAEfJ,EAAeI,GAGjB,MAAME,EAAW9F,WAAW4F,GACtBG,EAAiB/F,WAAWyF,GAEjB,YAAbd,EACGqB,MAAMF,IAAcE,MAAMD,GAQ7BzB,EAAS,MAPTA,EAAS,CACPW,KAAM,SACNZ,MAAO7D,KAAKC,IAAIqF,EAAUC,GAC1BN,YAAajF,KAAKG,IAAImF,EAAUC,GAChCpB,SAAU,YAMTqB,MAAMF,GAOTxB,EAAS,MANTA,EAAS,CACPW,KAAM,SACNZ,MAAOyB,EACPnB,SAAUA,KAyClB,OACEnD,EAAAA,YAAKE,UAAU,YACbI,SAAA,CAAAN,EAAAA,KAAA,MAAA,CAAAM,SAAA,CACEC,EAAAA,aAAOL,UAAU,+CAETI,SAAA,gBACRN,EAAAA,eACE6C,MAAOM,EACPL,SAAWO,GAzCU,CAACC,IAC5B,MAAMC,EAAKD,EASX,GAFAF,EAAYG,GAED,YAAPA,EAAkB,CACpB,MAAMe,EAAW9F,WAAWuF,GACtBQ,EAAiB/F,WAAWyF,GAC7BO,MAAMF,IAAcE,MAAMD,IAC7BzB,EAAS,CACPW,KAAM,SACNZ,MAAO7D,KAAKC,IAAIqF,EAAUC,GAC1BN,YAAajF,KAAKG,IAAImF,EAAUC,GAChCpB,SAAU,WAGf,KAAM,CACL,MAAMmB,EAAW9F,WAAWuF,GACvBS,MAAMF,IACTxB,EAAS,CACPW,KAAM,SACNZ,MAAOyB,EACPnB,SAAUI,GAGf,GAWsBG,CAAqBL,EAAEM,OAAOd,OAC/C3C,UAAU,sHAEVK,EAAQqB,IAAA,SAAA,CAAAiB,MAAM,SAAQvC,SAAA,WACtBC,EAAAA,IAAQ,SAAA,CAAAsC,MAAM,KAAIvC,SAAA,iBAClBC,EAAAA,IAAQ,SAAA,CAAAsC,MAAM,MAAKvC,SAAA,0BACnBC,EAAAA,IAAQ,SAAA,CAAAsC,MAAM,KAAIvC,SAAA,cAClBC,EAAAA,IAAQ,SAAA,CAAAsC,MAAM,MAAKvC,SAAA,uBACnBC,EAAAA,IAAQ,SAAA,CAAAsC,MAAM,UAASvC,SAAA,kBAI3BN,wBACEO,EAAAA,IAAO,QAAA,CAAAL,UAAU,+CAA8CI,SAC/C,YAAb6C,EAAyB,aAAe,iBAE3C5C,MACE,QAAA,CAAAkD,KAAK,SACLZ,MAAOkB,EACPjB,SAAWO,GAAMc,EAAkBd,EAAEM,OAAOd,OAC5C5D,IAAKA,EACLE,IAAKA,EACLe,UAAU,iHAIA,YAAbiD,GACCnD,EAAAA,sBACEO,EAAAA,IAAO,QAAA,CAAAL,UAAU,+CAA8CI,SAAA,aAG/DC,EAAAA,IACE,QAAA,CAAAkD,KAAK,SACLZ,MAAOoB,EACPnB,SAAWO,GAAMc,EAAkBd,EAAEM,OAAOd,OAAO,GACnD5D,IAAKA,EACLE,IAAKA,EACLe,UAAU,qHC5HTuE,EAAwC,EACnDC,OACAC,QACA9B,QACAC,WACA5C,YAAY,MAGVF,EAAKC,KAAA,MAAA,CAAAC,UAAW,aAAaA,IAC1BI,SAAA,CAAAqE,GACCpE,MAAO,QAAA,CAAAL,UAAU,oCACdI,SAAAqE,IAGLpE,MAAA,QAAA,CACEkD,KAAK,OACLiB,KAAMA,EACN7B,MAAOA,GAAS,GAChBC,SAAWO,GAAMP,EAASO,EAAEM,OAAOd,OAAS,MAC5C3C,UAAU,yHCjBL0E,EAAa,EAAG/B,QAAOC,WAAU7D,MAAKE,UAEjD,MAAM0F,EAAa1G,IACjB,IAAKA,EAAG,OAAO,KACf,GAAIA,aAAaH,KAAM,OAAOwG,MAAMrG,EAAEQ,WAAa,KAAOR,EAE1D,IACE,MAAM2G,EAAI7G,EAAKA,MAACK,OAAOH,GAAI,aAAc,IAAIH,MAC7C,OAAOwG,MAAMM,EAAEnG,WAAa,KAAOmG,CACpC,CAAC,MAAOzB,GACP,MAAMyB,EAAI,IAAI9G,KAAKG,GACnB,OAAOqG,MAAMM,EAAEnG,WAAa,KAAOmG,CACpC,GAEGC,EAAYD,GAChBA,EAAIE,EAAMA,OAACF,EAAG,cAAgB,IAEzBG,EAAWC,GAAgBhC,EAAAA,SAChC2B,EAAUhC,GAAOA,SAEZsC,EAAiBC,GAAsBlC,EAAAA,SACxB,YAApBL,GAAOM,SACH0B,EAAWhC,GAAOoB,aAAwBpB,GAAOA,OACjD,OAECM,EAAUC,GAAeF,EAAQA,SAErCL,GAAOM,UAAoB,WAG9BkC,EAAAA,UAAU,KACRjC,EAAaP,GAAOM,UAAoB,WACxC+B,EAAaL,EAAUhC,GAAOA,QAC9BuC,EACsB,YAApBvC,GAAOM,SACH0B,EAAWhC,GAAOoB,aAAwBpB,GAAOA,OACjD,OAEL,CAACA,GAAOA,MAAOA,GAAOoB,YAAapB,GAAOM,WAG7CkC,EAAAA,UAAU,KACS,YAAblC,IAA2BgC,GAAmBF,GAEhDG,EAAmBH,IAEpB,CAAC9B,EAAU8B,EAAWE,IAEzB,MAAMG,EAAY,CAACC,EAAmBC,EAAgBC,IAC/CF,EACDC,GAAWD,EAAOC,GAClBC,GAAWF,EAAOE,EADgB,KAE/BF,EAHW,KAMdG,EAAmB,CAACC,EAAsBtB,GAAW,KACzD,MAAM/B,EAAY+B,EAAWY,EAAYU,EACnCC,EAAavB,EAAWsB,EAAUR,EAElCU,EACS,YAAb1C,GAA0BgC,EAAkBA,EAAkBW,EAG1DC,EAAc1B,EAChBiB,EAAUK,EAHOV,GAAae,QAActF,EAGXoF,GACjCR,EAAUK,EAASK,EAAYH,GAKnC,GAHIxB,EAAUe,EAAmBW,GAC5Bb,EAAaa,GAED,YAAb5C,EACF,GAAIb,GAAasD,EAAY,CAC3B,MACMK,EAAM3D,GAAasD,EAAatD,EAAYsD,EAClD9C,EAAS,CACPW,KAAM,OACNZ,MAAOkC,EAJKzC,GAAasD,EAAatD,EAAYsD,GAKlD3B,YAAac,EAASkB,GACtB9C,SAAU,WAEb,MACCL,EAAS,WAITA,EADEiD,EACO,CACPtC,KAAM,OACNZ,MAAOkC,EAASgB,GAChB5C,SAAUA,GAGH,OAsCT6C,EAAatJ,EAAAA,QAAQ,IAAOuC,EAAM,IAAIjB,KAAKiB,QAAOyB,EAAY,CAACzB,IAC/D6G,EAAapJ,EAAAA,QAAQ,IAAOyC,EAAM,IAAInB,KAAKmB,QAAOuB,EAAY,CAACvB,IAErE,OACEa,EAAKC,KAAA,MAAA,CAAAC,UAAU,sBACbF,OACE,MAAA,CAAAM,SAAA,CAAAC,MAAA,QAAA,CAAOL,UAAU,+CAA8CI,SAAA,gBAG/DN,EAAAA,KACE,SAAA,CAAA6C,MAAOM,EACPL,SAAWO,GA5CU,CAACC,IAC5B,MAAMC,EAAKD,EAOXF,EAAYG,GAED,YAAPA,EACE0B,GAAaE,GAIfrC,EAAS,CACPW,KAAM,OACNZ,MAJAoC,GAAaE,EAAkBF,EAAYE,EAK3ClB,YAJUgB,GAAaE,EAAkBF,EAAYE,EAKrDhC,SAAU,YAIV8B,GACFnC,EAAS,CACPW,KAAM,OACNZ,MAAOoC,EACP9B,SAAUI,KAiBOG,CAAqBL,EAAEM,OAAOd,OAC/C3C,UAAU,4GAEVI,SAAA,CAAAC,EAAAqB,IAAA,SAAA,CAAQiB,MAAM,SAAyBvC,SAAA,YACvCC,EAAAA,IAAQ,SAAA,CAAAsC,MAAM,KAAmBvC,SAAA,UACjCC,EAAAA,IAAQ,SAAA,CAAAsC,MAAM,MAA0BvC,SAAA,gBACxCC,gBAAQsC,MAAM,KAAIvC,SAAA,WAClBC,MAAA,SAAA,CAAQsC,MAAM,MAA2BvC,SAAA,iBACzCC,EAAAA,IAAQ,SAAA,CAAAsC,MAAM,UAASvC,SAAA,wBAI3BN,EAAAA,KAAA,MAAA,CAAAM,SAAA,CACEC,EAAAA,IAAO,QAAA,CAAAL,UAAU,+CACdI,SAAa,YAAb6C,EAAyB,YAAc,SAE1C5C,EAAAA,IAACkE,EAAU,CACTC,KAAK,oBACL7B,MAAOoC,EAAYD,EAAAA,OAAOC,EAAW,cAAgB,KACrDnC,SAAWgC,GACTY,EACEZ,EAAI7G,EAAAA,MAAM6G,EAAG,aAAc,IAAI9G,MAAU,MACzC,GAGJkC,UAAU,cAIA,YAAbiD,GACCnD,EAAAA,KAAA,MAAA,CAAAM,SAAA,CACEC,EAAAA,IAAO,QAAA,CAAAL,UAAU,+CAETI,SAAA,YACRC,EAACqB,IAAA6C,EACC,CAAAC,KAAK,kBACL7B,MACEsC,EAAkBH,EAAMA,OAACG,EAAiB,cAAgB,KAE5DrC,SAAWgC,GACTY,EACEZ,EAAI7G,EAAKA,MAAC6G,EAAG,aAAc,IAAI9G,MAAU,MACzC,GAGJkC,UAAU,kBCzLTgG,EAAoB,EAC/BrD,QACAC,WACAqD,UACAhK,OACAiK,gBAEA,MAAOC,EAAYC,GAAiBpD,EAAQA,SAAC,KACtCqD,EAAgBC,GAAqBtD,EAAQA,SAClD,IAAI3G,IAAIsG,GAAOA,OAAS,KAI1BwC,EAAAA,UAAU,KACRmB,EAAkB,IAAIjK,IAAIsG,GAAOA,OAAS,MACzC,CAACA,IAGJ,MAAM4D,EAAmB/J,EAAAA,QAAQ,KAC/B,IAAIgK,EAAuB,GAG3B,MAAMC,EAAgBC,GAElBA,SAEQ,KAARA,GACgB,iBAARA,GAAmC,KAAfA,EAAIpD,OAIpC,GAAI2C,GAAWA,EAAQnJ,OAAS,EAC9B0J,EAAOP,MACF,CAEL,MAAMU,EAAmB1K,EAAKgB,IAAKC,IACjC,MAAMwJ,EAAMxJ,EAAIgJ,GAChB,OAAOO,EAAaC,GAAO,KAAOA,IAKpCF,EAFqB,IAAI,IAAInK,IAAIsK,IAEb1J,IAAKyJ,IAAS,CAChCjC,MAAe,OAARiC,EAAe,QAAUtI,OAAOsI,GACvC/D,MAAO+D,IAEV,CAID,MAAME,EAAmBJ,EAAKvJ,IAAK4J,IACjC,MAAMC,EAAUL,EAAaI,EAAIlE,OAEjC,MAAO,IACFkE,EACHpC,MAAOqC,EAAU,QAAUD,EAAIpC,MAC/B9B,MAAOmE,EAAU,KAAOD,EAAIlE,MAC5BmE,aAeJ,OAVwBC,MAAMC,KAC5B,IAAIzH,IACFqH,EAAiB3J,IAAK4J,GAAQ,CAC5BA,EAAIC,QAAU,YAAcD,EAAIlE,MAChCkE,KAEF7J,UAImBiK,KAAK,CAACrI,EAAGC,IAC1BD,EAAEkI,UAAYjI,EAAEiI,QAAgB,GAC/BlI,EAAEkI,SAAWjI,EAAEiI,SAAiB,EAC9B1I,OAAOQ,EAAE6F,OAAOyC,cAAc9I,OAAOS,EAAE4F,UAE/C,CAACwB,EAAShK,EAAMiK,IAGbiB,EAAkB3K,EAAAA,QAAQ,KAC9B,IAAK2J,EAAY,OAAOI,EACxB,MAAMa,EAAcjB,EAAWkB,cAC/B,OAAOd,EAAiB5J,OACrB2K,GACCA,EAAO7C,MAAM4C,cAAc1J,SAASyJ,IACpChJ,OAAOkJ,EAAO3E,OAAO0E,cAAc1J,SAASyJ,IAC3B,UAAhBA,GAA4BE,EAAeR,UAE/C,CAACP,EAAkBJ,IAwCtB,OACErG,OAAA,MAAA,CAAKE,UAAU,YAAWI,SAAA,CAExBN,EAAAA,KAAK,MAAA,CAAAE,UAAU,WACbI,SAAA,CAAAC,EAAAqB,IAAC6F,SAAO,CAAAvH,UAAU,6EAClBK,EAAAA,IACE,QAAA,CAAAkD,KAAK,OACLZ,MAAOwD,EACPvD,SAAWO,GAAMiD,EAAcjD,EAAEM,OAAOd,OACxCE,YAAY,oBACZ7C,UAAU,uHAKdF,EAAAC,KAAA,MAAA,CAAKC,UAAU,4CAA2CI,SAAA,CACxDN,EAAAA,KAAM,OAAA,CAAAE,UAAU,0BACbqG,EAAe5D,KAAI,OAAM8D,EAAiBzJ,OAAM,eAEnDgD,EAAAA,KAAA,MAAA,CAAKE,UAAU,aAAYI,SAAA,CACzBC,EAAAA,IACE,SAAA,CAAAmH,QApCc,KACtB,MAAMC,EAAY,IAAIpL,IAAI8K,EAAgBlK,IAAK4J,GAAQA,EAAIlE,QAC3D2D,EAAkBmB,GAClB7E,EAAS,CACPW,KAAM,cACNZ,MAAOoE,MAAMC,KAAKS,GAClBxE,SAAU,YA+BJjD,UAAU,sDAAqDI,SAAA,eAIjEC,EAAAqB,IAAA,SAAA,CACE8F,QAhCa,KACrBlB,EAAkB,IAAIjK,KACtBuG,EAAS,OA+BD5C,UAAU,sDAAqDI,SAAA,oBAQrEC,EAAAA,IAAK,MAAA,CAAAL,UAAU,6DACZI,SAA2B,IAA3B+G,EAAgBrK,OACfuD,EAAAA,WAAKL,UAAU,wCAAuCI,SAAA,qBAItD+G,EAAgBlK,IAAI,CAACqK,EAAQI,KAC3B,MAAMC,EAAatB,EAAe3G,IAAI4H,EAAO3E,OAC7C,OACE7C,EAAAC,KAAA,MAAA,CAEEyH,QAAS,IArFG,CAACI,IACzB,MAAMC,EAAc,IAAIxL,IAAIgK,GAExBwB,EAAYnI,IAAIkI,GAClBC,EAAYC,OAAOF,GAEnBC,EAAYE,IAAIH,GAGlBtB,EAAkBuB,GAGdA,EAAYpF,KAAO,EACrBG,EAAS,CACPW,KAAM,cACNZ,MAAOoE,MAAMC,KAAKa,GAClB5E,SAAU,WAGZL,EAAS,OAkEgBoF,CAAkBV,EAAO3E,OACxC3C,UAAW,kFACT2H,EAAa,aAAe,IAG9BvH,SAAA,CAAAC,MAAA,MAAA,CACEL,UAAW,8DACT2H,EACI,yCACA,mBACJvH,SAEDuH,GAActH,EAAAqB,IAACuG,EAAKA,MAAA,CAACjI,UAAU,cAElCK,EAAAA,IAAA,OAAA,CACEL,UAAW,WACT2H,EAAa,4BAA8B,mBACxCL,EAAeR,QAAU,uBAAyB,KAEtD1G,SAAAkH,EAAO7C,UApBLiD,WCnLRQ,EAAgB,EAAGvF,QAAOC,eACrC,MAAOuF,EAAcC,GAAmBpF,EAAAA,cACrBxC,IAAjBmC,GAAOA,MAAsBvE,OAAOuE,EAAMA,OAAS,IAiBrD,OACEtC,EAAKqB,IAAA,MAAA,CAAA1B,UAAU,YACbI,SAAAN,EAAAA,KAAA,MAAA,CAAAM,SAAA,CACEC,EAAOqB,IAAA,QAAA,CAAA1B,UAAU,+CAETI,SAAA,kBACRN,EACEC,KAAA,SAAA,CAAA4C,MAAOwF,EACPvF,SAAWO,IAAMc,OAtBEC,EAsBgBf,EAAEM,OAAOd,MArBlDyF,EAAgBlE,QAGdtB,EADe,KAAbsB,EACO,KAEA,CACPX,KAAM,UACNZ,MAAoB,SAAbuB,EACPjB,SAAU,WATU,IAACiB,GAuBnBlE,UAAU,4GAEVI,SAAA,CAAAC,EAAAqB,IAAA,SAAA,CAAQiB,MAAM,GAAEvC,SAAA,eAChBC,EAAAqB,IAAA,SAAA,CAAQiB,MAAM,OAAMvC,SAAA,SACpBC,EAAAqB,IAAA,SAAA,CAAQiB,MAAM,QAAOvC,SAAA,mBCzBlBiI,EAAgB,EAAGC,SAAQrM,OAAM0G,QAAOC,eACnD,MAAM2F,EAA6B/L,EAAAA,QAAQ,KACzC,GAAiC,iBAAtB8L,EAAOE,WAAyB,OAAOF,EAAOE,WAGzD,MAAMC,EAAexM,EAClByM,MAAM,EAAG,KACTzL,IAAKC,GAAQA,EAAIoL,EAAOnL,MACxBR,OAAQ+J,GAAe,MAAPA,GAEnB,GAA4B,IAAxB+B,EAAa3L,OAAc,MAAO,CAAEyG,KAAM,QAE9C,MAAMoF,EAAaF,EAAa,GAChC,GAA0B,kBAAfE,EAA0B,MAAO,CAAEpF,KAAM,WACpD,GAA0B,iBAAfoF,EAAyB,MAAO,CAAEpF,KAAM,UACnD,GACEoF,aAAsB7K,MACC,iBAAf6K,IAA4BrE,MAAMxG,KAAKC,MAAM4K,IAErD,MAAO,CAAEpF,KAAM,QAGjB,MAAMqF,EAAe,IAAI,IAAIvM,IAAIoM,IACjC,OAAIG,EAAa9L,QAAU,GAClB,CACLyG,KAAM,cACN0C,QAAS2C,EAAa3L,IAAKyJ,IAAG,CAAQjC,MAAOrG,OAAOsI,GAAM/D,MAAO+D,MAI9D,CAAEnD,KAAM,SACd,CAAC+E,EAAOE,WAAYF,EAAOnL,IAAKlB,IAEnC,OAAQsM,EAAahF,MACnB,IAAK,OACH,OACElD,MAACqC,GAAWC,MAAOA,EAAOC,SAAUA,EAAUC,YAAa0F,EAAa1F,cAE5E,IAAK,SACH,OACExC,EAACqB,IAAAkC,EAAa,CAAAjB,MAAOA,EAAOC,SAAUA,EAAU7D,IAAKwJ,EAAaxJ,IAAKE,IAAKsJ,EAAatJ,MAE7F,IAAK,OACH,OACEoB,EAACqB,IAAAgD,EAAW,CAAA/B,MAAOA,EAAOC,SAAUA,EAAU7D,IAAKwJ,EAAaxJ,IAAKE,IAAKsJ,EAAatJ,MAE3F,IAAK,cACH,OACEoB,EAAAA,IAAC2F,EAAiB,CAChBrD,MAAOA,EACPC,SAAUA,EACVqD,QAASsC,EAAatC,SAAW,GACjChK,KAAMA,EACNiK,UAAWoC,EAAOnL,MAGxB,IAAK,UACH,OAAOkD,EAAAA,IAAC6H,EAAa,CAACvF,MAAOA,EAAOC,SAAUA,IAChD,QACE,OAAO,OCdAiG,EAAa,EACxBzM,gBACA0M,kBACAC,YAAY,EACZC,aAAa,EACbC,eACAC,aACAC,gBACAjN,aACAkN,eACAC,YACApN,OACAqN,SACAC,iBACAC,cACAC,iBACAC,eAAe,IAAIrN,IACnBsN,cACAC,aAAY,EACZC,kBACAC,YACAC,mBAAmB,GACnBC,gBACAC,mBACAC,uBACAC,iBACAC,cACAC,sBACAC,iBAAiB,aAEjB,MAAMC,EAAa,IAAInO,KAAkB0M,IAClC0B,EAAkBC,GAAuBzH,EAAQA,SAAgB,OACjE0H,EAAcC,GAAmB3H,EAAQA,SAItC,OACH4H,EAAYC,GAAiB7H,EAAQA,SAAgB,OACrD8H,EAAQC,GAAa/H,EAAQA,SAAS,IACtCgI,EAAYC,GAAiBjI,EAAQA,SAAS,IAC9CkI,EAAWC,GAAgBnI,EAAQA,SACxC,SAEKoI,EAAYC,GAAiBrI,EAAQA,SAA2B,OAChEsI,EAAeC,GAAoBvI,EAAQA,UAAC,GAEnDmC,EAAAA,UAAU,KACRoG,EACE,iBAAkBC,QAChBC,UAAUC,eAAiB,GAC3BF,OAAOG,WAAW,qBAAqBC,UAE1C,IAGH,MAAOC,EAAwBC,GAA6B9I,EAAQA,SAElE,OACK+I,EAAqBC,GAA0BhJ,EAAQA,SAGpD,MAEJiJ,GAAgB7C,EAAa3G,OAAS4G,GAAaA,EAAY,EAC/D6C,GACJ9C,EAAa3G,KAAO,GAAK2G,EAAa3G,KAAO4G,EAEzC8C,GAAwB,CAC5BjG,EACAkG,KAGA,GADAA,EAAMC,kBACF7B,IAAqBtE,EACvBuE,EAAoB,UACf,CACLA,EAAoBvE,GACpBiF,EAAa,QACbE,EAAclC,EAAcjD,IAAc,MAG1C,MAAMoG,EAAOF,EAAMG,cAAcC,wBAE3BC,EAAcL,EAAMG,cAAcG,QACtC,yBAEIC,EAAgBF,EAClBA,EAAYD,wBACZ,IAAII,QAAQ,EAAG,EAAGpB,OAAOqB,WAAYrB,OAAOsB,aAE1CC,EAAa,IACbC,EAAU,GAGVC,EAAUN,EAAcpL,KAAOyL,EAC/BE,EAAUP,EAAcQ,MAAQJ,EAAaC,EAC7CI,EAActO,KAAKG,IAAIgO,EAASnO,KAAKC,IAAIuN,EAAK/K,KAAM2L,IAE1DvC,EAAgB,CACd0C,IAAKf,EAAKgB,OAAS,EACnB/L,KAAM6L,EACNT,iBAEH,GAGGY,GAAqBrH,IACzBqD,EAAerD,EAAWkF,GAAc,MACxCX,EAAoB,OAGhB+C,GAAmBtH,GACY,MAA5BiD,EAAcjD,GAGjBuH,GAAoB,CAACvH,EAAmBkG,KAC5CA,EAAMsB,iBACN7C,EAAc3E,GACd6E,EAAUqB,EAAMuB,SAChB,MAAMrF,EAAS,IAAIlM,KAAkB0M,GAAiBjJ,KACnDjD,GAAQA,EAAIO,MAAQ+I,GAEvB+E,EAAc3C,GAAQjJ,OAAS,MAG3BuO,GAAoBxB,IACxB,IAAKxB,IAAenB,EAAgB,OAEpC,MAAMoE,EAASzB,EAAMuB,QAAU7C,EACzBgD,EAAWhP,KAAKG,IAAI,GAAI+L,EAAa6C,GAC3CpE,EAAemB,EAAYkD,IAGvBC,GAAkB,KACtBlD,EAAc,MACdE,EAAU,GACVE,EAAc,KAIT+C,GAAaC,IAAkBjL,EAAQA,SAAgB,OACvDkL,GAAUC,IAAenL,EAAQA,SAG9B,MAIJoL,GAAkB,CACtBlI,EACA/C,KAEA8K,GAAe/H,GACf/C,EAAEkL,aAAaC,cAAgB,OAC/BnL,EAAEkL,aAAaE,QAAQ,aAAcrI,IAGjCsI,GAAiB,CACrBC,EACAtL,KAGAA,EAAEuK,iBACFvK,EAAEkL,aAAaK,WAAa,OAC5B,MAAMpC,EAAQnJ,EAAEoJ,cAAiCC,wBAC3CmC,EAAWrC,EAAK/K,KAAO+K,EAAKjN,MAAQ,EACpCuP,EAAWzL,EAAEwK,QAAUgB,EAAW,SAAW,QACnDR,GAAY,CAAEhR,IAAKsR,EAAWG,cAG1BC,GAAcJ,IAClB,MAAMK,EAAYd,GAClB,IAAKc,GAAaA,IAAcL,EAAW,OAK3C,GAFqB/E,EAAahK,IAAIoP,KACjBpF,EAAahK,IAAI+O,GAIpC,OAFAR,GAAe,WACfE,GAAY,MAId,MAAMY,EACJb,IAAU/Q,MAAQsR,GAAmC,WAAvBP,IAAUU,SACpCI,EAAO5E,EAAYzN,OAAQsS,GAAMA,IAAMH,GAC7C,IAAI7M,EAAM+M,EAAKE,QAAQT,GACvB,IAAa,IAATxM,EAIF,OAHAoI,EAAoB2E,GACpBf,GAAe,WACfE,GAAY,MAGTY,IAAQ9M,GAAO,GACpB+M,EAAKG,OAAOlN,EAAK,EAAG6M,GAEpBzE,EADa2E,GAEbf,GAAe,MACfE,GAAY,OAGRiB,GAAgB,KACpBnB,GAAe,MACfE,GAAY,OAIRkB,GAA6B,CACjCnJ,EACA/C,KAEA2I,EAA0B5F,GAC1B/C,EAAEkL,aAAaC,cAAgB,OAC/BnL,EAAEkL,aAAaE,QAAQ,aAAcrI,IAGjCoJ,GAA4B,CAChCb,EACAtL,KAEAA,EAAEuK,iBACFvK,EAAEkL,aAAaK,WAAa,OAC5B,MAAMpC,EAAQnJ,EAAEoJ,cAAiCC,wBAC3CmC,EAAWrC,EAAKe,IAAMf,EAAKnM,OAAS,EACpCyO,EAAWzL,EAAEoM,QAAUZ,EAAW,SAAW,QACnD3C,EAAuB,CAAE7O,IAAKsR,EAAWG,cAGrCY,GAAyBf,IAC7B,MAAMK,EAAYjD,EAClB,IAAKiD,GAAaA,IAAcL,EAAW,OAK3C,GAFqB/E,EAAahK,IAAIoP,KACjBpF,EAAahK,IAAI+O,GAIpC,OAFA3C,EAA0B,WAC1BE,EAAuB,MAIzB,MAAM+C,EACJhD,GAAqB5O,MAAQsR,GACS,WAAlC1C,GAAqB6C,SAErBI,EAAO5E,EAAYzN,OAAQsS,GAAMA,IAAMH,GAC7C,IAAI7M,EAAM+M,EAAKE,QAAQT,GACvB,IAAa,IAATxM,EAIF,OAHAoI,EAAoB2E,GACpBlD,EAA0B,WAC1BE,EAAuB,MAGpB+C,IAAQ9M,GAAO,GACpB+M,EAAKG,OAAOlN,EAAK,EAAG6M,GACpBzE,EAAoB2E,GACpBlD,EAA0B,MAC1BE,EAAuB,OAGnByD,GAA2B,KAC/B3D,EAA0B,MAC1BE,EAAuB,OAoCzB,OAhCA7G,EAAAA,UAAU,KACR,GAAIyF,EAMF,OALA8E,SAASC,iBAAiB,YAAa/B,IACvC8B,SAASC,iBAAiB,UAAW5B,IACrC2B,SAASE,KAAK3P,MAAM4P,OAAS,aAC7BH,SAASE,KAAK3P,MAAM6P,WAAa,OAE1B,KACLJ,SAASK,oBAAoB,YAAanC,IAC1C8B,SAASK,oBAAoB,UAAWhC,IACxC2B,SAASE,KAAK3P,MAAM4P,OAAS,GAC7BH,SAASE,KAAK3P,MAAM6P,WAAa,KAGpC,CAAClF,EAAYE,EAAQE,EAAYvB,IAGpCtE,EAAAA,UAAU,KACR,MAAM6K,EAAsB5D,IAC1B,MAAM3I,EAAS2I,EAAM3I,OACjB+G,IAAqB/G,EAAOiJ,QAAQ,uBACtCjC,EAAoB,OAIxB,GAAID,EAEF,OADAkF,SAASC,iBAAiB,YAAaK,GAChC,IACLN,SAASK,oBAAoB,YAAaC,IAE7C,CAACxF,IAGFnK,MAAA4P,EAAAA,SAAA,CAAA7P,SACEC,EACEqB,IAAA,MAAA,CAAA1B,UAAU,4EACVC,MAAO,CAAEE,OAAQ8I,GACjBiH,KAAK,MAAK,aACC,iBAAgB9P,SAE3BN,cAAKE,UAAU,cAAaI,SAAA,CACzBlE,GACCmE,EAAAqB,IAAA,MAAA,CACE1B,UAAU,2IACVC,MAAO,CAAEZ,MAAOxD,EAAkBsE,OAAQ8I,GAC1CiH,KAAK,eAAc,aACR,kBAAiB9P,SAE5BC,EACEqB,IAAA,QAAA,CAAA6B,KAAK,WACL4M,QAASlE,GACTmE,IAAMC,IACAA,IAAIA,EAAGC,cAAgBpE,KAE7BtJ,SAAU4G,EACV+G,UAAYpN,IACI,MAAVA,EAAEhG,KAAyB,UAAVgG,EAAEhG,MACrBgG,EAAEuK,iBACFlE,MAGJxJ,UAAU,6KAERiM,GACI,oBACAC,GACA,GAAG9C,EAAa3G,0CAChB,kBAENjB,MACEyK,GACI,gBAAgB5C,SAChB6C,GACA,GAAG9C,EAAa3G,WAAW4G,kBAC3B,cAAcA,aAOzBjN,EAAca,IAAI,CAACqL,EAAQZ,KAC1B,MAAM5G,EAAW4I,EAAahK,IAAI4I,EAAOnL,KACnCqC,EAAoBpD,EACvBO,OAAQ8C,GAAMiK,EAAahK,IAAID,EAAEtC,MACjCF,IAAKwC,GAAMA,EAAEtC,KACVqT,EAAchR,EAAkB0P,QAAQ5G,EAAOnL,KAC/C4D,EAAaD,EACftB,EAAkBkJ,MAAM,EAAG8H,GAAa7R,OACtC,CAACS,EAAKjC,KACJ,MAAMP,EAAMR,EAAcyD,KAAMJ,GAAMA,EAAEtC,MAAQA,GAChD,OAAOiC,GAAOxC,GAAKyC,OAAS,MAE9BnD,EAAaL,EAAmB,GAElC,EAEE4U,EACmB,WAAvBnI,EAAOoI,YACH,iBACuB,UAAvBpI,EAAOoI,YACP,cACA,gBAGAC,GAAkC,IAApBrI,EAAOsI,SAC3B,OACE9Q,EAAAC,KAAA,MAAA,CAEEC,UAAW,oFACTc,EACI,iFACA,MAENb,MAAO,CACLZ,MAAOiJ,EAAOjJ,OAAS,IACvBkC,KAAMT,EAAW,GAAGC,MAAiB,OACrCZ,OAAQ8I,GACT,kBACgBX,EAAOnL,IACxB0T,WACA,EAAAC,YAAc3N,GAAMiL,GAAgB9F,EAAOnL,IAAKgG,GAChD4N,WAAa5N,GAAMqL,GAAelG,EAAOnL,IAAKgG,GAC9C6N,OAAQ,IAAMnC,GAAWvG,EAAOnL,KAChC8T,UAAW7B,GAAahP,SAAA,CAGvB8N,IAAU/Q,MAAQmL,EAAOnL,KACxBkD,aACEL,UAAU,gDACVC,MAAO,CACLsB,KAA6B,WAAvB2M,IAAUU,SAAwB,OAAIpO,EAC5C2M,MAA8B,UAAvBe,IAAUU,SAAuB,OAAIpO,KAIlDH,MAAK,MAAA,CAAAL,UAAU,cAAaI,SAE1BN,EAAAA,KAAK,MAAA,CAAAE,UAAU,uDAAsDI,SAAA,CACnEN,EACEC,KAAA,MAAA,CAAAC,UAAW,oCAAoCyQ,KAC7CE,EAAc,qCAAuC,KAEvDnJ,QAAS,IAAMmJ,GAAerH,EAAOhB,EAAOnL,eAE5C2C,OAAK,MAAA,CAAAE,UAAU,4CAEZwN,GAAgBlF,EAAOnL,MACtBkD,EAAAqB,IAAA,MAAA,CACE1B,UAAU,2CACVwB,MAAM,mBAGVnB,EAAAA,IAAM,OAAA,CAAAL,UAAU,iFACbsI,EAAO7G,YAGXkP,GAAezH,EAAW/L,MAAQmL,EAAOnL,KACxCkD,EAAAqB,IAAA,MAAA,CAAK1B,UAAU,gDACZI,SAAyB,QAAzB8I,EAAWgI,UACV7Q,EAAAA,IAAC8Q,EAAAA,QAAQ,CAAAnR,UAAU,6CAEnBK,MAAC+Q,EAAAA,UAAS,CAACpR,UAAU,mDAO3BsI,EAAOE,YAAcmB,GAAeC,IACpC9J,EAAAA,KACE,MAAA,CAAAE,UAAW,aACTwK,IAAqBlC,EAAOnL,KAAOmO,EAC/B,QACA,4BACJ,oBAAA,EAAAlL,SAAA,CAGFC,gBACEL,UAAU,gEACVwH,QAAUrE,GAAMgJ,GAAsB7D,EAAOnL,IAAKgG,mCAGlD9C,EAAAA,IAACgR,EAAIA,MAACrR,UAAU,+CAIjBwK,IAAqBlC,EAAOnL,KAAOuN,GAClC5K,EAAAC,KAAA,MAAA,CACEC,UAAU,kIACVC,MAAO,CACLoN,IAAK,GAAG3C,EAAa2C,QACrB9L,KAAM,GAAGmJ,EAAanJ,SAEtB+P,UAAW,GAAGxS,KAAKG,IACjB,IACAyL,EAAaiC,cAAcW,OACzB,GACA5C,EAAa2C,UAMnB,oBAAA,EAAAjN,SAAA,CAAAN,EAAAA,KAAA,MAAA,CAAKE,UAAU,kEAAiEI,SAAA,CAC9EC,EACEqB,IAAA,SAAA,CAAA1B,UAAW,8CACK,SAAdkL,EACI,2CACA,iBAEN1D,QAAS,IAAM2D,EAAa,QAAO/K,SAEnCC,EAAAA,IAACgR,EAAIA,KAAA,CAACrR,UAAU,cAEjBsI,EAAOE,YACNnI,gBACEL,UAAW,8CACK,WAAdkL,EACI,2CACA,iBAEN1D,QAAS,IAAM2D,EAAa,UAE5B/K,SAAAC,MAACkR,EAAAA,OAAM,CAACvR,UAAU,cAGtBK,MAAA,SAAA,CACEL,UAAW,8CACK,YAAdkL,EACI,2CACA,iBAEN1D,QAAS,IAAM2D,EAAa,WAAU/K,SAEtCC,MAACmR,cAAW,CAACxR,UAAU,iBAIZ,SAAdkL,GACCpL,EAAAA,KAAK,MAAA,CAAAE,UAAU,iBACZ2J,IACEE,IAAoBvB,EAAOnL,IAC1B2C,EAAAA,KAAA,MAAA,CAAKE,UAAU,2EACbI,SAAA,CAAAC,EAAAA,IAACoR,EAAAA,IAAI,CAAAzR,UAAU,YAEX,wBAENK,EAAAqB,IAAA,SAAA,CACE1B,UAAU,8EACVwH,QAAUrE,IACRA,EAAEkJ,kBACF5B,EAAoB,MACpBd,IACErB,EAAOnL,KACNuM,EAAahK,IAAI4I,EAAOnL,OAE5BiD,SAEAsJ,EAAahK,IAAI4I,EAAOnL,KACvB2C,EAAAA,KACEmQ,EAAAA,SAAA,CAAA7P,SAAA,CAAAC,MAACqR,EAAAA,OAAM,CAAC1R,UAAU,YAAY,kBAIhCF,OACEmQ,EAAAA,SAAA,CAAA7P,SAAA,CAAAC,EAAAqB,IAAC+P,MAAG,CAACzR,UAAU,YAAY,mBAOpCiK,GACC5J,EAAAqB,IAAA,SAAA,CACE1B,UAAU,sDACVwH,QAAS,KACPyC,IAAmB3B,EAAOnL,KAC1BsN,EAAoB,OAIfrK,SAAA,yBAEV8J,GACC7J,MAAA,SAAA,CACEL,UAAU,sDACVwH,QAAS,KACP0C,MACAO,EAAoB,yCAOzBb,GACCvJ,EAAAA,IACE,SAAA,CAAAL,UAAU,8EACVwH,QAAUrE,IACRA,EAAEkJ,kBACF5B,EAAoB,MACpB,MAAMkH,EACJ5H,GAAkBpM,SAChB2K,EAAOnL,MACJ0M,IAAoBvB,EAAOnL,IAC9B6M,EACFA,EAAc1B,EAAOnL,KAAMwU,GAE3B7H,IACE6H,EAAY,KAAOrJ,EAAOnL,MAI/BiD,SAAAyJ,IAAoBvB,EAAOnL,KAC5B4M,GAAkBpM,SAAS2K,EAAOnL,KAChC2C,EAAAA,KAAAmQ,EAAA2B,SAAA,CAAAxR,SAAA,CACEC,EAAAA,IAACwR,EAAAA,QAAO,CAAC7R,UAAU,yBAIrBF,OAAAmQ,EAAAA,SAAA,CAAA7P,SAAA,CACEC,MAACyR,EAAAA,MAAK,CAAC9R,UAAU,wBACPsI,EAAO7G,YAMxB0I,GACC9J,EACEqB,IAAA,SAAA,CAAA1B,UAAU,sDACVwH,QAAS,KACP2C,MACAM,EAAoB,OAIfrK,SAAA,qBAIA,WAAd8K,GACCpL,OAAK,MAAA,CAAAE,UAAU,aAAYI,SAAA,CAExBoN,GAAgBlF,EAAOnL,MACtB2C,OAAA,SAAA,CACEE,UAAU,oEACVwH,QAAS,KACP6D,EAAc,MACd9B,EAAejB,EAAOnL,IAAK,MAC3BsN,EAAoB,iBAGtBpK,EAAAA,IAAC0R,EAAOA,QAAC,CAAA/R,UAAU,YACZ,mBAEXK,EAAAA,IAAK,MAAA,CAAAL,UAAU,uCACbK,EAAAA,IAACgI,EACC,CAAAC,OAAQA,EACRrM,KAAMA,EACN0G,MAAOyI,EACPxI,SAAW8D,IACT2E,EAAc3E,GAES,WAAnB4D,GACFf,EAAejB,EAAOnL,IAAKuJ,QAKf,WAAnB4D,EACCxK,EAAAA,KAAA,MAAA,CAAKE,UAAU,wCACbK,EACEqB,IAAA,SAAA,CAAA1B,UAAU,6EACVwH,QAAS,IAAMiD,EAAoB,MAAKrK,SAAA,WAI1CC,EAAAA,IACE,SAAA,CAAAL,UAAU,0EACVwH,QAAS,IACP+F,GAAkBjF,EAAOnL,KAIpBiD,SAAA,aAGXC,EAAAA,WAAKL,UAAU,wBACbI,SAAAC,EAAAqB,IAAA,SAAA,CACE1B,UAAU,0EACVwH,QAAS,IAAMiD,EAAoB,MAG5BrK,SAAA,eAKF,YAAd8K,GACCpL,EAAAA,KAAK,MAAA,CAAAE,UAAU,YACbI,SAAA,CAAAC,EAAAA,IAAA,MAAA,CAAKL,UAAU,yCAETI,SAAA,oBACNC,EAAAA,IAAK,MAAA,CAAAL,UAAU,2DAA0DI,SAGrD,IACXgK,EACAnN,IAAKgS,GACJ1E,EAAW1K,KAAMJ,GAAMA,EAAEtC,MAAQ8R,IAElCtS,OAAOqV,YAEPzH,EAAW5N,OACX8C,IAAO2K,EAAYzM,SAAS8B,EAAEtC,OAIpBF,IAAKwC,IAClB,MAAMwS,EAAevI,EAAahK,IAChCD,EAAEtC,KAEJ,OACE2C,EAAAA,YAEEE,UAAW,mHACT6L,IAA2BpM,EAAEtC,IACzB,aACA,MAEJ8U,EAAe,gBAAkB,KAEnCpB,WACA,EAAAC,YAAc3N,GACZkM,GAA2B5P,EAAEtC,IAAKgG,GAEpC4N,WAAa5N,GACXmM,GAA0B7P,EAAEtC,IAAKgG,GAEnC6N,OAAQ,IACNxB,GAAsB/P,EAAEtC,KAE1B8T,UAAWxB,GAGVrP,SAAA,CAAA2L,GAAqB5O,MACpBsC,EAAEtC,KACFkD,MAAA,MAAA,CACEL,UAAU,4CACVC,MAAO,CACLoN,IAEE,WADAtB,GAAqB6C,SAEjB,OACApO,EACN8M,OAEE,UADAvB,GAAqB6C,SAEjB,OACApO,KAIZV,EAAAA,KAAA,MAAA,CAAKE,UAAU,0BACbI,SAAA,CAAAC,EAAAqB,IAAA,OAAA,CAAM1B,UAAU,4BAETI,SAAA,OACPN,eAAME,UAAU,kCACbI,SAAA,CAAAX,EAAEgC,OACFwQ,GACC5R,EAAAA,IAAM,OAAA,CAAAL,UAAU,iCAAgCI,SAAA,qBA9CjDX,EAAEtC,SAyDjBkD,MAAA,MAAA,CAAKL,UAAU,iCAGTI,SAAA,8EAWrBsH,EAAQtL,EAAcU,OAAS,GAAK2M,GACnCpJ,EAAAA,IAAA,MAAA,CACEL,UAAU,gGACVkS,YAAc/O,GAAMsK,GAAkBnF,EAAOnL,IAAKgG,GAClDgP,cAAgBhP,IACdA,EAAEuK,iBACFvK,EAAEkJ,kBACFpC,IAAmB3B,EAAOnL,MAE5BqE,MAAM,qDAENnB,EAAAA,IAAK,MAAA,CAAAL,UAAU,8CAhZdsI,EAAOnL,IAAMuK,KAwZvBqB,EAAY,GACX1I,EACEqB,IAAA,MAAA,CAAAzB,MAAO,CAAEZ,MAAO0J,EAAW5I,OAAQ8I,GACnCjJ,UAAU,cAKb8I,EAAgB7L,IAAI,CAACqL,EAAQZ,KAE5B,MAAM+I,EACmB,WAAvBnI,EAAOoI,YACH,iBACuB,UAAvBpI,EAAOoI,YACP,cACA,gBAEAC,GAAkC,IAApBrI,EAAOsI,SAC3B,OACE9Q,OAEE,MAAA,CAAAE,UAAW,mFACXC,MAAO,CAAEZ,MAAOiJ,EAAOjJ,OAAS,IAAKc,OAAQ8I,qBAC5BX,EAAOnL,IACxB0T,aACAC,YAAc3N,GAAMiL,GAAgB9F,EAAOnL,IAAKgG,GAChD4N,WAAa5N,GAAMqL,GAAelG,EAAOnL,IAAKgG,GAC9C6N,OAAQ,IAAMnC,GAAWvG,EAAOnL,KAChC8T,UAAW7B,aAGVlB,IAAU/Q,MAAQmL,EAAOnL,KACxBkD,EAAAA,IACE,MAAA,CAAAL,UAAU,gDACVC,MAAO,CACLsB,KAA6B,WAAvB2M,IAAUU,SAAwB,OAAIpO,EAC5C2M,MAA8B,UAAvBe,IAAUU,SAAuB,OAAIpO,KAIlDH,MAAK,MAAA,CAAAL,UAAU,cAAaI,SAE1BN,EAAAA,KAAK,MAAA,CAAAE,UAAU,uDAAsDI,SAAA,CACnEN,EACEC,KAAA,MAAA,CAAAC,UAAW,oCAAoCyQ,KAC7CE,EAAc,qCAAuC,KAEvDnJ,QAAS,IAAMmJ,GAAerH,EAAOhB,EAAOnL,eAE5C2C,OAAK,MAAA,CAAAE,UAAU,4CAEZwN,GAAgBlF,EAAOnL,MACtBkD,EAAAqB,IAAA,MAAA,CACE1B,UAAU,2CACVwB,MAAM,mBAGVnB,EAAAA,IAAM,OAAA,CAAAL,UAAU,iFACbsI,EAAO7G,YAGXkP,GAAezH,EAAW/L,MAAQmL,EAAOnL,KACxCkD,EAAAqB,IAAA,MAAA,CAAK1B,UAAU,gDACZI,SAAyB,QAAzB8I,EAAWgI,UACV7Q,EAAAA,IAAC8Q,EAAAA,QAAQ,CAAAnR,UAAU,6CAEnBK,MAAC+Q,EAAAA,UAAS,CAACpR,UAAU,mDAO3BsI,EAAOE,YAAcmB,GAAeC,IACpC9J,EAAAA,KACE,MAAA,CAAAE,UAAW,aACTwK,IAAqBlC,EAAOnL,KAAOmO,EAC/B,QACA,4BACJ,oBAAA,EAAAlL,SAAA,CAGFC,gBACEL,UAAU,gEACVwH,QAAUrE,GAAMgJ,GAAsB7D,EAAOnL,IAAKgG,mCAGlD9C,EAAAA,IAACgR,EAAIA,MAACrR,UAAU,+CAIjBwK,IAAqBlC,EAAOnL,KAAOuN,GAClC5K,EAAAC,KAAA,MAAA,CACEC,UAAU,gIACVC,MAAO,CACLoN,IAAK,GAAG3C,EAAa2C,QACrB9L,KAAM,GAAGmJ,EAAanJ,SAEtB+P,UAAW,GAAGxS,KAAKG,IACjB,IACAyL,EAAaiC,cAAcW,OACzB,GACA5C,EAAa2C,UAMnB,oBAAA,EAAAjN,SAAA,CAAAN,EAAAA,KAAA,MAAA,CAAKE,UAAU,kEAAiEI,SAAA,CAC9EC,EACEqB,IAAA,SAAA,CAAA1B,UAAW,8CACK,SAAdkL,EACI,2CACA,iBAEN1D,QAAS,IAAM2D,EAAa,QAAO/K,SAEnCC,EAAAA,IAACgR,EAAIA,KAAA,CAACrR,UAAU,cAEjBsI,EAAOE,YACNnI,gBACEL,UAAW,8CACK,WAAdkL,EACI,2CACA,iBAEN1D,QAAS,IAAM2D,EAAa,UAE5B/K,SAAAC,MAACkR,EAAAA,OAAM,CAACvR,UAAU,cAGtBK,MAAA,SAAA,CACEL,UAAW,8CACK,YAAdkL,EACI,2CACA,iBAEN1D,QAAS,IAAM2D,EAAa,oBAE5B9K,EAACqB,IAAA8P,EAAWA,aAACxR,UAAU,iBAIZ,SAAdkL,GACCpL,cAAKE,UAAU,OAAMI,SAAA,CAClBuJ,GACCtJ,EACEqB,IAAA,SAAA,CAAA1B,UAAU,8EACVwH,QAAUrE,IACRA,EAAEkJ,kBACF5B,EAAoB,MACpB,MAAM2H,GAAc1I,EAAahK,IAC/B4I,EAAOnL,KAETwM,IAAcrB,EAAOnL,IAAKiV,IAC3BhS,SAEAsJ,EAAahK,IAAI4I,EAAOnL,KACvB2C,EAAAA,KAAAmQ,EAAAA,SAAA,CAAA7P,SAAA,CACEC,MAACqR,EAAAA,OAAO,CAAA1R,UAAU,YAEjB,kBAEHF,EAAAC,KAAAkQ,EAAA2B,SAAA,CAAAxR,SAAA,CACEC,EAAAA,IAACoR,EAAGA,IAAC,CAAAzR,UAAU,YAEd,kBAIRiK,GACC5J,EAAAqB,IAAA,SAAA,CACE1B,UAAU,sDACVwH,QAAS,KACPyC,IAAmB3B,EAAOnL,KAC1BsN,EAAoB,OAIfrK,SAAA,yBAEV8J,GACC7J,MAAA,SAAA,CACEL,UAAU,sDACVwH,QAAS,KACP0C,MACAO,EAAoB,yCAOzBb,GACCvJ,EAAAA,IACE,SAAA,CAAAL,UAAU,8EACVwH,QAAUrE,IACRA,EAAEkJ,kBACF5B,EAAoB,MACpB,MAAMkH,EACJ5H,GAAkBpM,SAChB2K,EAAOnL,MACJ0M,IAAoBvB,EAAOnL,IAC9B6M,EACFA,EAAc1B,EAAOnL,KAAMwU,GAE3B7H,IACE6H,EAAY,KAAOrJ,EAAOnL,MAI/BiD,SAAAyJ,IAAoBvB,EAAOnL,KAC5B4M,GAAkBpM,SAAS2K,EAAOnL,KAChC2C,EAAAA,KAAAmQ,EAAA2B,SAAA,CAAAxR,SAAA,CACEC,EAAAA,IAACwR,EAAAA,QAAO,CAAC7R,UAAU,yBAIrBF,OAAAmQ,EAAAA,SAAA,CAAA7P,SAAA,CACEC,MAACyR,EAAAA,MAAK,CAAC9R,UAAU,wBACPsI,EAAO7G,YAMxB0I,GACC9J,EACEqB,IAAA,SAAA,CAAA1B,UAAU,sDACVwH,QAAS,KACP2C,MACAM,EAAoB,qCAQf,WAAdS,GACCpL,EAAAC,KAAA,MAAA,CAAKC,UAAU,uBACbK,EAAKqB,IAAA,MAAA,CAAA1B,UAAU,8BAA6BI,SAC1CC,EAAAA,IAACgI,GACCC,OAAQA,EACRrM,KAAMA,EACN0G,MAAOyI,EACPxI,SAAW8D,IACT2E,EAAc3E,GAES,WAAnB4D,GACFf,EAAejB,EAAOnL,IAAKuJ,QAKnC5G,EAAKC,KAAA,MAAA,CAAAC,UAAU,8BAA6BI,SAAA,CACzCoN,GAAgBlF,EAAOnL,MACtB2C,OAAA,SAAA,CACEE,UAAU,+GACVwH,QAAS,KACP6D,EAAc,MACd9B,EAAejB,EAAOnL,IAAK,MAC3BsN,EAAoB,iBAGtBpK,EAACqB,IAAAqQ,EAAOA,QAAC,CAAA/R,UAAU,YAEZ,mBAEXK,EAAAA,IACE,SAAA,CAAAL,UAAU,+FACVwH,QAAS,IAAMiD,EAAoB,yBAIjB,WAAnBH,GACCjK,EAAAqB,IAAA,SAAA,CACE1B,UAAU,0EACVwH,QAAS,IACP+F,GAAkBjF,EAAOnL,KAIpBiD,SAAA,gBAKF,YAAd8K,GACCpL,EAAAA,KAAA,MAAA,CAAKE,UAAU,YACbI,SAAA,CAAAC,EAAAA,IAAA,MAAA,CAAKL,UAAU,yCAETI,SAAA,oBACNC,aAAKL,UAAU,2DACZI,SACiB,IACXgK,EACAnN,IAAKgS,GACJ1E,EAAW1K,KAAMJ,GAAMA,EAAEtC,MAAQ8R,IAElCtS,OAAOqV,YACPzH,EAAW5N,OACX8C,IAAO2K,EAAYzM,SAAS8B,EAAEtC,OAIpBF,IAAKwC,IAClB,MAAMwS,EAAevI,EAAahK,IAChCD,EAAEtC,KAEJ,OACE2C,EAAAA,YAEEE,UAAW,mHACT6L,IAA2BpM,EAAEtC,IACzB,aACA,MAEJ8U,EAAe,gBAAkB,KAEnCpB,WACA,EAAAC,YAAc3N,GACZkM,GAA2B5P,EAAEtC,IAAKgG,GAEpC4N,WAAa5N,GACXmM,GAA0B7P,EAAEtC,IAAKgG,GAEnC6N,OAAQ,IACNxB,GAAsB/P,EAAEtC,KAE1B8T,UAAWxB,GAEVrP,SAAA,CAAA2L,GAAqB5O,MACpBsC,EAAEtC,KACFkD,MAAA,MAAA,CACEL,UAAU,4CACVC,MAAO,CACLoN,IAEE,WADAtB,GAAqB6C,SAEjB,OACApO,EACN8M,OAEE,UADAvB,GAAqB6C,SAEjB,OACApO,KAIZV,EAAAA,KAAA,MAAA,CAAKE,UAAU,0BACbI,SAAA,CAAAC,EAAAqB,IAAA,OAAA,CAAM1B,UAAU,4BAETI,SAAA,OACPN,eAAME,UAAU,kCACbI,SAAA,CAAAX,EAAEgC,OACFwQ,GACC5R,EAAAA,IAAM,OAAA,CAAAL,UAAU,iCAAgCI,SAAA,qBA7CjDX,EAAEtC,SAwDjBkD,MAAA,MAAA,CAAKL,UAAU,iCAGTI,SAAA,8EAWrBsH,EAAQoB,EAAgBhM,OAAS,GAAK2M,GACrCpJ,EAAAA,IAAA,MAAA,CACEL,UAAU,gGACVkS,YAAc/O,GAAMsK,GAAkBnF,EAAOnL,IAAKgG,GAClDgP,cAAgBhP,IACdA,EAAEuK,iBACFvK,EAAEkJ,kBACFpC,IAAmB3B,EAAOnL,MAE5BqE,MAAM,qDAENnB,EAAAA,IAAK,MAAA,CAAAL,UAAU,8CArXdsI,EAAOnL,IAAMuK,KA6XvBsB,EAAa,GACZ3I,EAAAqB,IAAA,MAAA,CACEzB,MAAO,CAAEZ,MAAO2J,EAAY7I,OAAQ8I,GACpCjJ,UAAU,sBCzxBXqS,EAAWtW,EAAAA,KA/YE,EACxBE,OACAD,UACAoN,eACAkJ,cACAC,mBACApW,YACAD,aACAsW,kBACAC,cACArW,gBAAgB,IAAIC,IACpB0M,YAAY,EACZC,aAAa,EACb0J,WACAC,eACAC,gBACAC,mBACAC,aACAC,cACAC,gBACAC,iBACAC,oBACAC,kBACAC,mBACA9W,eAGA,MAkBM+W,EAAc7W,EAAAA,QAClB,IAAMP,EAAKa,OAASX,EACpB,CAACF,EAAKa,OAAQX,KAIVmD,sBAAEA,GAA0B9C,EAAAA,QAAQ,KAExC,GAA2B,IAAvBJ,EAAcqG,KAChB,MAAO,CAAEnD,sBAAuB,IAAIC,KAGtC,MAAMC,EAAoBxD,EACvBW,OAAQ8C,GAAMrD,EAAcsD,IAAID,EAAEtC,MAClCF,IAAKwC,GAAMA,EAAEtC,KAGVmW,EAAa,IAAI/T,IACvB,IAAK,MAAM3C,KAAOZ,EAChBsX,EAAW1T,IAAIhD,EAAIO,IAAKP,EAAIyC,OAAS,KAGvC,MAAMC,EAAwB,IAAIC,IAClC,IAAII,EAAMzD,EAAaL,EAAmB,EAE1C,IAAK,MAAMsB,KAAOqC,EAChBF,EAAsBM,IAAIzC,EAAKwC,GAC/BA,GAAO2T,EAAWtS,IAAI7D,IAAQ,IAGhC,MAAO,CAAEmC,0BACR,CAACtD,EAASI,EAAeF,IAGtBqX,EAAc/W,UAClB,IAAMR,EAAQ2C,OAAO,CAACN,EAAGoB,IAAMpB,GAAKjC,EAAcsD,IAAID,EAAEtC,KAAO,EAAI,GAAI,GACvE,CAACnB,EAASI,IAyBZ,OACEiE,EACEqB,IAAA,MAAA,CAAAzB,MAAO,CACLE,OAAQmS,EAAc,GAAGe,MAAkB,OAC3CzE,SAAU,YAGZxO,SAAAC,EAAAA,IAAA,MAAA,CACEJ,MAAO,CACLuT,UAAWlB,EACP,kBAAkBC,EAAiBkB,gBACnC,OACJ7E,SAAU0D,EAAc,WAAa,SACrCjF,IAAK,EACL9L,KAAM,EACN4L,MAAO,GAGR/M,SAAAnE,EAAKgB,IAAI,CAACC,EAAKwK,KACd,MAAMgM,EAAcpB,EAChBC,EAAiBoB,WAAajM,EAC9BA,EACEkM,EAAkBlB,IAAWxV,GAC7B2W,EAtGO,EAAC3W,EAAc4W,IAC9BxX,EACKA,EAASY,QAEHsD,IAAXtD,EAAIgF,GACChF,EAAIgF,IAGgB,gBAAzB6R,QAAQC,IAAIC,UAA2C,IAAbH,GAC5CxW,QAAQC,KACN,mPAMGuW,GAAY,GAsFCI,CAAahX,EAAKwW,GAC1BS,EACJT,EAAc,GAAM,EAChB,4BACA,8BACAU,EAAmBhL,EAAa1J,IAAImU,GACtC,iCACA,GAEJ,OACE/T,cAEEE,UAAW,8FACToU,IAAqBR,EAAkB,GAAKO,KAE9ClU,MAAO,CACLE,OAAQ,GAAGhE,SACRyX,GAELhB,cAAgBzP,GAhEF,EAACA,EAAejG,KACxCiG,EAAEuK,iBACFkF,IAAgB1V,EAAKiG,IA8DWkR,CAAkBlR,EAAGjG,GAC3CiV,cAAgBhP,GA5DF,EAACA,EAAejG,KACxC2V,IAAmB3V,EAAKiG,IA2DQmR,CAAkBnR,EAAGjG,GAC3CsK,QAAUrE,GAzDC,EAACA,EAAejG,KACrC4V,IAAa5V,EAAKiG,IAwDQoR,CAAepR,EAAGjG,GAEjCkD,SAAA,CAAAlE,GACCmE,EAAAA,IACE,MAAA,CAAAL,UAAU,6HACVC,MAAO,CAAEZ,MAAOxD,GAAkBuE,SAElCC,EAAAA,IACE,QAAA,CAAAkD,KAAK,WACL4M,QAAS/G,EAAa1J,IAAImU,GAC1BW,WAAUhC,IAAmBA,EAAgBtV,GAC7C0F,SAAWO,GAAMsP,EAAYoB,EAAO1Q,EAAEM,OAAO0M,SAC7CI,UAAYpN,IACI,MAAVA,EAAEhG,KAAyB,UAAVgG,EAAEhG,MACrBgG,EAAEuK,iBACF+E,EAAYoB,GAAQzK,EAAa1J,IAAImU,MAGzC7T,UAAU,iKAAgK,aAC9J,cAAc6T,IAC1BY,SAAU,MAKfzY,EAAQiB,IAAI,CAACqL,EAAQoM,KACpB,MAAM5T,EAAW1E,EAAcsD,IAAI4I,EAAOnL,KACpC4D,EAAaD,GACfxB,EAAsB0B,IAAIsH,EAAOnL,MACjC,EAGEwX,EAAUzX,EAAIoL,EAAOnL,KAC3B,IAAIyX,EACAnU,EAwBJ,GArBI6H,EAAOuM,cACTD,EAAgBtM,EAAOuM,aAAaF,EAASzX,GAGxB,iBAAZyX,GACY,iBAAZA,GACY,kBAAZA,IAEPlU,EAAerC,OAAOuW,KAEfrM,EAAO5H,WAChBkU,EAAgBtM,EAAO5H,UAAUiU,GACjClU,EAAerC,OAAOwW,KAEtBA,EAAgBD,GAAW,GAC3BlU,EAAemU,EACXxW,OAAOwW,QACPpU,GAKJmS,GACAA,EAAarP,SACZgF,EAAOuM,cACRD,EACA,CACA,MAAM9R,EAAY1E,OAAOwW,GC7MR,EACjCjS,EACAmS,KAEA,IAAKA,GAAwB,MAATnS,EAClB,OAAO,EAGT,MAAMoS,EAAQD,EAAYzN,cAAc/D,OACxC,QAAKyR,GAIQ3W,OAAOuE,GAAO0E,cACf1J,SAASoX,IDgMDC,CAAoBlS,EAAW6P,KACjCiC,ECrQS,EAC3BK,EACAH,KAEA,IAAKA,IAAgBG,EACnB,OAAOA,EAGT,MAAMF,EAAQD,EAAYzN,cAAc/D,OAClC4R,EAAYD,EAAK5N,cAEvB,IAAK0N,IAAUG,EAAUvX,SAASoX,GAChC,OAAOE,EAGT,MAAME,EAA2B,GACjC,IAAIC,EAAY,EACZC,EAAc,EACdC,EAAa,EAEjB,MAAgE,KAAxDD,EAAcH,EAAUhG,QAAQ6F,EAAOK,KAEzCC,EAAcD,GAChBD,EAAMI,KAAKN,EAAKvM,MAAM0M,EAAWC,IAInCF,EAAMI,KACJlV,MAAA,OAAA,CAEEL,UAAU,sFACVC,MAAO,CAAEuV,OAAQ,GAEhBpV,SAAA6U,EAAKvM,MAAM2M,EAAaA,EAAcN,EAAMjY,SAJxC,aAAawY,MAQtBF,EAAYC,EAAcN,EAAMjY,OAQlC,OAJIsY,EAAYH,EAAKnY,QACnBqY,EAAMI,KAAKN,EAAKvM,MAAM0M,IAGjB/U,EAAAqB,IAAAuO,EAAA2B,SAAA,CAAAxR,SAAG+U,KDwNwBM,CAAc3S,EAAW6P,GAE5C,CAGGlS,GAA+C,IAA/BA,EAAa6C,OAAOxG,SACtC2D,OAAeD,GAIjB,MAAMS,EACa,WAAjBqH,EAAOpH,MACH,cACiB,UAAjBoH,EAAOpH,MACL,aACA,YACFC,EACa,WAAjBmH,EAAOpH,MACH,iBACiB,UAAjBoH,EAAOpH,MACL,cACA,gBACFE,EAAekH,EAAOjH,UAAY,MAAQ,MAG1CqU,EACwB,mBAArBpN,EAAOtI,UACVsI,EAAOtI,UAAU2U,EAASzX,GAC1BoL,EAAOtI,WAAa,GACpB2V,EACwB,mBAArBrN,EAAOsN,UACVtN,EAAOsN,UAAUjB,EAASzX,GAC1BoL,EAAOsN,gBAAapV,EAEpBqV,EACJ/V,EAEEC,KAAA,MAAA,CAAAC,UAAW,4CAA4CoB,+BACrD6R,IAAiBY,EAAOvL,EAAOnL,KAC3B,qCACA,MAEJ2D,EACIkS,IAAgBa,EAAOvL,EAAOnL,KAC5B,wBACA,wBACF,MAEJ6V,IAAgBa,EAAOvL,EAAOnL,KAAO,WAAa,MAChD8D,KAAcE,KAAgBuU,IAClCzV,MAAO,CACLZ,MAAO,GAAGiJ,EAAOjJ,OAAS,QAC1BkC,KAAMT,EAAW,GAAGC,MAAiB,UAClC4U,GAGLnU,MAAOf,EACP+G,QAAUrE,GAhLJ,EACtBA,EACAjG,EACAoL,EACA3F,KAEAoQ,IAAc,CAAE7V,MAAKoL,SAAQ3F,QAAOyJ,MAAOjJ,KA2KzB2S,CAAgB3S,EAAGjG,EAAKoL,EAAQpL,EAAIoL,EAAOnL,MAE7CyV,cAAgBzP,IAGXjG,EAAY6Y,gBACZ7Y,EAAY8Y,gBAGX9C,IACF/P,EAAEuK,iBACFvK,EAAEkJ,kBACF6G,EAAkB,CAChBhW,MACAoL,SACA3F,MAAQzF,EAAYoL,EAAOnL,KAC3BsD,aAAcA,GAAgB,GAC9B2L,MAAOjJ,MAKb+O,YAAc/O,GACZgQ,IAAkB,CAAEjW,MAAKoL,SAAQ8D,MAAOjJ,IAE1C8S,aAAe9S,GACbiQ,IAAmB,CAAElW,MAAKoL,SAAQ8D,MAAOjJ,IAG1C/C,SAAA,CAAA4S,IAAgBa,EAAOvL,EAAOnL,MAC7BkD,EAAAA,WAAKL,UAAU,iEAEW,IAA3BsI,EAAO4N,gBACN7V,EAAAA,IAAA4P,EAAA2B,SAAA,CAAAxR,SAAGwU,IAEHvU,EAAAqB,IAAA,MAAA,CAAK1B,UAAU,2BAAmB4U,MAzD/B,GAAGtM,EAAOnL,OAAOuX,KA8D1B,OAAI3L,EAAY,GAAK2L,IAAanB,EAE9BzT,EAAAA,KACEmQ,EAAAA,SAAA,CAAA7P,SAAA,CAAAC,EAAAqB,IAAA,MAAA,CAAKzB,MAAO,CAAEZ,MAAO0J,GAAa/I,UAAU,cAC3C6V,KAKAA,IAGR7M,EAAa,GACZ3I,EAAAqB,IAAA,MAAA,CAAKzB,MAAO,CAAEZ,MAAO2J,GAAchJ,UAAU,gBA3L1C,GAAG6T,KAASH,YAqMd,CAAC/R,EAAqBC,KACrC,GAAID,EAAKxF,YAAcyF,EAAKzF,UAAW,OAAO,EAC9C,GAAIwF,EAAKzF,aAAe0F,EAAK1F,WAAY,OAAO,EAChD,GAAIyF,EAAK2Q,cAAgB1Q,EAAK0Q,YAAa,OAAO,EAClD,GAAI3Q,EAAKgR,eAAiB/Q,EAAK+Q,aAAc,OAAO,EACpD,GAAIhR,EAAK4Q,iBAAiBoB,aAAe/R,EAAK2Q,iBAAiBoB,WAC7D,OAAO,EACT,GAAIhS,EAAK4Q,iBAAiB4D,WAAavU,EAAK2Q,iBAAiB4D,SAC3D,OAAO,EACT,GAAIxU,EAAK4Q,iBAAiBkB,UAAY7R,EAAK2Q,iBAAiBkB,QAC1D,OAAO,EAET,GAAI9R,EAAKsR,iBAAmBrR,EAAKqR,eAAgB,OAAO,EACxD,GAAItR,EAAKqR,gBAAkBpR,EAAKoR,cAAe,OAAO,EAEtD,GAAIrR,EAAKwR,kBAAoBvR,EAAKuR,gBAAiB,OAAO,EAC1D,GAAIxR,EAAKyR,mBAAqBxR,EAAKwR,iBAAkB,OAAO,EAC5D,GAAIzR,EAAKrF,WAAasF,EAAKtF,SAAU,OAAO,EAC5C,GAAIqF,EAAKyH,aAAa3G,OAASb,EAAKwH,aAAa3G,KAAM,OAAO,EAC9D,GAAId,EAAKvF,eAAeqG,OAASb,EAAKxF,eAAeqG,KAAM,OAAO,EAGlE,GAFiBd,EAAK3F,QAAQiB,IAAKwC,GAAMA,EAAEtC,KAAKiZ,KAAK,OACpCxU,EAAK5F,QAAQiB,IAAKwC,GAAMA,EAAEtC,KAAKiZ,KAAK,KAC1B,OAAO,EAClC,GAAIzU,EAAK1F,KAAKa,SAAW8E,EAAK3F,KAAKa,OAAQ,OAAO,EAGlD,MAAMkF,EAAY,CAChB9E,EACA+E,EACA3F,IAEIA,EACKA,EAASY,QAEHsD,IAAXtD,EAAIgF,GACChF,EAAIgF,GAEND,EAGHE,EACJR,EAAK1F,KAAKa,OAAS,EACfkF,EAAUL,EAAK1F,KAAK,GAAI,EAAG0F,EAAKrF,eAChCkE,EACA4B,EACJR,EAAK3F,KAAKa,OAAS,EACfkF,EAAUJ,EAAK3F,KAAK,GAAI,EAAG2F,EAAKtF,eAChCkE,EACA6B,EAAcV,EAAK1F,KAAKa,OAAS,EACjCwF,EAAcV,EAAK3F,KAAKa,OAAS,EACjCyF,EACJZ,EAAK1F,KAAKa,OAAS,EACfkF,EAAUL,EAAK1F,KAAKoG,GAAcA,EAAaV,EAAKrF,eACpDkE,EACAgC,EACJZ,EAAK3F,KAAKa,OAAS,EACfkF,EAAUJ,EAAK3F,KAAKqG,GAAcA,EAAaV,EAAKtF,eACpDkE,EACN,OAAI2B,IAAcC,GAAaG,IAAaC,IEnbjC6T,EAAW,EACtBra,UACAsa,cACAC,WACAC,YACAC,kBACAC,oBACAC,iBAAgB,MAEhB,MAAOC,EAASC,GAAc7T,EAAQA,SAAgB,OAC/C8T,EAAaC,GAAkB/T,EAAQA,SAAgB,OACvDgU,EAAgBC,GAAqBjU,EAAQA,UAAU,GAExDkU,EAAY/Z,GAAgBnB,EAAQ6D,KAAMJ,GAAMA,EAAEtC,MAAQA,IAAMsE,QAAUtE,EAa1Ega,EAAuBha,GAAyDgG,IACpF0T,EAAW1Z,GACXgG,EAAEkL,aAAaC,cAAgB,OAC/BnL,EAAEkL,aAAaE,QAAQ,0BAA2BpR,IAG9Cia,EAAsBja,GAAyDgG,IACnFA,EAAEuK,iBACF,MAAMpB,EAAQnJ,EAAEoJ,cAAiCC,wBAC3CuC,EAAS5L,EAAEwK,QAAUrB,EAAK/K,KAAO+K,EAAKjN,MAAQ,EACpD0X,EAAe5Z,GACf8Z,EAAkBlI,IA2BpB,OACEjP,EAAAA,KACE,MAAA,CAAAE,UAAU,gFACVgR,OApDkE7N,IACpEA,EAAEuK,iBACF,MAAMvQ,EAAMgG,EAAEkL,aAAagJ,QAAQ,cAC9Bla,IACAmZ,EAAY3Y,SAASR,IAAMsZ,EAAgBtZ,KAiD9C4T,WA9C2D5N,IAC7DA,EAAEuK,kBA8CAuD,UAVa,KACf4F,EAAW,MACXE,EAAe,OAQM3W,SAAA,CAEnBC,EAAMqB,IAAA,OAAA,CAAA1B,UAAU,kCAAkDI,SAAA,cACjEkW,EAAYxZ,OAAS,GACpBuD,MACE,SAAA,CAAAkD,KAAK,SACLvD,UAAU,0EACVwB,MAAOmV,EAAgB,sBAAwB,oBAC/CnP,QAAUrE,IACRA,EAAEuK,iBACFvK,EAAEkJ,kBACFqK,gBAGDC,EAAgB,eAAiB,eAGd,IAAvBL,EAAYxZ,QACXuD,EAAAA,YAAML,UAAU,gBAAeI,SAAA,uBAEjCC,EAAAqB,IAAA,MAAA,CAAK1B,UAAU,oCACZI,SAAAkW,EAAYrZ,IAAKE,IAAG,OACnB2C,OAEE,MAAA,CAAAE,UAAU,4HACV6Q,WAAS,EACTC,YAAaqG,EAAoBha,GACjC4T,WAAYqG,EAAmBja,GAC/B6T,QAzDcvC,EAyDStR,EAzDqD,KACpF,IAAKyZ,GAAWA,IAAYnI,EAG1B,OAFAoI,EAAW,WACXE,EAAe,MAGjB,MAAMO,EAAQhB,EAAY3Z,OAAQsS,GAAMA,IAAM2H,GACxC3U,EAAMqV,EAAMpI,QAAQT,IACb,IAATxM,IACC+U,EAGHM,EAAMnI,OAAOlN,EAAK,EAAG2U,GAFrBU,EAAMnI,OAAOlN,EAAM,EAAG,EAAG2U,GAI3BJ,EAAUc,GACVT,EAAW,MACXE,EAAe,SA0CPvV,MAAO0V,EAAS/Z,GAAIiD,SAAA,CAEpBC,cAAML,UAAU,6CAA4CI,SAAE8W,EAAS/Z,KACvEkD,EAAAA,IACE,SAAA,CAAAL,UAAU,oCACVwH,QAAS,IAAM+O,EAASpZ,gBACZ,mBAAmB+Z,EAAS/Z,KAAMiD,SAAA,MAI/C0W,IAAgB3Z,GACfkD,EAAAA,IACE,OAAA,CAAAL,UAAU,2EACVC,MAAO,CAAEsB,KAAMyV,EAAiB,OAAIxW,EAAW2M,MAAO6J,OAAiBxW,EAAY,iBACvE,WApBXrD,GApDQ,IAACsR,UCrBb8I,EAAcxb,EAAAA,KACzB,EACEmB,MACAsa,aACAC,WACAC,gBACAxb,aACAC,YAAY,GACZwb,iBACAC,gBACAC,yBAEA,MAAMC,EAAe,KACf5a,EAAI6a,WACNN,EAASva,EAAI6a,YAIjB,OACEjY,EACEC,KAAA,MAAA,CAAAC,UAAU,wKACVC,MAAO,CACLC,SAAU,GAAGwX,GAAiBxb,EAAaL,EAAmB,OAC9DsE,OAAQ,GAAGhE,OAEbqL,QAASsQ,EACT5H,KAAK,SACLuE,SAAU,EAAC,gBACI+C,EACfjH,UAAYpN,IACI,UAAVA,EAAEhG,KAA6B,MAAVgG,EAAEhG,MACzBgG,EAAEuK,iBACFoK,MAEH1X,SAAA,CAEAlE,GACCmE,aAAKL,UAAU,0CAKjBF,cAAKE,UAAU,qCACbI,SAAA,CAAAC,EAAAA,IAAA,MAAA,CACEL,UAAU,yBACVC,MAAO,CACL+X,aAAiB9a,EAAY+a,YAAqD,GAArC/a,EAAY+a,YAA8B,GAA1E,MACd7X,SAEAoX,EACCnX,MAAC6X,EAAWA,YAAA,CAAClY,UAAU,0BAEvBK,EAACqB,IAAAyW,EAAYA,aAAC,CAAAnY,UAAU,4BAI5BF,EAAAA,YAAKE,UAAU,0BAAyBI,SAAA,CACtCN,EAAAA,aAAME,UAAU,4BACZI,SAAA,CAAAlD,EAAYkb,gBACV,GAAGT,EAAiBA,EAAgBza,EAAYkb,iBAAoBlb,EAAYkb,oBAChF,GACHR,GAAkB1a,EAAYkb,gBAC3BR,EACG1a,EAAYkb,gBACZlb,EAAYmb,aAEbnb,EAAYmb,cAAgBnb,EAAI6a,WAAa,cAErDjY,EAAMC,KAAA,OAAA,CAAAC,UAAU,8DACbI,SAAA,CAAAlD,EAAIob,YAAc,IAAoB,IAApBpb,EAAIob,YAAoB,OAAS,cAIvDT,GACCxX,EAAAA,IAAA,MAAA,CACEL,UAAU,0BACVwH,QAAUrE,GAAMA,EAAEkJ,kBAAiBjM,SAElCyX,EAAmB,CAClBU,SAAUrb,EAAI6a,WAAa,GAC3B7R,UAAYhJ,EAAYkb,iBAAmB,GAC3CI,WAAatb,EAAYmb,YACzBI,KAAOvb,EAAYwb,YAAc,GACjCC,MAAOzb,EAAIob,aAAe,EAC1BM,MAAQ1b,EAAY+a,aAAe,cC5GtCY,EAAgB,EAC3BC,cAAa,EACbC,WAAU,EACVC,cAEA,MAiCMxX,MAAEA,EAAKyX,YAAEA,EAAaC,KAAMC,GAhC5BH,EACK,CACLxX,MAAOwX,EACPC,YAAa,GACbC,KAAME,EAAQA,UAIbL,EAQDD,EACK,CACLtX,MAAO,mBACPyX,YACE,4EACFC,KAAM3R,EAAMA,QAIT,CACL/F,MAAO,qBACPyX,YAAa,4CACbC,KAAME,EAAQA,UAnBP,CACL5X,MAAO,oBACPyX,YAAa,6CACbC,KAAME,EAAQA,UAsBpB,OACEtZ,EAAAA,KAAK,MAAA,CAAAE,UAAU,mFACbK,MAAK,MAAA,CAAAL,UAAU,qGACbK,EAAAA,IAAC8Y,EAAK,CAAAnZ,UAAU,+CAElBK,EAAAA,UAAIL,UAAU,4DAA2DI,SACtEoB,IAEHnB,EAAGqB,IAAA,IAAA,CAAA1B,UAAU,oDAAmDI,SAC7D6Y,QChCII,EAAqB,EAChCC,mBACAC,cACAC,kBACAC,oBACAC,kBACAC,qBACArP,iBAAiB,SACjBsP,eACAC,uBAGA,MA2BMC,MAAEA,EAAK/T,IAAEA,EAAGgU,MAAEA,GAnBQ,MAC1B,MAAMC,EAAiBV,EAAiBW,SARjB,WAAnB3P,EASAgP,EAAiBjQ,WAAaqQ,EAN3BC,GAAsBD,EAoB7B,MAAO,CAAEI,MAXKR,EAAiBW,QAC3Bnb,KAAKC,KACFwa,EAAc,GAAKD,EAAiBY,SAAW,EAChDF,GAEF,EAMYjU,IAJJuT,EAAiBW,QACzBnb,KAAKC,IAAIwa,EAAcD,EAAiBY,SAAUF,GAClDA,EAEiBD,MAAOC,IAGAG,GACxBC,EACe,WAAnB9P,GACAqP,GACAA,IAAuBD,EAEzB,OACErZ,EAAKqB,IAAA,MAAA,CAAA1B,UAAU,uKAAsKI,SACnLN,EAAAA,YAAKE,UAAU,8EAEbI,SAAA,CAAAN,OAAA,MAAA,CAAKE,UAAU,iFAEbF,EAAKC,KAAA,MAAA,CAAAC,UAAU,6CAA4CI,SAAA,EAEvDkZ,EAAiBW,SACjBna,OAAK,MAAA,CAAAE,UAAU,wKAAuKI,SAAA,CACpLC,MAAC+Y,WAAQ,CAACpZ,UAAU,YACpBF,EAAAC,KAAA,OAAA,CAAMC,UAAU,YACbI,SAAA,CAAA2Z,EAAMM,qBAA6B,IAAVN,EAAc,SAAW,aAEpDK,GACCta,EAAAC,KAAA,OAAA,CAAMC,UAAU,4BACTI,SAAA,CAAA,OAAAsZ,EAAgBW,2BAO5BZ,EAAoB,GACnB3Z,EAAAA,KAAK,MAAA,CAAAE,UAAU,qKAAoKI,SAAA,CACjLC,MAACia,EAAAA,OAAMta,UAAU,YACjBF,yBAAO2Z,EAAkBY,iBAAgB,kBAK5CD,GACCta,cAAKE,UAAU,kKAAiKI,SAAA,CAC9KC,MAACka,EAAGA,IAAA,CAACva,UAAU,YACfK,EAAAqB,IAAA,OAAA,CAAAtB,SAAA,gBAKHkZ,EAAiBW,SAChB5Z,EAAAA,IAAA,MAAA,CAAKL,UAAU,uEACbI,SAAAN,EAAAA,KAAA,OAAA,CAAME,UAAU,2CACdK,MAAoB,OAAA,CAAAD,SAAA,YACpBC,cAAML,UAAU,iGACbI,SAAA0Z,EAAMO,mBAETha,MAAA,OAAA,CAAAD,SAAA,MACAC,MAAM,OAAA,CAAAL,UAAU,iGAAgGI,SAC7G2F,EAAIsU,mBAEPva,+BAAUia,EAAMM,4BAOvBf,EAAiBW,SAChBX,EAAiBkB,sBACf1a,EAAKC,KAAA,MAAA,CAAAC,UAAU,0BAAyBI,SAAA,CACtCC,EAAAA,YAAML,UAAU,yEAETI,SAAA,mBACPC,EAAAA,cACEsC,MAAO2W,EAAiBY,SACxBtX,SAAWO,GAAM0W,EAAiBjc,OAAOuF,EAAEM,OAAOd,QAClD3C,UAAU,8UACVwU,SAAUgF,EAAepZ,SAExBkZ,EAAiBmB,iBAAiBxd,IAAKwF,GACtCpC,gBAAmBsC,MAAOF,EACvBrC,SAAAqC,GADUA,YAUxB6W,EAAiBW,SAChBna,EAAKC,KAAA,MAAA,CAAAC,UAAU,uEAAsEI,SAAA,CAElFoZ,GACC1Z,EAAAA,KAAK,MAAA,CAAAE,UAAU,8HAA6HI,SAAA,CAC1IC,MAACqa,EAAOA,QAAA,CAAC1a,UAAU,sCACnBK,EAAAA,IAAA,OAAA,CAAML,UAAU,8DAOpBK,EAAKqB,IAAA,MAAA,CAAA1B,UAAU,0EACbF,EAAAA,KAAK,MAAA,CAAAE,UAAU,sBAAqBI,SAAA,CAClCN,eAAME,UAAU,iCACdI,SAAA,CAAAC,EAAAA,IAAA,OAAA,CAAAD,SAAA,SACAC,EAAMqB,IAAA,OAAA,CAAA1B,UAAU,iGAAgGI,SAC7GmZ,EAAYc,mBAEfha,EAAAA,IAAe,OAAA,CAAAD,SAAA,OACfC,EAAAA,YAAML,UAAU,iGACbI,SAAAkZ,EAAiBqB,WAAWN,sBAGjCva,EAAAA,KAAA,OAAA,CAAME,UAAU,qDACb8Z,EAAMO,iBAAgB,MAAKtU,EAAIsU,iBAAqB,MAAA,IACpDN,EAAMM,yBAMbva,EAAAA,YAAKE,UAAU,mCAAkCI,SAAA,CAE/CC,EAAAA,cACEmH,QAAS,IAAMoS,EAAa,EAAGN,EAAiBY,UAChD1F,SAA0B,IAAhB+E,GAAqBC,EAC/BxZ,UAAU,+cACVwB,MAAM,aACK,aAAA,4BAEXnB,EAAAA,IAACua,EAAAA,cAAa5a,UAAU,kBAI1BK,MACE,SAAA,CAAAmH,QAAS,IACPoS,EAAaL,EAAc,EAAGD,EAAiBY,UAEjD1F,SAA0B,IAAhB+E,GAAqBC,EAC/BxZ,UAAU,+cACVwB,MAAM,gBACK,aAAA,+BAEXnB,EAACqB,IAAAmZ,EAAWA,aAAC7a,UAAU,kBAIzBK,EAAKqB,IAAA,MAAA,CAAA1B,UAAU,YAAWI,SACxBC,EAAAA,IACE,SAAA,CAAAsC,MAAO4W,EACP3W,SAAWO,GACTyW,EACEhc,OAAOuF,EAAEM,OAAOd,OAChB2W,EAAiBY,UAGrBla,UAAU,8QACVwU,SAAUgF,EACC,aAAA,uBAEVzS,MAAMC,KACL,CAAElK,OAAQwc,EAAiBqB,YAC3B,CAACG,EAAGjZ,IAAMA,EAAI,GACd5E,IAAK8d,GACL1a,MAAmB,SAAA,CAAAsC,MAAOoY,EAAI3a,SAC3B2a,GADUA,QAQnB1a,EAAAA,WAAKL,UAAU,yCACZI,SAAA,MACC,MAAM4a,EAAQ,GACRL,EAAarB,EAAiBqB,WAC9BM,EAAU1B,EACV2B,EACc,oBAAX1P,QAA0BA,OAAOqB,YAAc,IAClD,EACA,EAEN,GAAI8N,GAAcO,EAChB,IAAK,IAAIrZ,EAAI,EAAGA,GAAK8Y,EAAY9Y,IAC/BmZ,EAAMzF,KAAK1T,OAER,CACL,MAAMsZ,EAAQrc,KAAKsc,MAAMF,EAAa,GAAK,EAC3C,GAAID,GAAWE,EAAQ,EAAG,CACxB,IAAK,IAAItZ,EAAI,EAAGA,GAAKqZ,EAAa,EAAGrZ,IAAKmZ,EAAMzF,KAAK1T,GACrDmZ,EAAMzF,KAAK,aACXyF,EAAMzF,KAAKoF,EACZ,MAAM,GAAIM,GAAWN,EAAaQ,EAAO,CACxCH,EAAMzF,KAAK,GACXyF,EAAMzF,KAAK,aACX,IACE,IAAI1T,EAAI8Y,GAAcO,EAAa,GACnCrZ,GAAK8Y,EACL9Y,IAEAmZ,EAAMzF,KAAK1T,EACd,KAAM,CACLmZ,EAAMzF,KAAK,GACXyF,EAAMzF,KAAK,aACX,IAAK,IAAI1T,EAAIoZ,EAAUE,EAAOtZ,GAAKoZ,EAAUE,EAAOtZ,IAClDmZ,EAAMzF,KAAK1T,GACbmZ,EAAMzF,KAAK,aACXyF,EAAMzF,KAAKoF,EACZ,CACF,CAED,OAAOK,EAAM/d,IAAK8d,IAChB,GAAoB,iBAATA,EACT,OACE1a,EAAAA,IAEE,MAAA,CAAAL,UAAU,sGACE,OAAMI,SAElBC,EAAAA,IAAM,OAAA,CAAAL,UAAU,uBAAsBI,SAAA,OAJjC2a,GASX,MAAMM,EAAWN,IAASE,EAC1B,OACE5a,gBAEEmH,QAAS,IACPoS,EAAamB,EAAMzB,EAAiBY,UAEtC1F,SAAUgF,EACVxZ,UAAW,wLACTqb,EACI,uHACA,mQACJ,aACU,cAAcN,IACZ,eAAAM,EAAW,YAAS7a,EAASJ,SAE1C2a,GAbIA,IAiBZ,EAvEA,KA2EH1a,gBACEmH,QAAS,IACPoS,EAAaL,EAAc,EAAGD,EAAiBY,UAEjD1F,SACE+E,IAAgBD,EAAiBqB,YAAcnB,EAEjDxZ,UAAU,+cACVwB,MAAM,YACK,aAAA,2BAEXnB,EAAAA,IAAC8X,EAAAA,cAAanY,UAAU,kBAI1BK,gBACEmH,QAAS,IACPoS,EACEN,EAAiBqB,WACjBrB,EAAiBY,UAGrB1F,SACE+E,IAAgBD,EAAiBqB,YAAcnB,EAEjDxZ,UAAU,+cACVwB,MAAM,YAAW,aACN,kBAAiBpB,SAE5BC,EAAAA,IAACib,EAAAA,eAActb,UAAU,8BClU5Bub,EAAe,EAC1BtV,UACAtD,QACAE,cAAc,sBACdD,WACA4Y,cAAa,EACbxb,YAAY,OAEZ,MAAOyb,EAAQC,GAAa1Y,EAAQA,UAAC,IAC9B2Y,EAAYC,GAAiB5Y,EAAQA,SAAC,KACtC6Y,EAAkBC,GAAuB9Y,EAAAA,UAAU,GAEpD+Y,EAAeC,SAAuB,MACtCC,EAAWD,SAAyB,MACpCE,EAAUF,SAAyB,MAEnCG,EAAiBlW,EAAQpG,KAAMyH,GAAWA,EAAO3E,QAAUA,GAE3DwE,EAAkBqU,EACpBvV,EAAQtJ,OAAQ2K,GACdA,EAAO7C,OAAO4C,eAAe1J,SAASge,GAAYtU,gBAEpDpB,EAGJd,EAAAA,UAAU,KACR,MAAM6K,EAAsB5D,IAExB2P,EAAad,UACZc,EAAad,QAAQmB,SAAShQ,EAAM3I,UAErCiY,GAAU,GACVE,EAAc,IACdE,GAAqB,KAKzB,OADApM,SAASC,iBAAiB,YAAaK,GAChC,IAAMN,SAASK,oBAAoB,YAAaC,IACtD,IAGH7K,EAAAA,UAAU,KACR,MAAMkX,EAAiBjQ,IACrB,GAAKqP,EAEL,OAAQrP,EAAMjP,KACZ,IAAK,YACHiP,EAAMsB,iBACNoO,EAAqBna,GACnBA,EAAOwF,EAAgBrK,OAAS,EAAI6E,EAAO,EAAI,GAEjD,MACF,IAAK,UACHyK,EAAMsB,iBACNoO,EAAqBna,GACnBA,EAAO,EAAIA,EAAO,EAAIwF,EAAgBrK,OAAS,GAEjD,MACF,IAAK,QACHsP,EAAMsB,iBACFmO,GAAoB,GAAK1U,EAAgB0U,IAC3CS,EAAanV,EAAgB0U,GAAkBlZ,OAEjD,MACF,IAAK,SACH+Y,GAAU,GACVE,EAAc,IACdE,GAAqB,KAK3B,GAAIL,EAEF,OADA/L,SAASC,iBAAiB,UAAW0M,GAC9B,IAAM3M,SAASK,oBAAoB,UAAWsM,IAEtD,CAACZ,EAAQI,EAAkB1U,IAG9BhC,EAAAA,UAAU,KACR,GAAI0W,GAAoB,GAAKK,EAAQjB,QAAS,CAC5C,MAAMsB,EAAqBL,EAAQjB,QAAQ7a,SACzCyb,GAEEU,GACFA,EAAmBC,eAAe,CAChCC,MAAO,UACPC,SAAU,UAGf,GACA,CAACb,IAEJ,MAAMS,EAAgB1U,IACpBhF,EAASgF,GACT8T,GAAU,GACVE,EAAc,IACdE,GAAqB,IAgBvB,OACEhc,EAAAA,KAAK,MAAA,CAAAE,UAAW,YAAYA,IAAaoQ,IAAK2L,EAC5C3b,SAAA,CAAAC,MAAA,MAAA,CACEL,UAAW,0GACTyb,EACI,uCACA,qGAENjU,QArBmB,KACvBkU,GAAWD,GACPD,IAAeC,GACjBkB,WAAW,IAAMV,EAAShB,SAAS2B,QAAS,IAkBjBxc,SAEzBN,EAAKC,KAAA,MAAA,CAAAC,UAAU,gDAA+CI,SAAA,CAC3Dob,GAAcC,EACbpb,EAAAA,aACE+P,IAAK6L,EACL1Y,KAAK,OACLvD,UAAU,6CACV6C,YAAa,UAAUA,GAAawE,mBACpC1E,MAAOgZ,EACP/Y,SAxBeO,IACzByY,EAAczY,EAAEM,OAAOd,OACvBmZ,GAAqB,GAChBL,GAAQC,GAAU,IAsBblU,QAAUrE,GAAMA,EAAEkJ,oBAGpBhM,EACEqB,IAAA,OAAA,CAAA1B,UAAW,4BACTmc,EAAiB,gBAAkB,iBACnC/b,SAED+b,EAAiBA,EAAe1X,MAAQ5B,IAI7CxC,EAAAA,IAAC6X,EAAWA,YACV,CAAAlY,UAAW,4DACTyb,EAAS,aAAe,WAO/BA,GACCpb,EAAAA,IAAK,MAAA,CAAAL,UAAU,uIACZmH,EAAgBrK,OAAS,EACxBuD,YAAI+P,IAAK8L,EAASlc,UAAU,gCACzBI,SAAA+G,EAAgBlK,IAAI,CAACqK,EAAQI,IAC5B5H,EAEEC,KAAA,KAAA,CAAAC,UAAW,yFACT0H,IAAUmU,EACN,2BACA,kCAENrU,QAAS,IAAM8U,EAAahV,EAAO3E,OACnCsT,aAAc,IAAM6F,EAAoBpU,aAExCrH,MAAM,OAAA,CAAAL,UAAU,oBAAYsH,EAAO7C,QAClC0X,GAAgBxZ,QAAU2E,EAAO3E,OAChCtC,MAAC4H,EAAAA,MAAK,CAACjI,UAAU,0CAXdsH,EAAO3E,UAiBlBtC,EAAAA,IAAK,MAAA,CAAAL,UAAU,kFC9Kd6c,EAAuB,EAClC7gB,UACAmN,gBACAI,qBAGA,MAAMuT,EAAoB9gB,EAAQW,OAAQC,GAAQA,EAAI4L,aAE/CuU,EAAgBC,GAAqBha,EAAAA,SAC1C8Z,EAAkBhgB,OAAS,EAAIggB,EAAkB,GAAG3f,IAAM,KAGrD8f,EAAaC,GAAkBla,EAAQA,SAAC,IAWzCma,EAAgBL,EAAkB7f,IAAKL,IAAS,CACpD6H,MAAO7H,EAAI6E,OACXkB,MAAO/F,EAAIO,OAGPigB,EAAqBN,EAAkBjd,KAC1CjD,GAAQA,EAAIO,MAAQ4f,GAIvB5X,EAAAA,UAAU,KACR+X,EAAe,KACd,CAACH,IAqRJ,OACEjd,EAAAA,KAAK,MAAA,CAAAE,UAAU,YAAWI,SAAA,CAExBN,wBACEO,EAAOqB,IAAA,QAAA,CAAA1B,UAAU,oFAGjBK,EAAAqB,IAAC6Z,EACC,CAAAtV,QAASkX,EACTxa,MAAOoa,EACPla,YAAY,qBACZD,SAtToBsD,IAC1B8W,EAAkB9W,IAsTZsV,YAAY,OAKfuB,GApSqB,MACxB,IAAKK,EAAoB,OAAO,KAEhC,MAAMC,EAAaD,EAAmB5U,YAAYjF,MAAQ,OACpD+Z,EAAgBnU,EAAc4T,GAEpC,GACiB,gBAAfM,GACAD,EAAmB5U,YAAYvC,QAC/B,CACA,MAAMsX,EAAUN,EAAY5V,cACtBF,EAAkBoW,EACpBH,EAAmB5U,WAAWvC,QAAQtJ,OACnCkK,GACCA,EAAIpC,MAAM4C,cAAc1J,SAAS4f,IACjCnf,OAAOyI,EAAIlE,OAAO0E,cAAc1J,SAAS4f,IAE7CH,EAAmB5U,WAAWvC,QAClC,OACEnG,EAAAC,KAAA,MAAA,CAAKC,UAAU,OAAMI,SAAA,CACnBN,EAAOC,KAAA,QAAA,CAAAC,UAAU,+CACJI,SAAA,CAAA,aAAAgd,EAAmB3b,UAEhC3B,EAAAA,KAAK,MAAA,CAAAE,UAAU,gBACbI,SAAA,CAAAC,MAACkH,EAAAA,OAAM,CAACvH,UAAU,mEAClBK,EAAAA,IAAA,QAAA,CACEkD,KAAK,OACLZ,MAAOsa,EACPra,SAAWO,GAAM+Z,EAAe/Z,EAAEM,OAAOd,OACzCE,YAAY,oBACZ7C,UAAU,6HAEXid,GACC5c,EAAAqB,IAAA,SAAA,CACE6B,KAAK,SACLiE,QAAS,IAAM0V,EAAe,IAC9Bld,UAAU,8EACVwB,MAAM,eAAcpB,SAEpBC,EAAAA,IAACmd,EAACA,EAAC,CAAAxd,UAAU,iBAInBF,EAAAA,KAAA,MAAA,CAAKE,UAAU,0EAAyEI,SAAA,CAC1D,IAA3B+G,EAAgBrK,QACfuD,EAAAA,IAAA,MAAA,CAAKL,UAAU,kCAAiCI,SAAA,eAEjD+G,EAAgBlK,IAAKqK,IACpB,MAAMjB,EAAiBiX,GAAe3a,OAAS,GACzCgF,EACJZ,MAAM0W,QAAQpX,IACdA,EAAe1I,SAAS2J,EAAO3E,OACjC,OACE7C,EAAAA,KAEE,QAAA,CAAAE,UAAU,yEAEVK,EAAAA,IACE,QAAA,CAAAkD,KAAK,WACLvD,UAAU,4DACVmQ,QAASxI,EACT/E,SAAWO,IACT,MAAMua,EAAgBJ,GAAe3a,OAAS,GAC9C,IAAIgb,EAEFA,EADExa,EAAEM,OAAO0M,QACC,IAAIuN,EAAepW,EAAO3E,OAE1B+a,EAAc/gB,OACvBsB,GAAWA,IAAMqJ,EAAO3E,OAGzBgb,EAAU7gB,OAAS,EACrByM,IAAiBwT,EAAgB,CAC/BxZ,KAAM,cACNZ,MAAOgb,EACP1a,SAAU,OAGZsG,IAAiBwT,EAAgB,SAIvC1c,EAAAqB,IAAA,OAAA,CAAM1B,UAAU,UAAWI,SAAAkH,EAAO7C,UA5B7B6C,EAAO3E,cAmCzB,CAED,GAAmB,WAAf0a,GAA2BD,EAAmB5U,YAAYvC,QAAS,CACrE,MAAM2X,EAAgB,CACpB,CAAEnZ,MAAO,OAAO2Y,EAAmB3b,SAAUkB,MAAO,OACjDya,EAAmB5U,WAAWvC,QAAQhJ,IAAK4J,IAAS,CACrDpC,MAAOoC,EAAIpC,MACX9B,MAAOvE,OAAOyI,EAAIlE,WAItB,OACE7C,EAAAA,YAAKE,UAAU,OACbI,SAAA,CAAAN,OAAA,QAAA,CAAOE,UAAU,sEACJod,EAAmB3b,UAEhCpB,EAAAA,IAACkb,EAAY,CACXtV,QAAS2X,EACTjb,MAAO2a,GAAe3a,OAAS,GAC/BE,YAAa,UAAUua,EAAmB3b,OAAO4F,mBACjDzE,SAAWD,IAEP4G,IAAiBwT,EADfpa,EAC+B,CAC/BY,KAAM,SACNZ,MAAOA,EACPM,SAAU,WAGqB,OAGrCuY,YAAY,MAInB,CAED,GAAmB,SAAf6B,EAAuB,CACzB,MAAMQ,EAAcnX,IAClB,IAAKA,EAAK,OAAO,KACjB,GAAIA,aAAe5I,OAASwG,MAAMoC,EAAIjI,WAAY,OAAOiI,EAEzD,MAAMoX,EAAS/f,EAAKA,MAACK,OAAOsI,GAAM,aAAc,IAAI5I,MACpD,IAAKwG,MAAMwZ,EAAOrf,WAAY,OAAOqf,EACrC,MAAMC,EAAS,IAAIjgB,KAAK4I,GACxB,OAAOpC,MAAMyZ,EAAOtf,WAAa,KAAOsf,GAGpCC,EAAcH,EAAWP,GAAe3a,OACxCsb,EAAaJ,EAAWP,GAAevZ,aACvCma,EAAoBZ,GAAera,UAAY,UAErD,OACEnD,OAAK,MAAA,CAAAE,UAAU,iBACbF,EAAAA,KAAO,QAAA,CAAAE,UAAU,+CACJI,SAAA,CAAA,aAAAgd,EAAmB3b,UAEhCpB,EAAAA,IAAA,MAAA,CAAKL,UAAU,OAAMI,SACnBC,EAAAA,IAACkb,EAEC,CAAAtV,QAAS,CACP,CAAExB,MAAO,gBAAiB9B,MAAO,WACjC,CAAE8B,MAAO,UAAW9B,MAAO,WAE7BA,MAAOub,EACPrb,YAAY,wBACZD,SAAWK,IACTsG,IAAiBwT,EAAgB,CAC/BxZ,KAAM,OACNZ,MAAO2a,GAAe3a,OAAS,GAC/BM,SAAUA,EACVc,YAAauZ,GAAevZ,aAAe,MAG/CyX,YAAY,GAfP,iBAAiBuB,KAAkBmB,OAkB5Cpe,OAAK,MAAA,CAAAE,UAAU,uBACbI,SAAA,CAAAC,EAAAA,IAAA,MAAA,CAAKL,UAAU,oBACbI,SAAAC,EAAAA,IAACkE,EAAU,CACTC,KAAK,sBACLC,MAA6B,YAAtByZ,EAAkC,aAAe,OACxDvb,MAAOqb,EAAclZ,EAAAA,OAAOkZ,EAAa,cAAgB,KACzDpb,SAAWsB,IACT,GAAIA,EAAU,CACZ,MAAMia,EAASrZ,SACb/G,EAAAA,MAAMmG,EAAU,aAAc,IAAIpG,MAClC,cAEFyL,IAAiBwT,EAAgB,CAC/BxZ,KAAM,OACNZ,MAAOwb,EACPlb,SAAUib,EACVna,YAAauZ,GAAevZ,aAAe,MAE9C,MACCwF,IAAiBwT,EAAgB,OAGrC/c,UAAU,aAGS,YAAtBke,GACC7d,EAAKqB,IAAA,MAAA,CAAA1B,UAAU,oBACbI,SAAAC,EAAAA,IAACkE,EACC,CAAAC,KAAK,oBACLC,MAAM,WACN9B,MAAOsb,EAAanZ,SAAOmZ,EAAY,cAAgB,KACvDrb,SAAWsB,IACT,GAAIA,GAAYoZ,EAAe,CAC7B,MAAMa,EAASrZ,SACb/G,EAAAA,MAAMmG,EAAU,aAAc,IAAIpG,MAClC,cAEFyL,IAAiBwT,EAAgB,IAC5BO,EACHvZ,YAAaoa,GAEhB,GAEHne,UAAU,kBAOvB,CAGD,OACEF,EAAAC,KAAA,MAAA,CAAKC,UAAU,OAAMI,SAAA,CACnBN,OAAO,QAAA,CAAAE,UAAU,+CACJI,SAAA,CAAA,aAAAgd,EAAmB3b,UAEhC3B,EAAAA,KACEmQ,EAAAA,SAAA,CAAA7P,SAAA,CAAAC,EAAAA,IAAA,QAAA,CACEkD,KAAK,OACLV,YACEua,EAAmB5U,YAAY3F,aAC/B,UAAUua,EAAmB3b,OAAO4F,mBAEtCrH,UAAU,kJACV2C,MAAO2a,GAAe3a,OAAS,GAC/BC,SAAWO,IACLA,EAAEM,OAAOd,MACX4G,IAAiBwT,EAAgB,CAC/BxZ,KAAM,OACNZ,MAAOQ,EAAEM,OAAOd,MAChBM,SAAU,aAGZsG,IAAiBwT,EAAgB,SAMtCO,GACCjd,EAAKqB,IAAA,MAAA,CAAA1B,UAAU,OACbI,SAAAC,EAAAA,IAACkb,EAAY,CACXtV,QAAS,CACP,CAAExB,MAAO,WAAY9B,MAAO,YAC5B,CAAE8B,MAAO,SAAU9B,MAAO,UAC1B,CAAE8B,MAAO,cAAe9B,MAAO,cAC/B,CAAE8B,MAAO,YAAa9B,MAAO,aAE/BA,MAAO2a,GAAera,UAAY,WAClCJ,YAAY,qBACZD,SAAWK,IACLqa,GACF/T,IAAiBwT,EAAgB,IAC5BO,EACHra,SAAUA,KAIhBuY,YAAY,aA0BH4C,GAGlBC,OAAOC,KAAKnV,GAAerM,OAAS,GACnCgD,EAAKC,KAAA,MAAA,CAAAC,UAAU,OAAMI,SAAA,CACnBC,EAAAA,aAAOL,UAAU,+CAETI,SAAA,mBACRC,EAAAA,IAAK,MAAA,CAAAL,UAAU,YAAWI,SACvBie,OAAOE,QAAQpV,GAAelM,IAAI,EAAEiJ,EAAWvJ,MAC9C,MAAM2L,EAASwU,EAAkBjd,KAC9BjD,GAAQA,EAAIO,MAAQ+I,GAEvB,OAAKoC,EAGHxI,cAEEE,UAAU,4HACVkQ,KAAK,SACL1O,MAAO,mBAAmB8G,EAAO7G,SACjC+F,QAAS,IA7UO,CAACtB,IAC/B8W,EAAkB9W,IA4UWsY,CAAwBtY,GAAU9F,SAAA,CAEjDN,EAAAA,YAAKE,UAAU,SACbI,SAAA,CAAAN,EAAAC,KAAA,OAAA,CAAMC,UAAU,oCAAmCI,SAAA,CAChDkI,EAAO7G,OACH,OACPpB,MAAM,OAAA,CAAAL,UAAU,6BAA4BI,SACzC,MACC,GAAI2G,MAAM0W,QAAQ9gB,EAAOgG,OACvB,MAAO,GAAGhG,EAAOgG,MAAM7F,kBAIzB,GAAoB,SAAhBH,EAAO4G,KAAiB,CAC1B,MAAMkb,EAAc1Z,IAClB,IACE,IAAKA,EAAW,MAAO,GACvB,GACEA,aAAqBjH,OACpBwG,MAAMS,EAAUtG,WAEjB,OAAOqG,EAAMA,OAACC,EAAW,cAE3B,GAAyB,iBAAdA,EAAwB,CAEjC,MAAM2Z,EAAS3gB,EAAAA,MACbgH,EACA,aACA,IAAIjH,MAEN,IAAKwG,MAAMoa,EAAOjgB,WAChB,OAAOqG,EAAMA,OAAC4Z,EAAQ,cAExB,MAAMX,EAAS,IAAIjgB,KAAKiH,GACxB,IAAKT,MAAMyZ,EAAOtf,WAChB,OAAOqG,EAAMA,OAACiZ,EAAQ,aAEzB,CACD,OAAO3f,OAAO2G,EACf,CAAC,MACA,OAAO3G,OAAO2G,GAAa,GAC5B,GAYG4Z,EATyC,CAC7CC,OAAQ,KACRC,GAAI,QACJC,IAAK,cACLC,GAAI,SACJC,IAAK,eACLC,QAAS,WAIMtiB,EAAOsG,WAAuB,KAE/C,MACsB,YAApBtG,EAAOsG,UACPtG,EAAOoH,YAEA,GAAG4a,KAAiBF,EACzB9hB,EAAOgG,YACF8b,EAAW9hB,EAAOoH,eAGpB,GAAG4a,KAAiBF,EAAW9hB,EAAOgG,QAC9C,CAED,OAAOhG,EAAOgG,KACf,EA9DA,QAiELtC,EAAAA,IACE,SAAA,CAAAmH,QAAUrE,IACRA,EAAEkJ,kBACF9C,IAAiBrD,EAAW,OAE9BlG,UAAU,qDACVwB,MAAM,eAENpB,SAAAC,EAAAA,IAACmd,EAAAA,EAAE,CAAAxd,UAAU,gBApFVkG,GAJW,gBChVrBgZ,EAAkC,EAC7CC,OACAC,WACAC,UACAC,YAAY,eACZC,SAAS,EACTC,aAAY,EACZlO,YACAmO,SAAS,IACTzf,YACAI,WACAsf,wBAEA,MAAMC,KAAEA,EAAIC,eAAEA,EAAcC,OAAEA,GAAWC,EAAAA,YAAY,CACnDR,YACAS,qBAAsBC,EAAUA,WAChCC,WAAY,CACVC,EAAAA,OAAQX,GACRY,OAAK,CAAEC,QAAS,IAChBC,QAAM,CAAED,QAAS,IACjB3d,OAAK,CACH,KAAA6d,EAAMC,MAAEA,EAAKC,SAAEA,EAAQC,gBAAEA,EAAeC,eAAEA,IACxC,MAAMrQ,EAAKmQ,EAASG,SACftQ,IACDmP,IACFnP,EAAGpQ,MAAMZ,MAAQ,GAAGkhB,EAAMK,UAAUvhB,WAGpCgR,EAAGpQ,MAAMqR,UADM,MAAbA,EAEqB,iBAAdA,EACH,GAAGA,MACHlT,OAAOkT,GAEQ,GAAGmP,MAEJ,MAAlBC,IACFrQ,EAAGpQ,MAAMqB,SAAW,GAAGof,OAE1B,OAuCP,OAjCAvb,EAAAA,UAAU,KACJia,GAAUO,EAAKkB,aAAazB,GAE5BD,GAAQU,GAAQA,KACnB,CAACT,EAAUD,EAAMQ,EAAME,IAG1B1a,EAAAA,UAAU,KACR,IAAKga,EAAM,OACX,MAAMjN,EAAe/O,IACnB,MAAMzE,EAAIyE,EAAEM,OACZ,GAAIic,GAAqBA,EAAkBhhB,GAAI,OAC/C,MAAMiiB,EAAWhB,EAAKgB,SAAS1F,QAC1B0F,IAEFvB,GAAYA,EAAShD,SAAS1d,IAC9BiiB,GAAYA,EAASvE,SAAS1d,IAIjC2gB,MAEI9O,EAAapN,IACH,WAAVA,EAAEhG,KAAkBkiB,KAI1B,OAFA3P,SAASC,iBAAiB,YAAauC,GACvCxC,SAASC,iBAAiB,UAAWY,GAC9B,KACLb,SAASK,oBAAoB,YAAamC,GAC1CxC,SAASK,oBAAoB,UAAWQ,KAEzC,CAAC4O,EAAME,EAASM,EAAKgB,SAAUvB,IAE7BD,GAASC,EAEP0B,EAAYA,aACjBzgB,MACE,MAAA,CAAA+P,IAAKuP,EAAKoB,YACQ,oBAAA,OAClB9gB,MAAO,IACF2f,EACHH,SACA7Q,SAAUgR,EAAehR,SACzBoS,UAAW,UAEbhhB,UAAWA,EAEVI,SAAAA,IAEHsP,SAASE,MAhBoB,MCSpBqR,EAAgB,EAC3BtO,eACAgH,qBACAF,oBAAoB,EACpBrQ,eAAe,IAAI/M,IACnB6kB,cAAa,EACbC,oBAAmB,EACnBC,mBAAkB,EAClBplB,UAAU,GACVqlB,mBAAmB,CAAE,EACrBlY,gBAAgB,CAAA,EAChBmY,cAAc,GACdC,gBACAC,uBACAC,iBACAC,2BACAnY,iBACAY,iBACAC,cAAc,GACdC,sBACAjO,gBACAulB,mBACAC,UAAU,KACVC,kBACAC,sBAAqB,EACrBC,oBACAC,yBAEA,MAAOC,EAAgBC,GAAqBlf,EAAQA,UAAC,IAC9Cmf,EAAaC,GAAkBpf,EAAQA,UAAC,IACxCqf,EAAqBC,GAA0Btf,EAAQA,UAAC,IACxDuf,EAAgBC,GAAqBxf,EAAQA,UAAC,IAC9Cyf,EAAcC,GAAmB1f,EAAQA,SAAC,KAE1CgL,EAAaC,GAAkBjL,EAAQA,SAAgB,OACvDkL,EAAUC,GAAenL,EAAQA,SAG9B,MAEJ2f,EAAiB3G,SAAyB,MAC1C4G,EAAgB5G,SAAuB,MACvC6G,EAAiB7G,SAAuB,MACxC8G,EAAgB9G,SAAuB,MACvC+G,EAAiB/G,SAAuB,MACxCgH,EAAmBhH,SAAiC,MACpDiH,EAAkBjH,SAAiC,MACnDkH,EAAuBlH,SAAiC,MACxDmH,EAAkBnH,SAAiC,MAGzD7W,EAAAA,UAAU,KACR,MAAM6K,EAAsB5D,IAgB1B,GAduB,CAAC3I,IACtB,IAAI2f,EAAY3f,EAChB,KAAO2f,GAAM,CACX,GACGA,EAAKC,cACuC,SAA3CD,EAAKC,aAAa,sBACnBD,EAAKE,SAAiD,SAArCF,EAAKE,QAAgBC,YAEvC,OAAO,EACTH,EAAOA,EAAKI,UACb,CACD,OAAO,GAGLC,CAAerX,EAAM3I,QAAS,OAqBhCmf,EAAc3H,UACb2H,EAAc3H,QAAQmB,SAAShQ,EAAM3I,SAEtCye,GAAkB,GAGlBW,EAAe5H,UACd4H,EAAe5H,QAAQmB,SAAShQ,EAAM3I,SAEvC6e,GAAuB,GAGvBQ,EAAc7H,UACb6H,EAAc7H,QAAQmB,SAAShQ,EAAM3I,SAEtC+e,GAAkB,IAGlBO,EAAe9H,SACd8H,EAAe9H,QAAQmB,SAAShQ,EAAM3I,SAtCf,CAACA,IACzB,IAAI2f,EAAY3f,EAChB,KAAO2f,GAAM,CACX,GAAIA,EAAKM,UAAW,CAClB,MAAMC,EAAM5c,MAAMC,KAAKoc,EAAKM,WAAWtN,KAAK,KAC5C,GAAI,4CAA4C1Y,KAAKimB,GACnD,OAAO,CACV,CAED,GAAIP,EAAKC,cAA8C,WAA9BD,EAAKC,aAAa,QAAsB,CAC/D,MAAMM,EAAMP,EAAKpjB,WAAWY,YAAc,GAC1C,GAAI,2BAA2BlD,KAAKimB,GAAM,OAAO,CAClD,CACDP,EAAOA,EAAKI,UACb,CACD,OAAO,GAwBNI,CAAkBxX,EAAM3I,SAEzB2e,GAAe,IAKnB,OADA1S,SAASC,iBAAiB,YAAaK,GAChC,IAAMN,SAASK,oBAAoB,YAAaC,IACtD,IAEH,MAAM6T,GAAgBtC,GAAeuC,SAAW,CAAC,QAC3CC,IACHpR,GAAc7V,QAAU,GAAK,GAC9BuhB,OAAOC,KAAKnV,GAAiB,CAAE,GAAErM,OAAS,EACtCknB,GAAkBvK,EAAoB,EACtCwK,GAAsBjoB,EAAQW,OACjCC,IAAsC,IAA9BykB,EAAiBzkB,EAAIO,MAC9BL,OAWIonB,GAHG,IAJW9Z,EACfnN,IAAKgS,GAAMjT,EAAQ6D,KAAMJ,GAAMA,EAAEtC,MAAQ8R,IACzCtS,OAAOqV,YACMhW,EAAQW,OAAQ8C,IAAO2K,EAAYzM,SAAS8B,EAAEtC,OAI7BR,OAAQ8C,GACzCA,EAAEgC,QAAQ4F,eAAe1J,SAAS8kB,GAAcpb,gBAK5C8c,GAAiBrf,IACrB,OAAQA,GACN,IAAK,MACH,OAAOzE,MAAC+jB,EAAKA,MAAA,CAAC3hB,KAAM,KACtB,IAAK,OACH,OAAOpC,MAAC+Y,EAAQA,SAAA,CAAC3W,KAAM,KACzB,IAAK,OACH,OAAOpC,MAACgkB,EAAQA,SAAA,CAAC5hB,KAAM,KACzB,QACE,OAAOpC,MAACikB,EAAQA,SAAA,CAAC7hB,KAAM,OAIvB8hB,GAAqBC,IACzB,OAAQA,GACN,IAAK,SACH,OAAOnkB,MAACokB,EAAMA,OAAA,CAAChiB,KAAM,KACvB,IAAK,UACH,OAAOpC,MAACqkB,EAAOA,QAAA,CAACjiB,KAAM,KACxB,IAAK,OACH,OAAOpC,MAACskB,EAAIA,KAAA,CAACliB,KAAM,KACrB,IAAK,OACH,OAAOpC,MAACukB,EAAIA,KAAA,CAACniB,KAAM,KACrB,IAAK,SACH,OAAOpC,MAACikB,EAAQA,SAAA,CAAC7hB,KAAM,KACzB,QACE,OAAOpC,MAACwkB,EAAcA,eAAA,CAACpiB,KAAM,OAI7BqiB,GAAkBhgB,IACtB,OAAQA,GACN,IAAK,MACH,MAAO,MACT,IAAK,OACH,MAAO,OACT,IAAK,OACH,MAAO,QACT,QACE,OAAOA,IAIPigB,GAAe,CACnBjgB,EACAkgB,GAA0B,KAE1BzD,GAAe0D,WAAWngB,EAAQkgB,GAClC9C,GAAkB,IAyEd9S,GAAgB,KACpBnB,EAAe,MACfE,EAAY,OAaR+W,GAAuB1oB,EAAAA,QAC3B,IAAMR,EAAQkD,KAAMtC,GAAQA,EAAI4L,YAChC,CAACxM,IAGH,OACE8D,OAAA,MAAA,CACEE,UAAU,0EACVkQ,KAAK,UAAS,aACH,oBAAmB9P,SAAA,CAG9BN,EAAAA,KACE,MAAA,CAAAoC,GAAG,wBACHlC,UAAU,UACVkQ,KAAK,SACK,YAAA,SACE,cAAA,iBAEX6T,GACG,WAAWpK,QAAyBA,SACpC,GAAGA,eACNqK,IACC,KAAKvK,QACmB,IAAtBA,EAA0B,IAAM,iBAKtCpZ,MAAA,MAAA,CAAKL,UAAU,YACbI,SAAAN,OAAA,MAAA,CAAKE,UAAU,6DAEbI,SAAA,CAAAN,EAAAC,KAAA,MAAA,CAAKC,UAAU,+DAEbF,EAAAA,KAAK,MAAA,CAAAE,UAAU,WAAWoQ,IAAK2S,EAAc3iB,SAAA,CAC1C8kB,IACCplB,EAAAC,KAAA,SAAA,CACEqQ,IAAMC,IACJ2S,EAAiB/H,QAAU5K,GAE7B7I,QAAS,IAAM4a,GAAgBD,GAC/BniB,UAAW,yFACTmiB,EACI,uGACA,iHAEN3gB,MAAM,iCAAgC,aAC3B,wBAAuB,gBACpB,OAAM,gBACL2gB,EACD,gBAAA,eACI,oBAAA,YAElB/hB,SAAA,CAAAC,MAACkR,EAAAA,OAAM,CAACvR,UAAU,YAClBK,MAAA,OAAA,CAAML,UAAU,mBAAiCI,SAAA,YAChDie,OAAOC,KAAKnV,GAAerM,OAAS,GACnCgD,EAAAA,KACE,SAAA,CAAAyD,KAAK,SACLiE,QAAUrE,IACRA,EAAEkJ,kBACFoV,OAEFzhB,UAAU,sKACVwB,MAAM,oBAAmBpB,SAAA,CAEzBC,EAAMqB,IAAA,OAAA,CAAA1B,UAAU,qBAAoBI,SACjCie,OAAOC,KAAKnV,GAAerM,SAE9BuD,EAAAA,IAACmd,EAAAA,EAAE,CAAAxd,UAAU,oDAKrBF,OAACof,EAAO,CACNC,KAAMgD,EACN/C,SAAU4D,EAAiB/H,QAC3BoE,QAAS,IAAM+C,GAAe,GAC9B9C,UAAU,eACVC,OAAQ,EACRE,OAAQ,IACRnO,UAAW,IACXtR,UAAU,2GACV0f,kBAAoBjc,IAElB,IAAI2f,EAAY3f,EAChB,KAAO2f,GAAM,CACX,GAAIA,EAAKM,UAAW,CAClB,MAAMC,EAAM5c,MAAMC,KAAKoc,EAAKM,WAAWtN,KAAK,KAC5C,GAAI,4CAA4C1Y,KAAKimB,GACnD,OAAO,CACV,CACD,GACEP,EAAKC,cACyB,WAA9BD,EAAKC,aAAa,QAClB,CACA,MAAMM,EAAMP,EAAKpjB,WAAWY,YAAc,GAC1C,GAAI,2BAA2BlD,KAAKimB,GAAM,OAAO,CAClD,CACDP,EAAOA,EAAKI,UACb,CACD,OAAO,GACRpjB,SAAA,CAEDN,cAAKE,UAAU,yCACbI,SAAA,CAAAN,EAAAA,KAAA,MAAA,CAAKE,UAAU,yCACbK,MAAI,KAAA,CAAAL,UAAU,4BAA2BI,SAAA,mBAGzCC,EAAGqB,IAAA,IAAA,CAAA1B,UAAU,6BAETI,SAAA,oCAENC,EAAAA,IACE,SAAA,CAAAmH,QAASia,EACTzhB,UAAU,yGAGHI,SAAA,iBAEXC,EAAAqB,IAAA,MAAA,CAAK1B,UAAU,yBACbK,EAACqB,IAAAmb,GACC7gB,QAASA,EACTmN,cAAeA,EACfI,eAAgBA,MAGpBlJ,MAAA,MAAA,CAAKL,UAAU,0EACbK,EAAAA,IAAK,MAAA,CAAAL,UAAU,wBAAuBI,SACnCie,OAAOC,KAAKnV,GAAerM,OAAS,GACnCgD,EAAAA,KAAA,OAAA,CAAAM,SAAA,CACGie,OAAOC,KAAKnV,GAAerM,OAC3B,UAAsC,IAAtCuhB,OAAOC,KAAKnV,GAAerM,OACxB,IACA,GAAI,2BAUnBilB,KAIHjiB,EAAKC,KAAA,MAAA,CAAAC,UAAU,oEAAmEI,SAAA,CAE/E4hB,EAIAZ,GAAmB4C,IAAmB1C,EAAYxkB,OAAS,GAC1DgD,EAAKC,KAAA,MAAA,CAAAC,UAAU,WAAWoQ,IAAKyS,EAC7BziB,SAAA,CAAAN,EAAAA,KAAA,SAAA,CACEsQ,IAAMC,IACJ6S,EAAqBjI,QAAU5K,GAEjC7I,QAAS,IAAM8a,GAAwBD,GACvCriB,UAAU,gHACVwB,MAAM,eAAcpB,SAAA,CAEpBC,EAACqB,IAAAmjB,kBAAe7kB,UAAU,YAC1BF,EAAMC,KAAA,OAAA,CAAAC,UAAU,6DACJyZ,EAAiB,UAG/B3Z,EAACC,KAAAmf,EACC,CAAAC,KAAMkD,EACNjD,SAAU8D,EAAqBjI,QAC/BoE,QAAS,IAAMiD,GAAuB,GACtChD,UAAU,aACVC,OAAQ,EACRE,OAAQ,IACRzf,UAAU,4DAA2DI,SAAA,CAErEN,EAAKC,KAAA,MAAA,CAAAC,UAAU,+BAA8BI,SAAA,CAC3CC,EAAIqB,IAAA,KAAA,CAAA1B,UAAU,4BAA6CI,SAAA,iBAC3DN,YAAGE,UAAU,6BAA4BI,SAAA,CACtCqZ,EAAiB,OACK,IAAtBA,EAA0B,IAAM,GAAE,kBAGvCpZ,MAAA,MAAA,CAAKL,UAAU,MAAKI,SACjBkhB,EAAYrkB,IAAKunB,GAChB1kB,EAEEC,KAAA,SAAA,CAAAyH,QAAS,IAjRN,CAACgd,IACnBA,EAAOhQ,WACVgQ,EAAOhd,QAAQ4B,GACfkZ,GAAuB,KA8QU6C,CAAiBX,GAChChQ,SAAUgQ,EAAOhQ,SACjBxU,UAAW,kFACTwkB,EAAOhQ,SACH,mCACAgQ,EAAOY,YACP,+BACA,kCACJhlB,SAAA,CAEDmkB,GAAkBC,EAAOtiB,IACzBsiB,EAAO/f,QAZH+f,EAAOtiB,aAqBvBif,GAAoBnlB,EAAQc,OAAS,GACpCgD,EAAAA,KAAA,MAAA,CAAKE,UAAU,WAAWoQ,IAAK0S,EAC7B1iB,SAAA,CAAAN,EAAAA,KAAA,SAAA,CACEsQ,IAAMC,IACJ8S,EAAgBlI,QAAU5K,GAE5B7I,QAAS,IAAMgb,GAAmBD,GAClCviB,UAAU,uJACVwB,MAAM,oBAAmB,aACb,sBAAsByiB,SAA0BjoB,EAAQc,yBAAwB,gBAC9E,OACC,gBAAAylB,EACD,gBAAA,mCAEdliB,EAAAA,IAACglB,EAAAA,UAASrlB,UAAU,YACpBK,MAAM,OAAA,CAAAL,UAAU,uCAAsCI,SAAA,YAGtDN,EAAMC,KAAA,OAAA,CAAAC,UAAU,yCAAwCI,SAAA,CAAA,IACpD6jB,GAAmB,IAAGjoB,EAAQc,iBAKpCgD,OAACof,EAAO,CACNC,KAAMoD,EACNnD,SAAU+D,EAAgBlI,QAC1BoE,QAAS,IAAMmD,GAAkB,GACjClD,UAAU,aACVC,OAAQ,EACRE,OAAQ,IACRzf,UAAU,+FAEVF,EAAAA,KAAK,MAAA,CAAAE,UAAU,+BAA8BI,SAAA,CAC3CC,EAAAA,UAAIL,UAAU,4BAA2BI,SAAA,sBAGzCN,EAAKC,KAAA,MAAA,CAAAC,UAAU,gBAAeI,SAAA,CAC5BC,MAACkH,EAAMA,OAAA,CAACvH,UAAU,mEAClBK,eACEkD,KAAK,OACLV,YAAY,oBACZ7C,UAAU,2HACV2C,MAAO8f,EACP7f,SAAWO,GAAMuf,EAAgBvf,EAAEM,OAAOd,eAIhDtC,aAAKL,UAAU,+BAA8BI,SAC1C8jB,GAAgBjnB,IAAKqL,IACpB,MAAMgd,GAA6C,IAAjCjE,EAAiB/Y,EAAOnL,KAC1C,OACE2C,EAEEC,KAAA,QAAA,CAAAC,UAAW,iFACTgO,IAAgB1F,EAAOnL,IAAM,aAAe,IAE9C0T,WAAS,EACTC,YAAc3N,GAlVd,EACtB+C,EACA/C,KAEA8K,EAAe/H,GACf/C,EAAEkL,aAAaC,cAAgB,OAC/BnL,EAAEkL,aAAaE,QAAQ,aAAcrI,IA4UKkI,CAAgB9F,EAAOnL,IAAKgG,GAChD4N,WAAa5N,GA1Ud,EACrBsL,EACAtL,KAEAA,EAAEuK,iBACFvK,EAAEkL,aAAaK,WAAa,OAC5B,MAAMpC,EAAQnJ,EAAEoJ,cAAmCC,wBAC7CmC,EAAWrC,EAAKe,IAAMf,EAAKnM,OAAS,EACpCyO,EAAWzL,EAAEoM,QAAUZ,EAAW,SAAW,QACnDR,EAAY,CAAEhR,IAAKsR,EAAWG,cAiUWJ,CAAelG,EAAOnL,IAAKgG,GAC9C6N,OAAQ,IA/Tb,CAACvC,IAClB,MAAMK,EAAYd,EAClB,IAAKc,GAAaA,IAAcL,EAAW,OAG3C,GAAIrS,GACcA,EAAcsD,IAAIoP,KAClB1S,EAAcsD,IAAI+O,GAIhC,OAFAR,EAAe,WACfE,EAAY,MAKhB,MAAMY,EACJb,GAAU/Q,MAAQsR,GAAkC,WAAtBP,EAASU,SACnCI,GACJ5E,GAAeA,EAAYtN,OACvBsN,EACApO,EAAQiB,IAAKwC,GAAMA,EAAEtC,MACzBR,OAAQsS,GAAMA,IAAMH,GACtB,IAAI7M,EAAM+M,EAAKE,QAAQT,GACvB,IAAa,IAATxM,EAIF,OAHAoI,IAAsB2E,GACtBf,EAAe,WACfE,EAAY,MAGTY,IAAQ9M,GAAO,GACpB+M,EAAKG,OAAOlN,EAAK,EAAG6M,GACpBzE,IAAsB2E,GACtBf,EAAe,MACfE,EAAY,OA8RwBU,CAAWvG,EAAOnL,KAChC8T,UAAW7B,aAEVlB,GAAU/Q,MAAQmL,EAAOnL,KACxBkD,MACE,OAAA,CAAAL,UAAU,4CACVC,MAAO,CACLoN,IACwB,WAAtBa,EAASU,SACL,OACApO,EACN8M,OACwB,UAAtBY,EAASU,SAAuB,OAAIpO,KAI5CH,EACEqB,IAAA,QAAA,CAAA6B,KAAK,WACL4M,QAASmV,EACT1iB,SAAU,IA7WT,CAACsD,IAC1B,MAAMof,GAA4C,IAAhCjE,EAAiBnb,GACnCwb,IAA2Bxb,GAAYof,IA2WCC,CAAmBjd,EAAOnL,KAC1C6C,UAAU,8DAEXslB,EACCjlB,MAACka,EAAAA,IAAI,CAAAva,UAAU,2BAEfK,MAACmlB,EAAAA,OAAM,CAACxlB,UAAU,0BAEpBK,MAAM,OAAA,CAAAL,UAAU,iCACbsI,EAAO7G,SAEVpB,MACE,SAAA,CAAAkD,KAAK,SACLiE,QAAUrE,IACRA,EAAEuK,iBACFvK,EAAEkJ,kBAEGiZ,EAOH3D,IAAmBrZ,EAAOnL,MAN1BukB,IAA2BpZ,EAAOnL,KAAK,GAEvCsoB,sBAAsB,IACpB9D,IAAmBrZ,EAAOnL,QAMhC6C,UAAU,gDACVwB,MAAM,wBACM,aAAA,oBAAoB8G,EAAO7G,SAEvCrB,SAAAC,MAACqlB,EAAAA,YAAW,CAAC1lB,UAAU,gBAzDpBsI,EAAOnL,SA+DpB2C,OAAA,MAAA,CAAKE,UAAU,+BACbI,SAAA,CAAAN,EAAAA,KAAA,MAAA,CAAKE,UAAU,oCACbI,SAAA,CAAAN,OAAA,QAAA,CAAOE,UAAU,4JACfK,EACEqB,IAAA,QAAA,CAAA6B,KAAK,WACL4M,QAAS+T,GAAgByB,MACtB/oB,IAAsC,IAA9BykB,EAAiBzkB,EAAIO,MAEhCyF,SAAWO,IACT+gB,GAAgBnnB,QAASuL,IACvBoZ,IACEpZ,EAAOnL,IACPgG,EAAEM,OAAO0M,YAIfnQ,UAAU,8DAEZK,EAAuBqB,IAAA,OAAA,CAAAtB,SAAA,kBAEzBC,EAAAA,IAAA,SAAA,CACEL,UAAU,2GACVwH,QAAS,KAjW7BxL,EAAQe,QAASuL,IACfoZ,IAA2BpZ,EAAOnL,KAAwB,IAAnBmL,EAAOsd,WAEhDvb,IAAsBrO,EAAQiB,IAAKqL,GAAWA,EAAOnL,MACrDgN,MA+VsBqY,GAAkB,IAIbpiB,SAAA,qBAEVqiB,GACCpiB,EAAAA,IACE,SAAA,CAAAL,UAAU,6CACVwH,QAAS,IAAMkb,EAAgB,IAAGtiB,SAAA,0BAW7C0hB,GACCzhB,EAAAA,WAAKL,UAAU,oBAAmBI,SAChCC,EAAAA,IACE,MAAA,CAAAL,UAAU,gEACVwB,MAAM,cAEJpB,SAAA,CAAC,KAAM,KAAM,MAAoBnD,IAAK2H,GACtCvE,EAAAA,IAAA,SAAA,CAEEL,UAAW,iCACT4hB,IAAYhd,EACR,yBACA,mCAEN4C,QAAS,IAAMqa,IAAkBjd,GAAExE,SAElCwE,EAAEihB,eAREjhB,QAgBfvE,EAAKqB,IAAA,MAAA,CAAA1B,UAAU,6EAA4EI,SAEzFN,EAAAA,YAAKE,UAAU,WACbI,SAAA,CAAAC,EAAAA,IAACkH,EAAAA,OACC,CAAAvH,UAAU,2EAA0E,cACxE,SAEdK,EACEqB,IAAA,QAAA,CAAA0O,IAAKuS,EACLpf,KAAK,OACLV,YAAa,YACb7C,UAAU,wMACV2C,MAAOgQ,EACP/P,SAAU4e,EACC,aAAA,yCACO,YAAW,mBACZ,0BAElB7O,GACCtS,EAAAA,cACEmH,QAAS,IACPga,EAAqB,CACnB/d,OAAQ,CAAEd,MAAO,MAGrB3C,UAAU,0GACVwB,MAAM,eAAcpB,SAEpBC,MAACmd,EAACA,EAAA,CAACxd,UAAU,mBAOpBkhB,GAAcK,GAAetH,SAC5Bna,EAAKC,KAAA,MAAA,CAAAC,UAAU,WAAWoQ,IAAKwS,YAC7BviB,EACEqB,IAAA,SAAA,CAAA0O,IAAMC,IACJ4S,EAAgBhI,QAAU5K,GAE5B7I,QAAS,IAAM0a,GAAmBD,GAClCjiB,UAAU,yKAAwK,aACtK,eACVgkB,GACI,KAAKvK,mBACL,KAAKE,WAEG,gBAAA,OACC,gBAAAsI,EACD,gBAAA,cACI,oBAAA,YAClBzgB,MAAM,uBAENpB,SAAAC,EAAAA,IAACikB,EAAAA,SAAQ,CAACtkB,UAAU,cAEtBF,EAAAA,KAACof,GACCC,KAAM8C,EACN7C,SAAU6D,EAAgBhI,QAC1BoE,QAAS,IAAM6C,GAAkB,GACjC5C,UAAU,aACVC,OAAQ,EACRE,OAAQ,IACRzf,UAAU,qFAEVI,SAAA,CAAAN,EAAAC,KAAA,MAAA,CAAKC,UAAU,+BAA8BI,SAAA,CAC3CC,EAAIqB,IAAA,KAAA,CAAA1B,UAAU,4BAA4CI,SAAA,gBAC1DC,EAAGqB,IAAA,IAAA,CAAA1B,UAAU,6BAETI,SAAA,oCAGNN,OAAA,MAAA,CAAKE,UAAU,MAAKI,SAAA,CAElBN,EAAAA,KAAK,MAAA,CAAAE,UAAU,OAAMI,SAAA,CACnBC,MAAG,IAAA,CAAAL,UAAU,8CAETI,SAAA,oBACJC,EAAAA,IAAK,MAAA,CAAAL,UAAU,YACZI,SAAAyjB,GAAc5mB,IAAK6H,GAClBhF,EAAAC,KAAA,SAAA,CAEEyH,QAAS,IAAMud,GAAajgB,GAAQ,GACpC9E,UAAU,+HAETI,SAAA,CAAA+jB,GAAcrf,GACfhF,EAAiBC,KAAA,OAAA,CAAAK,SAAA,CAAA,aAAA0kB,GAAehgB,MAChChF,EAAAA,KAAA,OAAA,CAAME,UAAU,gCAA+BI,SAAA,CAC5CuZ,EAAmBU,8BAPjB,OAAOvV,WAenBkf,IACClkB,EAAAC,KAAA,MAAA,CAAAK,SAAA,CACEC,EAAGqB,IAAA,IAAA,CAAA1B,UAAU,8CAETI,SAAA,oBACJC,EAAKqB,IAAA,MAAA,CAAA1B,UAAU,YACZI,SAAAyjB,GAAc5mB,IAAK6H,GAClBhF,EAAAA,KAEE,SAAA,CAAA0H,QAAS,IAAMud,GAAajgB,GAAQ,GACpC9E,UAAU,+GAETI,SAAA,CAAA+jB,GAAcrf,GACfhF,OAAiB,OAAA,CAAAM,SAAA,CAAA,aAAA0kB,GAAehgB,MAChChF,EAAMC,KAAA,OAAA,CAAAC,UAAU,gCACbI,SAAA,CAAAqZ,EACI,aARF,YAAY3U,gCCn3BzC,SAAUghB,EAAc7f,GAC5B,MAAMgU,EAAUhU,GAASgU,UAAW,EAC9B8L,EAAY/J,SAAsB,MAClCgK,EAAchK,SAAuC,MAE3D7W,EAAAA,UAAU,KACR,IAAK8U,EAAS,OAEd,IACE,MAAMgM,EAAO,IAAIC,EAActlB,gBACzBulB,EAAO,IAAIC,KAAK,CAACH,GAAO,CAAE1iB,KAAM,2BACtCwiB,EAAU9K,QAAU,IAAIoL,OAAOC,IAAIC,gBAAgBJ,GACpD,CAAC,MAAOK,GACPlpB,QAAQC,KACN,uEACAipB,GAEFT,EAAU9K,QAAU,IACrB,CACD,MAAMwL,EAAIV,EAAU9K,QAQpB,OAPAwL,EAAGC,UAAavjB,IACd,MAAMlH,EAAOkH,EAAElH,KACXA,GAAsB,WAAdA,EAAKsH,OACfyiB,EAAY/K,UAAUhf,EAAK0qB,QAAQlO,MACnCuN,EAAY/K,QAAU,OAGnB,KACLwL,GAAGG,YACHb,EAAU9K,QAAU,KACpB+K,EAAY/K,QAAU,OAEvB,CAAChB,IA+BJ,MAAO,CAAElG,QA7BOvX,EAAAA,QAAQ,IACf,CAACic,EAAaxR,KACnB,IAAKgT,IAAY8L,EAAU9K,QAAS,CAElC,IAAKxC,GAAwB,IAAhBA,EAAK3b,SAAiBmK,GAAM9J,IACvC,OAAO0pB,QAAQC,QAAQrO,GACzB,MAAMsO,EAAyB,QAAnB9f,EAAKiK,UAAsB,GAAK,EACtC8V,EAAM,IAAIvO,GAAMxR,KAAK,CAACrI,EAAGC,KAC7B,MAAMooB,EAAKroB,IAAIqI,EAAK9J,KACd+pB,EAAKroB,IAAIoI,EAAK9J,KACpB,OAAU,MAAN8pB,GAAoB,MAANC,EAAmB,EAC3B,MAAND,GAAoB,EAAIF,EAClB,MAANG,EAAmB,EAAIH,EACT,iBAAPE,GAAiC,iBAAPC,GAC3BD,EAAKC,GAAMH,EACd3oB,OAAO6oB,GAAI/f,cAAc9I,OAAO8oB,IAAOH,IAEhD,OAAOF,QAAQC,QAAQE,EACxB,CACD,OAAO,IAAIH,QAAgBC,IACzBd,EAAY/K,QAAU6L,EACtBf,EAAU9K,QAASkM,YAAY,CAC7B5jB,KAAM,UACNojB,QAAS,CAAElO,OAAMxR,aAItB,CAACgT,IAGN,CAGA,SAASiM,IACP,MAAMkB,EAAMC,KAyBXD,EAAYV,UAAavjB,IACxB,MAAMmkB,EAAMnkB,EAAElH,KACd,GAAKqrB,GAAoB,YAAbA,EAAI/jB,KAChB,IACE,MAAMkV,KAAEA,EAAIxR,KAAEA,GAASqgB,EAAIX,QACrBlqB,EA7BU,EAClBgc,EACAxR,KAEA,IAAKwR,GAAwB,IAAhBA,EAAK3b,OAAc,OAAO2b,EACvC,IAAIuO,EAAMvO,EACV,GAAIxR,GAAQA,EAAK9J,IAAK,CACpB,MAAMA,IAAEA,EAAG+T,UAAEA,GAAcjK,EACrB8f,EAAoB,QAAd7V,EAAsB,GAAK,EACvC8V,EAAM,IAAIvO,GAAMxR,KAAK,CAACrI,EAAGC,KACvB,MAAMooB,EAAKroB,IAAIzB,GACT+pB,EAAKroB,IAAI1B,GACf,GAAU,MAAN8pB,GAAoB,MAANC,EAAY,OAAO,EACrC,GAAU,MAAND,EAAY,OAAQ,EAAIF,EAC5B,GAAU,MAANG,EAAY,OAAO,EAAIH,EAC3B,GAAkB,iBAAPE,GAAiC,iBAAPC,EACnC,OAAQD,EAAKC,GAAMH,EACrB,MAAMQ,EAAKnpB,OAAO6oB,GACZO,EAAKppB,OAAO8oB,GAClB,OAAOK,EAAGrgB,cAAcsgB,GAAMT,GAEjC,CACD,OAAOC,GAOUS,CAAYhP,EAAMxR,GAAQ,MACxCmgB,EAAYD,YAAY,CAAE5jB,KAAM,SAAUojB,QAAS,CAAElO,KAAMhc,IAC7D,CAAC,MAAO+pB,GACNY,EAAYD,YAAY,CACvB5jB,KAAM,SACNojB,QAAS,CAAElO,KAAMtV,EAAElH,MAAM0qB,SAASlO,MAAQ,KAE7C,EAEL,CClGO,MCQMiP,EAAgB,EAAGzrB,OAAM0rB,iBACpC,MAAOpO,EAAaqO,GAAkB5kB,EAAQA,SAAC,IACxCkX,EAAU2N,GAAe7kB,EAAQA,SAAC2kB,GAAYzN,UAAY,KAC1DV,EAAiBsO,GAAsB9kB,EAAQA,UAAC,GAEjD+kB,EAAYC,EAAAA,YAAY,KAC5BJ,EAAe,IACd,IAEGtO,EAAqC9c,EAAAA,QAAQ,KACjD,MAAMyrB,GAAoC,IAAxBN,GAAY1N,QAExB5Q,EACK,YAFEse,GAAYO,MAAQ,UAG3BP,GAAYQ,cAAc9e,WAAa,EACvCpN,EAAKa,OACL6d,EAAa7b,KAAKspB,KAAK/e,EAAY6Q,GAEzC,MAAO,CACLD,QAASgO,EACT/N,WACAX,cACAoB,aACAtR,YACAmR,sBAA2D,IAArCmN,GAAYnN,qBAClCC,gBAAiBkN,GAAYlN,iBAAmB,CAAC,GAAI,GAAI,IAAK,IAAK,OAEpE,CAACkN,EAAYzN,EAAUX,EAAatd,EAAKa,SAEtCurB,EAAgB7rB,EAAAA,QAAQ,KAC5B,IAAK8c,EAAiBW,SAAgC,WAArB0N,GAAYO,KAC3C,OAAOjsB,EAGT,MAAM0X,GAAc4F,EAAc,GAAKW,EACjC/D,EAAWxC,EAAauG,EAC9B,OAAOje,EAAKyM,MAAMiL,EAAYwC,IAC7B,CAACla,EAAMsd,EAAaW,EAAUZ,EAAiBW,QAAS0N,GAAYO,OAEjEI,EAAmBN,cACvBO,MAAOxN,IACL,GACuB,WAArB4M,GAAYO,MACZP,GAAYQ,cAAcvO,aAC1B,CACAkO,GAAmB,GACnB,UACQH,EAAWQ,aAAavO,aAAamB,EAAMb,EAClD,CAAS,QACR4N,GAAmB,EACpB,CACF,CACDF,EAAe7M,IAEjB,CAAC4M,EAAYzN,IAGTsO,EAAuBR,cAC3BO,MAAOE,IACL,GACuB,WAArBd,GAAYO,MACZP,GAAYQ,cAActO,iBAC1B,CACAiO,GAAmB,GACnB,UACQH,EAAWQ,aAAatO,iBAAiB4O,EAChD,CAAS,QACRX,GAAmB,EACpB,CACF,CACDD,EAAYY,GACZb,EAAe,IAEjB,CAACD,IAGH,MAAO,CACLpO,cACAwO,YACAzO,mBACA+O,gBACA7O,kBACA8O,mBACAE,yBC9FEE,EAAe,CAAC9pB,EAAyBC,KAC7C,GAAID,EAAE6D,OAAS5D,EAAE4D,KAAM,OAAO,EAC9B,IAAK,MAAME,KAAS/D,EAClB,IAAKC,EAAEa,IAAIiD,GAAQ,OAAO,EAE5B,OAAO,GAGIgmB,EAAe,EAC1B1sB,OACA2sB,iBACAnW,cACAnW,eAGA,MAAM4X,EAAe8T,EAAAA,YACnB,CAAC9qB,EAAc4W,IACTxX,EACKA,EAASY,QAEHsD,IAAXtD,EAAIgF,GACChF,EAAIgF,IAGgB,gBAAzB6R,QAAQC,IAAIC,UAA2C,IAAbH,GAC5CxW,QAAQC,KACN,mPAMGuW,GAAY,GAErB,CAACxX,KAGI8M,EAAcyf,GAAmB7lB,EAAAA,SACtC,IAAM,IAAI3G,IAAIusB,EAAiB7hB,MAAMC,KAAK4hB,GAAkB,KAG9DzjB,EAAAA,UAAU,KACHyjB,GACLC,EAAiBlnB,IACf,MAAMC,EAAO,IAAIvF,IAAI0K,MAAMC,KAAK4hB,IAChC,OAAIF,EAAa/mB,EAAMC,GAAcD,EAC9BC,KAER,CAACgnB,IAEJzjB,EAAAA,UAAU,KACR,MAAM2jB,EAAW,IAAIzsB,IAAIJ,EAAKgB,IAAI,CAACC,EAAK+E,IAAQiS,EAAahX,EAAK+E,KAClE4mB,EAAiBlnB,IACf,MAAMC,EAAO,IAAIvF,IAIjB,OAHAsF,EAAK5E,QAASmF,IACR4mB,EAASppB,IAAIwC,IAAKN,EAAKmG,IAAI7F,KAE7BwmB,EAAa/mB,EAAMC,GAAcD,EAC9BC,KAER,CAAC3F,EAAMiY,IAiCV,MAAO,CACL9K,eACA2f,gBAjCsBf,EAAAA,YACtB,CAACnU,EAAwBlM,KAWvB,GAVAkhB,EAAiBlnB,IACf,MAAMqnB,EAAS,IAAI3sB,IAAIsF,GAMvB,OALIgG,EACFqhB,EAAOjhB,IAAI8L,GAEXmV,EAAOlhB,OAAO+L,GAETmV,IAGLvW,EAAa,CACf,MAAMvV,EAAMjB,EAAK4D,KAAK,CAACopB,EAAMhnB,IAAQiS,EAAa+U,EAAMhnB,KAAS4R,GAC7D3W,GAAKuV,EAAYvV,EACtB,GAEH,CAACjB,EAAMwW,EAAayB,IAiBpBgV,gBAdsBlB,cACrBrgB,IAEGkhB,EADElhB,EACc,IAAItL,IAAIJ,EAAKgB,IAAI,CAACC,EAAK+E,IAAQiS,EAAahX,EAAK+E,KAEjD,IAAI5F,MAGxB,CAACJ,EAAMiY,MClFEiV,EAAoB,EAC/BltB,OACAqW,eAAc,EACdnW,YAAY,GACZitB,WAAW,GACXjpB,aAEA,MAAMkpB,EAAYrN,SAAuB,OAClCsN,EAAWC,GAAgBvmB,EAAQA,SAAC,GACrCwmB,EAAWxN,SAAsB,MACjCyN,EAAsBzN,SAAe,IACpC0N,EAAiBC,GAAsB3mB,EAAQA,SAAC7C,GAEjDoS,EAAmB/V,EAAAA,QAAQ,KAC/B,IAAK8V,EACH,MAAO,CACLqB,WAAY,EACZwC,SAAUla,EAAKa,OAAS,EACxB2W,QAAS,GAKb,MAAMmW,EAAa9qB,KAAKG,IAAI,EAAGH,KAAKspB,KAAKsB,EAAkBvtB,IACrD0tB,EAAkB/qB,KAAKG,IAC3BmqB,EACAtqB,KAAKG,IAAI,EAAGH,KAAKC,IAAiB,EAAb6qB,EAAgB,MAGjCjW,EAAa7U,KAAKG,IACtB,EACAH,KAAKsc,MAAMkO,EAAYntB,GAAa0tB,GAEhCC,EAAehrB,KAAKspB,KAAKsB,EAAkBvtB,GAOjD,MAAO,CAAEwX,aAAYwC,SANJrX,KAAKC,IACpB9C,EAAKa,OAAS,EACd6W,EAAamW,EAAiC,EAAlBD,GAICpW,QADfE,EAAaxX,IAE5B,CACDmtB,EACAI,EACAztB,EAAKa,OACLX,EACAitB,EACA9W,IAGIyX,EAAcvtB,EAAAA,QAAQ,IACrB8V,EACErW,EAAKyM,MACV6J,EAAiBoB,WACjBpB,EAAiB4D,SAAW,GAHLla,EAKxB,CAACA,EAAMsW,EAAkBD,IAEtBe,EAAcpX,EAAKa,OAASX,EAE5B6tB,EAAehC,cAAa7kB,IAChC,MACM8mB,EADS9mB,EAAEM,OACM6lB,UACvBG,EAAoBxO,QAAUgP,EACN,MAApBT,EAASvO,UACXuO,EAASvO,QAAUwK,sBAAsB,KACvC+D,EAASvO,QAAU,KACnBsO,EAAaE,EAAoBxO,aAGpC,IAYH,OAVA9V,EAAAA,UAAU,IACD,KACmB,MAApBqkB,EAASvO,SAAiBiP,qBAAqBV,EAASvO,UAE7D,IAEH9V,EAAAA,UAAU,KACRwkB,EAAmBxpB,IAClB,CAACA,IAEG,CACLkpB,YACA9W,mBACAwX,cACA1W,cACA2W,iBC7ESG,EAAiBC,EAAAA,WAG5B,UACAnuB,KACEA,EAAID,QACJA,EAAOM,SACPA,EAAQ6D,OACRA,EACAyhB,QAASyI,EAAc,KAAIC,cAC3BA,EACArhB,aAAcshB,EAAgB1I,gBAC9BA,EAAeC,mBACfA,GAAqB,EAAKrP,YAC1BA,EAAW+X,qBACXA,EAAoBtuB,WACpBA,GAAa,EAAKsW,gBAClBA,EAAeiY,YACfA,GAAc,EAAIvJ,WAClBA,GAAa,EAAK1Y,WAClBA,GAAa,EAAIoB,UACjBA,GAAY,EAAK0I,YACjBA,GAAc,EACdnW,UAAWuuB,EAAatB,SACxBA,EAAW,EAAC1W,SACZA,EAAQE,cACRA,EAAaC,iBACbA,EAAgBC,WAChBA,EAAUC,YACVA,EAAWG,kBACXA,EAAiByX,iBACjBA,GAAmB,EAAKC,kBACxBA,EAAiBC,qBACjBA,GAAuB,EAAKC,cAC5BA,EAAgB,KAAM,EAAIC,sBAC1BA,EAAqBpD,WACrBA,EAAa,CAAE1N,SAAS,EAAOiO,KAAM,SAAUhO,SAAU,IAAI8Q,wBAC7DA,EAA0B,IAAGC,UAC7BA,GAAY,EAAKC,eACjBA,EAAc5hB,OACdA,EAAM6hB,qBACNA,EAAoBC,oBACpBA,EAAmBC,YACnBA,EAAWC,aACXA,GAAYC,mBACZA,GAAqB,OAAMC,mBAC3BA,GAAqB,QAAOC,YAC5BA,GAAW7C,eACXA,GAAc8C,kBACdA,GAAoB,CAAA,EACpBnK,cAAeoK,GAAiB9T,mBAChCA,IAEFzH,IAGA,MAAM2L,GAAeC,SAAuB,OACrC4P,GAAgBC,IAAqB7oB,EAAQA,SAAS,GAC7DmC,EAAAA,UAAU,KACR,MAAMkL,EAAK0L,GAAad,QACxB,IAAK5K,EAAI,OACT,MAAMwP,EAAS,IAAMgM,GAAkBxb,EAAGyb,aAAe,GACzDjM,IACA,MAAMkM,EAAK,IAAIC,eAAe,IAAMnM,KAGpC,OAFAkM,EAAGE,QAAQ5b,GACX7E,OAAOmE,iBAAiB,SAAUkQ,GAC3B,KACLkM,EAAGG,aACH1gB,OAAOuE,oBAAoB,SAAU8P,KAEtC,IAEH,MAAOsM,GAAaC,IAAkBppB,EAAQA,SAGpC,OACHqpB,GAAiBC,IAAsBtpB,EAAQA,SAG5C,OACHupB,GAAcC,IAAmBxpB,EAAQA,SAGtC,MACJypB,GAAiBzQ,UAAO,GACxB0Q,GAAkB1Q,SAAwC,MAC1D2Q,GAAmB3Q,SAAsB,OACxC4Q,GAAaC,IAAkB7pB,EAAQA,SAKpC,OAGH4e,GAASkL,IAAc9pB,EAAQA,SAAUqnB,GAChDllB,EAAAA,UAAU,KAER2nB,GAAWzC,IACV,CAACA,IAEJ,MAAM0C,GAAsCvwB,EAAAA,QAC1C,KAAO,CAAEwwB,GAAI,GAAIC,GAAI,GAAIC,GAAI,KAC7B,IAEIC,GAAoB3wB,EAAOA,QAC/B,IAAwB,MAAjBkuB,EAAwBA,EAAgBqC,GAAWnL,IAC1D,CAAC8I,EAAe9I,GAASmL,KAGrBK,GAAuB5wB,EAAAA,QAAQ,KACnC,GAAwB,MAApB+tB,EAA0B,OAAOA,EAErC,OAAOwC,GADGzC,GAAiB,OAE1B,CAACC,EAAkBD,EAAeyC,MAE/Bpa,aACJA,GAAYxJ,cACZA,GAAakkB,aACbA,GAAYC,gBACZA,GAAeC,gBACfA,GAAeC,gBACfA,ICnIgC,GAClCvxB,OACAD,cAEA,MAAO2W,EAAc2a,GAAmBtqB,EAAQA,SAAS,KAClDmG,EAAeskB,GAAoBzqB,EAAQA,SAAgB,CAAE,GAG9D0qB,EAAuBC,mBAAiBhb,GACxCib,EAAwBD,mBAAiBxkB,GAEzC0kB,EAAoB7F,EAAWA,YACnC,CAAC9qB,EAAcgJ,EAAmBvJ,KAChC,MAAMgG,EAAQzF,EAAIgJ,GAElB,OAAQvJ,EAAO4G,MACb,IAAK,OACH,MAAMT,EAAY1E,OAAOuE,GAAS,IAAI0E,cAChCymB,EAAa1vB,OAAOzB,EAAOgG,OAAS,IAAI0E,cAE9C,OAAQ1K,EAAOsG,UACb,IAAK,WAQL,QACE,OAAOH,EAAUnF,SAASmwB,GAP5B,IAAK,SACH,OAAOhrB,IAAcgrB,EACvB,IAAK,aACH,OAAOhrB,EAAUirB,WAAWD,GAC9B,IAAK,WACH,OAAOhrB,EAAUkrB,SAASF,GAKhC,IAAK,SACH,MAAM1pB,EAAW9F,WAAWqE,GACtBsrB,EAAY3vB,WAAW3B,EAAOgG,OAEpC,GAAI2B,MAAMF,IAAaE,MAAM2pB,GAAY,OAAO,EAEhD,OAAQtxB,EAAOsG,UACb,IAAK,SAiBL,QACE,OAAOmB,IAAa6pB,EAhBtB,IAAK,KACH,OAAO7pB,EAAW6pB,EACpB,IAAK,MACH,OAAO7pB,GAAY6pB,EACrB,IAAK,KACH,OAAO7pB,EAAW6pB,EACpB,IAAK,MACH,OAAO7pB,GAAY6pB,EACrB,IAAK,UACH,MAAMC,EAAY5vB,WAAW3B,EAAOoH,aACpC,OACGO,MAAM4pB,IACP9pB,GAAY6pB,GACZ7pB,GAAY8pB,EAMpB,IAAK,OACH,MAAMnpB,EAAY,IAAIjH,KAAK6E,GACrBwrB,EAAa,IAAIrwB,KAAKnB,EAAOgG,OAEnC,GAAI2B,MAAMS,EAAUtG,YAAc6F,MAAM6pB,EAAW1vB,WACjD,OAAO,EAGT,MAAM2vB,EAAW,IAAItwB,KACnBiH,EAAUspB,cACVtpB,EAAUupB,WACVvpB,EAAUwpB,WAENC,EAAiB,IAAI1wB,KACzBqwB,EAAWE,cACXF,EAAWG,WACXH,EAAWI,WAGb,OAAQ5xB,EAAOsG,UACb,IAAK,SAsBL,QACE,OAAOmrB,EAAS3vB,YAAc+vB,EAAe/vB,UArB/C,IAAK,KACH,OAAO2vB,EAAS3vB,UAAY+vB,EAAe/vB,UAC7C,IAAK,MACH,OAAO2vB,EAAS3vB,WAAa+vB,EAAe/vB,UAC9C,IAAK,KACH,OAAO2vB,EAAS3vB,UAAY+vB,EAAe/vB,UAC7C,IAAK,MACH,OAAO2vB,EAAS3vB,WAAa+vB,EAAe/vB,UAC9C,IAAK,UACH,MAAMwf,EAAa,IAAIngB,KAAKnB,EAAOoH,aACnC,GAAIO,MAAM2Z,EAAWxf,WAAY,OAAO,EACxC,MAAMgwB,EAAiB,IAAI3wB,KACzBmgB,EAAWoQ,cACXpQ,EAAWqQ,WACXrQ,EAAWsQ,WAEb,OACEH,EAAS3vB,WAAa+vB,EAAe/vB,WACrC2vB,EAAS3vB,WAAagwB,EAAehwB,UAM7C,IAAK,UACH,OAAOuT,QAAQrP,KAAWqP,QAAQrV,EAAOgG,OAE3C,IAAK,cACH,MAAM+rB,EAAe3nB,MAAM0W,QAAQ9gB,EAAOgG,OACtChG,EAAOgG,MACP,CAAChG,EAAOgG,OAGNgsB,EAAkBjoB,GAEpBA,SAEQ,KAARA,GACgB,iBAARA,GAAmC,KAAfA,EAAIpD,OAEzB,KAEFoD,EAGHkoB,EAAkBD,EAAehsB,GAGvC,OAF+B+rB,EAAazxB,IAAI0xB,GAElBhxB,SAASixB,GAEzC,QACE,OAAO,IAGb,IAIIC,EAAoB7S,EAAAA,OAAiC,IAAI8S,SACzDC,EAAe/S,SAAO,GAItBgT,EAAqBhH,EAAAA,YAAY,KACjC+G,EAAa9T,QAJE,MAKjB4T,EAAkB5T,QAAU,IAAI6T,QAChCC,EAAa9T,QAAU,IAExB,IAEGoS,EAAe7wB,EAAAA,QAAQ,KAC3BwyB,IAGA,MAAMC,EAAmB5Q,OAAOC,KAAKsP,GAAuB9wB,OAAS,EAC/DoyB,IAAcxB,EACpB,IAAKuB,IAAqBC,EAAW,OAAOjzB,EAE5C,MAAMkzB,EAAczB,EAAqBrmB,cACnC+nB,EAAQP,EAAkB5T,QAChC,IAAIoU,EAAUpzB,EAGd,GAAIgzB,EACF,IAAK,MAAO/oB,EAAWvJ,KAAW0hB,OAAOE,QAAQqP,GAC/C,GAAKjxB,IACL0yB,EAAUA,EAAQ1yB,OAAQO,GACxB2wB,EAAkB3wB,EAAKgJ,EAAWvJ,IAGb,IAAnB0yB,EAAQvyB,QAAc,MAK9B,GAAIoyB,GAAaG,EAAQvyB,OAAS,EAAG,CAEnC,MAAMwyB,EAAiBtzB,EAAQW,OAAQC,IAAwB,IAAhBA,EAAIgpB,SAEnDyJ,EAAUA,EAAQ1yB,OAAQO,IACxB,IAAIqyB,EAASH,EAAMpuB,IAAI9D,GAqBvB,OApBc,MAAVqyB,IACFA,EAASD,EACNryB,IAAKwC,IACJ,MAAMxB,EAAIf,EAAIuC,EAAEtC,KAChB,GAAS,MAALc,EAAW,MAAO,GACtB,GAAiB,iBAANA,EAAgB,OAAOA,EAAEoJ,cACpC,GAAiB,iBAANpJ,GAA+B,kBAANA,EAClC,OAAOG,OAAOH,GAAGoJ,cAEnB,MAAM5J,EAAIW,OAAOH,GACjB,OAAOR,EAAEX,OAAS,IAAMW,EAAEiL,MAAM,EAAG,KAAOjL,IAE3C2Y,KAAK,KAGJ2Y,EAAa9T,QAxDJ,MAyDXmU,EAAMxvB,IAAI1C,EAAKqyB,GACfR,EAAa9T,YAGVsU,EAAO5xB,SAASwxB,IAE1B,CAED,OAAOE,GACN,CACDpzB,EACA2xB,EACAF,EACA1xB,EACA6xB,EACAmB,IAGIzB,EAAkBvF,EAAAA,YACtB,CAAC9hB,EAAmBvJ,KAClB8wB,EAAkB9rB,IAChB,MAAM6tB,EAAa,IAAK7tB,GAMxB,OALIhF,EACF6yB,EAAWtpB,GAAavJ,SAEjB6yB,EAAWtpB,GAEbspB,KAGX,IAGIC,EAAoBzH,cACvB9hB,IACCqnB,EAAgBrnB,EAAW,OAE7B,CAACqnB,IAGGC,EAAkBxF,EAAAA,YAAY,KAClCyF,EAAiB,CAAE,GACnBH,EAAgB,IAEhBuB,EAAkB5T,QAAU,IAAI6T,QAChCC,EAAa9T,QAAU,GACtB,IAEGyU,EAAuB1H,EAAAA,YAAY,KACvC,IAAIrP,EAAQ,EAGZ,OAFI+U,GAAsB/U,IAC1BA,GAAS0F,OAAOC,KAAKsP,GAAuB9wB,OACrC6b,GACN,CAAC+U,EAAsBE,IAE1B,MAAO,CACLjb,eACA2a,kBACAnkB,cAAeykB,EACfP,eACAE,kBACAkC,oBACAjC,kBACAkC,yBDnIEC,CAAqB,CAAE1zB,OAAMD,aAG1BkN,GAAY0mB,IAAiB5sB,WAGjC,CACD7F,IAAK,KACL+T,UAAW,QAIN2e,GAAcC,IAAmB9sB,EAAQA,SAC9C,KACE,MAAM+sB,EAAwC,CAAA,EAI9C,OAHA/zB,EAAQe,QAASH,IACfmzB,EAAcnzB,EAAIO,KAAOP,EAAIyC,OAAS,MAEjC0wB,IAKLC,GAAmBhU,EAAAA,OACvBhgB,EAAQ2C,OACN,CAACgB,EAAKF,KACJE,EAAIF,EAAEtC,KAAOsC,EAAEJ,OAAS,IACjBM,GAET,CAAA,IAKEswB,GAAkBjU,EAAMA,OAAWhgB,EAAQiB,IAAKwC,GAAMA,EAAEtC,OACvDiN,GAAa8lB,IAAkBltB,EAAAA,SAAmB,IACvDitB,GAAgBhV,QAAQvS,UAInBtM,GAAe+zB,IAAoBntB,EAAAA,SAAsB,IAAI3G,MAG7DglB,GAAkB+O,IAAuBptB,EAAQA,SAEtD,KACA,MAAMqtB,EAA6C,CAAA,EAInD,OAHAr0B,EAAQe,QAASH,IACfyzB,EAAkBzzB,EAAIO,MAAO,IAExBkzB,IAGHC,GAAatI,cAChB7qB,IACC,IAAIozB,EAAyBpzB,EACzBqzB,EAAiC,MAEjCtnB,GAAW/L,MAAQA,GACrBozB,EAAUpzB,EACVqzB,EAAU,OACwB,QAAzBtnB,GAAWgI,UACpBsf,EAAU,OACwB,SAAzBtnB,GAAWgI,WAEpBqf,EAAU,KACVC,EAAU,MAEVA,EAAU,MAGZZ,GAAc,CAAEzyB,IAAKozB,EAASrf,UAAWsf,IAGrClnB,GAA+B,WAArBqe,GAAYO,MAAqBqI,GAAWC,GACxDlnB,EAAOinB,EAASC,IAGpB,CAAClnB,EAAQqe,GAAYO,KAAMhf,GAAW/L,IAAK+L,GAAWgI,YAGlDuf,GAAqBzI,EAAAA,YAAY,CAAC9hB,EAAmB7G,KACzDywB,GAAiBnuB,IAAU,IACtBA,EACHuE,CAACA,GAAY7G,MAEd,IAGGqxB,GAAuB1I,cAAa/S,IACxC,GAAY,MAARA,EAAc,OAAO,EAGzB,OADkB,GADQ,iBAATA,EAAoBA,EAAO7W,OAAO6W,IAE1CnY,QACR,IAEG6zB,GAAiB3I,cACpB9hB,IACC,MAAMtJ,EAAMZ,EAAQ6D,KAAMJ,GAAMA,EAAEtC,MAAQ+I,GAC1C,IAAKtJ,EAAK,OACV,MAAMg0B,EAAcF,GAAqB9zB,EAAI6E,QACvCovB,EAAS50B,EAAKyM,MAAM,EAAG,KAC7B,IAAIooB,EAAU,EACd,IAAK,MAAM5zB,KAAO2zB,EAAQ,CACxB,MAAMluB,EAAQzF,EAAIgJ,GAClB4qB,EAAUhyB,KAAKG,IAAI6xB,EAASJ,GAAqB/tB,GAClD,CACD,MACMouB,EAAWjyB,KAAKG,IACpB,GACAH,KAAKC,IAAI,IAAKD,KAAKG,IAAI2xB,EAAaE,GAHtB,KAKhBhB,GAAiBnuB,IAAI,IAAWA,EAAMuE,CAACA,GAAY6qB,MAErD,CAAC/0B,EAASC,EAAMy0B,KAGZM,GAAqBhJ,EAAAA,YAAY,KACrChsB,EAAQe,QAAS0C,GAAMkxB,GAAelxB,EAAEtC,OACvC,CAACnB,EAAS20B,KAEPM,GAAejJ,EAAAA,YAAY,KAE/B8H,GAAgB,IAAKE,GAAiB/U,UAEtCkV,GAAiB,IAAI9zB,KAErB+zB,GAAqBzuB,IACnB,MAAMC,EAAgC,CAAA,EAEtC,OADAyc,OAAOC,KAAK3c,GAAM5E,QAASkS,GAAOrN,EAAKqN,IAAK,GACrCrN,IAGTsuB,GAAeD,GAAgBhV,QAAQvS,UACtC,IAEGwoB,GAAkBlV,SAAuB,MACzCmV,GAAkBnV,SAAuB,MAEzCoV,GAAkBpJ,EAAAA,YAAY,CAAC9hB,EAAmBmrB,KACtDlB,GAAkBxuB,IAChB,MAAMqnB,EAAS,IAAI3sB,IAAIsF,GAMvB,OALI0vB,EACFrI,EAAOjhB,IAAI7B,GAEX8iB,EAAOlhB,OAAO5B,GAET8iB,KAER,IAEGsI,GAA+BtJ,EAAAA,YACnC,CAAC9hB,EAAmB0f,KAClBwK,GAAqBzuB,IAAU,IAC1BA,EACHuE,CAACA,GAAY0f,MAGjB,KAIM7R,QAASwd,IAAkBzL,EAAc,CAAE7L,SAAS,KACrDuX,GAAYC,IAAiBzuB,EAAQA,SAAQqqB,IAG9CqE,GACHhG,IAA2BiG,qBAAuB,IAErDxsB,EAAAA,UAAU,KACR,IAAIysB,GAAY,EAChB,IAAK1oB,GAAW/L,IAEd,YADAs0B,GAAcpE,IAGhB,MAAM5U,EAAO4U,GACb,GAAI5U,EAAK3b,OAAS40B,GAAiB,CAEjC,MAAM3K,EAA+B,QAAzB7d,GAAWgI,UAAsB,GAAK,EAC5C8V,EAAM,IAAIvO,GAAMxR,KAAK,CAACrI,EAAGC,KAC7B,MAAMooB,EAAKroB,IAAIsK,GAAW/L,KACpB+pB,EAAKroB,IAAIqK,GAAW/L,KAC1B,OAAU,MAAN8pB,GAAoB,MAANC,EAAmB,EAC3B,MAAND,GAAoB,EAAIF,EAClB,MAANG,EAAmB,EAAIH,EACT,iBAAPE,GAAiC,iBAAPC,GAC3BD,EAAKC,GAAMH,EACd3oB,OAAO6oB,GAAI/f,cAAc9I,OAAO8oB,IAAOH,IAGhD,YADA0K,GAAczK,EAEf,CAOD,OANAuK,GAAc9Y,EAAM,CAClBtb,IAAK+L,GAAW/L,IAChB+T,UAAWhI,GAAWgI,YACrB2gB,KAAM7K,IACF4K,GAAWH,GAAczK,KAEzB,KACL4K,GAAY,IAEb,CAACvE,GAAcnkB,GAAYqoB,GAAeG,KAE7C,MAAMpY,iBACJA,GAAgB+O,cAChBA,GAAa7O,gBACbA,GAAe8O,iBACfA,GAAgBE,qBAChBA,GAAoBT,UACpBA,GAASxO,YACTA,IACEmO,EAAc,CAAEzrB,KAAMu1B,GAAY7J,eAGhCmK,GAAiB7G,GAAazR,IAC7BuY,GAAYC,IAAiBhvB,EAAQA,SAAU8uB,KAC/CG,GAAiBC,IAAsBlvB,EAAQA,UAAU,IACzDmvB,GAAWC,IAAgBpvB,EAAQA,SAAS,GACnDmC,EAAAA,UAAU,KACR,GAAI2sB,GAGF,OAFAE,IAAc,QACdE,IAAmB,GAGrB,GAAIH,GAAY,CACdG,IAAmB,GACnB,MAAMxzB,EAAIie,WAAW,KACnBqV,IAAc,GACdE,IAAmB,IAClB,KACH,MAAO,IAAMG,aAAa3zB,EAC3B,GACA,CAACozB,KAEJ,MAAMQ,GAA2BtK,cAC9B7kB,IACCmqB,GAAgBnqB,EAAEM,OAAOd,QAE3B,CAAC2qB,MAIGiF,YACJA,GACAC,YAAaC,GAAkBC,cAC/BA,GAAaC,YACbA,GAAWC,eACXA,GAAcC,aACdA,GAAYC,qBACZA,GAAoBnhB,UACpBA,GAASohB,gBACTA,GAAeC,kBACfA,IE3YuB,GAAG/2B,OAAMD,cAClC,MAAOu2B,EAAaU,GAAkBjwB,WAAsB,CAC1DkwB,WAAY,GACZC,SAAU,IAAI92B,MAeV+2B,EAAU,UACVC,EAAale,GAAoBA,EAAMiB,KAAK,KAG5Coc,EAAch2B,EAAAA,QAAmB,KACrC,MAAM8hB,EAAOiU,EAAYW,WACzB,IAAK5U,EAAKxhB,OAAQ,OAAOb,EAEzB,MAAMq3B,EAAkB,GAElBC,EAAqB9a,IACzB,MAAMhc,EAA8B,CAAA,EACpC,IAAK,MAAMG,KAAOZ,EAAS,CACzB,IAAKY,EAAI42B,UAAW,SACpB,MAAMx2B,EAASyb,EAAKxb,IAAKw2B,GAAOA,EAAU72B,EAAIO,MAC9C,IAAIu2B,EAAc,KAClB,GAA6B,mBAAlB92B,EAAI42B,UACb,IACEE,EAAS92B,EAAI42B,UAAUx2B,EACxB,CAAC,MAAO8d,GACP4Y,EAAS,IACV,KACI,CAEL,MAAMl2B,EAAcC,MAEd,sBAAsBC,KAAKD,IAC3B,sBAAsBC,KAAKD,IAC3B,wBAAwBC,KAAKD,IAC7B,wBAAwBC,KAAKD,KAG9BA,EAAEE,SAAS,MAAQF,EAAEE,SAAS,OAC/BC,OAAOC,SAASC,KAAKC,MAAMN,KAMzBO,EAAOhB,EACVC,IAAKgB,IACJ,GAAiB,iBAANA,EAAgB,OAAOA,EAClC,GAAS,MAALA,EAAW,OAAOC,IACtB,MAAMC,EAAKC,OAAOH,GAElB,GAAIT,EAAWW,GAAK,OAAOD,IAC3B,MAAMG,EAAIC,WAAWH,EAAGI,QAAQ,KAAM,KACtC,OAAOX,OAAOC,SAASQ,GAAKA,EAAIH,MAEjCvB,OAAQ0B,GAAMT,OAAOC,SAASQ,IAE3BG,EAAiB,IAEJxB,EACdC,IAAKgB,IACJ,GAAIA,aAAaH,KAAM,OAAOG,EAAEQ,UAChC,GAAiB,iBAANR,GAA+B,iBAANA,EAAgB,CAClD,MAAMS,EAAIZ,KAAKC,MAAMK,OAAOH,IAC5B,OAAOL,OAAOC,SAASa,GAAKA,EAAIR,GACjC,CACD,OAAOA,MAERvB,OAAQ+B,GAAMd,OAAOC,SAASa,IAInC,GAAsB,QAAlB9B,EAAI42B,WAAyC,QAAlB52B,EAAI42B,UAG7BE,EAFA11B,EAAKlB,OACe,QAAlBF,EAAI42B,UACGx1B,EAAKW,OAAO,CAACC,EAAGC,IAAMD,EAAIC,EAAG,GAE5Bb,EAAKW,OAAO,CAACC,EAAGC,IAAMD,EAAIC,EAAG,GAC1Bb,EAAKlB,OAIX,UAEN,GAAsB,QAAlBF,EAAI42B,WAAyC,QAAlB52B,EAAI42B,UACxC,GAAIx1B,EAAKlB,OACP42B,EACoB,QAAlB92B,EAAI42B,UAAsB10B,KAAKC,OAAOf,GAAQc,KAAKG,OAAOjB,OACvD,CACL,MAAMgB,EAAWR,IACjB,GAAIQ,EAASlC,OAAQ,CACnB,MAAM62B,EACc,QAAlB/2B,EAAI42B,UACA10B,KAAKC,OAAOC,GACZF,KAAKG,OAAOD,GAClB00B,EAAS,IAAI51B,KAAK61B,EACnB,MACCD,EAAS,IAEZ,CAEJ,CACGA,UACFj3B,EAAOG,EAAIO,KAAOu2B,EAErB,CACD,OAAOj3B,GAGHm3B,EAAU,CAACnb,EAAiBG,EAAeib,KAC/C,MAAMC,EAASxV,EAAK1F,GACd3b,EAAM,IAAIsC,IAChB,IAAK,MAAMk0B,KAAKhb,EAAM,CACpB,MAAMsb,EAAM31B,OAAQq1B,EAAUK,IAAWV,GACnCY,EAAM/2B,EAAI+D,IAAI+yB,GAChBC,EAAKA,EAAIze,KAAKke,GACbx2B,EAAI2C,IAAIm0B,EAAK,CAACN,GACpB,CACD,MAAMlV,EAAUxX,MAAMC,KAAK/J,EAAIshB,WAAWtX,KAAK,EAAErI,IAAKC,KACpDD,EAAEsI,cAAcrI,IAElB,IAAK,MAAO8D,EAAOsxB,KAAc1V,EAAS,CACxC,MACM2V,EAAW,IAAIL,EADT,GAAGC,KAAUnxB,KAEnBwxB,EAAUd,EAAUa,GAa1B,GAZAZ,EAAK/d,KAAK,CACRrT,GAAI,SAASiyB,IACbpe,gBAAgB,EAChBgC,UAAWoc,EACX7b,YAAa2b,EAAUn3B,OACvBmb,YAAaW,EACbR,gBAAiB0b,EACjBzb,YAAa1V,EACb+V,WAAYub,KAGK1B,EAAYY,SAASzzB,IAAIy0B,GAC3B,SACjB,GAAIvb,EAAQ0F,EAAKxhB,OAAS,EACxB82B,EAAQK,EAAWrb,EAAQ,EAAGsb,QAE9B,IAAK,MAAMT,KAAKQ,EAAWX,EAAK/d,KAAKke,GAIvC,MAAMW,EAAMb,EAAkBU,GAC9BX,EAAK/d,KAAK,CACRrT,GAAI,gBAAgBiyB,IACpBne,gBAAgB,EAChB+B,UAAWoc,EACXlc,YAAaW,EACbyb,UAAWD,GAEd,GAIH,OADAR,EAAQ33B,EAAM,EAAG,IACVq3B,GACN,CAACr3B,EAAMs2B,EAAYW,WAAYX,EAAYY,WAsH9C,OAnHAhuB,EAAAA,UAAU,KACR,IAAKotB,EAAYW,WAAWp2B,OAAQ,OAEpC,MAAMwhB,EAAOiU,EAAYW,WACnBoB,EAAW,IAAIj4B,IACfk4B,EAAS,CAAC9b,EAAiBG,EAAezD,KAC9C,MAAM2e,EAASxV,EAAK1F,GACd3b,EAAM,IAAIsC,IAChB,IAAK,MAAMk0B,KAAKhb,EAAM,CACpB,MAAMsb,EAAM31B,OAAQq1B,EAAUK,IAAWV,GACnCY,EAAM/2B,EAAI+D,IAAI+yB,GAChBC,EAAKA,EAAIze,KAAKke,GACbx2B,EAAI2C,IAAIm0B,EAAK,CAACN,GACpB,CACD,IAAK,MAAO9wB,EAAOsxB,KAAch3B,EAAIshB,UAAW,CAC9C,MACM3c,EAAO,IAAIuT,EADL,GAAG2e,KAAUnxB,KAEnBwxB,EAAUd,EAAUzxB,GAC1B0yB,EAASvsB,IAAIosB,GACTvb,EAAQ0F,EAAKxhB,OAAS,GAAGy3B,EAAON,EAAWrb,EAAQ,EAAGhX,EAC3D,GAEH2yB,EAAOt4B,EAAM,EAAG,IAEhB,IAAIu4B,GAAU,EACd,GAAIF,EAAS7xB,OAAS8vB,EAAYY,SAAS1wB,KAAM+xB,GAAU,OAEzD,IAAK,MAAMC,KAAKH,EACd,IAAK/B,EAAYY,SAASzzB,IAAI+0B,GAAI,CAChCD,GAAU,EACV,KACD,CAEDA,GACFvB,EAAgBtxB,QAAeA,EAAMwxB,SAAUmB,MAEhD,CAAC/B,EAAYW,WAAYj3B,IA+ErB,CACLs2B,cACAC,cACAE,cA/EqBxsB,IACrB+sB,EAAe,CACbC,WAAYhtB,EAAY,CAACA,GAAa,GACtCitB,SAAU,IAAI92B,OA6EhBs2B,YAxEmBzsB,IACnB+sB,EAAgBtxB,GACdA,EAAKuxB,WAAWv1B,SAASuI,GACrBvE,EACA,IAAKA,EAAMuxB,WAAY,IAAIvxB,EAAKuxB,WAAYhtB,MAqElD0sB,eAjEsB1sB,IACtB+sB,EAAgBtxB,IACd,MAAM+yB,EAAW/yB,EAAKuxB,WAAWv2B,OAAQsS,GAAMA,IAAM/I,GAC/CyuB,EAAe,IAAIt4B,IACzB,IAAK,MAAMo4B,KAAK9yB,EAAKwxB,SACdsB,EAAE92B,SAAS,GAAGuI,OAAeyuB,EAAa5sB,IAAI0sB,GACrD,MAAO,CAAEvB,WAAYwB,EAAUvB,SAAUwB,MA4D3C9B,aAxDoBK,IACpBD,EAAe,CAAEC,WAAY,IAAIA,GAAaC,SAAU,IAAI92B,OAwD5Dy2B,qBArD4B8B,IAC5B3B,EAAgBtxB,IACd,MAAMC,EAAO,IAAIvF,IAAIsF,EAAKwxB,UAG1B,OAFIvxB,EAAKlC,IAAIk1B,GAAYhzB,EAAKkG,OAAO8sB,GAChChzB,EAAKmG,IAAI6sB,GACP,IAAKjzB,EAAMwxB,SAAUvxB,MAiD9BmxB,gBA5CsB,KACtB,MAAMzU,EAAOiU,EAAYW,WACzB,IAAK5U,EAAKxhB,OAAQ,OAElB,MAAM+3B,EAAiB,CACrBpc,EACAG,EACAzD,EACAxV,KAEA,MAAMm0B,EAASxV,EAAK1F,GACd3b,EAAM,IAAIsC,IAChB,IAAK,MAAMk0B,KAAKhb,EAAM,CACpB,MAAMsb,EAAM31B,OAAQq1B,EAAUK,IAAWV,GACnCY,EAAM/2B,EAAI+D,IAAI+yB,GAChBC,EAAKA,EAAIze,KAAKke,GACbx2B,EAAI2C,IAAIm0B,EAAK,CAACN,GACpB,CACD,IAAK,MAAO9wB,EAAOsxB,KAAch3B,EAAIshB,UAAW,CAC9C,MACM3c,EAAO,IAAIuT,EADL,GAAG2e,KAAUnxB,KAEnBwxB,EAAUd,EAAUzxB,GAC1BjC,EAAIoI,IAAIosB,GACJvb,EAAQ0F,EAAKxhB,OAAS,GACxB+3B,EAAeZ,EAAWrb,EAAQ,EAAGhX,EAAMjC,EAC9C,GAEGA,EAAM,IAAItD,IAChBw4B,EAAe54B,EAAM,EAAG,GAAI0D,GAC5BszB,EAAgBtxB,QAAeA,EAAMwxB,SAAUxzB,MAgB/CqzB,kBAbwB,KACxBC,EAAgBtxB,IAAI,IAAWA,EAAMwxB,SAAU,IAAI92B,QAanDsV,UAAW4gB,EAAYW,WAAWp2B,OAAS,IFkGzCg4B,CAAY,CAAE74B,KAAMu1B,GAAYx1B,YAGpC+4B,EAAmBA,oBACjB3kB,GACA,KAAO,CACLyiB,gBACAF,eACAC,kBACAG,mBACAC,qBACAF,wBACAkC,eAAgB,IAAMzC,GAEtBhF,gBAAiB,CAACrnB,EAAmBvJ,KACnC4wB,GAAgBrnB,EAAWvJ,IAE7B8yB,kBAAoBvpB,IAClBqnB,GAAgBrnB,EAAW,OAE7BsnB,gBAAiB,KACfA,MAEFF,gBAAkB3qB,IAChB2qB,GAAgB3qB,MAGpB,CAAC4vB,GAAahF,GAAiBC,GAAiBF,KAIlD,MAAM2H,GAAyBz4B,EAAAA,QAAQ,IAC9B,IAAIH,IAAID,IACd,CAACA,KAEE84B,GAAmBvN,GAAY1N,QACb,WAApB0N,EAAWO,KACTjsB,EACAosB,GACFmJ,GAEE2D,GAAgBxjB,GAAY8gB,GAAqByC,IAEjD9rB,aAAEA,GAAY2f,gBAAEA,GAAeG,gBAAEA,IAAoBP,EAAa,CACtE1sB,KAAMk5B,GACN1iB,cACAmW,kBACAtsB,aAII84B,GAA0BpZ,SAAOwO,GACvCrlB,EAAAA,UAAU,KACRiwB,GAAwBna,QAAUuP,GACjC,CAACA,IAEJrlB,EAAAA,UAAU,KACJiwB,GAAwBna,SAC1Bma,GAAwBna,QAAQ7R,KAEjC,CAACA,KAGJ,MAAQisB,WAAYC,IJtcG,GACvBr5B,OACAD,UACAoN,eACAmsB,WAAW,OACXlU,uBAGA,MAAMiO,EAAiBjO,EACnBrlB,EAAQW,OAAQC,IAAsC,IAA9BykB,EAAiBzkB,EAAIO,MAC7CnB,EACEw5B,EAAexN,EAAWA,YAC9B,CAACyN,EAAiBC,EAAkBC,KAClC,MAAMxP,EAAO,IAAIC,KAAK,CAACqP,GAAU,CAAElyB,KAAMoyB,IACnCC,EAAMtP,IAAIC,gBAAgBJ,GAC1B0P,EAAOnmB,SAASomB,cAAc,KACpCD,EAAKE,KAAOH,EACZC,EAAKG,SAAWN,EAChBhmB,SAASE,KAAKqmB,YAAYJ,GAC1BA,EAAKK,QACLxmB,SAASE,KAAKumB,YAAYN,GAC1BvP,IAAI8P,gBAAgBR,IAEtB,IAGIS,EAAcrO,EAAAA,YAClB,CAACqN,EAAuBiB,KACtB,MAcMC,EAAa,CAdHjH,EAAeryB,IAAKL,GAAQA,EAAI6E,QAAQ2U,KAAK,QAChDif,EAAWp4B,IAAKC,GAC3BoyB,EACGryB,IAAKL,IACJ,MAAM+F,EAAQzF,EAAIN,EAAIO,KAEhBq5B,EAAcp4B,OAAOuE,GAAS,IACpC,OAAO6zB,EAAY74B,SAAS,MAAQ64B,EAAY74B,SAAS,KACrD,IAAI64B,EAAYj4B,QAAQ,KAAM,SAC9Bi4B,IAELpgB,KAAK,OAG4BA,KAAK,MAC3Cof,EAAae,EAAY,GAAGD,QAAsB,aAEpD,CAAChH,EAAgBkG,IAGbiB,EAAezO,EAAAA,YACnB,CAACqN,EAAuBiB,KACtB,MAAMI,EAAWrB,EAAWp4B,IAAKC,IAC/B,MAAMy5B,EAAgC,CAAA,EAItC,OAHArH,EAAevyB,QAASH,IACtB+5B,EAAS/5B,EAAI6E,QAAUvE,EAAIN,EAAIO,OAE1Bw5B,IAGHC,EAAcC,KAAKC,UAAUJ,EAAU,KAAM,GACnDlB,EAAaoB,EAAa,GAAGN,SAAuB,qBAEtD,CAAChH,EAAgBkG,IAGbuB,EAAe/O,EAAAA,YACnBO,MAAO8M,EAAuBiB,KAE5B,MAAOU,GAASC,OAAEA,UAAkBpQ,QAAQqQ,IAAI,CAC9CC,OAAO,WACPA,OAAO,gBAGHC,EAAW,IAAKJ,EAAQK,SAAWL,GAASM,SAC5CC,EAAQH,EAASI,aAAa,QAI9BC,EAAUpC,EAAW3sB,MAAM,EADZ,IAGfgvB,EAAez5B,IACnB,GAAS,MAALA,EAAW,MAAO,GACtB,GAAiB,iBAANA,EAAgB,OAAOA,EAClC,GAAiB,kBAANA,EAAiB,OAAOA,EACnC,GAAIA,aAAaH,KAAM,OAAOG,EAC9B,MAAMR,EAAIW,OAAOH,GAGjB,GAAI,WAAWP,KAAKD,GAAI,OAAOA,EAG/B,GAAI,mBAAmBC,KAAKD,EAAE6F,QAAS,CACrC,MAAMjF,EAAIT,OAAOH,EAAEc,QAAQ,KAAM,KACjC,GAAIX,OAAOC,SAASQ,GAAI,OAAOA,CAChC,CAED,OAAOZ,GAIHk6B,EAAarI,EAAeryB,IAAKL,IACrC,MAAM6E,EAAS7E,EAAI6E,QAAU7E,EAAIO,IACjC,IAAIy6B,EAASn2B,GAAQ3E,QAAU,GAC/B,IAAK,MAAMI,KAAOu6B,EAAS,CACzB,MAAMI,EAAM36B,EAAIN,EAAIO,KACdM,EAAW,MAAPo6B,EAAc,GAAKz5B,OAAOy5B,GAChCp6B,EAAEX,OAAS86B,IAAQA,EAASn6B,EAAEX,OACnC,CAED,MAAMuC,EAAQP,KAAKG,IAAI,EAAGH,KAAKC,IAAI,GAAID,KAAKspB,KAAc,IAATwP,KACjD,MAAO,CAAEn2B,SAAQtE,IAAKP,EAAIO,IAAKkC,WAGjCk4B,EAAMv7B,QAAU27B,EAGhB,IAAK,MAAMz6B,KAAOm4B,EAAY,CAC5B,MAAMyC,EAA2B,CAAA,EACjC,IAAK,MAAMl7B,KAAO0yB,EAChBwI,EAAIl7B,EAAIO,KAAOu6B,EAAYx6B,EAAIN,EAAIO,MAErCo6B,EAAMQ,OAAOD,EACd,CAGiBP,EAAMS,OAAO,GACrBC,KAAO,CAAEC,MAAM,GAGzBX,EAAMY,WAAa,CACjBnxB,KAAM,CAAE9J,IAAK,EAAGoL,OAAQ,GACxB8vB,GAAI,CAAEl7B,IAAK4B,KAAKG,IAAI,EAAGs4B,EAAMc,UAAW/vB,OAAQgnB,EAAexyB,SAGjE,MAAMw7B,QAAelB,EAASmB,KAAKC,cAInCvB,EAHa,IAAI7Q,KAAK,CAACkS,GAAS,CAC9B/0B,KAAM,sEAEK,GAAG+yB,WAElB,CAAChH,IAmCH,MAAO,CACL+F,WAjCiBrN,EAAWA,YAC5B,CAACljB,EAAsBkgB,GAA0B,KAC/C,MAAMqQ,EACJrQ,GAAkB5b,GAAgBA,EAAa3G,KAAO,EAClDxG,EAAKU,OAAQO,GAAQkM,EAAa1J,IAAIxC,EAAIgF,KAC1CjG,EAEAq6B,EACJtR,GAAkB5b,GAAgBA,EAAa3G,KAAO,EAClD,GAAG8yB,cAAqBnsB,EAAa3G,YACrC8yB,EAEN,OAAQzwB,GACN,IAAK,MACHuxB,EAAYhB,EAAYiB,GACxB,MACF,IAAK,OACHG,EAAapB,EAAYiB,GACzB,MACF,IAAK,OAEES,EAAa1B,EAAYiB,GAC9B,MACF,QACEh5B,QAAQC,KAAK,8BAA8BuH,OAGjD,CAAC7I,EAAMmN,EAAcmsB,EAAUc,EAAaI,EAAcM,IAO1D0B,kBAJwBrvB,GAAgBA,EAAa3G,KAAO,EAK5Di2B,cAAetvB,GAAc3G,MAAQ,IIqRSk2B,CAAU,CAExD18B,KAAMi5B,GACNl5B,UACAoN,gBACAmsB,SAAW5J,IAAqBA,GAAkB4J,UAAa,YAC/DlU,sBAIIuX,GAAsB5Q,EAAAA,YAC1B,CAACljB,EAAakgB,KACZ,MAAM6T,EAAc9xB,MAAMC,KAAKoC,IAAgB,IAEX,WAAlCuiB,IAAmBmN,YAC0B,mBAAtCnN,IAAmBoN,eAE1BpN,GAAkBoN,eAAej0B,EAAQkgB,EAAgB6T,GAGzDvD,GAAwBxwB,EAAQkgB,IAGpC,CACE2G,IAAmBmN,WACnBnN,IAAmBoN,eACnBzD,GACAlsB,KAIE4vB,GAAqBhR,EAAAA,YAAY,KACrCwF,MACC,CAACA,KAEEyL,GAAmBjR,cACtBpjB,IACCkoB,GAAWloB,GACPid,GAAiBA,EAAgBjd,IAEvC,CAACid,IAGGqX,GAAuB18B,EAAAA,QAAQ,MAC9B8V,KACD6iB,GAAcr4B,QAAUkuB,GAE3B,CACD1Y,EACA6iB,GAAcr4B,OACdkuB,EACArZ,GACAkZ,IAIIsO,GAAa38B,EAAAA,QACjB,KAAO,CACL48B,gCAAgC,EAChCC,kCAAmC,GACnC1H,oBAAqB,IACrB2H,mBAAoB,IACpBC,6BAA6B,KAC1B7N,KAEL,CAACA,MAGGrC,UAAEA,GAAS9W,iBAAEA,GAAgBwX,YAAEA,GAAWC,aAAEA,IAChDb,EAAkB,CAChBltB,KAAMk5B,GACN7iB,YAAa4mB,GACb/8B,UAAWgxB,GACX/D,WACAjpB,OAAQA,GAAU,MAItBgF,EAAAA,UAAU,KACR,MAAM0a,EAAS,KACb,MAAMxP,EAAKgZ,GAAUpO,QACrB,IAAK5K,EAAI,OAET,MAAMhD,GAAOgD,EAAGmpB,WAAa,IAAMpM,IAAwB,GAAK,EAChEgF,GAAa/kB,IAEfwS,IACA,MAAMkM,EAAK,IAAIC,eAAenM,GAI9B,OAHI9D,GAAad,SAAS8Q,EAAGE,QAAQlQ,GAAad,SAC9CoO,GAAUpO,SAAS8Q,EAAGE,QAAQ5C,GAAUpO,SAC5CzP,OAAOmE,iBAAiB,SAAUkQ,GAC3B,KACLkM,EAAGG,aACH1gB,OAAOuE,oBAAoB,SAAU8P,KAEtC,CAACuN,KAGJ,MAAOqM,GAAaC,IAAkB12B,EAAQA,SAAC,GACzC22B,GAAiB3d,SAAO,GACxB4d,GAAgB5d,SAAsB,MACtC6d,GAAwB7R,cAAarlB,IACzCg3B,GAAe1e,QAAUtY,EACI,MAAzBi3B,GAAc3e,UAClB2e,GAAc3e,QAAUwK,sBAAsB,KAC5CmU,GAAc3e,QAAU,KACxBye,GAAeC,GAAe1e,aAE/B,KACIvD,GAAeoiB,IAAoB92B,EAAQA,SAAC,GACnDmC,EAAAA,UAAU,KACR,MAAMkL,EAAKgZ,GAAUpO,QACrB,IAAK5K,EAAI,OACT,MAAMwP,EAAS,IAAMia,GAAiBzpB,EAAGyb,aAAe,GACxDjM,IACA,MAAMkM,EAAK,IAAIC,eAAenM,GAE9B,OADAkM,EAAGE,QAAQ5b,GACJ,IAAM0b,EAAGG,cACf,CAAC7C,KAGJlkB,EAAAA,UAAU,KACR,IAAK+lB,EAAgB,OACrB,IAAMvD,GAAY1N,SAA+B,WAApB0N,EAAWO,KAAoB,OAE5D,MAAM6R,EAAiB/9B,EACpBW,OAAQ8C,KAAQA,EAAE+I,YAClBvL,IAAKwC,GAAMA,EAAEtC,KAEhB+tB,EAAe,CACbvY,gBACAxJ,iBACA4wB,oBAED,CAACpnB,GAAcxJ,KAGlBhE,EAAAA,UAAU,KACR,IAAKimB,EAAqB,OAC1B,MAAM4O,EAAe,IAAI39B,IAAIL,EAAQiB,IAAKwC,GAAMA,EAAEtC,MAElD,GAAIiuB,EAAoByE,aAAc,CACpC,MAAMjuB,EAAOyc,OAAO4b,YAClB5b,OAAOE,QAAQ6M,EAAoByE,cAAclzB,OAAO,EAAEsS,KACxD+qB,EAAat6B,IAAIuP,KAGrB6gB,GAAiBnuB,QAAeA,KAASC,IAC1C,CAED,GAAIwpB,EAAoB/J,iBAAkB,CACxC,MAAMzf,EAAOyc,OAAO4b,YAClB5b,OAAOE,QAAQ6M,EAAoB/J,kBAAkB1kB,OAAO,EAAEsS,KAC5D+qB,EAAat6B,IAAIuP,KAGrBmhB,GAAqBzuB,QAAeA,KAASC,IAC9C,CAED,GAAIwpB,EAAoBhvB,cAAe,CACrC,MAAM89B,EAAO9O,EAAoBhvB,cAAcO,OAAQsS,GACrD+qB,EAAat6B,IAAIuP,IAEnBkhB,GAAiB,IAAI9zB,IAAI69B,GAC1B,CAED,GAAI9O,EAAoBliB,WAAY,CAClC,MAAM6d,EACyC,QAA7CqE,EAAoBliB,WAAWgI,WACc,SAA7Cka,EAAoBliB,WAAWgI,UAC3Bka,EAAoBliB,WAAWgI,UAC/B,KACA/T,EAAM4pB,EAAMqE,EAAoBliB,WAAW/L,IAAM,KACvDyyB,GAAc,CAAEzyB,MAAK+T,UAAW6V,GACjC,CAED,GAAIqE,EAAoB+O,QAAS,CAC/B7M,GAAgBlC,EAAoB+O,QAAQxnB,cAAgB,IAC5D,MAAM4L,EAAUF,OAAOE,QACrB6M,EAAoB+O,QAAQhxB,eAAiB,CAAE,GAC/CxM,OAAO,EAAEsS,KAAO+qB,EAAat6B,IAAIuP,IACnC,IAAK,MAAOA,EAAGhR,KAAMsgB,EAASgP,GAAgBte,EAAGhR,EAClD,CAED,GACEmtB,EAAoBhhB,aACpBghB,EAAoBhhB,YAAYtN,OAChC,CACA,MAAMs9B,EAAWhP,EAAoBhhB,YAAYzN,OAAQsS,GACvD+qB,EAAat6B,IAAIuP,IAGborB,EAAYr+B,EACfiB,IAAKwC,GAAMA,EAAEtC,KACbR,OAAQsS,IAAOmrB,EAASz8B,SAASsR,IACpCihB,GAAe,IAAIkK,KAAaC,GACjC,CAGCjP,EAAoBmH,aAAaW,YACjC9H,EAAoBmH,YAAYW,WAAWp2B,OAE3C+1B,GAAazH,EAAoBmH,YAAYW,YACpC9H,EAAoBmH,aAAarsB,WAC1CwsB,GAActH,EAAoBmH,YAAYrsB,WAE5CklB,EAAoBxJ,SACtBkL,GAAW1B,EAAoBxJ,UAGhC,IAGHzc,EAAAA,UAAU,KACR,IAAKgmB,EAAsB,OAC3B,MAAMmP,EAAQ3d,WAAW,KACvBwO,EAAqB,CACnB0E,gBACAxO,oBACAjlB,cAAe2K,MAAMC,KAAK5K,IAC1B8M,cACAqpB,YAAa,CAAEW,WAAYX,GAAYW,YACvCiH,QAAS,CAAExnB,gBAAcxJ,kBACzBiB,eACAwX,cAED,KACH,MAAO,IAAMyQ,aAAaiI,IACzB,CACDnP,EACA0E,GACAxO,GACAjlB,GACA8M,GACAqpB,GACA5f,GACAxJ,GACAiB,GACAwX,KAOF,MAAM2Y,GAAoB/9B,EAAAA,QAAQ,KAEhC,GAAuB,IAAnBR,EAAQc,OAAc,MAAO,GAGjC,MAAM09B,EAAa,IAAIj7B,IACrB6K,GAAYnN,IAAI,CAACgS,EAAGpN,IAAM,CAACoN,EAAGpN,KAS1BmN,EALiBhT,EAAQW,OAC5BC,IAAsC,IAA9BykB,GAAiBzkB,EAAIO,MAK7B8J,KACC,CAACrI,EAAGC,KAAO27B,EAAWx5B,IAAIpC,EAAEzB,MAAQ,IAAMq9B,EAAWx5B,IAAInC,EAAE1B,MAAQ,IAEpEF,IAAKL,IACJ,MAAM69B,EAAY5K,GAAajzB,EAAIO,MAAQP,EAAIyC,MACzCA,EAAQzB,OAAOC,SAASD,OAAO68B,IACjC78B,OAAO68B,GACP,IAEE7pB,GAA4B,IAAjBhU,EAAIgU,SACrB,MAAO,IAAKhU,EAAKyC,QAAOuR,cAOtB8pB,EAAY57B,KAAKG,IACrB,EACA2sB,IAAkB1vB,EAAaL,EAAmB,IAE9C8+B,EAAY3rB,EAAKrQ,OAAO,CAACS,EAAKK,IAAML,EAAMK,EAAEJ,MAAO,GAEzD,GAAIq7B,EAAYC,EAAW,CACzB,MAAMC,EAAW5rB,EAAKrS,OAAQ8C,GAAMA,EAAEo7B,MACtC,GAAID,EAAS99B,OAAS,EAAG,CACvB,MAAMg+B,EAAQJ,EAAYC,EACpBI,EAAcH,EAASj8B,OAAO,CAACgB,EAAKF,KACxC,MAAMu7B,GACO,IAAXv7B,EAAEo7B,KAAgB,EAAsB,iBAAXp7B,EAAEo7B,KAAoBp7B,EAAEo7B,KAAO,EAC9D,OAAOl7B,EAAMb,KAAKG,IAAI,EAAG+7B,IACxB,GAEH,GAAID,EAAc,EAEhB,IAAK,MAAMn+B,KAAOg+B,EAAU,CAC1B,MAMMK,EAAaH,IALJ,IAAbl+B,EAAIi+B,KACA,EACoB,iBAAbj+B,EAAIi+B,KACTj+B,EAAIi+B,KACJ,GAC6BE,EAC/BG,EAAOt+B,EAAIsD,UAAY,EACvBi7B,EAAOv+B,EAAI0E,UAAY1D,OAAOw9B,kBACpCx+B,EAAIyC,MAAQP,KAAKG,IAAIi8B,EAAMp8B,KAAKC,IAAIo8B,EAAMv+B,EAAIyC,MAAQ47B,GACvD,CAEJ,CACF,CAGD,MAAM5J,EAAsB,GACtBgK,EAAwB,GAE9B,IAAK,MAAMz+B,KAAOoS,EACZimB,GAAuBv1B,IAAI9C,EAAIO,KACjCk0B,EAAO9b,KAAK3Y,GAEZy+B,EAAS9lB,KAAK3Y,GAKlB,GAAI21B,GAAYW,YAAcX,GAAYW,WAAWp2B,OAAS,EAAG,CAC/D,MAAMw+B,EAAa,IAAI/7B,IAAIgzB,GAAYW,WAAWj2B,IAAI,CAACgS,EAAGpN,IAAM,CAACoN,EAAGpN,KACpEw5B,EAASp0B,KAAK,CAACrI,EAAGC,KACHy8B,EAAWt6B,IAAIpC,EAAEzB,MAAQS,OAAOw9B,oBAChCE,EAAWt6B,IAAInC,EAAE1B,MAAQS,OAAOw9B,mBAGhD,CAED,MAAO,IAAI/J,KAAWgK,IACrB,CACDr/B,EACA6zB,GACAoF,GACA5T,GACAjX,GACAmoB,GAAYW,WACZtH,GACA1vB,IAIIq/B,GAAgC/+B,EAAAA,QAAQ,IAE1C28B,GAAWC,gCACXmB,GAAkBz9B,OAASq8B,GAAWE,kCAEvC,CACDkB,GAAkBz9B,OAClBq8B,GAAWC,+BACXD,GAAWE,oCAIPmC,GAAYh/B,EAAAA,QAChB,IAAM+9B,GAAkB59B,OAAQ8C,GAAMw1B,GAAuBv1B,IAAID,EAAEtC,MACnE,CAACo9B,GAAmBtF,KAEhBwG,GAAcj/B,EAAAA,QAClB,IAAM+9B,GAAkB59B,OAAQ8C,IAAOw1B,GAAuBv1B,IAAID,EAAEtC,MACpE,CAACo9B,GAAmBtF,KAEhByG,GAAel/B,EAAAA,QAAQ,KAC3B,MAAMm/B,EAASF,GAAYx+B,IAAKwC,GAAOA,EAAEJ,OAAoB,KACvDu8B,EAAS,IAAI70B,MAAM40B,EAAO7+B,OAAS,GAAG++B,KAAK,GACjD,IAAK,IAAIh6B,EAAI,EAAGA,EAAI85B,EAAO7+B,OAAQ+E,IACjC+5B,EAAO/5B,EAAI,GAAK+5B,EAAO/5B,GAAK85B,EAAO95B,GAErC,MAAO,CAAE85B,SAAQC,SAAQz8B,WADNy8B,EAAOD,EAAO7+B,SAAW,IAE3C,CAAC2+B,KAEEnM,GAAiB9yB,EAAAA,QAAQ,KAC7B,IAAK++B,GAA+B,OAAOhB,GAE3C,MAAMuB,EAAU5/B,EAAaL,EAAmB,EAC1CkgC,EAAcP,GAAU78B,OAC5B,CAACS,EAAKK,IAAML,GAAQK,EAAEJ,OAAoB,KAC1C,GAGI28B,EAAal9B,KAAKG,IAAI,EAAGw6B,GAAc,KACvCwC,EAAcxC,GAAc/hB,GAAgB,IAG5C1I,EAAO8sB,EAAUC,EACjBG,EAAYp9B,KAAKG,IAAI,EAAG+8B,EAAahtB,GACrCmtB,EAAar9B,KAAKG,IACtB,EACAH,KAAKC,IAAI28B,GAAav8B,WAAY88B,EAAcjtB,IAuC5CotB,EAnCyB,CAACC,IAC9B,IAAKX,GAAaC,OAAO7+B,OAAQ,OAAO,EACxC,IAAIw/B,EAAK,EACPC,EAAKb,GAAaC,OAAO7+B,OAAS,EAClC0/B,EAAM,EACR,KAAOF,GAAMC,GAAI,CACf,MAAME,EAAOH,EAAKC,GAAO,EACXb,GAAaE,OAAOa,EAAM,IAC3BJ,GACXG,EAAMC,EACNF,EAAKE,EAAM,GAEXH,EAAKG,EAAM,CAEd,CACD,OAAO39B,KAAKG,IAAI,EAAGH,KAAKC,IAAIy9B,EAAKd,GAAaC,OAAO7+B,OAAS,KAoB/C4/B,CAAuBR,GAClCS,EAnBuB,CAACN,IAC5B,IAAKX,GAAaC,OAAO7+B,OAAQ,OAAQ,EACzC,IAAIw/B,EAAK,EACPC,EAAKb,GAAaC,OAAO7+B,OAAS,EAClC0/B,EAAMd,GAAaC,OAAO7+B,OAAS,EACrC,KAAOw/B,GAAMC,GAAI,CACf,MAAME,EAAOH,EAAKC,GAAO,EACZb,GAAaE,OAAOa,IACrBJ,GACVG,EAAMC,EACNH,EAAKG,EAAM,GAEXF,EAAKE,EAAM,CAEd,CACD,OAAO39B,KAAKG,IAAI,EAAGH,KAAKC,IAAIy9B,EAAKd,GAAaC,OAAO7+B,OAAS,KAIjD8/B,CAAqBT,GAC9BU,EACJF,GAAUP,GAAYO,GAAU,EAC5BlB,GAAY/yB,MAAM0zB,EAAUO,EAAS,GACrC,GACN,MAAO,IAAInB,MAAcqB,IACxB,CACDtB,GACAhB,GACAd,GACA/hB,GACAxb,EACAs/B,GACAC,GACAC,KAIIoB,GAAqBtgC,EAAAA,QAAQ,KACjC,MAAMugC,EAAI,IAAIx9B,IAEd,OADAk8B,GAAY1+B,QAAQ,CAAC0C,EAAGoC,IAAMk7B,EAAEn9B,IAAIH,EAAEtC,IAAK0E,IACpCk7B,GACN,CAACtB,MACEuB,cAAEA,GAAaC,eAAEA,IAAmBzgC,EAAOA,QAAC,KAChD,IAAK++B,GACH,MAAO,CAAEyB,cAAe,EAAGC,eAAgB,GAC7C,MAAMC,EAAkB5N,GAAe3yB,OACpC8C,IAAOw1B,GAAuBv1B,IAAID,EAAEtC,MAEvC,IAAK+/B,EAAgBpgC,OACnB,MAAO,CAAEkgC,cAAe,EAAGC,eAAgB,GAC7C,MAAME,EAAWL,GAAmB97B,IAAIk8B,EAAgB,GAAG//B,KACrDigC,EAAUN,GAAmB97B,IACjCk8B,EAAgBA,EAAgBpgC,OAAS,GAAGK,KAE9C,GAAgB,MAAZggC,GAA+B,MAAXC,EACtB,MAAO,CAAEJ,cAAe,EAAGC,eAAgB,GAG7C,MAAO,CAAED,cAFItB,GAAaE,OAAOuB,GAEHF,eADhBvB,GAAav8B,WAAau8B,GAAaE,OAAOwB,EAAU,KAErE,CACD7B,GACAjM,GACA2F,GACA6H,GACApB,KAII2B,GAAqB7gC,EAAAA,QACzB,IAAM+9B,GAAkBr7B,KAAMO,GAAMA,EAAE5C,kBACtC,CAAC09B,KAGHp1B,EAAAA,UAAU,KACR,MAAMm4B,EAAcjU,GAAUpO,QAC9B,IAAKqiB,EAAa,OAElB,IAAIC,GAAkB,EAEtB,MAAMC,EAAe,KACnB,GAAID,EAAiB,OACrBA,GAAkB,EAClB,MAAME,EAAWtM,GAAgBlW,QAC7BwiB,GAAYA,EAASC,aAAeJ,EAAYI,aAClDD,EAASC,WAAaJ,EAAYI,YAEpC7D,GAAsByD,EAAYI,YAClCjY,sBAAsB,KACpB8X,GAAkB,KAUtB,OANAD,EAAY3tB,iBAAiB,SAAU6tB,EAAc,CAAEG,SAAS,IAE5DxM,GAAgBlW,UAClBkW,GAAgBlW,QAAQyiB,WAAaJ,EAAYI,YAG5C,KACLJ,EAAYvtB,oBAAoB,SAAUytB,KAE3C,CAACnU,GAAWgU,KAGf,MAAMO,GAAgBphC,EAAAA,QAAQ,KAC5B,MAAMugC,EAAI,IAAIx9B,IAEd,OADAg7B,GAAkBx9B,QAAQ,CAAC0C,EAAGoC,IAAMk7B,EAAEn9B,IAAIH,EAAEtC,IAAK0E,IAC1Ck7B,GACN,CAACxC,KAEEsD,GAAWrhC,EAAAA,QAAQ,KACvB,MAAMugC,EAAI,IAAIx9B,IAEd,OADAg7B,GAAkBx9B,QAAS0C,GAAMs9B,EAAEn9B,IAAIH,EAAEtC,IAAKsC,IACvCs9B,GACN,CAACxC,KAGEuD,GAAWthC,EAAAA,QACf,IACE24B,GAAcx4B,OAAQ82B,IAAYA,EAAE1d,iBAAmB0d,EAAEzd,gBAC3D,CAACmf,KAEG4I,GAAevhC,EAAAA,QAAQ,KAC3B,MAAMugC,EAAI,IAAIx9B,IAEd,OADAu+B,GAAS/gC,QAAQ,CAAC02B,EAAG5xB,IAAMk7B,EAAEn9B,IAAI6zB,EAAEvxB,GAAIL,IAChCk7B,GACN,CAACe,KAEEE,GAAUxhC,EAAAA,QAAQ,KACtB,MAAMugC,EAAI,IAAIx9B,IAEd,OADAu+B,GAAS/gC,QAAS02B,GAAMsJ,EAAEn9B,IAAI6zB,EAAEvxB,GAAIuxB,IAC7BsJ,GACN,CAACe,KAGEG,GAA4BjiB,SAAsB,MAClDkiB,GAA2BliB,SAAO+O,GAClCoT,GAAiBniB,SAAOyP,IAC9BtmB,EAAAA,UAAU,KACRg5B,GAAeljB,QAAUwQ,IACxB,CAACA,KACJtmB,EAAAA,UAAU,KACR+4B,GAAyBjjB,QAAU8P,GAClC,CAACA,IAIJ,MAAMmI,GAAa12B,EAAAA,QACjB,IAAM+9B,GAAkBt9B,IAAKwC,GAAMA,EAAEtC,KACrC,CAACo9B,KAIGtnB,GAAiBzW,EAAOA,QAC5B,IAAM,CAACqX,EAAwB3N,KAC7B,IAAK2kB,EAAsB,OAAO,EAClC,IAAKwB,KAAoBE,GAAc,OAAO,EAC9C,MAAMkH,EAAIsK,GAAa/8B,IAAI6S,GACrBpU,EAAIm+B,GAAc58B,IAAIkF,GAC5B,GAAS,MAALutB,GAAkB,MAALh0B,EAAW,OAAO,EACnC,MAAM2+B,EAASJ,GAAQh9B,IAAI6S,GACrBwqB,EAASR,GAAS78B,IAAIkF,GAC5B,GAAIk4B,GAAUC,IAAWvT,EAAcsT,EAAQC,GAAS,OAAO,EAC/D,MAAMC,EAAOx/B,KAAKC,IAAIstB,GAAgBvY,SAAUyY,GAAazY,UACvDyqB,EAAOz/B,KAAKG,IAAIotB,GAAgBvY,SAAUyY,GAAazY,UACvD0qB,EAAO1/B,KAAKC,IAAIstB,GAAgB3X,SAAU6X,GAAa7X,UACvD+pB,EAAO3/B,KAAKG,IAAIotB,GAAgB3X,SAAU6X,GAAa7X,UAC7D,OAAO+e,GAAK6K,GAAQ7K,GAAK8K,GAAQ9+B,GAAK++B,GAAQ/+B,GAAKg/B,GAErD,CACE5T,EACAwB,GACAE,GACAwR,GACAH,GACAI,GACAH,GACA/S,IAIE9X,GAAgBxW,EAAOA,QAC3B,IAAM,CAACqX,EAAwB3N,IAC7BimB,IAAatY,QAAUA,GAASsY,IAAajmB,YAAcA,EAC7D,CAACimB,KAIGuS,GAAqB1W,cACxB7kB,IACC,MAAMw7B,EAAStV,GAAUpO,QACzB,IAAK0jB,EAAQ,OACb,MACMC,EADsB9/B,KAAK+/B,IAAI17B,EAAE0K,SAAW/O,KAAK+/B,IAAI17B,EAAE27B,QAC5B37B,EAAE0K,OAAS1K,EAAE27B,OACnC,IAAPF,IACFz7B,EAAEuK,iBACFixB,EAAOI,SAAS,CAAEx9B,KAAMq9B,EAAIvxB,IAAK,EAAGqP,SAAU,WAGlD,IAIIsiB,GAAsBhX,EAAAA,YAC1B,EAAG9qB,MAAKoL,SAAQ8D,YACd,MAAM6yB,EAASlB,GAAa/8B,IAAI9D,EAAIgF,IAC9Bg9B,EAAStB,GAAc58B,IAAIsH,EAAOnL,KAGpCwtB,GAA8B,MAAVsU,GAA4B,MAAVC,IAEnCxvB,SAASE,KAAKuvB,aAAa,qBAC9B/S,GAAe,CAAEvY,MAAO3W,EAAIgF,GAAIgE,UAAWoC,EAAOnL,MAC9CytB,GACFA,EAAkB,CAChB1tB,MACAoL,SACA3F,MAAOzF,EAAIoL,EAAOnL,KAClB2W,SAAUmrB,EACVvqB,SAAUwqB,MAObrU,GACAC,EAAc5tB,EAAKoL,IACH,IAAjB8D,EAAMgzB,SACV3S,GAAexR,SAAU,EACzByR,GAAgBzR,QAAU,CAAEohB,EAAGjwB,EAAMuB,QAAS0xB,EAAGjzB,EAAMmD,SACzC,MAAV0vB,GAA4B,MAAVC,IACtB9S,GAAe,CAAEvY,MAAO3W,EAAIgF,GAAIgE,UAAWoC,EAAOnL,MAClDmvB,GAAmB,CAAExY,SAAUmrB,EAAQvqB,SAAUwqB,IACjD1S,GAAgB,CAAE1Y,SAAUmrB,EAAQvqB,SAAUwqB,IAE9CrS,GAAe,CACbwP,EAAGjwB,EAAMuB,QACT0xB,EAAGjzB,EAAMmD,QACTkJ,KAAM,EACN6mB,KAAM,OAGV,CACE3U,EACAC,EACAC,EACAC,EACAiT,GACAH,KAIE2B,GAAuBvX,EAAAA,YAC3B,EAAG9qB,MAAKoL,SAAQ8D,YACd,IAAKye,EAAsB,OAC3B,IAAK4B,GAAexR,UAAYoR,GAAiB,OACjD,IAAKvB,EAAc5tB,EAAKoL,GAAS,OACjC,MAAM22B,EAASlB,GAAa/8B,IAAI9D,EAAIgF,IAC9Bg9B,EAAStB,GAAc58B,IAAIsH,EAAOnL,KACxC,GAAc,MAAV8hC,GAA4B,MAAVC,EAAgB,OACtC1S,GAAgB,CAAE1Y,SAAUmrB,EAAQvqB,SAAUwqB,IAE9CxS,GAAgBzR,QAAU,CAAEohB,EAAGjwB,EAAMuB,QAAS0xB,EAAGjzB,EAAMmD,SACvD,MAAM+uB,EAAOx/B,KAAKC,IAAIstB,GAAgBvY,SAAUmrB,GAC1CV,EAAOz/B,KAAKG,IAAIotB,GAAgBvY,SAAUmrB,GAC1CT,EAAO1/B,KAAKC,IAAIstB,GAAgB3X,SAAUwqB,GAC1CT,EAAO3/B,KAAKG,IAAIotB,GAAgB3X,SAAUwqB,GAChDrS,GAAe,CACbwP,EAAGjwB,EAAMuB,QACT0xB,EAAGjzB,EAAMmD,QACTkJ,KAAM8lB,EAAOD,EAAO,EACpBgB,KAAMb,EAAOD,EAAO,KAGxB,CACE3T,EACAC,EACAuB,GACA0R,GACAH,KAIJz4B,EAAAA,UAAU,KACR,MAAMq6B,EAAO,KACX/S,GAAexR,SAAU,EACzB4R,GAAe,MACXF,GAAiB1R,UACnBiP,qBAAqByC,GAAiB1R,SACtC0R,GAAiB1R,QAAU,OAI/B,OADAzP,OAAOmE,iBAAiB,UAAW6vB,GAC5B,IAAMh0B,OAAOuE,oBAAoB,UAAWyvB,IAClD,IAGHr6B,EAAAA,UAAU,KACR,MAAMs6B,EAAUt8B,IACTspB,GAAexR,UACpByR,GAAgBzR,QAAU,CAAEohB,EAAGl5B,EAAEwK,QAAS0xB,EAAGl8B,EAAEoM,WAGjD,OADA/D,OAAOmE,iBAAiB,YAAa8vB,GAC9B,IAAMj0B,OAAOuE,oBAAoB,YAAa0vB,IACpD,IAGH,MAAMC,GAAuB1X,EAAAA,YAAY,KACvC,GAAgC,MAA5B2E,GAAiB1R,QAAiB,OACtC,MAAM0kB,EAAO,KAEX,GADAhT,GAAiB1R,QAAU,MACtBwR,GAAexR,QAAS,OAC7B,MAAM5K,EAAKgZ,GAAUpO,QACf2kB,EAAKlT,GAAgBzR,QAC3B,GAAI5K,GAAMuvB,EAAI,CACZ,MAAMtzB,EAAO+D,EAAG7D,wBACVgJ,EAAS,GACTqqB,EAAW,GACjB,IAAIjB,EAAK,EACLkB,EAAK,EACLF,EAAGvD,EAAI/vB,EAAK/K,KAAOiU,EAAQopB,IAAOtyB,EAAK/K,KAAOiU,EAASoqB,EAAGvD,GACrDuD,EAAGvD,EAAI/vB,EAAKa,MAAQqI,IAAQopB,EAAKgB,EAAGvD,GAAK/vB,EAAKa,MAAQqI,IAC3DoqB,EAAGP,EAAI/yB,EAAKe,IAAMmI,EAAQsqB,IAAOxzB,EAAKe,IAAMmI,EAASoqB,EAAGP,GACnDO,EAAGP,EAAI/yB,EAAKgB,OAASkI,IAC5BsqB,EAAKF,EAAGP,GAAK/yB,EAAKgB,OAASkI,IAE7B,MAAMuqB,EAAS9hC,GAAca,KAAKG,KAAK4gC,EAAU/gC,KAAKC,IAAI8gC,EAAU5hC,IACpE2gC,EAAKmB,EAAMnB,GACXkB,EAAKC,EAAMD,GACA,IAAPlB,GAAmB,IAAPkB,GACdzvB,EAAG0uB,SAAS,CAAEx9B,KAAMq9B,EAAIvxB,IAAKyyB,EAAIpjB,SAAU,QAE9C,CACDiQ,GAAiB1R,QAAUwK,sBAAsBka,IAEnDhT,GAAiB1R,QAAUwK,sBAAsBka,IAChD,IAGHx6B,EAAAA,UAAU,KACJsnB,GAAexR,SACjBykB,MAED,CAACA,GAAsBrT,KAG1BlnB,EAAAA,UAAU,KACR,MAAM66B,EAAS78B,IACb,GAAc,WAAVA,EAAEhG,IAKJ,OAJAmvB,GAAmB,MACnBE,GAAgB,MAChBJ,GAAe,WACfS,GAAe,MAGjB,MAAMoT,EAAU98B,EAAE+8B,SACZC,EAAQ,CACZ,UACA,YACA,YACA,cACAxiC,SAASwF,EAAEhG,KACb,IAAK8iC,IAAYE,IAAUtV,EAAsB,OACjD1nB,EAAEuK,iBAEF,IAAIsB,EAAOud,GACX,IAAKvd,EAAM,CACT,IAAImd,GAUF,OAVe,CACf,MAAMsH,EAAIsK,GAAa/8B,IAAImrB,GAAYtY,OACjCpU,EAAIm+B,GAAc58B,IAAImrB,GAAYjmB,WACxC,GAAS,MAALutB,GAAkB,MAALh0B,EAIf,OAHA6sB,GAAmB,CAAExY,SAAU2f,EAAG/e,SAAUjV,IAC5CuP,EAAO,CAAE8E,SAAU2f,EAAG/e,SAAUjV,EAInC,CAGF,CACD,MAAM2gC,EAAStC,GAAShhC,OAAS,EAC3BujC,EAAS9F,GAAkBz9B,OAAS,EAC1C,IAAIwjC,EAAKtxB,EAAK8E,SACV/R,EAAKiN,EAAK0F,SACA,YAAVvR,EAAEhG,MAAmBmjC,EAAKxhC,KAAKG,IAAI,EAAGqhC,EAAK,IACjC,cAAVn9B,EAAEhG,MAAqBmjC,EAAKxhC,KAAKC,IAAIqhC,EAAQE,EAAK,IACxC,cAAVn9B,EAAEhG,MAAqB4E,EAAKjD,KAAKG,IAAI,EAAG8C,EAAK,IACnC,eAAVoB,EAAEhG,MAAsB4E,EAAKjD,KAAKC,IAAIshC,EAAQt+B,EAAK,IACvDyqB,GAAgB,CAAE1Y,SAAUwsB,EAAI5rB,SAAU3S,IAE1C,MAAMsO,EAAKgZ,GAAUpO,QACrB,GAAI5K,EAAI,CACN,MAAM/D,EAAO+D,EAAG7D,wBAChBqgB,GAAe,CACbwP,EAAG/vB,EAAKa,MAAQ,GAChBkyB,EAAG/yB,EAAKgB,OAAS,GACjBmL,KAAM3Z,KAAK+/B,KAAKxS,IAAiBvY,UAAYwsB,GAAMA,GAAM,EACzDhB,KAAMxgC,KAAK+/B,KAAKxS,IAAiB3X,UAAY3S,GAAMA,GAAM,GAE5D,GAGH,OADAyJ,OAAOmE,iBAAiB,UAAWqwB,GAC5B,IAAMx0B,OAAOuE,oBAAoB,UAAWiwB,IAClD,CACDnV,EACAsB,GACAE,GACAE,GACAwR,GACAH,GACAE,GAAShhC,OACTy9B,GAAkBz9B,SAIpBqI,EAAAA,UAAU,KACR,IAAKwlB,EAAkB,OACvB,MAAMgU,EAAStV,GAAUpO,QACnBslB,EAAWrP,GAAgBjW,QA4B3BulB,EAAWr9B,IACf,IAAKgpB,GAAa,OAElB,GAAIzc,SAASE,KAAKuvB,aAAa,mBAAoB,OACnD,MAAMhiC,IAAEA,GAAQgG,EAChB,GACU,cAARhG,GACQ,eAARA,GACQ,YAARA,GACQ,cAARA,EAEA,OACFgG,EAAEuK,iBACF,MAAM+yB,EAAgB1C,GAAa/8B,IAAImrB,GAAYtY,OAC7C6sB,EAAgB9C,GAAc58B,IAAImrB,GAAYjmB,WACpD,GAAqB,MAAjBu6B,GAA0C,MAAjBC,EAAuB,OACpD,IAAIC,EAAUF,EACVG,EAAUF,EACA,cAAVv9B,EAAEhG,MAAqByjC,EAAU9hC,KAAKG,IAAI,EAAGyhC,EAAgB,IACnD,eAAVv9B,EAAEhG,MACJyjC,EAAU9hC,KAAKC,IAAIw7B,GAAkBz9B,OAAS,EAAG4jC,EAAgB,IACrD,YAAVv9B,EAAEhG,MAAmBwjC,EAAU7hC,KAAKG,IAAI,EAAGwhC,EAAgB,IACjD,cAAVt9B,EAAEhG,MACJwjC,EAAU7hC,KAAKC,IAAI++B,GAAShhC,OAAS,EAAG2jC,EAAgB,IAE1D,MAAMvjC,EAAM4gC,GAAS6C,GACfr4B,EAASiyB,GAAkBqG,GAC5B1jC,GAAQoL,IAEb8jB,GAAe,CAAEvY,MAAO3W,EAAIgF,GAAIgE,UAAWoC,EAAOnL,MAC9CytB,GACFA,EAAkB,CAChB1tB,MACAoL,SACA3F,MAAOzF,EAAIoL,EAAOnL,KAClB2W,SAAU6sB,EACVjsB,SAAUksB,IA9DM,EAAC9sB,EAAkBY,KACvC,IAAKiqB,EAAQ,OAEb,MAAMkC,EAAYlC,EAAOmC,aACnBC,EAAYjtB,EAAWqZ,GACvB6T,EAAeD,EAAY5T,GAC7B4T,EAAYpC,EAAOrV,UAAWqV,EAAOrV,UAAYyX,EAC5CC,EAAerC,EAAOrV,UAAYuX,IACzClC,EAAOrV,UAAY0X,EAAeH,GAGpC,MAAMI,EAAY1G,GAAkB7lB,IAAWrV,OAAoB,IACnE,IAAIkC,EAAOrF,EAAaL,EAAmB,EAC3C,IAAK,IAAIgG,EAAI,EAAGA,EAAI6S,EAAU7S,IAC5BN,GAASg5B,GAAkB14B,IAAIxC,OAAoB,IAErD,MAAM8N,EAAQ5L,EAAO0/B,EACfvG,EAAYiE,EAAO7S,YACrBvqB,EAAOo9B,EAAOjB,WAChBiB,EAAOjB,WAAan8B,EACX4L,EAAQwxB,EAAOjB,WAAahD,IACrCiE,EAAOjB,WAAavwB,EAAQutB,GAE1B6F,IAAUA,EAAS7C,WAAaiB,EAAOjB,aA0C3CwD,CAAcP,EAASC,KAGzB,OADAp1B,OAAOmE,iBAAiB,UAAW6wB,GAC5B,IAAMh1B,OAAOuE,oBAAoB,UAAWywB,IAClD,CACD7V,EACAwB,GACA2R,GACAvD,GACApN,GACAjxB,EACA6hC,GACAH,GACAhT,IAIFzlB,EAAAA,UAAU,KACR,IAAK0lB,IAAyBwB,KAAoBE,GAAc,CAC9D,MAAM5F,EAAU,CAAEwa,OAAQ,KAAaC,MAAO,IACxCC,EAAM,OAKZ,YAJIA,IAAQpD,GAA0BhjB,UACpCgjB,GAA0BhjB,QAAUomB,EACpCnD,GAAyBjjB,UAAU0L,IAGtC,CAED,MAAM2a,EAAWxiC,KAAKC,IAAIstB,GAAgBvY,SAAUyY,GAAazY,UAC3DytB,EAASziC,KAAKG,IAAIotB,GAAgBvY,SAAUyY,GAAazY,UACzD0tB,EAAW1iC,KAAKC,IAAIstB,GAAgB3X,SAAU6X,GAAa7X,UAC3D+sB,EAAS3iC,KAAKG,IAAIotB,GAAgB3X,SAAU6X,GAAa7X,UAEzD0sB,EAMD,GACL,IAAK,IAAI3N,EAAI6N,EAAU7N,GAAK8N,EAAQ9N,IAAK,CACvC,MAAMv2B,EAAM4gC,GAASrK,GACrB,GAAKv2B,EACL,IAAK,IAAIuC,EAAI+hC,EAAU/hC,GAAKgiC,EAAQhiC,IAAK,CACvC,MAAM6I,EAASiyB,GAAkB96B,GAC5B6I,IACAwiB,EAAc5tB,EAAKoL,IACxB84B,EAAM7rB,KAAK,CACTrY,MACAoL,SACA3F,MAAOzF,EAAIoL,EAAOnL,KAClB2W,SAAU2f,EACV/e,SAAUjV,IAEb,CACF,CAED,MAAMknB,EAAU,CACdwa,OAAQ,CAAEG,WAAUC,SAAQC,WAAUC,UACtCL,SAEIC,EAAM,GAAGC,KAAYC,KAAUC,KAAYC,SAAcL,EAAMtkC,SACjEukC,IAAQpD,GAA0BhjB,UACtCgjB,GAA0BhjB,QAAUomB,EACpCnD,GAAyBjjB,UAAU0L,KAClC,CACDkE,EACAwB,GACAE,GACAuR,GACA5K,GACApI,IAIF3lB,EAAAA,UAAU,KAER,IAAK0lB,IAAyBF,EAAkB,OAChD,MAAMpa,EAAYgY,MAAOplB,IAEvB,KADaA,EAAEu+B,SAAWv+B,EAAEw+B,UACS,MAAxBx+B,EAAEhG,IAAIkK,cAAuB,OAC1C,MAAMu6B,IAAmBz+B,EAAE+8B,SAErB2B,KAAaxV,KAAmBE,IAChCuV,KAAcnX,IAAoBwB,IACxC,IAAK0V,IAAYC,EAAU,OAG3B,IAAKD,GAAWC,EAAU,CACxB,MAAMC,EAAMv2B,OAAOw2B,eACnB,GAAID,GAAOA,EAAInhC,WAAW9D,OAAS,EAAG,OACtC,MAAMmlC,EAASvyB,SAASwyB,cACxB,GACED,IACoB,UAAnBA,EAAOE,SACa,aAAnBF,EAAOE,SACPF,EAAOG,mBAET,MAEH,CAGDj/B,EAAEuK,iBAEF,MAmEM0zB,EAnEe,MAOnB,MAAMA,EAMD,GACL,GAAIS,GAAWxV,IAAmBE,GAAc,CAC9C,MAAM+U,EAAWxiC,KAAKC,IACpBstB,GAAgBvY,SAChByY,GAAazY,UAETytB,EAASziC,KAAKG,IAClBotB,GAAgBvY,SAChByY,GAAazY,UAET0tB,EAAW1iC,KAAKC,IACpBstB,GAAgB3X,SAChB6X,GAAa7X,UAET+sB,EAAS3iC,KAAKG,IAClBotB,GAAgB3X,SAChB6X,GAAa7X,UAEf,IAAK,IAAI+e,EAAI6N,EAAU7N,GAAK8N,EAAQ9N,IAAK,CACvC,MAAMv2B,EAAM4gC,GAASrK,GACrB,GAAKv2B,EACL,IAAK,IAAIuC,EAAI+hC,EAAU/hC,GAAKgiC,EAAQhiC,IAAK,CACvC,MAAM6I,EAASiyB,GAAkB96B,GAC5B6I,IACAwiB,EAAc5tB,EAAKoL,IACxB84B,EAAM7rB,KAAK,CACTrY,MACAoL,SACA3F,MAAOzF,EAAIoL,EAAOnL,KAClB2W,SAAU2f,EACV/e,SAAUjV,IAEb,CACF,CACF,MAAM,GAAIqiC,GAAY3V,GAAa,CAClC,MAAM8S,EAASlB,GAAa/8B,IAAImrB,GAAYtY,OACtCqrB,EAAStB,GAAc58B,IAAImrB,GAAYjmB,WAC7C,GAAc,MAAV+4B,GAA4B,MAAVC,EAAgB,CACpC,MAAMhiC,EAAM4gC,GAASmB,GACf32B,EAASiyB,GAAkB2E,GAC7BhiC,GAAOoL,GACT84B,EAAM7rB,KAAK,CACTrY,MACAoL,SACA3F,MAAOzF,EAAIoL,EAAOnL,KAClB2W,SAAUmrB,EACVvqB,SAAUwqB,GAGf,CACF,CACD,OAAOkC,GAGKiB,GACd,IAAKjB,EAAMtkC,OAAQ,OAEnB,MAAMwlC,EAAc57B,IAClB,MAAMjJ,EAAW,MAAPiJ,EAAc,GAAKtI,OAAOsI,GACpC,OACEjJ,EAAEE,SAAS,MACXF,EAAEE,SAAS,OACXF,EAAEE,SAAS,OACXF,EAAEE,SAAS,MAEJ,IAAMF,EAAEc,QAAQ,KAAM,MAAQ,IAEhCd,GAIT,IAAIwX,EAAO,GACX,GAAI4sB,GAAWxV,IAAmBE,GAAc,CAC9C,MAAM+U,EAAWxiC,KAAKC,IACpBstB,GAAgBvY,SAChByY,GAAazY,UAETytB,EAASziC,KAAKG,IAClBotB,GAAgBvY,SAChByY,GAAazY,UAET0tB,EAAW1iC,KAAKC,IACpBstB,GAAgB3X,SAChB6X,GAAa7X,UAET+sB,EAAS3iC,KAAKG,IAClBotB,GAAgB3X,SAChB6X,GAAa7X,UAEf,GAAIktB,EAAgB,CAClB,MAAMW,EAAoB,GAC1B,IAAK,IAAI9iC,EAAI+hC,EAAU/hC,GAAKgiC,EAAQhiC,IAAK,CACvC,MAAM6I,EAASiyB,GAAkB96B,GAC5B6I,GACLi6B,EAAQhtB,KAAK+sB,EAAYh6B,EAAe7G,QAAU6G,EAAOnL,KAC1D,CACD8X,GAAQstB,EAAQnsB,KAAK,MAAQ,IAC9B,CACD,IAAK,IAAIqd,EAAI6N,EAAU7N,GAAK8N,EAAQ9N,IAAK,CACvC,MAAMv2B,EAAM4gC,GAASrK,GACrB,IAAKv2B,EAAK,SACV,MAAMslC,EAAiB,GACvB,IAAK,IAAI/iC,EAAI+hC,EAAU/hC,GAAKgiC,EAAQhiC,IAAK,CACvC,MAAM6I,EAASiyB,GAAkB96B,GACjC,IAAK6I,EAAQ,SACb,IAAKwiB,EAAc5tB,EAAKoL,GAAS,CAC/Bk6B,EAAKjtB,KAAK,IACV,QACD,CACD,MAAMsiB,EAAM36B,EAAIoL,EAAOnL,KACvBqlC,EAAKjtB,KAAK+sB,EAAWzK,GACtB,CACD5iB,GAAQutB,EAAKpsB,KAAK,MACdqd,EAAI8N,IAAQtsB,GAAQ,KACzB,CACF,KAAM,CAGLA,EAAOqtB,EADMlB,EAAM,GACIz+B,MACxB,CAED,UACQ8I,UAAUg3B,UAAUC,UAAUztB,EACrC,CAAC,MACA3X,QAAQC,KAAK,+BACd,CAGD,GAAsC,mBAA3B4gC,GAAeljB,QAAwB,CAChD,MAAMkmB,EACJU,GAAWxV,IAAmBE,GAC1B,CACE+U,SAAUxiC,KAAKC,IACbstB,GAAgBvY,SAChByY,GAAazY,UAEfytB,OAAQziC,KAAKG,IACXotB,GAAgBvY,SAChByY,GAAazY,UAEf0tB,SAAU1iC,KAAKC,IACbstB,GAAgB3X,SAChB6X,GAAa7X,UAEf+sB,OAAQ3iC,KAAKG,IACXotB,GAAgB3X,SAChB6X,GAAa7X,WAGjB,KACN,IACEypB,GAAeljB,QAAQ,CACrBhG,OACAksB,SACAC,QACAuB,gBAAiBxB,EACjByB,eAAgBzB,GAEnB,CAAC,MAED,CACF,GAGH,OADA31B,OAAOmE,iBAAiB,UAAWY,GAC5B,IAAM/E,OAAOuE,oBAAoB,UAAWQ,IAClD,CACDsa,EACAF,EACA0B,GACAE,GACAJ,GACA2R,GACAvD,GACAzP,EACAiT,GACAH,GACAxtB,KAGF,MAAMjR,GAAao7B,GAAkB57B,OACnC,CAACS,EAAKxC,IAAQwC,GAAOxC,EAAIyC,OAAS,KAClC,GAkBF,OAfA8F,EAAAA,UAAU,KACY,IAAhBoU,KAGEoO,GAAY1N,SAA+B,WAApB0N,EAAWO,KACpCI,GAAiB,GAEjBP,OAMH,CAACpV,GAAcxJ,KAGhBrJ,EAAAC,KAAA,MAAA,CACEqQ,IAAK2L,GACL/b,UAAU,wHAAuH,uBAAA,EAAAI,SAAA,CAIhIqqB,GAAejiB,GACdnI,MAAC4gB,EACC,CAAAtO,aAAcA,GACdgH,mBAAoB0T,GAAavwB,OACjC4c,gBAAiBJ,GAAiBjQ,UAClCiB,eAAgBqd,GAAYO,KAC5BzO,kBAAmBrQ,GAAa3G,KAChCye,WAAYA,EACZllB,QAASA,EACTmN,cAAeA,GACfkY,iBAAkBA,GAClBlX,eAAgB8mB,GAChB1P,cAAe,CACbtH,SAAU0R,IAAmB1R,UAAW,IAASiH,EACjD4C,QAAS6H,IAAmB7H,SAAW,CAAC,QACxCyR,SAAU5J,IAAmB4J,UAAY,YACzCtQ,SAAU2T,IAEZ7W,kBAAmBsJ,EACnBrJ,mBAAoBsJ,GACpB9J,qBAAsB8Q,GACtB7Q,eAAgBuX,GAChBzvB,eAAgBgkB,GAChB7L,yBAA0B4P,GAC1BlnB,YAAaA,GACbC,oBAAqB6lB,GACrB9zB,cAAeA,GACfulB,iBAAmBzb,IACjB,MAAMy4B,EAAStV,GAAUpO,QACnBslB,EAAWrP,GAAgBjW,QACjC,IAAK0jB,EAAQ,OAEb,MAAM18B,EAAM27B,GAAc58B,IAAIkF,GAC9B,GAAW,MAAPjE,EAAa,OAEjB,MAAMg/B,EAAY1G,GAAkBt4B,IAAM5C,OAAoB,IAC9D,IAAIkC,EAAOrF,EAAaL,EAAmB,EAC3C,IAAK,IAAIgG,EAAI,EAAGA,EAAII,EAAKJ,IACvBN,GAASg5B,GAAkB14B,IAAIxC,OAAoB,IAErD,MAAM8N,EAAQ5L,EAAO0/B,EACfvG,EAAYiE,EAAO7S,YAEzB,GAAIvqB,EAAOo9B,EAAOjB,WAChBiB,EAAOkE,SAAS,CAAEthC,OAAMmb,SAAU,gBAC7B,GAAIvP,EAAQwxB,EAAOjB,WAAahD,EACrCiE,EAAOkE,SAAS,CAAEthC,KAAM4L,EAAQutB,EAAWhe,SAAU,eAChD,CAEL,MAAMomB,EAAShkC,KAAKG,IAClB,EACAsC,EAAOzC,KAAKG,IAAI,GAAIy7B,EAAYuG,GAAY,IAE9CtC,EAAOkE,SAAS,CAAEthC,KAAMuhC,EAAQpmB,SAAU,UAC3C,CACG6jB,IAAUA,EAAS7C,WAAaiB,EAAOjB,aAE7C9b,QAASA,GACTC,gBAAiBoX,GACjBnX,mBAAoBA,IAKxBhiB,EAAAC,KAAA,MAAA,CAAKC,UAAU,mDAEZ4J,IACyB,YAAvB2hB,IACyB,SAAvBA,IACCgH,GAAYW,WAAWp2B,OAAS,IAClCuD,EAAAA,IAACgW,EACC,CAAAra,QAASA,EACTsa,YAAaic,GAAYW,WACzB3c,SAAUqc,GACVpc,UAAWqc,GACXpc,gBAAiBkc,GACjBhc,cAAe4b,GAAYY,SAAS1wB,KAAO,EAC3CiU,kBAAmB,KACb6b,GAAYY,SAAS1wB,KAAO,EAC9BuwB,KAEAD,QAQVjzB,OAAA,MAAA,CACEsQ,IAAKiZ,GACLrpB,UAAW,8DACT6qB,EAAuB,cAAgB,IAEzCkY,SAAU/Y,GAGV5pB,SAAA,CAAAC,EAAAA,IAAA,MAAA,CAAKL,UAAU,mBACbI,SAAAC,EAAAA,IAAA,MAAA,CACEJ,MAAO,CACLC,SAAU,GACRf,IAAcjD,EAAaL,EAAmB,QAIjDuE,SAAA,MACC,MAAM4iC,EAAexH,GACfyH,EAAiB3T,GAAe3yB,OACnC8C,IAAOw1B,GAAuBv1B,IAAID,EAAEtC,MAGvC,OACEkD,EAACqB,IAAAmH,GACCzM,cAAe4mC,EACfl6B,gBAAiBm6B,EACjBl6B,UAAWi0B,GACXh0B,WAAYi0B,GACZh0B,aAAcmkB,GACdlkB,WAAYA,GACZC,cAAeA,GACfjN,WAAYA,EACZkN,aAAcA,GACdC,UAAW8rB,GAAcr4B,OACzBb,KAAMA,EACNqN,OAAQgnB,GACR/mB,eAAgBgkB,GAChB/jB,YAAa,IACX0f,GACE9f,GAAa3G,OAAS0yB,GAAcr4B,QAGxC2M,eAAgBgnB,GAChB/mB,aAAcurB,GACdtrB,YAAaynB,GACbxnB,UAAWA,EACXC,gBAAiB0oB,GAAYW,WAAW,IAAM,KAC9CnpB,iBAAkBwoB,GAAYW,WAC9BlpB,cAAe,CAAC7M,EAAKyE,IACnBA,EAAO+wB,GAAYx1B,GAAOy1B,GAAez1B,GAE3C2M,UAAW4oB,GACXzoB,iBAAkB0mB,GAClBzmB,qBAAsB8mB,GACtB7mB,eAAgB8mB,GAChB7mB,YAAaA,GACbC,oBAAqB6lB,GACrB5lB,eACEqd,GAAY1N,QAAU0N,EAAWO,KAAO,MAI/C,EA9CA,OAkDL7nB,EAAAqB,IAAA,MAAA,CACEzB,MAAO,CACLC,SAAU,GAAGf,IAAcjD,EAAaL,EAAmB,OAC3DsE,OAAQ+4B,GACD/D,GAAcr4B,OAASqwB,GAA1B,KACA,QAKL/sB,SAAC6qB,GAAsC,IAAzBkK,GAAcr4B,OAG3BuD,EAAAqB,IAAA,MAAA,CACEzB,MAAO,CACLuT,UAAW,cACT0lB,GAAuB3mB,GAAiBkB,QAAU,OAEpDyvB,WAAY,YACZC,QAAS,WAGV/iC,SAAA,MACC,MAAMgjC,EAA2B,GACjC,IAAIC,EAAe,GACnB,MAAMC,EAAQ,KACZ,IAAKD,EAAMvmC,OAAQ,OACnB,MAAMK,EAAM,SAASkmC,EAAM,IAAInhC,MAAMmhC,EAAMvmC,UAAUyV,GAAiBkB,UACtE2vB,EAAM7tB,KACJlV,aAAeJ,MAAO,CAAEsjC,QAAStY,EAAY,GAAM,GAAG7qB,SACpDC,EAACqB,IAAA2Q,GACCpW,KAAMonC,EACNrnC,QAASszB,GACTlmB,aAAcA,GACdkJ,aAAa,EACbC,iBAAkB,CAChBoB,WAAY,EACZwC,SAAUktB,EAAMvmC,OAAS,EACzB2W,QAAS,GAEXtX,UAAWgxB,GACXjxB,WAAYA,EACZsW,gBAAiBA,EACjBC,YAAasW,GACb3sB,cAAe64B,GACflsB,UAAWi0B,GACXh0B,WAAYi0B,GACZvqB,SAAUA,EACVC,aAAcA,GACdC,cAAeA,EACfC,iBAAkBA,EAClBC,WAAYA,EACZC,YAAaA,EACbG,kBAAmBA,EACnBF,cAAeA,GACfC,eAAgBA,GAChBE,gBAAiB6rB,GACjB5rB,iBAAkBmsB,GAClBjjC,SAAUA,KA7BJa,IAiCZkmC,EAAQ,IAGV,IAAK,MAAMnmC,KAAO6sB,GAChB,GAAK7sB,EAAY6Y,eACfutB,IACAF,EAAM7tB,KACJlV,EAAAqB,IAAC6V,EAAW,CAEVra,IAAKA,EACLsa,WAAY+a,GAAYY,SAASzzB,IAC/BxC,EAAI6a,WAAa,IAEnBN,SAAUqb,GACVpb,cAAekU,GACf1vB,WAAYA,EACZC,UAAWgxB,GACXxV,eAAiBmc,GACf93B,EAAQ6D,KAAMJ,GAAMA,EAAEtC,MAAQ22B,IAASryB,QACvCqyB,EAEFlc,cAAe,CAACkc,EAAQ+D,KACtB,MAAMj7B,EAAMZ,EAAQ6D,KAAMJ,GAAMA,EAAEtC,MAAQ22B,GAC1C,GAAIl3B,GAAK8D,UACP,IACE,OAAO9D,EAAI8D,UAAUm3B,EACtB,CAAC,MAAO/c,GACP,OAAO1c,OAAOy5B,GAAO,GACtB,CAEH,OAAOz5B,OAAOy5B,GAAO,KAEvBhgB,mBAAoBA,IAxBf3a,EAAIgF,UA2BR,GAAKhF,EAAY8Y,eAGtB,GAFAstB,IAE2B,UAAvB9X,GAAgC,CAElC,MAAMgY,EAAQjJ,GACX59B,OACE8C,KACGA,EAAE+zB,WACHt2B,EAAYm3B,gBACqB7zB,IAAjCtD,EAAYm3B,UAAU50B,EAAEtC,MAE5BF,IAAKwC,IACJ,MAAMgkC,EAAUvmC,EAAYm3B,UAAU50B,EAAEtC,KAClCumC,EAAMjkC,EAAEkkC,oBAAsBlkC,EAAEiB,UAChCkjC,EAAUF,EACZ,MACE,IACE,OAAOA,EAAID,EACZ,CAAC,MACA,OAAOrlC,OAAOqlC,EACf,CACF,EAND,GAOArlC,OAAOqlC,GACX,OACE3jC,eAEEE,UAEE,6GAEFwB,MAAO,GAAG/B,EAAEgC,SAEXrB,SAAA,CAAAX,EAAEgC,OAAM,KAAImiC,IAPRnkC,EAAEtC,OAYfimC,EAAM7tB,KACJlV,MAAA,MAAA,CAEEL,UAAU,oBACVC,MAAO,CACLC,SAAU,GACRf,IAAcjD,EAAaL,EAAmB,OAEhDsE,OAAQ,GAAGgtB,QACZ/sB,SAEDN,EAAAA,YACEE,UAEE,gIAEFkQ,KAAK,cAEL9P,SAAA,CAAAC,EAAAqB,IAAA,MAAA,CAAK1B,UAAU,6DACZwjC,EAAM1mC,OACL,EAEAuD,EAAAA,IAAA,OAAA,CAAML,UAAU,qDAKpBK,EAAAA,IAAM,OAAA,CAAAL,UAAU,uEAAsEI,SAAA,sBAzBnFlD,EAAIgF,IA+Bd,MAECkhC,EAAM7tB,KACJlV,MAAA,MAAA,CAEEL,UAAU,iDACVC,MAAO,CACLC,SAAU,GACRf,IAAcjD,EAAaL,EAAmB,OAEhDsE,OAAQ,GAAGgtB,QACZ/sB,SAEDN,EAAAA,YACEE,UAEE,8FAEFkQ,KAAK,cAEJ9P,SAAA,CAAAlE,GACCmE,EACEqB,IAAA,MAAA,CAAAzB,MAAO,CAAEZ,MAAOxD,GAChBmE,UAAU,aAGbu6B,GAAkBt9B,IAAKL,IACtB,MAAMyC,EAASzC,EAAIyC,OAAoB,IACjCokC,EAAUvmC,EAAYm3B,UACvBn3B,EAAYm3B,UAAUz3B,EAAIO,UAC3BqD,EACEkjC,EACJ9mC,EAAI+mC,oBAAsB/mC,EAAI8D,UAC1Bm3B,OACOr3B,IAAXijC,EACI,GACAC,EACE,MACE,IACE,OAAOA,EAAID,EACZ,CAAC,MACA,OAAOA,CACR,CACF,EAND,GAOAA,EAGR,IAAIG,EACJ,GAAmB,iBAAR/L,EACT+L,EAAUhmC,OAAOimC,UAAUhM,GACvBz5B,OAAOy5B,GACPA,EAAIh3B,QAAQ,QACX,GACJyD,MAAMuzB,IACC,KAARA,GACQ,OAARA,EAQA+L,EAAUxlC,OAAOy5B,GAAO,QAPxB,CAEA,MAAMiM,EAAMlmC,OAAOi6B,GACnB+L,EAAUhmC,OAAOimC,UAAUC,GACvB1lC,OAAO0lC,GACPA,EAAIjjC,QAAQ,EACjB,CAGD,OACER,EAIEqB,IAAA,MAAA,CAAA1B,UAAU,8FACVC,MAAO,CACLZ,QACAa,SAAUb,EACViC,SAAUjC,GAEZmC,MACEoiC,EACI,GAAGhnC,EAAI6E,WAAWmiC,SAClBpjC,EAGNJ,SAAAC,MAAA,OAAA,CAAML,UAAU,kBACbI,SAAAwjC,KAhBE,YAAa1mC,EAAY6a,aAC5Bnb,EAAIO,aAhETD,EAAIgF,UAyFfmhC,EAAM9tB,KAAKrY,GAIf,OADAomC,IACOF,CACR,EApPA,KAXH/iC,MAAA4P,EAAAA,SAAA,CAAA,QAqQL8hB,IACC1xB,EAAAqB,IAAA,MAAA,CACE1B,UAAW,qFACTiyB,GACI,4CACA,eAENhyB,MAAO,CAAEoN,IAAK8kB,IAEd/xB,SAAAC,MAAA,MAAA,CAAKL,UAAU,qEAIjBirB,GAAsC,IAAzBkK,GAAcr4B,QAC3BuD,EAAAqB,IAAA,MAAA,CAAK1B,UAAU,4EAA2EI,SACxFC,MAAK,MAAA,CAAAL,UAAU,sBACbI,SAAAC,EAAAqB,IAACmX,EAAa,CACZC,aACInG,IAAgB0L,OAAOC,KAAKnV,IAAerM,OAAS,EAExDic,QAAS9c,EAAKa,OAAS,WAQhCy9B,GAAkBr7B,KAAMtC,GAAQA,EAAIC,mBACnCwD,EAAAA,IACE,MAAA,CAAA+P,IAAK+gB,GACLnxB,UAAU,8EACVC,MAAO,CAAE8jC,eAAgB,OAAQC,gBAAiB,QAClDC,QAASvF,GAAkBt+B,SAE3BC,EACEqB,IAAA,MAAA,CAAA1B,UAAU,4EACVC,MAAO,CACLC,SAAU,GAAGf,IAAcjD,EAAaL,EAAmB,OAC3DsE,OAAQ,GAAGgtB,QACZ/sB,SAEDC,EAAAA,IAACvE,EACC,CAAAE,QAASu+B,GACTt+B,KAAM6hC,GACN5hC,WAAYA,EACZC,UAAWgxB,GACX/wB,cAAe64B,SAQvB50B,EAAAA,IAACgZ,EACC,CAAAC,iBAAkBA,GAClBC,YAAaD,GAAiBC,YAC9BC,gBAAiBA,GACjBC,kBAAmBrQ,GAAa3G,KAChCiX,gBAAiBzd,EAAKa,OACtB6c,mBACuB,WAArBgO,GAAYO,UACR1nB,EACA6sB,GAAavwB,OAEnBwN,eAAgBqd,EAAWO,KAC3BtO,aAAc0O,GACdzO,iBAAkB2O,KAInBoE,IACCvsB,EACEqB,IAAA,MAAA,CAAA1B,UAAU,6CACVC,MAAO,CAAEoN,IAAKuf,GAAYyS,EAAI,GAAI99B,KAAMqrB,GAAYyP,EAAI,IAExDj8B,SAAAN,EAAAA,KAAA,MAAA,CAAKE,UAAU,wEAAuEI,SAAA,CACnFwsB,GAAYnU,KAAS,MAAAmU,GAAY0S,YAM9C,kLGnoEsC,EACpCrjC,OACAD,cAEA,MAAOkN,EAAY0mB,GAAiB5sB,WAAqB,CACvD7F,IAAK,KACL+T,UAAW,QAENipB,EAAS+J,GAAclhC,EAAQA,SAAyB,CAAE,IAC1D2P,EAAc2a,GAAmBtqB,EAAQA,SAAS,IAGnD0qB,EAAuBC,mBAAiBhb,GACxCwxB,EAAkBxW,mBAAiBwM,GAGnC3I,EAAah1B,EAAAA,QAAQ,IACpB0M,EAAW/L,IAET,IAAIlB,GAAMgL,KAAK,CAACrI,EAAGC,KACxB,MAAMulC,EAASxlC,EAAEsK,EAAW/L,KAAO,IAC7BknC,EAASxlC,EAAEqK,EAAW/L,KAAO,IAEnC,GAAc,MAAVinC,GAA4B,MAAVC,EAAgB,OAAO,EAC7C,GAAc,MAAVD,EAAgB,MAAgC,QAAzBl7B,EAAWgI,WAAuB,EAAI,EACjE,GAAc,MAAVmzB,EAAgB,MAAgC,QAAzBn7B,EAAWgI,UAAsB,GAAK,EAEjE,GAAsB,iBAAXkzB,GAAyC,iBAAXC,EACvC,MAAgC,QAAzBn7B,EAAWgI,UACdkzB,EAAOl9B,cAAcm9B,GACrBA,EAAOn9B,cAAck9B,GAG3B,GAAsB,iBAAXA,GAAyC,iBAAXC,EACvC,MAAgC,QAAzBn7B,EAAWgI,UACdkzB,EAASC,EACTA,EAASD,EAIf,MAAME,EAAOlmC,OAAOgmC,GACdG,EAAOnmC,OAAOimC,GACpB,MAAgC,QAAzBn7B,EAAWgI,UACdozB,EAAKp9B,cAAcq9B,GACnBA,EAAKr9B,cAAco9B,KA3BGroC,EA6B3B,CAACA,EAAMiN,IAGJmkB,EAAe7wB,EAAAA,QAAQ,KAC3B,IAAIC,EAAS+0B,EAiBb,GAdAnT,OAAOE,QAAQ4lB,GAAiBpnC,QAAQ,EAAEI,EAAKwF,MAC7C,GAAIA,EAAO,CACT,MAAM6hC,EAAa7hC,EAAM0E,cACzB5K,EAASA,EAAOE,OAAQO,IACtB,MAAMunC,EAAYvnC,EAAIC,GACtB,OACe,MAAbsnC,GACArmC,OAAOqmC,GAAWp9B,cAAc1J,SAAS6mC,IAG9C,IAIC9W,EAAsB,CACxB,MAAMgX,EAAoBhX,EAAqBrmB,cAC/C5K,EAASA,EAAOE,OAAQO,GAEflB,EAAQkD,KAAMoJ,IACnB,MAAM3F,EAAQzF,EAAIoL,EAAOnL,KACzB,OACW,MAATwF,GACAvE,OAAOuE,GAAO0E,cAAc1J,SAAS+mC,KAI5C,CAED,OAAOjoC,GACN,CAAC+0B,EAAY2S,EAAiBzW,EAAsB1xB,IAGjDs0B,EAAatI,cAAa7qB,IAC9ByyB,EAAejuB,IAAU,CACvBxE,MACA+T,UAAWvP,EAAKxE,MAAQA,GAA0B,QAAnBwE,EAAKuP,UAAsB,OAAS,UAEpE,IAEGyzB,EAAe3c,EAAAA,YAAY,CAAC7qB,EAAawF,KAC7CuhC,EAAYviC,IAAI,IAAWA,EAAMxE,CAACA,GAAMwF,MACvC,IAGGiiC,EAA2BpoC,EAAAA,QAC/B,IA/GJ,SAAqDqoC,EAASC,GAC5D,IAAIC,EACJ,MAAQ,IAAIC,KACV3S,aAAa0S,GACbA,EAAUpoB,WAAW,IAAMkoB,KAAQG,GAAOF,EAC3C,CACH,CAyGUG,CAAUtiC,GAAkB2qB,EAAgB3qB,GAAQ,KAC1D,IAGI2vB,EAA2BtK,cAC9B7kB,IACCyhC,EAAyBzhC,EAAEM,OAAOd,QAEpC,CAACiiC,IAGH,MAAO,CACL17B,aACAixB,UACAxnB,eACA0a,eACAiD,aACAqU,eACArS"}