@proef/common-ui 2.2.1 → 2.2.3
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.
- package/dist/esm/v1/capabilities/documents/QRScanning/scanQRCodes.js.map +1 -1
- package/dist/esm/v1/components/documents/DocumentDropzone/DocumentDropzone.js.map +1 -1
- package/dist/esm/v1/components/lists/ListFinder/ListFinder.js +1 -1
- package/dist/esm/v1/components/lists/ListFinder/ListFinder.js.map +1 -1
- package/dist/esm/v1/components/tables/PaginatedTable/PaginatedTable.js +2 -0
- package/dist/esm/v1/components/tables/PaginatedTable/PaginatedTable.js.map +1 -1
- package/dist/esm/v1/components/tables/Table/ColumnFilter.js +14 -14
- package/dist/esm/v1/components/tables/Table/ColumnFilter.js.map +1 -1
- package/dist/esm/v1/components/tables/Table/Table.js +60 -13
- package/dist/esm/v1/components/tables/Table/Table.js.map +1 -1
- package/dist/esm/v1/components/tables/Table/labels.js +3 -1
- package/dist/esm/v1/components/tables/Table/labels.js.map +1 -1
- package/dist/esm/v1/primitives/documents/PdfPreview/PdfPreview.js.map +1 -1
- package/dist/esm/v1/primitives/documents/PdfPreview/pdfjs.js.map +1 -1
- package/dist/esm/v1/primitives/forms/TextInput/TextInput.js.map +1 -1
- package/dist/style.components.css +1 -1
- package/dist/style.css +1 -1
- package/dist/style.layered.css +1 -1
- package/dist/types/v1/components/tables/PaginatedTable/PaginatedTable.d.ts +2 -2
- package/dist/types/v1/components/tables/Table/Table.d.ts +16 -1
- package/dist/types/v1/components/tables/Table/labels.d.ts +4 -0
- package/package.json +1 -1
|
@@ -1 +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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"}
|
|
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 overflow-hidden rounded-lg 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 rounded-md 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 rounded-md 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 rounded-md 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 rounded-md 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 rounded-md 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 rounded-md 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 rounded-md 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 rounded-sm 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 rounded-md 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 rounded-sm 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 rounded-md 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 rounded-md 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 rounded-md 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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"}
|
|
@@ -52,6 +52,8 @@ function TableBase({
|
|
|
52
52
|
overlayScrollbars: barraSobreposta = false,
|
|
53
53
|
headerRowClassName: classeCabecalho = "bg-slate-50",
|
|
54
54
|
scrollbarOnEdge: barraNaBorda = true,
|
|
55
|
+
bordered: comCaixa = false,
|
|
56
|
+
resizableColumns: redimensionavel = false,
|
|
55
57
|
horizontalScrollbarBelow: barraHorizontalFora = false,
|
|
56
58
|
skeleton: esqueletoAoCarregar = false,
|
|
57
59
|
fadeRight: esbaterDireita = false,
|
|
@@ -652,7 +654,7 @@ function TableBase({
|
|
|
652
654
|
selectable && /* @__PURE__ */ jsx("td", { style: fixa(0, fundoLinha(rowKey(row))).style, className: `px-4 py-3 ${fixa(0, fundoLinha(rowKey(row))).className}`, onClick: (e) => {
|
|
653
655
|
e.stopPropagation();
|
|
654
656
|
toggleRow(rowKey(row));
|
|
655
|
-
}, children: /* @__PURE__ */ jsx("div", { className: `w-4 h-4 flex items-center justify-center border transition-colors ${selectedKeys.has(rowKey(row)) ? "bg-primary border-primary" : "border-slate-300 hover:border-slate-400"}`, children: selectedKeys.has(rowKey(row)) && /* @__PURE__ */ jsx(CheckIcon, { size: 9, strokeWidth: 3, className: "text-white" }) }) }),
|
|
657
|
+
}, children: /* @__PURE__ */ jsx("div", { className: `w-4 h-4 flex items-center justify-center rounded-sm border transition-colors ${selectedKeys.has(rowKey(row)) ? "bg-primary border-primary" : "border-slate-300 hover:border-slate-400"}`, children: selectedKeys.has(rowKey(row)) && /* @__PURE__ */ jsx(CheckIcon, { size: 9, strokeWidth: 3, className: "text-white" }) }) }),
|
|
656
658
|
columns.map((col, i) => {
|
|
657
659
|
const f = fixa(i + (selectable ? 1 : 0), fundoLinha(rowKey(row)));
|
|
658
660
|
return /* @__PURE__ */ jsx("td", { style: f.style, className: `px-3 py-1.5 ${col.cellClassName ?? ""} ${f.className} ${recuoFim(i)} ${realce(col.key)} ${transicaoRealce}`, "data-col": col.key, children: col.render(row) }, col.key);
|
|
@@ -885,10 +887,45 @@ function TableBase({
|
|
|
885
887
|
ro.observe(c);
|
|
886
888
|
return () => ro.disconnect();
|
|
887
889
|
}, [preencheAltura, paginatedData]);
|
|
890
|
+
const redimensionar = useRef(null);
|
|
891
|
+
const MIN_LARGURA = 48;
|
|
892
|
+
const pegaRedimensionar = (chave) => redimensionavel && larguras ? /* @__PURE__ */ jsx(
|
|
893
|
+
"span",
|
|
894
|
+
{
|
|
895
|
+
role: "separator",
|
|
896
|
+
"aria-orientation": "vertical",
|
|
897
|
+
title: L.resizeColumn,
|
|
898
|
+
onPointerDown: (e) => {
|
|
899
|
+
e.stopPropagation();
|
|
900
|
+
e.preventDefault();
|
|
901
|
+
e.currentTarget.setPointerCapture?.(e.pointerId);
|
|
902
|
+
const th = e.currentTarget.parentElement;
|
|
903
|
+
redimensionar.current = { chave, x: e.clientX, largura: th.getBoundingClientRect().width };
|
|
904
|
+
},
|
|
905
|
+
onPointerMove: (e) => {
|
|
906
|
+
const r = redimensionar.current;
|
|
907
|
+
if (!r) return;
|
|
908
|
+
const nova = Math.max(MIN_LARGURA, Math.round(r.largura + e.clientX - r.x));
|
|
909
|
+
setLarguras((l) => l ? { ...l, [r.chave]: nova } : l);
|
|
910
|
+
},
|
|
911
|
+
onPointerUp: () => {
|
|
912
|
+
redimensionar.current = null;
|
|
913
|
+
},
|
|
914
|
+
onPointerCancel: () => {
|
|
915
|
+
redimensionar.current = null;
|
|
916
|
+
},
|
|
917
|
+
onClick: (e) => e.stopPropagation(),
|
|
918
|
+
onDoubleClick: (e) => {
|
|
919
|
+
e.stopPropagation();
|
|
920
|
+
setLarguras(null);
|
|
921
|
+
},
|
|
922
|
+
className: "absolute right-0 top-0 z-[5] h-full w-1.5 cursor-col-resize touch-none bg-transparent transition-colors hover:bg-primary/40"
|
|
923
|
+
}
|
|
924
|
+
) : null;
|
|
888
925
|
const grupoColunas = largurasVista ? /* @__PURE__ */ jsx("colgroup", { children: chavesVista.map((k) => /* @__PURE__ */ jsx("col", { style: { width: largurasVista[k] } }, k)) }) : null;
|
|
889
926
|
const estiloTabela = largurasVista ? { tableLayout: "fixed", width: chavesVista.reduce((a, k) => a + (largurasVista[k] ?? 0), 0), minWidth: "100%" } : { width: "max-content" };
|
|
890
927
|
const cabecalho = (fixoNoTopo) => /* @__PURE__ */ jsx("thead", { className: fixoNoTopo ? "sticky top-0 z-20" : void 0, children: /* @__PURE__ */ jsxs("tr", { ref: cabecalhoRef, className: `text-micro font-black uppercase text-slate-400 tracking-[0.15em] ${!bordasSeparadas ? "border-b-2 border-slate-200 bg-slate-50" : fundoNosTitulos ? "[&>th]:border-b-2 [&>th]:border-slate-200" : "shadow-[inset_0_-2px_0_0_var(--color-slate-200)]"}`, children: [
|
|
891
|
-
selectable && /* @__PURE__ */ jsx("th", { style: cabecaFixa(0).style, className: `px-4 py-3 w-10 ${fundoNosTitulos ? "bg-slate-50" : ""} ${cabecaFixa(0).className}`, children: /* @__PURE__ */ jsx("button", { onClick: toggleAll, className: `w-4 h-4 flex items-center justify-center border transition-colors ${allVisibleSelected ? "bg-primary border-primary" : someVisibleSelected ? "bg-slate-300 border-slate-300" : "border-slate-300 hover:border-slate-400"}`, children: (allVisibleSelected || someVisibleSelected) && /* @__PURE__ */ jsx(CheckIcon, { size: 9, strokeWidth: 3, className: "text-white" }) }) }),
|
|
928
|
+
selectable && /* @__PURE__ */ jsx("th", { style: cabecaFixa(0).style, className: `px-4 py-3 w-10 ${fundoNosTitulos ? "bg-slate-50" : ""} ${cabecaFixa(0).className}`, children: /* @__PURE__ */ jsx("button", { onClick: toggleAll, className: `w-4 h-4 flex items-center justify-center rounded-sm border transition-colors ${allVisibleSelected ? "bg-primary border-primary" : someVisibleSelected ? "bg-slate-300 border-slate-300" : "border-slate-300 hover:border-slate-400"}`, children: (allVisibleSelected || someVisibleSelected) && /* @__PURE__ */ jsx(CheckIcon, { size: 9, strokeWidth: 3, className: "text-white" }) }) }),
|
|
892
929
|
columns.map((col, i) => {
|
|
893
930
|
const canSort = col.sortable !== false && !!col.value;
|
|
894
931
|
const canFilter = col.filterable !== false && !!col.value;
|
|
@@ -903,7 +940,7 @@ function TableBase({
|
|
|
903
940
|
{
|
|
904
941
|
style: cabecaFixa(i + (selectable ? 1 : 0)).style,
|
|
905
942
|
onPointerDown: arrastavel ? (e) => premirTitulo(e, col.key) : void 0,
|
|
906
|
-
className: `px-4 py-3 ${col.headerClassName ?? ""} ${cabecaFixa(i + (selectable ? 1 : 0)).className} ${arrastavel ? "cursor-grab" : ""} ${realce(col.key, true)} ${transicaoRealce}`,
|
|
943
|
+
className: `px-4 py-3 ${cabecaFixa(i + (selectable ? 1 : 0)).style ? "" : "relative"} ${col.headerClassName ?? ""} ${cabecaFixa(i + (selectable ? 1 : 0)).className} ${arrastavel ? "cursor-grab" : ""} ${realce(col.key, true)} ${transicaoRealce}`,
|
|
907
944
|
"data-col": col.key,
|
|
908
945
|
children: [
|
|
909
946
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5 group/hdr whitespace-nowrap select-none", children: [
|
|
@@ -912,12 +949,16 @@ function TableBase({
|
|
|
912
949
|
{
|
|
913
950
|
onClick: () => {
|
|
914
951
|
if (!allData) onDataNeeded?.();
|
|
915
|
-
if (sortCol === col.key
|
|
952
|
+
if (sortCol === col.key && sortDir === "desc") {
|
|
953
|
+
setSortCol(defaultSortCol ?? null);
|
|
954
|
+
setSortDir(defaultSortDir ?? "asc");
|
|
955
|
+
} else if (sortCol === col.key) setSortDir("desc");
|
|
916
956
|
else {
|
|
917
957
|
setSortCol(col.key);
|
|
918
958
|
setSortDir("asc");
|
|
919
959
|
}
|
|
920
960
|
},
|
|
961
|
+
title: isSortActive && sortDir === "desc" ? L.resetSort : void 0,
|
|
921
962
|
className: `flex items-center gap-0.5 transition-colors hover:text-slate-700 ${isSortActive ? "text-proef-blue" : ""}`,
|
|
922
963
|
children: [
|
|
923
964
|
/* @__PURE__ */ jsx("span", { children: col.label }),
|
|
@@ -971,8 +1012,13 @@ function TableBase({
|
|
|
971
1012
|
ordenadaPor: sortCol === col.key ? sortDir : null,
|
|
972
1013
|
aCarregar: !allData && !!isAllDataLoading,
|
|
973
1014
|
onOrdenar: (dir) => {
|
|
974
|
-
|
|
975
|
-
|
|
1015
|
+
if (sortCol === col.key && sortDir === dir) {
|
|
1016
|
+
setSortCol(defaultSortCol ?? null);
|
|
1017
|
+
setSortDir(defaultSortDir ?? "asc");
|
|
1018
|
+
} else {
|
|
1019
|
+
setSortCol(col.key);
|
|
1020
|
+
setSortDir(dir);
|
|
1021
|
+
}
|
|
976
1022
|
},
|
|
977
1023
|
onAplicar: (vals) => {
|
|
978
1024
|
aplicarValores(col.key, vals);
|
|
@@ -1054,7 +1100,8 @@ function TableBase({
|
|
|
1054
1100
|
] }, sf.key);
|
|
1055
1101
|
})
|
|
1056
1102
|
] }),
|
|
1057
|
-
col.headerExtra && /* @__PURE__ */ jsx("div", { className: "mt-0.5 normal-case font-normal tracking-normal", children: col.headerExtra(filteredData) })
|
|
1103
|
+
col.headerExtra && /* @__PURE__ */ jsx("div", { className: "mt-0.5 normal-case font-normal tracking-normal", children: col.headerExtra(filteredData) }),
|
|
1104
|
+
pegaRedimensionar(col.key)
|
|
1058
1105
|
]
|
|
1059
1106
|
},
|
|
1060
1107
|
col.key
|
|
@@ -1063,7 +1110,7 @@ function TableBase({
|
|
|
1063
1110
|
] }) });
|
|
1064
1111
|
const LARGURAS_ESQUELETO = ["w-3/4", "w-1/2", "w-2/3", "w-5/6", "w-2/5"];
|
|
1065
1112
|
const linhasEsqueleto = Array.from({ length: 20 }, (_, r) => /* @__PURE__ */ jsxs("tr", { "aria-hidden": true, className: "animate-pulse motion-reduce:animate-none", children: [
|
|
1066
|
-
selectable && /* @__PURE__ */ jsx("td", { style: fixa(0, "bg-white").style, className: `px-4 py-3 ${fixa(0, "bg-white").className}`, children: /* @__PURE__ */ jsx("div", { className: "w-4 h-4 border border-slate-200 bg-slate-50" }) }),
|
|
1113
|
+
selectable && /* @__PURE__ */ jsx("td", { style: fixa(0, "bg-white").style, className: `px-4 py-3 ${fixa(0, "bg-white").className}`, children: /* @__PURE__ */ jsx("div", { className: "w-4 h-4 rounded-sm border border-slate-200 bg-slate-50" }) }),
|
|
1067
1114
|
columns.map((col, i) => {
|
|
1068
1115
|
const f = fixa(i + (selectable ? 1 : 0), "bg-white");
|
|
1069
1116
|
return /* @__PURE__ */ jsx("td", { style: f.style, className: `px-3 py-2.5 ${f.className} ${recuoFim(i)}`, children: /* @__PURE__ */ jsx("div", { className: `h-2 bg-slate-200 ${LARGURAS_ESQUELETO[(r + i) % LARGURAS_ESQUELETO.length]} ${/text-right/.test(col.cellClassName ?? "") ? "ml-auto" : /text-center/.test(col.cellClassName ?? "") ? "mx-auto" : ""}` }) }, col.key);
|
|
@@ -1079,7 +1126,7 @@ function TableBase({
|
|
|
1079
1126
|
selectable && /* @__PURE__ */ jsx("td", { style: fixa(0, fundoLinha(rowKey(row))).style, className: `px-4 py-3 ${fixa(0, fundoLinha(rowKey(row))).className}`, onClick: (e) => {
|
|
1080
1127
|
e.stopPropagation();
|
|
1081
1128
|
toggleRow(rowKey(row));
|
|
1082
|
-
}, children: /* @__PURE__ */ jsx("div", { className: `w-4 h-4 flex items-center justify-center border transition-colors ${selectedKeys.has(rowKey(row)) ? "bg-primary border-primary" : "border-slate-300 hover:border-slate-400"}`, children: selectedKeys.has(rowKey(row)) && /* @__PURE__ */ jsx(CheckIcon, { size: 9, strokeWidth: 3, className: "text-white" }) }) }),
|
|
1129
|
+
}, children: /* @__PURE__ */ jsx("div", { className: `w-4 h-4 flex items-center justify-center rounded-sm border transition-colors ${selectedKeys.has(rowKey(row)) ? "bg-primary border-primary" : "border-slate-300 hover:border-slate-400"}`, children: selectedKeys.has(rowKey(row)) && /* @__PURE__ */ jsx(CheckIcon, { size: 9, strokeWidth: 3, className: "text-white" }) }) }),
|
|
1083
1130
|
columns.map((col, i) => {
|
|
1084
1131
|
const f = fixa(i + (selectable ? 1 : 0), fundoLinha(rowKey(row)));
|
|
1085
1132
|
return /* @__PURE__ */ jsx("td", { style: f.style, className: `px-3 py-1.5 ${col.cellClassName ?? ""} ${f.className} ${recuoFim(i)} ${realce(col.key)} ${transicaoRealce}`, "data-col": col.key, children: col.render(row) }, col.key);
|
|
@@ -1295,7 +1342,7 @@ function TableBase({
|
|
|
1295
1342
|
/* A coluna só existe a preencher: as barras de chips e o rodapé são irmãos
|
|
1296
1343
|
da caixa de scroll, e é a coluna que lhes dá topo e fundo fixos com o
|
|
1297
1344
|
scroll pelo meio. */
|
|
1298
|
-
/* @__PURE__ */ jsxs("div", { ref: raizRef, className: `v1-reset text-control text-slate-800 ${preencheAltura ? "h-full min-h-0 flex flex-col" : ""}`, children: [
|
|
1345
|
+
/* @__PURE__ */ jsxs("div", { ref: raizRef, className: `v1-reset text-control text-slate-800 ${preencheAltura ? "h-full min-h-0 flex flex-col" : ""} ${comCaixa ? "rounded-xl border border-slate-200 bg-white overflow-hidden" : ""}`, children: [
|
|
1299
1346
|
barraTopo,
|
|
1300
1347
|
!topo && isGrouped && /* @__PURE__ */ jsx("div", { className: "px-4 py-2 bg-slate-50 border-b border-slate-200 flex items-center gap-1.5 flex-wrap", children: chipsGrupo }),
|
|
1301
1348
|
!topo && totalActiveFilters > 0 && /* @__PURE__ */ jsx("div", { className: "px-4 py-2 bg-slate-50 border-b border-slate-200 flex items-center gap-1.5 flex-wrap", children: chipsFiltro }),
|
|
@@ -1337,7 +1384,7 @@ function TableBase({
|
|
|
1337
1384
|
}
|
|
1338
1385
|
),
|
|
1339
1386
|
barraSobreposta && /* @__PURE__ */ jsx(OverlayScrollbar, { alvo: caixaCorpo, borda: 0, canto: !barraHorizontalFora }),
|
|
1340
|
-
barraSobreposta && /* @__PURE__ */ jsx(OverlayScrollbar, { alvo: caixaCorpo, eixo: "horizontal", borda: 0, canto: !barraHorizontalFora, naBorda: !barraHorizontalFora && barraNaBorda && temRodape })
|
|
1387
|
+
barraSobreposta && /* @__PURE__ */ jsx(OverlayScrollbar, { alvo: caixaCorpo, eixo: "horizontal", borda: 0, canto: !barraHorizontalFora, naBorda: !barraHorizontalFora && barraNaBorda && !comCaixa && temRodape })
|
|
1341
1388
|
] }),
|
|
1342
1389
|
exemplares && /* @__PURE__ */ jsx(
|
|
1343
1390
|
"table",
|
|
@@ -1376,7 +1423,7 @@ function TableBase({
|
|
|
1376
1423
|
{
|
|
1377
1424
|
disabled: internalPage <= 1,
|
|
1378
1425
|
onClick: () => setInternalPage((p) => p - 1),
|
|
1379
|
-
className: "flex items-center gap-1 px-3 py-1.5 text-micro font-black uppercase tracking-widest bg-white border border-slate-200 disabled:opacity-30 hover:enabled:border-proef-blue hover:enabled:text-proef-blue transition-colors",
|
|
1426
|
+
className: "flex items-center gap-1 px-3 py-1.5 rounded-md text-micro font-black uppercase tracking-widest bg-white border border-slate-200 disabled:opacity-30 hover:enabled:border-proef-blue hover:enabled:text-proef-blue transition-colors",
|
|
1380
1427
|
children: [
|
|
1381
1428
|
/* @__PURE__ */ jsx(ChevronLeftIcon, { size: 12 }),
|
|
1382
1429
|
L.prev
|
|
@@ -1388,7 +1435,7 @@ function TableBase({
|
|
|
1388
1435
|
{
|
|
1389
1436
|
disabled: internalPage >= internalTotalPages,
|
|
1390
1437
|
onClick: () => setInternalPage((p) => p + 1),
|
|
1391
|
-
className: "flex items-center gap-1 px-3 py-1.5 text-micro font-black uppercase tracking-widest bg-white border border-slate-200 disabled:opacity-30 hover:enabled:border-proef-blue hover:enabled:text-proef-blue transition-colors",
|
|
1438
|
+
className: "flex items-center gap-1 px-3 py-1.5 rounded-md text-micro font-black uppercase tracking-widest bg-white border border-slate-200 disabled:opacity-30 hover:enabled:border-proef-blue hover:enabled:text-proef-blue transition-colors",
|
|
1392
1439
|
children: [
|
|
1393
1440
|
L.next,
|
|
1394
1441
|
/* @__PURE__ */ jsx(ChevronRightIcon, { size: 12 })
|