@nextelco/common-ui 2.1.104 → 2.1.106

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 (61) hide show
  1. package/dist/esm/assets/imagem-6ee08a37d9.png +0 -0
  2. package/dist/esm/assets/imagem-6fcae1b87b.png +0 -0
  3. package/dist/esm/index.js +142 -119
  4. package/dist/esm/index.js.map +1 -1
  5. package/dist/esm/legacy/components/layout/Header/Header.css +1 -1
  6. package/dist/esm/legacy/components/molecules/Sidebar/Sidebar.css +1 -1
  7. package/dist/esm/legacy/components/organisms/RoleGuard/RoleGuard.js +14 -3
  8. package/dist/esm/legacy/components/organisms/RoleGuard/RoleGuard.js.map +1 -1
  9. package/dist/esm/legacy/pages/Error/ErrorPage.css +1 -1
  10. package/dist/esm/legacy/pages/Loading/Loading.css +2 -2
  11. package/dist/esm/legacy/pages/NotFound/NotFound.css +1 -1
  12. package/dist/esm/v1/components/forms/FormField/FormField.js.map +1 -1
  13. package/dist/esm/v1/components/forms/FormGrid/FormGrid.js.map +1 -1
  14. package/dist/esm/v1/components/lists/ListFinder/ListFinder.js +214 -0
  15. package/dist/esm/v1/components/lists/ListFinder/ListFinder.js.map +1 -0
  16. package/dist/esm/v1/components/lists/ListFinder/labels.js +18 -0
  17. package/dist/esm/v1/components/lists/ListFinder/labels.js.map +1 -0
  18. package/dist/esm/v1/components/overlays/Dialog/Dialog.js.map +1 -1
  19. package/dist/esm/v1/components/tables/PaginatedTable/PaginatedTable.js +50 -0
  20. package/dist/esm/v1/components/tables/PaginatedTable/PaginatedTable.js.map +1 -0
  21. package/dist/esm/v1/components/tables/Table/ColumnFilter.js +292 -0
  22. package/dist/esm/v1/components/tables/Table/ColumnFilter.js.map +1 -0
  23. package/dist/esm/v1/components/tables/Table/OverlayScrollbar.js +129 -0
  24. package/dist/esm/v1/components/tables/Table/OverlayScrollbar.js.map +1 -0
  25. package/dist/esm/v1/components/tables/Table/Table.js +1410 -0
  26. package/dist/esm/v1/components/tables/Table/Table.js.map +1 -0
  27. package/dist/esm/v1/components/tables/Table/labels.js +44 -0
  28. package/dist/esm/v1/components/tables/Table/labels.js.map +1 -0
  29. package/dist/esm/v1/foundations/icons/icons.js +45 -2
  30. package/dist/esm/v1/foundations/icons/icons.js.map +1 -1
  31. package/dist/esm/v1/primitives/buttons/Button/Button.js.map +1 -1
  32. package/dist/esm/v1/primitives/forms/Checkbox/Checkbox.js +37 -0
  33. package/dist/esm/v1/primitives/forms/Checkbox/Checkbox.js.map +1 -0
  34. package/dist/esm/v1/primitives/forms/LookupInput/LookupInput.js.map +1 -1
  35. package/dist/esm/v1/primitives/forms/SelectInput/SelectInput.js.map +1 -1
  36. package/dist/esm/v1/primitives/forms/TextArea/TextArea.js.map +1 -1
  37. package/dist/esm/v1/primitives/lists/ListItem/ListItem.js +39 -0
  38. package/dist/esm/v1/primitives/lists/ListItem/ListItem.js.map +1 -0
  39. package/dist/style.components.css +1 -1
  40. package/dist/style.css +1 -1
  41. package/dist/style.layered.css +1 -1
  42. package/dist/theme.css +256 -0
  43. package/dist/types/v1/components/forms/FormField/FormField.d.ts +3 -3
  44. package/dist/types/v1/components/forms/FormGrid/FormGrid.d.ts +2 -0
  45. package/dist/types/v1/components/lists/ListFinder/ListFinder.d.ts +60 -0
  46. package/dist/types/v1/components/lists/ListFinder/labels.d.ts +24 -0
  47. package/dist/types/v1/components/overlays/Dialog/Dialog.d.ts +3 -1
  48. package/dist/types/v1/components/tables/PaginatedTable/PaginatedTable.d.ts +41 -0
  49. package/dist/types/v1/components/tables/Table/ColumnFilter.d.ts +48 -0
  50. package/dist/types/v1/components/tables/Table/OverlayScrollbar.d.ts +34 -0
  51. package/dist/types/v1/components/tables/Table/Table.d.ts +250 -0
  52. package/dist/types/v1/components/tables/Table/labels.d.ts +54 -0
  53. package/dist/types/v1/foundations/icons/icons.d.ts +17 -0
  54. package/dist/types/v1/index.d.ts +15 -1
  55. package/dist/types/v1/primitives/buttons/Button/Button.d.ts +2 -0
  56. package/dist/types/v1/primitives/forms/Checkbox/Checkbox.d.ts +25 -0
  57. package/dist/types/v1/primitives/forms/LookupInput/LookupInput.d.ts +3 -0
  58. package/dist/types/v1/primitives/forms/SelectInput/SelectInput.d.ts +3 -0
  59. package/dist/types/v1/primitives/forms/TextArea/TextArea.d.ts +3 -0
  60. package/dist/types/v1/primitives/lists/ListItem/ListItem.d.ts +36 -0
  61. package/package.json +2 -2
