@nextelco/common-ui 2.1.106 → 2.1.107

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 (177) hide show
  1. package/dist/esm/index.js +201 -145
  2. package/dist/esm/index.js.map +1 -1
  3. package/dist/esm/legacy/components/atoms/InputData/InputData.js +2 -13
  4. package/dist/esm/legacy/components/atoms/InputData/InputData.js.map +1 -1
  5. package/dist/esm/legacy/components/charts/Charts.js +147 -57
  6. package/dist/esm/legacy/components/charts/Charts.js.map +1 -1
  7. package/dist/esm/legacy/components/layout/Layout/Layout.js +21 -82
  8. package/dist/esm/legacy/components/layout/Layout/Layout.js.map +1 -1
  9. package/dist/esm/legacy/components/layout/Sidebar/Sidebar.js +29 -218
  10. package/dist/esm/legacy/components/layout/Sidebar/Sidebar.js.map +1 -1
  11. package/dist/esm/legacy/components/molecules/ColumnPicker/ColumnPicker.js +77 -10
  12. package/dist/esm/legacy/components/molecules/ColumnPicker/ColumnPicker.js.map +1 -1
  13. package/dist/esm/legacy/components/molecules/DataTable/DataTable.js +445 -337
  14. package/dist/esm/legacy/components/molecules/DataTable/DataTable.js.map +1 -1
  15. package/dist/esm/legacy/components/molecules/UserAvatar/UserAvatar.js +15 -97
  16. package/dist/esm/legacy/components/molecules/UserAvatar/UserAvatar.js.map +1 -1
  17. package/dist/esm/legacy/components/organisms/MapaPontos/MapaPontos.js +3 -3
  18. package/dist/esm/legacy/components/organisms/MapaPontos/MapaPontos.js.map +1 -1
  19. package/dist/esm/legacy/components/organisms/PainelFiltros/PainelFiltros.js +15 -3
  20. package/dist/esm/legacy/components/organisms/PainelFiltros/PainelFiltros.js.map +1 -1
  21. package/dist/esm/legacy/components/organisms/Selector/Selector.js +4 -4
  22. package/dist/esm/legacy/components/organisms/Selector/Selector.js.map +1 -1
  23. package/dist/esm/legacy/components/ui/Avisos.js +23 -76
  24. package/dist/esm/legacy/components/ui/Avisos.js.map +1 -1
  25. package/dist/esm/legacy/components/ui/CabecalhoPagina.js +14 -27
  26. package/dist/esm/legacy/components/ui/CabecalhoPagina.js.map +1 -1
  27. package/dist/esm/legacy/components/ui/CaixaPesquisa.js +3 -51
  28. package/dist/esm/legacy/components/ui/CaixaPesquisa.js.map +1 -1
  29. package/dist/esm/legacy/components/ui/Confirmacao.js +13 -31
  30. package/dist/esm/legacy/components/ui/Confirmacao.js.map +1 -1
  31. package/dist/esm/legacy/components/ui/Escolha.js +21 -137
  32. package/dist/esm/legacy/components/ui/Escolha.js.map +1 -1
  33. package/dist/esm/legacy/components/ui/FaixaAviso.js +13 -11
  34. package/dist/esm/legacy/components/ui/FaixaAviso.js.map +1 -1
  35. package/dist/esm/legacy/components/ui/Separadores.js +11 -46
  36. package/dist/esm/legacy/components/ui/Separadores.js.map +1 -1
  37. package/dist/esm/legacy/components/ui/Ui.js +18 -115
  38. package/dist/esm/legacy/components/ui/Ui.js.map +1 -1
  39. package/dist/esm/legacy/components/ui/painelFlutuante.js +5 -44
  40. package/dist/esm/legacy/components/ui/painelFlutuante.js.map +1 -1
  41. package/dist/esm/v1/capabilities/overlay/useFloatingPanel.js +52 -0
  42. package/dist/esm/v1/capabilities/overlay/useFloatingPanel.js.map +1 -0
  43. package/dist/esm/v1/capabilities/theme/useIsDark.js +15 -0
  44. package/dist/esm/v1/capabilities/theme/useIsDark.js.map +1 -0
  45. package/dist/esm/v1/capabilities/theme/useTheme.js +66 -0
  46. package/dist/esm/v1/capabilities/theme/useTheme.js.map +1 -0
  47. package/dist/esm/v1/components/data/Pagination/Pagination.js +66 -0
  48. package/dist/esm/v1/components/data/Pagination/Pagination.js.map +1 -0
  49. package/dist/esm/v1/components/feedback/Toast/Toast.js +105 -0
  50. package/dist/esm/v1/components/feedback/Toast/Toast.js.map +1 -0
  51. package/dist/esm/v1/components/layout/AppShell/AppShell.js +89 -0
  52. package/dist/esm/v1/components/layout/AppShell/AppShell.js.map +1 -0
  53. package/dist/esm/v1/components/layout/Page/Page.js +16 -0
  54. package/dist/esm/v1/components/layout/Page/Page.js.map +1 -0
  55. package/dist/esm/v1/components/layout/PageHeader/PageHeader.js +26 -0
  56. package/dist/esm/v1/components/layout/PageHeader/PageHeader.js.map +1 -0
  57. package/dist/esm/v1/components/layout/SideNav/SideNav.js +277 -0
  58. package/dist/esm/v1/components/layout/SideNav/SideNav.js.map +1 -0
  59. package/dist/esm/v1/components/layout/navTrail.js +19 -0
  60. package/dist/esm/v1/components/layout/navTrail.js.map +1 -0
  61. package/dist/esm/v1/components/overlays/ConfirmDialog/ConfirmDialog.js +95 -0
  62. package/dist/esm/v1/components/overlays/ConfirmDialog/ConfirmDialog.js.map +1 -0
  63. package/dist/esm/v1/components/overlays/Dialog/Dialog.js.map +1 -1
  64. package/dist/esm/v1/components/overlays/Tooltip/Tooltip.js +98 -0
  65. package/dist/esm/v1/components/overlays/Tooltip/Tooltip.js.map +1 -0
  66. package/dist/esm/v1/components/overlays/Tooltip/TooltipLayer.js +84 -0
  67. package/dist/esm/v1/components/overlays/Tooltip/TooltipLayer.js.map +1 -0
  68. package/dist/esm/v1/components/overlays/Tooltip/TooltipPanel.js +33 -0
  69. package/dist/esm/v1/components/overlays/Tooltip/TooltipPanel.js.map +1 -0
  70. package/dist/esm/v1/components/overlays/topLayer.js +28 -0
  71. package/dist/esm/v1/components/overlays/topLayer.js.map +1 -0
  72. package/dist/esm/v1/foundations/icons/icons.js +61 -2
  73. package/dist/esm/v1/foundations/icons/icons.js.map +1 -1
  74. package/dist/esm/v1/primitives/buttons/Button/Button.js +18 -4
  75. package/dist/esm/v1/primitives/buttons/Button/Button.js.map +1 -1
  76. package/dist/esm/v1/primitives/buttons/SegmentedControl/SegmentedControl.js +65 -0
  77. package/dist/esm/v1/primitives/buttons/SegmentedControl/SegmentedControl.js.map +1 -0
  78. package/dist/esm/v1/primitives/buttons/SegmentedControl/segmentedStyles.js +10 -0
  79. package/dist/esm/v1/primitives/buttons/SegmentedControl/segmentedStyles.js.map +1 -0
  80. package/dist/esm/v1/primitives/display/Avatar/Avatar.js +120 -0
  81. package/dist/esm/v1/primitives/display/Avatar/Avatar.js.map +1 -0
  82. package/dist/esm/v1/primitives/display/Badge/Badge.js +23 -0
  83. package/dist/esm/v1/primitives/display/Badge/Badge.js.map +1 -0
  84. package/dist/esm/v1/primitives/display/Card/Card.js +22 -0
  85. package/dist/esm/v1/primitives/display/Card/Card.js.map +1 -0
  86. package/dist/esm/v1/primitives/display/FilterChips/FilterChips.js +45 -0
  87. package/dist/esm/v1/primitives/display/FilterChips/FilterChips.js.map +1 -0
  88. package/dist/esm/v1/primitives/display/Kpi/Kpi.js +103 -0
  89. package/dist/esm/v1/primitives/display/Kpi/Kpi.js.map +1 -0
  90. package/dist/esm/v1/primitives/display/Notice/Notice.js +71 -0
  91. package/dist/esm/v1/primitives/display/Notice/Notice.js.map +1 -0
  92. package/dist/esm/v1/primitives/display/Person/Person.js +14 -0
  93. package/dist/esm/v1/primitives/display/Person/Person.js.map +1 -0
  94. package/dist/esm/v1/primitives/forms/Checkbox/Checkbox.js +1 -1
  95. package/dist/esm/v1/primitives/forms/Checkbox/Checkbox.js.map +1 -1
  96. package/dist/esm/v1/primitives/forms/DateInput/DateInput.js +150 -0
  97. package/dist/esm/v1/primitives/forms/DateInput/DateInput.js.map +1 -0
  98. package/dist/esm/v1/primitives/forms/SearchInput/SearchInput.js +43 -0
  99. package/dist/esm/v1/primitives/forms/SearchInput/SearchInput.js.map +1 -0
  100. package/dist/esm/v1/primitives/forms/SelectInput/SelectInput.js +190 -12
  101. package/dist/esm/v1/primitives/forms/SelectInput/SelectInput.js.map +1 -1
  102. package/dist/esm/v1/primitives/forms/field.js +4 -2
  103. package/dist/esm/v1/primitives/forms/field.js.map +1 -1
  104. package/dist/esm/v1/primitives/navigation/Tabs/Tabs.js +96 -0
  105. package/dist/esm/v1/primitives/navigation/Tabs/Tabs.js.map +1 -0
  106. package/dist/style.components.css +1 -1
  107. package/dist/style.css +1 -1
  108. package/dist/style.layered.css +1 -1
  109. package/dist/tailwind.css +24 -0
  110. package/dist/theme-boot.js +1 -0
  111. package/dist/theme.css +10 -3
  112. package/dist/tokens.css +119 -0
  113. package/dist/types/legacy/components/atoms/InputData/InputData.d.ts +4 -1
  114. package/dist/types/legacy/components/charts/Charts.d.ts +58 -5
  115. package/dist/types/legacy/components/layout/Layout/Layout.d.ts +7 -20
  116. package/dist/types/legacy/components/layout/Sidebar/Sidebar.d.ts +13 -26
  117. package/dist/types/legacy/components/molecules/ColumnPicker/ColumnPicker.d.ts +56 -3
  118. package/dist/types/legacy/components/molecules/DataTable/DataTable.d.ts +72 -7
  119. package/dist/types/legacy/components/molecules/UserAvatar/UserAvatar.d.ts +4 -5
  120. package/dist/types/legacy/components/ui/Avisos.d.ts +6 -16
  121. package/dist/types/legacy/components/ui/CabecalhoPagina.d.ts +6 -20
  122. package/dist/types/legacy/components/ui/CaixaPesquisa.d.ts +2 -4
  123. package/dist/types/legacy/components/ui/Confirmacao.d.ts +3 -10
  124. package/dist/types/legacy/components/ui/Escolha.d.ts +5 -15
  125. package/dist/types/legacy/components/ui/FaixaAviso.d.ts +2 -6
  126. package/dist/types/legacy/components/ui/Separadores.d.ts +3 -10
  127. package/dist/types/legacy/components/ui/Ui.d.ts +12 -54
  128. package/dist/types/legacy/components/ui/painelFlutuante.d.ts +6 -10
  129. package/dist/types/legacy/exports/atoms.d.ts +1 -1
  130. package/dist/types/legacy/exports/charts.d.ts +2 -2
  131. package/dist/types/legacy/exports/hooks.d.ts +2 -2
  132. package/dist/types/legacy/exports/molecules.d.ts +2 -2
  133. package/dist/types/v1/capabilities/overlay/useFloatingPanel.d.ts +20 -0
  134. package/dist/types/v1/capabilities/theme/useIsDark.d.ts +10 -0
  135. package/dist/types/v1/capabilities/theme/useTheme.d.ts +29 -0
  136. package/dist/types/v1/components/data/Pagination/Pagination.d.ts +33 -0
  137. package/dist/types/v1/components/feedback/Toast/Toast.d.ts +51 -0
  138. package/dist/types/v1/components/layout/AppShell/AppShell.d.ts +35 -0
  139. package/dist/types/v1/components/layout/Page/Page.d.ts +23 -0
  140. package/dist/types/v1/components/layout/PageHeader/PageHeader.d.ts +35 -0
  141. package/dist/types/v1/components/layout/SideNav/SideNav.d.ts +92 -0
  142. package/dist/types/v1/components/layout/navTrail.d.ts +15 -0
  143. package/dist/types/v1/components/overlays/ConfirmDialog/ConfirmDialog.d.ts +59 -0
  144. package/dist/types/v1/components/overlays/Dialog/Dialog.d.ts +3 -2
  145. package/dist/types/v1/components/overlays/Tooltip/Tooltip.d.ts +34 -0
  146. package/dist/types/v1/components/overlays/Tooltip/TooltipLayer.d.ts +18 -0
  147. package/dist/types/v1/components/overlays/Tooltip/TooltipPanel.d.ts +23 -0
  148. package/dist/types/v1/components/overlays/topLayer.d.ts +23 -0
  149. package/dist/types/v1/foundations/icons/icons.d.ts +20 -0
  150. package/dist/types/v1/index.d.ts +43 -1
  151. package/dist/types/v1/primitives/buttons/Button/Button.d.ts +20 -5
  152. package/dist/types/v1/primitives/buttons/SegmentedControl/SegmentedControl.d.ts +28 -0
  153. package/dist/types/v1/primitives/buttons/SegmentedControl/segmentedStyles.d.ts +16 -0
  154. package/dist/types/v1/primitives/display/Avatar/Avatar.d.ts +30 -0
  155. package/dist/types/v1/primitives/display/Badge/Badge.d.ts +25 -0
  156. package/dist/types/v1/primitives/display/Card/Card.d.ts +22 -0
  157. package/dist/types/v1/primitives/display/FilterChips/FilterChips.d.ts +33 -0
  158. package/dist/types/v1/primitives/display/Kpi/Kpi.d.ts +68 -0
  159. package/dist/types/v1/primitives/display/Notice/Notice.d.ts +29 -0
  160. package/dist/types/v1/primitives/display/Person/Person.d.ts +15 -0
  161. package/dist/types/v1/primitives/forms/DateInput/DateInput.d.ts +43 -0
  162. package/dist/types/v1/primitives/forms/SearchInput/SearchInput.d.ts +23 -0
  163. package/dist/types/v1/primitives/forms/SelectInput/SelectInput.d.ts +60 -12
  164. package/dist/types/v1/primitives/forms/field.d.ts +2 -0
  165. package/dist/types/v1/primitives/navigation/Tabs/Tabs.d.ts +58 -0
  166. package/package.json +3 -3
  167. package/dist/esm/legacy/components/atoms/Tooltip/Tooltip.css +0 -17
  168. package/dist/esm/legacy/components/atoms/Tooltip/Tooltip.js +0 -41
  169. package/dist/esm/legacy/components/atoms/Tooltip/Tooltip.js.map +0 -1
  170. package/dist/esm/legacy/components/molecules/Search/Search.css +0 -18
  171. package/dist/esm/legacy/components/molecules/Search/Search.js +0 -30
  172. package/dist/esm/legacy/components/molecules/Search/Search.js.map +0 -1
  173. package/dist/esm/legacy/hooks/useTheme.js +0 -39
  174. package/dist/esm/legacy/hooks/useTheme.js.map +0 -1
  175. package/dist/types/legacy/components/atoms/Tooltip/Tooltip.d.ts +0 -13
  176. package/dist/types/legacy/components/molecules/Search/Search.d.ts +0 -7
  177. package/dist/types/legacy/hooks/useTheme.d.ts +0 -17
