@proef/common-ui 2.2.0 → 2.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/esm/index.js +26 -3
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/legacy/components/molecules/DataTable/DataTable.js +1 -1
- package/dist/esm/legacy/components/molecules/DataTable/DataTable.js.map +1 -1
- 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 +111 -54
- package/dist/esm/v1/components/lists/ListFinder/ListFinder.js.map +1 -1
- package/dist/esm/v1/foundations/icons/icons.js +49 -0
- package/dist/esm/v1/foundations/icons/icons.js.map +1 -1
- package/dist/esm/v1/primitives/display/RichText/RichText.js +12 -0
- package/dist/esm/v1/primitives/display/RichText/RichText.js.map +1 -0
- package/dist/esm/v1/primitives/display/RichText/utils.js +56 -0
- package/dist/esm/v1/primitives/display/RichText/utils.js.map +1 -0
- 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/RichTextEditor/RichTextEditor.js +253 -0
- package/dist/esm/v1/primitives/forms/RichTextEditor/RichTextEditor.js.map +1 -0
- package/dist/esm/v1/primitives/forms/TextInput/TextInput.js.map +1 -1
- package/dist/esm/v1/primitives/lists/ListItem/ListItem.js +45 -11
- package/dist/esm/v1/primitives/lists/ListItem/ListItem.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/lists/ListFinder/ListFinder.d.ts +31 -5
- package/dist/types/v1/foundations/icons/icons.d.ts +20 -0
- package/dist/types/v1/index.d.ts +8 -3
- package/dist/types/v1/primitives/display/RichText/RichText.d.ts +17 -0
- package/dist/types/v1/primitives/display/RichText/utils.d.ts +22 -0
- package/dist/types/v1/primitives/forms/RichTextEditor/RichTextEditor.d.ts +64 -0
- package/dist/types/v1/primitives/lists/ListItem/ListItem.d.ts +39 -8
- package/package.json +6 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ListFinder.js","sources":["../../../../../../src/v1/components/lists/ListFinder/ListFinder.tsx"],"sourcesContent":["import React, { useEffect, useMemo, useState } from 'react'\r\nimport { ChevronLeftIcon, SearchIcon } from '../../../foundations/icons/icons'\r\nimport ListItem, { type ListItemContent } from '../../../primitives/lists/ListItem/ListItem'\r\nimport Checkbox from '../../../primitives/forms/Checkbox/Checkbox'\r\nimport { OverlayScrollbar } from '../../tables/Table/OverlayScrollbar'\r\nimport { DEFAULT_LIST_FINDER_LABELS, type ListFinderLabels } from './labels'\r\n\r\nexport interface ListFinderProps<T> {\r\n /** As linhas: todas, ou só a página atual quando o servidor pagina (com `total`). */\r\n items: T[]\r\n /** Uma chave única por linha (ex: o id). */\r\n itemKey: (item: T) => string\r\n /** O que cada linha mostra: as partes do [ListItem](?path=/docs/v1-primitives-lists-listitem--docs). */\r\n itemContent: (item: T) => ListItemContent\r\n /** A linha aberta (fica realçada). */\r\n activeKey?: string | null\r\n /** Ao clicar numa linha: normalmente, abri-la ao lado. */\r\n onItemClick?: (item: T) => void\r\n /** As marcadas. Só há caixas de marcar com `onSelectionChange`. */\r\n selectedKeys?: ReadonlySet<string>\r\n onSelectionChange?: (keys: Set<string>) => void\r\n /** Filtros ao lado do voltar, por cima da pesquisa (ex: um ou dois [SelectInput](?path=/docs/v1-primitives-forms-selectinput--docs)). */\r\n filters?: React.ReactNode\r\n /** Com isto aparece um ‹ à esquerda, para fechar ou esconder a lista. */\r\n onBack?: () => void\r\n /** O texto da pesquisa. Sem ele, a pesquisa guarda o seu. */\r\n search?: string\r\n onSearchChange?: (search: string) => void\r\n /**\r\n * O texto onde a pesquisa procura, em cada linha. Com isto, a lista filtra-se\r\n * sozinha (sem acentos nem maiúsculas); sem isto, quem filtra é a app (ex: o\r\n * servidor, com `onSearchChange`). Uma lista colada (uma por linha, ou\r\n * separadas por vírgulas) mostra as que tiverem qualquer uma delas.\r\n */\r\n searchText?: (item: T) => string\r\n /** O servidor pagina: quantas linhas há lá ao todo. `items` é só a página atual. */\r\n total?: number\r\n /** A página atual (1, 2, ...), quando o servidor pagina. */\r\n page?: number\r\n onPageChange?: (page: number) => void\r\n /** Quantas linhas por página. Com o servidor a paginar, quantas ele manda. */\r\n pageSize?: number\r\n /** A carregar: sem linhas ainda, mostra o esqueleto delas. */\r\n loading?: boolean\r\n labels?: Partial<ListFinderLabels>\r\n className?: string\r\n}\r\n\r\nconst semAcentos = (s: string) => s.normalize('NFD').replace(/[̀-ͯ]/g, '').toLowerCase()\r\n\r\n/** Os números das páginas numa coluna estreita: no máximo cinco lugares. */\r\nfunction numerosPagina(atual: number, total: number): (number | '…')[] {\r\n if (total <= 5) return Array.from({ length: total }, (_, i) => i + 1)\r\n if (atual <= 2) return [1, 2, 3, '…', total]\r\n if (atual >= total - 1) return [1, '…', total - 2, total - 1, total]\r\n return [1, '…', atual, '…', total]\r\n}\r\n\r\n/**\r\n * Uma lista para encontrar e escolher: filtros e pesquisa em cima, as linhas\r\n * ([ListItem](?path=/docs/v1-primitives-lists-listitem--docs)) a rolar no meio e\r\n * as páginas em baixo. Pensada para a coluna ao lado do que se abre (a lista à\r\n * esquerda, o detalhe da linha escolhida à direita).\r\n *\r\n * Enche a altura do pai. O topo, a barra de \"selecionar todos\" e o rodapé\r\n * ficam sempre no sítio: só as linhas rolam.\r\n *\r\n * - **Pesquisa**: com `searchText` filtra sozinha; sem ele, a app filtra\r\n * (`search`/`onSearchChange`).\r\n * - **Páginas**: pagina sozinha (`pageSize`), ou é o servidor (`total`, `page`,\r\n * `onPageChange`).\r\n * - **Marcar vários**: com `onSelectionChange`. O \"selecionar todos\" só marca\r\n * as linhas à vista, nunca as que não se veem.\r\n */\r\nexport default function ListFinder<T>({\r\n items, itemKey, itemContent, activeKey, onItemClick, selectedKeys, onSelectionChange, filters, onBack,\r\n search: pesquisaDeFora, onSearchChange, searchText, total, page, onPageChange, pageSize = 50, loading = false,\r\n labels, className = '',\r\n}: ListFinderProps<T>) {\r\n const L = { ...DEFAULT_LIST_FINDER_LABELS, ...labels }\r\n const [pesquisaPropria, setPesquisaPropria] = useState('')\r\n const pesquisa = pesquisaDeFora ?? pesquisaPropria\r\n const pesquisar = (s: string) => { setPesquisaPropria(s); onSearchChange?.(s) }\r\n // Uma caixa de uma linha deita fora as quebras de linha: uma coluna colada\r\n // de uma folha de cálculo chegava como um número só, colado. Passam a vírgulas.\r\n const colar = (e: React.ClipboardEvent<HTMLInputElement>) => {\r\n const texto = e.clipboardData.getData('text')\r\n if (!/\\r?\\n/.test(texto.trim())) return\r\n e.preventDefault()\r\n const lista = texto.split(/\\r?\\n/).map(t => t.trim()).filter(Boolean).join(', ')\r\n const el = e.currentTarget\r\n pesquisar(el.value.slice(0, el.selectionStart ?? el.value.length) + lista + el.value.slice(el.selectionEnd ?? el.value.length))\r\n }\r\n\r\n const filtradas = useMemo(() => {\r\n if (!searchText || !pesquisa.trim()) return items\r\n const termos = pesquisa.split(/[\\n,;\\t]+/).map(t => semAcentos(t.trim())).filter(Boolean)\r\n return items.filter(it => {\r\n const texto = semAcentos(searchText(it))\r\n return termos.some(t => texto.includes(t))\r\n })\r\n }, [items, pesquisa, searchText])\r\n\r\n // Paginação: do servidor (com `total`), ou feita aqui.\r\n const peloServidor = total !== undefined\r\n const [paginaPropria, setPaginaPropria] = useState(1)\r\n const totalLinhas = peloServidor ? total : filtradas.length\r\n const totalPaginas = Math.max(1, Math.ceil(totalLinhas / pageSize))\r\n const pagina = Math.min(peloServidor ? (page ?? 1) : paginaPropria, totalPaginas)\r\n // Uma pesquisa nova volta à primeira página: a terceira de antes já não é a mesma lista.\r\n useEffect(() => { if (!peloServidor) setPaginaPropria(1) }, [pesquisa, peloServidor])\r\n const irPara = (p: number) => { if (peloServidor) onPageChange?.(p); else setPaginaPropria(p) }\r\n const visiveis = peloServidor ? filtradas : filtradas.slice((pagina - 1) * pageSize, pagina * pageSize)\r\n\r\n // Ao mudar de página, a lista volta ao topo.\r\n const [caixa, setCaixa] = useState<HTMLDivElement | null>(null)\r\n useEffect(() => { caixa?.scrollTo?.({ top: 0 }) }, [pagina, caixa])\r\n\r\n const marcadas = selectedKeys ?? new Set<string>()\r\n const marcaveis = !!onSelectionChange\r\n const nMarcadasVisiveis = visiveis.filter(it => marcadas.has(itemKey(it))).length\r\n const todasMarcadas = visiveis.length > 0 && nMarcadasVisiveis === visiveis.length\r\n const marcar = (key: string, sim: boolean) => {\r\n const n = new Set(marcadas)\r\n if (sim) n.add(key); else n.delete(key)\r\n onSelectionChange?.(n)\r\n }\r\n const marcarVisiveis = () => {\r\n const n = new Set(marcadas)\r\n for (const it of visiveis) { if (todasMarcadas) n.delete(itemKey(it)); else n.add(itemKey(it)) }\r\n onSelectionChange?.(n)\r\n }\r\n\r\n const aCarregar = loading && visiveis.length === 0\r\n const esqueleto = <span className=\"inline-block h-2 w-20 rounded-sm bg-slate-200 animate-pulse motion-reduce:animate-none\" aria-hidden />\r\n\r\n return (\r\n <div className={`v1-reset flex h-full min-h-0 flex-col border border-slate-200 bg-white text-control text-slate-800 ${className}`}>\r\n <div className=\"space-y-2 border-b border-slate-100 px-3 py-2.5\">\r\n {(onBack || filters) && (\r\n <div className=\"flex items-center gap-1.5\">\r\n {onBack && (\r\n <button type=\"button\" onClick={onBack} aria-label={L.back} title={L.back}\r\n className=\"-ml-1 flex h-7 w-6 shrink-0 items-center justify-center rounded text-slate-400 transition-colors hover:bg-slate-100 hover:text-slate-700\">\r\n <ChevronLeftIcon size={14} />\r\n </button>\r\n )}\r\n <div className=\"flex min-w-0 flex-1 items-center gap-1.5\">{filters}</div>\r\n </div>\r\n )}\r\n <div className=\"relative\">\r\n <SearchIcon size={13} className=\"pointer-events-none absolute top-1/2 left-2.5 -translate-y-1/2 text-slate-400\" />\r\n <input\r\n type=\"search\"\r\n value={pesquisa}\r\n onChange={e => pesquisar(e.target.value)}\r\n onPaste={colar}\r\n placeholder={L.searchPlaceholder}\r\n aria-label={L.search}\r\n className=\"block h-control w-full min-w-0 rounded-lg border border-border bg-card pr-2.5 pl-8 text-control text-foreground outline-none transition-[border-color,box-shadow] duration-150 placeholder:text-muted-foreground/70 focus:border-primary/40 focus:ring-3 focus:ring-primary/10\"\r\n />\r\n </div>\r\n </div>\r\n\r\n {/* Fora da área que rola: é a cabeça da lista, e é a descer a lista que\r\n se decide marcá-la toda. */}\r\n {marcaveis && visiveis.length > 0 && (\r\n <div className=\"flex items-center gap-2.5 border-b border-slate-100 bg-slate-50 px-3 py-1.5 text-caption text-slate-500\">\r\n <Checkbox\r\n checked={todasMarcadas}\r\n indeterminate={nMarcadasVisiveis > 0 && !todasMarcadas}\r\n onChange={marcarVisiveis}\r\n aria-label={L.selectVisible(visiveis.length)}\r\n />\r\n <span aria-hidden>{nMarcadasVisiveis > 0 ? L.selectedCount(nMarcadasVisiveis, visiveis.length) : L.selectVisible(visiveis.length)}</span>\r\n {marcadas.size > 0 && (\r\n <button type=\"button\" onClick={() => onSelectionChange?.(new Set())}\r\n className=\"ml-auto font-medium text-proef-blue hover:underline\">\r\n {L.clearSelection}\r\n </button>\r\n )}\r\n </div>\r\n )}\r\n\r\n <div className=\"relative flex min-h-0 flex-1 flex-col\">\r\n <div ref={setCaixa} className=\"min-h-0 flex-1 overflow-y-auto scrollbar-none\">\r\n {aCarregar ? (\r\n // A forma das linhas já diz que vem ali a lista. Mais do que cabem,\r\n // cortadas pela caixa: chega ao fundo em qualquer altura.\r\n <div role=\"status\" aria-busy=\"true\" className=\"h-full overflow-hidden\">\r\n <span className=\"sr-only\">{L.loading}</span>\r\n {['w-11/12', 'w-3/4', 'w-5/6', 'w-2/3', 'w-4/5', 'w-7/12'].flatMap(l => [l, l, l, l]).map((largura, i) => (\r\n <div key={i} className=\"flex items-start gap-2.5 border-b border-slate-100 px-3 py-2.5 animate-pulse motion-reduce:animate-none\">\r\n {marcaveis && <div className=\"mt-0.5 h-4 w-4 shrink-0 border border-slate-200 bg-slate-100\" />}\r\n <div className=\"min-w-0 flex-1 space-y-1.5\">\r\n <div className=\"h-3 w-12 bg-slate-200\" />\r\n <div className=\"h-1.5 w-16 bg-slate-100\" />\r\n <div className={`h-2.5 ${largura} bg-slate-200`} />\r\n <div className=\"h-1.5 w-1/3 bg-slate-100\" />\r\n </div>\r\n </div>\r\n ))}\r\n </div>\r\n ) : visiveis.length === 0 ? (\r\n <p className=\"m-0 px-3 py-6 text-center text-caption text-slate-400\">{L.empty}</p>\r\n ) : (\r\n <ul className=\"m-0 list-none p-0\">\r\n {visiveis.map(it => {\r\n const key = itemKey(it)\r\n return (\r\n <ListItem\r\n key={key}\r\n {...itemContent(it)}\r\n active={activeKey === key}\r\n onClick={onItemClick ? () => onItemClick(it) : undefined}\r\n checked={marcadas.has(key)}\r\n onCheckedChange={marcaveis ? sim => marcar(key, sim) : undefined}\r\n checkboxLabel={L.checkItem(key)}\r\n />\r\n )\r\n })}\r\n </ul>\r\n )}\r\n </div>\r\n <OverlayScrollbar alvo={caixa} borda={0} />\r\n </div>\r\n\r\n {/* Sempre à vista e com a mesma altura, carregada ou não: aparecer do\r\n nada fazia saltar o fundo do painel. */}\r\n <div className=\"flex h-11 shrink-0 items-center justify-between gap-2 border-t border-slate-100 bg-slate-50 px-3 text-caption text-slate-500\">\r\n <span className=\"whitespace-nowrap\">\r\n {aCarregar ? esqueleto : <><span className=\"font-semibold text-slate-800 tabular-nums\">{totalLinhas.toLocaleString('pt-PT')}</span> {L.itemsNoun(totalLinhas)}</>}\r\n </span>\r\n {!aCarregar && totalPaginas > 1 && (\r\n <nav aria-label={L.pagination} className=\"flex items-center gap-0.5\">\r\n <span className=\"sr-only\">{L.page} {pagina} / {totalPaginas}</span>\r\n {numerosPagina(pagina, totalPaginas).map((p, i) => p === '…' ? (\r\n <span key={`r${i}`} aria-hidden className=\"flex h-7 min-w-4 items-center justify-center text-slate-300\">…</span>\r\n ) : (\r\n <button\r\n key={p}\r\n type=\"button\"\r\n onClick={() => irPara(p)}\r\n aria-current={p === pagina ? 'page' : undefined}\r\n className={`h-7 min-w-7 rounded-md px-1.5 font-semibold tabular-nums transition-colors ${\r\n p === pagina ? 'cursor-default bg-primary text-white' : 'text-slate-500 hover:bg-slate-200/70 hover:text-slate-800'}`}\r\n >\r\n {p.toLocaleString('pt-PT')}\r\n </button>\r\n ))}\r\n </nav>\r\n )}\r\n </div>\r\n </div>\r\n )\r\n}\r\n"],"names":[],"mappings":";;;;;;;AAgDA,MAAM,aAAa,CAAC,MAAc,EAAE,UAAU,KAAK,EAAE,QAAQ,UAAU,EAAE,EAAE,YAAA;AAG3E,SAAS,cAAc,OAAe,OAAiC;AACrE,MAAI,SAAS,EAAG,QAAO,MAAM,KAAK,EAAE,QAAQ,MAAA,GAAS,CAAC,GAAG,MAAM,IAAI,CAAC;AACpE,MAAI,SAAS,EAAG,QAAO,CAAC,GAAG,GAAG,GAAG,KAAK,KAAK;AAC3C,MAAI,SAAS,QAAQ,EAAG,QAAO,CAAC,GAAG,KAAK,QAAQ,GAAG,QAAQ,GAAG,KAAK;AACnE,SAAO,CAAC,GAAG,KAAK,OAAO,KAAK,KAAK;AACnC;AAkBA,SAAwB,WAAc;AAAA,EACpC;AAAA,EAAO;AAAA,EAAS;AAAA,EAAa;AAAA,EAAW;AAAA,EAAa;AAAA,EAAc;AAAA,EAAmB;AAAA,EAAS;AAAA,EAC/F,QAAQ;AAAA,EAAgB;AAAA,EAAgB;AAAA,EAAY;AAAA,EAAO;AAAA,EAAM;AAAA,EAAc,WAAW;AAAA,EAAI,UAAU;AAAA,EACxG;AAAA,EAAQ,YAAY;AACtB,GAAuB;AACrB,QAAM,IAAI,EAAE,GAAG,4BAA4B,GAAG,OAAA;AAC9C,QAAM,CAAC,iBAAiB,kBAAkB,IAAI,SAAS,EAAE;AACzD,QAAM,WAAW,kBAAkB;AACnC,QAAM,YAAY,CAAC,MAAc;AAAE,uBAAmB,CAAC;AAAG,qBAAiB,CAAC;AAAA,EAAE;AAG9E,QAAM,QAAQ,CAAC,MAA8C;AAC3D,UAAM,QAAQ,EAAE,cAAc,QAAQ,MAAM;AAC5C,QAAI,CAAC,QAAQ,KAAK,MAAM,KAAA,CAAM,EAAG;AACjC,MAAE,eAAA;AACF,UAAM,QAAQ,MAAM,MAAM,OAAO,EAAE,IAAI,CAAA,MAAK,EAAE,KAAA,CAAM,EAAE,OAAO,OAAO,EAAE,KAAK,IAAI;AAC/E,UAAM,KAAK,EAAE;AACb,cAAU,GAAG,MAAM,MAAM,GAAG,GAAG,kBAAkB,GAAG,MAAM,MAAM,IAAI,QAAQ,GAAG,MAAM,MAAM,GAAG,gBAAgB,GAAG,MAAM,MAAM,CAAC;AAAA,EAChI;AAEA,QAAM,YAAY,QAAQ,MAAM;AAC9B,QAAI,CAAC,cAAc,CAAC,SAAS,KAAA,EAAQ,QAAO;AAC5C,UAAM,SAAS,SAAS,MAAM,WAAW,EAAE,IAAI,CAAA,MAAK,WAAW,EAAE,KAAA,CAAM,CAAC,EAAE,OAAO,OAAO;AACxF,WAAO,MAAM,OAAO,CAAA,OAAM;AACxB,YAAM,QAAQ,WAAW,WAAW,EAAE,CAAC;AACvC,aAAO,OAAO,KAAK,CAAA,MAAK,MAAM,SAAS,CAAC,CAAC;AAAA,IAC3C,CAAC;AAAA,EACH,GAAG,CAAC,OAAO,UAAU,UAAU,CAAC;AAGhC,QAAM,eAAe,UAAU;AAC/B,QAAM,CAAC,eAAe,gBAAgB,IAAI,SAAS,CAAC;AACpD,QAAM,cAAc,eAAe,QAAQ,UAAU;AACrD,QAAM,eAAe,KAAK,IAAI,GAAG,KAAK,KAAK,cAAc,QAAQ,CAAC;AAClE,QAAM,SAAS,KAAK,IAAI,eAAgB,QAAQ,IAAK,eAAe,YAAY;AAEhF,YAAU,MAAM;AAAE,QAAI,CAAC,aAAc,kBAAiB,CAAC;AAAA,EAAE,GAAG,CAAC,UAAU,YAAY,CAAC;AACpF,QAAM,SAAS,CAAC,MAAc;AAAE,QAAI,6BAA6B,CAAC;AAAA,0BAAyB,CAAC;AAAA,EAAE;AAC9F,QAAM,WAAW,eAAe,YAAY,UAAU,OAAO,SAAS,KAAK,UAAU,SAAS,QAAQ;AAGtG,QAAM,CAAC,OAAO,QAAQ,IAAI,SAAgC,IAAI;AAC9D,YAAU,MAAM;AAAE,WAAO,WAAW,EAAE,KAAK,EAAA,CAAG;AAAA,EAAE,GAAG,CAAC,QAAQ,KAAK,CAAC;AAElE,QAAM,WAAW,gBAAgB,oBAAI,IAAA;AACrC,QAAM,YAAY,CAAC,CAAC;AACpB,QAAM,oBAAoB,SAAS,OAAO,CAAA,OAAM,SAAS,IAAI,QAAQ,EAAE,CAAC,CAAC,EAAE;AAC3E,QAAM,gBAAgB,SAAS,SAAS,KAAK,sBAAsB,SAAS;AAC5E,QAAM,SAAS,CAAC,KAAa,QAAiB;AAC5C,UAAM,IAAI,IAAI,IAAI,QAAQ;AAC1B,QAAI,IAAK,GAAE,IAAI,GAAG;AAAA,QAAQ,GAAE,OAAO,GAAG;AACtC,wBAAoB,CAAC;AAAA,EACvB;AACA,QAAM,iBAAiB,MAAM;AAC3B,UAAM,IAAI,IAAI,IAAI,QAAQ;AAC1B,eAAW,MAAM,UAAU;AAAE,UAAI,cAAe,GAAE,OAAO,QAAQ,EAAE,CAAC;AAAA,UAAQ,GAAE,IAAI,QAAQ,EAAE,CAAC;AAAA,IAAE;AAC/F,wBAAoB,CAAC;AAAA,EACvB;AAEA,QAAM,YAAY,WAAW,SAAS,WAAW;AACjD,QAAM,YAAY,oBAAC,QAAA,EAAK,WAAU,0FAAyF,eAAW,MAAC;AAEvI,SACE,qBAAC,OAAA,EAAI,WAAW,sGAAsG,SAAS,IAC7H,UAAA;AAAA,IAAA,qBAAC,OAAA,EAAI,WAAU,mDACX,UAAA;AAAA,OAAA,UAAU,YACV,qBAAC,OAAA,EAAI,WAAU,6BACZ,UAAA;AAAA,QAAA,UACC;AAAA,UAAC;AAAA,UAAA;AAAA,YAAO,MAAK;AAAA,YAAS,SAAS;AAAA,YAAQ,cAAY,EAAE;AAAA,YAAM,OAAO,EAAE;AAAA,YAClE,WAAU;AAAA,YACV,UAAA,oBAAC,iBAAA,EAAgB,MAAM,GAAA,CAAI;AAAA,UAAA;AAAA,QAAA;AAAA,QAG/B,oBAAC,OAAA,EAAI,WAAU,4CAA4C,UAAA,QAAA,CAAQ;AAAA,MAAA,GACrE;AAAA,MAEF,qBAAC,OAAA,EAAI,WAAU,YACb,UAAA;AAAA,QAAA,oBAAC,YAAA,EAAW,MAAM,IAAI,WAAU,iFAAgF;AAAA,QAChH;AAAA,UAAC;AAAA,UAAA;AAAA,YACC,MAAK;AAAA,YACL,OAAO;AAAA,YACP,UAAU,CAAA,MAAK,UAAU,EAAE,OAAO,KAAK;AAAA,YACvC,SAAS;AAAA,YACT,aAAa,EAAE;AAAA,YACf,cAAY,EAAE;AAAA,YACd,WAAU;AAAA,UAAA;AAAA,QAAA;AAAA,MACZ,EAAA,CACF;AAAA,IAAA,GACF;AAAA,IAIC,aAAa,SAAS,SAAS,KAC9B,qBAAC,OAAA,EAAI,WAAU,2GACb,UAAA;AAAA,MAAA;AAAA,QAAC;AAAA,QAAA;AAAA,UACC,SAAS;AAAA,UACT,eAAe,oBAAoB,KAAK,CAAC;AAAA,UACzC,UAAU;AAAA,UACV,cAAY,EAAE,cAAc,SAAS,MAAM;AAAA,QAAA;AAAA,MAAA;AAAA,0BAE5C,QAAA,EAAK,eAAW,MAAE,UAAA,oBAAoB,IAAI,EAAE,cAAc,mBAAmB,SAAS,MAAM,IAAI,EAAE,cAAc,SAAS,MAAM,GAAE;AAAA,MACjI,SAAS,OAAO,KACf;AAAA,QAAC;AAAA,QAAA;AAAA,UAAO,MAAK;AAAA,UAAS,SAAS,MAAM,oBAAoB,oBAAI,KAAK;AAAA,UAChE,WAAU;AAAA,UACT,UAAA,EAAE;AAAA,QAAA;AAAA,MAAA;AAAA,IACL,GAEJ;AAAA,IAGF,qBAAC,OAAA,EAAI,WAAU,yCACb,UAAA;AAAA,MAAA,oBAAC,OAAA,EAAI,KAAK,UAAU,WAAU,iDAC3B,UAAA;AAAA;AAAA;AAAA,6BAGE,OAAA,EAAI,MAAK,UAAS,aAAU,QAAO,WAAU,0BAC5C,UAAA;AAAA,UAAA,oBAAC,QAAA,EAAK,WAAU,WAAW,UAAA,EAAE,SAAQ;AAAA,UACpC,CAAC,WAAW,SAAS,SAAS,SAAS,SAAS,QAAQ,EAAE,QAAQ,CAAA,MAAK,CAAC,GAAG,GAAG,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,SAAS,MAClG,qBAAC,OAAA,EAAY,WAAU,2GACpB,UAAA;AAAA,YAAA,aAAa,oBAAC,OAAA,EAAI,WAAU,+DAAA,CAA+D;AAAA,YAC5F,qBAAC,OAAA,EAAI,WAAU,8BACb,UAAA;AAAA,cAAA,oBAAC,OAAA,EAAI,WAAU,wBAAA,CAAwB;AAAA,cACvC,oBAAC,OAAA,EAAI,WAAU,0BAAA,CAA0B;AAAA,cACzC,oBAAC,OAAA,EAAI,WAAW,SAAS,OAAO,iBAAiB;AAAA,cACjD,oBAAC,OAAA,EAAI,WAAU,2BAAA,CAA2B;AAAA,YAAA,EAAA,CAC5C;AAAA,UAAA,EAAA,GAPQ,CAQV,CACD;AAAA,QAAA,EAAA,CACH;AAAA,UACE,SAAS,WAAW,IACtB,oBAAC,KAAA,EAAE,WAAU,yDAAyD,UAAA,EAAE,MAAA,CAAM,wBAE7E,MAAA,EAAG,WAAU,qBACX,UAAA,SAAS,IAAI,CAAA,OAAM;AAClB,cAAM,MAAM,QAAQ,EAAE;AACtB,eACE;AAAA,UAAC;AAAA,UAAA;AAAA,YAEE,GAAG,YAAY,EAAE;AAAA,YAClB,QAAQ,cAAc;AAAA,YACtB,SAAS,cAAc,MAAM,YAAY,EAAE,IAAI;AAAA,YAC/C,SAAS,SAAS,IAAI,GAAG;AAAA,YACzB,iBAAiB,YAAY,CAAA,QAAO,OAAO,KAAK,GAAG,IAAI;AAAA,YACvD,eAAe,EAAE,UAAU,GAAG;AAAA,UAAA;AAAA,UANzB;AAAA,QAAA;AAAA,MASX,CAAC,GACH,GAEJ;AAAA,MACA,oBAAC,kBAAA,EAAiB,MAAM,OAAO,OAAO,EAAA,CAAG;AAAA,IAAA,GAC3C;AAAA,IAIA,qBAAC,OAAA,EAAI,WAAU,gIACb,UAAA;AAAA,MAAA,oBAAC,QAAA,EAAK,WAAU,qBACb,UAAA,YAAY,YAAY,qBAAA,UAAA,EAAE,UAAA;AAAA,QAAA,oBAAC,UAAK,WAAU,6CAA6C,UAAA,YAAY,eAAe,OAAO,GAAE;AAAA,QAAO;AAAA,QAAE,EAAE,UAAU,WAAW;AAAA,MAAA,EAAA,CAAE,EAAA,CAChK;AAAA,MACC,CAAC,aAAa,eAAe,KAC5B,qBAAC,SAAI,cAAY,EAAE,YAAY,WAAU,6BACvC,UAAA;AAAA,QAAA,qBAAC,QAAA,EAAK,WAAU,WAAW,UAAA;AAAA,UAAA,EAAE;AAAA,UAAK;AAAA,UAAE;AAAA,UAAO;AAAA,UAAI;AAAA,QAAA,GAAa;AAAA,QAC3D,cAAc,QAAQ,YAAY,EAAE,IAAI,CAAC,GAAG,MAAM,MAAM,0BACtD,QAAA,EAAmB,eAAW,MAAC,WAAU,+DAA8D,iBAA7F,IAAI,CAAC,EAAyF,IAEzG;AAAA,UAAC;AAAA,UAAA;AAAA,YAEC,MAAK;AAAA,YACL,SAAS,MAAM,OAAO,CAAC;AAAA,YACvB,gBAAc,MAAM,SAAS,SAAS;AAAA,YACtC,WAAW,8EACT,MAAM,SAAS,yCAAyC,2DAA2D;AAAA,YAEpH,UAAA,EAAE,eAAe,OAAO;AAAA,UAAA;AAAA,UAPpB;AAAA,QAAA,CASR;AAAA,MAAA,EAAA,CACH;AAAA,IAAA,EAAA,CAEJ;AAAA,EAAA,GACF;AAEJ;"}
|
|
1
|
+
{"version":3,"file":"ListFinder.js","sources":["../../../../../../src/v1/components/lists/ListFinder/ListFinder.tsx"],"sourcesContent":["import React, { useEffect, useMemo, useState } from 'react'\r\nimport { ChevronLeftIcon, SearchIcon } from '../../../foundations/icons/icons'\r\nimport ListItem, { LINHA_ESBATIDA, type ListItemContent } from '../../../primitives/lists/ListItem/ListItem'\r\nimport Checkbox from '../../../primitives/forms/Checkbox/Checkbox'\r\nimport { OverlayScrollbar } from '../../tables/Table/OverlayScrollbar'\r\nimport { DEFAULT_LIST_FINDER_LABELS, type ListFinderLabels } from './labels'\r\n\r\nexport interface ListFinderProps<T> {\r\n /** As linhas: todas, ou só a página atual quando o servidor pagina (com `total`). */\r\n items: T[]\r\n /** Uma chave única por linha (ex: o id). */\r\n itemKey: (item: T) => string\r\n /** O que cada linha mostra: as partes do [ListItem](?path=/docs/v1-primitives-lists-listitem--docs). */\r\n itemContent: (item: T) => ListItemContent\r\n /** A linha aberta (fica realçada). */\r\n activeKey?: string | null\r\n /** Ao clicar numa linha: normalmente, abri-la ao lado. */\r\n onItemClick?: (item: T) => void\r\n /** As marcadas. Só há caixas de marcar com `onSelectionChange`. */\r\n selectedKeys?: ReadonlySet<string>\r\n onSelectionChange?: (keys: Set<string>) => void\r\n /** Filtros ao lado do voltar, por cima da pesquisa (ex: um ou dois selects, no traço fino da pesquisa). */\r\n filters?: React.ReactNode\r\n /** Com isto aparece um ‹ à esquerda, para fechar ou esconder a lista. */\r\n onBack?: () => void\r\n /** Com a caixa de pesquisa (o normal). Sem ela, a lista mostra tudo o que recebe. */\r\n searchable?: boolean\r\n /** O texto da pesquisa. Sem ele, a pesquisa guarda o seu. */\r\n search?: string\r\n onSearchChange?: (search: string) => void\r\n /**\r\n * O texto onde a pesquisa procura, em cada linha. Com isto, a lista filtra-se\r\n * sozinha (sem acentos nem maiúsculas); sem isto, quem filtra é a app (ex: o\r\n * servidor, com `onSearchChange`). Uma lista colada (uma por linha, ou\r\n * separadas por vírgulas) mostra as que tiverem qualquer uma delas.\r\n */\r\n searchText?: (item: T) => string\r\n /**\r\n * Como se chega ao resto da lista:\r\n * - `'pages'` (o normal): páginas numeradas no rodapé;\r\n * - `'scroll'`: uma lista só, que vai mostrando mais `pageSize` linhas ao\r\n * chegar ao fundo. Com o servidor a paginar, `items` são as que já vieram e\r\n * pede-se o lote seguinte com `onLoadMore`.\r\n */\r\n mode?: 'pages' | 'scroll'\r\n /** Com `mode=\"scroll\"` e o servidor a paginar: buscar o lote seguinte (junta-o a `items`). */\r\n onLoadMore?: () => void\r\n /** O servidor pagina: quantas linhas há lá ao todo. `items` é só a página atual (ou, a rolar, as que já vieram). */\r\n total?: number\r\n /** A página atual (1, 2, ...), quando o servidor pagina. */\r\n page?: number\r\n onPageChange?: (page: number) => void\r\n /** Quantas linhas por página. Com o servidor a paginar, quantas ele manda. */\r\n pageSize?: number\r\n /** A carregar: sem linhas ainda, mostra o esqueleto delas. A rolar, com linhas, é o lote seguinte a chegar. */\r\n loading?: boolean\r\n labels?: Partial<ListFinderLabels>\r\n /** No rodapé, a seguir à contagem: algo pequeno da app (ex: um ícone de ajuda com as regras da lista). */\r\n footerExtra?: React.ReactNode\r\n /** Com a moldura à volta (o normal). Sem ela, para ir dentro de um painel que já tem a sua. */\r\n bordered?: boolean\r\n className?: string\r\n}\r\n\r\nconst semAcentos = (s: string) => s.normalize('NFD').replace(/[̀-ͯ]/g, '').toLowerCase()\r\n\r\n/**\r\n * Parte o texto da pesquisa nos termos a procurar: vírgula, ponto e vírgula,\r\n * tab e mudança de linha. O espaço só separa quando tudo o que lá está são\r\n * números de 6 ou mais dígitos (uma coluna colada de uma folha de cálculo);\r\n * fora disso \"vila real\" é uma coisa só, e não \"vila\" ou \"real\".\r\n */\r\nexport function termosDaPesquisa(texto: string): string[] {\r\n const porEspacos = texto.trim().split(/\\s+/).filter(Boolean)\r\n if (porEspacos.length > 1 && porEspacos.every(t => /^\\d{6,}$/.test(t))) return porEspacos\r\n return texto.split(/[\\n,;\\t]+/).map(t => t.trim()).filter(Boolean)\r\n}\r\n\r\n/** Os números das páginas numa coluna estreita: no máximo cinco lugares. */\r\nfunction numerosPagina(atual: number, total: number): (number | '…')[] {\r\n if (total <= 5) return Array.from({ length: total }, (_, i) => i + 1)\r\n if (atual <= 2) return [1, 2, 3, '…', total]\r\n if (atual >= total - 1) return [1, '…', total - 2, total - 1, total]\r\n return [1, '…', atual, '…', total]\r\n}\r\n\r\n/** Uma linha a carregar: a forma das linhas já diz que vem ali a lista. */\r\nfunction LinhaEsqueleto({ marcavel, largura }: { marcavel: boolean; largura: string }) {\r\n return (\r\n <div className={`flex items-start gap-1.5 px-3 py-3 animate-pulse motion-reduce:animate-none ${LINHA_ESBATIDA}`}>\r\n {marcavel && <span className=\"flex w-7 shrink-0 justify-center\"><span className=\"mt-0.5 h-4 w-4 border border-slate-200 bg-slate-100\" /></span>}\r\n <div className=\"min-w-0 flex-1 space-y-1.5\">\r\n <div className=\"h-3 w-20 bg-slate-200\" />\r\n <div className={`h-2.5 ${largura} bg-slate-200`} />\r\n <div className=\"h-1.5 w-1/3 bg-slate-100\" />\r\n </div>\r\n </div>\r\n )\r\n}\r\n\r\n/**\r\n * Uma lista para encontrar e escolher: filtros e pesquisa em cima, as linhas\r\n * ([ListItem](?path=/docs/v1-primitives-lists-listitem--docs)) a rolar no meio e\r\n * as páginas (ou a contagem, a rolar) em baixo. Pensada para a coluna ao lado do que se abre (a lista à\r\n * esquerda, o detalhe da linha escolhida à direita).\r\n *\r\n * Enche a altura do pai. O topo, a barra de \"selecionar todos\" e o rodapé\r\n * ficam sempre no sítio: só as linhas rolam.\r\n *\r\n * - **Pesquisa**: com `searchText` filtra sozinha; sem ele, a app filtra\r\n * (`search`/`onSearchChange`).\r\n * - **Páginas**: pagina sozinha (`pageSize`), ou é o servidor (`total`, `page`,\r\n * `onPageChange`).\r\n * - **A rolar** (`mode=\"scroll\"`): sem páginas, vai mostrando mais ao descer;\r\n * com o servidor, `items` são as que já vieram e `onLoadMore` pede mais.\r\n * - **Realce**: o que se procurou realça-se no código e no título das linhas.\r\n * - **Marcar vários**: com `onSelectionChange`. O \"selecionar todos\" só marca\r\n * as linhas à vista, nunca as que não se veem.\r\n */\r\nexport default function ListFinder<T>({\r\n items, itemKey, itemContent, activeKey, onItemClick, selectedKeys, onSelectionChange, filters, onBack,\r\n searchable = true, search: pesquisaDeFora, onSearchChange, searchText, mode = 'pages', onLoadMore, total, page, onPageChange, pageSize = 50, loading = false,\r\n labels, footerExtra, bordered = true, className = '',\r\n}: ListFinderProps<T>) {\r\n const L = { ...DEFAULT_LIST_FINDER_LABELS, ...labels }\r\n const [pesquisaPropria, setPesquisaPropria] = useState('')\r\n const pesquisa = searchable ? (pesquisaDeFora ?? pesquisaPropria) : ''\r\n const pesquisar = (s: string) => { setPesquisaPropria(s); onSearchChange?.(s) }\r\n // Uma caixa de uma linha deita fora as quebras de linha: uma coluna colada\r\n // de uma folha de cálculo chegava como um número só, colado. Passam a vírgulas.\r\n const colar = (e: React.ClipboardEvent<HTMLInputElement>) => {\r\n const texto = e.clipboardData.getData('text')\r\n if (!/\\r?\\n/.test(texto.trim())) return\r\n e.preventDefault()\r\n const lista = texto.split(/\\r?\\n/).map(t => t.trim()).filter(Boolean).join(', ')\r\n const el = e.currentTarget\r\n pesquisar(el.value.slice(0, el.selectionStart ?? el.value.length) + lista + el.value.slice(el.selectionEnd ?? el.value.length))\r\n }\r\n\r\n // Os mesmos termos filtram (com `searchText`) e realçam-se nas linhas.\r\n const termos = useMemo(() => termosDaPesquisa(pesquisa).map(semAcentos), [pesquisa])\r\n const filtradas = useMemo(() => {\r\n if (!searchText || !termos.length) return items\r\n return items.filter(it => {\r\n const texto = semAcentos(searchText(it))\r\n return termos.some(t => texto.includes(t))\r\n })\r\n }, [items, termos, searchText])\r\n\r\n // Paginação: do servidor (com `total`), ou feita aqui.\r\n const peloServidor = total !== undefined\r\n const aRolar = mode === 'scroll'\r\n const [paginaPropria, setPaginaPropria] = useState(1)\r\n const totalLinhas = peloServidor ? total : filtradas.length\r\n const totalPaginas = Math.max(1, Math.ceil(totalLinhas / pageSize))\r\n const pagina = Math.min(peloServidor ? (page ?? 1) : paginaPropria, totalPaginas)\r\n // Uma pesquisa nova volta à primeira página: a terceira de antes já não é a mesma lista.\r\n useEffect(() => { if (!peloServidor) setPaginaPropria(1) }, [pesquisa, peloServidor])\r\n const irPara = (p: number) => { if (peloServidor) onPageChange?.(p); else setPaginaPropria(p) }\r\n // A rolar sem servidor: quantos lotes já se mostram.\r\n const [lotes, setLotes] = useState(1)\r\n useEffect(() => { setLotes(1) }, [pesquisa])\r\n const visiveis = peloServidor ? filtradas\r\n : aRolar ? filtradas.slice(0, lotes * pageSize)\r\n : filtradas.slice((pagina - 1) * pageSize, pagina * pageSize)\r\n const haMais = aRolar && (peloServidor ? !!onLoadMore && items.length < totalLinhas : visiveis.length < filtradas.length)\r\n\r\n // Ao mudar de página, ou de pesquisa a rolar, a lista volta ao topo.\r\n const [caixa, setCaixa] = useState<HTMLDivElement | null>(null)\r\n useEffect(() => { caixa?.scrollTo?.({ top: 0 }) }, [pagina, caixa])\r\n useEffect(() => { if (aRolar) caixa?.scrollTo?.({ top: 0 }) }, [pesquisa, aRolar, caixa])\r\n\r\n // A rolar: a linha-sentinela no fundo, ao chegar a 300px da vista, pede o\r\n // lote seguinte. Volta a observar a cada lote que chega: se continuar à\r\n // vista (uma janela alta), pede logo outro.\r\n const [fim, setFim] = useState<HTMLDivElement | null>(null)\r\n useEffect(() => {\r\n if (!fim || !caixa || !haMais || typeof IntersectionObserver === 'undefined') return\r\n const io = new IntersectionObserver(([e]) => {\r\n if (!e?.isIntersecting) return\r\n if (peloServidor) { if (!loading) onLoadMore?.() } else setLotes(n => n + 1)\r\n }, { root: caixa, rootMargin: '300px' })\r\n io.observe(fim)\r\n return () => io.disconnect()\r\n }, [fim, caixa, haMais, peloServidor, loading, onLoadMore, visiveis.length])\r\n\r\n const marcadas = selectedKeys ?? new Set<string>()\r\n const marcaveis = !!onSelectionChange\r\n const nMarcadasVisiveis = visiveis.filter(it => marcadas.has(itemKey(it))).length\r\n const todasMarcadas = visiveis.length > 0 && nMarcadasVisiveis === visiveis.length\r\n const marcar = (key: string, sim: boolean) => {\r\n const n = new Set(marcadas)\r\n if (sim) n.add(key); else n.delete(key)\r\n onSelectionChange?.(n)\r\n }\r\n const marcarVisiveis = () => {\r\n const n = new Set(marcadas)\r\n for (const it of visiveis) { if (todasMarcadas) n.delete(itemKey(it)); else n.add(itemKey(it)) }\r\n onSelectionChange?.(n)\r\n }\r\n\r\n const aCarregar = loading && visiveis.length === 0\r\n const esqueleto = <span className=\"inline-block h-2 w-20 rounded-sm bg-slate-200 animate-pulse motion-reduce:animate-none\" aria-hidden />\r\n\r\n return (\r\n <div className={`v1-reset flex h-full min-h-0 flex-col bg-white text-control text-slate-800 ${bordered ? 'border border-slate-200' : ''} ${className}`}>\r\n {/* Sem pesquisa, filtros nem voltar, não há cabeça: as linhas começam no topo. */}\r\n {(searchable || onBack || filters) && (\r\n <div className=\"space-y-2 border-b border-slate-100 px-3 py-2\">\r\n {(onBack || filters) && (\r\n <div className=\"flex items-center gap-1.5\">\r\n {onBack && (\r\n <button type=\"button\" onClick={onBack} aria-label={L.back} title={L.back}\r\n className=\"-ml-1 flex h-7 w-6 shrink-0 items-center justify-center rounded text-slate-400 transition-colors hover:bg-slate-100 hover:text-slate-700\">\r\n <ChevronLeftIcon size={14} />\r\n </button>\r\n )}\r\n <div className=\"flex min-w-0 flex-1 items-center gap-1.5\">{filters}</div>\r\n </div>\r\n )}\r\n {searchable && (\r\n <div className=\"relative\">\r\n {/* Caixa direita e fina, no tamanho do texto das linhas: a lista é\r\n uma coluna estreita, e a pesquisa não deve pesar mais do que ela. */}\r\n <SearchIcon size={11} className=\"pointer-events-none absolute top-1/2 left-2 -translate-y-1/2 text-slate-400\" />\r\n <input\r\n type=\"search\"\r\n value={pesquisa}\r\n onChange={e => pesquisar(e.target.value)}\r\n onPaste={colar}\r\n placeholder={L.searchPlaceholder}\r\n aria-label={L.search}\r\n className=\"block w-full min-w-0 border border-slate-200 bg-white py-1.5 pr-2 pl-6 text-micro text-slate-700 outline-none transition-colors placeholder:text-slate-400 focus:border-proef-blue\"\r\n />\r\n </div>\r\n )}\r\n </div>\r\n )}\r\n\r\n {/* Fora da área que rola: é a cabeça da lista, e é a descer a lista que\r\n se decide marcá-la toda. */}\r\n {marcaveis && visiveis.length > 0 && (\r\n <div className=\"flex items-center gap-1.5 border-b border-slate-100 bg-slate-50 px-3 py-1.5 text-[10px] text-slate-500\">\r\n {/* Na coluna de 28px das linhas, para as caixas ficarem umas por baixo das outras. */}\r\n <span className=\"flex w-7 shrink-0 justify-center\">\r\n <Checkbox\r\n checked={todasMarcadas}\r\n indeterminate={nMarcadasVisiveis > 0 && !todasMarcadas}\r\n onChange={marcarVisiveis}\r\n aria-label={L.selectVisible(visiveis.length)}\r\n />\r\n </span>\r\n <span aria-hidden>{nMarcadasVisiveis > 0 ? L.selectedCount(nMarcadasVisiveis, visiveis.length) : L.selectVisible(visiveis.length)}</span>\r\n {marcadas.size > 0 && (\r\n <button type=\"button\" onClick={() => onSelectionChange?.(new Set())}\r\n className=\"ml-auto text-proef-blue hover:underline\">\r\n {L.clearSelection}\r\n </button>\r\n )}\r\n </div>\r\n )}\r\n\r\n <div className=\"relative flex min-h-0 flex-1 flex-col\">\r\n {/* `relative`: o texto só para leitores de ecrã (`sr-only`, posição\r\n absoluta) do fundo da lista tem de ficar preso a esta caixa. Sem isso\r\n ficava no fim das linhas, fora do corte do scroll, e esticava a\r\n página — dava para rolar até um branco sem nada. */}\r\n <div ref={setCaixa} className=\"relative min-h-0 flex-1 overflow-y-auto scrollbar-none\">\r\n {aCarregar ? (\r\n // A forma das linhas já diz que vem ali a lista. Mais do que cabem,\r\n // cortadas pela caixa: chega ao fundo em qualquer altura.\r\n <div role=\"status\" aria-busy=\"true\" className=\"h-full overflow-hidden\">\r\n <span className=\"sr-only\">{L.loading}</span>\r\n {['w-11/12', 'w-3/4', 'w-5/6', 'w-2/3', 'w-4/5', 'w-7/12'].flatMap(l => [l, l, l, l]).map((largura, i) => (\r\n <LinhaEsqueleto key={i} marcavel={marcaveis} largura={largura} />\r\n ))}\r\n </div>\r\n ) : visiveis.length === 0 ? (\r\n <p className=\"m-0 px-3 py-4 text-center text-[10px] text-slate-400\">{L.empty}</p>\r\n ) : (\r\n <ul className=\"m-0 list-none p-0\">\r\n {visiveis.map(it => {\r\n const key = itemKey(it)\r\n return (\r\n <ListItem\r\n key={key}\r\n {...itemContent(it)}\r\n active={activeKey === key}\r\n onClick={onItemClick ? () => onItemClick(it) : undefined}\r\n checked={marcadas.has(key)}\r\n onCheckedChange={marcaveis ? sim => marcar(key, sim) : undefined}\r\n checkboxLabel={L.checkItem(key)}\r\n highlight={termos}\r\n />\r\n )\r\n })}\r\n </ul>\r\n )}\r\n {/* A rolar: o lote seguinte a chegar, e a sentinela que o pede. */}\r\n {!aCarregar && haMais && (\r\n <div ref={setFim} role=\"status\" aria-busy=\"true\">\r\n <span className=\"sr-only\">{L.loading}</span>\r\n {['w-3/4', 'w-5/6', 'w-2/3'].map((largura, i) => <LinhaEsqueleto key={i} marcavel={marcaveis} largura={largura} />)}\r\n </div>\r\n )}\r\n </div>\r\n <OverlayScrollbar alvo={caixa} borda={0} />\r\n </div>\r\n\r\n {/* Sempre à vista e com a mesma altura, carregada ou não: aparecer do\r\n nada fazia saltar o fundo do painel. */}\r\n <div className={`flex shrink-0 items-center gap-2 border-t border-slate-100 bg-slate-50 px-3 text-micro text-slate-400 ${aRolar ? 'h-8 justify-center' : 'h-9 justify-between'}`}>\r\n <span className=\"flex items-center gap-1 whitespace-nowrap\">\r\n {aCarregar ? esqueleto : aRolar ? (\r\n // A rolar: quantas já se mostram, de quantas há.\r\n <><span className=\"font-medium text-slate-500 tabular-nums\">{visiveis.length.toLocaleString('pt-PT')}</span> / <span className=\"font-medium text-slate-500 tabular-nums\">{totalLinhas.toLocaleString('pt-PT')}</span> {L.itemsNoun(totalLinhas)}</>\r\n ) : <><span className=\"font-medium text-slate-500 tabular-nums\">{totalLinhas.toLocaleString('pt-PT')}</span> {L.itemsNoun(totalLinhas)}</>}\r\n {footerExtra}\r\n {/* Com linhas e a pedir outra vez (ex: outra página, ou a atualizar): um \"…\" a pulsar. */}\r\n {loading && !aCarregar && <span aria-hidden className=\"animate-pulse motion-reduce:animate-none\">…</span>}\r\n </span>\r\n {!aRolar && !aCarregar && totalPaginas > 1 && (\r\n <nav aria-label={L.pagination} className=\"flex items-center gap-0.5\">\r\n <span className=\"sr-only\">{L.page} {pagina} / {totalPaginas}</span>\r\n {numerosPagina(pagina, totalPaginas).map((p, i) => p === '…' ? (\r\n <span key={`r${i}`} aria-hidden className=\"flex h-6 min-w-3 items-center justify-center text-slate-300\">…</span>\r\n ) : (\r\n <button\r\n key={p}\r\n type=\"button\"\r\n onClick={() => irPara(p)}\r\n aria-current={p === pagina ? 'page' : undefined}\r\n className={`grid h-6 min-w-5 place-items-center px-1 tabular-nums transition-colors ${\r\n p === pagina ? 'cursor-default font-semibold text-slate-800' : 'text-slate-400 hover:text-slate-700'}`}\r\n >\r\n {/* Sem caixa nem fundo: a página atual é só o número mais forte.\r\n A cópia invisível a negrito guarda a largura, para os números\r\n não se mexerem ao trocar de página. */}\r\n <span aria-hidden className=\"invisible col-start-1 row-start-1 font-semibold\">{p.toLocaleString('pt-PT')}</span>\r\n <span className=\"col-start-1 row-start-1\">{p.toLocaleString('pt-PT')}</span>\r\n </button>\r\n ))}\r\n </nav>\r\n )}\r\n </div>\r\n </div>\r\n )\r\n}\r\n"],"names":[],"mappings":"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"}
|
|
@@ -99,6 +99,45 @@ function PanelOpenIcon(props) {
|
|
|
99
99
|
function ListIcon(props) {
|
|
100
100
|
return /* @__PURE__ */ jsx(Svg, { ...props, children: /* @__PURE__ */ jsx("path", { d: "M9 6.5h11M9 12h11M9 17.5h11M4.5 6.5h.01M4.5 12h.01M4.5 17.5h.01" }) });
|
|
101
101
|
}
|
|
102
|
+
function BoldIcon(props) {
|
|
103
|
+
return /* @__PURE__ */ jsx(Svg, { strokeWidth: 2.2, ...props, children: /* @__PURE__ */ jsx("path", { d: "M7 5h6a3.5 3.5 0 0 1 0 7H7zM7 12h7a3.5 3.5 0 0 1 0 7H7z" }) });
|
|
104
|
+
}
|
|
105
|
+
function ItalicIcon(props) {
|
|
106
|
+
return /* @__PURE__ */ jsx(Svg, { ...props, children: /* @__PURE__ */ jsx("path", { d: "M10 5h8M6 19h8M14.5 5l-5 14" }) });
|
|
107
|
+
}
|
|
108
|
+
function UnderlineIcon(props) {
|
|
109
|
+
return /* @__PURE__ */ jsx(Svg, { ...props, children: /* @__PURE__ */ jsx("path", { d: "M7 4.5v6a5 5 0 0 0 10 0v-6M5.5 20h13" }) });
|
|
110
|
+
}
|
|
111
|
+
function StrikethroughIcon(props) {
|
|
112
|
+
return /* @__PURE__ */ jsx(Svg, { ...props, children: /* @__PURE__ */ jsx("path", { d: "M4.5 12h15M16.5 6.5A4.5 3.5 0 0 0 12 5c-2.5 0-4.5 1.3-4.5 3.2 0 1.4 1 2.4 2.7 2.9M8 17.3A5 3.6 0 0 0 12 19c2.6 0 4.6-1.4 4.6-3.3 0-.8-.3-1.5-.9-2" }) });
|
|
113
|
+
}
|
|
114
|
+
function ListBulletIcon(props) {
|
|
115
|
+
return /* @__PURE__ */ jsxs(Svg, { ...props, children: [
|
|
116
|
+
/* @__PURE__ */ jsx("path", { d: "M9.5 6.5h10M9.5 12h10M9.5 17.5h10" }),
|
|
117
|
+
/* @__PURE__ */ jsx("circle", { cx: "5", cy: "6.5", r: "1", fill: "currentColor" }),
|
|
118
|
+
/* @__PURE__ */ jsx("circle", { cx: "5", cy: "12", r: "1", fill: "currentColor" }),
|
|
119
|
+
/* @__PURE__ */ jsx("circle", { cx: "5", cy: "17.5", r: "1", fill: "currentColor" })
|
|
120
|
+
] });
|
|
121
|
+
}
|
|
122
|
+
function ListNumberedIcon(props) {
|
|
123
|
+
return /* @__PURE__ */ jsx(Svg, { ...props, children: /* @__PURE__ */ jsx("path", { d: "M10 6.5h10M10 12h10M10 17.5h10M4 5.5l1.3-.8v4M3.8 15.2c.3-.6.9-.9 1.5-.9.8 0 1.4.5 1.4 1.2 0 1-1.6 1.6-2.9 2.8h3" }) });
|
|
124
|
+
}
|
|
125
|
+
function QuoteIcon(props) {
|
|
126
|
+
return /* @__PURE__ */ jsx(Svg, { ...props, children: /* @__PURE__ */ jsx("path", { d: "M5 18V12.5C5 9 6.5 7 9.5 6M14 18v-5.5C14 9 15.5 7 18.5 6M5 12.5h4.5V18H5M14 12.5h4.5V18H14" }) });
|
|
127
|
+
}
|
|
128
|
+
function ImageIcon(props) {
|
|
129
|
+
return /* @__PURE__ */ jsxs(Svg, { ...props, children: [
|
|
130
|
+
/* @__PURE__ */ jsx("rect", { x: "3.5", y: "4.5", width: "17", height: "15", rx: "2" }),
|
|
131
|
+
/* @__PURE__ */ jsx("circle", { cx: "9", cy: "10", r: "1.6" }),
|
|
132
|
+
/* @__PURE__ */ jsx("path", { d: "M20.5 15.5l-4.5-4.5-8.5 8.5" })
|
|
133
|
+
] });
|
|
134
|
+
}
|
|
135
|
+
function MaximizeIcon(props) {
|
|
136
|
+
return /* @__PURE__ */ jsx(Svg, { ...props, children: /* @__PURE__ */ jsx("path", { d: "M14 4.5h5.5V10M10 19.5H4.5V14M19.5 4.5L13.5 10.5M4.5 19.5l6-6" }) });
|
|
137
|
+
}
|
|
138
|
+
function MinimizeIcon(props) {
|
|
139
|
+
return /* @__PURE__ */ jsx(Svg, { ...props, children: /* @__PURE__ */ jsx("path", { d: "M19.5 10H14V4.5M4.5 14H10v5.5M14 10l6-6M4 20l6-6" }) });
|
|
140
|
+
}
|
|
102
141
|
function SunIcon(props) {
|
|
103
142
|
return /* @__PURE__ */ jsxs(Svg, { ...props, children: [
|
|
104
143
|
/* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "4" }),
|
|
@@ -147,6 +186,7 @@ function SlidersIcon(props) {
|
|
|
147
186
|
] });
|
|
148
187
|
}
|
|
149
188
|
export {
|
|
189
|
+
BoldIcon,
|
|
150
190
|
CheckIcon,
|
|
151
191
|
ChevronDownIcon,
|
|
152
192
|
ChevronLeftIcon,
|
|
@@ -158,21 +198,30 @@ export {
|
|
|
158
198
|
ExternalLinkIcon,
|
|
159
199
|
FilterIcon,
|
|
160
200
|
GlobeIcon,
|
|
201
|
+
ImageIcon,
|
|
161
202
|
InfoIcon,
|
|
203
|
+
ItalicIcon,
|
|
162
204
|
LayersIcon,
|
|
205
|
+
ListBulletIcon,
|
|
163
206
|
ListIcon,
|
|
207
|
+
ListNumberedIcon,
|
|
164
208
|
LockIcon,
|
|
165
209
|
LogOutIcon,
|
|
210
|
+
MaximizeIcon,
|
|
166
211
|
MenuIcon,
|
|
212
|
+
MinimizeIcon,
|
|
167
213
|
MonitorIcon,
|
|
168
214
|
MoonIcon,
|
|
169
215
|
PanelOpenIcon,
|
|
170
216
|
PrefilledIcon,
|
|
171
217
|
QrCodeIcon,
|
|
218
|
+
QuoteIcon,
|
|
172
219
|
SearchIcon,
|
|
173
220
|
SlidersIcon,
|
|
174
221
|
SparklesIcon,
|
|
222
|
+
StrikethroughIcon,
|
|
175
223
|
SunIcon,
|
|
224
|
+
UnderlineIcon,
|
|
176
225
|
WarningIcon
|
|
177
226
|
};
|
|
178
227
|
//# sourceMappingURL=icons.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"icons.js","sources":["../../../../../src/v1/foundations/icons/icons.tsx"],"sourcesContent":["import React from 'react'\r\n\r\n/**\r\n * Conjunto de ícones do V1: traço de 1,7 a 1,9 em grelha de 24, cor do texto\r\n * (`currentColor`), sem preenchimento. Decorativos por omissão (`aria-hidden`):\r\n * quem os usa dá o nome acessível ao botão ou etiqueta à volta.\r\n */\r\nexport interface IconProps {\r\n /** Lado em px. */\r\n size?: number\r\n className?: string\r\n /** Espessura do traço. Por omissão 1,8; mais grosso em ícones muito pequenos (ex: o visto de uma caixa). */\r\n strokeWidth?: number\r\n}\r\n\r\nfunction Svg({ size = 16, className, strokeWidth = 1.8, children, fill = 'none' }: IconProps & { children: React.ReactNode; fill?: string }) {\r\n return (\r\n <svg\r\n width={size}\r\n height={size}\r\n viewBox=\"0 0 24 24\"\r\n fill={fill}\r\n stroke={fill === 'none' ? 'currentColor' : 'none'}\r\n strokeWidth={strokeWidth}\r\n strokeLinecap=\"round\"\r\n strokeLinejoin=\"round\"\r\n className={className}\r\n aria-hidden\r\n >\r\n {children}\r\n </svg>\r\n )\r\n}\r\n\r\n/** Lido de um QR code. */\r\nexport function QrCodeIcon(props: IconProps) {\r\n return (\r\n <Svg {...props}>\r\n <rect x=\"3.5\" y=\"3.5\" width=\"6.5\" height=\"6.5\" rx=\"1.3\" />\r\n <rect x=\"14\" y=\"3.5\" width=\"6.5\" height=\"6.5\" rx=\"1.3\" />\r\n <rect x=\"3.5\" y=\"14\" width=\"6.5\" height=\"6.5\" rx=\"1.3\" />\r\n <path d=\"M14 14h3.3v3.3M20.5 14v.01M14 20.5h.01M17.3 20.5h3.2v-3.2\" />\r\n </Svg>\r\n )\r\n}\r\n\r\n/** Identificado automaticamente (IA). */\r\nexport function SparklesIcon(props: IconProps) {\r\n return (\r\n <Svg {...props} fill=\"currentColor\">\r\n <path d=\"M12 2.5l1.7 4.6 4.6 1.7-4.6 1.7L12 15.1l-1.7-4.6L5.7 8.8l4.6-1.7z\" />\r\n <path d=\"M18.5 13.5l.85 2.35 2.35.85-2.35.85-.85 2.35-.85-2.35-2.35-.85 2.35-.85z\" />\r\n </Svg>\r\n )\r\n}\r\n\r\n/** Pré-preenchido a partir de outro registo (ex: linha de um extrato). */\r\nexport function PrefilledIcon(props: IconProps) {\r\n return (\r\n <Svg {...props}>\r\n <rect x=\"4\" y=\"3.5\" width=\"16\" height=\"17\" rx=\"2\" />\r\n <path d=\"M7.5 8.5h9M7.5 12h9M7.5 15.5h5.5\" />\r\n </Svg>\r\n )\r\n}\r\n\r\n/** Bloqueado: o valor não se pode alterar. */\r\nexport function LockIcon(props: IconProps) {\r\n return (\r\n <Svg {...props}>\r\n <rect x=\"5\" y=\"10.5\" width=\"14\" height=\"10\" rx=\"2\" />\r\n <path d=\"M8.5 10.5V7.5a3.5 3.5 0 0 1 7 0v3\" />\r\n <circle cx=\"12\" cy=\"15.5\" r=\"1.1\" fill=\"currentColor\" stroke=\"none\" />\r\n </Svg>\r\n )\r\n}\r\n\r\n/** Mais informação. */\r\nexport function InfoIcon(props: IconProps) {\r\n return (\r\n <Svg {...props}>\r\n <circle cx=\"12\" cy=\"12\" r=\"9.5\" />\r\n <path d=\"M12 11v5.5\" />\r\n <circle cx=\"12\" cy=\"7.8\" r=\"1.1\" fill=\"currentColor\" stroke=\"none\" />\r\n </Svg>\r\n )\r\n}\r\n\r\nexport function SearchIcon(props: IconProps) {\r\n return (\r\n <Svg {...props}>\r\n <circle cx=\"10.5\" cy=\"10.5\" r=\"6.5\" />\r\n <path d=\"M15.5 15.5L20 20\" />\r\n </Svg>\r\n )\r\n}\r\n\r\nexport function CheckIcon(props: IconProps) {\r\n return (\r\n <Svg {...props}>\r\n <path d=\"M4.5 12.5l5 5 10-11\" />\r\n </Svg>\r\n )\r\n}\r\n\r\nexport function ChevronDownIcon(props: IconProps) {\r\n return (\r\n <Svg {...props}>\r\n <path d=\"M6 9.5l6 6 6-6\" />\r\n </Svg>\r\n )\r\n}\r\n\r\nexport function ChevronUpIcon(props: IconProps) {\r\n return (\r\n <Svg {...props}>\r\n <path d=\"M6 14.5l6-6 6 6\" />\r\n </Svg>\r\n )\r\n}\r\n\r\nexport function ChevronLeftIcon(props: IconProps) {\r\n return (\r\n <Svg {...props}>\r\n <path d=\"M14.5 6l-6 6 6 6\" />\r\n </Svg>\r\n )\r\n}\r\n\r\nexport function ChevronRightIcon(props: IconProps) {\r\n return (\r\n <Svg {...props}>\r\n <path d=\"M9.5 6l6 6-6 6\" />\r\n </Svg>\r\n )\r\n}\r\n\r\n/** Ordenável: ainda sem ordem escolhida. */\r\nexport function ChevronsUpDownIcon(props: IconProps) {\r\n return (\r\n <Svg {...props}>\r\n <path d=\"M7.5 9.5L12 5l4.5 4.5M7.5 14.5L12 19l4.5-4.5\" />\r\n </Svg>\r\n )\r\n}\r\n\r\n/** Filtro (funil). */\r\nexport function FilterIcon(props: IconProps) {\r\n return (\r\n <Svg {...props}>\r\n <path d=\"M4 5h16l-6.2 7.6v5.6l-3.6 1.8v-7.4z\" />\r\n </Svg>\r\n )\r\n}\r\n\r\n/** Fechar, remover (×). */\r\nexport function CloseIcon(props: IconProps) {\r\n return (\r\n <Svg {...props}>\r\n <path d=\"M6.5 6.5l11 11M17.5 6.5l-11 11\" />\r\n </Svg>\r\n )\r\n}\r\n\r\n/** Abrir o menu (gaveta em ecrãs pequenos). */\r\nexport function MenuIcon(props: IconProps) {\r\n return (\r\n <Svg {...props}>\r\n <path d=\"M4 7h16M4 12h16M4 17h16\" />\r\n </Svg>\r\n )\r\n}\r\n\r\n/** Agrupar (camadas). */\r\nexport function LayersIcon(props: IconProps) {\r\n return (\r\n <Svg {...props}>\r\n <path d=\"M12 3.5l8.5 4.5-8.5 4.5L3.5 8z\" />\r\n <path d=\"M3.5 12l8.5 4.5 8.5-4.5M3.5 16l8.5 4.5 8.5-4.5\" />\r\n </Svg>\r\n )\r\n}\r\n\r\n/** Expandir o menu lateral recolhido. */\r\nexport function PanelOpenIcon(props: IconProps) {\r\n return (\r\n <Svg {...props}>\r\n <rect x=\"3.5\" y=\"4\" width=\"17\" height=\"16\" rx=\"2\" />\r\n <path d=\"M9 4v16M13 10l2 2-2 2\" />\r\n </Svg>\r\n )\r\n}\r\n\r\n/** Lista de valores. */\r\nexport function ListIcon(props: IconProps) {\r\n return (\r\n <Svg {...props}>\r\n <path d=\"M9 6.5h11M9 12h11M9 17.5h11M4.5 6.5h.01M4.5 12h.01M4.5 17.5h.01\" />\r\n </Svg>\r\n )\r\n}\r\n\r\n/** Tema claro. */\r\nexport function SunIcon(props: IconProps) {\r\n return (\r\n <Svg {...props}>\r\n <circle cx=\"12\" cy=\"12\" r=\"4\" />\r\n <path d=\"M12 2.5v2M12 19.5v2M4.6 4.6L6 6M18 18l1.4 1.4M2.5 12h2M19.5 12h2M4.6 19.4L6 18M18 6l1.4-1.4\" />\r\n </Svg>\r\n )\r\n}\r\n\r\n/** Tema escuro. */\r\nexport function MoonIcon(props: IconProps) {\r\n return (\r\n <Svg {...props}>\r\n <path d=\"M20 14.5A8 8 0 0 1 9.5 4a8 8 0 1 0 10.5 10.5z\" />\r\n </Svg>\r\n )\r\n}\r\n\r\n/** Tema do sistema. */\r\nexport function MonitorIcon(props: IconProps) {\r\n return (\r\n <Svg {...props}>\r\n <rect x=\"3\" y=\"4\" width=\"18\" height=\"12.5\" rx=\"2\" />\r\n <path d=\"M8.5 20.5h7M12 16.5v4\" />\r\n </Svg>\r\n )\r\n}\r\n\r\n/** Idioma. */\r\nexport function GlobeIcon(props: IconProps) {\r\n return (\r\n <Svg {...props}>\r\n <circle cx=\"12\" cy=\"12\" r=\"9\" />\r\n <path d=\"M3 12h18M12 3a14 14 0 0 1 0 18M12 3a14 14 0 0 0 0 18\" />\r\n </Svg>\r\n )\r\n}\r\n\r\n/** Terminar sessão. */\r\nexport function LogOutIcon(props: IconProps) {\r\n return (\r\n <Svg {...props}>\r\n <path d=\"M9.5 20.5H6a2 2 0 0 1-2-2v-13a2 2 0 0 1 2-2h3.5M15.5 16.5L20 12l-4.5-4.5M20 12H9.5\" />\r\n </Svg>\r\n )\r\n}\r\n\r\n/** Abre fora da aplicação (separador novo). */\r\nexport function ExternalLinkIcon(props: IconProps) {\r\n return (\r\n <Svg {...props}>\r\n <path d=\"M14 4h6v6M20 4l-9 9M18 13.5V18a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h4.5\" />\r\n </Svg>\r\n )\r\n}\r\n\r\n/** Atenção (avisos, confirmações perigosas). */\r\nexport function WarningIcon(props: IconProps) {\r\n return (\r\n <Svg {...props}>\r\n <path d=\"M10.3 4.3L2.8 17.5A2 2 0 0 0 4.5 20.5h15a2 2 0 0 0 1.7-3L13.7 4.3a2 2 0 0 0-3.4 0z\" />\r\n <path d=\"M12 9.5v4.5\" />\r\n <circle cx=\"12\" cy=\"17\" r=\"1.1\" fill=\"currentColor\" stroke=\"none\" />\r\n </Svg>\r\n )\r\n}\r\n\r\n/** Erro. */\r\nexport function ErrorIcon(props: IconProps) {\r\n return (\r\n <Svg {...props}>\r\n <circle cx=\"12\" cy=\"12\" r=\"9.5\" />\r\n <path d=\"M9 9l6 6M15 9l-6 6\" />\r\n </Svg>\r\n )\r\n}\r\n\r\n/** Intervalo (mínimo e máximo). */\r\nexport function SlidersIcon(props: IconProps) {\r\n return (\r\n <Svg {...props}>\r\n <path d=\"M4 7h10M18 7h2M4 17h2M10 17h10\" />\r\n <circle cx=\"16\" cy=\"7\" r=\"2\" />\r\n <circle cx=\"8\" cy=\"17\" r=\"2\" />\r\n </Svg>\r\n )\r\n}\r\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"icons.js","sources":["../../../../../src/v1/foundations/icons/icons.tsx"],"sourcesContent":["import React from 'react'\r\n\r\n/**\r\n * Conjunto de ícones do V1: traço de 1,7 a 1,9 em grelha de 24, cor do texto\r\n * (`currentColor`), sem preenchimento. Decorativos por omissão (`aria-hidden`):\r\n * quem os usa dá o nome acessível ao botão ou etiqueta à volta.\r\n */\r\nexport interface IconProps {\r\n /** Lado em px. */\r\n size?: number\r\n className?: string\r\n /** Espessura do traço. Por omissão 1,8; mais grosso em ícones muito pequenos (ex: o visto de uma caixa). */\r\n strokeWidth?: number\r\n}\r\n\r\nfunction Svg({ size = 16, className, strokeWidth = 1.8, children, fill = 'none' }: IconProps & { children: React.ReactNode; fill?: string }) {\r\n return (\r\n <svg\r\n width={size}\r\n height={size}\r\n viewBox=\"0 0 24 24\"\r\n fill={fill}\r\n stroke={fill === 'none' ? 'currentColor' : 'none'}\r\n strokeWidth={strokeWidth}\r\n strokeLinecap=\"round\"\r\n strokeLinejoin=\"round\"\r\n className={className}\r\n aria-hidden\r\n >\r\n {children}\r\n </svg>\r\n )\r\n}\r\n\r\n/** Lido de um QR code. */\r\nexport function QrCodeIcon(props: IconProps) {\r\n return (\r\n <Svg {...props}>\r\n <rect x=\"3.5\" y=\"3.5\" width=\"6.5\" height=\"6.5\" rx=\"1.3\" />\r\n <rect x=\"14\" y=\"3.5\" width=\"6.5\" height=\"6.5\" rx=\"1.3\" />\r\n <rect x=\"3.5\" y=\"14\" width=\"6.5\" height=\"6.5\" rx=\"1.3\" />\r\n <path d=\"M14 14h3.3v3.3M20.5 14v.01M14 20.5h.01M17.3 20.5h3.2v-3.2\" />\r\n </Svg>\r\n )\r\n}\r\n\r\n/** Identificado automaticamente (IA). */\r\nexport function SparklesIcon(props: IconProps) {\r\n return (\r\n <Svg {...props} fill=\"currentColor\">\r\n <path d=\"M12 2.5l1.7 4.6 4.6 1.7-4.6 1.7L12 15.1l-1.7-4.6L5.7 8.8l4.6-1.7z\" />\r\n <path d=\"M18.5 13.5l.85 2.35 2.35.85-2.35.85-.85 2.35-.85-2.35-2.35-.85 2.35-.85z\" />\r\n </Svg>\r\n )\r\n}\r\n\r\n/** Pré-preenchido a partir de outro registo (ex: linha de um extrato). */\r\nexport function PrefilledIcon(props: IconProps) {\r\n return (\r\n <Svg {...props}>\r\n <rect x=\"4\" y=\"3.5\" width=\"16\" height=\"17\" rx=\"2\" />\r\n <path d=\"M7.5 8.5h9M7.5 12h9M7.5 15.5h5.5\" />\r\n </Svg>\r\n )\r\n}\r\n\r\n/** Bloqueado: o valor não se pode alterar. */\r\nexport function LockIcon(props: IconProps) {\r\n return (\r\n <Svg {...props}>\r\n <rect x=\"5\" y=\"10.5\" width=\"14\" height=\"10\" rx=\"2\" />\r\n <path d=\"M8.5 10.5V7.5a3.5 3.5 0 0 1 7 0v3\" />\r\n <circle cx=\"12\" cy=\"15.5\" r=\"1.1\" fill=\"currentColor\" stroke=\"none\" />\r\n </Svg>\r\n )\r\n}\r\n\r\n/** Mais informação. */\r\nexport function InfoIcon(props: IconProps) {\r\n return (\r\n <Svg {...props}>\r\n <circle cx=\"12\" cy=\"12\" r=\"9.5\" />\r\n <path d=\"M12 11v5.5\" />\r\n <circle cx=\"12\" cy=\"7.8\" r=\"1.1\" fill=\"currentColor\" stroke=\"none\" />\r\n </Svg>\r\n )\r\n}\r\n\r\nexport function SearchIcon(props: IconProps) {\r\n return (\r\n <Svg {...props}>\r\n <circle cx=\"10.5\" cy=\"10.5\" r=\"6.5\" />\r\n <path d=\"M15.5 15.5L20 20\" />\r\n </Svg>\r\n )\r\n}\r\n\r\nexport function CheckIcon(props: IconProps) {\r\n return (\r\n <Svg {...props}>\r\n <path d=\"M4.5 12.5l5 5 10-11\" />\r\n </Svg>\r\n )\r\n}\r\n\r\nexport function ChevronDownIcon(props: IconProps) {\r\n return (\r\n <Svg {...props}>\r\n <path d=\"M6 9.5l6 6 6-6\" />\r\n </Svg>\r\n )\r\n}\r\n\r\nexport function ChevronUpIcon(props: IconProps) {\r\n return (\r\n <Svg {...props}>\r\n <path d=\"M6 14.5l6-6 6 6\" />\r\n </Svg>\r\n )\r\n}\r\n\r\nexport function ChevronLeftIcon(props: IconProps) {\r\n return (\r\n <Svg {...props}>\r\n <path d=\"M14.5 6l-6 6 6 6\" />\r\n </Svg>\r\n )\r\n}\r\n\r\nexport function ChevronRightIcon(props: IconProps) {\r\n return (\r\n <Svg {...props}>\r\n <path d=\"M9.5 6l6 6-6 6\" />\r\n </Svg>\r\n )\r\n}\r\n\r\n/** Ordenável: ainda sem ordem escolhida. */\r\nexport function ChevronsUpDownIcon(props: IconProps) {\r\n return (\r\n <Svg {...props}>\r\n <path d=\"M7.5 9.5L12 5l4.5 4.5M7.5 14.5L12 19l4.5-4.5\" />\r\n </Svg>\r\n )\r\n}\r\n\r\n/** Filtro (funil). */\r\nexport function FilterIcon(props: IconProps) {\r\n return (\r\n <Svg {...props}>\r\n <path d=\"M4 5h16l-6.2 7.6v5.6l-3.6 1.8v-7.4z\" />\r\n </Svg>\r\n )\r\n}\r\n\r\n/** Fechar, remover (×). */\r\nexport function CloseIcon(props: IconProps) {\r\n return (\r\n <Svg {...props}>\r\n <path d=\"M6.5 6.5l11 11M17.5 6.5l-11 11\" />\r\n </Svg>\r\n )\r\n}\r\n\r\n/** Abrir o menu (gaveta em ecrãs pequenos). */\r\nexport function MenuIcon(props: IconProps) {\r\n return (\r\n <Svg {...props}>\r\n <path d=\"M4 7h16M4 12h16M4 17h16\" />\r\n </Svg>\r\n )\r\n}\r\n\r\n/** Agrupar (camadas). */\r\nexport function LayersIcon(props: IconProps) {\r\n return (\r\n <Svg {...props}>\r\n <path d=\"M12 3.5l8.5 4.5-8.5 4.5L3.5 8z\" />\r\n <path d=\"M3.5 12l8.5 4.5 8.5-4.5M3.5 16l8.5 4.5 8.5-4.5\" />\r\n </Svg>\r\n )\r\n}\r\n\r\n/** Expandir o menu lateral recolhido. */\r\nexport function PanelOpenIcon(props: IconProps) {\r\n return (\r\n <Svg {...props}>\r\n <rect x=\"3.5\" y=\"4\" width=\"17\" height=\"16\" rx=\"2\" />\r\n <path d=\"M9 4v16M13 10l2 2-2 2\" />\r\n </Svg>\r\n )\r\n}\r\n\r\n/** Lista de valores. */\r\nexport function ListIcon(props: IconProps) {\r\n return (\r\n <Svg {...props}>\r\n <path d=\"M9 6.5h11M9 12h11M9 17.5h11M4.5 6.5h.01M4.5 12h.01M4.5 17.5h.01\" />\r\n </Svg>\r\n )\r\n}\r\n\r\n/** Texto: negrito. */\r\nexport function BoldIcon(props: IconProps) {\r\n return (\r\n <Svg strokeWidth={2.2} {...props}>\r\n <path d=\"M7 5h6a3.5 3.5 0 0 1 0 7H7zM7 12h7a3.5 3.5 0 0 1 0 7H7z\" />\r\n </Svg>\r\n )\r\n}\r\n\r\n/** Texto: itálico. */\r\nexport function ItalicIcon(props: IconProps) {\r\n return (\r\n <Svg {...props}>\r\n <path d=\"M10 5h8M6 19h8M14.5 5l-5 14\" />\r\n </Svg>\r\n )\r\n}\r\n\r\n/** Texto: sublinhado. */\r\nexport function UnderlineIcon(props: IconProps) {\r\n return (\r\n <Svg {...props}>\r\n <path d=\"M7 4.5v6a5 5 0 0 0 10 0v-6M5.5 20h13\" />\r\n </Svg>\r\n )\r\n}\r\n\r\n/** Texto: riscado. */\r\nexport function StrikethroughIcon(props: IconProps) {\r\n return (\r\n <Svg {...props}>\r\n <path d=\"M4.5 12h15M16.5 6.5A4.5 3.5 0 0 0 12 5c-2.5 0-4.5 1.3-4.5 3.2 0 1.4 1 2.4 2.7 2.9M8 17.3A5 3.6 0 0 0 12 19c2.6 0 4.6-1.4 4.6-3.3 0-.8-.3-1.5-.9-2\" />\r\n </Svg>\r\n )\r\n}\r\n\r\n/** Lista com marcas (no texto). */\r\nexport function ListBulletIcon(props: IconProps) {\r\n return (\r\n <Svg {...props}>\r\n <path d=\"M9.5 6.5h10M9.5 12h10M9.5 17.5h10\" />\r\n <circle cx=\"5\" cy=\"6.5\" r=\"1\" fill=\"currentColor\" />\r\n <circle cx=\"5\" cy=\"12\" r=\"1\" fill=\"currentColor\" />\r\n <circle cx=\"5\" cy=\"17.5\" r=\"1\" fill=\"currentColor\" />\r\n </Svg>\r\n )\r\n}\r\n\r\n/** Lista numerada (no texto). */\r\nexport function ListNumberedIcon(props: IconProps) {\r\n return (\r\n <Svg {...props}>\r\n <path d=\"M10 6.5h10M10 12h10M10 17.5h10M4 5.5l1.3-.8v4M3.8 15.2c.3-.6.9-.9 1.5-.9.8 0 1.4.5 1.4 1.2 0 1-1.6 1.6-2.9 2.8h3\" />\r\n </Svg>\r\n )\r\n}\r\n\r\n/** Citação. */\r\nexport function QuoteIcon(props: IconProps) {\r\n return (\r\n <Svg {...props}>\r\n <path d=\"M5 18V12.5C5 9 6.5 7 9.5 6M14 18v-5.5C14 9 15.5 7 18.5 6M5 12.5h4.5V18H5M14 12.5h4.5V18H14\" />\r\n </Svg>\r\n )\r\n}\r\n\r\n/** Imagem. */\r\nexport function ImageIcon(props: IconProps) {\r\n return (\r\n <Svg {...props}>\r\n <rect x=\"3.5\" y=\"4.5\" width=\"17\" height=\"15\" rx=\"2\" />\r\n <circle cx=\"9\" cy=\"10\" r=\"1.6\" />\r\n <path d=\"M20.5 15.5l-4.5-4.5-8.5 8.5\" />\r\n </Svg>\r\n )\r\n}\r\n\r\n/** Abrir em ecrã quase inteiro. */\r\nexport function MaximizeIcon(props: IconProps) {\r\n return (\r\n <Svg {...props}>\r\n <path d=\"M14 4.5h5.5V10M10 19.5H4.5V14M19.5 4.5L13.5 10.5M4.5 19.5l6-6\" />\r\n </Svg>\r\n )\r\n}\r\n\r\n/** Voltar ao tamanho normal. */\r\nexport function MinimizeIcon(props: IconProps) {\r\n return (\r\n <Svg {...props}>\r\n <path d=\"M19.5 10H14V4.5M4.5 14H10v5.5M14 10l6-6M4 20l6-6\" />\r\n </Svg>\r\n )\r\n}\r\n\r\n/** Tema claro. */\r\nexport function SunIcon(props: IconProps) {\r\n return (\r\n <Svg {...props}>\r\n <circle cx=\"12\" cy=\"12\" r=\"4\" />\r\n <path d=\"M12 2.5v2M12 19.5v2M4.6 4.6L6 6M18 18l1.4 1.4M2.5 12h2M19.5 12h2M4.6 19.4L6 18M18 6l1.4-1.4\" />\r\n </Svg>\r\n )\r\n}\r\n\r\n/** Tema escuro. */\r\nexport function MoonIcon(props: IconProps) {\r\n return (\r\n <Svg {...props}>\r\n <path d=\"M20 14.5A8 8 0 0 1 9.5 4a8 8 0 1 0 10.5 10.5z\" />\r\n </Svg>\r\n )\r\n}\r\n\r\n/** Tema do sistema. */\r\nexport function MonitorIcon(props: IconProps) {\r\n return (\r\n <Svg {...props}>\r\n <rect x=\"3\" y=\"4\" width=\"18\" height=\"12.5\" rx=\"2\" />\r\n <path d=\"M8.5 20.5h7M12 16.5v4\" />\r\n </Svg>\r\n )\r\n}\r\n\r\n/** Idioma. */\r\nexport function GlobeIcon(props: IconProps) {\r\n return (\r\n <Svg {...props}>\r\n <circle cx=\"12\" cy=\"12\" r=\"9\" />\r\n <path d=\"M3 12h18M12 3a14 14 0 0 1 0 18M12 3a14 14 0 0 0 0 18\" />\r\n </Svg>\r\n )\r\n}\r\n\r\n/** Terminar sessão. */\r\nexport function LogOutIcon(props: IconProps) {\r\n return (\r\n <Svg {...props}>\r\n <path d=\"M9.5 20.5H6a2 2 0 0 1-2-2v-13a2 2 0 0 1 2-2h3.5M15.5 16.5L20 12l-4.5-4.5M20 12H9.5\" />\r\n </Svg>\r\n )\r\n}\r\n\r\n/** Abre fora da aplicação (separador novo). */\r\nexport function ExternalLinkIcon(props: IconProps) {\r\n return (\r\n <Svg {...props}>\r\n <path d=\"M14 4h6v6M20 4l-9 9M18 13.5V18a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h4.5\" />\r\n </Svg>\r\n )\r\n}\r\n\r\n/** Atenção (avisos, confirmações perigosas). */\r\nexport function WarningIcon(props: IconProps) {\r\n return (\r\n <Svg {...props}>\r\n <path d=\"M10.3 4.3L2.8 17.5A2 2 0 0 0 4.5 20.5h15a2 2 0 0 0 1.7-3L13.7 4.3a2 2 0 0 0-3.4 0z\" />\r\n <path d=\"M12 9.5v4.5\" />\r\n <circle cx=\"12\" cy=\"17\" r=\"1.1\" fill=\"currentColor\" stroke=\"none\" />\r\n </Svg>\r\n )\r\n}\r\n\r\n/** Erro. */\r\nexport function ErrorIcon(props: IconProps) {\r\n return (\r\n <Svg {...props}>\r\n <circle cx=\"12\" cy=\"12\" r=\"9.5\" />\r\n <path d=\"M9 9l6 6M15 9l-6 6\" />\r\n </Svg>\r\n )\r\n}\r\n\r\n/** Intervalo (mínimo e máximo). */\r\nexport function SlidersIcon(props: IconProps) {\r\n return (\r\n <Svg {...props}>\r\n <path d=\"M4 7h10M18 7h2M4 17h2M10 17h10\" />\r\n <circle cx=\"16\" cy=\"7\" r=\"2\" />\r\n <circle cx=\"8\" cy=\"17\" r=\"2\" />\r\n </Svg>\r\n )\r\n}\r\n"],"names":[],"mappings":";AAeA,SAAS,IAAI,EAAE,OAAO,IAAI,WAAW,cAAc,KAAK,UAAU,OAAO,UAAoE;AAC3I,SACE;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,OAAO;AAAA,MACP,QAAQ;AAAA,MACR,SAAQ;AAAA,MACR;AAAA,MACA,QAAQ,SAAS,SAAS,iBAAiB;AAAA,MAC3C;AAAA,MACA,eAAc;AAAA,MACd,gBAAe;AAAA,MACf;AAAA,MACA,eAAW;AAAA,MAEV;AAAA,IAAA;AAAA,EAAA;AAGP;AAGO,SAAS,WAAW,OAAkB;AAC3C,SACE,qBAAC,KAAA,EAAK,GAAG,OACP,UAAA;AAAA,IAAA,oBAAC,QAAA,EAAK,GAAE,OAAM,GAAE,OAAM,OAAM,OAAM,QAAO,OAAM,IAAG,MAAA,CAAM;AAAA,IACxD,oBAAC,QAAA,EAAK,GAAE,MAAK,GAAE,OAAM,OAAM,OAAM,QAAO,OAAM,IAAG,MAAA,CAAM;AAAA,IACvD,oBAAC,QAAA,EAAK,GAAE,OAAM,GAAE,MAAK,OAAM,OAAM,QAAO,OAAM,IAAG,MAAA,CAAM;AAAA,IACvD,oBAAC,QAAA,EAAK,GAAE,4DAAA,CAA4D;AAAA,EAAA,GACtE;AAEJ;AAGO,SAAS,aAAa,OAAkB;AAC7C,SACE,qBAAC,KAAA,EAAK,GAAG,OAAO,MAAK,gBACnB,UAAA;AAAA,IAAA,oBAAC,QAAA,EAAK,GAAE,oEAAA,CAAoE;AAAA,IAC5E,oBAAC,QAAA,EAAK,GAAE,2EAAA,CAA2E;AAAA,EAAA,GACrF;AAEJ;AAGO,SAAS,cAAc,OAAkB;AAC9C,SACE,qBAAC,KAAA,EAAK,GAAG,OACP,UAAA;AAAA,IAAA,oBAAC,QAAA,EAAK,GAAE,KAAI,GAAE,OAAM,OAAM,MAAK,QAAO,MAAK,IAAG,IAAA,CAAI;AAAA,IAClD,oBAAC,QAAA,EAAK,GAAE,mCAAA,CAAmC;AAAA,EAAA,GAC7C;AAEJ;AAGO,SAAS,SAAS,OAAkB;AACzC,SACE,qBAAC,KAAA,EAAK,GAAG,OACP,UAAA;AAAA,IAAA,oBAAC,QAAA,EAAK,GAAE,KAAI,GAAE,QAAO,OAAM,MAAK,QAAO,MAAK,IAAG,IAAA,CAAI;AAAA,IACnD,oBAAC,QAAA,EAAK,GAAE,oCAAA,CAAoC;AAAA,IAC5C,oBAAC,UAAA,EAAO,IAAG,MAAK,IAAG,QAAO,GAAE,OAAM,MAAK,gBAAe,QAAO,OAAA,CAAO;AAAA,EAAA,GACtE;AAEJ;AAGO,SAAS,SAAS,OAAkB;AACzC,SACE,qBAAC,KAAA,EAAK,GAAG,OACP,UAAA;AAAA,IAAA,oBAAC,YAAO,IAAG,MAAK,IAAG,MAAK,GAAE,OAAM;AAAA,IAChC,oBAAC,QAAA,EAAK,GAAE,aAAA,CAAa;AAAA,IACrB,oBAAC,UAAA,EAAO,IAAG,MAAK,IAAG,OAAM,GAAE,OAAM,MAAK,gBAAe,QAAO,OAAA,CAAO;AAAA,EAAA,GACrE;AAEJ;AAEO,SAAS,WAAW,OAAkB;AAC3C,SACE,qBAAC,KAAA,EAAK,GAAG,OACP,UAAA;AAAA,IAAA,oBAAC,YAAO,IAAG,QAAO,IAAG,QAAO,GAAE,OAAM;AAAA,IACpC,oBAAC,QAAA,EAAK,GAAE,mBAAA,CAAmB;AAAA,EAAA,GAC7B;AAEJ;AAEO,SAAS,UAAU,OAAkB;AAC1C,SACE,oBAAC,OAAK,GAAG,OACP,8BAAC,QAAA,EAAK,GAAE,uBAAsB,EAAA,CAChC;AAEJ;AAEO,SAAS,gBAAgB,OAAkB;AAChD,SACE,oBAAC,OAAK,GAAG,OACP,8BAAC,QAAA,EAAK,GAAE,kBAAiB,EAAA,CAC3B;AAEJ;AAEO,SAAS,cAAc,OAAkB;AAC9C,SACE,oBAAC,OAAK,GAAG,OACP,8BAAC,QAAA,EAAK,GAAE,mBAAkB,EAAA,CAC5B;AAEJ;AAEO,SAAS,gBAAgB,OAAkB;AAChD,SACE,oBAAC,OAAK,GAAG,OACP,8BAAC,QAAA,EAAK,GAAE,oBAAmB,EAAA,CAC7B;AAEJ;AAEO,SAAS,iBAAiB,OAAkB;AACjD,SACE,oBAAC,OAAK,GAAG,OACP,8BAAC,QAAA,EAAK,GAAE,kBAAiB,EAAA,CAC3B;AAEJ;AAGO,SAAS,mBAAmB,OAAkB;AACnD,SACE,oBAAC,OAAK,GAAG,OACP,8BAAC,QAAA,EAAK,GAAE,gDAA+C,EAAA,CACzD;AAEJ;AAGO,SAAS,WAAW,OAAkB;AAC3C,SACE,oBAAC,OAAK,GAAG,OACP,8BAAC,QAAA,EAAK,GAAE,uCAAsC,EAAA,CAChD;AAEJ;AAGO,SAAS,UAAU,OAAkB;AAC1C,SACE,oBAAC,OAAK,GAAG,OACP,8BAAC,QAAA,EAAK,GAAE,kCAAiC,EAAA,CAC3C;AAEJ;AAGO,SAAS,SAAS,OAAkB;AACzC,SACE,oBAAC,OAAK,GAAG,OACP,8BAAC,QAAA,EAAK,GAAE,2BAA0B,EAAA,CACpC;AAEJ;AAGO,SAAS,WAAW,OAAkB;AAC3C,SACE,qBAAC,KAAA,EAAK,GAAG,OACP,UAAA;AAAA,IAAA,oBAAC,QAAA,EAAK,GAAE,iCAAA,CAAiC;AAAA,IACzC,oBAAC,QAAA,EAAK,GAAE,iDAAA,CAAiD;AAAA,EAAA,GAC3D;AAEJ;AAGO,SAAS,cAAc,OAAkB;AAC9C,SACE,qBAAC,KAAA,EAAK,GAAG,OACP,UAAA;AAAA,IAAA,oBAAC,QAAA,EAAK,GAAE,OAAM,GAAE,KAAI,OAAM,MAAK,QAAO,MAAK,IAAG,IAAA,CAAI;AAAA,IAClD,oBAAC,QAAA,EAAK,GAAE,wBAAA,CAAwB;AAAA,EAAA,GAClC;AAEJ;AAGO,SAAS,SAAS,OAAkB;AACzC,SACE,oBAAC,OAAK,GAAG,OACP,8BAAC,QAAA,EAAK,GAAE,mEAAkE,EAAA,CAC5E;AAEJ;AAGO,SAAS,SAAS,OAAkB;AACzC,SACE,oBAAC,KAAA,EAAI,aAAa,KAAM,GAAG,OACzB,UAAA,oBAAC,QAAA,EAAK,GAAE,0DAAA,CAA0D,EAAA,CACpE;AAEJ;AAGO,SAAS,WAAW,OAAkB;AAC3C,SACE,oBAAC,OAAK,GAAG,OACP,8BAAC,QAAA,EAAK,GAAE,+BAA8B,EAAA,CACxC;AAEJ;AAGO,SAAS,cAAc,OAAkB;AAC9C,SACE,oBAAC,OAAK,GAAG,OACP,8BAAC,QAAA,EAAK,GAAE,wCAAuC,EAAA,CACjD;AAEJ;AAGO,SAAS,kBAAkB,OAAkB;AAClD,SACE,oBAAC,OAAK,GAAG,OACP,8BAAC,QAAA,EAAK,GAAE,qJAAoJ,EAAA,CAC9J;AAEJ;AAGO,SAAS,eAAe,OAAkB;AAC/C,SACE,qBAAC,KAAA,EAAK,GAAG,OACP,UAAA;AAAA,IAAA,oBAAC,QAAA,EAAK,GAAE,oCAAA,CAAoC;AAAA,IAC5C,oBAAC,YAAO,IAAG,KAAI,IAAG,OAAM,GAAE,KAAI,MAAK,eAAA,CAAe;AAAA,IAClD,oBAAC,YAAO,IAAG,KAAI,IAAG,MAAK,GAAE,KAAI,MAAK,eAAA,CAAe;AAAA,IACjD,oBAAC,YAAO,IAAG,KAAI,IAAG,QAAO,GAAE,KAAI,MAAK,eAAA,CAAe;AAAA,EAAA,GACrD;AAEJ;AAGO,SAAS,iBAAiB,OAAkB;AACjD,SACE,oBAAC,OAAK,GAAG,OACP,8BAAC,QAAA,EAAK,GAAE,oHAAmH,EAAA,CAC7H;AAEJ;AAGO,SAAS,UAAU,OAAkB;AAC1C,SACE,oBAAC,OAAK,GAAG,OACP,8BAAC,QAAA,EAAK,GAAE,8FAA6F,EAAA,CACvG;AAEJ;AAGO,SAAS,UAAU,OAAkB;AAC1C,SACE,qBAAC,KAAA,EAAK,GAAG,OACP,UAAA;AAAA,IAAA,oBAAC,QAAA,EAAK,GAAE,OAAM,GAAE,OAAM,OAAM,MAAK,QAAO,MAAK,IAAG,IAAA,CAAI;AAAA,wBACnD,UAAA,EAAO,IAAG,KAAI,IAAG,MAAK,GAAE,OAAM;AAAA,IAC/B,oBAAC,QAAA,EAAK,GAAE,8BAAA,CAA8B;AAAA,EAAA,GACxC;AAEJ;AAGO,SAAS,aAAa,OAAkB;AAC7C,SACE,oBAAC,OAAK,GAAG,OACP,8BAAC,QAAA,EAAK,GAAE,iEAAgE,EAAA,CAC1E;AAEJ;AAGO,SAAS,aAAa,OAAkB;AAC7C,SACE,oBAAC,OAAK,GAAG,OACP,8BAAC,QAAA,EAAK,GAAE,oDAAmD,EAAA,CAC7D;AAEJ;AAGO,SAAS,QAAQ,OAAkB;AACxC,SACE,qBAAC,KAAA,EAAK,GAAG,OACP,UAAA;AAAA,IAAA,oBAAC,YAAO,IAAG,MAAK,IAAG,MAAK,GAAE,KAAI;AAAA,IAC9B,oBAAC,QAAA,EAAK,GAAE,8FAAA,CAA8F;AAAA,EAAA,GACxG;AAEJ;AAGO,SAAS,SAAS,OAAkB;AACzC,SACE,oBAAC,OAAK,GAAG,OACP,8BAAC,QAAA,EAAK,GAAE,iDAAgD,EAAA,CAC1D;AAEJ;AAGO,SAAS,YAAY,OAAkB;AAC5C,SACE,qBAAC,KAAA,EAAK,GAAG,OACP,UAAA;AAAA,IAAA,oBAAC,QAAA,EAAK,GAAE,KAAI,GAAE,KAAI,OAAM,MAAK,QAAO,QAAO,IAAG,IAAA,CAAI;AAAA,IAClD,oBAAC,QAAA,EAAK,GAAE,wBAAA,CAAwB;AAAA,EAAA,GAClC;AAEJ;AAGO,SAAS,UAAU,OAAkB;AAC1C,SACE,qBAAC,KAAA,EAAK,GAAG,OACP,UAAA;AAAA,IAAA,oBAAC,YAAO,IAAG,MAAK,IAAG,MAAK,GAAE,KAAI;AAAA,IAC9B,oBAAC,QAAA,EAAK,GAAE,uDAAA,CAAuD;AAAA,EAAA,GACjE;AAEJ;AAGO,SAAS,WAAW,OAAkB;AAC3C,SACE,oBAAC,OAAK,GAAG,OACP,8BAAC,QAAA,EAAK,GAAE,sFAAqF,EAAA,CAC/F;AAEJ;AAGO,SAAS,iBAAiB,OAAkB;AACjD,SACE,oBAAC,OAAK,GAAG,OACP,8BAAC,QAAA,EAAK,GAAE,oFAAmF,EAAA,CAC7F;AAEJ;AAGO,SAAS,YAAY,OAAkB;AAC5C,SACE,qBAAC,KAAA,EAAK,GAAG,OACP,UAAA;AAAA,IAAA,oBAAC,QAAA,EAAK,GAAE,qFAAA,CAAqF;AAAA,IAC7F,oBAAC,QAAA,EAAK,GAAE,cAAA,CAAc;AAAA,IACtB,oBAAC,UAAA,EAAO,IAAG,MAAK,IAAG,MAAK,GAAE,OAAM,MAAK,gBAAe,QAAO,OAAA,CAAO;AAAA,EAAA,GACpE;AAEJ;AAGO,SAAS,UAAU,OAAkB;AAC1C,SACE,qBAAC,KAAA,EAAK,GAAG,OACP,UAAA;AAAA,IAAA,oBAAC,YAAO,IAAG,MAAK,IAAG,MAAK,GAAE,OAAM;AAAA,IAChC,oBAAC,QAAA,EAAK,GAAE,qBAAA,CAAqB;AAAA,EAAA,GAC/B;AAEJ;AAGO,SAAS,YAAY,OAAkB;AAC5C,SACE,qBAAC,KAAA,EAAK,GAAG,OACP,UAAA;AAAA,IAAA,oBAAC,QAAA,EAAK,GAAE,iCAAA,CAAiC;AAAA,wBACxC,UAAA,EAAO,IAAG,MAAK,IAAG,KAAI,GAAE,KAAI;AAAA,wBAC5B,UAAA,EAAO,IAAG,KAAI,IAAG,MAAK,GAAE,IAAA,CAAI;AAAA,EAAA,GAC/B;AAEJ;"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useMemo } from "react";
|
|
3
|
+
import { sanitizeRichText, richTextContentClasses } from "./utils.js";
|
|
4
|
+
function RichText({ value, className = "", ...props }) {
|
|
5
|
+
const html = useMemo(() => sanitizeRichText(value), [value]);
|
|
6
|
+
return /* @__PURE__ */ jsx("div", { className: `${richTextContentClasses} ${className}`, dangerouslySetInnerHTML: { __html: html }, ...props });
|
|
7
|
+
}
|
|
8
|
+
export {
|
|
9
|
+
RichText,
|
|
10
|
+
RichText as default
|
|
11
|
+
};
|
|
12
|
+
//# sourceMappingURL=RichText.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RichText.js","sources":["../../../../../../src/v1/primitives/display/RichText/RichText.tsx"],"sourcesContent":["import React, { useMemo } from 'react'\r\nimport { richTextContentClasses, sanitizeRichText } from './utils'\r\n\r\nexport interface RichTextProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'children' | 'dangerouslySetInnerHTML'> {\r\n /** O texto: HTML do RichTextEditor ou texto simples (antigo, sem formatação). */\r\n value: string | null | undefined\r\n}\r\n\r\n/**\r\n * Mostra um texto com formatação (negrito, listas, citações, ligações, imagens) escrito no\r\n * [RichTextEditor](?path=/docs/v1-primitives-forms-richtexteditor--docs).\r\n *\r\n * O HTML é sempre limpo antes de entrar na página: só ficam as marcas de formatação (sem\r\n * scripts, estilos nem eventos) e as ligações abrem noutro separador. Texto sem marcas\r\n * (guardado antes do editor) aparece com as mudanças de linha.\r\n *\r\n * Não tem cor própria: herda a de quem o põe (ex: branco numa bolha navy de uma conversa).\r\n */\r\nexport function RichText({ value, className = '', ...props }: RichTextProps) {\r\n const html = useMemo(() => sanitizeRichText(value), [value])\r\n return <div className={`${richTextContentClasses} ${className}`} dangerouslySetInnerHTML={{ __html: html }} {...props} />\r\n}\r\n\r\nexport default RichText\r\n"],"names":[],"mappings":";;;AAkBO,SAAS,SAAS,EAAE,OAAO,YAAY,IAAI,GAAG,SAAwB;AAC3E,QAAM,OAAO,QAAQ,MAAM,iBAAiB,KAAK,GAAG,CAAC,KAAK,CAAC;AAC3D,SAAO,oBAAC,OAAA,EAAI,WAAW,GAAG,sBAAsB,IAAI,SAAS,IAAI,yBAAyB,EAAE,QAAQ,KAAA,GAAS,GAAG,OAAO;AACzH;"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import DOMPurify from "dompurify";
|
|
2
|
+
const richTextContentClasses = "break-words [&_p]:my-0 [&_p+p]:mt-1.5 [&_ul]:my-1 [&_ul]:list-disc [&_ul]:pl-5 [&_ol]:my-1 [&_ol]:list-decimal [&_ol]:pl-5 [&_li>p]:my-0 [&_blockquote]:my-1.5 [&_blockquote]:border-l-2 [&_blockquote]:border-current/30 [&_blockquote]:pl-3 [&_blockquote]:opacity-80 [&_a]:underline [&_a]:underline-offset-2 [&_u]:underline [&_s]:line-through [&_code]:rounded [&_code]:bg-current/10 [&_code]:px-1 [&_code]:text-[0.92em] [&_img]:my-2 [&_img]:max-w-full [&_img]:rounded-lg";
|
|
3
|
+
const ALLOWED_TAGS = ["p", "br", "strong", "b", "em", "i", "u", "s", "strike", "del", "ul", "ol", "li", "blockquote", "a", "img", "h1", "h2", "h3", "code", "pre", "hr"];
|
|
4
|
+
const ALLOWED_ATTR = ["href", "src", "alt", "title", "target", "rel", "start"];
|
|
5
|
+
let purifier = null;
|
|
6
|
+
function getPurifier() {
|
|
7
|
+
if (purifier) return purifier;
|
|
8
|
+
purifier = DOMPurify(window);
|
|
9
|
+
purifier.addHook("afterSanitizeAttributes", (node) => {
|
|
10
|
+
if (node.tagName === "A" && node.getAttribute("href")) {
|
|
11
|
+
node.setAttribute("target", "_blank");
|
|
12
|
+
node.setAttribute("rel", "noopener noreferrer");
|
|
13
|
+
}
|
|
14
|
+
});
|
|
15
|
+
return purifier;
|
|
16
|
+
}
|
|
17
|
+
function isRichText(value) {
|
|
18
|
+
return !!value && /<\/?[a-z][\s\S]*?>/i.test(value);
|
|
19
|
+
}
|
|
20
|
+
function escapeHtml(text) {
|
|
21
|
+
return text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
22
|
+
}
|
|
23
|
+
function textToRichText(text) {
|
|
24
|
+
if (!text) return "";
|
|
25
|
+
return text.split(/\r?\n/).map((line) => `<p>${escapeHtml(line)}</p>`).join("");
|
|
26
|
+
}
|
|
27
|
+
function sanitizeRichText(value) {
|
|
28
|
+
if (!value) return "";
|
|
29
|
+
const html = isRichText(value) ? value : textToRichText(value);
|
|
30
|
+
return getPurifier().sanitize(html, { ALLOWED_TAGS, ALLOWED_ATTR });
|
|
31
|
+
}
|
|
32
|
+
function richTextToPlain(value) {
|
|
33
|
+
if (!value) return "";
|
|
34
|
+
if (!isRichText(value)) return value;
|
|
35
|
+
const doc = new DOMParser().parseFromString(sanitizeRichText(value), "text/html");
|
|
36
|
+
doc.querySelectorAll("br").forEach((br) => br.replaceWith("\n"));
|
|
37
|
+
doc.querySelectorAll("p, li, blockquote, h1, h2, h3, pre").forEach((el) => {
|
|
38
|
+
if (el.tagName !== "P" && el.querySelector(":scope > p")) return;
|
|
39
|
+
el.append("\n");
|
|
40
|
+
});
|
|
41
|
+
return (doc.body.textContent ?? "").replace(/\n{3,}/g, "\n\n").trim();
|
|
42
|
+
}
|
|
43
|
+
function richTextIsEmpty(value) {
|
|
44
|
+
if (!value) return true;
|
|
45
|
+
if (!isRichText(value)) return !value.trim();
|
|
46
|
+
return !richTextToPlain(value) && !/<img\b/i.test(value);
|
|
47
|
+
}
|
|
48
|
+
export {
|
|
49
|
+
isRichText,
|
|
50
|
+
richTextContentClasses,
|
|
51
|
+
richTextIsEmpty,
|
|
52
|
+
richTextToPlain,
|
|
53
|
+
sanitizeRichText,
|
|
54
|
+
textToRichText
|
|
55
|
+
};
|
|
56
|
+
//# sourceMappingURL=utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.js","sources":["../../../../../../src/v1/primitives/display/RichText/utils.ts"],"sourcesContent":["import DOMPurify, { type DOMPurify as Purifier } from 'dompurify'\r\n\r\n/**\r\n * Texto com formatação (HTML do [RichTextEditor](?path=/docs/v1-primitives-forms-richtexteditor--docs)):\r\n * as classes do conteúdo e as funções para o guardar, ler e mostrar em segurança.\r\n */\r\n\r\n/**\r\n * Aspeto do conteúdo (listas, citações, ligações, imagens). Sem cor de texto: herda a de quem o põe\r\n * (ex: branco numa bolha navy de conversa).\r\n */\r\nexport const richTextContentClasses =\r\n 'break-words [&_p]:my-0 [&_p+p]:mt-1.5 ' +\r\n '[&_ul]:my-1 [&_ul]:list-disc [&_ul]:pl-5 [&_ol]:my-1 [&_ol]:list-decimal [&_ol]:pl-5 [&_li>p]:my-0 ' +\r\n '[&_blockquote]:my-1.5 [&_blockquote]:border-l-2 [&_blockquote]:border-current/30 [&_blockquote]:pl-3 [&_blockquote]:opacity-80 ' +\r\n '[&_a]:underline [&_a]:underline-offset-2 [&_u]:underline [&_s]:line-through ' +\r\n '[&_code]:rounded [&_code]:bg-current/10 [&_code]:px-1 [&_code]:text-[0.92em] ' +\r\n '[&_img]:my-2 [&_img]:max-w-full [&_img]:rounded-lg'\r\n\r\n// Só o que o editor produz. Tudo o resto (script, style, iframe, on*, javascript:…) sai.\r\nconst ALLOWED_TAGS = ['p', 'br', 'strong', 'b', 'em', 'i', 'u', 's', 'strike', 'del', 'ul', 'ol', 'li', 'blockquote', 'a', 'img', 'h1', 'h2', 'h3', 'code', 'pre', 'hr']\r\nconst ALLOWED_ATTR = ['href', 'src', 'alt', 'title', 'target', 'rel', 'start']\r\n\r\nlet purifier: Purifier | null = null\r\n\r\n/** Instância própria do DOMPurify: o hook das ligações não mexe no DOMPurify global da app. */\r\nfunction getPurifier(): Purifier {\r\n if (purifier) return purifier\r\n purifier = DOMPurify(window)\r\n purifier.addHook('afterSanitizeAttributes', (node) => {\r\n // Ligações abrem noutro separador e sem acesso a esta janela.\r\n if (node.tagName === 'A' && node.getAttribute('href')) {\r\n node.setAttribute('target', '_blank')\r\n node.setAttribute('rel', 'noopener noreferrer')\r\n }\r\n })\r\n return purifier\r\n}\r\n\r\n/** Parece HTML (tem pelo menos uma marca)? Textos antigos, guardados sem formatação, não. */\r\nexport function isRichText(value: string | null | undefined): boolean {\r\n return !!value && /<\\/?[a-z][\\s\\S]*?>/i.test(value)\r\n}\r\n\r\nfunction escapeHtml(text: string): string {\r\n return text.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/\"/g, '"')\r\n}\r\n\r\n/** Texto simples → HTML: um parágrafo por linha (as linhas em branco ficam). */\r\nexport function textToRichText(text: string): string {\r\n if (!text) return ''\r\n return text\r\n .split(/\\r?\\n/)\r\n .map((line) => `<p>${escapeHtml(line)}</p>`)\r\n .join('')\r\n}\r\n\r\n/**\r\n * HTML seguro para pôr na página: só as marcas de formatação, sem scripts nem atributos de eventos.\r\n * Texto sem marcas é convertido (uma linha, um parágrafo). Usar sempre antes de `dangerouslySetInnerHTML`.\r\n */\r\nexport function sanitizeRichText(value: string | null | undefined): string {\r\n if (!value) return ''\r\n const html = isRichText(value) ? value : textToRichText(value)\r\n return getPurifier().sanitize(html, { ALLOWED_TAGS, ALLOWED_ATTR })\r\n}\r\n\r\n/** Só o texto (ex: pré-visualização numa lista, notificação, pesquisa). Parágrafos e itens viram linhas. */\r\nexport function richTextToPlain(value: string | null | undefined): string {\r\n if (!value) return ''\r\n if (!isRichText(value)) return value\r\n const doc = new DOMParser().parseFromString(sanitizeRichText(value), 'text/html')\r\n doc.querySelectorAll('br').forEach((br) => br.replaceWith('\\n'))\r\n // Um item ou citação com parágrafos lá dentro já muda de linha pelo parágrafo.\r\n doc.querySelectorAll('p, li, blockquote, h1, h2, h3, pre').forEach((el) => {\r\n if (el.tagName !== 'P' && el.querySelector(':scope > p')) return\r\n el.append('\\n')\r\n })\r\n return (doc.body.textContent ?? '').replace(/\\n{3,}/g, '\\n\\n').trim()\r\n}\r\n\r\n/** Sem texto nem imagens (ex: `<p></p>` de um editor vazio)? Para validar \"obrigatório\". */\r\nexport function richTextIsEmpty(value: string | null | undefined): boolean {\r\n if (!value) return true\r\n if (!isRichText(value)) return !value.trim()\r\n return !richTextToPlain(value) && !/<img\\b/i.test(value)\r\n}\r\n"],"names":[],"mappings":";AAWO,MAAM,yBACX;AAQF,MAAM,eAAe,CAAC,KAAK,MAAM,UAAU,KAAK,MAAM,KAAK,KAAK,KAAK,UAAU,OAAO,MAAM,MAAM,MAAM,cAAc,KAAK,OAAO,MAAM,MAAM,MAAM,QAAQ,OAAO,IAAI;AACvK,MAAM,eAAe,CAAC,QAAQ,OAAO,OAAO,SAAS,UAAU,OAAO,OAAO;AAE7E,IAAI,WAA4B;AAGhC,SAAS,cAAwB;AAC/B,MAAI,SAAU,QAAO;AACrB,aAAW,UAAU,MAAM;AAC3B,WAAS,QAAQ,2BAA2B,CAAC,SAAS;AAEpD,QAAI,KAAK,YAAY,OAAO,KAAK,aAAa,MAAM,GAAG;AACrD,WAAK,aAAa,UAAU,QAAQ;AACpC,WAAK,aAAa,OAAO,qBAAqB;AAAA,IAChD;AAAA,EACF,CAAC;AACD,SAAO;AACT;AAGO,SAAS,WAAW,OAA2C;AACpE,SAAO,CAAC,CAAC,SAAS,sBAAsB,KAAK,KAAK;AACpD;AAEA,SAAS,WAAW,MAAsB;AACxC,SAAO,KAAK,QAAQ,MAAM,OAAO,EAAE,QAAQ,MAAM,MAAM,EAAE,QAAQ,MAAM,MAAM,EAAE,QAAQ,MAAM,QAAQ;AACvG;AAGO,SAAS,eAAe,MAAsB;AACnD,MAAI,CAAC,KAAM,QAAO;AAClB,SAAO,KACJ,MAAM,OAAO,EACb,IAAI,CAAC,SAAS,MAAM,WAAW,IAAI,CAAC,MAAM,EAC1C,KAAK,EAAE;AACZ;AAMO,SAAS,iBAAiB,OAA0C;AACzE,MAAI,CAAC,MAAO,QAAO;AACnB,QAAM,OAAO,WAAW,KAAK,IAAI,QAAQ,eAAe,KAAK;AAC7D,SAAO,cAAc,SAAS,MAAM,EAAE,cAAc,cAAc;AACpE;AAGO,SAAS,gBAAgB,OAA0C;AACxE,MAAI,CAAC,MAAO,QAAO;AACnB,MAAI,CAAC,WAAW,KAAK,EAAG,QAAO;AAC/B,QAAM,MAAM,IAAI,UAAA,EAAY,gBAAgB,iBAAiB,KAAK,GAAG,WAAW;AAChF,MAAI,iBAAiB,IAAI,EAAE,QAAQ,CAAC,OAAO,GAAG,YAAY,IAAI,CAAC;AAE/D,MAAI,iBAAiB,oCAAoC,EAAE,QAAQ,CAAC,OAAO;AACzE,QAAI,GAAG,YAAY,OAAO,GAAG,cAAc,YAAY,EAAG;AAC1D,OAAG,OAAO,IAAI;AAAA,EAChB,CAAC;AACD,UAAQ,IAAI,KAAK,eAAe,IAAI,QAAQ,WAAW,MAAM,EAAE,KAAA;AACjE;AAGO,SAAS,gBAAgB,OAA2C;AACzE,MAAI,CAAC,MAAO,QAAO;AACnB,MAAI,CAAC,WAAW,KAAK,EAAG,QAAO,CAAC,MAAM,KAAA;AACtC,SAAO,CAAC,gBAAgB,KAAK,KAAK,CAAC,UAAU,KAAK,KAAK;AACzD;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PdfPreview.js","sources":["../../../../../../src/v1/primitives/documents/PdfPreview/PdfPreview.tsx"],"sourcesContent":["import React, { useCallback, useEffect, useRef, useState } from 'react'\r\nimport type { PDFDocumentProxy } from 'pdfjs-dist'\r\nimport { loadPdfjs } from './pdfjs'\r\n\r\nexport interface PdfPreviewProps {\r\n file: File\r\n /** Texto alternativo das páginas e mensagem quando o PDF não abre. */\r\n label: string\r\n /** Imagem da 1.ª página já renderizada pela app, para aparecer de imediato. */\r\n initialFirstPage?: string\r\n className?: string\r\n}\r\n\r\n// Proporção A4 vertical, usada até se saber o tamanho real da página.\r\nconst DEFAULT_ASPECT = 0.707\r\n\r\nasync function renderPage(pdf: PDFDocumentProxy, pageNum: number, maxSide = 1200): Promise<string> {\r\n const page = await pdf.getPage(pageNum)\r\n const base = page.getViewport({ scale: 1 })\r\n const scale = Math.min(3, Math.max(1, maxSide / Math.max(base.width, base.height)))\r\n const viewport = page.getViewport({ scale })\r\n const canvas = document.createElement('canvas')\r\n canvas.width = Math.ceil(viewport.width)\r\n canvas.height = Math.ceil(viewport.height)\r\n const ctx = canvas.getContext('2d')!\r\n ctx.fillStyle = '#fff'\r\n ctx.fillRect(0, 0, canvas.width, canvas.height)\r\n await page.render({ canvasContext: ctx, viewport }).promise\r\n return canvas.toDataURL('image/jpeg', 0.85)\r\n}\r\n\r\n/**\r\n * Pré-visualização de PDF com pdf.js: as páginas em coluna, com scroll vertical\r\n * e sem a barra de ferramentas do visualizador nativo (que transborda em caixas\r\n * estreitas). Cada página só é renderizada quando se aproxima da área visível.\r\n *\r\n * **Usado por:** [DocumentDropzone](?path=/docs/v1-components-documents-documentdropzone--docs) (quando o ficheiro é um PDF).\r\n */\r\nexport default function PdfPreview({ file, label, initialFirstPage, className = '' }: PdfPreviewProps) {\r\n const [pdf, setPdf] = useState<PDFDocumentProxy | null>(null)\r\n const [numPages, setNumPages] = useState(0)\r\n const [aspect, setAspect] = useState<number | null>(null)\r\n const [imgByPage, setImgByPage] = useState<Record<number, string>>(initialFirstPage ? { 1: initialFirstPage } : {})\r\n const [failed, setFailed] = useState(false)\r\n\r\n const scrollRef = useRef<HTMLDivElement>(null)\r\n const slotRefs = useRef<(HTMLDivElement | null)[]>([])\r\n const requested = useRef<Set<number>>(new Set(initialFirstPage ? [1] : []))\r\n\r\n useEffect(() => {\r\n let alive = true\r\n let opened: PDFDocumentProxy | null = null\r\n let idleId = 0\r\n setPdf(null)\r\n setNumPages(0)\r\n setAspect(null)\r\n setFailed(false)\r\n // Não semear a página 1 com initialFirstPage aqui: ao mudar de ficheiro, a\r\n // prop ainda traz a imagem do anterior. O efeito seguinte adota-a quando chegar.\r\n setImgByPage({})\r\n requested.current = new Set()\r\n slotRefs.current = []\r\n\r\n const open = async () => {\r\n try {\r\n const { getDocument } = await loadPdfjs()\r\n const data = new Uint8Array(await file.arrayBuffer())\r\n const doc = await getDocument({ data }).promise\r\n if (!alive) {\r\n doc.destroy()\r\n return\r\n }\r\n opened = doc\r\n setPdf(doc)\r\n setNumPages(doc.numPages)\r\n const { width, height } = (await doc.getPage(1)).getViewport({ scale: 1 })\r\n if (alive && height > 0) setAspect(width / height)\r\n } catch {\r\n if (alive) setFailed(true)\r\n }\r\n }\r\n\r\n // O parse corre na thread principal: espera que a animação de entrada acabe\r\n // e que a página fique livre, com um limite para nunca ficar parado.\r\n const timer = window.setTimeout(() => {\r\n if (typeof requestIdleCallback === 'function') idleId = requestIdleCallback(open, { timeout: 1200 })\r\n else open()\r\n }, 500)\r\n\r\n return () => {\r\n alive = false\r\n opened?.destroy()\r\n window.clearTimeout(timer)\r\n if (idleId && typeof cancelIdleCallback === 'function') cancelIdleCallback(idleId)\r\n }\r\n }, [file])\r\n\r\n useEffect(() => {\r\n if (!initialFirstPage) return\r\n requested.current.add(1)\r\n setImgByPage((cur) => (cur[1] ? cur : { ...cur, 1: initialFirstPage }))\r\n }, [initialFirstPage])\r\n\r\n const requestPage = useCallback(\r\n (p: number) => {\r\n if (!pdf || requested.current.has(p)) return\r\n requested.current.add(p)\r\n renderPage(pdf, p)\r\n .then((url) => setImgByPage((cur) => ({ ...cur, [p]: url })))\r\n .catch(() => {\r\n requested.current.delete(p)\r\n })\r\n },\r\n [pdf],\r\n )\r\n\r\n useEffect(() => {\r\n if (!pdf || !numPages) return\r\n const io = new IntersectionObserver(\r\n (entries) => {\r\n for (const e of entries) {\r\n if (!e.isIntersecting) continue\r\n requestPage(Number((e.target as HTMLElement).dataset.page))\r\n io.unobserve(e.target)\r\n }\r\n },\r\n { root: scrollRef.current, rootMargin: '400px 0px' },\r\n )\r\n slotRefs.current.forEach((el) => el && io.observe(el))\r\n return () => io.disconnect()\r\n }, [pdf, numPages, requestPage])\r\n\r\n const count = numPages || 1\r\n const slotAspect = aspect ?? DEFAULT_ASPECT\r\n\r\n if (failed) {\r\n return (\r\n <div className={`flex h-full w-full flex-col items-center justify-center gap-2.5 bg-card text-muted-foreground ${className}`}>\r\n <svg width=\"44\" height=\"44\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.6\" aria-hidden>\r\n <path d=\"M14 2H7a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V7z\" strokeLinecap=\"round\" strokeLinejoin=\"round\" />\r\n <path d=\"M14 2v5h5\" strokeLinecap=\"round\" strokeLinejoin=\"round\" />\r\n </svg>\r\n <span className=\"text-xs\">{label}</span>\r\n </div>\r\n )\r\n }\r\n\r\n return (\r\n <div\r\n ref={scrollRef}\r\n className={`flex h-full min-h-0 w-full flex-col items-center gap-3.5 overflow-x-hidden overflow-y-auto bg-card px-4 pt-3.5 pb-[18px] [&::-webkit-scrollbar]:w-2.5 [&::-webkit-scrollbar-track]:bg-transparent [&::-webkit-scrollbar-thumb]:rounded-full [&::-webkit-scrollbar-thumb]:border-2 [&::-webkit-scrollbar-thumb]:border-solid [&::-webkit-scrollbar-thumb]:border-transparent [&::-webkit-scrollbar-thumb]:bg-scrollbar [&::-webkit-scrollbar-thumb]:bg-clip-padding [&::-webkit-scrollbar-thumb:hover]:bg-scrollbar-hover ${className}`}\r\n >\r\n {Array.from({ length: count }, (_, i) => {\r\n const p = i + 1\r\n const url = imgByPage[p]\r\n return (\r\n <div\r\n key={p}\r\n ref={(el) => {\r\n slotRefs.current[i] = el\r\n }}\r\n data-page={p}\r\n style={{ aspectRatio: String(slotAspect) }}\r\n className=\"flex w-full flex-none items-center justify-center overflow-hidden rounded border border-border bg-white shadow-page\"\r\n >\r\n {url ? (\r\n <img src={url} alt={`${label} — ${p}/${count}`} className=\"block h-full w-full object-contain\" />\r\n ) : (\r\n <span role=\"status\" aria-label={label} className=\"inline-block h-[22px] w-[22px] animate-spin rounded-full border-[2.5px] border-primary/20 border-t-primary\" />\r\n )}\r\n </div>\r\n )\r\n })}\r\n </div>\r\n )\r\n}\r\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"PdfPreview.js","sources":["../../../../../../src/v1/primitives/documents/PdfPreview/PdfPreview.tsx"],"sourcesContent":["import React, { useCallback, useEffect, useRef, useState } from 'react'\nimport type { PDFDocumentProxy } from 'pdfjs-dist'\nimport { loadPdfjs } from './pdfjs'\n\nexport interface PdfPreviewProps {\n file: File\n /** Texto alternativo das páginas e mensagem quando o PDF não abre. */\n label: string\n /** Imagem da 1.ª página já renderizada pela app, para aparecer de imediato. */\n initialFirstPage?: string\n className?: string\n}\n\n// Proporção A4 vertical, usada até se saber o tamanho real da página.\nconst DEFAULT_ASPECT = 0.707\n\nasync function renderPage(pdf: PDFDocumentProxy, pageNum: number, maxSide = 1200): Promise<string> {\n const page = await pdf.getPage(pageNum)\n const base = page.getViewport({ scale: 1 })\n const scale = Math.min(3, Math.max(1, maxSide / Math.max(base.width, base.height)))\n const viewport = page.getViewport({ scale })\n const canvas = document.createElement('canvas')\n canvas.width = Math.ceil(viewport.width)\n canvas.height = Math.ceil(viewport.height)\n const ctx = canvas.getContext('2d')!\n ctx.fillStyle = '#fff'\n ctx.fillRect(0, 0, canvas.width, canvas.height)\n await page.render({ canvasContext: ctx, viewport }).promise\n return canvas.toDataURL('image/jpeg', 0.85)\n}\n\n/**\n * Pré-visualização de PDF com pdf.js: as páginas em coluna, com scroll vertical\n * e sem a barra de ferramentas do visualizador nativo (que transborda em caixas\n * estreitas). Cada página só é renderizada quando se aproxima da área visível.\n *\n * **Usado por:** [DocumentDropzone](?path=/docs/v1-components-documents-documentdropzone--docs) (quando o ficheiro é um PDF).\n */\nexport default function PdfPreview({ file, label, initialFirstPage, className = '' }: PdfPreviewProps) {\n const [pdf, setPdf] = useState<PDFDocumentProxy | null>(null)\n const [numPages, setNumPages] = useState(0)\n const [aspect, setAspect] = useState<number | null>(null)\n const [imgByPage, setImgByPage] = useState<Record<number, string>>(initialFirstPage ? { 1: initialFirstPage } : {})\n const [failed, setFailed] = useState(false)\n\n const scrollRef = useRef<HTMLDivElement>(null)\n const slotRefs = useRef<(HTMLDivElement | null)[]>([])\n const requested = useRef<Set<number>>(new Set(initialFirstPage ? [1] : []))\n\n useEffect(() => {\n let alive = true\n let opened: PDFDocumentProxy | null = null\n let idleId = 0\n setPdf(null)\n setNumPages(0)\n setAspect(null)\n setFailed(false)\n // Não semear a página 1 com initialFirstPage aqui: ao mudar de ficheiro, a\n // prop ainda traz a imagem do anterior. O efeito seguinte adota-a quando chegar.\n setImgByPage({})\n requested.current = new Set()\n slotRefs.current = []\n\n const open = async () => {\n try {\n const { getDocument } = await loadPdfjs()\n const data = new Uint8Array(await file.arrayBuffer())\n const doc = await getDocument({ data }).promise\n if (!alive) {\n doc.destroy()\n return\n }\n opened = doc\n setPdf(doc)\n setNumPages(doc.numPages)\n const { width, height } = (await doc.getPage(1)).getViewport({ scale: 1 })\n if (alive && height > 0) setAspect(width / height)\n } catch {\n if (alive) setFailed(true)\n }\n }\n\n // O parse corre na thread principal: espera que a animação de entrada acabe\n // e que a página fique livre, com um limite para nunca ficar parado.\n const timer = window.setTimeout(() => {\n if (typeof requestIdleCallback === 'function') idleId = requestIdleCallback(open, { timeout: 1200 })\n else open()\n }, 500)\n\n return () => {\n alive = false\n opened?.destroy()\n window.clearTimeout(timer)\n if (idleId && typeof cancelIdleCallback === 'function') cancelIdleCallback(idleId)\n }\n }, [file])\n\n useEffect(() => {\n if (!initialFirstPage) return\n requested.current.add(1)\n setImgByPage((cur) => (cur[1] ? cur : { ...cur, 1: initialFirstPage }))\n }, [initialFirstPage])\n\n const requestPage = useCallback(\n (p: number) => {\n if (!pdf || requested.current.has(p)) return\n requested.current.add(p)\n renderPage(pdf, p)\n .then((url) => setImgByPage((cur) => ({ ...cur, [p]: url })))\n .catch(() => {\n requested.current.delete(p)\n })\n },\n [pdf],\n )\n\n useEffect(() => {\n if (!pdf || !numPages) return\n const io = new IntersectionObserver(\n (entries) => {\n for (const e of entries) {\n if (!e.isIntersecting) continue\n requestPage(Number((e.target as HTMLElement).dataset.page))\n io.unobserve(e.target)\n }\n },\n { root: scrollRef.current, rootMargin: '400px 0px' },\n )\n slotRefs.current.forEach((el) => el && io.observe(el))\n return () => io.disconnect()\n }, [pdf, numPages, requestPage])\n\n const count = numPages || 1\n const slotAspect = aspect ?? DEFAULT_ASPECT\n\n if (failed) {\n return (\n <div className={`flex h-full w-full flex-col items-center justify-center gap-2.5 bg-card text-muted-foreground ${className}`}>\n <svg width=\"44\" height=\"44\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.6\" aria-hidden>\n <path d=\"M14 2H7a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V7z\" strokeLinecap=\"round\" strokeLinejoin=\"round\" />\n <path d=\"M14 2v5h5\" strokeLinecap=\"round\" strokeLinejoin=\"round\" />\n </svg>\n <span className=\"text-xs\">{label}</span>\n </div>\n )\n }\n\n return (\n <div\n ref={scrollRef}\n className={`flex h-full min-h-0 w-full flex-col items-center gap-3.5 overflow-x-hidden overflow-y-auto bg-card px-4 pt-3.5 pb-[18px] [&::-webkit-scrollbar]:w-2.5 [&::-webkit-scrollbar-track]:bg-transparent [&::-webkit-scrollbar-thumb]:rounded-full [&::-webkit-scrollbar-thumb]:border-2 [&::-webkit-scrollbar-thumb]:border-solid [&::-webkit-scrollbar-thumb]:border-transparent [&::-webkit-scrollbar-thumb]:bg-scrollbar [&::-webkit-scrollbar-thumb]:bg-clip-padding [&::-webkit-scrollbar-thumb:hover]:bg-scrollbar-hover ${className}`}\n >\n {Array.from({ length: count }, (_, i) => {\n const p = i + 1\n const url = imgByPage[p]\n return (\n <div\n key={p}\n ref={(el) => {\n slotRefs.current[i] = el\n }}\n data-page={p}\n style={{ aspectRatio: String(slotAspect) }}\n className=\"flex w-full flex-none items-center justify-center overflow-hidden rounded border border-border bg-white shadow-page\"\n >\n {url ? (\n <img src={url} alt={`${label} — ${p}/${count}`} className=\"block h-full w-full object-contain\" />\n ) : (\n <span role=\"status\" aria-label={label} className=\"inline-block h-[22px] w-[22px] animate-spin rounded-full border-[2.5px] border-primary/20 border-t-primary\" />\n )}\n </div>\n )\n })}\n </div>\n )\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pdfjs.js","sources":["../../../../../../src/v1/primitives/documents/PdfPreview/pdfjs.ts"],"sourcesContent":["import type * as PdfJs from 'pdfjs-dist'\
|
|
1
|
+
{"version":3,"file":"pdfjs.js","sources":["../../../../../../src/v1/primitives/documents/PdfPreview/pdfjs.ts"],"sourcesContent":["import type * as PdfJs from 'pdfjs-dist'\n\ntype PdfJsModule = typeof PdfJs\n\nlet workerSrc: string | null = null\nlet loaded: PdfJsModule | null = null\nlet loading: Promise<PdfJsModule> | null = null\n\n/**\n * Indica ao pdf.js onde está o worker (`pdf.worker.min.mjs`) servido pela app.\n * Chamar uma vez no arranque. Se a app já define `GlobalWorkerOptions.workerSrc`\n * pela sua própria cópia do pdf.js (a mesma, quando deduplicada), não é preciso.\n */\nexport function configurePdfWorker(src: string) {\n workerSrc = src\n if (loaded) loaded.GlobalWorkerOptions.workerSrc = src\n}\n\n/** Carrega o pdf.js só quando há um PDF para mostrar; depois fica em cache. */\nexport function loadPdfjs(): Promise<PdfJsModule> {\n if (!loading) {\n loading = import('pdfjs-dist')\n .then((pdfjs) => {\n // Salvaguarda: se o pdf.js não chegar (ex: ambiente sem ele), cai no ícone.\n if (typeof pdfjs.getDocument !== 'function') throw new Error('pdfjs-dist indisponível')\n if (workerSrc) pdfjs.GlobalWorkerOptions.workerSrc = workerSrc\n loaded = pdfjs\n return pdfjs\n })\n .catch((e) => {\n loading = null\n throw e\n })\n }\n return loading\n}\n"],"names":[],"mappings":"AAIA,IAAI,YAA2B;AAC/B,IAAI,SAA6B;AACjC,IAAI,UAAuC;AAOpC,SAAS,mBAAmB,KAAa;AAC9C,cAAY;AACZ,MAAI,OAAQ,QAAO,oBAAoB,YAAY;AACrD;AAGO,SAAS,YAAkC;AAChD,MAAI,CAAC,SAAS;AACZ,cAAU,OAAO,YAAY,EAC1B,KAAK,CAAC,UAAU;AAEf,UAAI,OAAO,MAAM,gBAAgB,WAAY,OAAM,IAAI,MAAM,yBAAyB;AACtF,UAAI,UAAW,OAAM,oBAAoB,YAAY;AACrD,eAAS;AACT,aAAO;AAAA,IACT,CAAC,EACA,MAAM,CAAC,MAAM;AACZ,gBAAU;AACV,YAAM;AAAA,IACR,CAAC;AAAA,EACL;AACA,SAAO;AACT;"}
|