@@ -0,0 +1,292 @@
1
+ import { jsxs, jsx, Fragment } from "react/jsx-runtime";
2
+ import { useRef, useId, useState, useMemo, useEffect } from "react";
3
+ import { createPortal } from "react-dom";
4
+ import { SlidersIcon, ListIcon, ChevronUpIcon, ChevronDownIcon, CheckIcon } from "../../../foundations/icons/icons.js";
5
+ const ALTURA = 420;
6
+ const LARGURA = 320;
7
+ function ColumnFilter({
8
+ labels: L,
9
+ label,
10
+ canSort,
11
+ tipo,
12
+ valores,
13
+ seleccionados,
14
+ intervalo,
15
+ numero,
16
+ ordenadaPor,
17
+ aCarregar,
18
+ ancora,
19
+ onOrdenar,
20
+ onAplicar,
21
+ onAplicarDatas,
22
+ onAplicarNumeros,
23
+ onLimpar,
24
+ onFechar
25
+ }) {
26
+ const caixa = useRef(null);
27
+ const idMin = useId();
28
+ const [busca, setBusca] = useState("");
29
+ const [rascunho, setRascunho] = useState(() => new Set(seleccionados));
30
+ const [de, setDe] = useState(intervalo.from ?? "");
31
+ const [ate, setAte] = useState(intervalo.to ?? "");
32
+ const [min, setMin] = useState(numero?.min ?? "");
33
+ const [max, setMax] = useState(numero?.max ?? "");
34
+ const [modoLista, setModoLista] = useState(tipo === "numero" && !numero?.min && !numero?.max);
35
+ const lista = tipo === "texto" || tipo === "numero" && modoLista;
36
+ const visiveis = useMemo(() => {
37
+ const achados = valores.filter((v) => v.toLowerCase().includes(busca.toLowerCase()));
38
+ if (tipo !== "numero") return achados;
39
+ return [...achados].sort((a, b) => {
40
+ const na = Number(a), nb = Number(b);
41
+ const va = a !== "—" && Number.isFinite(na), vb = b !== "—" && Number.isFinite(nb);
42
+ if (va && vb) return na - nb;
43
+ return va ? -1 : vb ? 1 : a.localeCompare(b);
44
+ });
45
+ }, [valores, busca, tipo]);
46
+ const todosVisiveis = visiveis.length > 0 && visiveis.every((v) => rascunho.has(v));
47
+ const algunsVisiveis = visiveis.some((v) => rascunho.has(v)) && !todosVisiveis;
48
+ const alternarTodos = () => setRascunho((prev) => {
49
+ const n = new Set(prev);
50
+ if (todosVisiveis) for (const v of visiveis) n.delete(v);
51
+ else for (const v of visiveis) n.add(v);
52
+ return n;
53
+ });
54
+ const alternar = (v) => setRascunho((prev) => {
55
+ const n = new Set(prev);
56
+ if (n.has(v)) n.delete(v);
57
+ else n.add(v);
58
+ return n;
59
+ });
60
+ const mesmoConjunto = (a, b) => a.size === b.size && [...a].every((v) => b.has(v));
61
+ const sujo = tipo === "data" ? de !== (intervalo.from ?? "") || ate !== (intervalo.to ?? "") : tipo === "numero" && !modoLista ? min !== (numero?.min ?? "") || max !== (numero?.max ?? "") : !mesmoConjunto(rascunho, seleccionados);
62
+ const temFiltro = seleccionados.size > 0 || !!intervalo.from || !!intervalo.to || !!numero?.min || !!numero?.max;
63
+ useEffect(() => {
64
+ const fora = (e) => {
65
+ if (caixa.current && !caixa.current.contains(e.target)) onFechar();
66
+ };
67
+ const tecla = (e) => {
68
+ if (e.key === "Escape") onFechar();
69
+ };
70
+ const aoRolar = (e) => {
71
+ if (caixa.current && e.target instanceof Node && caixa.current.contains(e.target)) return;
72
+ onFechar();
73
+ };
74
+ document.addEventListener("mousedown", fora);
75
+ document.addEventListener("keydown", tecla);
76
+ window.addEventListener("scroll", aoRolar, true);
77
+ window.addEventListener("resize", onFechar);
78
+ return () => {
79
+ document.removeEventListener("mousedown", fora);
80
+ document.removeEventListener("keydown", tecla);
81
+ window.removeEventListener("scroll", aoRolar, true);
82
+ window.removeEventListener("resize", onFechar);
83
+ };
84
+ }, [onFechar]);
85
+ const botaoModo = tipo === "numero" && /* @__PURE__ */ jsxs(
86
+ "button",
87
+ {
88
+ type: "button",
89
+ onClick: (e) => {
90
+ e.preventDefault();
91
+ setModoLista((m) => !m);
92
+ },
93
+ title: modoLista ? L.showRange : L.showList,
94
+ className: "inline-flex items-center gap-1 text-[9px] font-black uppercase tracking-widest text-slate-400 hover:text-proef-blue flex-shrink-0 whitespace-nowrap",
95
+ children: [
96
+ modoLista ? /* @__PURE__ */ jsx(SlidersIcon, { size: 10 }) : /* @__PURE__ */ jsx(ListIcon, { size: 10 }),
97
+ modoLista ? L.showRange : L.showList
98
+ ]
99
+ }
100
+ );
101
+ const paraCima = window.innerHeight - ancora.bottom < ALTURA && ancora.top > ALTURA;
102
+ return createPortal(
103
+ /* @__PURE__ */ jsxs(
104
+ "div",
105
+ {
106
+ ref: caixa,
107
+ style: {
108
+ position: "fixed",
109
+ left: Math.max(4, Math.min(ancora.left, window.innerWidth - LARGURA - 4)),
110
+ ...paraCima ? { bottom: window.innerHeight - ancora.top + 2 } : { top: ancora.bottom + 2 },
111
+ width: LARGURA,
112
+ maxHeight: ALTURA
113
+ },
114
+ className: "v1-reset z-[250] flex flex-col bg-white border border-slate-200 shadow-lg",
115
+ children: [
116
+ label && /* @__PURE__ */ jsx("div", { className: "px-3 pt-2.5 pb-2 border-b border-slate-100 flex-shrink-0", children: /* @__PURE__ */ jsx("span", { className: "text-[9px] font-black uppercase tracking-widest text-proef-blue", children: label }) }),
117
+ aCarregar && /* @__PURE__ */ jsxs("div", { className: "px-3 py-1.5 bg-amber-50 border-b border-amber-100 flex items-center gap-1.5 text-micro text-amber-700 flex-shrink-0", children: [
118
+ /* @__PURE__ */ jsx("span", { "aria-hidden": true, className: "inline-block h-2.5 w-2.5 animate-spin rounded-full border-2 border-current border-t-transparent " }),
119
+ L.loadingOptions
120
+ ] }),
121
+ canSort && onOrdenar && /* @__PURE__ */ jsxs("div", { className: "border-b border-slate-100 p-1.5 space-y-0.5 flex-shrink-0", children: [
122
+ /* @__PURE__ */ jsxs(
123
+ "button",
124
+ {
125
+ onClick: () => onOrdenar("asc"),
126
+ className: `w-full flex items-center gap-2 px-2.5 py-1.5 text-caption font-semibold text-left hover:bg-slate-50 transition-colors ${ordenadaPor === "asc" ? "text-proef-blue bg-slate-50" : "text-slate-600"}`,
127
+ children: [
128
+ /* @__PURE__ */ jsx(ChevronUpIcon, { size: 11 }),
129
+ " ",
130
+ tipo === "data" ? L.sortDateAsc : tipo === "numero" ? L.sortNumAsc : L.sortAsc
131
+ ]
132
+ }
133
+ ),
134
+ /* @__PURE__ */ jsxs(
135
+ "button",
136
+ {
137
+ onClick: () => onOrdenar("desc"),
138
+ className: `w-full flex items-center gap-2 px-2.5 py-1.5 text-caption font-semibold text-left hover:bg-slate-50 transition-colors ${ordenadaPor === "desc" ? "text-proef-blue bg-slate-50" : "text-slate-600"}`,
139
+ children: [
140
+ /* @__PURE__ */ jsx(ChevronDownIcon, { size: 11 }),
141
+ " ",
142
+ tipo === "data" ? L.sortDateDesc : tipo === "numero" ? L.sortNumDesc : L.sortDesc
143
+ ]
144
+ }
145
+ )
146
+ ] }),
147
+ tipo === "data" ? /* @__PURE__ */ jsxs("div", { className: "p-3 space-y-2", children: [
148
+ /* @__PURE__ */ jsxs("label", { className: "block", children: [
149
+ /* @__PURE__ */ jsx("span", { className: "text-[9px] font-black uppercase tracking-widest text-slate-400", children: L.dateFrom }),
150
+ /* @__PURE__ */ jsx(
151
+ "input",
152
+ {
153
+ type: "date",
154
+ value: de,
155
+ onChange: (e) => setDe(e.target.value),
156
+ onKeyDown: (e) => {
157
+ if (e.key === "Enter") onAplicarDatas?.(de, ate);
158
+ },
159
+ className: "w-full mt-0.5 bg-slate-50 border border-slate-100 px-2.5 py-1.5 text-xs outline-none focus:border-proef-blue"
160
+ }
161
+ )
162
+ ] }),
163
+ /* @__PURE__ */ jsxs("label", { className: "block", children: [
164
+ /* @__PURE__ */ jsx("span", { className: "text-[9px] font-black uppercase tracking-widest text-slate-400", children: L.dateTo }),
165
+ /* @__PURE__ */ jsx(
166
+ "input",
167
+ {
168
+ type: "date",
169
+ value: ate,
170
+ onChange: (e) => setAte(e.target.value),
171
+ onKeyDown: (e) => {
172
+ if (e.key === "Enter") onAplicarDatas?.(de, ate);
173
+ },
174
+ className: "w-full mt-0.5 bg-slate-50 border border-slate-100 px-2.5 py-1.5 text-xs outline-none focus:border-proef-blue"
175
+ }
176
+ )
177
+ ] })
178
+ ] }) : !lista ? (
179
+ /* Mín/Máx em vez da lista: numa coluna de dinheiro ou de contagem há
180
+ tantos valores distintos quantas as linhas, e ninguém procura "10.04".
181
+ Vazio de um dos lados vale "sem limite desse lado". */
182
+ /* @__PURE__ */ jsxs("div", { className: "p-3 space-y-2", children: [
183
+ /* @__PURE__ */ jsxs("div", { children: [
184
+ /* @__PURE__ */ jsxs("span", { className: "flex items-center justify-between", children: [
185
+ /* @__PURE__ */ jsx("label", { htmlFor: idMin, className: "text-[9px] font-black uppercase tracking-widest text-slate-400", children: L.min }),
186
+ botaoModo
187
+ ] }),
188
+ /* @__PURE__ */ jsx(
189
+ "input",
190
+ {
191
+ id: idMin,
192
+ type: "number",
193
+ value: min,
194
+ onChange: (e) => setMin(e.target.value),
195
+ onKeyDown: (e) => {
196
+ if (e.key === "Enter") onAplicarNumeros?.(min, max);
197
+ },
198
+ className: "w-full mt-0.5 bg-slate-50 border border-slate-100 px-2.5 py-1.5 text-xs outline-none focus:border-proef-blue"
199
+ }
200
+ )
201
+ ] }),
202
+ /* @__PURE__ */ jsxs("label", { className: "block", children: [
203
+ /* @__PURE__ */ jsx("span", { className: "text-[9px] font-black uppercase tracking-widest text-slate-400", children: L.max }),
204
+ /* @__PURE__ */ jsx(
205
+ "input",
206
+ {
207
+ type: "number",
208
+ value: max,
209
+ onChange: (e) => setMax(e.target.value),
210
+ onKeyDown: (e) => {
211
+ if (e.key === "Enter") onAplicarNumeros?.(min, max);
212
+ },
213
+ className: "w-full mt-0.5 bg-slate-50 border border-slate-100 px-2.5 py-1.5 text-xs outline-none focus:border-proef-blue"
214
+ }
215
+ )
216
+ ] })
217
+ ] })
218
+ ) : /* @__PURE__ */ jsxs(Fragment, { children: [
219
+ /* @__PURE__ */ jsxs("div", { className: "p-1.5 flex-shrink-0 flex items-center gap-2", children: [
220
+ /* @__PURE__ */ jsx(
221
+ "input",
222
+ {
223
+ autoFocus: true,
224
+ type: "text",
225
+ value: busca,
226
+ onChange: (e) => setBusca(e.target.value),
227
+ placeholder: L.search,
228
+ className: "flex-1 min-w-0 bg-slate-50 border border-slate-100 px-2.5 py-1.5 text-xs outline-none focus:border-proef-blue"
229
+ }
230
+ ),
231
+ botaoModo && /* @__PURE__ */ jsx("span", { className: "pr-1.5", children: botaoModo })
232
+ ] }),
233
+ visiveis.length > 0 && /* @__PURE__ */ jsxs(
234
+ "button",
235
+ {
236
+ onClick: alternarTodos,
237
+ className: "flex items-center gap-2 px-3 py-1.5 border-y border-slate-100 hover:bg-slate-50 text-left flex-shrink-0",
238
+ children: [
239
+ /* @__PURE__ */ jsx("div", { className: `w-3.5 h-3.5 flex items-center justify-center border flex-shrink-0 transition-colors ${todosVisiveis ? "bg-primary border-primary" : algunsVisiveis ? "bg-primary/30 border-primary" : "border-slate-300"}`, children: todosVisiveis && /* @__PURE__ */ jsx(CheckIcon, { size: 9, strokeWidth: 3, className: "text-white" }) }),
240
+ /* @__PURE__ */ jsx("span", { className: "text-caption font-semibold text-slate-600", children: busca ? L.selectAllFound(visiveis.length) : L.selectAll })
241
+ ]
242
+ }
243
+ ),
244
+ /* @__PURE__ */ jsx("div", { className: "flex-1 overflow-y-auto p-1.5 space-y-0.5 min-h-0", children: visiveis.length === 0 ? /* @__PURE__ */ jsx("p", { className: "text-caption text-slate-400 text-center py-3", children: L.noResults }) : visiveis.map((v) => {
245
+ const escolhido = rascunho.has(v);
246
+ return /* @__PURE__ */ jsxs("button", { onClick: () => alternar(v), className: "w-full flex items-center gap-2 px-2 py-1.5 hover:bg-slate-50 text-left", children: [
247
+ /* @__PURE__ */ jsx("div", { className: `w-3.5 h-3.5 flex items-center justify-center border flex-shrink-0 transition-colors ${escolhido ? "bg-primary border-primary" : "border-slate-300"}`, children: escolhido && /* @__PURE__ */ jsx(CheckIcon, { size: 9, strokeWidth: 3, className: "text-white" }) }),
248
+ /* @__PURE__ */ jsx("span", { className: "text-xs text-slate-700 whitespace-normal break-words leading-snug", children: v })
249
+ ] }, v);
250
+ }) })
251
+ ] }),
252
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5 p-1.5 border-t border-slate-100 flex-shrink-0 bg-white", children: [
253
+ /* @__PURE__ */ jsx(
254
+ "button",
255
+ {
256
+ onClick: () => {
257
+ if (tipo === "data") onAplicarDatas?.(de, ate);
258
+ else if (tipo === "numero" && !modoLista) onAplicarNumeros?.(min, max);
259
+ else onAplicar(rascunho);
260
+ },
261
+ disabled: !sujo,
262
+ className: "flex-1 text-micro font-black uppercase tracking-wider text-white bg-primary hover:bg-primary/90 py-1.5 disabled:opacity-30",
263
+ children: L.apply
264
+ }
265
+ ),
266
+ /* @__PURE__ */ jsx(
267
+ "button",
268
+ {
269
+ onClick: onFechar,
270
+ className: "text-micro font-black uppercase tracking-wider text-slate-500 hover:text-slate-700 px-2 py-1.5",
271
+ children: L.cancel
272
+ }
273
+ ),
274
+ temFiltro && /* @__PURE__ */ jsx(
275
+ "button",
276
+ {
277
+ onClick: onLimpar,
278
+ className: "text-micro font-black uppercase tracking-wider text-red-400 hover:text-red-500 px-2 py-1.5",
279
+ children: L.clearFilter
280
+ }
281
+ )
282
+ ] })
283
+ ]
284
+ }
285
+ ),
286
+ document.body
287
+ );
288
+ }
289
+ export {
290
+ ColumnFilter
291
+ };
292
+ //# sourceMappingURL=ColumnFilter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ColumnFilter.js","sources":["../../../../../../src/v1/components/tables/Table/ColumnFilter.tsx"],"sourcesContent":["import React, { useEffect, useId, useMemo, useRef, useState } from 'react'\r\nimport { createPortal } from 'react-dom'\r\nimport { CheckIcon, ChevronDownIcon, ChevronUpIcon, ListIcon, SlidersIcon } from '../../../foundations/icons/icons'\r\nimport type { TableLabels } from './labels'\r\n\r\n/** Onde o popup abre e quanto pode crescer. */\r\nconst ALTURA = 420\r\nconst LARGURA = 320\r\n\r\n/**\r\n * O filtro de uma coluna: ordenar, procurar, escolher valores, aplicar.\r\n *\r\n * Vive fora do `Table` de propósito. Lá dentro era uma função declarada no\r\n * corpo do componente, logo uma função NOVA a cada render — e o React\r\n * desmontava e remontava o popup a cada tecla escrita e a cada opção marcada.\r\n * Era isso que devolvia a lista de valores ao topo a meio de uma escolha.\r\n *\r\n * Nada se aplica até ao \"Aplicar\". Com a aplicação imediata, cada visto refazia\r\n * a tabela por baixo: mudava-lhe o tamanho e o conteúdo enquanto se escolhia, e\r\n * a lista de onde se estava a escolher encolhia à medida que se escolhia.\r\n *\r\n * `position: fixed` no `<body>`: como filho da célula, o `overflow` da tabela\r\n * cortava-o — e era por isso que a caixa da tabela tinha de perder o tecto de\r\n * altura enquanto um filtro estivesse aberto, o que empurrava a página inteira.\r\n */\r\nexport function ColumnFilter({\r\n labels: L, label, canSort, tipo, valores, seleccionados, intervalo, numero, ordenadaPor, aCarregar, ancora,\r\n onOrdenar, onAplicar, onAplicarDatas, onAplicarNumeros, onLimpar, onFechar,\r\n}: {\r\n labels: TableLabels\r\n label?: string\r\n canSort?: boolean\r\n /** `texto` = lista de valores; `data` = De/Até; `numero` = Mín/Máx, ou a lista (botão no topo). */\r\n tipo: 'texto' | 'data' | 'numero'\r\n /** Valores distintos da coluna, já contados com os outros filtros activos. */\r\n valores: string[]\r\n /** O que está aplicado neste momento — o ponto de partida do rascunho. */\r\n seleccionados: Set<string>\r\n intervalo: { from?: string; to?: string }\r\n /** Intervalo numérico aplicado, quando `tipo` é `numero`. */\r\n numero?: { min?: string; max?: string }\r\n ordenadaPor: 'asc' | 'desc' | null\r\n aCarregar: boolean\r\n /** Rect do botão que abriu isto. */\r\n ancora: DOMRect\r\n onOrdenar?: (dir: 'asc' | 'desc') => void\r\n onAplicar: (vals: Set<string>) => void\r\n onAplicarDatas?: (de: string, ate: string) => void\r\n onAplicarNumeros?: (min: string, max: string) => void\r\n onLimpar: () => void\r\n onFechar: () => void\r\n}) {\r\n const caixa = useRef<HTMLDivElement>(null)\r\n const idMin = useId()\r\n const [busca, setBusca] = useState('')\r\n /** O que está escolhido AGORA, sem ter saído daqui. Só o \"Aplicar\" o leva lá fora. */\r\n const [rascunho, setRascunho] = useState<Set<string>>(() => new Set(seleccionados))\r\n const [de, setDe] = useState(intervalo.from ?? '')\r\n const [ate, setAte] = useState(intervalo.to ?? '')\r\n const [min, setMin] = useState(numero?.min ?? '')\r\n const [max, setMax] = useState(numero?.max ?? '')\r\n /* Numa coluna de números, Mín/Máx ou a lista de valores. Abre na lista, salvo\r\n se já houver Mín/Máx aplicado: quem filtrou por intervalo e volta a abrir\r\n quer ver o intervalo. */\r\n const [modoLista, setModoLista] = useState(tipo === 'numero' && !numero?.min && !numero?.max)\r\n const lista = tipo === 'texto' || (tipo === 'numero' && modoLista)\r\n\r\n const visiveis = useMemo(() => {\r\n const achados = valores.filter(v => v.toLowerCase().includes(busca.toLowerCase()))\r\n // Por valor e não por texto: \"10\" antes de \"2\" numa lista de números é ruído.\r\n if (tipo !== 'numero') return achados\r\n return [...achados].sort((a, b) => {\r\n const na = Number(a), nb = Number(b)\r\n const va = a !== '—' && Number.isFinite(na), vb = b !== '—' && Number.isFinite(nb)\r\n if (va && vb) return na - nb\r\n return va ? -1 : vb ? 1 : a.localeCompare(b)\r\n })\r\n }, [valores, busca, tipo])\r\n const todosVisiveis = visiveis.length > 0 && visiveis.every(v => rascunho.has(v))\r\n const algunsVisiveis = visiveis.some(v => rascunho.has(v)) && !todosVisiveis\r\n\r\n /**\r\n * Marca ou desmarca os valores À VISTA — os que a pesquisa deixou passar.\r\n *\r\n * Com pesquisa activa, \"tudo\" é o que está no ecrã: escolher de uma vez os\r\n * valores que começam por \"Norte\" é o gesto que isto serve, e mexer nos\r\n * outros quarenta por baixo era o contrário do que se pediu.\r\n */\r\n const alternarTodos = () => setRascunho(prev => {\r\n const n = new Set(prev)\r\n if (todosVisiveis) for (const v of visiveis) n.delete(v)\r\n else for (const v of visiveis) n.add(v)\r\n return n\r\n })\r\n\r\n const alternar = (v: string) => setRascunho(prev => {\r\n const n = new Set(prev)\r\n if (n.has(v)) n.delete(v)\r\n else n.add(v)\r\n return n\r\n })\r\n\r\n const mesmoConjunto = (a: Set<string>, b: Set<string>) =>\r\n a.size === b.size && [...a].every(v => b.has(v))\r\n const sujo = tipo === 'data'\r\n ? de !== (intervalo.from ?? '') || ate !== (intervalo.to ?? '')\r\n : tipo === 'numero' && !modoLista\r\n ? min !== (numero?.min ?? '') || max !== (numero?.max ?? '')\r\n : !mesmoConjunto(rascunho, seleccionados)\r\n const temFiltro = seleccionados.size > 0 || !!intervalo.from || !!intervalo.to || !!numero?.min || !!numero?.max\r\n\r\n useEffect(() => {\r\n const fora = (e: MouseEvent) => {\r\n if (caixa.current && !caixa.current.contains(e.target as Node)) onFechar()\r\n }\r\n const tecla = (e: KeyboardEvent) => { if (e.key === 'Escape') onFechar() }\r\n /*\r\n Rolar FORA fecha; rolar DENTRO da lista não.\r\n A caixa está presa a coordenadas do ecrã, logo tem de fechar quando a\r\n página se move por baixo dela — mas a própria lista rola (podem ser\r\n dezenas de valores), e o `capture` também apanha esse.\r\n */\r\n const aoRolar = (e: Event) => {\r\n if (caixa.current && e.target instanceof Node && caixa.current.contains(e.target)) return\r\n onFechar()\r\n }\r\n document.addEventListener('mousedown', fora)\r\n document.addEventListener('keydown', tecla)\r\n window.addEventListener('scroll', aoRolar, true)\r\n window.addEventListener('resize', onFechar)\r\n return () => {\r\n document.removeEventListener('mousedown', fora)\r\n document.removeEventListener('keydown', tecla)\r\n window.removeEventListener('scroll', aoRolar, true)\r\n window.removeEventListener('resize', onFechar)\r\n }\r\n }, [onFechar])\r\n\r\n /* Troca Mín/Máx ↔ lista. Vai na linha que já existe (a do \"Mínimo\", ou a da\r\n pesquisa) e não numa só para ele: uma linha inteira para um botão pequeno\r\n era espaço vazio à esquerda. */\r\n const botaoModo = tipo === 'numero' && (\r\n <button\r\n type=\"button\"\r\n onClick={e => { e.preventDefault(); setModoLista(m => !m) }}\r\n title={modoLista ? L.showRange : L.showList}\r\n className=\"inline-flex items-center gap-1 text-[9px] font-black uppercase tracking-widest text-slate-400 hover:text-proef-blue flex-shrink-0 whitespace-nowrap\"\r\n >\r\n {modoLista ? <SlidersIcon size={10} /> : <ListIcon size={10} />}\r\n {modoLista ? L.showRange : L.showList}\r\n </button>\r\n )\r\n\r\n // Abre para cima quando não há espaço por baixo — nas colunas da direita e\r\n // com a janela baixa, o que ficava de fora eram os botões de aplicar.\r\n const paraCima = window.innerHeight - ancora.bottom < ALTURA && ancora.top > ALTURA\r\n\r\n return createPortal(\r\n <div\r\n ref={caixa}\r\n style={{\r\n position: 'fixed',\r\n left: Math.max(4, Math.min(ancora.left, window.innerWidth - LARGURA - 4)),\r\n ...(paraCima ? { bottom: window.innerHeight - ancora.top + 2 } : { top: ancora.bottom + 2 }),\r\n width: LARGURA,\r\n maxHeight: ALTURA,\r\n }}\r\n className=\"v1-reset z-[250] flex flex-col bg-white border border-slate-200 shadow-lg\"\r\n >\r\n {label && (\r\n <div className=\"px-3 pt-2.5 pb-2 border-b border-slate-100 flex-shrink-0\">\r\n <span className=\"text-[9px] font-black uppercase tracking-widest text-proef-blue\">{label}</span>\r\n </div>\r\n )}\r\n {aCarregar && (\r\n <div className=\"px-3 py-1.5 bg-amber-50 border-b border-amber-100 flex items-center gap-1.5 text-micro text-amber-700 flex-shrink-0\">\r\n <span aria-hidden className=\"inline-block h-2.5 w-2.5 animate-spin rounded-full border-2 border-current border-t-transparent \" />\r\n {L.loadingOptions}\r\n </div>\r\n )}\r\n {canSort && onOrdenar && (\r\n <div className=\"border-b border-slate-100 p-1.5 space-y-0.5 flex-shrink-0\">\r\n <button\r\n onClick={() => onOrdenar('asc')}\r\n className={`w-full flex items-center gap-2 px-2.5 py-1.5 text-caption font-semibold text-left hover:bg-slate-50 transition-colors ${ordenadaPor === 'asc' ? 'text-proef-blue bg-slate-50' : 'text-slate-600'}`}\r\n >\r\n <ChevronUpIcon size={11} /> {tipo === 'data' ? L.sortDateAsc : tipo === 'numero' ? L.sortNumAsc : L.sortAsc}\r\n </button>\r\n <button\r\n onClick={() => onOrdenar('desc')}\r\n className={`w-full flex items-center gap-2 px-2.5 py-1.5 text-caption font-semibold text-left hover:bg-slate-50 transition-colors ${ordenadaPor === 'desc' ? 'text-proef-blue bg-slate-50' : 'text-slate-600'}`}\r\n >\r\n <ChevronDownIcon size={11} /> {tipo === 'data' ? L.sortDateDesc : tipo === 'numero' ? L.sortNumDesc : L.sortDesc}\r\n </button>\r\n </div>\r\n )}\r\n\r\n {tipo === 'data' ? (\r\n <div className=\"p-3 space-y-2\">\r\n <label className=\"block\">\r\n <span className=\"text-[9px] font-black uppercase tracking-widest text-slate-400\">{L.dateFrom}</span>\r\n <input\r\n type=\"date\" value={de} onChange={e => setDe(e.target.value)}\r\n onKeyDown={e => { if (e.key === 'Enter') onAplicarDatas?.(de, ate) }}\r\n className=\"w-full mt-0.5 bg-slate-50 border border-slate-100 px-2.5 py-1.5 text-xs outline-none focus:border-proef-blue\"\r\n />\r\n </label>\r\n <label className=\"block\">\r\n <span className=\"text-[9px] font-black uppercase tracking-widest text-slate-400\">{L.dateTo}</span>\r\n <input\r\n type=\"date\" value={ate} onChange={e => setAte(e.target.value)}\r\n onKeyDown={e => { if (e.key === 'Enter') onAplicarDatas?.(de, ate) }}\r\n className=\"w-full mt-0.5 bg-slate-50 border border-slate-100 px-2.5 py-1.5 text-xs outline-none focus:border-proef-blue\"\r\n />\r\n </label>\r\n </div>\r\n ) : !lista ? (\r\n /* Mín/Máx em vez da lista: numa coluna de dinheiro ou de contagem há\r\n tantos valores distintos quantas as linhas, e ninguém procura \"10.04\".\r\n Vazio de um dos lados vale \"sem limite desse lado\". */\r\n <div className=\"p-3 space-y-2\">\r\n {/* O botão da lista fica FORA da etiqueta: dentro dela, era o botão\r\n (e não a caixa) que recebia o nome \"Mínimo\", e a caixa ficava sem\r\n nome nenhum para os leitores de ecrã. */}\r\n <div>\r\n <span className=\"flex items-center justify-between\">\r\n <label htmlFor={idMin} className=\"text-[9px] font-black uppercase tracking-widest text-slate-400\">{L.min}</label>\r\n {botaoModo}\r\n </span>\r\n <input\r\n id={idMin}\r\n type=\"number\" value={min} onChange={e => setMin(e.target.value)}\r\n onKeyDown={e => { if (e.key === 'Enter') onAplicarNumeros?.(min, max) }}\r\n className=\"w-full mt-0.5 bg-slate-50 border border-slate-100 px-2.5 py-1.5 text-xs outline-none focus:border-proef-blue\"\r\n />\r\n </div>\r\n <label className=\"block\">\r\n <span className=\"text-[9px] font-black uppercase tracking-widest text-slate-400\">{L.max}</span>\r\n <input\r\n type=\"number\" value={max} onChange={e => setMax(e.target.value)}\r\n onKeyDown={e => { if (e.key === 'Enter') onAplicarNumeros?.(min, max) }}\r\n className=\"w-full mt-0.5 bg-slate-50 border border-slate-100 px-2.5 py-1.5 text-xs outline-none focus:border-proef-blue\"\r\n />\r\n </label>\r\n </div>\r\n ) : (\r\n <>\r\n <div className=\"p-1.5 flex-shrink-0 flex items-center gap-2\">\r\n <input\r\n autoFocus type=\"text\" value={busca} onChange={e => setBusca(e.target.value)} placeholder={L.search}\r\n className=\"flex-1 min-w-0 bg-slate-50 border border-slate-100 px-2.5 py-1.5 text-xs outline-none focus:border-proef-blue\"\r\n />\r\n {botaoModo && <span className=\"pr-1.5\">{botaoModo}</span>}\r\n </div>\r\n {/* \"Tudo\" à cabeça da lista, como no Excel: marcar tudo e tirar o que\r\n não se quer é mais rápido do que marcar quarenta à mão. */}\r\n {visiveis.length > 0 && (\r\n <button\r\n onClick={alternarTodos}\r\n className=\"flex items-center gap-2 px-3 py-1.5 border-y border-slate-100 hover:bg-slate-50 text-left flex-shrink-0\"\r\n >\r\n <div className={`w-3.5 h-3.5 flex items-center justify-center border flex-shrink-0 transition-colors ${\r\n todosVisiveis ? 'bg-primary border-primary'\r\n : algunsVisiveis ? 'bg-primary/30 border-primary'\r\n : 'border-slate-300'\r\n }`}>\r\n {todosVisiveis && <CheckIcon size={9} strokeWidth={3} className=\"text-white\" />}\r\n </div>\r\n <span className=\"text-caption font-semibold text-slate-600\">\r\n {busca ? L.selectAllFound(visiveis.length) : L.selectAll}\r\n </span>\r\n </button>\r\n )}\r\n <div className=\"flex-1 overflow-y-auto p-1.5 space-y-0.5 min-h-0\">\r\n {visiveis.length === 0\r\n ? <p className=\"text-caption text-slate-400 text-center py-3\">{L.noResults}</p>\r\n : visiveis.map(v => {\r\n const escolhido = rascunho.has(v)\r\n return (\r\n <button key={v} onClick={() => alternar(v)} className=\"w-full flex items-center gap-2 px-2 py-1.5 hover:bg-slate-50 text-left\">\r\n <div className={`w-3.5 h-3.5 flex items-center justify-center border flex-shrink-0 transition-colors ${escolhido ? 'bg-primary border-primary' : 'border-slate-300'}`}>\r\n {escolhido && <CheckIcon size={9} strokeWidth={3} className=\"text-white\" />}\r\n </div>\r\n <span className=\"text-xs text-slate-700 whitespace-normal break-words leading-snug\">{v}</span>\r\n </button>\r\n )\r\n })}\r\n </div>\r\n </>\r\n )}\r\n\r\n {/* Aplicar/Cancelar fora da zona que rola, sempre à vista: são eles que\r\n dizem que ainda não aconteceu nada. */}\r\n <div className=\"flex items-center gap-1.5 p-1.5 border-t border-slate-100 flex-shrink-0 bg-white\">\r\n <button\r\n onClick={() => {\r\n if (tipo === 'data') onAplicarDatas?.(de, ate)\r\n else if (tipo === 'numero' && !modoLista) onAplicarNumeros?.(min, max)\r\n else onAplicar(rascunho)\r\n }}\r\n disabled={!sujo}\r\n className=\"flex-1 text-micro font-black uppercase tracking-wider text-white bg-primary hover:bg-primary/90 py-1.5 disabled:opacity-30\"\r\n >\r\n {L.apply}\r\n </button>\r\n <button\r\n onClick={onFechar}\r\n className=\"text-micro font-black uppercase tracking-wider text-slate-500 hover:text-slate-700 px-2 py-1.5\"\r\n >\r\n {L.cancel}\r\n </button>\r\n {temFiltro && (\r\n <button\r\n onClick={onLimpar}\r\n className=\"text-micro font-black uppercase tracking-wider text-red-400 hover:text-red-500 px-2 py-1.5\"\r\n >\r\n {L.clearFilter}\r\n </button>\r\n )}\r\n </div>\r\n </div>,\r\n document.body,\r\n )\r\n}\r\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,129 @@
1
+ import { jsx } from "react/jsx-runtime";
2
+ import { useState, useRef, useEffect } from "react";
3
+ const MS_BARRA_OCIOSA = 2e3;
4
+ const CANTO = 12;
5
+ function metricaBarra(alvo, eixo, canto) {
6
+ const vertical = eixo === "vertical";
7
+ const inicio = vertical ? alvo.querySelector("[data-barra-cabecalho]")?.offsetHeight ?? 0 : 0;
8
+ const visivel = vertical ? alvo.clientHeight : alvo.clientWidth;
9
+ const total = vertical ? alvo.scrollHeight : alvo.scrollWidth;
10
+ const outra = canto && (vertical ? alvo.scrollWidth - alvo.clientWidth > 1 : alvo.scrollHeight - alvo.clientHeight > 1);
11
+ const pista = Math.max(0, visivel - inicio - (outra ? CANTO : 0));
12
+ const rolavel = total - visivel;
13
+ const tamanho = Math.min(pista, Math.max(24, visivel / total * pista));
14
+ return { inicio, pista, rolavel, tamanho, percurso: pista - tamanho };
15
+ }
16
+ function OverlayScrollbar({ alvo, eixo = "vertical", borda = 1, canto = false, naBorda = false }) {
17
+ const vertical = eixo === "vertical";
18
+ const [cursor, setCursor] = useState(null);
19
+ const [visivel, setVisivel] = useState(false);
20
+ const visivelRef = useRef(false);
21
+ const posicao = useRef(0);
22
+ const elemento = useRef(null);
23
+ const arrasto = useRef(null);
24
+ const ocioso = useRef(void 0);
25
+ const mostrar = (v) => {
26
+ if (visivelRef.current === v) return;
27
+ visivelRef.current = v;
28
+ setVisivel(v);
29
+ };
30
+ const translacao = (pos) => vertical ? `translateY(${pos}px)` : `translateX(${pos}px)`;
31
+ useEffect(() => {
32
+ if (!alvo) return;
33
+ const medir = () => {
34
+ const g = metricaBarra(alvo, eixo, canto);
35
+ const scroll = vertical ? alvo.scrollTop : alvo.scrollLeft;
36
+ if (g.rolavel <= 1) {
37
+ setCursor(null);
38
+ return;
39
+ }
40
+ posicao.current = g.inicio + (g.percurso > 0 ? scroll / g.rolavel * g.percurso : 0);
41
+ if (elemento.current) elemento.current.style.transform = translacao(posicao.current);
42
+ setCursor((c) => c && Math.abs(c.tamanho - g.tamanho) < 0.5 ? c : { tamanho: g.tamanho });
43
+ };
44
+ const acordar = () => {
45
+ mostrar(true);
46
+ clearTimeout(ocioso.current);
47
+ ocioso.current = setTimeout(() => {
48
+ if (!arrasto.current) mostrar(false);
49
+ }, MS_BARRA_OCIOSA);
50
+ };
51
+ let frame = 0;
52
+ const aoScroll = () => {
53
+ if (frame) return;
54
+ frame = requestAnimationFrame(() => {
55
+ frame = 0;
56
+ medir();
57
+ acordar();
58
+ });
59
+ };
60
+ medir();
61
+ acordar();
62
+ alvo.addEventListener("scroll", aoScroll, { passive: true });
63
+ alvo.addEventListener("pointermove", acordar, { passive: true });
64
+ const ro = new ResizeObserver(() => {
65
+ medir();
66
+ acordar();
67
+ });
68
+ const observarFilhos = () => {
69
+ ro.disconnect();
70
+ ro.observe(alvo);
71
+ for (const filho of Array.from(alvo.children)) ro.observe(filho);
72
+ };
73
+ observarFilhos();
74
+ const mo = new MutationObserver(() => {
75
+ observarFilhos();
76
+ medir();
77
+ });
78
+ mo.observe(alvo, { childList: true });
79
+ return () => {
80
+ cancelAnimationFrame(frame);
81
+ clearTimeout(ocioso.current);
82
+ alvo.removeEventListener("scroll", aoScroll);
83
+ alvo.removeEventListener("pointermove", acordar);
84
+ ro.disconnect();
85
+ mo.disconnect();
86
+ };
87
+ }, [alvo, eixo, vertical, canto]);
88
+ useEffect(() => {
89
+ if (!alvo) return;
90
+ const mover = (e) => {
91
+ const a = arrasto.current;
92
+ if (!a) return;
93
+ const g = metricaBarra(alvo, eixo, canto);
94
+ if (g.percurso <= 0) return;
95
+ const novo = a.scroll0 + ((vertical ? e.clientY : e.clientX) - a.p0) / g.percurso * g.rolavel;
96
+ if (vertical) alvo.scrollTop = novo;
97
+ else alvo.scrollLeft = novo;
98
+ };
99
+ const largar = () => {
100
+ if (!arrasto.current) return;
101
+ arrasto.current = null;
102
+ clearTimeout(ocioso.current);
103
+ ocioso.current = setTimeout(() => mostrar(false), MS_BARRA_OCIOSA);
104
+ };
105
+ window.addEventListener("pointermove", mover);
106
+ window.addEventListener("pointerup", largar);
107
+ return () => {
108
+ window.removeEventListener("pointermove", mover);
109
+ window.removeEventListener("pointerup", largar);
110
+ };
111
+ }, [alvo, eixo, vertical, canto]);
112
+ if (!alvo || !cursor) return null;
113
+ return /* @__PURE__ */ jsx(
114
+ "div",
115
+ {
116
+ ref: elemento,
117
+ className: `absolute ${vertical ? "right-[3px] w-1.5" : `${naBorda ? "-bottom-[3px]" : "bottom-[3px]"} h-1.5`} rounded-full bg-slate-400/70 hover:bg-slate-500/80 z-20 transition-opacity duration-200 ${visivel ? "opacity-100" : "opacity-0 pointer-events-none"}`,
118
+ style: vertical ? { top: borda, height: cursor.tamanho, transform: translacao(posicao.current) } : { left: borda, width: cursor.tamanho, transform: translacao(posicao.current) },
119
+ onPointerDown: (e) => {
120
+ e.preventDefault();
121
+ arrasto.current = { p0: vertical ? e.clientY : e.clientX, scroll0: vertical ? alvo.scrollTop : alvo.scrollLeft };
122
+ }
123
+ }
124
+ );
125
+ }
126
+ export {
127
+ OverlayScrollbar
128
+ };
129
+ //# sourceMappingURL=OverlayScrollbar.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"OverlayScrollbar.js","sources":["../../../../../../src/v1/components/tables/Table/OverlayScrollbar.tsx"],"sourcesContent":["import React, { useEffect, useRef, useState } from 'react'\r\n\r\n/** Ao fim de quanto tempo sem movimento a barra de scroll se esconde. */\r\nconst MS_BARRA_OCIOSA = 2000\r\n\r\n/** Espessura da barra (w-1.5 / h-1.5) mais a folga à borda — o canto que a outra deixa livre. */\r\nconst CANTO = 12\r\n\r\nexport type Eixo = 'vertical' | 'horizontal'\r\n\r\n/**\r\n * Geometria do cursor da barra de scroll. Uma só conta, partilhada por quem a\r\n * desenha e por quem a arrasta — separadas, arrastar acabava a meio da pista.\r\n *\r\n * Na vertical, o percurso começa ABAIXO do cabeçalho colante (o filho com\r\n * `data-barra-cabecalho`): aquele bloco nunca sai da vista, logo a altura que\r\n * ocupa não representa conteúdo nenhum por onde se possa rolar — e uma barra a\r\n * passar-lhe por cima dizia que sim.\r\n *\r\n * Com as duas barras sobrepostas à vista (`canto`), cada uma acaba antes do\r\n * canto: senão cruzavam-se no fundo à direita. Com a horizontal do browser\r\n * não é preciso — essa já tira a sua altura à caixa.\r\n */\r\nfunction metricaBarra(alvo: HTMLElement, eixo: Eixo, canto: boolean) {\r\n const vertical = eixo === 'vertical'\r\n const inicio = vertical ? alvo.querySelector<HTMLElement>('[data-barra-cabecalho]')?.offsetHeight ?? 0 : 0\r\n const visivel = vertical ? alvo.clientHeight : alvo.clientWidth\r\n const total = vertical ? alvo.scrollHeight : alvo.scrollWidth\r\n const outra = canto && (vertical ? alvo.scrollWidth - alvo.clientWidth > 1 : alvo.scrollHeight - alvo.clientHeight > 1)\r\n const pista = Math.max(0, visivel - inicio - (outra ? CANTO : 0))\r\n const rolavel = total - visivel\r\n const tamanho = Math.min(pista, Math.max(24, (visivel / total) * pista))\r\n return { inicio, pista, rolavel, tamanho, percurso: pista - tamanho }\r\n}\r\n\r\n/**\r\n * Barra de scroll desenhada POR CIMA do conteúdo (a da Table com\r\n * `overlayScrollbars`, nos dois eixos).\r\n *\r\n * A barra do browser ocupa espaço: numa grelha de colunas `1fr` nascia ao\r\n * expandir uma secção e a grelha inteira reflowia de lado; na tabela obrigava\r\n * o cabeçalho a reservar-lhe uma goteira, e a horizontal punha uma faixa\r\n * cinzenta por baixo das linhas. Guardar-lhe o espaço de véspera\r\n * (scrollbar-gutter) deixava uma faixa vazia. Por cima não ocupa nada, e não\r\n * há nem salto nem faixa.\r\n *\r\n * A caixa que rola tem de estar num pai `relative`, e a barra do browser tem\r\n * de estar escondida (classe `scrollbar-none`). Onde o browser não a souber\r\n * esconder, fica também a dele — pior, mas não partido.\r\n *\r\n * Cinzento médio de propósito: passa por cima de linhas escuras e de linhas\r\n * brancas, e tem de se ver nas duas.\r\n */\r\nexport function OverlayScrollbar({ alvo, eixo = 'vertical', borda = 1, canto = false, naBorda = false }: {\r\n alvo: HTMLElement | null\r\n eixo?: Eixo\r\n /** Espessura da borda da caixa do lado onde a pista começa — o cursor começa depois dela. */\r\n borda?: number\r\n /** A outra barra também é sobreposta: deixar-lhe o canto livre. */\r\n canto?: boolean\r\n /**\r\n * Horizontal: centrada na aresta de baixo da caixa em vez de 3px acima dela —\r\n * meio por cima das linhas, meio por cima do que vem a seguir (ex: o rodapé da\r\n * tabela). Tira menos altura visível às linhas.\r\n */\r\n naBorda?: boolean\r\n}) {\r\n const vertical = eixo === 'vertical'\r\n /*\r\n O React só sabe SE há cursor e de que TAMANHO — muda pouco. A POSIÇÃO vai\r\n direta ao elemento (transform), uma vez por frame: a cada evento de scroll\r\n o setState redesenhava o componente e mexia no `top`, o que obrigava o\r\n browser a recalcular estilos e layout a cada passo da roda — medido numa\r\n lista grande, ~2/3 do trabalho do scroll, e com uma tabela grande por trás\r\n cada recálculo ainda custa mais.\r\n */\r\n const [cursor, setCursor] = useState<{ tamanho: number } | null>(null)\r\n const [visivel, setVisivel] = useState(false)\r\n const visivelRef = useRef(false)\r\n const posicao = useRef(0)\r\n const elemento = useRef<HTMLDivElement>(null)\r\n const arrasto = useRef<{ p0: number; scroll0: number } | null>(null)\r\n const ocioso = useRef<ReturnType<typeof setTimeout>>(undefined)\r\n const mostrar = (v: boolean) => {\r\n if (visivelRef.current === v) return\r\n visivelRef.current = v\r\n setVisivel(v)\r\n }\r\n const translacao = (pos: number) => (vertical ? `translateY(${pos}px)` : `translateX(${pos}px)`)\r\n\r\n useEffect(() => {\r\n if (!alvo) return\r\n const medir = () => {\r\n const g = metricaBarra(alvo, eixo, canto)\r\n const scroll = vertical ? alvo.scrollTop : alvo.scrollLeft\r\n if (g.rolavel <= 1) { setCursor(null); return }\r\n posicao.current = g.inicio + (g.percurso > 0 ? (scroll / g.rolavel) * g.percurso : 0)\r\n if (elemento.current) elemento.current.style.transform = translacao(posicao.current)\r\n // Novo estado só quando o tamanho muda mesmo (ou o cursor aparece).\r\n setCursor(c => (c && Math.abs(c.tamanho - g.tamanho) < 0.5 ? c : { tamanho: g.tamanho }))\r\n }\r\n // Aparece com o movimento e esconde-se sozinha ao fim de MS_BARRA_OCIOSA:\r\n // desenhada por cima da grelha, parada em cima dos números é ruído.\r\n // A arrastar nunca se esconde — largar a barra por baixo do rato seria\r\n // perdê-la a meio do gesto.\r\n const acordar = () => {\r\n mostrar(true)\r\n clearTimeout(ocioso.current)\r\n ocioso.current = setTimeout(() => { if (!arrasto.current) mostrar(false) }, MS_BARRA_OCIOSA)\r\n }\r\n // No máximo uma medição por frame, por muitos eventos que a roda mande.\r\n let frame = 0\r\n const aoScroll = () => {\r\n if (frame) return\r\n frame = requestAnimationFrame(() => { frame = 0; medir(); acordar() })\r\n }\r\n medir()\r\n acordar() // depois de expandir vê-se logo que há mais para rolar\r\n alvo.addEventListener('scroll', aoScroll, { passive: true })\r\n alvo.addEventListener('pointermove', acordar, { passive: true })\r\n // O conteúdo cresce e encolhe sozinho (secções a abrir) e, durante a\r\n // animação, a cada frame. Sem observar o próprio conteúdo, o cursor ficava\r\n // do tamanho anterior até ao render seguinte.\r\n // TODOS os filhos, e não só o primeiro: com vários blocos soltos (ex: grupos\r\n // que abrem e fecham), dobrar um que não era o primeiro\r\n // não avisava ninguém e a barra ficava a dizer que havia scroll.\r\n const ro = new ResizeObserver(() => { medir(); acordar() })\r\n const observarFilhos = () => {\r\n ro.disconnect()\r\n ro.observe(alvo)\r\n for (const filho of Array.from(alvo.children)) ro.observe(filho)\r\n }\r\n observarFilhos()\r\n // Filhos que entram ou saem (listas que chegam depois) também contam.\r\n const mo = new MutationObserver(() => { observarFilhos(); medir() })\r\n mo.observe(alvo, { childList: true })\r\n return () => {\r\n cancelAnimationFrame(frame)\r\n clearTimeout(ocioso.current)\r\n alvo.removeEventListener('scroll', aoScroll)\r\n alvo.removeEventListener('pointermove', acordar)\r\n ro.disconnect()\r\n mo.disconnect()\r\n }\r\n }, [alvo, eixo, vertical, canto])\r\n\r\n // Arrastar o cursor. Os eventos são da janela e não do cursor: sair-lhe de\r\n // cima a meio do arrasto é o normal, e preso a ele o scroll parava aí.\r\n useEffect(() => {\r\n if (!alvo) return\r\n const mover = (e: PointerEvent) => {\r\n const a = arrasto.current\r\n if (!a) return\r\n const g = metricaBarra(alvo, eixo, canto)\r\n if (g.percurso <= 0) return\r\n const novo = a.scroll0 + (((vertical ? e.clientY : e.clientX) - a.p0) / g.percurso) * g.rolavel\r\n if (vertical) alvo.scrollTop = novo\r\n else alvo.scrollLeft = novo\r\n }\r\n const largar = () => {\r\n if (!arrasto.current) return\r\n arrasto.current = null\r\n // Recomeça a contagem: largada sem mais nenhum movimento, a barra ficaria\r\n // à vista para sempre.\r\n clearTimeout(ocioso.current)\r\n ocioso.current = setTimeout(() => mostrar(false), MS_BARRA_OCIOSA)\r\n }\r\n window.addEventListener('pointermove', mover)\r\n window.addEventListener('pointerup', largar)\r\n return () => {\r\n window.removeEventListener('pointermove', mover)\r\n window.removeEventListener('pointerup', largar)\r\n }\r\n }, [alvo, eixo, vertical, canto])\r\n\r\n if (!alvo || !cursor) return null\r\n return (\r\n <div\r\n ref={elemento}\r\n // z-20 para ficar acima de um cabeçalho colante z-10 — senão desaparecia\r\n // por baixo dele.\r\n className={`absolute ${vertical ? 'right-[3px] w-1.5' : `${naBorda ? '-bottom-[3px]' : 'bottom-[3px]'} h-1.5`} rounded-full bg-slate-400/70 hover:bg-slate-500/80 z-20 transition-opacity duration-200 ${\r\n visivel ? 'opacity-100' : 'opacity-0 pointer-events-none'}`}\r\n // A posição é o transform, posto à mão no scroll; aqui só o ponto de\r\n // partida (a borda) e o tamanho — e a posição atual, para um re-render\r\n // não a pôr a zero.\r\n style={vertical\r\n ? { top: borda, height: cursor.tamanho, transform: translacao(posicao.current) }\r\n : { left: borda, width: cursor.tamanho, transform: translacao(posicao.current) }}\r\n onPointerDown={e => {\r\n e.preventDefault() // senão arrastar selecionava o texto da grelha\r\n arrasto.current = { p0: vertical ? e.clientY : e.clientX, scroll0: vertical ? alvo.scrollTop : alvo.scrollLeft }\r\n }}\r\n />\r\n )\r\n}\r\n"],"names":[],"mappings":"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"}