@@ -1 +1 @@
1
- {"version":3,"file":"DataTable.js","sources":["../../../../../../src/legacy/components/molecules/DataTable/DataTable.tsx"],"sourcesContent":["import React, { Fragment, useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react'\r\nimport Escolha from '../../ui/Escolha'\r\nimport { createPortal } from 'react-dom'\r\n\r\n// ─── Tipos ──────────────────────────────────────────────────────────────────\r\n\r\n/** @deprecated Legacy — não usar em código novo. Ver Storybook › Legacy. */\r\nexport interface DataTableColumn<T> {\r\n key: string\r\n label: string\r\n /** Valor usado p/ ordenar, filtrar e exportar. Sem isto usa-se row[key]. */\r\n value?: (row: T) => string | number | null | undefined\r\n /** Conteúdo apresentado. Sem isto mostra o value. */\r\n render?: (row: T) => React.ReactNode\r\n sortType?: 'text' | 'number' | 'date'\r\n /**\r\n * Fixa a coluna no scroll horizontal (position: sticky).\r\n * `true` ou `'left'` prende à esquerda; `'right'` prende à direita — útil para\r\n * a coluna de ações, que de outro modo fica fora de vista (e com os botões\r\n * cortados) assim que a tabela e mais larga do que o espaço disponível.\r\n */\r\n sticky?: boolean | 'left' | 'right'\r\n align?: 'left' | 'right' | 'center'\r\n /** minWidth em px. */\r\n width?: number\r\n /** Largura máxima em px (o conteúdo corta com \"…\" e mostra o valor completo no tooltip). Sobrepõe-se a `defaultMaxColumnWidth`. */\r\n maxWidth?: number\r\n /**\r\n * Filtro por coluna (distinct dos valores, multi-seleção; intervalo nas colunas number/date).\r\n * Por omissão LIGADO em todas as colunas com dados — `false` desliga. Colunas sem dados (ex.: ações) não o mostram.\r\n */\r\n filterable?: boolean\r\n /** Desliga a ordenação nesta coluna (default: ordenável). */\r\n sortable?: boolean\r\n /**\r\n * Agrupar por esta coluna. Por omissão LIGADO nas colunas de texto com dados e DESLIGADO nas de valor contínuo\r\n * (`sortType` number/date, ou com `groupAggregate`), onde cada linha daria o seu próprio grupo. `true`/`false` forçam.\r\n */\r\n groupable?: boolean\r\n /** Chave usada só para ORDENAR os grupos (sobrepõe-se a `value` ao agrupar por esta coluna). */\r\n groupSortKey?: (row: T) => string | number | null | undefined\r\n /**\r\n * Filtros extra desenhados como funis adicionais no cabeçalho desta coluna — para filtrar\r\n * por um campo que não tem coluna própria (ex: filtrar a coluna \"Obra\" por concelho).\r\n */\r\n extraFilters?: { key: string; label: string; value: (row: T) => string | number | null | undefined }[]\r\n headerClassName?: string\r\n cellClassName?: string\r\n /** Conteúdo extra por baixo do título da coluna (ex: somatório). Recebe as linhas já filtradas. */\r\n headerExtra?: (rows: T[]) => React.ReactNode\r\n /**\r\n * Valor desta coluna na linha de cada grupo (ex: soma em €), alinhado com a coluna. Recebe as linhas do grupo\r\n * (já filtradas). Também é usado na linha de total (`totalRow`), com todas as linhas filtradas.\r\n */\r\n groupAggregate?: (rows: T[]) => React.ReactNode\r\n /**\r\n * Cores por valor para os chips de filtro ativo (valor → classes tailwind). Com isto, cada\r\n * valor escolhido ganha o seu chip, removível à parte.\r\n */\r\n valueChipColors?: Record<string, string>\r\n}\r\n\r\n/**\r\n * Ordenação, filtros e agrupamento do cabeçalho vivem dentro da tabela. Quem está por fora\r\n * (ex: exportar para Excel, guardar uma vista) precisa deles — daí este handle, preenchido\r\n * em `viewRef`.\r\n * @deprecated Legacy — não usar em código novo. Ver Storybook › Legacy.\r\n */\r\nexport interface DataTableView<T> {\r\n /** Aplica ordenação + filtros de coluna a qualquer lista (ex: ao dataset completo). */\r\n apply: (rows: T[]) => T[]\r\n /** Linhas à vista, já filtradas e ordenadas (todas as páginas). */\r\n rows: T[]\r\n hasFilters: boolean\r\n /**\r\n * Há filtros, ordenação ou agrupamento ativos — tudo do lado do cliente. Quem alimenta a\r\n * tabela usa isto para não largar o dataset completo: sem ele, filtrar sobre a página dá\r\n * uma lista vazia com o total nos milhares.\r\n */\r\n precisaDataset: boolean\r\n /** Estado guardável da vista. */\r\n estado: () => DataTableEstado\r\n /** Repõe um estado guardado (ex: ao aplicar uma vista do utilizador). */\r\n repor: (e: DataTableEstado | undefined) => void\r\n}\r\n\r\n/**\r\n * O que uma vista guardada leva da tabela. Sets viram arrays porque isto é serializado\r\n * para JSON.\r\n * @deprecated Legacy — não usar em código novo. Ver Storybook › Legacy.\r\n */\r\nexport interface DataTableEstado {\r\n columnFilters?: Record<string, string[]>\r\n rangeFilters?: Record<string, { min?: number; max?: number }>\r\n sortKey?: string | null\r\n sortDir?: 'asc' | 'desc'\r\n /** Colunas por que se agrupa, pela ordem dos níveis. */\r\n groupBy?: string[]\r\n}\r\n\r\n/** @deprecated Legacy — não usar em código novo. Ver Storybook › Legacy. */\r\nexport interface DataTableLabels {\r\n /** {count} é substituído pelo total (após filtros). */\r\n results: string\r\n /** Tooltip da pega de redimensionar coluna. */\r\n resizeColumn?: string\r\n export: string\r\n loading: string\r\n empty: string\r\n error: string\r\n search: string\r\n clear: string\r\n clearAll: string\r\n noOptions: string\r\n /** {count} = nº de valores selecionados (quando > 2, mostra \"N sel.\"). */\r\n selected: string\r\n /** {shown}/{total} — aparece quando o distinct da coluna tem mais valores do que os mostrados. */\r\n moreOptions: string\r\n /** Aviso no popup enquanto `allData` não chegou — as opções ainda são só as de `data`. */\r\n loadingOptions: string\r\n /** Placeholders dos dois campos do filtro por intervalo (colunas numéricas e de datas). */\r\n min: string\r\n max: string\r\n /** {page}/{pages} são substituídos. */\r\n page: string\r\n prev: string\r\n next: string\r\n /** Ordenação a partir do popup de filtro. */\r\n sortAsc: string\r\n sortDesc: string\r\n /** Agrupamento. {label} é o nome da coluna. */\r\n groupBy: string\r\n ungroup: string\r\n clearGroups: string\r\n /** Selector de linhas por página (só com `pageSizeOptions`). */\r\n perPage: string\r\n /** `filterMode: 'excel'`: primeira linha da lista de valores, o \"(Selecionar tudo)\" do Excel. */\r\n selectAll: string\r\n /** O mesmo, com pesquisa activa — aí só abrange os valores que correspondem. */\r\n selectAllResults: string\r\n /** Rótulo da linha de total (`totalRow`). */\r\n total: string\r\n}\r\n\r\nconst DEFAULT_LABELS: DataTableLabels = {\r\n total: 'Total',\r\n results: '{count} results',\r\n resizeColumn: 'Drag to resize · double-click to reset',\r\n export: 'Export',\r\n loading: 'Loading…',\r\n empty: 'No data',\r\n error: 'Failed to load',\r\n search: 'Search…',\r\n clear: 'Clear',\r\n clearAll: 'Clear all',\r\n noOptions: 'No options',\r\n selected: '{count} sel.',\r\n moreOptions: 'Showing {shown} of {total} — refine your search',\r\n loadingOptions: 'Loading options from all pages…',\r\n min: 'From',\r\n max: 'To',\r\n page: 'Page {page}/{pages}',\r\n prev: 'Prev',\r\n next: 'Next',\r\n sortAsc: 'Sort ascending',\r\n sortDesc: 'Sort descending',\r\n groupBy: 'Group by {label}',\r\n ungroup: 'Remove grouping by {label}',\r\n clearGroups: 'Clear grouping',\r\n perPage: 'Per page',\r\n selectAll: '(Select all)',\r\n selectAllResults: '(Select results)',\r\n}\r\n\r\n// ─── Ícones (inline, sem dependências) ───────────────────────────────────────\r\n\r\nconst Funnel = ({ size = 12, className }: { size?: number; className?: string }) => (\r\n <svg xmlns=\"http://www.w3.org/2000/svg\" width={size} height={size} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2.5\" strokeLinecap=\"round\" strokeLinejoin=\"round\" className={className}>\r\n <path d=\"M22 3H2l8 9.46V19l4 2v-8.54L22 3z\" />\r\n </svg>\r\n)\r\n\r\nconst Layers = ({ size = 11, className }: { size?: number; className?: string }) => (\r\n <svg xmlns=\"http://www.w3.org/2000/svg\" width={size} height={size} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2.5\" strokeLinecap=\"round\" strokeLinejoin=\"round\" className={className}>\r\n <path d=\"m12 2 9 5-9 5-9-5 9-5z\" />\r\n <path d=\"m3 17 9 5 9-5M3 12l9 5 9-5\" />\r\n </svg>\r\n)\r\n\r\nconst Caret = ({ size = 11, className }: { size?: number; className?: string }) => (\r\n <svg xmlns=\"http://www.w3.org/2000/svg\" width={size} height={size} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"3\" strokeLinecap=\"round\" strokeLinejoin=\"round\" className={className}>\r\n <path d=\"m9 18 6-6-6-6\" />\r\n </svg>\r\n)\r\n\r\ninterface DataTableProps<T> {\r\n columns: DataTableColumn<T>[]\r\n data: T[]\r\n /**\r\n * Dataset completo, para o distinct dos filtros e para filtrar/ordenar/agrupar sobre ele.\r\n *\r\n * Sem isto a tabela só conhece `data` — e numa app que pagina no servidor isso são as\r\n * linhas da página, pelo que o funil de cada coluna listava só esses valores (e filtrar\r\n * por um valor que só existe noutra página dava zero linhas).\r\n */\r\n allData?: T[]\r\n /**\r\n * Chamado quando a tabela precisa de `allData` e ainda não o tem: ao abrir um funil, ao\r\n * ordenar, ao agrupar, ou quando já há filtros ativos. É o sinal para a app ir buscar o\r\n * dataset completo — uma vez, e só se alguém mexer.\r\n */\r\n onDataNeeded?: () => void\r\n /** `allData` a caminho — mostra um aviso no popup em vez de opções silenciosamente incompletas. */\r\n isAllDataLoading?: boolean\r\n /** Força o modo cliente mesmo sem interação (desde que `allData` exista). */\r\n alwaysClient?: boolean\r\n /**\r\n * Avisa quando a tabela passa (ou deixa) de trabalhar sobre `allData`. Quem tem paginação\r\n * de servidor por fora usa isto para a esconder: em modo cliente as linhas à vista já não\r\n * são a página que esse controlo diz estar a mostrar.\r\n */\r\n onClientModeChange?: (active: boolean) => void\r\n /** Chave estável por linha. Obrigatória para a seleção; sem ela usa-se o índice. */\r\n rowKey?: (row: T, index: number) => string | number\r\n onRowClick?: (row: T) => void\r\n /** Duplo clique — atalho para o detalhe sem roubar o clique simples (seleção, edição inline). */\r\n onRowDoubleClick?: (row: T) => void\r\n /**\r\n * Classes extra por linha, a partir dos dados — estado de risco, linha desactivada, etc.\r\n * Vem depois das classes da tabela, por isso ganha em caso de conflito.\r\n */\r\n rowClassName?: (row: T) => string | undefined\r\n /**\r\n * Chamado quando as linhas à vista mudam (filtros do cabeçalho, ordenação ou dados), com todas as páginas.\r\n * Serve para indicadores fora da tabela (ex.: cartões no topo da página) acompanharem os filtros.\r\n */\r\n onVisibleRowsChange?: (rows: T[]) => void\r\n isLoading?: boolean\r\n isError?: boolean\r\n /** max-height do viewport com scroll (default 70vh). */\r\n maxHeight?: string\r\n /** Linhas por página. Sem isto, mostra tudo (pode ser lento com muitas linhas). */\r\n pageSize?: number\r\n /** Mostra um selector de linhas por página com estas opções. */\r\n pageSizeOptions?: number[]\r\n /** Caixa de seleção por linha + no cabeçalho. */\r\n selectable?: boolean\r\n onSelectionChange?: (selectedKeys: Set<string>) => void\r\n /** Mudar este número limpa a seleção (ex: depois de uma ação em massa). */\r\n clearSelectionTrigger?: number\r\n /** Guarda filtros e intervalos no sessionStorage com esta chave (e as larguras das colunas no localStorage). */\r\n storageKey?: string\r\n /** Largura máxima por defeito das colunas, em px (default 360). `0` desliga o limite. */\r\n defaultMaxColumnWidth?: number\r\n /** Colunas redimensionáveis arrastando o limite do cabeçalho; duplo clique repõe (default true). */\r\n resizableColumns?: boolean\r\n /**\r\n * \"Ordenar A→Z / Z→A\" no topo do popup de filtro. Ligado por omissão (o cabeçalho é o mesmo em todos os\r\n * portais); `false` desliga. Os textos vêm de `labels.sortAsc`/`sortDesc` (por omissão em inglês).\r\n */\r\n sortFromFilter?: boolean\r\n /**\r\n * Semântica do filtro por coluna.\r\n *\r\n * - `'simple'`: abre com tudo **desmarcado** e marcar um valor quer dizer \"só este\".\r\n * Conjunto vazio = sem filtro.\r\n * - `'excel'` (default): abre com tudo **marcado** e desmarca-se o que se quer excluir, com uma linha\r\n * `(Selecionar tudo)` no topo e curingas (`*`, `?`, `~`) na pesquisa. É o que a folha de\r\n * cálculo faz, e o que quem vem de lá espera. Conjunto vazio = **nada seleccionado**, e a\r\n * tabela fica sem linhas.\r\n *\r\n * No modo Excel o distinto da coluna aberta deixa de respeitar os filtros das outras: o que a\r\n * lista mostra tem de ser exactamente o que a selecção materializa, senão marcar \"tudo\"\r\n * apagaria em silêncio os valores que outra coluna escondeu.\r\n */\r\n filterMode?: 'simple' | 'excel'\r\n /** Ordenação inicial. */\r\n defaultSortKey?: string\r\n defaultSortDir?: 'asc' | 'desc'\r\n /** Recebe o estado da vista (ordenação + filtros + agrupamento) para uso externo. */\r\n viewRef?: React.MutableRefObject<DataTableView<T> | null>\r\n /** Mostra o botão de exportar. Sem `onExport`, faz download CSV nativo. */\r\n exportFileName?: string\r\n /** Export custom (ex: XLSX na app). Recebe as linhas filtradas + ordenadas. */\r\n onExport?: (rows: T[]) => void\r\n /** Conteúdo extra na toolbar (à direita, antes do export). */\r\n extraToolbar?: React.ReactNode\r\n /** Texto por baixo do estado vazio. */\r\n emptySubLabel?: string\r\n /** Ilustração do estado vazio. */\r\n emptyIcon?: React.ReactNode\r\n labels?: Partial<DataTableLabels>\r\n /** Linha de total no fim (fica fixa em baixo), com o `groupAggregate` de cada coluna sobre as linhas filtradas. */\r\n totalRow?: boolean\r\n /** Agrupamento inicial (chaves de colunas, por ordem de nível). A pessoa pode mudá-lo depois. */\r\n defaultGroupBy?: string[]\r\n}\r\n\r\n// ─── Helpers ─────────────────────────────────────────────────────────────────\r\n\r\nconst EMPTY = '—'\r\n\r\n/** Nº máximo de opções renderizadas no popup de filtro por coluna — sem isto, uma coluna\r\n * quase-única por linha (ex: um ID) põe milhares de `<button>` no DOM de cada vez que o popup\r\n * abre, mesmo só visualmente cortado pelo scroll. A pesquisa continua a correr sobre o distinct\r\n * completo (memoizado), só o que é DESENHADO fica limitado. */\r\nconst MAX_OPCOES_FILTRO = 50\r\n\r\n/** Largura assumida p/ acumular o offset de colunas sticky sem `width` definido. */\r\nconst DEFAULT_STICKY_WIDTH = 160\r\n\r\n/** Collator único, reutilizado em todas as comparações de texto.\r\n * `a.localeCompare(b, undefined, { numeric: true })` constrói um `Intl.Collator` **a cada\r\n * chamada**; num sort de milhares de valores (ex: o distinct de uma coluna de IDs, com ~12k\r\n * valores ≈ 150k comparações) era isso, e não o render, que fazia o popup demorar a abrir. */\r\nconst COLLATOR = new Intl.Collator(undefined, { numeric: true })\r\n\r\nfunction rawValue<T>(row: T, col: DataTableColumn<T>): string | number | null {\r\n if (col.value) {\r\n const v = col.value(row)\r\n return v == null ? null : v\r\n }\r\n const v = (row as Record<string, unknown>)[col.key]\r\n if (v == null) return null\r\n if (typeof v === 'number' || typeof v === 'string') return v\r\n return String(v)\r\n}\r\n\r\n/** Valor normalizado p/ filtro (string; vazio → EMPTY). */\r\nfunction filterKey<T>(row: T, col: DataTableColumn<T>): string {\r\n const v = rawValue(row, col)\r\n return v == null || v === '' ? EMPTY : String(v)\r\n}\r\n\r\n/**\r\n * Timestamp de um valor de coluna de data. As apps passam a data já formatada (`value:\r\n * dataPt(...)` → \"14/08/2026\"), e `new Date(\"14/08/2026\")` devolve `Invalid Date` — ou, pior,\r\n * lê \"06/03/2026\" como 3 de junho, à americana. Daí o parse explícito de dd/mm/aaaa antes do\r\n * fallback para o `Date` nativo (que trata do ISO).\r\n * @deprecated Legacy — não usar em código novo. Ver Storybook › Legacy.\r\n */\r\nexport function timestampDeData(v: unknown): number {\r\n if (v == null || v === '') return NaN\r\n if (v instanceof Date) return v.getTime()\r\n const s = String(v).trim()\r\n const m = /^(\\d{1,2})[/-](\\d{1,2})[/-](\\d{4})/.exec(s)\r\n if (m) return Date.UTC(Number(m[3]), Number(m[2]) - 1, Number(m[1]))\r\n const t = new Date(s).getTime()\r\n return Number.isNaN(t) ? NaN : t\r\n}\r\n\r\nfunction compare(\r\n a: string | number | null,\r\n b: string | number | null,\r\n type: DataTableColumn<unknown>['sortType']\r\n): number {\r\n if (a == null && b == null) return 0\r\n if (a == null) return 1\r\n if (b == null) return -1\r\n if (type === 'number') return Number(a) - Number(b)\r\n if (type === 'date') return timestampDeData(a) - timestampDeData(b)\r\n return COLLATOR.compare(String(a), String(b))\r\n}\r\n\r\n/** Valor comparável de uma célula para o filtro por intervalo (número ou data). */\r\nfunction valorParaIntervalo<T>(row: T, col: DataTableColumn<T>): number {\r\n const v = rawValue(row, col)\r\n return col.sortType === 'date' ? timestampDeData(v) : Number(v)\r\n}\r\n\r\nfunction toCsv<T>(columns: DataTableColumn<T>[], rows: T[]): string {\r\n const esc = (v: unknown) => {\r\n const s = v == null ? '' : String(v)\r\n return /[\",\\n]/.test(s) ? `\"${s.replace(/\"/g, '\"\"')}\"` : s\r\n }\r\n const head = columns.map((c) => esc(c.label)).join(',')\r\n const body = rows.map((r) => columns.map((c) => esc(rawValue(r, c))).join(',')).join('\\n')\r\n return `${head}\\n${body}`\r\n}\r\n\r\nconst ordenarRotulos = (a: string, b: string) => (a === EMPTY ? 1 : b === EMPTY ? -1 : COLLATOR.compare(a, b))\r\n\r\n/** Identidade estável para as dependências que o modo Excel não usa — ver `cruzFiltros`. */\r\nconst SEM_CRUZAMENTO: [string, never][] = []\r\n\r\nconst escaparRegex = (s: string) => s.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')\r\n\r\n/**\r\n * Predicado da caixa de pesquisa do filtro por coluna, com os curingas do Excel.\r\n *\r\n * `*` = qualquer sequência, `?` = exactamente um carácter, `~` escapa qualquer um dos três. Sem\r\n * curinga nenhum é \"contém\"; **com** curinga o padrão passa a ser comparado com o valor **inteiro**,\r\n * e é daí que sai o \"começa por\" (`25VCT*`), o \"termina em\" (`*.fbo`) e o \"tem isto no meio\"\r\n * (`*RA2*`).\r\n *\r\n * No Excel esse ancorar vive nos Filtros de Texto (Começa por/Termina em) e não na caixa de\r\n * pesquisa, que é sempre \"contém\" — juntar os dois na caixa evita ter de trazer também o submenu, e\r\n * sem curingas o comportamento é o mesmo dos dois lados.\r\n *\r\n * Tudo em minúsculas: quem chama passa a pesquisa e o valor já em minúsculas.\r\n */\r\nexport function pesquisaCuringa(q: string): (v: string) => boolean {\r\n let fonte = ''\r\n let literal = ''\r\n let temCuringa = false\r\n for (let i = 0; i < q.length; i++) {\r\n const c = q[i]!\r\n const seguinte = q[i + 1]\r\n if (c === '~' && (seguinte === '*' || seguinte === '?' || seguinte === '~')) {\r\n fonte += escaparRegex(seguinte)\r\n literal += seguinte\r\n i++\r\n continue\r\n }\r\n if (c === '*' || c === '?') {\r\n fonte += c === '*' ? '.*' : '.'\r\n literal += c\r\n temCuringa = true\r\n continue\r\n }\r\n fonte += escaparRegex(c)\r\n literal += c\r\n }\r\n // Sem curinga usa-se o literal (e não `q`), para `~*` procurar mesmo um asterisco.\r\n if (!temCuringa) return (v) => v.includes(literal)\r\n const rx = new RegExp(`^${fonte}$`)\r\n return (v) => rx.test(v)\r\n}\r\n\r\ninterface GrupoNo<T> {\r\n val: string\r\n ordem: string\r\n path: string\r\n items: T[]\r\n filhos: GrupoNo<T>[]\r\n}\r\n\r\n// ─── Componente ──────────────────────────────────────────────────────────────\r\n\r\n/**\r\n * Tabela genérica: ordenação, filtro por coluna com distinct (multi-seleção) e por intervalo,\r\n * agrupamento multi-nível, seleção de linhas, colunas fixas (sticky), align/width, export\r\n * (CSV nativo ou custom) e estados loading/vazio/erro. Sem dependências de ícones/i18n —\r\n * textos via `labels`.\r\n *\r\n * Com paginação de servidor, passa também `allData`/`onDataNeeded`: sem o dataset completo,\r\n * filtrar, ordenar e agrupar só olhariam para a página à vista.\r\n * @deprecated Legacy — não usar em código novo. Ver Storybook › Legacy.\r\n */\r\nexport default function DataTable<T>({\r\n columns,\r\n data,\r\n allData,\r\n onDataNeeded,\r\n isAllDataLoading,\r\n alwaysClient,\r\n onClientModeChange,\r\n rowKey,\r\n onRowClick,\r\n onRowDoubleClick,\r\n rowClassName,\r\n onVisibleRowsChange,\r\n isLoading,\r\n isError,\r\n maxHeight = '70vh',\r\n pageSize,\r\n pageSizeOptions,\r\n selectable,\r\n onSelectionChange,\r\n clearSelectionTrigger,\r\n storageKey,\r\n defaultMaxColumnWidth = 360,\r\n resizableColumns = true,\r\n sortFromFilter = true,\r\n filterMode = 'excel',\r\n defaultSortKey,\r\n defaultSortDir,\r\n viewRef,\r\n exportFileName,\r\n onExport,\r\n extraToolbar,\r\n emptySubLabel,\r\n emptyIcon,\r\n labels,\r\n totalRow = false,\r\n defaultGroupBy,\r\n}: DataTableProps<T>) {\r\n const L = { ...DEFAULT_LABELS, ...labels }\r\n\r\n // Larguras definidas pela pessoa (arrastar o limite do cabeçalho). Guardadas por tabela no localStorage.\r\n const [larguras, setLarguras] = useState<Record<string, number>>(() => {\r\n if (!storageKey) return {}\r\n try {\r\n return JSON.parse(localStorage.getItem(`dt_widths_${storageKey}`) ?? '{}') as Record<string, number>\r\n } catch {\r\n return {}\r\n }\r\n })\r\n useEffect(() => {\r\n if (!storageKey) return\r\n try {\r\n localStorage.setItem(`dt_widths_${storageKey}`, JSON.stringify(larguras))\r\n } catch {\r\n /* storage indisponível */\r\n }\r\n }, [larguras, storageKey])\r\n const larguraMax = (c: DataTableColumn<T>) => larguras[c.key] ?? c.maxWidth ?? (defaultMaxColumnWidth > 0 ? defaultMaxColumnWidth : undefined)\r\n const iniciarResize = (e: React.MouseEvent, key: string) => {\r\n e.preventDefault()\r\n e.stopPropagation()\r\n const th = (e.currentTarget as HTMLElement).parentElement\r\n const x0 = e.clientX\r\n const w0 = th?.getBoundingClientRect().width ?? 120\r\n const mover = (ev: MouseEvent) => setLarguras((l) => ({ ...l, [key]: Math.max(60, Math.round(w0 + ev.clientX - x0)) }))\r\n const parar = () => {\r\n window.removeEventListener('mousemove', mover)\r\n window.removeEventListener('mouseup', parar)\r\n document.body.style.cursor = ''\r\n }\r\n document.body.style.cursor = 'col-resize'\r\n window.addEventListener('mousemove', mover)\r\n window.addEventListener('mouseup', parar)\r\n }\r\n const reporLargura = (key: string) =>\r\n setLarguras((l) => {\r\n const n = { ...l }\r\n delete n[key]\r\n return n\r\n })\r\n // Com `storageKey`, a ordenação e o agrupamento ficam na sessão como os filtros: ao voltar à tabela (ex.: mudar\r\n // de separador e regressar) a vista é a mesma. Valor guardado (mesmo vazio) ganha aos de omissão.\r\n const lerSessao = <V,>(sufixo: string): V | undefined => {\r\n if (!storageKey) return undefined\r\n try {\r\n const raw = sessionStorage.getItem(`dt_${sufixo}_${storageKey}`)\r\n return raw === null ? undefined : (JSON.parse(raw) as V)\r\n } catch {\r\n return undefined\r\n }\r\n }\r\n const [sort, setSort] = useState<{ key: string; dir: 1 | -1 } | null>(() => {\r\n const guardada = lerSessao<{ key: string; dir: 1 | -1 } | null>('sort')\r\n if (guardada !== undefined) return guardada && columns.some((c) => c.key === guardada.key) ? guardada : null\r\n return defaultSortKey ? { key: defaultSortKey, dir: defaultSortDir === 'desc' ? -1 : 1 } : null\r\n })\r\n const [columnFilters, setColumnFilters] = useState<Record<string, Set<string>>>(() => {\r\n if (!storageKey) return {}\r\n try {\r\n const raw = sessionStorage.getItem(`dt_filters_${storageKey}`)\r\n if (!raw) return {}\r\n const parsed = JSON.parse(raw) as Record<string, string[]>\r\n return Object.fromEntries(Object.entries(parsed).map(([k, v]) => [k, new Set(v)]))\r\n } catch {\r\n return {}\r\n }\r\n })\r\n // Filtro por intervalo, em colunas numéricas e de datas (`sortType`). Numa coluna como\r\n // \"Dias Penalização\" a lista de valores distintos é inútil — ninguém procura \"88 valores\" um a\r\n // um, quer é \"acima de 30\". Convive com a seleção por valor: se as duas estiverem ativas, a\r\n // linha tem de passar nas duas.\r\n const [rangeFilters, setRangeFilters] = useState<Record<string, { min?: number; max?: number }>>(() => {\r\n if (!storageKey) return {}\r\n try {\r\n const raw = sessionStorage.getItem(`dt_ranges_${storageKey}`)\r\n return raw ? (JSON.parse(raw) as Record<string, { min?: number; max?: number }>) : {}\r\n } catch {\r\n return {}\r\n }\r\n })\r\n const [groupByKeys, setGroupByKeys] = useState<string[]>(() =>\r\n (lerSessao<string[]>('group') ?? defaultGroupBy ?? []).filter((k) => columns.some((c) => c.key === k))\r\n )\r\n const [collapsedGroups, setCollapsedGroups] = useState<Set<string>>(new Set())\r\n const [selectedKeys, setSelectedKeys] = useState<Set<string>>(new Set())\r\n const [openFilter, setOpenFilter] = useState<string | null>(null)\r\n const [filterSearch, setFilterSearch] = useState('')\r\n const filterRefs = useRef<Map<string, HTMLDivElement>>(new Map())\r\n const popupRef = useRef<HTMLDivElement | null>(null)\r\n // O popup é desenhado num portal, não dentro do `<th>`: a tabela vive num contentor com\r\n // `overflow: auto` e, quando tem poucas linhas, esse contentor é baixo e cortava o popup ao\r\n // fim de dois ou três valores. Fora da árvore da tabela, com `position: fixed`, deixa de haver\r\n // nada que o corte.\r\n const [popupPos, setPopupPos] = useState<{ top: number; left: number } | null>(null)\r\n\r\n const isGrouped = groupByKeys.length > 0\r\n\r\n useEffect(() => {\r\n if (!storageKey) return\r\n const serializavel = Object.fromEntries(Object.entries(columnFilters).map(([k, v]) => [k, Array.from(v)]))\r\n try {\r\n sessionStorage.setItem(`dt_filters_${storageKey}`, JSON.stringify(serializavel))\r\n sessionStorage.setItem(`dt_ranges_${storageKey}`, JSON.stringify(rangeFilters))\r\n sessionStorage.setItem(`dt_sort_${storageKey}`, JSON.stringify(sort))\r\n sessionStorage.setItem(`dt_group_${storageKey}`, JSON.stringify(groupByKeys))\r\n } catch {\r\n /* sessionStorage indisponível (private mode) — a tabela funciona na mesma, só não recorda. */\r\n }\r\n }, [columnFilters, rangeFilters, sort, groupByKeys, storageKey])\r\n\r\n useLayoutEffect(() => {\r\n if (!openFilter) {\r\n setPopupPos(null)\r\n return\r\n }\r\n const recalcular = () => {\r\n const el = filterRefs.current.get(openFilter)\r\n if (!el) return\r\n const r = el.getBoundingClientRect()\r\n // Tem de acompanhar o `w-72` do popup — é o que o impede de ficar fora do ecrã à direita.\r\n const largura = Math.min(288, window.innerWidth * 0.8)\r\n // Encosta à direita quando a coluna está no fim da tabela e o popup passaria do ecrã.\r\n const left = Math.max(8, Math.min(r.left, window.innerWidth - largura - 8))\r\n setPopupPos({ top: r.bottom + 6, left })\r\n }\r\n recalcular()\r\n // `true` na captura: apanha também o scroll do contentor da tabela, que não borbulha.\r\n window.addEventListener('scroll', recalcular, true)\r\n window.addEventListener('resize', recalcular)\r\n return () => {\r\n window.removeEventListener('scroll', recalcular, true)\r\n window.removeEventListener('resize', recalcular)\r\n }\r\n }, [openFilter])\r\n\r\n // Fecha o dropdown de filtro em clique fora (o popup está fora do `<th>`, daí os dois testes).\r\n useEffect(() => {\r\n const onDown = (e: MouseEvent) => {\r\n if (!openFilter) return\r\n const alvo = e.target as Node\r\n const botao = filterRefs.current.get(openFilter)\r\n if (botao?.contains(alvo) || popupRef.current?.contains(alvo)) return\r\n setOpenFilter(null)\r\n setFilterSearch('')\r\n }\r\n document.addEventListener('mousedown', onDown)\r\n return () => document.removeEventListener('mousedown', onDown)\r\n }, [openFilter])\r\n\r\n /**\r\n * Função de valor por chave de filtro. A chave tanto pode ser uma coluna como um\r\n * `extraFilters` pendurado no cabeçalho de outra coluna.\r\n */\r\n const resolverValor = (key: string): ((row: T) => string | number | null | undefined) | undefined => {\r\n const col = columns.find((c) => c.key === key)\r\n if (col) return (row: T) => rawValue(row, col)\r\n for (const c of columns) {\r\n const ef = c.extraFilters?.find((f) => f.key === key)\r\n if (ef) return ef.value\r\n }\r\n return undefined\r\n }\r\n\r\n const rotuloDe = (fn: (row: T) => string | number | null | undefined, row: T) => {\r\n const v = fn(row)\r\n return v == null || v === '' ? EMPTY : String(v)\r\n }\r\n\r\n const modoExcel = filterMode === 'excel'\r\n\r\n /**\r\n * No modo Excel é a **presença da chave** que liga o filtro, e não o tamanho do conjunto: sem\r\n * chave = sem filtro = tudo marcado no popover (o estado em que o Excel abre). Com chave passam\r\n * só os valores do conjunto, e um conjunto vazio é \"nada seleccionado\" — a tabela fica sem\r\n * linhas, como no Excel quando se desmarca o `(Selecionar tudo)`.\r\n */\r\n const activeFilters = useMemo(\r\n () => Object.entries(columnFilters).filter(([, s]) => s && (modoExcel || s.size > 0)),\r\n [columnFilters, modoExcel]\r\n )\r\n const activeRanges = useMemo(\r\n () => Object.entries(rangeFilters).filter(([, r]) => r && (r.min != null || r.max != null)),\r\n [rangeFilters]\r\n )\r\n const hasActiveFilters = activeFilters.length > 0 || activeRanges.length > 0\r\n\r\n /**\r\n * Com o dataset completo em mão, é sobre ele que se filtra, ordena e agrupa — não sobre\r\n * `data`. Sem isto, escolher no funil um valor que só existe noutra página dava uma tabela\r\n * vazia: as opções vinham de todos os dados mas o filtro corria só sobre a página.\r\n */\r\n const isClientMode = !!allData && (!!alwaysClient || hasActiveFilters || !!sort || isGrouped)\r\n const effectiveData = isClientMode ? allData! : data\r\n\r\n // Pedir o dataset completo. Não só no arranque: os filtros podem vir do sessionStorage ou\r\n // de uma vista reposta, e quem alimenta a tabela pode largar o dataset a meio.\r\n useEffect(() => {\r\n if (!allData && (hasActiveFilters || sort || isGrouped)) onDataNeeded?.()\r\n // eslint-disable-next-line react-hooks/exhaustive-deps\r\n }, [allData, hasActiveFilters, sort, isGrouped])\r\n\r\n useEffect(() => {\r\n onClientModeChange?.(isClientMode)\r\n // eslint-disable-next-line react-hooks/exhaustive-deps\r\n }, [isClientMode])\r\n\r\n useEffect(() => {\r\n if (clearSelectionTrigger === undefined) return\r\n setSelectedKeys(new Set())\r\n }, [clearSelectionTrigger])\r\n\r\n /**\r\n * Filtrar pelo cabeçalho também limpa a seleção.\r\n *\r\n * O que estava marcado era-o daquela lista: filtrar para outra e manter as marcas deixava\r\n * escolhidas linhas que já não se veem — e quem age sobre a seleção (uma ação em massa)\r\n * agia sobre elas na mesma. Avisa-se o ecrã, e não só se limpa por dentro: as caixas\r\n * ficavam vazias e o botão de cima continuava a dizer \"Tratar 250\" — e tratava-as.\r\n *\r\n * Ordenar não conta: são as mesmas linhas por outra ordem.\r\n */\r\n const chaveFiltrosCol = JSON.stringify(\r\n Object.entries(columnFilters)\r\n .map(([k, v]) => [k, [...v].sort()])\r\n .sort()\r\n )\r\n useEffect(() => {\r\n if (!selectable) return\r\n setSelectedKeys(new Set())\r\n onSelectionChange?.(new Set())\r\n // `onSelectionChange` fora das dependências de propósito: é uma função do ecrã, recriada a\r\n // cada render, e segui-la limpava a seleção sozinha.\r\n // eslint-disable-next-line react-hooks/exhaustive-deps\r\n }, [chaveFiltrosCol])\r\n\r\n /**\r\n * No modo Excel o distinto é o puro da coluna (ver `filterMode`), por isso não depende dos\r\n * filtros das outras. Fixar aqui a identidade evita recalcular a lista inteira a cada clique\r\n * numa caixa — que é precisamente o que uma coluna quase-única por linha não perdoa.\r\n */\r\n const cruzFiltros = modoExcel ? SEM_CRUZAMENTO : activeFilters\r\n const cruzRanges = modoExcel ? SEM_CRUZAMENTO : activeRanges\r\n\r\n /**\r\n * Distinct de uma chave, a partir do dataset completo quando ele existe: as opções do funil\r\n * são as da tabela toda, não as da página que calhou estar à vista.\r\n *\r\n * Os filtros das OUTRAS colunas aplicam-se (mas não o da própria): assim escolher \"Porto\"\r\n * numa coluna faz a coluna ao lado deixar de oferecer valores que já não existem — sem\r\n * impedir de alargar a escolha na coluna que se está a mexer.\r\n */\r\n const distinctValues = (key: string): string[] => {\r\n const valueFn = resolverValor(key)\r\n if (!valueFn) return []\r\n let fonte = allData ?? data\r\n for (const [outraKey, escolhidos] of cruzFiltros) {\r\n if (outraKey === key) continue\r\n const outraFn = resolverValor(outraKey)\r\n if (!outraFn) continue\r\n fonte = fonte.filter((row) => escolhidos.has(rotuloDe(outraFn, row)))\r\n }\r\n for (const [outraKey, r] of cruzRanges) {\r\n if (outraKey === key) continue\r\n const col = columns.find((c) => c.key === outraKey)\r\n if (!col) continue\r\n fonte = fonte.filter((row) => {\r\n const n = valorParaIntervalo(row, col)\r\n if (!Number.isFinite(n)) return false\r\n if (r.min != null && n < r.min) return false\r\n if (r.max != null && n > r.max) return false\r\n return true\r\n })\r\n }\r\n const set = new Set<string>()\r\n fonte.forEach((row) => set.add(rotuloDe(valueFn, row)))\r\n return Array.from(set).sort(ordenarRotulos)\r\n }\r\n\r\n // Memoizado ao abrir/mudar de coluna (não a cada tecla da pesquisa) — numa coluna quase-única\r\n // por linha, recalcular o distinct (iterar + Set + sort) em cada keystroke era o grosso da\r\n // lentidão do popup, não só a 1ª abertura.\r\n const openFilterOptions = useMemo(() => {\r\n if (!openFilter) return []\r\n return distinctValues(openFilter)\r\n // eslint-disable-next-line react-hooks/exhaustive-deps\r\n }, [openFilter, data, allData, columns, cruzFiltros, cruzRanges])\r\n\r\n /** Versão em minúsculas do distinct, calculada uma vez por abertura — a pesquisa do popup\r\n * corria `v.toLowerCase()` sobre a lista inteira a cada tecla. */\r\n const openFilterOptionsLower = useMemo(() => openFilterOptions.map((v) => v.toLowerCase()), [openFilterOptions])\r\n\r\n /** Opções a desenhar (no máximo `MAX_OPCOES_FILTRO`) + total de correspondências, para o\r\n * rodapé \"a mostrar X de Y\". Um só varrimento, sem alocar o array completo dos matches. */\r\n const opcoesVisiveis = useMemo(() => {\r\n const q = filterSearch.toLowerCase()\r\n // No modo Excel a pesquisa aceita curingas; no simples mantém-se o \"contém\" de sempre.\r\n const corresponde = q ? (modoExcel ? pesquisaCuringa(q) : (v: string) => v.includes(q)) : null\r\n const opts: string[] = []\r\n // `correspondentes` é tudo o que a pesquisa apanha, não só o que se desenha: com 200\r\n // correspondências e 50 desenhadas, o `(Selecionar tudo)` tem de apanhar as 200 — é o que o\r\n // Excel faz, e o contrário seria uma armadilha silenciosa.\r\n const correspondentes: string[] = []\r\n for (let i = 0; i < openFilterOptions.length; i++) {\r\n if (corresponde && !corresponde(openFilterOptionsLower[i])) continue\r\n correspondentes.push(openFilterOptions[i])\r\n if (opts.length < MAX_OPCOES_FILTRO) opts.push(openFilterOptions[i])\r\n }\r\n return { opts, total: correspondentes.length, correspondentes }\r\n }, [openFilterOptions, openFilterOptionsLower, filterSearch, modoExcel])\r\n\r\n // Filtrar e ordenar são funções sobre uma lista qualquer (não só sobre a vista atual): é o\r\n // que permite reaplicar a mesma vista ao dataset completo — ver `viewRef`/`DataTableView`.\r\n const filtrar = (rows: T[]) => {\r\n if (activeFilters.length === 0 && activeRanges.length === 0) return rows\r\n return rows.filter(\r\n (row) =>\r\n activeFilters.every(([key, sel]) => {\r\n const fn = resolverValor(key)\r\n return fn ? sel.has(rotuloDe(fn, row)) : true\r\n }) &&\r\n activeRanges.every(([key, r]) => {\r\n const col = columns.find((c) => c.key === key)\r\n if (!col) return true\r\n const n = valorParaIntervalo(row, col)\r\n // Sem valor não passa num intervalo — \"maior que 30\" não inclui \"sem dias\", e um\r\n // intervalo de datas não inclui linhas sem data.\r\n if (!Number.isFinite(n)) return false\r\n if (r.min != null && n < r.min) return false\r\n if (r.max != null && n > r.max) return false\r\n return true\r\n })\r\n )\r\n }\r\n\r\n const ordenar = (rows: T[]) => {\r\n if (!sort) return rows\r\n const col = columns.find((c) => c.key === sort.key)\r\n if (!col) return rows\r\n return [...rows].sort((a, b) => compare(rawValue(a, col), rawValue(b, col), col.sortType) * sort.dir)\r\n }\r\n\r\n const filtered = useMemo(\r\n () => filtrar(effectiveData),\r\n // eslint-disable-next-line react-hooks/exhaustive-deps\r\n [effectiveData, activeFilters, activeRanges, columns]\r\n )\r\n\r\n const sorted = useMemo(\r\n () => ordenar(filtered),\r\n // eslint-disable-next-line react-hooks/exhaustive-deps\r\n [filtered, sort, columns]\r\n )\r\n\r\n // Ref para o aviso não depender da identidade da função (quem chama passa normalmente uma arrow nova).\r\n const avisarVisiveis = useRef(onVisibleRowsChange)\r\n avisarVisiveis.current = onVisibleRowsChange\r\n useEffect(() => {\r\n avisarVisiveis.current?.(sorted)\r\n }, [sorted])\r\n\r\n // ─── Agrupamento ───────────────────────────────────────────────────────────\r\n\r\n const alternarGrupo = (key: string) => {\r\n if (!allData) onDataNeeded?.()\r\n setGroupByKeys((prev) => (prev.includes(key) ? prev.filter((k) => k !== key) : [...prev, key]))\r\n setCollapsedGroups(new Set())\r\n }\r\n const removerGrupo = (key: string) => {\r\n setGroupByKeys((prev) => prev.filter((k) => k !== key))\r\n setCollapsedGroups(new Set())\r\n }\r\n const limparGrupos = () => {\r\n setGroupByKeys([])\r\n setCollapsedGroups(new Set())\r\n }\r\n const alternarColapso = (path: string) =>\r\n setCollapsedGroups((prev) => {\r\n const n = new Set(prev)\r\n if (n.has(path)) n.delete(path)\r\n else n.add(path)\r\n return n\r\n })\r\n\r\n const construirGrupos = (items: T[], keys: string[], nivel: number, pathPai: string): GrupoNo<T>[] => {\r\n if (nivel >= keys.length) return []\r\n const key = keys[nivel]\r\n const col = columns.find((c) => c.key === key)\r\n if (!col) return []\r\n // `groupSortKey` manda na ORDEM dos grupos; o rótulo continua a ser o valor da coluna.\r\n const ordemFn = col.groupSortKey\r\n const mapa = new Map<string, { ordem: string; items: T[] }>()\r\n items.forEach((item) => {\r\n const rotulo = filterKey(item, col)\r\n const ordem = ordemFn ? String(ordemFn(item) ?? '') : rotulo\r\n const atual = mapa.get(rotulo)\r\n if (atual) atual.items.push(item)\r\n else mapa.set(rotulo, { ordem, items: [item] })\r\n })\r\n return Array.from(mapa.entries())\r\n .sort(([ra, a], [rb, b]) => (ordemFn ? COLLATOR.compare(a.ordem, b.ordem) : ordenarRotulos(ra, rb)))\r\n .map(([val, { ordem, items: itensGrupo }]) => {\r\n const path = pathPai ? `${pathPai}\\u0000${val}` : val\r\n return { val, ordem, path, items: itensGrupo, filhos: construirGrupos(itensGrupo, keys, nivel + 1, path) }\r\n })\r\n }\r\n\r\n const grupos = useMemo(\r\n () => (isGrouped ? construirGrupos(sorted, groupByKeys, 0, '') : []),\r\n // eslint-disable-next-line react-hooks/exhaustive-deps\r\n [sorted, groupByKeys, isGrouped, columns]\r\n )\r\n\r\n // ─── Paginação (desligada ao agrupar) ──────────────────────────────────────\r\n\r\n const [page, setPage] = useState(1)\r\n const [pageSizeAtual, setPageSizeAtual] = useState<number | undefined>(pageSize)\r\n useEffect(() => setPageSizeAtual(pageSize), [pageSize])\r\n useEffect(() => {\r\n setPage(1)\r\n }, [columnFilters, rangeFilters, sort, groupByKeys, pageSizeAtual])\r\n const paginando = !!pageSizeAtual && !isGrouped\r\n const totalPages = paginando ? Math.max(1, Math.ceil(sorted.length / pageSizeAtual!)) : 1\r\n const currentPage = Math.min(page, totalPages)\r\n const pageData = paginando ? sorted.slice((currentPage - 1) * pageSizeAtual!, currentPage * pageSizeAtual!) : sorted\r\n\r\n // ─── Seleção ───────────────────────────────────────────────────────────────\r\n\r\n const chaveDe = (row: T, i: number) => String(rowKey ? rowKey(row, i) : i)\r\n\r\n /**\r\n * Índice de cada linha na lista completa. Ao agrupar, as linhas são desenhadas grupo a\r\n * grupo e o índice local recomeçava em 0 em cada um — o que dava chaves React repetidas,\r\n * a zebra a reiniciar e, sem `rowKey`, linhas de grupos diferentes a partilhar a mesma\r\n * chave de seleção.\r\n */\r\n const indicePorLinha = useMemo(() => {\r\n const m = new Map<T, number>()\r\n sorted.forEach((r, i) => m.set(r, i))\r\n return m\r\n }, [sorted])\r\n const visibleKeys = useMemo(\r\n () => pageData.map((row, i) => chaveDe(row, i)),\r\n // eslint-disable-next-line react-hooks/exhaustive-deps\r\n [pageData, rowKey]\r\n )\r\n const alternarLinha = (key: string) =>\r\n setSelectedKeys((prev) => {\r\n const next = new Set(prev)\r\n if (next.has(key)) next.delete(key)\r\n else next.add(key)\r\n onSelectionChange?.(next)\r\n return next\r\n })\r\n const alternarTodas = () =>\r\n setSelectedKeys((prev) => {\r\n const todasMarcadas = visibleKeys.length > 0 && visibleKeys.every((k) => prev.has(k))\r\n const next = new Set(prev)\r\n if (todasMarcadas) visibleKeys.forEach((k) => next.delete(k))\r\n else visibleKeys.forEach((k) => next.add(k))\r\n onSelectionChange?.(next)\r\n return next\r\n })\r\n const todasVisiveisMarcadas = visibleKeys.length > 0 && visibleKeys.every((k) => selectedKeys.has(k))\r\n const algumaVisivelMarcada = visibleKeys.some((k) => selectedKeys.has(k)) && !todasVisiveisMarcadas\r\n\r\n // ─── Handle externo da vista ───────────────────────────────────────────────\r\n\r\n // Sem deps: é lido de fora a pedido (ex: ao exportar), tem de estar sempre coerente com o\r\n // último render.\r\n useEffect(() => {\r\n if (!viewRef) return\r\n viewRef.current = {\r\n apply: (rows) => ordenar(filtrar(rows)),\r\n rows: sorted,\r\n hasFilters: hasActiveFilters,\r\n precisaDataset: hasActiveFilters || !!sort || isGrouped,\r\n estado: () => ({\r\n columnFilters: Object.fromEntries(activeFilters.map(([k, v]) => [k, [...v]])),\r\n rangeFilters: Object.fromEntries(activeRanges),\r\n sortKey: sort?.key ?? null,\r\n sortDir: sort ? (sort.dir === 1 ? 'asc' : 'desc') : 'asc',\r\n groupBy: groupByKeys,\r\n }),\r\n repor: (e) => {\r\n setColumnFilters(Object.fromEntries(Object.entries(e?.columnFilters ?? {}).map(([k, v]) => [k, new Set(v)])))\r\n setRangeFilters(e?.rangeFilters ?? {})\r\n setSort(e?.sortKey ? { key: e.sortKey, dir: e.sortDir === 'desc' ? -1 : 1 } : null)\r\n setGroupByKeys(e?.groupBy ?? [])\r\n // Grupos recolhidos não se guardam: a vista diz por que se agrupa, não o que estava\r\n // aberto no momento em que foi gravada.\r\n setCollapsedGroups(new Set())\r\n const precisaTudo =\r\n !!e?.sortKey ||\r\n !!e?.groupBy?.length ||\r\n Object.keys(e?.columnFilters ?? {}).length > 0 ||\r\n Object.keys(e?.rangeFilters ?? {}).length > 0\r\n if (precisaTudo && !allData) onDataNeeded?.()\r\n },\r\n }\r\n })\r\n\r\n // ─── Ações de filtro/ordenação ─────────────────────────────────────────────\r\n\r\n const toggleSort = (key: string) => {\r\n if (!allData) onDataNeeded?.()\r\n setSort((s) => (s?.key === key ? (s.dir === 1 ? { key, dir: -1 } : null) : { key, dir: 1 }))\r\n }\r\n\r\n /**\r\n * Selecção actual da coluna aberta, já materializada.\r\n *\r\n * No modo Excel, sem entrada no mapa está tudo seleccionado — e é aqui que isso passa de\r\n * implícito a explícito, na primeira vez que se desmarca alguma coisa. Só serve a coluna\r\n * aberta: `openFilterOptions` é dela.\r\n */\r\n const selecaoDaColuna = (key: string) =>\r\n new Set(columnFilters[key] ?? (modoExcel ? openFilterOptions : []))\r\n\r\n const aplicarSelecao = (key: string, sel: Set<string>) =>\r\n setColumnFilters((prev) => {\r\n const proximo = { ...prev }\r\n // Tudo marcado é o mesmo que não haver filtro. Apagar a chave desliga o funil e tira o\r\n // chip, em vez de deixar um filtro que não filtra nada.\r\n if (modoExcel && sel.size === openFilterOptions.length) delete proximo[key]\r\n else proximo[key] = sel\r\n return proximo\r\n })\r\n\r\n const toggleFilterValue = (key: string, val: string) => {\r\n if (!allData) onDataNeeded?.()\r\n const sel = selecaoDaColuna(key)\r\n if (sel.has(val)) sel.delete(val)\r\n else sel.add(val)\r\n aplicarSelecao(key, sel)\r\n }\r\n\r\n /** `(Selecionar tudo)` — trabalha sobre os `correspondentes`, não sobre o que está desenhado. */\r\n const alternarTodos = (key: string, marcar: boolean) => {\r\n if (!allData) onDataNeeded?.()\r\n const sel = selecaoDaColuna(key)\r\n for (const v of opcoesVisiveis.correspondentes) {\r\n if (marcar) sel.add(v)\r\n else sel.delete(v)\r\n }\r\n aplicarSelecao(key, sel)\r\n }\r\n\r\n const setRange = (key: string, campo: 'min' | 'max', valor: string, tipo: DataTableColumn<T>['sortType']) => {\r\n if (!allData) onDataNeeded?.()\r\n setRangeFilters((prev) => {\r\n const atual = { ...(prev[key] ?? {}) }\r\n // Datas chegam do `<input type=\"date\">` em 'aaaa-mm-dd'; guarda-se o timestamp para o\r\n // filtro comparar sempre números. O `max` de uma data cobre o dia todo.\r\n const n = tipo === 'date' ? timestampDeData(valor) + (campo === 'max' ? 86_399_999 : 0) : Number(valor)\r\n if (valor === '' || !Number.isFinite(n)) delete atual[campo]\r\n else atual[campo] = n\r\n const next = { ...prev }\r\n if (atual.min == null && atual.max == null) delete next[key]\r\n else next[key] = atual\r\n return next\r\n })\r\n }\r\n\r\n const clearColumnFilter = (key: string) => {\r\n setColumnFilters((prev) => {\r\n const n = { ...prev }\r\n delete n[key]\r\n return n\r\n })\r\n setRangeFilters((prev) => {\r\n const n = { ...prev }\r\n delete n[key]\r\n return n\r\n })\r\n }\r\n\r\n const clearAllFilters = () => {\r\n setColumnFilters({})\r\n setRangeFilters({})\r\n }\r\n\r\n const temIntervalo = (key: string) => {\r\n const r = rangeFilters[key]\r\n return !!r && (r.min != null || r.max != null)\r\n }\r\n // No modo Excel o funil acende com a chave presente, mesmo com o conjunto vazio.\r\n const temFiltro = (key: string) =>\r\n (modoExcel ? columnFilters[key] !== undefined : (columnFilters[key]?.size ?? 0) > 0) || temIntervalo(key)\r\n\r\n const handleExport = () => {\r\n if (onExport) return onExport(sorted)\r\n const csv = toCsv(columns, sorted)\r\n const blob = new Blob([`${csv}`], { type: 'text/csv;charset=utf-8;' })\r\n const url = URL.createObjectURL(blob)\r\n const a = document.createElement('a')\r\n a.href = url\r\n a.download = `${exportFileName}.csv`\r\n a.click()\r\n URL.revokeObjectURL(url)\r\n }\r\n\r\n const alignClass = (a?: DataTableColumn<T>['align']) =>\r\n a === 'right' ? 'text-right' : a === 'center' ? 'text-center' : 'text-left'\r\n\r\n /** `sticky: true` mantem o significado antigo: presa a esquerda. */\r\n const ePresaAEsquerda = (c: DataTableColumn<T>) => c.sticky === true || c.sticky === 'left'\r\n\r\n // Offset acumulado por coluna sticky — sem isto, 2+ colunas sticky ficavam todas em\r\n // `left:0`, sobrepostas (a 2.ª por cima da 1.ª). Colunas sticky sem `width` usam um valor\r\n // por defeito razoável para a acumulação (não afeta a largura real da coluna).\r\n const stickyLeft = useMemo(() => {\r\n const offsets: Record<string, number> = {}\r\n let acc = selectable ? 40 : 0\r\n for (const c of columns) {\r\n if (!ePresaAEsquerda(c)) continue\r\n offsets[c.key] = acc\r\n acc += c.width ?? DEFAULT_STICKY_WIDTH\r\n }\r\n return offsets\r\n }, [columns, selectable])\r\n\r\n // À direita acumula-se ao contrário: a última coluna encosta a 0 e as\r\n // anteriores empilham-se por cima da largura das seguintes.\r\n const stickyRight = useMemo(() => {\r\n const offsets: Record<string, number> = {}\r\n let acc = 0\r\n for (let i = columns.length - 1; i >= 0; i--) {\r\n const c = columns[i]\r\n if (c.sticky !== 'right') continue\r\n offsets[c.key] = acc\r\n acc += c.width ?? DEFAULT_STICKY_WIDTH\r\n }\r\n return offsets\r\n }, [columns])\r\n\r\n const estiloPreso = (c: DataTableColumn<T>): React.CSSProperties | undefined => {\r\n if (ePresaAEsquerda(c)) return { left: stickyLeft[c.key] }\r\n if (c.sticky === 'right') return { right: stickyRight[c.key] }\r\n return undefined\r\n }\r\n\r\n const nColunas = columns.length + (selectable ? 1 : 0)\r\n\r\n // Enquanto o dataset completo não chega, filtrar/ordenar/agrupar só olharia para a página —\r\n // melhor esperar do que mostrar uma lista errada por baixo de um total nos milhares.\r\n const aEsperarDataset = !allData && !!isAllDataLoading && (hasActiveFilters || !!sort || isGrouped)\r\n\r\n // Antes dos returns de 'a carregar'/erro: um hook depois deles muda a ordem dos hooks entre renders.\r\n // Por omissão o cabeçalho oferece ordenar, filtrar e agrupar (ver `filterable`/`groupable`). Colunas sem dados\r\n // em nenhuma linha (ex.: botões de ações) não têm o que filtrar nem agrupar.\r\n const linhasBase = allData ?? data\r\n const semDados = useMemo(\r\n () => new Set(columns.filter((c) => !linhasBase.some((r) => rawValue(r, c) !== null)).map((c) => c.key)),\r\n [columns, linhasBase]\r\n )\r\n const filtravel = (c: DataTableColumn<T>) => c.filterable ?? !semDados.has(c.key)\r\n const agrupavel = (c: DataTableColumn<T>) =>\r\n c.groupable ?? (!semDados.has(c.key) && !c.groupAggregate && (!c.sortType || c.sortType === 'text'))\r\n\r\n if (isLoading) {\r\n return <div className=\"py-16 text-center text-sm font-bold uppercase tracking-widest text-slate-400\">{L.loading}</div>\r\n }\r\n if (isError) {\r\n return <div className=\"py-16 text-center text-sm font-bold uppercase tracking-widest text-red-400\">{L.error}</div>\r\n }\r\n\r\n // ─── Sub-render: popup de filtro ───────────────────────────────────────────\r\n\r\n const renderPopup = (key: string, col: DataTableColumn<T> | undefined, tituloExtra?: string) => {\r\n const { opts, total: totalOpcoes } = opcoesVisiveis\r\n const sel = columnFilters[key]\r\n const isFiltered = temFiltro(key)\r\n const podeOrdenar = !!sortFromFilter && !!col && col.sortable !== false\r\n const temRange = !!col && (col.sortType === 'number' || col.sortType === 'date')\r\n return createPortal(\r\n <div\r\n ref={popupRef}\r\n style={{ position: 'fixed', top: popupPos!.top, left: popupPos!.left }}\r\n className=\"z-[100] w-72 max-w-[80vw] bg-white dark:bg-slate-900 border border-slate-200 dark:border-slate-700 shadow-2xl rounded-xl overflow-hidden\"\r\n >\r\n {tituloExtra && (\r\n <div className=\"px-3 pt-2.5 pb-2 border-b border-slate-100 dark:border-slate-800\">\r\n <span className=\"text-[9px] font-black uppercase tracking-widest text-proef-blue dark:text-proef-turquoise\">{tituloExtra}</span>\r\n </div>\r\n )}\r\n {!allData && isAllDataLoading && (\r\n // As opções à vista ainda são só as de `data`. Dizê-lo é melhor do que deixar\r\n // escolher de uma lista incompleta.\r\n <div className=\"px-2 py-1.5 bg-amber-50 dark:bg-amber-500/10 border-b border-amber-100 dark:border-amber-500/20 text-[10px] text-amber-700 dark:text-amber-300\">\r\n {L.loadingOptions}\r\n </div>\r\n )}\r\n {podeOrdenar && (\r\n <div className=\"border-b border-slate-100 dark:border-slate-800 p-1.5 space-y-0.5\">\r\n <button\r\n onClick={() => {\r\n if (!allData) onDataNeeded?.()\r\n setSort({ key, dir: 1 })\r\n }}\r\n className={`appearance-none bg-transparent border-0 cursor-pointer w-full flex items-center gap-2 px-2.5 py-1.5 text-[11px] font-semibold text-left hover:bg-slate-50 dark:hover:bg-slate-800 ${\r\n sort?.key === key && sort.dir === 1 ? 'text-proef-blue dark:text-proef-turquoise bg-slate-50 dark:bg-slate-800' : 'text-slate-600 dark:text-slate-300'\r\n }`}\r\n >\r\n ▲ {L.sortAsc}\r\n </button>\r\n <button\r\n onClick={() => {\r\n if (!allData) onDataNeeded?.()\r\n setSort({ key, dir: -1 })\r\n }}\r\n className={`appearance-none bg-transparent border-0 cursor-pointer w-full flex items-center gap-2 px-2.5 py-1.5 text-[11px] font-semibold text-left hover:bg-slate-50 dark:hover:bg-slate-800 ${\r\n sort?.key === key && sort.dir === -1 ? 'text-proef-blue dark:text-proef-turquoise bg-slate-50 dark:bg-slate-800' : 'text-slate-600 dark:text-slate-300'\r\n }`}\r\n >\r\n ▼ {L.sortDesc}\r\n </button>\r\n </div>\r\n )}\r\n <div className=\"p-2\">\r\n {temRange && (\r\n // Intervalo antes da lista: numa coluna numérica ou de datas é o que se procura\r\n // (\"acima de 30 dias\", \"a partir de julho\"), não um valor exato numa lista de dezenas.\r\n <div className=\"flex items-center gap-1.5 mb-2\">\r\n <input\r\n type={col!.sortType === 'date' ? 'date' : 'number'}\r\n defaultValue=\"\"\r\n onChange={(e) => setRange(key, 'min', e.target.value, col!.sortType)}\r\n placeholder={L.min}\r\n title={L.min}\r\n className=\"w-1/2 bg-slate-50 dark:bg-slate-800 dark:text-slate-100 dark:[color-scheme:dark] border border-slate-200 dark:border-slate-700 rounded-lg px-2 py-1.5 text-xs outline-none focus:border-proef-blue dark:focus:border-proef-turquoise\"\r\n />\r\n <input\r\n type={col!.sortType === 'date' ? 'date' : 'number'}\r\n defaultValue=\"\"\r\n onChange={(e) => setRange(key, 'max', e.target.value, col!.sortType)}\r\n placeholder={L.max}\r\n title={L.max}\r\n className=\"w-1/2 bg-slate-50 dark:bg-slate-800 dark:text-slate-100 dark:[color-scheme:dark] border border-slate-200 dark:border-slate-700 rounded-lg px-2 py-1.5 text-xs outline-none focus:border-proef-blue dark:focus:border-proef-turquoise\"\r\n />\r\n </div>\r\n )}\r\n <input\r\n autoFocus\r\n value={filterSearch}\r\n onChange={(e) => setFilterSearch(e.target.value)}\r\n placeholder={L.search}\r\n className=\"w-full bg-slate-50 dark:bg-slate-800 dark:text-slate-100 border border-slate-200 dark:border-slate-700 rounded-lg px-2 py-1.5 text-xs outline-none focus:border-proef-blue dark:focus:border-proef-turquoise mb-2\"\r\n />\r\n {/* `overflow-x-hidden` explícito: com só `overflow-y-auto`, o CSS calcula\r\n `overflow-x: auto` e a barra vertical rouba largura ao conteúdo até aparecer uma\r\n segunda barra, horizontal, que não faz falta nenhuma. */}\r\n <div className=\"max-h-64 overflow-y-auto overflow-x-hidden space-y-0.5\">\r\n {modoExcel && opts.length > 0 && (() => {\r\n // Tri-state sobre os valores que a pesquisa apanha: ✓ todos, – alguns, vazio nenhum.\r\n const marcados = opcoesVisiveis.correspondentes.filter((v) => (sel ? sel.has(v) : true)).length\r\n const todos = marcados === opcoesVisiveis.correspondentes.length\r\n const alguns = marcados > 0 && !todos\r\n return (\r\n <button\r\n onClick={() => alternarTodos(key, !todos)}\r\n className=\"appearance-none bg-transparent border-0 border-b border-slate-100 dark:border-slate-800 cursor-pointer w-full flex items-center gap-2 px-1.5 py-1 mb-0.5 hover:bg-slate-50 dark:hover:bg-slate-800 text-left normal-case tracking-normal\"\r\n >\r\n <span\r\n className={`w-3.5 h-3.5 flex items-center justify-center border shrink-0 rounded ${\r\n todos || alguns ? 'bg-proef-navy dark:bg-proef-turquoise border-proef-navy dark:border-proef-turquoise text-white dark:text-proef-navy' : 'border-slate-300 dark:border-slate-600'\r\n }`}\r\n >\r\n {todos && <span className=\"text-[9px] leading-none\">✓</span>}\r\n {alguns && <span className=\"text-[9px] leading-none\">–</span>}\r\n </span>\r\n <span className=\"text-xs text-slate-700 dark:text-slate-200 font-semibold break-words\">\r\n {filterSearch ? L.selectAllResults : L.selectAll}\r\n </span>\r\n </button>\r\n )\r\n })()}\r\n {opts.length === 0 ? (\r\n <p className=\"text-[11px] text-slate-400 text-center py-3\">{L.noOptions}</p>\r\n ) : (\r\n opts.map((v) => {\r\n // Sem entrada no mapa está tudo marcado, daí o `sel ? … : modoExcel`.\r\n const checked = sel ? sel.has(v) : modoExcel\r\n return (\r\n <button\r\n key={v}\r\n onClick={() => toggleFilterValue(key, v)}\r\n className=\"appearance-none bg-transparent border-0 cursor-pointer w-full flex items-center gap-2 px-1.5 py-1 hover:bg-slate-50 dark:hover:bg-slate-800 text-left normal-case tracking-normal\"\r\n >\r\n <span\r\n className={`w-3.5 h-3.5 flex items-center justify-center border shrink-0 rounded ${\r\n checked ? 'bg-proef-navy dark:bg-proef-turquoise border-proef-navy dark:border-proef-turquoise text-white dark:text-proef-navy' : 'border-slate-300 dark:border-slate-600'\r\n }`}\r\n >\r\n {checked && <span className=\"text-[9px] leading-none\">✓</span>}\r\n </span>\r\n <span className=\"text-xs text-slate-700 dark:text-slate-200 font-normal break-words\">{v}</span>\r\n </button>\r\n )\r\n })\r\n )}\r\n </div>\r\n {totalOpcoes > opts.length && (\r\n <p className=\"text-[10.5px] text-slate-400 text-center pt-2\">\r\n {L.moreOptions.replace('{shown}', String(opts.length)).replace('{total}', String(totalOpcoes))}\r\n </p>\r\n )}\r\n {isFiltered && (\r\n <button\r\n onClick={() => clearColumnFilter(key)}\r\n className=\"appearance-none bg-transparent border-0 border-t border-slate-100 dark:border-slate-800 cursor-pointer w-full mt-2 pt-2 text-[10px] font-black uppercase tracking-wider text-red-400 hover:text-red-500\"\r\n >\r\n {L.clear}\r\n </button>\r\n )}\r\n </div>\r\n </div>,\r\n document.body\r\n )\r\n }\r\n\r\n const botaoFunil = (key: string, col: DataTableColumn<T> | undefined, extra?: { label: string }) => {\r\n const isOpen = openFilter === key\r\n const isFiltered = temFiltro(key)\r\n return (\r\n <div\r\n key={key}\r\n className=\"relative\"\r\n ref={(el) => {\r\n if (el) filterRefs.current.set(key, el)\r\n else filterRefs.current.delete(key)\r\n }}\r\n >\r\n <button\r\n onClick={() => {\r\n // Abrir o funil é a 1.ª vez que as opções de todas as páginas fazem falta — é aqui\r\n // que se pede o dataset completo.\r\n if (!isOpen && !allData) onDataNeeded?.()\r\n setOpenFilter(isOpen ? null : key)\r\n setFilterSearch('')\r\n }}\r\n className={`appearance-none border-0 bg-transparent cursor-pointer p-0.5 transition-colors ${\r\n extra\r\n ? isFiltered || isOpen\r\n ? 'text-violet-600 dark:text-violet-300'\r\n : 'text-slate-300 dark:text-slate-600 hover:text-slate-500 dark:hover:text-slate-300'\r\n : isFiltered || isOpen\r\n ? 'text-proef-turquoise'\r\n : 'text-slate-300 dark:text-slate-600 hover:text-slate-500 dark:hover:text-slate-300'\r\n }`}\r\n // Sem isto o funil e um botao sem nome acessivel: nem leitor de ecra nem teste lhe\r\n // chegam pelo nome da coluna.\r\n title={extra?.label ?? col?.label}\r\n >\r\n <Funnel size={extra ? 10 : 11} className={isFiltered ? 'fill-current' : ''} />\r\n </button>\r\n {isOpen && popupPos && renderPopup(key, col, extra?.label)}\r\n </div>\r\n )\r\n }\r\n\r\n // ─── Sub-render: linhas ────────────────────────────────────────────────────\r\n\r\n const renderLinha = (row: T, i: number) => {\r\n const k = chaveDe(row, i)\r\n const marcada = selectable && selectedKeys.has(k)\r\n return (\r\n <tr\r\n key={k}\r\n onClick={() => onRowClick?.(row)}\r\n onDoubleClick={() => onRowDoubleClick?.(row)}\r\n className={`group even:bg-slate-50/50 dark:even:bg-slate-800/40 hover:bg-slate-100/70 dark:hover:bg-slate-800/70 transition-colors ${\r\n onRowClick || onRowDoubleClick ? 'cursor-pointer' : ''\r\n } ${marcada ? 'bg-proef-turquoise/5' : ''} ${rowClassName?.(row) ?? ''}`}\r\n >\r\n {selectable && (\r\n <td\r\n className=\"px-3 py-2.5 border-b border-slate-100 dark:border-slate-800 w-10\"\r\n onClick={(e) => {\r\n e.stopPropagation()\r\n alternarLinha(k)\r\n }}\r\n >\r\n <span\r\n className={`w-4 h-4 flex items-center justify-center border transition-colors rounded ${\r\n marcada ? 'bg-proef-navy dark:bg-proef-turquoise border-proef-navy dark:border-proef-turquoise text-white dark:text-proef-navy' : 'border-slate-300 dark:border-slate-600 hover:border-slate-400 dark:hover:border-slate-500'\r\n }`}\r\n >\r\n {marcada && <span className=\"text-[9px] leading-none\">✓</span>}\r\n </span>\r\n </td>\r\n )}\r\n {columns.map((c) => (\r\n <td\r\n key={c.key}\r\n style={estiloPreso(c)}\r\n // Fundo sólido explícito (não `bg-inherit`) — uma coluna sticky com fundo\r\n // transparente/translúcido deixa o conteúdo que passa por baixo, ao fazer\r\n // scroll horizontal, transparecer por trás do texto (sobreposição ilegível).\r\n // `group-hover` porque `:hover` isolado na célula não reage ao hover na linha.\r\n className={`px-3 py-2.5 border-b border-slate-100 dark:border-slate-800 whitespace-nowrap ${alignClass(c.align)} ${\r\n c.sticky ? `sticky z-[1] ${c.sticky === 'right' ? 'border-l border-slate-200 dark:border-slate-700' : ''} ${i % 2 === 1 ? 'bg-slate-50 dark:bg-slate-800' : 'bg-white dark:bg-slate-900'} group-hover:bg-slate-100 dark:group-hover:bg-slate-800` : ''\r\n } ${c.cellClassName ?? ''}`}\r\n >\r\n {(() => {\r\n const conteudo = c.render ? c.render(row) : rawValue(row, c) ?? <span className=\"text-slate-400\">{EMPTY}</span>\r\n const max = larguraMax(c)\r\n if (!max) return conteudo\r\n const v = rawValue(row, c)\r\n return (\r\n <div className=\"overflow-hidden text-ellipsis\" style={{ maxWidth: max, width: larguras[c.key] }} title={v !== null && v !== undefined ? String(v) : undefined}>\r\n {conteudo}\r\n </div>\r\n )\r\n })()}\r\n </td>\r\n ))}\r\n </tr>\r\n )\r\n }\r\n\r\n // Colunas com valor nas linhas de grupo/total: o rótulo ocupa as colunas até à primeira com valor; daí em\r\n // diante cada coluna tem a sua célula, alinhada com as linhas.\r\n\r\n const primeiraComValor = columns.findIndex((c) => !!c.groupAggregate)\r\n const colunasComValor = primeiraComValor >= 0 ? columns.slice(primeiraComValor) : []\r\n const colunasRotuloGrupo = primeiraComValor >= 0 ? primeiraComValor + (selectable ? 1 : 0) : nColunas\r\n\r\n const renderGrupos = (nos: GrupoNo<T>[], nivel: number): React.ReactNode =>\r\n nos.map((no) => {\r\n const recolhido = collapsedGroups.has(no.path)\r\n return (\r\n <Fragment key={no.path}>\r\n <tr className=\"bg-slate-50/80 dark:bg-slate-800/60 cursor-pointer select-none\" onClick={() => alternarColapso(no.path)}>\r\n <td colSpan={colunasRotuloGrupo} className=\"px-3 py-1.5 border-b border-t border-slate-100 dark:border-slate-800\">\r\n <div className=\"flex items-center gap-1.5\" style={{ paddingLeft: nivel * 16 }}>\r\n <Caret\r\n size={11}\r\n className={`text-proef-blue/60 dark:text-proef-turquoise/70 transition-transform shrink-0 ${recolhido ? '' : 'rotate-90'}`}\r\n />\r\n <span className=\"text-[10px] font-black uppercase tracking-[0.15em] text-proef-blue/70 dark:text-proef-turquoise/80\">{no.val}</span>\r\n <span className=\"text-[10px] text-slate-400 ml-1\">({no.items.length})</span>\r\n </div>\r\n </td>\r\n {colunasComValor.map((c) => (\r\n <td key={c.key} className={`px-3 py-1.5 border-b border-t border-slate-100 dark:border-slate-800 whitespace-nowrap text-xs font-bold text-slate-700 dark:text-slate-200 ${alignClass(c.align)}`}>\r\n {c.groupAggregate?.(no.items)}\r\n </td>\r\n ))}\r\n </tr>\r\n {!recolhido &&\r\n (no.filhos.length > 0\r\n ? renderGrupos(no.filhos, nivel + 1)\r\n : no.items.map((row) => renderLinha(row, indicePorLinha.get(row) ?? 0)))}\r\n </Fragment>\r\n )\r\n })\r\n\r\n // ─── Render ────────────────────────────────────────────────────────────────\r\n\r\n return (\r\n <div>\r\n <div className=\"flex items-center gap-2 mb-2.5\">\r\n <span className=\"text-xs text-slate-500 dark:text-slate-400\">{L.results.replace('{count}', String(sorted.length))}</span>\r\n <div className=\"ml-auto flex items-center gap-2\">\r\n {extraToolbar}\r\n {exportFileName && sorted.length > 0 && (\r\n <button\r\n onClick={handleExport}\r\n className=\"appearance-none cursor-pointer inline-flex items-center gap-1.5 h-8 px-3.5 rounded-lg border border-proef-navy dark:border-proef-turquoise bg-proef-navy dark:bg-proef-turquoise text-white dark:text-proef-navy hover:bg-proef-blue dark:hover:bg-proef-turquoise/80 text-[11px] font-black uppercase tracking-widest transition-colors\"\r\n >\r\n ⭳ {L.export}\r\n </button>\r\n )}\r\n </div>\r\n </div>\r\n\r\n {/* Chips do agrupamento. */}\r\n {isGrouped && (\r\n <div className=\"mb-2 flex items-center gap-2 flex-wrap\">\r\n {groupByKeys.map((key) => {\r\n const col = columns.find((c) => c.key === key)\r\n return (\r\n <div\r\n key={key}\r\n className=\"flex items-center gap-1.5 bg-violet-100 dark:bg-violet-500/20 text-violet-800 dark:text-violet-200 px-2.5 py-1 text-[10px] font-black uppercase tracking-wider\"\r\n >\r\n <Layers size={9} />\r\n <span>{col?.label ?? key}</span>\r\n <button\r\n onClick={() => removerGrupo(key)}\r\n className=\"appearance-none bg-transparent border-0 cursor-pointer ml-0.5 hover:text-violet-600\"\r\n >\r\n ✕\r\n </button>\r\n </div>\r\n )\r\n })}\r\n <button\r\n onClick={limparGrupos}\r\n className=\"appearance-none bg-transparent border-0 cursor-pointer text-[10px] font-black uppercase tracking-wider text-slate-400 hover:text-red-500 transition-colors\"\r\n >\r\n {L.clearGroups}\r\n </button>\r\n </div>\r\n )}\r\n\r\n {/* Chips dos filtros ativos. */}\r\n {(activeFilters.length > 0 || activeRanges.length > 0) && (\r\n <div className=\"mb-2 flex items-center gap-2 flex-wrap\">\r\n {activeRanges.map(([key, r]) => {\r\n const col = columns.find((c) => c.key === key)\r\n const fmt = (v?: number) =>\r\n v == null ? '' : col?.sortType === 'date' ? new Date(v).toLocaleDateString() : String(v)\r\n const txt =\r\n r.min != null && r.max != null\r\n ? `${fmt(r.min)} – ${fmt(r.max)}`\r\n : r.min != null\r\n ? `≥ ${fmt(r.min)}`\r\n : `≤ ${fmt(r.max)}`\r\n return (\r\n <div\r\n key={`r-${key}`}\r\n className=\"flex items-center gap-1.5 bg-proef-turquoise/15 text-proef-navy dark:text-proef-turquoise px-2.5 py-1 text-[10px] font-black uppercase tracking-wider\"\r\n >\r\n <span>\r\n {col?.label ?? key}: {txt}\r\n </span>\r\n <button\r\n onClick={() => clearColumnFilter(key)}\r\n className=\"appearance-none bg-transparent border-0 cursor-pointer ml-0.5 hover:text-proef-blue dark:hover:text-proef-turquoise\"\r\n >\r\n ✕\r\n </button>\r\n </div>\r\n )\r\n })}\r\n {activeFilters.flatMap(([key, sel]) => {\r\n const col = columns.find((c) => c.key === key)\r\n const extra = !col ? columns.flatMap((c) => c.extraFilters ?? []).find((f) => f.key === key) : undefined\r\n const chipLabel = col?.label ?? extra?.label ?? key\r\n const arr = Array.from(sel)\r\n\r\n // Coluna com cores por valor: um chip colorido por valor, removível à parte.\r\n if (col?.valueChipColors) {\r\n const cores = col.valueChipColors\r\n return arr.map((v) => (\r\n <div\r\n key={`${key}:${v}`}\r\n className={`flex items-center gap-1.5 px-2.5 py-1 text-[10px] font-black uppercase tracking-wider ${\r\n cores[v] ?? 'bg-proef-turquoise/15 text-proef-navy dark:text-proef-turquoise'\r\n }`}\r\n >\r\n <span>\r\n {chipLabel}: {v}\r\n </span>\r\n <button\r\n onClick={() => toggleFilterValue(key, v)}\r\n className=\"appearance-none bg-transparent border-0 cursor-pointer ml-0.5 hover:opacity-70\"\r\n >\r\n ✕\r\n </button>\r\n </div>\r\n ))\r\n }\r\n\r\n // Com 0 marcados o chip tem de dizer \"0\" — juntar uma lista vazia dava um chip mudo.\r\n const txt =\r\n arr.length > 2 || arr.length === 0\r\n ? L.selected.replace('{count}', String(arr.length))\r\n : arr.join(', ')\r\n return [\r\n <div\r\n key={key}\r\n className=\"flex items-center gap-1.5 bg-proef-turquoise/15 text-proef-navy dark:text-proef-turquoise px-2.5 py-1 text-[10px] font-black uppercase tracking-wider\"\r\n >\r\n <span>\r\n {chipLabel}: {txt}\r\n </span>\r\n <button\r\n onClick={() => clearColumnFilter(key)}\r\n className=\"appearance-none bg-transparent border-0 cursor-pointer ml-0.5 hover:text-proef-blue dark:hover:text-proef-turquoise\"\r\n >\r\n ✕\r\n </button>\r\n </div>,\r\n ]\r\n })}\r\n <button\r\n onClick={clearAllFilters}\r\n className=\"appearance-none bg-transparent border-0 cursor-pointer text-[10px] font-black uppercase tracking-wider text-slate-400 hover:text-red-500 transition-colors\"\r\n >\r\n {L.clearAll}\r\n </button>\r\n </div>\r\n )}\r\n\r\n {/* rounded-xl: os mesmos cantos dos cartões da biblioteca (Ui.tsx) e das tabelas dos portais. */}\r\n <div className=\"overflow-auto rounded-xl border border-slate-200 dark:border-slate-700 bg-white dark:bg-slate-900\" style={{ maxHeight }}>\r\n <table className=\"w-full text-[13px] border-collapse dark:text-slate-200\">\r\n <thead className=\"sticky top-0 z-[2]\">\r\n <tr>\r\n {selectable && (\r\n <th className=\"bg-slate-50 dark:bg-slate-800 px-3 py-2.5 border-b border-slate-200 dark:border-slate-700 w-10 sticky left-0 z-[3]\">\r\n <button\r\n onClick={alternarTodas}\r\n className={`appearance-none cursor-pointer w-4 h-4 flex items-center justify-center border transition-colors rounded ${\r\n todasVisiveisMarcadas\r\n ? 'bg-proef-navy dark:bg-proef-turquoise border-proef-navy dark:border-proef-turquoise text-white dark:text-proef-navy'\r\n : algumaVisivelMarcada\r\n ? 'bg-slate-300 dark:bg-slate-600 border-slate-300 dark:border-slate-600 text-white'\r\n : 'bg-white dark:bg-slate-900 border-slate-300 dark:border-slate-600 hover:border-slate-400 dark:hover:border-slate-500'\r\n }`}\r\n >\r\n {(todasVisiveisMarcadas || algumaVisivelMarcada) && <span className=\"text-[9px] leading-none\">✓</span>}\r\n </button>\r\n </th>\r\n )}\r\n {columns.map((c) => {\r\n const isFiltered = temFiltro(c.key)\r\n const podeOrdenar = c.sortable !== false\r\n const podeAgrupar = agrupavel(c)\r\n const agrupada = groupByKeys.includes(c.key)\r\n return (\r\n <th\r\n key={c.key}\r\n style={{\r\n ...(c.width ? { minWidth: c.width } : {}),\r\n ...(larguras[c.key] ? { width: larguras[c.key], minWidth: larguras[c.key], maxWidth: larguras[c.key] } : {}),\r\n ...(estiloPreso(c) ?? {}),\r\n }}\r\n className={`group/th relative bg-slate-50 dark:bg-slate-800 text-[11.5px] uppercase tracking-wide text-slate-500 dark:text-slate-400 font-semibold px-3 py-2.5 border-b border-slate-200 dark:border-slate-700 whitespace-nowrap select-none ${alignClass(\r\n c.align\r\n )} ${c.sticky ? `sticky z-[3] bg-slate-50 dark:bg-slate-800 ${c.sticky === 'right' ? 'border-l border-slate-200 dark:border-slate-700' : ''}` : ''} ${c.headerClassName ?? ''}`}\r\n >\r\n {resizableColumns && (\r\n // Pega no limite direito: arrastar redimensiona; duplo clique repõe a largura automática\r\n <span\r\n role=\"separator\"\r\n aria-orientation=\"vertical\"\r\n title={L.resizeColumn}\r\n onMouseDown={(e) => iniciarResize(e, c.key)}\r\n onDoubleClick={() => reporLargura(c.key)}\r\n onClick={(e) => e.stopPropagation()}\r\n className=\"absolute right-0 top-0 z-[4] h-full w-1.5 cursor-col-resize bg-transparent hover:bg-proef-blue/40 group-hover/th:bg-slate-300/60 dark:group-hover/th:bg-slate-600/60\"\r\n />\r\n )}\r\n <div className=\"flex items-center gap-1.5\">\r\n {podeOrdenar ? (\r\n <button\r\n onClick={() => toggleSort(c.key)}\r\n className=\"appearance-none bg-transparent border-0 cursor-pointer p-0 uppercase tracking-wide text-slate-500 dark:text-slate-400 hover:text-proef-blue dark:hover:text-proef-turquoise font-semibold\"\r\n >\r\n {c.label}\r\n {sort?.key === c.key && <span className=\"ml-1\">{sort.dir === 1 ? '▲' : '▼'}</span>}\r\n </button>\r\n ) : (\r\n <span>{c.label}</span>\r\n )}\r\n\r\n {podeAgrupar && (\r\n <button\r\n onClick={() => alternarGrupo(c.key)}\r\n title={(agrupada ? L.ungroup : L.groupBy).replace('{label}', c.label)}\r\n className={`appearance-none bg-transparent border-0 cursor-pointer p-0.5 transition-colors ${\r\n agrupada ? 'text-violet-600 dark:text-violet-300' : 'text-slate-300 dark:text-slate-600 hover:text-slate-500 dark:hover:text-slate-300'\r\n }`}\r\n >\r\n <Layers size={10} />\r\n </button>\r\n )}\r\n\r\n {filtravel(c) && botaoFunil(c.key, c)}\r\n\r\n {c.extraFilters?.map((ef) => botaoFunil(ef.key, undefined, { label: ef.label }))}\r\n </div>\r\n {c.headerExtra && (\r\n <div className=\"mt-0.5 normal-case font-normal tracking-normal\">{c.headerExtra(filtered)}</div>\r\n )}\r\n </th>\r\n )\r\n })}\r\n </tr>\r\n </thead>\r\n <tbody>\r\n {aEsperarDataset ? (\r\n <tr>\r\n <td colSpan={nColunas} className=\"py-16 text-center text-sm font-bold uppercase tracking-widest text-slate-400\">\r\n {L.loading}\r\n </td>\r\n </tr>\r\n ) : isGrouped ? (\r\n grupos.length === 0 ? (\r\n <tr>\r\n <td colSpan={nColunas} className=\"py-16 text-center text-sm text-slate-400\">\r\n {L.empty}\r\n </td>\r\n </tr>\r\n ) : (\r\n renderGrupos(grupos, 0)\r\n )\r\n ) : sorted.length === 0 ? (\r\n <tr>\r\n <td colSpan={nColunas} className=\"py-16 text-center\">\r\n {emptyIcon && <div className=\"flex justify-center mb-2\">{emptyIcon}</div>}\r\n <p className=\"text-sm text-slate-400\">{L.empty}</p>\r\n {emptySubLabel && <p className=\"text-xs text-slate-400 mt-1\">{emptySubLabel}</p>}\r\n </td>\r\n </tr>\r\n ) : (\r\n pageData.map((row, i) => renderLinha(row, i))\r\n )}\r\n </tbody>\r\n {totalRow && !aEsperarDataset && sorted.length > 0 && (\r\n <tfoot className=\"sticky bottom-0 z-[2]\">\r\n <tr className=\"bg-slate-100 dark:bg-slate-800 font-black text-slate-800 dark:text-slate-100\">\r\n <td colSpan={colunasRotuloGrupo} className=\"px-3 py-2 border-t-2 border-slate-200 dark:border-slate-700 text-[10px] uppercase tracking-widest\">\r\n {L.total}\r\n </td>\r\n {colunasComValor.map((c) => (\r\n <td key={c.key} className={`px-3 py-2 border-t-2 border-slate-200 dark:border-slate-700 whitespace-nowrap text-xs ${alignClass(c.align)}`}>\r\n {c.groupAggregate?.(sorted)}\r\n </td>\r\n ))}\r\n </tr>\r\n </tfoot>\r\n )}\r\n </table>\r\n </div>\r\n\r\n {paginando && (totalPages > 1 || (pageSizeOptions?.length ?? 0) > 0) && (\r\n <div className=\"mt-2.5 flex items-center justify-between gap-2\">\r\n <span className=\"text-[10px] font-black uppercase tracking-widest text-slate-400\">\r\n {L.page.replace('{page}', String(currentPage)).replace('{pages}', String(totalPages))}\r\n </span>\r\n <div className=\"flex items-center gap-1\">\r\n {(pageSizeOptions?.length ?? 0) > 0 && (\r\n <span className=\"flex items-center gap-1.5 mr-2 text-[10px] font-black uppercase tracking-widest text-slate-400\">\r\n {L.perPage}\r\n {/* Mesma lista suspensa dos portais (Escolha), não o <select> nativo. */}\r\n <Escolha\r\n rotulo={L.perPage}\r\n valor={String(pageSizeAtual)}\r\n onMudar={(v) => setPageSizeAtual(Number(v))}\r\n className=\"w-20 normal-case tracking-normal font-normal\"\r\n opcoes={[...new Set([...(pageSizeOptions ?? []), pageSizeAtual!])].sort((a, b) => a - b).map(String)}\r\n />\r\n </span>\r\n )}\r\n <button\r\n disabled={currentPage <= 1}\r\n onClick={() => setPage((p) => Math.max(1, p - 1))}\r\n className=\"appearance-none cursor-pointer bg-white dark:bg-slate-900 border border-slate-200 dark:border-slate-700 text-[10px] font-black uppercase rounded-lg tracking-widest px-3 py-1.5 text-slate-500 dark:text-slate-400 hover:enabled:border-proef-navy dark:hover:enabled:border-proef-turquoise hover:enabled:text-proef-navy dark:hover:enabled:text-proef-turquoise disabled:opacity-30 disabled:cursor-not-allowed transition-colors\"\r\n >\r\n {L.prev}\r\n </button>\r\n <button\r\n disabled={currentPage >= totalPages}\r\n onClick={() => setPage((p) => Math.min(totalPages, p + 1))}\r\n className=\"appearance-none cursor-pointer bg-white dark:bg-slate-900 border border-slate-200 dark:border-slate-700 text-[10px] font-black uppercase rounded-lg tracking-widest px-3 py-1.5 text-slate-500 dark:text-slate-400 hover:enabled:border-proef-navy dark:hover:enabled:border-proef-turquoise hover:enabled:text-proef-navy dark:hover:enabled:text-proef-turquoise disabled:opacity-30 disabled:cursor-not-allowed transition-colors\"\r\n >\r\n {L.next}\r\n </button>\r\n </div>\r\n </div>\r\n )}\r\n </div>\r\n )\r\n}\r\n"],"names":["v"],"mappings":"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"}
1
+ {"version":3,"file":"DataTable.js","sources":["../../../../../../src/legacy/components/molecules/DataTable/DataTable.tsx"],"sourcesContent":["import React, { Fragment, useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react'\r\nimport Escolha from '../../ui/Escolha'\r\nimport FilterChips from '../../../../v1/primitives/display/FilterChips/FilterChips'\r\nimport Pagination from '../../../../v1/components/data/Pagination/Pagination'\r\nimport { createPortal } from 'react-dom'\r\n\r\n// ─── Tipos ──────────────────────────────────────────────────────────────────\r\n\r\n/** @deprecated Legacy — não usar em código novo. Ver Storybook › Legacy. */\r\nexport interface DataTableColumn<T> {\r\n key: string\r\n label: string\r\n /** Valor usado p/ ordenar, filtrar e exportar. Sem isto usa-se row[key]. */\r\n value?: (row: T) => string | number | null | undefined\r\n /** Conteúdo apresentado. Sem isto mostra o value. */\r\n render?: (row: T) => React.ReactNode\r\n sortType?: 'text' | 'number' | 'date'\r\n /**\r\n * Fixa a coluna no scroll horizontal (position: sticky).\r\n * `true` ou `'left'` prende à esquerda; `'right'` prende à direita — útil para\r\n * a coluna de ações, que de outro modo fica fora de vista (e com os botões\r\n * cortados) assim que a tabela e mais larga do que o espaço disponível.\r\n */\r\n sticky?: boolean | 'left' | 'right'\r\n align?: 'left' | 'right' | 'center'\r\n /** minWidth em px. */\r\n width?: number\r\n /** Largura máxima em px (o conteúdo corta com \"…\" e mostra o valor completo no tooltip). Sobrepõe-se a `defaultMaxColumnWidth`. */\r\n maxWidth?: number\r\n /**\r\n * Filtro por coluna (distinct dos valores, multi-seleção; intervalo nas colunas number/date).\r\n * Por omissão LIGADO em todas as colunas com dados — `false` desliga. Colunas sem dados (ex.: ações) não o mostram.\r\n */\r\n filterable?: boolean\r\n /** Desliga a ordenação nesta coluna (default: ordenável). */\r\n sortable?: boolean\r\n /**\r\n * Agrupar por esta coluna. Por omissão LIGADO nas colunas de texto com dados e DESLIGADO nas de valor contínuo\r\n * (`sortType` number/date, ou com `groupAggregate`), onde cada linha daria o seu próprio grupo. `true`/`false` forçam.\r\n */\r\n groupable?: boolean\r\n /** Chave usada só para ORDENAR os grupos (sobrepõe-se a `value` ao agrupar por esta coluna). */\r\n groupSortKey?: (row: T) => string | number | null | undefined\r\n /**\r\n * Filtros extra no cabeçalho desta coluna — para filtrar por um campo que não tem coluna\r\n * própria (ex: filtrar a coluna \"Obra\" por concelho). Partilham o funil da coluna: o popup\r\n * mostra no topo um seletor (coluna + cada filtro extra) para escolher a lista a filtrar.\r\n */\r\n extraFilters?: { key: string; label: string; value: (row: T) => string | number | null | undefined }[]\r\n headerClassName?: string\r\n cellClassName?: string\r\n /** Conteúdo extra por baixo do título da coluna (ex: somatório). Recebe as linhas já filtradas. */\r\n headerExtra?: (rows: T[]) => React.ReactNode\r\n /**\r\n * Valor desta coluna na linha de cada grupo (ex: soma em €), alinhado com a coluna. Recebe as linhas do grupo\r\n * (já filtradas). Também é usado na linha de total (`totalRow`), com todas as linhas filtradas.\r\n */\r\n groupAggregate?: (rows: T[]) => React.ReactNode\r\n /**\r\n * Cores por valor para os chips de filtro ativo (valor → classes tailwind). Com isto, cada\r\n * valor escolhido ganha o seu chip, removível à parte.\r\n */\r\n valueChipColors?: Record<string, string>\r\n /**\r\n * Vários valores de filtro por linha (ex.: um equipamento com várias aplicações). Alimenta a lista do funil,\r\n * o filtro (a linha passa se QUALQUER um dos seus valores estiver escolhido) e os chips. Sem isto, um valor\r\n * por linha (`value`). Ao agrupar, usa o `value` da linha (a combinação de valores, ex.: \"Hardware, Rede\").\r\n */\r\n filterValues?: (row: T) => ReadonlyArray<string | number | null | undefined>\r\n /**\r\n * Escondida à partida no layout de colunas (`useColumnLayout` e o \"Repor\" do `ColumnPicker`). A tabela em\r\n * si desenha as colunas que recebe.\r\n */\r\n defaultHidden?: boolean\r\n}\r\n\r\n/**\r\n * Ordenação, filtros e agrupamento do cabeçalho vivem dentro da tabela. Quem está por fora\r\n * (ex: exportar para Excel, guardar uma vista) precisa deles — daí este handle, preenchido\r\n * em `viewRef`.\r\n * @deprecated Legacy — não usar em código novo. Ver Storybook › Legacy.\r\n */\r\nexport interface DataTableView<T> {\r\n /** Aplica ordenação + filtros de coluna a qualquer lista (ex: ao dataset completo). */\r\n apply: (rows: T[]) => T[]\r\n /** Linhas à vista, já filtradas e ordenadas (todas as páginas). */\r\n rows: T[]\r\n hasFilters: boolean\r\n /**\r\n * Há filtros, ordenação ou agrupamento ativos — tudo do lado do cliente. Quem alimenta a\r\n * tabela usa isto para não largar o dataset completo: sem ele, filtrar sobre a página dá\r\n * uma lista vazia com o total nos milhares.\r\n */\r\n precisaDataset: boolean\r\n /** Estado guardável da vista. */\r\n estado: () => DataTableEstado\r\n /** Repõe um estado guardado (ex: ao aplicar uma vista do utilizador). */\r\n repor: (e: DataTableEstado | undefined) => void\r\n}\r\n\r\n/**\r\n * O que uma vista guardada leva da tabela. Sets viram arrays porque isto é serializado\r\n * para JSON.\r\n * @deprecated Legacy — não usar em código novo. Ver Storybook › Legacy.\r\n */\r\nexport interface DataTableEstado {\r\n columnFilters?: Record<string, string[]>\r\n rangeFilters?: Record<string, { min?: number; max?: number }>\r\n sortKey?: string | null\r\n sortDir?: 'asc' | 'desc'\r\n /** Colunas por que se agrupa, pela ordem dos níveis. */\r\n groupBy?: string[]\r\n}\r\n\r\n/** @deprecated Legacy — não usar em código novo. Ver Storybook › Legacy. */\r\nexport interface DataTableLabels {\r\n /** {count} é substituído pelo total (após filtros). */\r\n results: string\r\n /** Tooltip da pega de redimensionar coluna. */\r\n resizeColumn?: string\r\n export: string\r\n loading: string\r\n empty: string\r\n error: string\r\n search: string\r\n clear: string\r\n clearAll: string\r\n noOptions: string\r\n /** {count} = nº de valores selecionados (quando > 2, mostra \"N sel.\"). */\r\n selected: string\r\n /** {shown}/{total} — aparece quando o distinct da coluna tem mais valores do que os mostrados. */\r\n moreOptions: string\r\n /** Aviso no popup enquanto `allData` não chegou — as opções ainda são só as de `data`. */\r\n loadingOptions: string\r\n /** Placeholders dos dois campos do filtro por intervalo nas colunas numéricas. */\r\n min: string\r\n max: string\r\n /** Rótulos dos dois campos do filtro por intervalo nas colunas de datas (default 'From'/'To'). */\r\n from?: string\r\n to?: string\r\n /** {page}/{pages} são substituídos. */\r\n page: string\r\n prev: string\r\n next: string\r\n /** Ordenação a partir do popup de filtro, nas colunas de texto (A→Z / Z→A). */\r\n sortAsc: string\r\n sortDesc: string\r\n /** Ordenação a partir do popup nas colunas de datas (default 'Oldest first'/'Newest first'). */\r\n sortOldest?: string\r\n sortNewest?: string\r\n /** Ordenação a partir do popup nas colunas numéricas (default 'Smallest first'/'Largest first'). */\r\n sortSmallest?: string\r\n sortLargest?: string\r\n /** Agrupamento. {label} é o nome da coluna. */\r\n groupBy: string\r\n ungroup: string\r\n clearGroups: string\r\n /** Selector de linhas por página (só com `pageSizeOptions`). */\r\n perPage: string\r\n /** `filterMode: 'excel'`: primeira linha da lista de valores, o \"(Selecionar tudo)\" do Excel. */\r\n selectAll: string\r\n /** O mesmo, com pesquisa activa — aí só abrange os valores que correspondem. */\r\n selectAllResults: string\r\n /** Rótulo da linha de total (`totalRow`). */\r\n total: string\r\n /** Nome acessível da zona com scroll horizontal (só com `scrollAffordance`). */\r\n scrollRegion?: string\r\n}\r\n\r\nconst DEFAULT_LABELS: Required<DataTableLabels> = {\r\n total: 'Total',\r\n scrollRegion: 'Tabela (deslizar na horizontal)',\r\n results: '{count} results',\r\n resizeColumn: 'Drag to resize · double-click to reset',\r\n export: 'Export',\r\n loading: 'Loading…',\r\n empty: 'No data',\r\n error: 'Failed to load',\r\n search: 'Search…',\r\n clear: 'Clear',\r\n clearAll: 'Clear all',\r\n noOptions: 'No options',\r\n selected: '{count} sel.',\r\n moreOptions: 'Showing {shown} of {total} — refine your search',\r\n loadingOptions: 'Loading options from all pages…',\r\n min: 'From',\r\n max: 'To',\r\n from: 'From',\r\n to: 'To',\r\n page: 'Page {page}/{pages}',\r\n prev: 'Prev',\r\n next: 'Next',\r\n sortAsc: 'Sort ascending',\r\n sortDesc: 'Sort descending',\r\n sortOldest: 'Oldest first',\r\n sortNewest: 'Newest first',\r\n sortSmallest: 'Smallest first',\r\n sortLargest: 'Largest first',\r\n groupBy: 'Group by {label}',\r\n ungroup: 'Remove grouping by {label}',\r\n clearGroups: 'Clear grouping',\r\n perPage: 'Per page',\r\n selectAll: '(Select all)',\r\n selectAllResults: '(Select results)',\r\n}\r\n\r\n// ─── Ícones (inline, sem dependências) ───────────────────────────────────────\r\n\r\nconst Funnel = ({ size = 12, className }: { size?: number; className?: string }) => (\r\n <svg xmlns=\"http://www.w3.org/2000/svg\" width={size} height={size} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2.5\" strokeLinecap=\"round\" strokeLinejoin=\"round\" className={className}>\r\n <path d=\"M22 3H2l8 9.46V19l4 2v-8.54L22 3z\" />\r\n </svg>\r\n)\r\n\r\nconst Layers = ({ size = 11, className }: { size?: number; className?: string }) => (\r\n <svg xmlns=\"http://www.w3.org/2000/svg\" width={size} height={size} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2.5\" strokeLinecap=\"round\" strokeLinejoin=\"round\" className={className}>\r\n <path d=\"m12 2 9 5-9 5-9-5 9-5z\" />\r\n <path d=\"m3 17 9 5 9-5M3 12l9 5 9-5\" />\r\n </svg>\r\n)\r\n\r\nconst Caret = ({ size = 11, className }: { size?: number; className?: string }) => (\r\n <svg xmlns=\"http://www.w3.org/2000/svg\" width={size} height={size} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"3\" strokeLinecap=\"round\" strokeLinejoin=\"round\" className={className}>\r\n <path d=\"m9 18 6-6-6-6\" />\r\n </svg>\r\n)\r\n\r\nconst Spinner = ({ size = 16, className }: { size?: number; className?: string }) => (\r\n <svg xmlns=\"http://www.w3.org/2000/svg\" width={size} height={size} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2.5\" strokeLinecap=\"round\" strokeLinejoin=\"round\" className={`animate-spin ${className ?? ''}`} aria-hidden>\r\n <path d=\"M21 12a9 9 0 1 1-6.219-8.56\" />\r\n </svg>\r\n)\r\n\r\ninterface DataTableProps<T> {\r\n columns: DataTableColumn<T>[]\r\n data: T[]\r\n /**\r\n * Dataset completo, para o distinct dos filtros e para filtrar/ordenar/agrupar sobre ele.\r\n *\r\n * Sem isto a tabela só conhece `data` — e numa app que pagina no servidor isso são as\r\n * linhas da página, pelo que o funil de cada coluna listava só esses valores (e filtrar\r\n * por um valor que só existe noutra página dava zero linhas).\r\n */\r\n allData?: T[]\r\n /**\r\n * Chamado quando a tabela precisa de `allData` e ainda não o tem: ao abrir um funil, ao\r\n * ordenar, ao agrupar, ou quando já há filtros ativos. É o sinal para a app ir buscar o\r\n * dataset completo — uma vez, e só se alguém mexer.\r\n */\r\n onDataNeeded?: () => void\r\n /** `allData` a caminho — mostra um aviso no popup em vez de opções silenciosamente incompletas. */\r\n isAllDataLoading?: boolean\r\n /** Força o modo cliente mesmo sem interação (desde que `allData` exista). */\r\n alwaysClient?: boolean\r\n /**\r\n * Total de linhas no servidor (paginação de servidor). Fora do modo cliente, `data` é só a página à\r\n * vista — o texto de resultados da toolbar usa então este total em vez do nº de linhas de `data`.\r\n * Em modo cliente (ou com filtros ativos) conta-se, como sempre, as linhas filtradas.\r\n */\r\n totalCount?: number\r\n /**\r\n * Avisa quando a tabela passa (ou deixa) de trabalhar sobre `allData`. Quem tem paginação\r\n * de servidor por fora usa isto para a esconder: em modo cliente as linhas à vista já não\r\n * são a página que esse controlo diz estar a mostrar.\r\n */\r\n onClientModeChange?: (active: boolean) => void\r\n /** Chave estável por linha. Obrigatória para a seleção; sem ela usa-se o índice. */\r\n rowKey?: (row: T, index: number) => string | number\r\n onRowClick?: (row: T) => void\r\n /** Duplo clique — atalho para o detalhe sem roubar o clique simples (seleção, edição inline). */\r\n onRowDoubleClick?: (row: T) => void\r\n /**\r\n * Classes extra por linha, a partir dos dados — estado de risco, linha desactivada, etc.\r\n * Vem depois das classes da tabela, por isso ganha em caso de conflito.\r\n */\r\n rowClassName?: (row: T) => string | undefined\r\n /**\r\n * Chamado quando as linhas à vista mudam (filtros do cabeçalho, ordenação ou dados), com todas as páginas.\r\n * Serve para indicadores fora da tabela (ex.: cartões no topo da página) acompanharem os filtros.\r\n */\r\n onVisibleRowsChange?: (rows: T[]) => void\r\n isLoading?: boolean\r\n isError?: boolean\r\n /** max-height do viewport com scroll (default 70vh). */\r\n maxHeight?: string\r\n /**\r\n * Linhas por página. Sem isto, mostra tudo (pode ser lento com muitas linhas).\r\n *\r\n * Com `allData` quem pagina `data` é a app (paginação de servidor): a tabela só pagina em modo cliente\r\n * (filtros/ordenação/agrupamento ativos), senão paginava outra vez a página que já recebeu.\r\n * Com `storageKey`, o tamanho escolhido no selector fica na sessão (`dt_pagesize_<storageKey>`).\r\n */\r\n pageSize?: number\r\n /** Mostra um selector de linhas por página com estas opções. */\r\n pageSizeOptions?: number[]\r\n /** Caixa de seleção por linha + no cabeçalho. */\r\n selectable?: boolean\r\n onSelectionChange?: (selectedKeys: Set<string>) => void\r\n /** Linhas que se podem selecionar (default: todas). As outras mostram a caixa desativada e ficam fora do \"selecionar todas\". */\r\n isRowSelectable?: (row: T) => boolean\r\n /**\r\n * Seleção inicial (chaves de `rowKey`). Volta a ser aplicada quando muda por valor (não por identidade) —\r\n * ex.: uma pré-seleção vinda de outro ecrã. Não chama `onSelectionChange`: quem a passa já a conhece.\r\n */\r\n initialSelectedKeys?: Iterable<string | number>\r\n /** Mudar este número limpa a seleção (ex: depois de uma ação em massa). */\r\n clearSelectionTrigger?: number\r\n /** Guarda filtros e intervalos no sessionStorage com esta chave (e as larguras das colunas no localStorage). */\r\n storageKey?: string\r\n /** Largura máxima por defeito das colunas, em px (default 360). `0` desliga o limite. */\r\n defaultMaxColumnWidth?: number\r\n /** Colunas redimensionáveis arrastando o limite do cabeçalho; duplo clique repõe (default true). */\r\n resizableColumns?: boolean\r\n /**\r\n * \"Ordenar A→Z / Z→A\" no topo do popup de filtro. Ligado por omissão (o cabeçalho é o mesmo em todos os\r\n * portais); `false` desliga. Os textos dependem do `sortType`: `labels.sortAsc`/`sortDesc` no texto,\r\n * `sortOldest`/`sortNewest` nas datas e `sortSmallest`/`sortLargest` nos números (por omissão em inglês).\r\n */\r\n sortFromFilter?: boolean\r\n /**\r\n * Semântica do filtro por coluna.\r\n *\r\n * - `'simple'`: abre com tudo **desmarcado** e marcar um valor quer dizer \"só este\".\r\n * Conjunto vazio = sem filtro.\r\n * - `'excel'` (default): abre com tudo **marcado** e desmarca-se o que se quer excluir, com uma linha\r\n * `(Selecionar tudo)` no topo e curingas (`*`, `?`, `~`) na pesquisa. É o que a folha de\r\n * cálculo faz, e o que quem vem de lá espera. Conjunto vazio = **nada seleccionado**, e a\r\n * tabela fica sem linhas.\r\n *\r\n * No modo Excel o distinto da coluna aberta deixa de respeitar os filtros das outras: o que a\r\n * lista mostra tem de ser exactamente o que a selecção materializa, senão marcar \"tudo\"\r\n * apagaria em silêncio os valores que outra coluna escondeu.\r\n */\r\n filterMode?: 'simple' | 'excel'\r\n /** Ordenação inicial. */\r\n defaultSortKey?: string\r\n defaultSortDir?: 'asc' | 'desc'\r\n /** Recebe o estado da vista (ordenação + filtros + agrupamento) para uso externo. */\r\n viewRef?: React.MutableRefObject<DataTableView<T> | null>\r\n /** Mostra o botão de exportar. Sem `onExport`, faz download CSV nativo. */\r\n exportFileName?: string\r\n /** Export custom (ex: XLSX na app). Recebe as linhas filtradas + ordenadas. */\r\n onExport?: (rows: T[]) => void\r\n /** Conteúdo extra na toolbar (à direita, antes do export). */\r\n extraToolbar?: React.ReactNode\r\n /** Texto por baixo do estado vazio. */\r\n emptySubLabel?: string\r\n /** Ilustração do estado vazio. */\r\n emptyIcon?: React.ReactNode\r\n labels?: Partial<DataTableLabels>\r\n /** Linha de total no fim (fica fixa em baixo), com o `groupAggregate` de cada coluna sobre as linhas filtradas. */\r\n totalRow?: boolean\r\n /** Agrupamento inicial (chaves de colunas, por ordem de nível). A pessoa pode mudá-lo depois. */\r\n defaultGroupBy?: string[]\r\n /**\r\n * Agrupamento controlado (chaves de colunas, por ordem de nível). Com isto a tabela usa este valor (ignora\r\n * `defaultGroupBy` e a sessão) e as mudanças da pessoa chegam por `onGroupByChange`.\r\n */\r\n groupBy?: string[]\r\n /** Chamado quando a pessoa muda o agrupamento (também sem `groupBy`, só para acompanhar). */\r\n onGroupByChange?: (keys: string[]) => void\r\n /** Chamado quando a ordenação muda (`null` = sem ordenação). Também ao montar, se a inicial não for nenhuma. */\r\n onSortChange?: (sort: { key: string; dir: 'asc' | 'desc' } | null) => void\r\n /**\r\n * Chamado quando os filtros por valor das colunas mudam (chave → valores escolhidos). Também ao montar, se\r\n * vierem filtros da sessão. Só os ativos; os intervalos ficam de fora (ver `viewRef`).\r\n */\r\n onColumnFiltersChange?: (filters: Record<string, string[]>) => void\r\n /**\r\n * Torna focável a zona com scroll horizontal (`role=\"region\"`, nome em `labels.scrollRegion`) e deixa\r\n * deslizá-la com ←/→/Home/End. Default desligado.\r\n */\r\n scrollAffordance?: boolean\r\n}\r\n\r\n// ─── Helpers ─────────────────────────────────────────────────────────────────\r\n\r\nconst EMPTY = '—'\r\n\r\n/** Nº máximo de opções renderizadas no popup de filtro por coluna — sem isto, uma coluna\r\n * quase-única por linha (ex: um ID) põe milhares de `<button>` no DOM de cada vez que o popup\r\n * abre, mesmo só visualmente cortado pelo scroll. A pesquisa continua a correr sobre o distinct\r\n * completo (memoizado), só o que é DESENHADO fica limitado. */\r\nconst MAX_OPCOES_FILTRO = 50\r\n\r\n/** Largura assumida p/ acumular o offset de colunas sticky sem `width` definido. */\r\nconst DEFAULT_STICKY_WIDTH = 160\r\n\r\n/** Collator único, reutilizado em todas as comparações de texto.\r\n * `a.localeCompare(b, undefined, { numeric: true })` constrói um `Intl.Collator` **a cada\r\n * chamada**; num sort de milhares de valores (ex: o distinct de uma coluna de IDs, com ~12k\r\n * valores ≈ 150k comparações) era isso, e não o render, que fazia o popup demorar a abrir. */\r\nconst COLLATOR = new Intl.Collator(undefined, { numeric: true })\r\n\r\nfunction rawValue<T>(row: T, col: DataTableColumn<T>): string | number | null {\r\n if (col.value) {\r\n const v = col.value(row)\r\n return v == null ? null : v\r\n }\r\n const v = (row as Record<string, unknown>)[col.key]\r\n if (v == null) return null\r\n if (typeof v === 'number' || typeof v === 'string') return v\r\n return String(v)\r\n}\r\n\r\n/** Rótulo de filtro de um valor (string; vazio → EMPTY). */\r\nconst normalizarRotulo = (v: string | number | null | undefined): string => (v == null || v === '' ? EMPTY : String(v))\r\n\r\n/** Valor normalizado p/ filtro (string; vazio → EMPTY). */\r\nfunction filterKey<T>(row: T, col: DataTableColumn<T>): string {\r\n return normalizarRotulo(rawValue(row, col))\r\n}\r\n\r\n/** A coluna tem valor nesta linha (com `filterValues`, pelo menos um não vazio). */\r\nfunction temValor<T>(row: T, col: DataTableColumn<T>): boolean {\r\n if (col.filterValues) return col.filterValues(row).some((v) => v != null && v !== '')\r\n return rawValue(row, col) !== null\r\n}\r\n\r\n/**\r\n * Filtros guardados (sessão, vista do `viewRef`) → conjuntos, com os valores das colunas de datas passados a\r\n * chave de dia. Um filtro gravado antes de as datas se agruparem por dia (ex.: o ISO com hora) continua a\r\n * apanhar as linhas desse dia, em vez de deixar a tabela vazia.\r\n */\r\nfunction lerFiltrosGuardados<T>(columns: DataTableColumn<T>[], f: Record<string, string[]>): Record<string, Set<string>> {\r\n return Object.fromEntries(\r\n Object.entries(f).map(([k, v]) => {\r\n const data = columns.find((c) => c.key === k)?.sortType === 'date'\r\n return [k, new Set(data ? v.map(rotuloDeData) : v)]\r\n })\r\n )\r\n}\r\n\r\n/** Chave estável de um mapa de filtros por coluna — compara estados por valor, não por identidade. */\r\nfunction chaveDosFiltros(f: Record<string, Set<string> | string[]>): string {\r\n return JSON.stringify(\r\n Object.entries(f)\r\n .map(([k, v]) => [k, [...v].sort()])\r\n .sort()\r\n )\r\n}\r\n\r\n/**\r\n * Timestamp de um valor de coluna de data. As apps passam a data já formatada (`value:\r\n * dataPt(...)` → \"14/08/2026\"), e `new Date(\"14/08/2026\")` devolve `Invalid Date` — ou, pior,\r\n * lê \"06/03/2026\" como 3 de junho, à americana. Daí o parse explícito de dd/mm/aaaa antes do\r\n * fallback para o `Date` nativo (que trata do ISO).\r\n * @deprecated Legacy — não usar em código novo. Ver Storybook › Legacy.\r\n */\r\nexport function timestampDeData(v: unknown): number {\r\n if (v == null || v === '') return NaN\r\n if (v instanceof Date) return v.getTime()\r\n // Timestamp numérico (ms): `new Date(\"1767346000000\")` (string) é `Invalid Date`.\r\n if (typeof v === 'number') return Number.isFinite(v) ? v : NaN\r\n const s = String(v).trim()\r\n const m = /^(\\d{1,2})[/-](\\d{1,2})[/-](\\d{4})/.exec(s)\r\n if (m) return Date.UTC(Number(m[3]), Number(m[2]) - 1, Number(m[1]))\r\n const t = new Date(s).getTime()\r\n return Number.isNaN(t) ? NaN : t\r\n}\r\n\r\nconst doisDigitos = (n: number | string) => String(n).padStart(2, '0')\r\n\r\n/** Chave de dia `aaaa-mm-dd`, o formato guardado nos filtros das colunas de datas. */\r\nconst RX_CHAVE_DIA = /^(\\d{4})-(\\d{2})-(\\d{2})$/\r\n\r\n/**\r\n * Dia (hora local) de um valor de coluna de data, como chave `aaaa-mm-dd`; `null` se não for data.\r\n *\r\n * O texto dd/mm/aaaa e a data ISO sem hora (\"2026-01-02\") leem-se diretamente: passar pelo timestamp\r\n * (meia-noite UTC) dava o dia anterior em fusos a oeste de Greenwich.\r\n */\r\nfunction chaveDia(v: unknown): string | null {\r\n if (v == null || v === '') return null\r\n if (typeof v === 'string') {\r\n const s = v.trim()\r\n const pt = /^(\\d{1,2})[/-](\\d{1,2})[/-](\\d{4})/.exec(s)\r\n if (pt) return `${pt[3]}-${doisDigitos(pt[2])}-${doisDigitos(pt[1])}`\r\n if (RX_CHAVE_DIA.test(s)) return s\r\n }\r\n const t = timestampDeData(v)\r\n if (!Number.isFinite(t)) return null\r\n const d = new Date(t)\r\n return `${d.getFullYear()}-${doisDigitos(d.getMonth() + 1)}-${doisDigitos(d.getDate())}`\r\n}\r\n\r\n/** Rótulo de filtro numa coluna de datas: o dia (`aaaa-mm-dd`); vazio → EMPTY; texto que não é data fica como está. */\r\nfunction rotuloDeData(v: unknown): string {\r\n if (v == null || v === '') return EMPTY\r\n return chaveDia(v) ?? String(v)\r\n}\r\n\r\n/** Chave de dia → `dd/mm/aaaa` para mostrar; o resto (EMPTY, texto que não é data) fica igual. */\r\nfunction diaVisivel(v: string): string {\r\n const m = RX_CHAVE_DIA.exec(v)\r\n return m ? `${m[3]}/${m[2]}/${m[1]}` : v\r\n}\r\n\r\n/** Dias do mais recente para o mais antigo; o que não é dia a seguir; EMPTY no fim. */\r\nfunction ordenarDias(a: string, b: string): number {\r\n if (a === EMPTY) return 1\r\n if (b === EMPTY) return -1\r\n const da = RX_CHAVE_DIA.test(a)\r\n const db = RX_CHAVE_DIA.test(b)\r\n if (da && db) return a < b ? 1 : a > b ? -1 : 0\r\n if (da) return -1\r\n if (db) return 1\r\n return COLLATOR.compare(a, b)\r\n}\r\n\r\nfunction compare(\r\n a: string | number | null,\r\n b: string | number | null,\r\n type: DataTableColumn<unknown>['sortType']\r\n): number {\r\n if (a == null && b == null) return 0\r\n if (a == null) return 1\r\n if (b == null) return -1\r\n if (type === 'number') return Number(a) - Number(b)\r\n if (type === 'date') return timestampDeData(a) - timestampDeData(b)\r\n return COLLATOR.compare(String(a), String(b))\r\n}\r\n\r\n/** Valor comparável de uma célula para o filtro por intervalo (número ou data). */\r\nfunction valorParaIntervalo<T>(row: T, col: DataTableColumn<T>): number {\r\n const v = rawValue(row, col)\r\n return col.sortType === 'date' ? timestampDeData(v) : Number(v)\r\n}\r\n\r\nfunction toCsv<T>(columns: DataTableColumn<T>[], rows: T[]): string {\r\n const esc = (v: unknown) => {\r\n const s = v == null ? '' : String(v)\r\n return /[\",\\n]/.test(s) ? `\"${s.replace(/\"/g, '\"\"')}\"` : s\r\n }\r\n const head = columns.map((c) => esc(c.label)).join(',')\r\n const body = rows.map((r) => columns.map((c) => esc(rawValue(r, c))).join(',')).join('\\n')\r\n return `${head}\\n${body}`\r\n}\r\n\r\nconst ordenarRotulos = (a: string, b: string) => (a === EMPTY ? 1 : b === EMPTY ? -1 : COLLATOR.compare(a, b))\r\n\r\n/** Identidade estável para as dependências que o modo Excel não usa — ver `cruzFiltros`. */\r\nconst SEM_CRUZAMENTO: [string, never][] = []\r\n\r\nconst escaparRegex = (s: string) => s.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')\r\n\r\n/**\r\n * Predicado da caixa de pesquisa do filtro por coluna, com os curingas do Excel.\r\n *\r\n * `*` = qualquer sequência, `?` = exactamente um carácter, `~` escapa qualquer um dos três. Sem\r\n * curinga nenhum é \"contém\"; **com** curinga o padrão passa a ser comparado com o valor **inteiro**,\r\n * e é daí que sai o \"começa por\" (`25VCT*`), o \"termina em\" (`*.fbo`) e o \"tem isto no meio\"\r\n * (`*RA2*`).\r\n *\r\n * No Excel esse ancorar vive nos Filtros de Texto (Começa por/Termina em) e não na caixa de\r\n * pesquisa, que é sempre \"contém\" — juntar os dois na caixa evita ter de trazer também o submenu, e\r\n * sem curingas o comportamento é o mesmo dos dois lados.\r\n *\r\n * Tudo em minúsculas: quem chama passa a pesquisa e o valor já em minúsculas.\r\n */\r\nexport function pesquisaCuringa(q: string): (v: string) => boolean {\r\n let fonte = ''\r\n let literal = ''\r\n let temCuringa = false\r\n for (let i = 0; i < q.length; i++) {\r\n const c = q[i]!\r\n const seguinte = q[i + 1]\r\n if (c === '~' && (seguinte === '*' || seguinte === '?' || seguinte === '~')) {\r\n fonte += escaparRegex(seguinte)\r\n literal += seguinte\r\n i++\r\n continue\r\n }\r\n if (c === '*' || c === '?') {\r\n fonte += c === '*' ? '.*' : '.'\r\n literal += c\r\n temCuringa = true\r\n continue\r\n }\r\n fonte += escaparRegex(c)\r\n literal += c\r\n }\r\n // Sem curinga usa-se o literal (e não `q`), para `~*` procurar mesmo um asterisco.\r\n if (!temCuringa) return (v) => v.includes(literal)\r\n const rx = new RegExp(`^${fonte}$`)\r\n return (v) => rx.test(v)\r\n}\r\n\r\ninterface GrupoNo<T> {\r\n val: string\r\n ordem: string\r\n path: string\r\n items: T[]\r\n filhos: GrupoNo<T>[]\r\n}\r\n\r\n// ─── Componente ──────────────────────────────────────────────────────────────\r\n\r\n/**\r\n * Tabela genérica: ordenação, filtro por coluna com distinct (multi-seleção) e por intervalo,\r\n * agrupamento multi-nível, seleção de linhas, colunas fixas (sticky), align/width, export\r\n * (CSV nativo ou custom) e estados loading/vazio/erro. Sem dependências de ícones/i18n —\r\n * textos via `labels`.\r\n *\r\n * Com paginação de servidor, passa também `allData`/`onDataNeeded`: sem o dataset completo,\r\n * filtrar, ordenar e agrupar só olhariam para a página à vista.\r\n * @deprecated Legacy — não usar em código novo. Ver Storybook › Legacy.\r\n */\r\nexport default function DataTable<T>({\r\n columns,\r\n data,\r\n allData,\r\n onDataNeeded,\r\n isAllDataLoading,\r\n alwaysClient,\r\n totalCount,\r\n onClientModeChange,\r\n rowKey,\r\n onRowClick,\r\n onRowDoubleClick,\r\n rowClassName,\r\n onVisibleRowsChange,\r\n isLoading,\r\n isError,\r\n maxHeight = '70vh',\r\n pageSize,\r\n pageSizeOptions,\r\n selectable,\r\n onSelectionChange,\r\n isRowSelectable,\r\n initialSelectedKeys,\r\n clearSelectionTrigger,\r\n storageKey,\r\n defaultMaxColumnWidth = 360,\r\n resizableColumns = true,\r\n sortFromFilter = true,\r\n filterMode = 'excel',\r\n defaultSortKey,\r\n defaultSortDir,\r\n viewRef,\r\n exportFileName,\r\n onExport,\r\n extraToolbar,\r\n emptySubLabel,\r\n emptyIcon,\r\n labels,\r\n totalRow = false,\r\n defaultGroupBy,\r\n groupBy,\r\n onGroupByChange,\r\n onSortChange,\r\n onColumnFiltersChange,\r\n scrollAffordance = false,\r\n}: DataTableProps<T>) {\r\n const L = { ...DEFAULT_LABELS, ...labels }\r\n\r\n // Larguras definidas pela pessoa (arrastar o limite do cabeçalho). Guardadas por tabela no localStorage.\r\n const [larguras, setLarguras] = useState<Record<string, number>>(() => {\r\n if (!storageKey) return {}\r\n try {\r\n return JSON.parse(localStorage.getItem(`dt_widths_${storageKey}`) ?? '{}') as Record<string, number>\r\n } catch {\r\n return {}\r\n }\r\n })\r\n useEffect(() => {\r\n if (!storageKey) return\r\n try {\r\n localStorage.setItem(`dt_widths_${storageKey}`, JSON.stringify(larguras))\r\n } catch {\r\n /* storage indisponível */\r\n }\r\n }, [larguras, storageKey])\r\n const larguraMax = (c: DataTableColumn<T>) => larguras[c.key] ?? c.maxWidth ?? (defaultMaxColumnWidth > 0 ? defaultMaxColumnWidth : undefined)\r\n const iniciarResize = (e: React.MouseEvent, key: string) => {\r\n e.preventDefault()\r\n e.stopPropagation()\r\n const th = (e.currentTarget as HTMLElement).parentElement\r\n const x0 = e.clientX\r\n const w0 = th?.getBoundingClientRect().width ?? 120\r\n const mover = (ev: MouseEvent) => setLarguras((l) => ({ ...l, [key]: Math.max(60, Math.round(w0 + ev.clientX - x0)) }))\r\n const parar = () => {\r\n window.removeEventListener('mousemove', mover)\r\n window.removeEventListener('mouseup', parar)\r\n document.body.style.cursor = ''\r\n }\r\n document.body.style.cursor = 'col-resize'\r\n window.addEventListener('mousemove', mover)\r\n window.addEventListener('mouseup', parar)\r\n }\r\n const reporLargura = (key: string) =>\r\n setLarguras((l) => {\r\n const n = { ...l }\r\n delete n[key]\r\n return n\r\n })\r\n // Com `storageKey`, a ordenação e o agrupamento ficam na sessão como os filtros: ao voltar à tabela (ex.: mudar\r\n // de separador e regressar) a vista é a mesma. Valor guardado (mesmo vazio) ganha aos de omissão.\r\n const lerSessao = <V,>(sufixo: string): V | undefined => {\r\n if (!storageKey) return undefined\r\n try {\r\n const raw = sessionStorage.getItem(`dt_${sufixo}_${storageKey}`)\r\n return raw === null ? undefined : (JSON.parse(raw) as V)\r\n } catch {\r\n return undefined\r\n }\r\n }\r\n const [sort, setSort] = useState<{ key: string; dir: 1 | -1 } | null>(() => {\r\n const guardada = lerSessao<{ key: string; dir: 1 | -1 } | null>('sort')\r\n if (guardada !== undefined) return guardada && columns.some((c) => c.key === guardada.key) ? guardada : null\r\n return defaultSortKey ? { key: defaultSortKey, dir: defaultSortDir === 'desc' ? -1 : 1 } : null\r\n })\r\n const [columnFilters, setColumnFilters] = useState<Record<string, Set<string>>>(() => {\r\n if (!storageKey) return {}\r\n try {\r\n const raw = sessionStorage.getItem(`dt_filters_${storageKey}`)\r\n if (!raw) return {}\r\n return lerFiltrosGuardados(columns, JSON.parse(raw) as Record<string, string[]>)\r\n } catch {\r\n return {}\r\n }\r\n })\r\n // Filtro por intervalo, em colunas numéricas e de datas (`sortType`). Numa coluna como\r\n // \"Dias Penalização\" a lista de valores distintos é inútil — ninguém procura \"88 valores\" um a\r\n // um, quer é \"acima de 30\". Convive com a seleção por valor: se as duas estiverem ativas, a\r\n // linha tem de passar nas duas.\r\n const [rangeFilters, setRangeFilters] = useState<Record<string, { min?: number; max?: number }>>(() => {\r\n if (!storageKey) return {}\r\n try {\r\n const raw = sessionStorage.getItem(`dt_ranges_${storageKey}`)\r\n return raw ? (JSON.parse(raw) as Record<string, { min?: number; max?: number }>) : {}\r\n } catch {\r\n return {}\r\n }\r\n })\r\n const [groupByInterno, setGroupByInterno] = useState<string[]>(() =>\r\n (lerSessao<string[]>('group') ?? defaultGroupBy ?? []).filter((k) => columns.some((c) => c.key === k))\r\n )\r\n // Com `groupBy` manda quem está por fora. Memoizado por valor: um array novo a cada render (o habitual\r\n // em `groupBy={['x']}`) refazia os grupos e voltava à página 1 em todos os renders.\r\n const grupoControlado = groupBy !== undefined\r\n const chaveGrupoExterno = groupBy?.join('\\u0000')\r\n const chaveColunas = columns.map((c) => c.key).join('\\u0000')\r\n const groupByKeys = useMemo(\r\n () => (grupoControlado ? groupBy!.filter((k) => columns.some((c) => c.key === k)) : groupByInterno),\r\n // eslint-disable-next-line react-hooks/exhaustive-deps\r\n [grupoControlado, chaveGrupoExterno, groupByInterno, chaveColunas]\r\n )\r\n const [collapsedGroups, setCollapsedGroups] = useState<Set<string>>(new Set())\r\n const selecaoInicial = initialSelectedKeys ? Array.from(initialSelectedKeys, String) : null\r\n const [selectedKeys, setSelectedKeys] = useState<Set<string>>(() => new Set(selecaoInicial ?? []))\r\n const [openFilter, setOpenFilter] = useState<string | null>(null)\r\n const [filterSearch, setFilterSearch] = useState('')\r\n const filterRefs = useRef<Map<string, HTMLDivElement>>(new Map())\r\n const popupRef = useRef<HTMLDivElement | null>(null)\r\n // O popup é desenhado num portal, não dentro do `<th>`: a tabela vive num contentor com\r\n // `overflow: auto` e, quando tem poucas linhas, esse contentor é baixo e cortava o popup ao\r\n // fim de dois ou três valores. Fora da árvore da tabela, com `position: fixed`, deixa de haver\r\n // nada que o corte.\r\n const [popupPos, setPopupPos] = useState<{ top: number; left: number } | null>(null)\r\n\r\n const isGrouped = groupByKeys.length > 0\r\n\r\n // Um funil por coluna: `openFilter` é a lista em filtro (a coluna ou um dos seus `extraFilters`) e o\r\n // popup ancora-se no funil da coluna dona dessa lista.\r\n const funilAberto = openFilter\r\n ? (columns.find((c) => c.key === openFilter || c.extraFilters?.some((f) => f.key === openFilter))?.key ?? openFilter)\r\n : null\r\n\r\n useEffect(() => {\r\n if (!storageKey) return\r\n const serializavel = Object.fromEntries(Object.entries(columnFilters).map(([k, v]) => [k, Array.from(v)]))\r\n try {\r\n sessionStorage.setItem(`dt_filters_${storageKey}`, JSON.stringify(serializavel))\r\n sessionStorage.setItem(`dt_ranges_${storageKey}`, JSON.stringify(rangeFilters))\r\n sessionStorage.setItem(`dt_sort_${storageKey}`, JSON.stringify(sort))\r\n // Agrupamento controlado não se guarda: quem o controla é que decide se o recorda.\r\n if (!grupoControlado) sessionStorage.setItem(`dt_group_${storageKey}`, JSON.stringify(groupByKeys))\r\n } catch {\r\n /* sessionStorage indisponível (private mode) — a tabela funciona na mesma, só não recorda. */\r\n }\r\n }, [columnFilters, rangeFilters, sort, groupByKeys, grupoControlado, storageKey])\r\n\r\n useLayoutEffect(() => {\r\n if (!funilAberto) {\r\n setPopupPos(null)\r\n return\r\n }\r\n const recalcular = () => {\r\n const el = filterRefs.current.get(funilAberto)\r\n if (!el) return\r\n const r = el.getBoundingClientRect()\r\n // Tem de acompanhar o `w-72` do popup — é o que o impede de ficar fora do ecrã à direita.\r\n const largura = Math.min(288, window.innerWidth * 0.8)\r\n // Encosta à direita quando a coluna está no fim da tabela e o popup passaria do ecrã.\r\n const left = Math.max(8, Math.min(r.left, window.innerWidth - largura - 8))\r\n setPopupPos({ top: r.bottom + 6, left })\r\n }\r\n recalcular()\r\n // `true` na captura: apanha também o scroll do contentor da tabela, que não borbulha.\r\n window.addEventListener('scroll', recalcular, true)\r\n window.addEventListener('resize', recalcular)\r\n return () => {\r\n window.removeEventListener('scroll', recalcular, true)\r\n window.removeEventListener('resize', recalcular)\r\n }\r\n }, [funilAberto])\r\n\r\n // Fecha o dropdown de filtro em clique fora (o popup está fora do `<th>`, daí os dois testes).\r\n useEffect(() => {\r\n const onDown = (e: MouseEvent) => {\r\n if (!funilAberto) return\r\n const alvo = e.target as Node\r\n const botao = filterRefs.current.get(funilAberto)\r\n if (botao?.contains(alvo) || popupRef.current?.contains(alvo)) return\r\n setOpenFilter(null)\r\n setFilterSearch('')\r\n }\r\n document.addEventListener('mousedown', onDown)\r\n return () => document.removeEventListener('mousedown', onDown)\r\n }, [funilAberto])\r\n\r\n /**\r\n * Função de valor por chave de filtro. A chave tanto pode ser uma coluna como um\r\n * `extraFilters` pendurado no cabeçalho de outra coluna.\r\n */\r\n const resolverValor = (key: string): ((row: T) => string | number | null | undefined) | undefined => {\r\n const col = columns.find((c) => c.key === key)\r\n if (col) return (row: T) => rawValue(row, col)\r\n for (const c of columns) {\r\n const ef = c.extraFilters?.find((f) => f.key === key)\r\n if (ef) return ef.value\r\n }\r\n return undefined\r\n }\r\n\r\n const rotuloDe = (fn: (row: T) => string | number | null | undefined, row: T) => normalizarRotulo(fn(row))\r\n\r\n /**\r\n * Coluna de datas: a lista do funil tem uma entrada por DIA (chave `aaaa-mm-dd`, mostrada como dd/mm/aaaa),\r\n * não uma por instante — um ISO com hora dava uma entrada por linha.\r\n */\r\n const eColunaData = (key: string) => columns.find((c) => c.key === key)?.sortType === 'date'\r\n\r\n /** Rótulos de filtro de uma linha: vários com `filterValues` (sem nenhum → EMPTY), senão o valor único. */\r\n const resolverRotulos = (key: string): ((row: T) => string[]) | undefined => {\r\n const col = columns.find((c) => c.key === key)\r\n const normalizar: (v: unknown) => string =\r\n col?.sortType === 'date' ? rotuloDeData : (v) => normalizarRotulo(v as string | number | null | undefined)\r\n if (col?.filterValues) {\r\n const fv = col.filterValues\r\n return (row: T) => {\r\n const vs = Array.from(new Set(fv(row).map(normalizar)))\r\n return vs.length > 0 ? vs : [EMPTY]\r\n }\r\n }\r\n if (col?.sortType === 'date') {\r\n // Sem `value`, lê-se o campo em bruto: o `rawValue` passa um `Date` a texto.\r\n const ler = col.value ?? ((row: T) => (row as Record<string, unknown>)[col.key])\r\n return (row: T) => [rotuloDeData(ler(row))]\r\n }\r\n const fn = resolverValor(key)\r\n return fn ? (row: T) => [rotuloDe(fn, row)] : undefined\r\n }\r\n\r\n /** Texto de um rótulo de filtro como aparece no popup e nos chips (dias em dd/mm/aaaa). */\r\n const rotuloVisivel = (key: string, v: string) => (eColunaData(key) ? diaVisivel(v) : v)\r\n\r\n /** Predicado do filtro por valor: a linha passa se QUALQUER um dos seus rótulos estiver escolhido. */\r\n const passaFiltro = (key: string, sel: Set<string>): ((row: T) => boolean) | undefined => {\r\n const col = columns.find((c) => c.key === key)\r\n if (col?.filterValues || col?.sortType === 'date') {\r\n const rotulos = resolverRotulos(key)!\r\n return (row: T) => rotulos(row).some((v) => sel.has(v))\r\n }\r\n const fn = resolverValor(key)\r\n return fn ? (row: T) => sel.has(rotuloDe(fn, row)) : undefined\r\n }\r\n\r\n const modoExcel = filterMode === 'excel'\r\n\r\n /**\r\n * No modo Excel é a **presença da chave** que liga o filtro, e não o tamanho do conjunto: sem\r\n * chave = sem filtro = tudo marcado no popover (o estado em que o Excel abre). Com chave passam\r\n * só os valores do conjunto, e um conjunto vazio é \"nada seleccionado\" — a tabela fica sem\r\n * linhas, como no Excel quando se desmarca o `(Selecionar tudo)`.\r\n */\r\n const activeFilters = useMemo(\r\n () => Object.entries(columnFilters).filter(([, s]) => s && (modoExcel || s.size > 0)),\r\n [columnFilters, modoExcel]\r\n )\r\n const activeRanges = useMemo(\r\n () => Object.entries(rangeFilters).filter(([, r]) => r && (r.min != null || r.max != null)),\r\n [rangeFilters]\r\n )\r\n const hasActiveFilters = activeFilters.length > 0 || activeRanges.length > 0\r\n\r\n /**\r\n * Com o dataset completo em mão, é sobre ele que se filtra, ordena e agrupa — não sobre\r\n * `data`. Sem isto, escolher no funil um valor que só existe noutra página dava uma tabela\r\n * vazia: as opções vinham de todos os dados mas o filtro corria só sobre a página.\r\n */\r\n const isClientMode = !!allData && (!!alwaysClient || hasActiveFilters || !!sort || isGrouped)\r\n const effectiveData = isClientMode ? allData! : data\r\n\r\n // Pedir o dataset completo. Não só no arranque: os filtros podem vir do sessionStorage ou\r\n // de uma vista reposta, e quem alimenta a tabela pode largar o dataset a meio.\r\n useEffect(() => {\r\n if (!allData && (hasActiveFilters || sort || isGrouped)) onDataNeeded?.()\r\n // eslint-disable-next-line react-hooks/exhaustive-deps\r\n }, [allData, hasActiveFilters, sort, isGrouped])\r\n\r\n useEffect(() => {\r\n onClientModeChange?.(isClientMode)\r\n // eslint-disable-next-line react-hooks/exhaustive-deps\r\n }, [isClientMode])\r\n\r\n useEffect(() => {\r\n if (clearSelectionTrigger === undefined) return\r\n setSelectedKeys(new Set())\r\n }, [clearSelectionTrigger])\r\n\r\n // `initialSelectedKeys` volta a aplicar-se quando muda por valor. Ao montar já está no estado inicial.\r\n const chaveSelecaoInicial = selecaoInicial ? JSON.stringify([...selecaoInicial].sort()) : null\r\n const selecaoInicialAnterior = useRef(chaveSelecaoInicial)\r\n useEffect(() => {\r\n if (chaveSelecaoInicial === selecaoInicialAnterior.current) return\r\n selecaoInicialAnterior.current = chaveSelecaoInicial\r\n if (chaveSelecaoInicial !== null) setSelectedKeys(new Set(JSON.parse(chaveSelecaoInicial) as string[]))\r\n }, [chaveSelecaoInicial])\r\n\r\n /**\r\n * Filtrar pelo cabeçalho também limpa a seleção.\r\n *\r\n * O que estava marcado era-o daquela lista: filtrar para outra e manter as marcas deixava\r\n * escolhidas linhas que já não se veem — e quem age sobre a seleção (uma ação em massa)\r\n * agia sobre elas na mesma. Avisa-se o ecrã, e não só se limpa por dentro: as caixas\r\n * ficavam vazias e o botão de cima continuava a dizer \"Tratar 250\" — e tratava-as.\r\n *\r\n * Só quando é a pessoa a mexer nos filtros (ver `mudarFiltrosCol`): filtros vindos da sessão ao montar, ou\r\n * de uma vista reposta (`viewRef.repor`), não limpam — senão perdia-se a `initialSelectedKeys` logo à\r\n * entrada e o ecrã recebia um `onSelectionChange(vazio)` sem ninguém ter feito nada.\r\n *\r\n * Ordenar não conta: são as mesmas linhas por outra ordem.\r\n */\r\n const mudarFiltrosCol = (proximo: (prev: Record<string, Set<string>>) => Record<string, Set<string>>) => {\r\n const next = proximo(columnFilters)\r\n if (chaveDosFiltros(next) === chaveDosFiltros(columnFilters)) return\r\n setColumnFilters(next)\r\n if (!selectable) return\r\n setSelectedKeys(new Set())\r\n onSelectionChange?.(new Set())\r\n }\r\n\r\n // Avisos de estado para fora. Por valor, contra o último avisado (que começa vazio): não dispara em todos\r\n // os renders, mas dispara ao montar quando o estado inicial (sessão, `defaultSortKey`) não é vazio.\r\n // Refs para não depender da identidade das funções (o ecrã passa normalmente arrows novas).\r\n const avisarOrdenacao = useRef(onSortChange)\r\n avisarOrdenacao.current = onSortChange\r\n const ordenacaoAvisada = useRef('null')\r\n useEffect(() => {\r\n const s = sort ? { key: sort.key, dir: sort.dir === 1 ? ('asc' as const) : ('desc' as const) } : null\r\n const chave = JSON.stringify(s)\r\n if (chave === ordenacaoAvisada.current) return\r\n ordenacaoAvisada.current = chave\r\n avisarOrdenacao.current?.(s)\r\n }, [sort])\r\n\r\n const avisarFiltros = useRef(onColumnFiltersChange)\r\n avisarFiltros.current = onColumnFiltersChange\r\n const filtrosAvisados = useRef(chaveDosFiltros({}))\r\n const chaveFiltrosAtivos = chaveDosFiltros(Object.fromEntries(activeFilters))\r\n useEffect(() => {\r\n if (chaveFiltrosAtivos === filtrosAvisados.current) return\r\n filtrosAvisados.current = chaveFiltrosAtivos\r\n avisarFiltros.current?.(Object.fromEntries((JSON.parse(chaveFiltrosAtivos) as [string, string[]][])))\r\n }, [chaveFiltrosAtivos])\r\n\r\n /**\r\n * No modo Excel o distinto é o puro da coluna (ver `filterMode`), por isso não depende dos\r\n * filtros das outras. Fixar aqui a identidade evita recalcular a lista inteira a cada clique\r\n * numa caixa — que é precisamente o que uma coluna quase-única por linha não perdoa.\r\n */\r\n const cruzFiltros = modoExcel ? SEM_CRUZAMENTO : activeFilters\r\n const cruzRanges = modoExcel ? SEM_CRUZAMENTO : activeRanges\r\n\r\n /**\r\n * Distinct de uma chave, a partir do dataset completo quando ele existe: as opções do funil\r\n * são as da tabela toda, não as da página que calhou estar à vista.\r\n *\r\n * Os filtros das OUTRAS colunas aplicam-se (mas não o da própria): assim escolher \"Porto\"\r\n * numa coluna faz a coluna ao lado deixar de oferecer valores que já não existem — sem\r\n * impedir de alargar a escolha na coluna que se está a mexer.\r\n */\r\n const distinctValues = (key: string): string[] => {\r\n const rotulosFn = resolverRotulos(key)\r\n if (!rotulosFn) return []\r\n let fonte = allData ?? data\r\n for (const [outraKey, escolhidos] of cruzFiltros) {\r\n if (outraKey === key) continue\r\n const passa = passaFiltro(outraKey, escolhidos)\r\n if (!passa) continue\r\n fonte = fonte.filter(passa)\r\n }\r\n for (const [outraKey, r] of cruzRanges) {\r\n if (outraKey === key) continue\r\n const col = columns.find((c) => c.key === outraKey)\r\n if (!col) continue\r\n fonte = fonte.filter((row) => {\r\n const n = valorParaIntervalo(row, col)\r\n if (!Number.isFinite(n)) return false\r\n if (r.min != null && n < r.min) return false\r\n if (r.max != null && n > r.max) return false\r\n return true\r\n })\r\n }\r\n const set = new Set<string>()\r\n fonte.forEach((row) => rotulosFn(row).forEach((v) => set.add(v)))\r\n return Array.from(set).sort(eColunaData(key) ? ordenarDias : ordenarRotulos)\r\n }\r\n\r\n // Memoizado ao abrir/mudar de coluna (não a cada tecla da pesquisa) — numa coluna quase-única\r\n // por linha, recalcular o distinct (iterar + Set + sort) em cada keystroke era o grosso da\r\n // lentidão do popup, não só a 1ª abertura.\r\n const openFilterOptions = useMemo(() => {\r\n if (!openFilter) return []\r\n return distinctValues(openFilter)\r\n // eslint-disable-next-line react-hooks/exhaustive-deps\r\n }, [openFilter, data, allData, columns, cruzFiltros, cruzRanges])\r\n\r\n /** Versão em minúsculas do distinct, calculada uma vez por abertura — a pesquisa do popup\r\n * corria `v.toLowerCase()` sobre a lista inteira a cada tecla. Pesquisa-se o texto que se vê\r\n * (nas datas, dd/mm/aaaa — não a chave guardada). */\r\n const openFilterOptionsLower = useMemo(\r\n () => openFilterOptions.map((v) => (openFilter ? rotuloVisivel(openFilter, v) : v).toLowerCase()),\r\n // eslint-disable-next-line react-hooks/exhaustive-deps\r\n [openFilterOptions]\r\n )\r\n\r\n /** Opções a desenhar (no máximo `MAX_OPCOES_FILTRO`) + total de correspondências, para o\r\n * rodapé \"a mostrar X de Y\". Um só varrimento, sem alocar o array completo dos matches. */\r\n const opcoesVisiveis = useMemo(() => {\r\n const q = filterSearch.toLowerCase()\r\n // No modo Excel a pesquisa aceita curingas; no simples mantém-se o \"contém\" de sempre.\r\n const corresponde = q ? (modoExcel ? pesquisaCuringa(q) : (v: string) => v.includes(q)) : null\r\n const opts: string[] = []\r\n // `correspondentes` é tudo o que a pesquisa apanha, não só o que se desenha: com 200\r\n // correspondências e 50 desenhadas, o `(Selecionar tudo)` tem de apanhar as 200 — é o que o\r\n // Excel faz, e o contrário seria uma armadilha silenciosa.\r\n const correspondentes: string[] = []\r\n for (let i = 0; i < openFilterOptions.length; i++) {\r\n if (corresponde && !corresponde(openFilterOptionsLower[i])) continue\r\n correspondentes.push(openFilterOptions[i])\r\n if (opts.length < MAX_OPCOES_FILTRO) opts.push(openFilterOptions[i])\r\n }\r\n return { opts, total: correspondentes.length, correspondentes }\r\n }, [openFilterOptions, openFilterOptionsLower, filterSearch, modoExcel])\r\n\r\n // Filtrar e ordenar são funções sobre uma lista qualquer (não só sobre a vista atual): é o\r\n // que permite reaplicar a mesma vista ao dataset completo — ver `viewRef`/`DataTableView`.\r\n const filtrar = (rows: T[]) => {\r\n if (activeFilters.length === 0 && activeRanges.length === 0) return rows\r\n // Predicados resolvidos uma vez por lista, não por linha.\r\n const predicados = activeFilters.flatMap(([key, sel]) => passaFiltro(key, sel) ?? [])\r\n return rows.filter(\r\n (row) =>\r\n predicados.every((passa) => passa(row)) &&\r\n activeRanges.every(([key, r]) => {\r\n const col = columns.find((c) => c.key === key)\r\n if (!col) return true\r\n const n = valorParaIntervalo(row, col)\r\n // Sem valor não passa num intervalo — \"maior que 30\" não inclui \"sem dias\", e um\r\n // intervalo de datas não inclui linhas sem data.\r\n if (!Number.isFinite(n)) return false\r\n if (r.min != null && n < r.min) return false\r\n if (r.max != null && n > r.max) return false\r\n return true\r\n })\r\n )\r\n }\r\n\r\n const ordenar = (rows: T[]) => {\r\n if (!sort) return rows\r\n const col = columns.find((c) => c.key === sort.key)\r\n if (!col) return rows\r\n return [...rows].sort((a, b) => compare(rawValue(a, col), rawValue(b, col), col.sortType) * sort.dir)\r\n }\r\n\r\n const filtered = useMemo(\r\n () => filtrar(effectiveData),\r\n // eslint-disable-next-line react-hooks/exhaustive-deps\r\n [effectiveData, activeFilters, activeRanges, columns]\r\n )\r\n\r\n const sorted = useMemo(\r\n () => ordenar(filtered),\r\n // eslint-disable-next-line react-hooks/exhaustive-deps\r\n [filtered, sort, columns]\r\n )\r\n\r\n // Ref para o aviso não depender da identidade da função (quem chama passa normalmente uma arrow nova).\r\n const avisarVisiveis = useRef(onVisibleRowsChange)\r\n avisarVisiveis.current = onVisibleRowsChange\r\n useEffect(() => {\r\n avisarVisiveis.current?.(sorted)\r\n }, [sorted])\r\n\r\n // ─── Agrupamento ───────────────────────────────────────────────────────────\r\n\r\n /** Muda o agrupamento: no estado interno, ou só avisa quem o controla (`groupBy`). */\r\n const mudarGrupos = (proximo: string[]) => {\r\n if (!grupoControlado) setGroupByInterno(proximo)\r\n onGroupByChange?.(proximo)\r\n setCollapsedGroups(new Set())\r\n }\r\n const alternarGrupo = (key: string) => {\r\n if (!allData) onDataNeeded?.()\r\n mudarGrupos(groupByKeys.includes(key) ? groupByKeys.filter((k) => k !== key) : [...groupByKeys, key])\r\n }\r\n const removerGrupo = (key: string) => mudarGrupos(groupByKeys.filter((k) => k !== key))\r\n const limparGrupos = () => mudarGrupos([])\r\n const alternarColapso = (path: string) =>\r\n setCollapsedGroups((prev) => {\r\n const n = new Set(prev)\r\n if (n.has(path)) n.delete(path)\r\n else n.add(path)\r\n return n\r\n })\r\n\r\n const construirGrupos = (items: T[], keys: string[], nivel: number, pathPai: string): GrupoNo<T>[] => {\r\n if (nivel >= keys.length) return []\r\n const key = keys[nivel]\r\n const col = columns.find((c) => c.key === key)\r\n if (!col) return []\r\n // `groupSortKey` manda na ORDEM dos grupos; o rótulo continua a ser o valor da coluna.\r\n const ordemFn = col.groupSortKey\r\n const mapa = new Map<string, { ordem: string; items: T[] }>()\r\n items.forEach((item) => {\r\n const rotulo = filterKey(item, col)\r\n const ordem = ordemFn ? String(ordemFn(item) ?? '') : rotulo\r\n const atual = mapa.get(rotulo)\r\n if (atual) atual.items.push(item)\r\n else mapa.set(rotulo, { ordem, items: [item] })\r\n })\r\n return Array.from(mapa.entries())\r\n .sort(([ra, a], [rb, b]) => (ordemFn ? COLLATOR.compare(a.ordem, b.ordem) : ordenarRotulos(ra, rb)))\r\n .map(([val, { ordem, items: itensGrupo }]) => {\r\n const path = pathPai ? `${pathPai}\\u0000${val}` : val\r\n return { val, ordem, path, items: itensGrupo, filhos: construirGrupos(itensGrupo, keys, nivel + 1, path) }\r\n })\r\n }\r\n\r\n const grupos = useMemo(\r\n () => (isGrouped ? construirGrupos(sorted, groupByKeys, 0, '') : []),\r\n // eslint-disable-next-line react-hooks/exhaustive-deps\r\n [sorted, groupByKeys, isGrouped, columns]\r\n )\r\n\r\n // ─── Paginação (desligada ao agrupar) ──────────────────────────────────────\r\n\r\n const [page, setPage] = useState(1)\r\n const chavePageSize = storageKey ? `dt_pagesize_${storageKey}` : null\r\n // O tamanho escolhido no selector volta da sessão; só faz sentido com paginação ligada (`pageSize`).\r\n const [pageSizeAtual, setPageSizeAtual] = useState<number | undefined>(() => {\r\n if (!pageSize || !chavePageSize) return pageSize\r\n try {\r\n const guardado = Number(sessionStorage.getItem(chavePageSize))\r\n return Number.isFinite(guardado) && guardado > 0 ? guardado : pageSize\r\n } catch {\r\n return pageSize\r\n }\r\n })\r\n // Mudar a prop depois de montar continua a ganhar à escolha (como antes); ao montar não, senão apagava a da sessão.\r\n const pageSizeProp = useRef(pageSize)\r\n useEffect(() => {\r\n if (pageSizeProp.current === pageSize) return\r\n pageSizeProp.current = pageSize\r\n setPageSizeAtual(pageSize)\r\n }, [pageSize])\r\n const escolherPageSize = (n: number) => {\r\n setPageSizeAtual(n)\r\n if (!chavePageSize) return\r\n try {\r\n sessionStorage.setItem(chavePageSize, String(n))\r\n } catch {\r\n /* sessionStorage indisponível */\r\n }\r\n }\r\n useEffect(() => {\r\n setPage(1)\r\n }, [columnFilters, rangeFilters, sort, groupByKeys, pageSizeAtual, isClientMode])\r\n // Com `allData`, `data` já vem paginado pela app: paginar aqui só em modo cliente (aí as linhas são as de\r\n // `allData`). Fora dele seria paginar a página — com a página 2 da app a mostrar a 1.ª fatia dela.\r\n const paginando = !!pageSizeAtual && !isGrouped && (!allData || isClientMode)\r\n const totalPages = paginando ? Math.max(1, Math.ceil(sorted.length / pageSizeAtual!)) : 1\r\n const currentPage = Math.min(page, totalPages)\r\n const pageData = paginando ? sorted.slice((currentPage - 1) * pageSizeAtual!, currentPage * pageSizeAtual!) : sorted\r\n\r\n // ─── Seleção ───────────────────────────────────────────────────────────────\r\n\r\n const chaveDe = (row: T, i: number) => String(rowKey ? rowKey(row, i) : i)\r\n\r\n /**\r\n * Índice de cada linha na lista completa. Ao agrupar, as linhas são desenhadas grupo a\r\n * grupo e o índice local recomeçava em 0 em cada um — o que dava chaves React repetidas,\r\n * a zebra a reiniciar e, sem `rowKey`, linhas de grupos diferentes a partilhar a mesma\r\n * chave de seleção.\r\n */\r\n const indicePorLinha = useMemo(() => {\r\n const m = new Map<T, number>()\r\n sorted.forEach((r, i) => m.set(r, i))\r\n return m\r\n }, [sorted])\r\n const podeSelecionar = (row: T) => !isRowSelectable || isRowSelectable(row)\r\n // Só as selecionáveis: são estas que o \"selecionar todas\" marca e que contam para o seu estado.\r\n const visibleKeys = useMemo(\r\n () => pageData.flatMap((row, i) => (podeSelecionar(row) ? [chaveDe(row, i)] : [])),\r\n // eslint-disable-next-line react-hooks/exhaustive-deps\r\n [pageData, rowKey, isRowSelectable]\r\n )\r\n const alternarLinha = (key: string) =>\r\n setSelectedKeys((prev) => {\r\n const next = new Set(prev)\r\n if (next.has(key)) next.delete(key)\r\n else next.add(key)\r\n onSelectionChange?.(next)\r\n return next\r\n })\r\n const alternarTodas = () =>\r\n setSelectedKeys((prev) => {\r\n const todasMarcadas = visibleKeys.length > 0 && visibleKeys.every((k) => prev.has(k))\r\n const next = new Set(prev)\r\n if (todasMarcadas) visibleKeys.forEach((k) => next.delete(k))\r\n else visibleKeys.forEach((k) => next.add(k))\r\n onSelectionChange?.(next)\r\n return next\r\n })\r\n const todasVisiveisMarcadas = visibleKeys.length > 0 && visibleKeys.every((k) => selectedKeys.has(k))\r\n const algumaVisivelMarcada = visibleKeys.some((k) => selectedKeys.has(k)) && !todasVisiveisMarcadas\r\n\r\n // ─── Handle externo da vista ───────────────────────────────────────────────\r\n\r\n // Sem deps: é lido de fora a pedido (ex: ao exportar), tem de estar sempre coerente com o\r\n // último render.\r\n useEffect(() => {\r\n if (!viewRef) return\r\n viewRef.current = {\r\n apply: (rows) => ordenar(filtrar(rows)),\r\n rows: sorted,\r\n hasFilters: hasActiveFilters,\r\n precisaDataset: hasActiveFilters || !!sort || isGrouped,\r\n estado: () => ({\r\n columnFilters: Object.fromEntries(activeFilters.map(([k, v]) => [k, [...v]])),\r\n rangeFilters: Object.fromEntries(activeRanges),\r\n sortKey: sort?.key ?? null,\r\n sortDir: sort ? (sort.dir === 1 ? 'asc' : 'desc') : 'asc',\r\n groupBy: groupByKeys,\r\n }),\r\n repor: (e) => {\r\n setColumnFilters(lerFiltrosGuardados(columns, e?.columnFilters ?? {}))\r\n setRangeFilters(e?.rangeFilters ?? {})\r\n setSort(e?.sortKey ? { key: e.sortKey, dir: e.sortDir === 'desc' ? -1 : 1 } : null)\r\n // Também limpa os grupos recolhidos: a vista diz por que se agrupa, não o que estava\r\n // aberto no momento em que foi gravada.\r\n mudarGrupos(e?.groupBy ?? [])\r\n const precisaTudo =\r\n !!e?.sortKey ||\r\n !!e?.groupBy?.length ||\r\n Object.keys(e?.columnFilters ?? {}).length > 0 ||\r\n Object.keys(e?.rangeFilters ?? {}).length > 0\r\n if (precisaTudo && !allData) onDataNeeded?.()\r\n },\r\n }\r\n })\r\n\r\n // ─── Ações de filtro/ordenação ─────────────────────────────────────────────\r\n\r\n const toggleSort = (key: string) => {\r\n if (!allData) onDataNeeded?.()\r\n setSort((s) => (s?.key === key ? (s.dir === 1 ? { key, dir: -1 } : null) : { key, dir: 1 }))\r\n }\r\n\r\n /**\r\n * Selecção actual da coluna aberta, já materializada.\r\n *\r\n * No modo Excel, sem entrada no mapa está tudo seleccionado — e é aqui que isso passa de\r\n * implícito a explícito, na primeira vez que se desmarca alguma coisa. Só serve a coluna\r\n * aberta: `openFilterOptions` é dela.\r\n */\r\n const selecaoDaColuna = (key: string) =>\r\n new Set(columnFilters[key] ?? (modoExcel ? openFilterOptions : []))\r\n\r\n const aplicarSelecao = (key: string, sel: Set<string>) =>\r\n mudarFiltrosCol((prev) => {\r\n const proximo = { ...prev }\r\n // Tudo marcado é o mesmo que não haver filtro. Apagar a chave desliga o funil e tira o\r\n // chip, em vez de deixar um filtro que não filtra nada.\r\n if (modoExcel && sel.size === openFilterOptions.length) delete proximo[key]\r\n else proximo[key] = sel\r\n return proximo\r\n })\r\n\r\n const toggleFilterValue = (key: string, val: string) => {\r\n if (!allData) onDataNeeded?.()\r\n const sel = selecaoDaColuna(key)\r\n if (sel.has(val)) sel.delete(val)\r\n else sel.add(val)\r\n aplicarSelecao(key, sel)\r\n }\r\n\r\n /** `(Selecionar tudo)` — trabalha sobre os `correspondentes`, não sobre o que está desenhado. */\r\n const alternarTodos = (key: string, marcar: boolean) => {\r\n if (!allData) onDataNeeded?.()\r\n const sel = selecaoDaColuna(key)\r\n for (const v of opcoesVisiveis.correspondentes) {\r\n if (marcar) sel.add(v)\r\n else sel.delete(v)\r\n }\r\n aplicarSelecao(key, sel)\r\n }\r\n\r\n const setRange = (key: string, campo: 'min' | 'max', valor: string, tipo: DataTableColumn<T>['sortType']) => {\r\n if (!allData) onDataNeeded?.()\r\n setRangeFilters((prev) => {\r\n const atual = { ...(prev[key] ?? {}) }\r\n // Datas chegam do `<input type=\"date\">` em 'aaaa-mm-dd'; guarda-se o timestamp para o\r\n // filtro comparar sempre números. O `max` de uma data cobre o dia todo.\r\n const n = tipo === 'date' ? timestampDeData(valor) + (campo === 'max' ? 86_399_999 : 0) : Number(valor)\r\n if (valor === '' || !Number.isFinite(n)) delete atual[campo]\r\n else atual[campo] = n\r\n const next = { ...prev }\r\n if (atual.min == null && atual.max == null) delete next[key]\r\n else next[key] = atual\r\n return next\r\n })\r\n }\r\n\r\n const clearColumnFilter = (key: string) => {\r\n mudarFiltrosCol((prev) => {\r\n const n = { ...prev }\r\n delete n[key]\r\n return n\r\n })\r\n setRangeFilters((prev) => {\r\n const n = { ...prev }\r\n delete n[key]\r\n return n\r\n })\r\n }\r\n\r\n const clearAllFilters = () => {\r\n mudarFiltrosCol(() => ({}))\r\n setRangeFilters({})\r\n }\r\n\r\n const temIntervalo = (key: string) => {\r\n const r = rangeFilters[key]\r\n return !!r && (r.min != null || r.max != null)\r\n }\r\n // No modo Excel o funil acende com a chave presente, mesmo com o conjunto vazio.\r\n const temFiltro = (key: string) =>\r\n (modoExcel ? columnFilters[key] !== undefined : (columnFilters[key]?.size ?? 0) > 0) || temIntervalo(key)\r\n\r\n const handleExport = () => {\r\n if (onExport) return onExport(sorted)\r\n const csv = toCsv(columns, sorted)\r\n const blob = new Blob([`${csv}`], { type: 'text/csv;charset=utf-8;' })\r\n const url = URL.createObjectURL(blob)\r\n const a = document.createElement('a')\r\n a.href = url\r\n a.download = `${exportFileName}.csv`\r\n a.click()\r\n URL.revokeObjectURL(url)\r\n }\r\n\r\n const alignClass = (a?: DataTableColumn<T>['align']) =>\r\n a === 'right' ? 'text-right' : a === 'center' ? 'text-center' : 'text-left'\r\n\r\n /** `sticky: true` mantem o significado antigo: presa a esquerda. */\r\n const ePresaAEsquerda = (c: DataTableColumn<T>) => c.sticky === true || c.sticky === 'left'\r\n\r\n // Offset acumulado por coluna sticky — sem isto, 2+ colunas sticky ficavam todas em\r\n // `left:0`, sobrepostas (a 2.ª por cima da 1.ª). Colunas sticky sem `width` usam um valor\r\n // por defeito razoável para a acumulação (não afeta a largura real da coluna).\r\n const stickyLeft = useMemo(() => {\r\n const offsets: Record<string, number> = {}\r\n let acc = selectable ? 40 : 0\r\n for (const c of columns) {\r\n if (!ePresaAEsquerda(c)) continue\r\n offsets[c.key] = acc\r\n acc += c.width ?? DEFAULT_STICKY_WIDTH\r\n }\r\n return offsets\r\n }, [columns, selectable])\r\n\r\n // À direita acumula-se ao contrário: a última coluna encosta a 0 e as\r\n // anteriores empilham-se por cima da largura das seguintes.\r\n const stickyRight = useMemo(() => {\r\n const offsets: Record<string, number> = {}\r\n let acc = 0\r\n for (let i = columns.length - 1; i >= 0; i--) {\r\n const c = columns[i]\r\n if (c.sticky !== 'right') continue\r\n offsets[c.key] = acc\r\n acc += c.width ?? DEFAULT_STICKY_WIDTH\r\n }\r\n return offsets\r\n }, [columns])\r\n\r\n const estiloPreso = (c: DataTableColumn<T>): React.CSSProperties | undefined => {\r\n if (ePresaAEsquerda(c)) return { left: stickyLeft[c.key] }\r\n if (c.sticky === 'right') return { right: stickyRight[c.key] }\r\n return undefined\r\n }\r\n\r\n const nColunas = columns.length + (selectable ? 1 : 0)\r\n\r\n // Paginação de servidor: fora do modo cliente `data` é só a página, e o total é o do servidor. Com filtros\r\n // ativos (mesmo antes de `allData` chegar) conta-se o que passa nos filtros, como sempre.\r\n const totalResultados = totalCount != null && !isClientMode && !hasActiveFilters ? totalCount : sorted.length\r\n\r\n // Enquanto o dataset completo não chega, filtrar/ordenar/agrupar só olharia para a página —\r\n // melhor esperar do que mostrar uma lista errada (ou vazia) por baixo de um total nos milhares.\r\n // Com `onDataNeeded` a tabela já o pediu: espera mesmo que a app ainda não tenha começado a\r\n // carregá-lo (`isAllDataLoading` ainda a false no primeiro render depois do pedido).\r\n const aEsperarDataset = !allData && (!!isAllDataLoading || !!onDataNeeded) && (hasActiveFilters || !!sort || isGrouped)\r\n\r\n // Antes dos returns de 'a carregar'/erro: um hook depois deles muda a ordem dos hooks entre renders.\r\n // Por omissão o cabeçalho oferece ordenar, filtrar e agrupar (ver `filterable`/`groupable`). Colunas sem dados\r\n // em nenhuma linha (ex.: botões de ações) não têm o que filtrar nem agrupar.\r\n const linhasBase = allData ?? data\r\n const semDados = useMemo(\r\n () => new Set(columns.filter((c) => !linhasBase.some((r) => temValor(r, c))).map((c) => c.key)),\r\n [columns, linhasBase]\r\n )\r\n const filtravel = (c: DataTableColumn<T>) => c.filterable ?? !semDados.has(c.key)\r\n // Com `filterValues` agrupa pelo `value` da linha (a combinação): cada linha fica num grupo só.\r\n const agrupavel = (c: DataTableColumn<T>) =>\r\n c.groupable ?? (!semDados.has(c.key) && !c.groupAggregate && (!c.sortType || c.sortType === 'text'))\r\n\r\n /** ←/→ deslizam, Home/End vão aos extremos. Só com o foco no próprio contentor (não num campo de uma célula). */\r\n const rolarComTeclado = (e: React.KeyboardEvent<HTMLDivElement>) => {\r\n if (e.target !== e.currentTarget) return\r\n const el = e.currentTarget\r\n const passo = Math.max(40, Math.round(el.clientWidth * 0.2))\r\n if (e.key === 'ArrowRight') el.scrollLeft += passo\r\n else if (e.key === 'ArrowLeft') el.scrollLeft -= passo\r\n else if (e.key === 'Home') el.scrollLeft = 0\r\n else if (e.key === 'End') el.scrollLeft = el.scrollWidth\r\n else return\r\n e.preventDefault()\r\n }\r\n\r\n // A carregar já não substitui a tabela inteira: o cabeçalho fica e a linha de \"a carregar\" vive no corpo.\r\n if (isError) {\r\n return <div className=\"py-16 text-center text-sm font-bold uppercase tracking-widest text-red-400\">{L.error}</div>\r\n }\r\n\r\n // ─── Sub-render: popup de filtro ───────────────────────────────────────────\r\n\r\n /**\r\n * Popup do funil de `coluna`, a filtrar a lista `key` (a própria coluna ou um dos seus `extraFilters`).\r\n * Com mais de uma lista, um seletor no topo troca entre elas.\r\n */\r\n const renderPopup = (coluna: DataTableColumn<T>, key: string, listas: { key: string; label: string }[]) => {\r\n const col = key === coluna.key ? coluna : undefined\r\n const { opts, total: totalOpcoes } = opcoesVisiveis\r\n const sel = columnFilters[key]\r\n const isFiltered = temFiltro(key)\r\n const podeOrdenar = !!sortFromFilter && !!col && col.sortable !== false\r\n const eData = col?.sortType === 'date'\r\n const temRange = !!col && (col.sortType === 'number' || eData)\r\n // Textos da ordenação conforme o tipo: A→Z não diz nada numa data ou num valor.\r\n const [rotuloAsc, rotuloDesc] = eData\r\n ? [L.sortOldest, L.sortNewest]\r\n : col?.sortType === 'number'\r\n ? [L.sortSmallest, L.sortLargest]\r\n : [L.sortAsc, L.sortDesc]\r\n const [rotuloMin, rotuloMax] = eData ? [L.from, L.to] : [L.min, L.max]\r\n // Valor atual do intervalo nos campos (ao reabrir ou trocar de lista). Datas: 'aaaa-mm-dd', em UTC como o `setRange` grava.\r\n const intervalo = rangeFilters[key]\r\n const valorCampo = (n: number | undefined, campo: 'min' | 'max') =>\r\n n == null ? '' : eData ? new Date(campo === 'max' ? n - 86_399_999 : n).toISOString().slice(0, 10) : String(n)\r\n return createPortal(\r\n <div\r\n ref={popupRef}\r\n style={{ position: 'fixed', top: popupPos!.top, left: popupPos!.left }}\r\n className=\"z-[100] w-72 max-w-[80vw] bg-white dark:bg-slate-900 border border-slate-200 dark:border-slate-700 shadow-2xl rounded-xl overflow-hidden\"\r\n >\r\n {listas.length > 1 && (\r\n // Seletor da lista a filtrar: a coluna e cada filtro extra partilham o mesmo funil.\r\n <div className=\"px-2 pt-2 pb-2 border-b border-slate-100 dark:border-slate-800\">\r\n <div role=\"group\" aria-label={coluna.label} className=\"flex flex-wrap gap-0.5 p-0.5 rounded-lg bg-slate-100 dark:bg-slate-800\">\r\n {listas.map((l) => {\r\n const ativa = l.key === key\r\n return (\r\n <button\r\n key={l.key}\r\n type=\"button\"\r\n aria-pressed={ativa}\r\n onClick={() => {\r\n if (ativa) return\r\n setOpenFilter(l.key)\r\n setFilterSearch('')\r\n }}\r\n className={`appearance-none border-0 cursor-pointer flex-1 min-w-0 inline-flex items-center justify-center gap-1 px-2 py-1 rounded-md text-[10px] font-black uppercase tracking-wider truncate transition-colors ${\r\n ativa\r\n ? 'bg-white dark:bg-slate-900 text-proef-blue dark:text-proef-turquoise shadow-sm'\r\n : 'bg-transparent text-slate-500 dark:text-slate-400 hover:text-slate-700 dark:hover:text-slate-200'\r\n }`}\r\n >\r\n <span className=\"truncate\">{l.label}</span>\r\n {/* Marca as listas com filtro ativo: o funil acende por qualquer uma delas. */}\r\n {temFiltro(l.key) && <span aria-hidden className=\"w-1.5 h-1.5 rounded-full bg-proef-turquoise shrink-0\" />}\r\n </button>\r\n )\r\n })}\r\n </div>\r\n </div>\r\n )}\r\n {!allData && isAllDataLoading && (\r\n // As opções à vista ainda são só as de `data`. Dizê-lo é melhor do que deixar\r\n // escolher de uma lista incompleta.\r\n <div className=\"px-2 py-1.5 bg-amber-50 dark:bg-amber-500/10 border-b border-amber-100 dark:border-amber-500/20 text-[10px] text-amber-700 dark:text-amber-300\">\r\n {L.loadingOptions}\r\n </div>\r\n )}\r\n {podeOrdenar && (\r\n <div className=\"border-b border-slate-100 dark:border-slate-800 p-1.5 space-y-0.5\">\r\n <button\r\n onClick={() => {\r\n if (!allData) onDataNeeded?.()\r\n setSort({ key, dir: 1 })\r\n }}\r\n className={`appearance-none bg-transparent border-0 cursor-pointer w-full flex items-center gap-2 px-2.5 py-1.5 text-[11px] font-semibold text-left hover:bg-slate-50 dark:hover:bg-slate-800 ${\r\n sort?.key === key && sort.dir === 1 ? 'text-proef-blue dark:text-proef-turquoise bg-slate-50 dark:bg-slate-800' : 'text-slate-600 dark:text-slate-300'\r\n }`}\r\n >\r\n ▲ {rotuloAsc}\r\n </button>\r\n <button\r\n onClick={() => {\r\n if (!allData) onDataNeeded?.()\r\n setSort({ key, dir: -1 })\r\n }}\r\n className={`appearance-none bg-transparent border-0 cursor-pointer w-full flex items-center gap-2 px-2.5 py-1.5 text-[11px] font-semibold text-left hover:bg-slate-50 dark:hover:bg-slate-800 ${\r\n sort?.key === key && sort.dir === -1 ? 'text-proef-blue dark:text-proef-turquoise bg-slate-50 dark:bg-slate-800' : 'text-slate-600 dark:text-slate-300'\r\n }`}\r\n >\r\n ▼ {rotuloDesc}\r\n </button>\r\n </div>\r\n )}\r\n <div className=\"p-2\">\r\n {temRange && (\r\n // Intervalo antes da lista: numa coluna numérica ou de datas é o que se procura\r\n // (\"acima de 30 dias\", \"a partir de julho\"), não um valor exato numa lista de dezenas.\r\n <div className=\"flex items-center gap-1.5 mb-2\">\r\n <input\r\n key={`${key}-min`}\r\n type={eData ? 'date' : 'number'}\r\n defaultValue={valorCampo(intervalo?.min, 'min')}\r\n onChange={(e) => setRange(key, 'min', e.target.value, col!.sortType)}\r\n placeholder={rotuloMin}\r\n title={rotuloMin}\r\n aria-label={rotuloMin}\r\n className=\"w-1/2 bg-slate-50 dark:bg-slate-800 dark:text-slate-100 dark:[color-scheme:dark] border border-slate-200 dark:border-slate-700 rounded-lg px-2 py-1.5 text-xs outline-none focus:border-proef-blue dark:focus:border-proef-turquoise\"\r\n />\r\n <input\r\n key={`${key}-max`}\r\n type={eData ? 'date' : 'number'}\r\n defaultValue={valorCampo(intervalo?.max, 'max')}\r\n onChange={(e) => setRange(key, 'max', e.target.value, col!.sortType)}\r\n placeholder={rotuloMax}\r\n title={rotuloMax}\r\n aria-label={rotuloMax}\r\n className=\"w-1/2 bg-slate-50 dark:bg-slate-800 dark:text-slate-100 dark:[color-scheme:dark] border border-slate-200 dark:border-slate-700 rounded-lg px-2 py-1.5 text-xs outline-none focus:border-proef-blue dark:focus:border-proef-turquoise\"\r\n />\r\n </div>\r\n )}\r\n <input\r\n autoFocus\r\n value={filterSearch}\r\n onChange={(e) => setFilterSearch(e.target.value)}\r\n placeholder={L.search}\r\n className=\"w-full bg-slate-50 dark:bg-slate-800 dark:text-slate-100 border border-slate-200 dark:border-slate-700 rounded-lg px-2 py-1.5 text-xs outline-none focus:border-proef-blue dark:focus:border-proef-turquoise mb-2\"\r\n />\r\n {/* `overflow-x-hidden` explícito: com só `overflow-y-auto`, o CSS calcula\r\n `overflow-x: auto` e a barra vertical rouba largura ao conteúdo até aparecer uma\r\n segunda barra, horizontal, que não faz falta nenhuma. */}\r\n <div className=\"max-h-64 overflow-y-auto overflow-x-hidden space-y-0.5\">\r\n {modoExcel && opts.length > 0 && (() => {\r\n // Tri-state sobre os valores que a pesquisa apanha: ✓ todos, – alguns, vazio nenhum.\r\n const marcados = opcoesVisiveis.correspondentes.filter((v) => (sel ? sel.has(v) : true)).length\r\n const todos = marcados === opcoesVisiveis.correspondentes.length\r\n const alguns = marcados > 0 && !todos\r\n return (\r\n <button\r\n onClick={() => alternarTodos(key, !todos)}\r\n className=\"appearance-none bg-transparent border-0 border-b border-slate-100 dark:border-slate-800 cursor-pointer w-full flex items-center gap-2 px-1.5 py-1 mb-0.5 hover:bg-slate-50 dark:hover:bg-slate-800 text-left normal-case tracking-normal\"\r\n >\r\n <span\r\n className={`w-3.5 h-3.5 flex items-center justify-center border shrink-0 rounded ${\r\n todos || alguns ? 'bg-proef-navy dark:bg-proef-turquoise border-proef-navy dark:border-proef-turquoise text-white dark:text-proef-navy' : 'border-slate-300 dark:border-slate-600'\r\n }`}\r\n >\r\n {todos && <span className=\"text-[9px] leading-none\">✓</span>}\r\n {alguns && <span className=\"text-[9px] leading-none\">–</span>}\r\n </span>\r\n <span className=\"text-xs text-slate-700 dark:text-slate-200 font-semibold break-words\">\r\n {filterSearch ? L.selectAllResults : L.selectAll}\r\n </span>\r\n </button>\r\n )\r\n })()}\r\n {opts.length === 0 ? (\r\n <p className=\"text-[11px] text-slate-400 text-center py-3\">{L.noOptions}</p>\r\n ) : (\r\n opts.map((v) => {\r\n // Sem entrada no mapa está tudo marcado, daí o `sel ? … : modoExcel`.\r\n const checked = sel ? sel.has(v) : modoExcel\r\n return (\r\n <button\r\n key={v}\r\n onClick={() => toggleFilterValue(key, v)}\r\n className=\"appearance-none bg-transparent border-0 cursor-pointer w-full flex items-center gap-2 px-1.5 py-1 hover:bg-slate-50 dark:hover:bg-slate-800 text-left normal-case tracking-normal\"\r\n >\r\n <span\r\n className={`w-3.5 h-3.5 flex items-center justify-center border shrink-0 rounded ${\r\n checked ? 'bg-proef-navy dark:bg-proef-turquoise border-proef-navy dark:border-proef-turquoise text-white dark:text-proef-navy' : 'border-slate-300 dark:border-slate-600'\r\n }`}\r\n >\r\n {checked && <span className=\"text-[9px] leading-none\">✓</span>}\r\n </span>\r\n <span className=\"text-xs text-slate-700 dark:text-slate-200 font-normal break-words\">{rotuloVisivel(key, v)}</span>\r\n </button>\r\n )\r\n })\r\n )}\r\n </div>\r\n {totalOpcoes > opts.length && (\r\n <p className=\"text-[10.5px] text-slate-400 text-center pt-2\">\r\n {L.moreOptions.replace('{shown}', String(opts.length)).replace('{total}', String(totalOpcoes))}\r\n </p>\r\n )}\r\n {isFiltered && (\r\n <button\r\n onClick={() => clearColumnFilter(key)}\r\n className=\"appearance-none bg-transparent border-0 border-t border-slate-100 dark:border-slate-800 cursor-pointer w-full mt-2 pt-2 text-[10px] font-black uppercase tracking-wider text-red-400 hover:text-red-500\"\r\n >\r\n {L.clear}\r\n </button>\r\n )}\r\n </div>\r\n </div>,\r\n document.body\r\n )\r\n }\r\n\r\n /**\r\n * Um só funil por coluna, mesmo com `extraFilters` (antes era um funil por filtro extra, e o cabeçalho\r\n * enchia-se de funis iguais). Acende se a coluna OU qualquer um dos filtros extra estiver filtrado.\r\n */\r\n const botaoFunil = (c: DataTableColumn<T>) => {\r\n const listas = [...(filtravel(c) ? [{ key: c.key, label: c.label }] : []), ...(c.extraFilters ?? []).map((f) => ({ key: f.key, label: f.label }))]\r\n if (listas.length === 0) return null\r\n const isOpen = funilAberto === c.key\r\n const isFiltered = listas.some((l) => temFiltro(l.key))\r\n return (\r\n <div\r\n key={c.key}\r\n className=\"relative\"\r\n ref={(el) => {\r\n if (el) filterRefs.current.set(c.key, el)\r\n else filterRefs.current.delete(c.key)\r\n }}\r\n >\r\n <button\r\n onClick={() => {\r\n // Abrir o funil é a 1.ª vez que as opções de todas as páginas fazem falta — é aqui\r\n // que se pede o dataset completo.\r\n if (!isOpen && !allData) onDataNeeded?.()\r\n setOpenFilter(isOpen ? null : listas[0].key)\r\n setFilterSearch('')\r\n }}\r\n className={`appearance-none border-0 bg-transparent cursor-pointer p-0.5 transition-colors ${\r\n isFiltered || isOpen ? 'text-proef-turquoise' : 'text-slate-300 dark:text-slate-600 hover:text-slate-500 dark:hover:text-slate-300'\r\n }`}\r\n // Sem isto o funil e um botao sem nome acessivel: nem leitor de ecra nem teste lhe\r\n // chegam pelo nome da coluna.\r\n title={c.label}\r\n aria-label={c.label}\r\n aria-expanded={isOpen}\r\n >\r\n <Funnel size={11} className={isFiltered ? 'fill-current' : ''} />\r\n </button>\r\n {isOpen && popupPos && openFilter && renderPopup(c, openFilter, listas)}\r\n </div>\r\n )\r\n }\r\n\r\n // ─── Sub-render: linhas ────────────────────────────────────────────────────\r\n\r\n const renderLinha = (row: T, i: number) => {\r\n const k = chaveDe(row, i)\r\n const marcada = selectable && selectedKeys.has(k)\r\n const selecionavel = !!selectable && podeSelecionar(row)\r\n return (\r\n <tr\r\n key={k}\r\n onClick={() => onRowClick?.(row)}\r\n onDoubleClick={() => onRowDoubleClick?.(row)}\r\n className={`group even:bg-slate-50/50 dark:even:bg-slate-800/40 hover:bg-slate-100/70 dark:hover:bg-slate-800/70 transition-colors ${\r\n onRowClick || onRowDoubleClick ? 'cursor-pointer' : ''\r\n } ${marcada ? 'bg-proef-turquoise/5' : ''} ${rowClassName?.(row) ?? ''}`}\r\n >\r\n {selectable && (\r\n <td\r\n className=\"px-3 py-2.5 border-b border-slate-100 dark:border-slate-800 w-10\"\r\n onClick={(e) => {\r\n e.stopPropagation()\r\n if (selecionavel) alternarLinha(k)\r\n }}\r\n >\r\n <span\r\n role=\"checkbox\"\r\n aria-checked={!!marcada}\r\n aria-disabled={!selecionavel || undefined}\r\n className={`w-4 h-4 flex items-center justify-center border transition-colors rounded ${\r\n marcada ? 'bg-proef-navy dark:bg-proef-turquoise border-proef-navy dark:border-proef-turquoise text-white dark:text-proef-navy' : 'border-slate-300 dark:border-slate-600 hover:border-slate-400 dark:hover:border-slate-500'\r\n } ${selecionavel ? '' : 'opacity-40 cursor-not-allowed bg-slate-100 dark:bg-slate-800'}`}\r\n >\r\n {marcada && <span className=\"text-[9px] leading-none\">✓</span>}\r\n </span>\r\n </td>\r\n )}\r\n {columns.map((c) => (\r\n <td\r\n key={c.key}\r\n style={estiloPreso(c)}\r\n // Fundo sólido explícito (não `bg-inherit`) — uma coluna sticky com fundo\r\n // transparente/translúcido deixa o conteúdo que passa por baixo, ao fazer\r\n // scroll horizontal, transparecer por trás do texto (sobreposição ilegível).\r\n // `group-hover` porque `:hover` isolado na célula não reage ao hover na linha.\r\n className={`px-3 py-2.5 border-b border-slate-100 dark:border-slate-800 whitespace-nowrap ${alignClass(c.align)} ${\r\n c.sticky ? `sticky z-[1] ${c.sticky === 'right' ? 'border-l border-slate-200 dark:border-slate-700' : ''} ${i % 2 === 1 ? 'bg-slate-50 dark:bg-slate-800' : 'bg-white dark:bg-slate-900'} group-hover:bg-slate-100 dark:group-hover:bg-slate-800` : ''\r\n } ${c.cellClassName ?? ''}`}\r\n >\r\n {(() => {\r\n const conteudo = c.render ? c.render(row) : rawValue(row, c) ?? <span className=\"text-slate-400\">{EMPTY}</span>\r\n const max = larguraMax(c)\r\n if (!max) return conteudo\r\n // Texto completo ao passar o rato (a célula corta com \"…\"): o que se vê, se for texto; senão o valor,\r\n // exceto em datas — aí o valor é o técnico (ISO/timestamp), diferente do que a célula mostra.\r\n const v = rawValue(row, c)\r\n const dica =\r\n typeof conteudo === 'string' || typeof conteudo === 'number'\r\n ? String(conteudo)\r\n : c.sortType !== 'date' && v !== null && v !== undefined\r\n ? String(v)\r\n : undefined\r\n return (\r\n <div className=\"overflow-hidden text-ellipsis\" style={{ maxWidth: max, width: larguras[c.key] }} title={dica}>\r\n {conteudo}\r\n </div>\r\n )\r\n })()}\r\n </td>\r\n ))}\r\n </tr>\r\n )\r\n }\r\n\r\n // Colunas com valor nas linhas de grupo/total: o rótulo ocupa as colunas até à primeira com valor; daí em\r\n // diante cada coluna tem a sua célula, alinhada com as linhas.\r\n\r\n const primeiraComValor = columns.findIndex((c) => !!c.groupAggregate)\r\n const colunasComValor = primeiraComValor >= 0 ? columns.slice(primeiraComValor) : []\r\n const colunasRotuloGrupo = primeiraComValor >= 0 ? primeiraComValor + (selectable ? 1 : 0) : nColunas\r\n\r\n const renderGrupos = (nos: GrupoNo<T>[], nivel: number): React.ReactNode =>\r\n nos.map((no) => {\r\n const recolhido = collapsedGroups.has(no.path)\r\n return (\r\n <Fragment key={no.path}>\r\n <tr className=\"bg-slate-50/80 dark:bg-slate-800/60 cursor-pointer select-none\" onClick={() => alternarColapso(no.path)}>\r\n <td colSpan={colunasRotuloGrupo} className=\"px-3 py-1.5 border-b border-t border-slate-100 dark:border-slate-800\">\r\n <div className=\"flex items-center gap-1.5\" style={{ paddingLeft: nivel * 16 }}>\r\n <Caret\r\n size={11}\r\n className={`text-proef-blue/60 dark:text-proef-turquoise/70 transition-transform shrink-0 ${recolhido ? '' : 'rotate-90'}`}\r\n />\r\n <span className=\"text-[10px] font-black uppercase tracking-[0.15em] text-proef-blue/70 dark:text-proef-turquoise/80\">{no.val}</span>\r\n <span className=\"text-[10px] text-slate-400 ml-1\">({no.items.length})</span>\r\n </div>\r\n </td>\r\n {colunasComValor.map((c) => (\r\n <td key={c.key} className={`px-3 py-1.5 border-b border-t border-slate-100 dark:border-slate-800 whitespace-nowrap text-xs font-bold text-slate-700 dark:text-slate-200 ${alignClass(c.align)}`}>\r\n {c.groupAggregate?.(no.items)}\r\n </td>\r\n ))}\r\n </tr>\r\n {!recolhido &&\r\n (no.filhos.length > 0\r\n ? renderGrupos(no.filhos, nivel + 1)\r\n : no.items.map((row) => renderLinha(row, indicePorLinha.get(row) ?? 0)))}\r\n </Fragment>\r\n )\r\n })\r\n\r\n // ─── Render ────────────────────────────────────────────────────────────────\r\n\r\n return (\r\n <div>\r\n {/* Com vários filtros no `extraToolbar` a barra passa para a linha seguinte em vez de sair do ecrã. */}\r\n <div className=\"flex flex-wrap items-center gap-2 mb-2.5\">\r\n {/* A carregar o total ainda não diz nada (seria \"0\"). */}\r\n <span className=\"text-xs text-slate-500 dark:text-slate-400\">{isLoading ? '' : L.results.replace('{count}', String(totalResultados))}</span>\r\n <div className=\"ml-auto flex flex-wrap items-center justify-end gap-2\">\r\n {extraToolbar}\r\n {exportFileName && sorted.length > 0 && (\r\n <button\r\n onClick={handleExport}\r\n className=\"appearance-none cursor-pointer inline-flex items-center gap-1.5 h-8 px-3.5 rounded-lg border border-proef-navy dark:border-proef-turquoise bg-proef-navy dark:bg-proef-turquoise text-white dark:text-proef-navy hover:bg-proef-blue dark:hover:bg-proef-turquoise/80 text-[11px] font-black uppercase tracking-widest transition-colors\"\r\n >\r\n ⭳ {L.export}\r\n </button>\r\n )}\r\n </div>\r\n </div>\r\n\r\n {/* Agrupamento e filtros ativos: as mesmas etiquetas (FilterChips) que os ecrãs usam fora da tabela. */}\r\n {isGrouped && (\r\n <FilterChips\r\n className=\"mb-2\"\r\n items={groupByKeys.map((key) => ({\r\n key,\r\n tone: 'group' as const,\r\n icon: <Layers size={9} />,\r\n label: columns.find((c) => c.key === key)?.label ?? key,\r\n onRemove: () => removerGrupo(key),\r\n }))}\r\n onClearAll={limparGrupos}\r\n clearAllLabel={L.clearGroups}\r\n />\r\n )}\r\n\r\n {(activeFilters.length > 0 || activeRanges.length > 0) && (\r\n <FilterChips\r\n className=\"mb-2\"\r\n items={[\r\n ...activeRanges.map(([key, r]) => {\r\n const col = columns.find((c) => c.key === key)\r\n const fmt = (v?: number) =>\r\n v == null ? '' : col?.sortType === 'date' ? new Date(v).toLocaleDateString() : String(v)\r\n const txt =\r\n r.min != null && r.max != null\r\n ? `${fmt(r.min)} – ${fmt(r.max)}`\r\n : r.min != null\r\n ? `≥ ${fmt(r.min)}`\r\n : `≤ ${fmt(r.max)}`\r\n return { key: `r-${key}`, label: `${col?.label ?? key}: ${txt}`, onRemove: () => clearColumnFilter(key) }\r\n }),\r\n ...activeFilters.flatMap(([key, sel]) => {\r\n const col = columns.find((c) => c.key === key)\r\n const extra = !col ? columns.flatMap((c) => c.extraFilters ?? []).find((f) => f.key === key) : undefined\r\n const chipLabel = col?.label ?? extra?.label ?? key\r\n const arr = Array.from(sel)\r\n // Coluna com cores por valor: um chip colorido por valor, removível à parte.\r\n if (col?.valueChipColors) {\r\n const cores = col.valueChipColors\r\n return arr.map((v) => ({\r\n key: `${key}:${v}`,\r\n label: `${chipLabel}: ${rotuloVisivel(key, v)}`,\r\n colorClassName: cores[v],\r\n onRemove: () => toggleFilterValue(key, v),\r\n }))\r\n }\r\n // Com 0 marcados o chip tem de dizer \"0\" — juntar uma lista vazia dava um chip mudo.\r\n const txt =\r\n arr.length > 2 || arr.length === 0\r\n ? L.selected.replace('{count}', String(arr.length))\r\n : arr.map((v) => rotuloVisivel(key, v)).join(', ')\r\n return [{ key, label: `${chipLabel}: ${txt}`, onRemove: () => clearColumnFilter(key) }]\r\n }),\r\n ]}\r\n onClearAll={clearAllFilters}\r\n clearAllLabel={L.clearAll}\r\n />\r\n )}\r\n {/* rounded-xl: os mesmos cantos dos cartões da biblioteca (Ui.tsx) e das tabelas dos portais. */}\r\n <div\r\n className={`overflow-auto rounded-xl border border-slate-200 dark:border-slate-700 bg-white dark:bg-slate-900 ${\r\n scrollAffordance ? 'outline-none focus-visible:ring-2 focus-visible:ring-proef-blue dark:focus-visible:ring-proef-turquoise' : ''\r\n }`}\r\n style={{ maxHeight }}\r\n {...(scrollAffordance\r\n ? { tabIndex: 0, role: 'region', 'aria-label': L.scrollRegion ?? DEFAULT_LABELS.scrollRegion, onKeyDown: rolarComTeclado }\r\n : {})}\r\n >\r\n <table className=\"w-full text-[13px] border-collapse dark:text-slate-200\">\r\n <thead className=\"sticky top-0 z-[2]\">\r\n <tr>\r\n {selectable && (\r\n <th className=\"bg-slate-50 dark:bg-slate-800 px-3 py-2.5 border-b border-slate-200 dark:border-slate-700 w-10 sticky left-0 z-[3]\">\r\n <button\r\n onClick={alternarTodas}\r\n className={`appearance-none cursor-pointer w-4 h-4 flex items-center justify-center border transition-colors rounded ${\r\n todasVisiveisMarcadas\r\n ? 'bg-proef-navy dark:bg-proef-turquoise border-proef-navy dark:border-proef-turquoise text-white dark:text-proef-navy'\r\n : algumaVisivelMarcada\r\n ? 'bg-slate-300 dark:bg-slate-600 border-slate-300 dark:border-slate-600 text-white'\r\n : 'bg-white dark:bg-slate-900 border-slate-300 dark:border-slate-600 hover:border-slate-400 dark:hover:border-slate-500'\r\n }`}\r\n >\r\n {(todasVisiveisMarcadas || algumaVisivelMarcada) && <span className=\"text-[9px] leading-none\">✓</span>}\r\n </button>\r\n </th>\r\n )}\r\n {columns.map((c) => {\r\n const podeOrdenar = c.sortable !== false\r\n const podeAgrupar = agrupavel(c)\r\n const agrupada = groupByKeys.includes(c.key)\r\n return (\r\n <th\r\n key={c.key}\r\n style={{\r\n ...(c.width ? { minWidth: c.width } : {}),\r\n ...(larguras[c.key] ? { width: larguras[c.key], minWidth: larguras[c.key], maxWidth: larguras[c.key] } : {}),\r\n ...(estiloPreso(c) ?? {}),\r\n }}\r\n className={`group/th relative bg-slate-50 dark:bg-slate-800 text-[11.5px] uppercase tracking-wide text-slate-500 dark:text-slate-400 font-semibold px-3 py-2.5 border-b border-slate-200 dark:border-slate-700 whitespace-nowrap select-none ${alignClass(\r\n c.align\r\n )} ${c.sticky ? `sticky z-[3] bg-slate-50 dark:bg-slate-800 ${c.sticky === 'right' ? 'border-l border-slate-200 dark:border-slate-700' : ''}` : ''} ${c.headerClassName ?? ''}`}\r\n >\r\n {resizableColumns && (\r\n // Pega no limite direito: arrastar redimensiona; duplo clique repõe a largura automática\r\n <span\r\n role=\"separator\"\r\n aria-orientation=\"vertical\"\r\n title={L.resizeColumn}\r\n onMouseDown={(e) => iniciarResize(e, c.key)}\r\n onDoubleClick={() => reporLargura(c.key)}\r\n onClick={(e) => e.stopPropagation()}\r\n className=\"absolute right-0 top-0 z-[4] h-full w-1.5 cursor-col-resize bg-transparent hover:bg-proef-blue/40 group-hover/th:bg-slate-300/60 dark:group-hover/th:bg-slate-600/60\"\r\n />\r\n )}\r\n <div className=\"flex items-center gap-1.5\">\r\n {podeOrdenar ? (\r\n <button\r\n onClick={() => toggleSort(c.key)}\r\n className=\"appearance-none bg-transparent border-0 cursor-pointer p-0 uppercase tracking-wide text-slate-500 dark:text-slate-400 hover:text-proef-blue dark:hover:text-proef-turquoise font-semibold\"\r\n >\r\n {c.label}\r\n {sort?.key === c.key && <span className=\"ml-1\">{sort.dir === 1 ? '▲' : '▼'}</span>}\r\n </button>\r\n ) : (\r\n <span>{c.label}</span>\r\n )}\r\n\r\n {podeAgrupar && (\r\n <button\r\n onClick={() => alternarGrupo(c.key)}\r\n title={(agrupada ? L.ungroup : L.groupBy).replace('{label}', c.label)}\r\n className={`appearance-none bg-transparent border-0 cursor-pointer p-0.5 transition-colors ${\r\n agrupada ? 'text-violet-600 dark:text-violet-300' : 'text-slate-300 dark:text-slate-600 hover:text-slate-500 dark:hover:text-slate-300'\r\n }`}\r\n >\r\n <Layers size={10} />\r\n </button>\r\n )}\r\n\r\n {botaoFunil(c)}\r\n </div>\r\n {c.headerExtra && (\r\n <div className=\"mt-0.5 normal-case font-normal tracking-normal\">{c.headerExtra(filtered)}</div>\r\n )}\r\n </th>\r\n )\r\n })}\r\n </tr>\r\n </thead>\r\n <tbody>\r\n {isLoading ? (\r\n <tr>\r\n <td colSpan={nColunas} className=\"py-16 text-center text-sm font-bold uppercase tracking-widest text-slate-400\">\r\n <div role=\"status\" aria-live=\"polite\" className=\"inline-flex items-center gap-2\">\r\n <Spinner size={16} className=\"text-proef-blue dark:text-proef-turquoise\" />\r\n <span>{L.loading}</span>\r\n </div>\r\n </td>\r\n </tr>\r\n ) : aEsperarDataset ? (\r\n <tr>\r\n <td colSpan={nColunas} className=\"py-16 text-center text-sm font-bold uppercase tracking-widest text-slate-400\">\r\n {L.loading}\r\n </td>\r\n </tr>\r\n ) : isGrouped ? (\r\n grupos.length === 0 ? (\r\n <tr>\r\n <td colSpan={nColunas} className=\"py-16 text-center text-sm text-slate-400\">\r\n {L.empty}\r\n </td>\r\n </tr>\r\n ) : (\r\n renderGrupos(grupos, 0)\r\n )\r\n ) : sorted.length === 0 ? (\r\n <tr>\r\n <td colSpan={nColunas} className=\"py-16 text-center\">\r\n {emptyIcon && <div className=\"flex justify-center mb-2\">{emptyIcon}</div>}\r\n <p className=\"text-sm text-slate-400\">{L.empty}</p>\r\n {emptySubLabel && <p className=\"text-xs text-slate-400 mt-1\">{emptySubLabel}</p>}\r\n </td>\r\n </tr>\r\n ) : (\r\n pageData.map((row, i) => renderLinha(row, i))\r\n )}\r\n </tbody>\r\n {totalRow && !isLoading && !aEsperarDataset && sorted.length > 0 && (\r\n <tfoot className=\"sticky bottom-0 z-[2]\">\r\n <tr className=\"bg-slate-100 dark:bg-slate-800 font-black text-slate-800 dark:text-slate-100\">\r\n <td colSpan={colunasRotuloGrupo} className=\"px-3 py-2 border-t-2 border-slate-200 dark:border-slate-700 text-[10px] uppercase tracking-widest\">\r\n {L.total}\r\n </td>\r\n {colunasComValor.map((c) => (\r\n <td key={c.key} className={`px-3 py-2 border-t-2 border-slate-200 dark:border-slate-700 whitespace-nowrap text-xs ${alignClass(c.align)}`}>\r\n {c.groupAggregate?.(sorted)}\r\n </td>\r\n ))}\r\n </tr>\r\n </tfoot>\r\n )}\r\n </table>\r\n </div>\r\n\r\n {/* A mesma Pagination que as apps usam quando paginam no servidor. */}\r\n {!isLoading && paginando && (totalPages > 1 || (pageSizeOptions?.length ?? 0) > 0) && (\r\n <Pagination\r\n className=\"mt-2.5\"\r\n page={currentPage}\r\n pages={totalPages}\r\n onPageChange={(p) => setPage(Math.min(totalPages, Math.max(1, p)))}\r\n pageSize={pageSizeAtual}\r\n pageSizeOptions={pageSizeOptions}\r\n onPageSizeChange={escolherPageSize}\r\n labels={{ page: L.page, perPage: L.perPage, prev: L.prev, next: L.next }}\r\n />\r\n )}\r\n </div>\r\n )\r\n}\r\n"],"names":["v"],"mappings":"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"}