@proef/common-ui 2.2.3 → 2.2.4

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.
@@ -106,13 +106,13 @@ function Estado({
106
106
  if (vazio) return /* @__PURE__ */ jsx("div", { className: "py-10 text-center text-sm text-slate-400", children: labels?.empty ?? "No data" });
107
107
  return null;
108
108
  }
109
- function Modal({ open, onClose, title, children, wide }) {
109
+ function Modal({ open, onClose, title, children, wide, fecharAoClicarFora = true }) {
110
110
  if (!open) return null;
111
111
  return /* @__PURE__ */ jsx(
112
112
  "div",
113
113
  {
114
114
  onClick: (e) => {
115
- if (e.target === e.currentTarget) onClose();
115
+ if (fecharAoClicarFora && e.target === e.currentTarget) onClose();
116
116
  },
117
117
  className: "fixed inset-0 bg-[rgba(20,30,50,0.45)] z-50 flex items-center justify-center p-4",
118
118
  children: /* @__PURE__ */ jsxs("div", { className: `bg-white dark:bg-slate-900 rounded-xl w-full ${wide ? "max-w-[min(1600px,95vw)]" : "max-w-[720px]"} max-h-[85vh] flex flex-col shadow-2xl`, children: [
@@ -1 +1 @@
1
- {"version":3,"file":"Ui.js","sources":["../../../../../src/legacy/components/ui/Ui.tsx"],"sourcesContent":["import React, { type ReactNode } from 'react'\r\nimport Escolha from './Escolha'\r\nimport SegmentedControl from '../../../v1/primitives/buttons/SegmentedControl/SegmentedControl'\r\nimport Button from '../../../v1/primitives/buttons/Button/Button'\r\n\r\n/** Visual UI primitives (proef design system). Prefer these over repeating classes. */\r\n\r\n/**\r\n * SLA status accepted by BadgeSla.\r\n * @deprecated Legacy — não usar em código novo. Ver Storybook › Legacy.\r\n */\r\nexport type StatusSla = 'ok' | 'risco' | 'incumprido'\r\n\r\nfunction IconX({ className = '' }: { className?: string }) {\r\n return (\r\n <svg\r\n viewBox=\"0 0 24 24\"\r\n fill=\"none\"\r\n stroke=\"currentColor\"\r\n strokeWidth={2}\r\n strokeLinecap=\"round\"\r\n strokeLinejoin=\"round\"\r\n className={className}\r\n aria-hidden=\"true\"\r\n >\r\n <line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\" />\r\n <line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\" />\r\n </svg>\r\n )\r\n}\r\n\r\n// Migrado para o V1: o Card é o do V1 (aceita o nome antigo `extra`). Reexportado daqui para o código legacy.\r\nimport { Card } from '../../../v1/primitives/display/Card/Card'\r\nexport { Card }\r\n\r\n/**\r\n * Colour-bordered group — the overview sections in the prototype.\r\n * @deprecated Legacy — não usar em código novo. Ver Storybook › Legacy.\r\n */\r\nexport function SectionGroup({\r\n title,\r\n tone = 'accent',\r\n children,\r\n}: {\r\n title: string\r\n tone?: 'accent' | 'ok' | 'blue' | 'red' | 'purple' | 'yellow'\r\n children: ReactNode\r\n}) {\r\n const tones: Record<string, [string, string]> = {\r\n accent: ['border-slate-200 dark:border-slate-700', 'text-proef-navy dark:text-slate-100'],\r\n ok: ['border-ok', 'text-ok'],\r\n blue: ['border-[#4FA8FF]', 'text-[#2E8CE0]'],\r\n red: ['border-incumprido', 'text-incumprido'],\r\n purple: ['border-[#8B5CF6]', 'text-[#7C3AED]'],\r\n yellow: ['border-[#D9A400]', 'text-[#B78400]'],\r\n }\r\n const [borda, texto] = tones[tone]!\r\n return (\r\n <div className={`border-[1.5px] ${borda} rounded-2xl p-4 pb-1 mb-5 bg-[rgba(24,34,56,0.015)] dark:bg-slate-900/40`}>\r\n <div className={`text-[15px] font-bold text-center tracking-wide mb-3.5 ${texto}`}>{title}</div>\r\n {children}\r\n </div>\r\n )\r\n}\r\n\r\n// Migrado para o V1: o Kpi é o do V1 (aceita os nomes antigos `delta`, `bgTint`, `barra`, `rodape`, `denso`;\r\n// `hint` passou de `title` para a camada `data-tip`). Reexportado daqui para o código legacy que o importa.\r\nimport { Kpi, type KpiTone } from '../../../v1/primitives/display/Kpi/Kpi'\r\nexport { Kpi, type KpiTone }\r\n\r\n// Migrado para o V1: o Badge é o do V1 (mesmo nome e API; ganhou `dot` e `icon`). Reexportado daqui para\r\n// o código legacy que o importa deste ficheiro.\r\nimport { Badge, type BadgeTone } from '../../../v1/primitives/display/Badge/Badge'\r\nexport { Badge, type BadgeTone }\r\n\r\n/**\r\n * SLA badge — picks the tone and label from `status`. Labels overridable via `labels`.\r\n * @deprecated Legacy — não usar em código novo. Ver Storybook › Legacy.\r\n */\r\nexport function BadgeSla({ status, labels }: { status: StatusSla; labels?: { ok?: string; risco?: string; incumprido?: string } }) {\r\n const tone: BadgeTone = status === 'ok' ? 'ok' : status === 'risco' ? 'risco' : 'incumprido'\r\n const defaults: Record<StatusSla, string> = { ok: 'On time', risco: 'At risk', incumprido: 'Breached' }\r\n const label = labels?.[status] ?? defaults[status]\r\n return <Badge tone={tone}>{label}</Badge>\r\n}\r\n\r\n/** Internal-state chip — prototype colours (ESTADO_INTERNO_COLORS). */\r\nconst CORES_ESTADO_INTERNO: Record<string, [string, string]> = {\r\n 'Concluído': ['#1F8A55', '#E5F6EC'],\r\n 'Aferição': ['#2E6DA4', '#E5F0FA'],\r\n 'Para Tratamento': ['#1868B0', '#E5F0FA'],\r\n 'Aguarda Validação LS': ['#2E6DA4', '#E5F0FA'],\r\n 'Aguarda Produção LS': ['#6E8CAD', '#EDF2F7'],\r\n 'Incompleto/Devolvido': ['#C2255C', '#FBE5EF'],\r\n 'Falta de Documentação': ['#B96E00', '#FCF0DC'],\r\n Ignorar: ['#767C88', '#EEF0F2'],\r\n Cancelado: ['#767C88', '#EEF0F2'],\r\n}\r\n\r\n/** @deprecated Legacy — não usar em código novo. Ver Storybook › Legacy. */\r\nexport function ChipEstadoInterno({ estado }: { estado: string | null }) {\r\n if (!estado) return <span className=\"text-slate-400\">—</span>\r\n const cor = CORES_ESTADO_INTERNO[estado]\r\n if (!cor) return <span>{estado}</span>\r\n return (\r\n <span className=\"px-2 py-0.5 rounded text-[11px] font-medium inline-block\" style={{ color: cor[0], background: cor[1] }}>\r\n {estado}\r\n </span>\r\n )\r\n}\r\n\r\nconst VARIANTE_V1 = { default: 'secondary', accent: 'primary', danger: 'danger', ghost: 'ghost' } as const\r\n\r\n/**\r\n * Migrado para o V1: é o `Button` do V1 (mesmo aspeto); isto só mantém a API antiga.\r\n * @deprecated Legacy — usar `Button` do V1.\r\n */\r\nexport function Btn({\r\n children,\r\n onClick,\r\n variant = 'default',\r\n active = false,\r\n disabled = false,\r\n title,\r\n type = 'button',\r\n}: {\r\n children: ReactNode\r\n onClick?: () => void\r\n variant?: 'default' | 'accent' | 'danger' | 'ghost'\r\n active?: boolean\r\n disabled?: boolean\r\n title?: string\r\n type?: 'button' | 'submit'\r\n}) {\r\n return (\r\n <Button\r\n type={type}\r\n onClick={onClick}\r\n disabled={disabled}\r\n title={title}\r\n variant={VARIANTE_V1[variant]}\r\n // Só o `default` tinha estado ligado; nos outros o `active` não fazia nada.\r\n active={variant === 'default' && active ? true : undefined}\r\n >\r\n {children}\r\n </Button>\r\n )\r\n}\r\n\r\n/**\r\n * Mutually exclusive button group (granularity, dimension…).\r\n * Migrado para o V1: é o `SegmentedControl` do V1; isto só mantém a API antiga.\r\n * @deprecated Legacy — usar `SegmentedControl` do V1.\r\n */\r\nexport function BtnGroup<T extends string>({\r\n value,\r\n options,\r\n onChange,\r\n label,\r\n}: {\r\n value: T\r\n options: { value: T; label: string }[]\r\n onChange: (v: T) => void\r\n /** Rótulo opcional à esquerda (ex: \"Situação\"), com espaçamento próprio. */\r\n label?: string\r\n}) {\r\n return <SegmentedControl<T> value={value} options={options} onChange={onChange} label={label} />\r\n}\r\n\r\n/** @deprecated Legacy — não usar em código novo. Ver Storybook › Legacy. */\r\nexport function Estado({\r\n carregando,\r\n erro,\r\n vazio,\r\n labels,\r\n}: {\r\n carregando?: boolean\r\n erro?: boolean\r\n vazio?: boolean\r\n labels?: { loading?: string; error?: string; empty?: string }\r\n}) {\r\n if (carregando) return <div className=\"py-10 text-center text-sm text-slate-400\">{labels?.loading ?? 'Loading…'}</div>\r\n if (erro) return <div className=\"py-10 text-center text-sm text-incumprido\">{labels?.error ?? 'Failed to load'}</div>\r\n if (vazio) return <div className=\"py-10 text-center text-sm text-slate-400\">{labels?.empty ?? 'No data'}</div>\r\n return null\r\n}\r\n\r\n/** @deprecated Legacy — não usar em código novo. Ver Storybook › Legacy. */\r\nexport function Modal({ open, onClose, title, children, wide }: { open: boolean; onClose: () => void; title: ReactNode; children: ReactNode; wide?: boolean }) {\r\n if (!open) return null\r\n return (\r\n <div\r\n onClick={(e) => {\r\n if (e.target === e.currentTarget) onClose()\r\n }}\r\n className=\"fixed inset-0 bg-[rgba(20,30,50,0.45)] z-50 flex items-center justify-center p-4\"\r\n >\r\n {/*\r\n `wide` acompanha o ecrã em vez de parar num número fixo.\r\n\r\n 1200px era estreito num ecrã grande: um modal com mapa e tabela lado a\r\n lado ficava com a tabela cortada e o mapa pequeno, enquanto meio ecrã\r\n ficava vazio. O tecto de 1600px existe para o texto não ficar a\r\n atravessar o monitor todo, e os 95vw para o modal nunca encostar às\r\n margens.\r\n */}\r\n <div className={`bg-white dark:bg-slate-900 rounded-xl w-full ${wide ? 'max-w-[min(1600px,95vw)]' : 'max-w-[720px]'} max-h-[85vh] flex flex-col shadow-2xl`}>\r\n <div className=\"flex items-center justify-between gap-4 px-5 py-4 border-b border-slate-200 dark:border-slate-700\">\r\n <div className=\"text-[15px] font-bold text-proef-navy dark:text-slate-100\">{title}</div>\r\n <button onClick={onClose} aria-label=\"Close\" className=\"text-slate-500 hover:text-slate-800 dark:text-slate-400 dark:hover:text-slate-100\">\r\n <IconX className=\"w-5 h-5\" />\r\n </button>\r\n </div>\r\n <div className=\"overflow-auto thin-scroll px-5 py-4\">{children}</div>\r\n </div>\r\n </div>\r\n )\r\n}\r\n\r\n// ─── Filtros ────────────────────────────────────────────────────────────────\r\n// O kit não tinha campos de formulário, e cada ecrã acabava a repetir o mesmo\r\n// <label> com <select> e as mesmas classes. Estes três resolvem a barra de\r\n// filtros que praticamente todos os ecrãs de listagem têm.\r\n\r\n/**\r\n * Rótulo pequeno em maiúsculas por cima do campo.\r\n * @deprecated Legacy — não usar em código novo. Ver Storybook › Legacy.\r\n */\r\nexport function Campo({ label, children, className = '' }: { label: string; children: ReactNode; className?: string }) {\r\n return (\r\n <label className={`flex flex-col gap-1 ${className}`}>\r\n <span className=\"text-[10px] font-black uppercase tracking-wider text-slate-400 leading-normal\">{label}</span>\r\n {children}\r\n </label>\r\n )\r\n}\r\n\r\n/**\r\n * Select com opção vazia à cabeça.\r\n *\r\n * A opção vazia é obrigatória e não opcional: sem ela não há como desfazer uma\r\n * escolha, e o utilizador fica preso ao primeiro valor que carregou.\r\n * @deprecated Legacy — não usar em código novo. Ver Storybook › Legacy.\r\n */\r\nexport function CampoSelect({\r\n label,\r\n value,\r\n options,\r\n onChange,\r\n placeholderVazio,\r\n disabled = false,\r\n className = '',\r\n}: {\r\n label: string\r\n value: string\r\n options: readonly string[] | readonly { value: string; label: string }[]\r\n onChange: (v: string) => void\r\n placeholderVazio: string\r\n disabled?: boolean\r\n className?: string\r\n}) {\r\n const normalizadas = options.map((o) => (typeof o === 'string' ? { value: o, label: o } : o))\r\n return (\r\n <Campo label={label} className={className}>\r\n {/* Mesma lista suspensa em todos os portais (Escolha), em vez do <select> nativo. */}\r\n <Escolha\r\n valor={value}\r\n opcoes={normalizadas.map((o) => ({ valor: o.value, rotulo: o.label }))}\r\n onMudar={onChange}\r\n rotulo={label}\r\n opcaoVazia={placeholderVazio}\r\n placeholder={placeholderVazio}\r\n disabled={disabled}\r\n className=\"min-w-[10rem] max-w-56\"\r\n />\r\n </Campo>\r\n )\r\n}\r\n\r\n/**\r\n * Campo de pesquisa que só dispara ao confirmar.\r\n *\r\n * Deliberadamente NÃO pesquisa a cada tecla: sobre dezenas de milhares de\r\n * registos isso seria um pedido por letra. Confirma-se com Enter ou ao sair do\r\n * campo.\r\n * @deprecated Legacy — não usar em código novo. Ver Storybook › Legacy.\r\n */\r\nexport function CampoPesquisa({\r\n label,\r\n valorInicial = '',\r\n onConfirmar,\r\n placeholder,\r\n className = '',\r\n}: {\r\n label: string\r\n valorInicial?: string\r\n onConfirmar: (v: string | undefined) => void\r\n placeholder?: string\r\n className?: string\r\n}) {\r\n const confirmar = (v: string): void => onConfirmar(v.trim() || undefined)\r\n return (\r\n <Campo label={label} className={className}>\r\n <input\r\n type=\"search\"\r\n defaultValue={valorInicial}\r\n placeholder={placeholder}\r\n onKeyDown={(e) => {\r\n if (e.key === 'Enter') confirmar((e.target as HTMLInputElement).value)\r\n }}\r\n onBlur={(e) => confirmar(e.target.value)}\r\n className=\"h-8 border border-slate-300 rounded-lg px-2.5 text-sm w-64 bg-white dark:bg-slate-800 dark:border-slate-600 dark:text-slate-100 dark:placeholder:text-slate-500\"\r\n />\r\n </Campo>\r\n )\r\n}\r\n\r\n/**\r\n * Caixa de interruptor com rótulo à direita, alinhada com os campos ao lado.\r\n * @deprecated Legacy — não usar em código novo. Ver Storybook › Legacy.\r\n */\r\nexport function CampoInterruptor({\r\n label,\r\n checked,\r\n onChange,\r\n}: {\r\n label: string\r\n checked: boolean\r\n onChange: (v: boolean) => void\r\n}) {\r\n return (\r\n <label className=\"flex items-center gap-2 text-sm pb-1.5 cursor-pointer select-none\">\r\n <input type=\"checkbox\" checked={checked} onChange={(e) => onChange(e.target.checked)} />\r\n {label}\r\n </label>\r\n )\r\n}\r\n\r\n/**\r\n * Barra onde os campos de filtro assentam.\r\n * @deprecated Legacy — não usar em código novo. Ver Storybook › Legacy.\r\n */\r\nexport function BarraFiltros({ children }: { children: ReactNode }) {\r\n return (\r\n <div className=\"bg-white border border-slate-200 dark:bg-slate-900 dark:border-slate-700 rounded-xl px-4 py-3 flex flex-wrap items-end gap-3\">\r\n {children}\r\n </div>\r\n )\r\n}\r\n\r\n// ─── Listas ─────────────────────────────────────────────────────────────────\r\n\r\n/** @deprecated Legacy — não usar em código novo. Ver Storybook › Legacy. */\r\nexport type SeveridadeAlerta = 'critico' | 'grave' | 'atencao' | 'info'\r\n\r\n/**\r\n * Linha de uma lista de alertas.\r\n *\r\n * A severidade está na barra lateral colorida e não no fundo: uma lista de\r\n * cinquenta linhas com fundos saturados é ilegível, e o que se quer é poder\r\n * varrer a coluna da esquerda e saber onde parar.\r\n * @deprecated Legacy — não usar em código novo. Ver Storybook › Legacy.\r\n */\r\nexport function LinhaAlerta({\r\n severidade,\r\n icone,\r\n titulo,\r\n detalhe,\r\n referencia,\r\n onReferenciaClick,\r\n quando,\r\n}: {\r\n severidade: SeveridadeAlerta\r\n icone?: ReactNode\r\n titulo: ReactNode\r\n detalhe?: ReactNode\r\n /** Identificador clicável à direita (ex: número da ordem). */\r\n referencia?: string\r\n onReferenciaClick?: () => void\r\n quando?: ReactNode\r\n}) {\r\n const barras: Record<SeveridadeAlerta, string> = {\r\n critico: 'border-l-incumprido',\r\n grave: 'border-l-orange-500',\r\n atencao: 'border-l-risco',\r\n info: 'border-l-sky-400',\r\n }\r\n const textos: Record<SeveridadeAlerta, string> = {\r\n critico: 'text-incumprido',\r\n grave: 'text-orange-600 dark:text-orange-400',\r\n atencao: 'text-risco',\r\n info: 'text-sky-600 dark:text-sky-400',\r\n }\r\n\r\n return (\r\n <div className={`bg-white border border-slate-200 dark:bg-slate-900 dark:border-y-slate-700 dark:border-r-slate-700 border-l-4 ${barras[severidade]} rounded-lg px-4 py-2.5 flex items-start gap-3`}>\r\n {icone && <span className={`${textos[severidade]} shrink-0 mt-0.5`}>{icone}</span>}\r\n <div className=\"min-w-0 flex-1\">\r\n <div className=\"text-sm font-semibold text-slate-800 dark:text-slate-100\">{titulo}</div>\r\n {detalhe && <div className=\"text-xs text-slate-500 dark:text-slate-400\">{detalhe}</div>}\r\n </div>\r\n {referencia && (\r\n <button\r\n type=\"button\"\r\n onClick={onReferenciaClick}\r\n className=\"text-xs font-mono text-proef-navy dark:text-sky-300 hover:underline shrink-0\"\r\n >\r\n {referencia}\r\n </button>\r\n )}\r\n {quando && <span className=\"text-xs text-slate-400 shrink-0 tabular-nums\">{quando}</span>}\r\n </div>\r\n )\r\n}\r\n\r\n/**\r\n * Grelha de pares rótulo/valor, para painéis de detalhe.\r\n *\r\n * Os campos vazios são omitidos por omissão: num registo vindo de várias\r\n * origens, metade dos campos costuma não existir, e uma coluna de travessões\r\n * não informa ninguém. `mostrarVazios` repõe-os quando a ausência é o que\r\n * interessa ver.\r\n * @deprecated Legacy — não usar em código novo. Ver Storybook › Legacy.\r\n */\r\nexport function ListaCampos({\r\n campos,\r\n colunas = 2,\r\n mostrarVazios = false,\r\n}: {\r\n campos: { label: string; valor: ReactNode }[]\r\n colunas?: 1 | 2 | 3\r\n mostrarVazios?: boolean\r\n}) {\r\n const vazio = (v: ReactNode): boolean => v == null || v === '' || v === '—'\r\n const visiveis = mostrarVazios ? campos : campos.filter((c) => !vazio(c.valor))\r\n if (!visiveis.length) return null\r\n\r\n const grelha = { 1: '', 2: 'md:grid-cols-2', 3: 'md:grid-cols-3' }[colunas]\r\n\r\n return (\r\n <dl className={`grid ${grelha} gap-x-6 gap-y-1`}>\r\n {visiveis.map((c) => (\r\n <div key={c.label} className=\"flex justify-between gap-3 border-b border-slate-100 dark:border-slate-800 py-1\">\r\n <dt className=\"text-xs text-slate-500 dark:text-slate-400\">{c.label}</dt>\r\n <dd className=\"text-xs font-medium text-slate-800 dark:text-slate-100 text-right\">{c.valor}</dd>\r\n </div>\r\n ))}\r\n </dl>\r\n )\r\n}\r\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"Ui.js","sources":["../../../../../src/legacy/components/ui/Ui.tsx"],"sourcesContent":["import React, { type ReactNode } from 'react'\r\nimport Escolha from './Escolha'\r\nimport SegmentedControl from '../../../v1/primitives/buttons/SegmentedControl/SegmentedControl'\r\nimport Button from '../../../v1/primitives/buttons/Button/Button'\r\n\r\n/** Visual UI primitives (proef design system). Prefer these over repeating classes. */\r\n\r\n/**\r\n * SLA status accepted by BadgeSla.\r\n * @deprecated Legacy — não usar em código novo. Ver Storybook › Legacy.\r\n */\r\nexport type StatusSla = 'ok' | 'risco' | 'incumprido'\r\n\r\nfunction IconX({ className = '' }: { className?: string }) {\r\n return (\r\n <svg\r\n viewBox=\"0 0 24 24\"\r\n fill=\"none\"\r\n stroke=\"currentColor\"\r\n strokeWidth={2}\r\n strokeLinecap=\"round\"\r\n strokeLinejoin=\"round\"\r\n className={className}\r\n aria-hidden=\"true\"\r\n >\r\n <line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\" />\r\n <line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\" />\r\n </svg>\r\n )\r\n}\r\n\r\n// Migrado para o V1: o Card é o do V1 (aceita o nome antigo `extra`). Reexportado daqui para o código legacy.\r\nimport { Card } from '../../../v1/primitives/display/Card/Card'\r\nexport { Card }\r\n\r\n/**\r\n * Colour-bordered group — the overview sections in the prototype.\r\n * @deprecated Legacy — não usar em código novo. Ver Storybook › Legacy.\r\n */\r\nexport function SectionGroup({\r\n title,\r\n tone = 'accent',\r\n children,\r\n}: {\r\n title: string\r\n tone?: 'accent' | 'ok' | 'blue' | 'red' | 'purple' | 'yellow'\r\n children: ReactNode\r\n}) {\r\n const tones: Record<string, [string, string]> = {\r\n accent: ['border-slate-200 dark:border-slate-700', 'text-proef-navy dark:text-slate-100'],\r\n ok: ['border-ok', 'text-ok'],\r\n blue: ['border-[#4FA8FF]', 'text-[#2E8CE0]'],\r\n red: ['border-incumprido', 'text-incumprido'],\r\n purple: ['border-[#8B5CF6]', 'text-[#7C3AED]'],\r\n yellow: ['border-[#D9A400]', 'text-[#B78400]'],\r\n }\r\n const [borda, texto] = tones[tone]!\r\n return (\r\n <div className={`border-[1.5px] ${borda} rounded-2xl p-4 pb-1 mb-5 bg-[rgba(24,34,56,0.015)] dark:bg-slate-900/40`}>\r\n <div className={`text-[15px] font-bold text-center tracking-wide mb-3.5 ${texto}`}>{title}</div>\r\n {children}\r\n </div>\r\n )\r\n}\r\n\r\n// Migrado para o V1: o Kpi é o do V1 (aceita os nomes antigos `delta`, `bgTint`, `barra`, `rodape`, `denso`;\r\n// `hint` passou de `title` para a camada `data-tip`). Reexportado daqui para o código legacy que o importa.\r\nimport { Kpi, type KpiTone } from '../../../v1/primitives/display/Kpi/Kpi'\r\nexport { Kpi, type KpiTone }\r\n\r\n// Migrado para o V1: o Badge é o do V1 (mesmo nome e API; ganhou `dot` e `icon`). Reexportado daqui para\r\n// o código legacy que o importa deste ficheiro.\r\nimport { Badge, type BadgeTone } from '../../../v1/primitives/display/Badge/Badge'\r\nexport { Badge, type BadgeTone }\r\n\r\n/**\r\n * SLA badge — picks the tone and label from `status`. Labels overridable via `labels`.\r\n * @deprecated Legacy — não usar em código novo. Ver Storybook › Legacy.\r\n */\r\nexport function BadgeSla({ status, labels }: { status: StatusSla; labels?: { ok?: string; risco?: string; incumprido?: string } }) {\r\n const tone: BadgeTone = status === 'ok' ? 'ok' : status === 'risco' ? 'risco' : 'incumprido'\r\n const defaults: Record<StatusSla, string> = { ok: 'On time', risco: 'At risk', incumprido: 'Breached' }\r\n const label = labels?.[status] ?? defaults[status]\r\n return <Badge tone={tone}>{label}</Badge>\r\n}\r\n\r\n/** Internal-state chip — prototype colours (ESTADO_INTERNO_COLORS). */\r\nconst CORES_ESTADO_INTERNO: Record<string, [string, string]> = {\r\n 'Concluído': ['#1F8A55', '#E5F6EC'],\r\n 'Aferição': ['#2E6DA4', '#E5F0FA'],\r\n 'Para Tratamento': ['#1868B0', '#E5F0FA'],\r\n 'Aguarda Validação LS': ['#2E6DA4', '#E5F0FA'],\r\n 'Aguarda Produção LS': ['#6E8CAD', '#EDF2F7'],\r\n 'Incompleto/Devolvido': ['#C2255C', '#FBE5EF'],\r\n 'Falta de Documentação': ['#B96E00', '#FCF0DC'],\r\n Ignorar: ['#767C88', '#EEF0F2'],\r\n Cancelado: ['#767C88', '#EEF0F2'],\r\n}\r\n\r\n/** @deprecated Legacy — não usar em código novo. Ver Storybook › Legacy. */\r\nexport function ChipEstadoInterno({ estado }: { estado: string | null }) {\r\n if (!estado) return <span className=\"text-slate-400\">—</span>\r\n const cor = CORES_ESTADO_INTERNO[estado]\r\n if (!cor) return <span>{estado}</span>\r\n return (\r\n <span className=\"px-2 py-0.5 rounded text-[11px] font-medium inline-block\" style={{ color: cor[0], background: cor[1] }}>\r\n {estado}\r\n </span>\r\n )\r\n}\r\n\r\nconst VARIANTE_V1 = { default: 'secondary', accent: 'primary', danger: 'danger', ghost: 'ghost' } as const\r\n\r\n/**\r\n * Migrado para o V1: é o `Button` do V1 (mesmo aspeto); isto só mantém a API antiga.\r\n * @deprecated Legacy — usar `Button` do V1.\r\n */\r\nexport function Btn({\r\n children,\r\n onClick,\r\n variant = 'default',\r\n active = false,\r\n disabled = false,\r\n title,\r\n type = 'button',\r\n}: {\r\n children: ReactNode\r\n onClick?: () => void\r\n variant?: 'default' | 'accent' | 'danger' | 'ghost'\r\n active?: boolean\r\n disabled?: boolean\r\n title?: string\r\n type?: 'button' | 'submit'\r\n}) {\r\n return (\r\n <Button\r\n type={type}\r\n onClick={onClick}\r\n disabled={disabled}\r\n title={title}\r\n variant={VARIANTE_V1[variant]}\r\n // Só o `default` tinha estado ligado; nos outros o `active` não fazia nada.\r\n active={variant === 'default' && active ? true : undefined}\r\n >\r\n {children}\r\n </Button>\r\n )\r\n}\r\n\r\n/**\r\n * Mutually exclusive button group (granularity, dimension…).\r\n * Migrado para o V1: é o `SegmentedControl` do V1; isto só mantém a API antiga.\r\n * @deprecated Legacy — usar `SegmentedControl` do V1.\r\n */\r\nexport function BtnGroup<T extends string>({\r\n value,\r\n options,\r\n onChange,\r\n label,\r\n}: {\r\n value: T\r\n options: { value: T; label: string }[]\r\n onChange: (v: T) => void\r\n /** Rótulo opcional à esquerda (ex: \"Situação\"), com espaçamento próprio. */\r\n label?: string\r\n}) {\r\n return <SegmentedControl<T> value={value} options={options} onChange={onChange} label={label} />\r\n}\r\n\r\n/** @deprecated Legacy — não usar em código novo. Ver Storybook › Legacy. */\r\nexport function Estado({\r\n carregando,\r\n erro,\r\n vazio,\r\n labels,\r\n}: {\r\n carregando?: boolean\r\n erro?: boolean\r\n vazio?: boolean\r\n labels?: { loading?: string; error?: string; empty?: string }\r\n}) {\r\n if (carregando) return <div className=\"py-10 text-center text-sm text-slate-400\">{labels?.loading ?? 'Loading…'}</div>\r\n if (erro) return <div className=\"py-10 text-center text-sm text-incumprido\">{labels?.error ?? 'Failed to load'}</div>\r\n if (vazio) return <div className=\"py-10 text-center text-sm text-slate-400\">{labels?.empty ?? 'No data'}</div>\r\n return null\r\n}\r\n\r\n/** @deprecated Legacy — não usar em código novo. Ver Storybook › Legacy. */\r\n// `fecharAoClicarFora={false}` em modais com formulário: um clique no fundo\r\n// fechava a janela e perdia-se tudo o que estava preenchido.\r\nexport function Modal({ open, onClose, title, children, wide, fecharAoClicarFora = true }: { open: boolean; onClose: () => void; title: ReactNode; children: ReactNode; wide?: boolean; fecharAoClicarFora?: boolean }) {\r\n if (!open) return null\r\n return (\r\n <div\r\n onClick={(e) => {\r\n if (fecharAoClicarFora && e.target === e.currentTarget) onClose()\r\n }}\r\n className=\"fixed inset-0 bg-[rgba(20,30,50,0.45)] z-50 flex items-center justify-center p-4\"\r\n >\r\n {/*\r\n `wide` acompanha o ecrã em vez de parar num número fixo.\r\n\r\n 1200px era estreito num ecrã grande: um modal com mapa e tabela lado a\r\n lado ficava com a tabela cortada e o mapa pequeno, enquanto meio ecrã\r\n ficava vazio. O tecto de 1600px existe para o texto não ficar a\r\n atravessar o monitor todo, e os 95vw para o modal nunca encostar às\r\n margens.\r\n */}\r\n <div className={`bg-white dark:bg-slate-900 rounded-xl w-full ${wide ? 'max-w-[min(1600px,95vw)]' : 'max-w-[720px]'} max-h-[85vh] flex flex-col shadow-2xl`}>\r\n <div className=\"flex items-center justify-between gap-4 px-5 py-4 border-b border-slate-200 dark:border-slate-700\">\r\n <div className=\"text-[15px] font-bold text-proef-navy dark:text-slate-100\">{title}</div>\r\n <button onClick={onClose} aria-label=\"Close\" className=\"text-slate-500 hover:text-slate-800 dark:text-slate-400 dark:hover:text-slate-100\">\r\n <IconX className=\"w-5 h-5\" />\r\n </button>\r\n </div>\r\n <div className=\"overflow-auto thin-scroll px-5 py-4\">{children}</div>\r\n </div>\r\n </div>\r\n )\r\n}\r\n\r\n// ─── Filtros ────────────────────────────────────────────────────────────────\r\n// O kit não tinha campos de formulário, e cada ecrã acabava a repetir o mesmo\r\n// <label> com <select> e as mesmas classes. Estes três resolvem a barra de\r\n// filtros que praticamente todos os ecrãs de listagem têm.\r\n\r\n/**\r\n * Rótulo pequeno em maiúsculas por cima do campo.\r\n * @deprecated Legacy — não usar em código novo. Ver Storybook › Legacy.\r\n */\r\nexport function Campo({ label, children, className = '' }: { label: string; children: ReactNode; className?: string }) {\r\n return (\r\n <label className={`flex flex-col gap-1 ${className}`}>\r\n <span className=\"text-[10px] font-black uppercase tracking-wider text-slate-400 leading-normal\">{label}</span>\r\n {children}\r\n </label>\r\n )\r\n}\r\n\r\n/**\r\n * Select com opção vazia à cabeça.\r\n *\r\n * A opção vazia é obrigatória e não opcional: sem ela não há como desfazer uma\r\n * escolha, e o utilizador fica preso ao primeiro valor que carregou.\r\n * @deprecated Legacy — não usar em código novo. Ver Storybook › Legacy.\r\n */\r\nexport function CampoSelect({\r\n label,\r\n value,\r\n options,\r\n onChange,\r\n placeholderVazio,\r\n disabled = false,\r\n className = '',\r\n}: {\r\n label: string\r\n value: string\r\n options: readonly string[] | readonly { value: string; label: string }[]\r\n onChange: (v: string) => void\r\n placeholderVazio: string\r\n disabled?: boolean\r\n className?: string\r\n}) {\r\n const normalizadas = options.map((o) => (typeof o === 'string' ? { value: o, label: o } : o))\r\n return (\r\n <Campo label={label} className={className}>\r\n {/* Mesma lista suspensa em todos os portais (Escolha), em vez do <select> nativo. */}\r\n <Escolha\r\n valor={value}\r\n opcoes={normalizadas.map((o) => ({ valor: o.value, rotulo: o.label }))}\r\n onMudar={onChange}\r\n rotulo={label}\r\n opcaoVazia={placeholderVazio}\r\n placeholder={placeholderVazio}\r\n disabled={disabled}\r\n className=\"min-w-[10rem] max-w-56\"\r\n />\r\n </Campo>\r\n )\r\n}\r\n\r\n/**\r\n * Campo de pesquisa que só dispara ao confirmar.\r\n *\r\n * Deliberadamente NÃO pesquisa a cada tecla: sobre dezenas de milhares de\r\n * registos isso seria um pedido por letra. Confirma-se com Enter ou ao sair do\r\n * campo.\r\n * @deprecated Legacy — não usar em código novo. Ver Storybook › Legacy.\r\n */\r\nexport function CampoPesquisa({\r\n label,\r\n valorInicial = '',\r\n onConfirmar,\r\n placeholder,\r\n className = '',\r\n}: {\r\n label: string\r\n valorInicial?: string\r\n onConfirmar: (v: string | undefined) => void\r\n placeholder?: string\r\n className?: string\r\n}) {\r\n const confirmar = (v: string): void => onConfirmar(v.trim() || undefined)\r\n return (\r\n <Campo label={label} className={className}>\r\n <input\r\n type=\"search\"\r\n defaultValue={valorInicial}\r\n placeholder={placeholder}\r\n onKeyDown={(e) => {\r\n if (e.key === 'Enter') confirmar((e.target as HTMLInputElement).value)\r\n }}\r\n onBlur={(e) => confirmar(e.target.value)}\r\n className=\"h-8 border border-slate-300 rounded-lg px-2.5 text-sm w-64 bg-white dark:bg-slate-800 dark:border-slate-600 dark:text-slate-100 dark:placeholder:text-slate-500\"\r\n />\r\n </Campo>\r\n )\r\n}\r\n\r\n/**\r\n * Caixa de interruptor com rótulo à direita, alinhada com os campos ao lado.\r\n * @deprecated Legacy — não usar em código novo. Ver Storybook › Legacy.\r\n */\r\nexport function CampoInterruptor({\r\n label,\r\n checked,\r\n onChange,\r\n}: {\r\n label: string\r\n checked: boolean\r\n onChange: (v: boolean) => void\r\n}) {\r\n return (\r\n <label className=\"flex items-center gap-2 text-sm pb-1.5 cursor-pointer select-none\">\r\n <input type=\"checkbox\" checked={checked} onChange={(e) => onChange(e.target.checked)} />\r\n {label}\r\n </label>\r\n )\r\n}\r\n\r\n/**\r\n * Barra onde os campos de filtro assentam.\r\n * @deprecated Legacy — não usar em código novo. Ver Storybook › Legacy.\r\n */\r\nexport function BarraFiltros({ children }: { children: ReactNode }) {\r\n return (\r\n <div className=\"bg-white border border-slate-200 dark:bg-slate-900 dark:border-slate-700 rounded-xl px-4 py-3 flex flex-wrap items-end gap-3\">\r\n {children}\r\n </div>\r\n )\r\n}\r\n\r\n// ─── Listas ─────────────────────────────────────────────────────────────────\r\n\r\n/** @deprecated Legacy — não usar em código novo. Ver Storybook › Legacy. */\r\nexport type SeveridadeAlerta = 'critico' | 'grave' | 'atencao' | 'info'\r\n\r\n/**\r\n * Linha de uma lista de alertas.\r\n *\r\n * A severidade está na barra lateral colorida e não no fundo: uma lista de\r\n * cinquenta linhas com fundos saturados é ilegível, e o que se quer é poder\r\n * varrer a coluna da esquerda e saber onde parar.\r\n * @deprecated Legacy — não usar em código novo. Ver Storybook › Legacy.\r\n */\r\nexport function LinhaAlerta({\r\n severidade,\r\n icone,\r\n titulo,\r\n detalhe,\r\n referencia,\r\n onReferenciaClick,\r\n quando,\r\n}: {\r\n severidade: SeveridadeAlerta\r\n icone?: ReactNode\r\n titulo: ReactNode\r\n detalhe?: ReactNode\r\n /** Identificador clicável à direita (ex: número da ordem). */\r\n referencia?: string\r\n onReferenciaClick?: () => void\r\n quando?: ReactNode\r\n}) {\r\n const barras: Record<SeveridadeAlerta, string> = {\r\n critico: 'border-l-incumprido',\r\n grave: 'border-l-orange-500',\r\n atencao: 'border-l-risco',\r\n info: 'border-l-sky-400',\r\n }\r\n const textos: Record<SeveridadeAlerta, string> = {\r\n critico: 'text-incumprido',\r\n grave: 'text-orange-600 dark:text-orange-400',\r\n atencao: 'text-risco',\r\n info: 'text-sky-600 dark:text-sky-400',\r\n }\r\n\r\n return (\r\n <div className={`bg-white border border-slate-200 dark:bg-slate-900 dark:border-y-slate-700 dark:border-r-slate-700 border-l-4 ${barras[severidade]} rounded-lg px-4 py-2.5 flex items-start gap-3`}>\r\n {icone && <span className={`${textos[severidade]} shrink-0 mt-0.5`}>{icone}</span>}\r\n <div className=\"min-w-0 flex-1\">\r\n <div className=\"text-sm font-semibold text-slate-800 dark:text-slate-100\">{titulo}</div>\r\n {detalhe && <div className=\"text-xs text-slate-500 dark:text-slate-400\">{detalhe}</div>}\r\n </div>\r\n {referencia && (\r\n <button\r\n type=\"button\"\r\n onClick={onReferenciaClick}\r\n className=\"text-xs font-mono text-proef-navy dark:text-sky-300 hover:underline shrink-0\"\r\n >\r\n {referencia}\r\n </button>\r\n )}\r\n {quando && <span className=\"text-xs text-slate-400 shrink-0 tabular-nums\">{quando}</span>}\r\n </div>\r\n )\r\n}\r\n\r\n/**\r\n * Grelha de pares rótulo/valor, para painéis de detalhe.\r\n *\r\n * Os campos vazios são omitidos por omissão: num registo vindo de várias\r\n * origens, metade dos campos costuma não existir, e uma coluna de travessões\r\n * não informa ninguém. `mostrarVazios` repõe-os quando a ausência é o que\r\n * interessa ver.\r\n * @deprecated Legacy — não usar em código novo. Ver Storybook › Legacy.\r\n */\r\nexport function ListaCampos({\r\n campos,\r\n colunas = 2,\r\n mostrarVazios = false,\r\n}: {\r\n campos: { label: string; valor: ReactNode }[]\r\n colunas?: 1 | 2 | 3\r\n mostrarVazios?: boolean\r\n}) {\r\n const vazio = (v: ReactNode): boolean => v == null || v === '' || v === '—'\r\n const visiveis = mostrarVazios ? campos : campos.filter((c) => !vazio(c.valor))\r\n if (!visiveis.length) return null\r\n\r\n const grelha = { 1: '', 2: 'md:grid-cols-2', 3: 'md:grid-cols-3' }[colunas]\r\n\r\n return (\r\n <dl className={`grid ${grelha} gap-x-6 gap-y-1`}>\r\n {visiveis.map((c) => (\r\n <div key={c.label} className=\"flex justify-between gap-3 border-b border-slate-100 dark:border-slate-800 py-1\">\r\n <dt className=\"text-xs text-slate-500 dark:text-slate-400\">{c.label}</dt>\r\n <dd className=\"text-xs font-medium text-slate-800 dark:text-slate-100 text-right\">{c.valor}</dd>\r\n </div>\r\n ))}\r\n </dl>\r\n )\r\n}\r\n"],"names":[],"mappings":"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"}
@@ -76,12 +76,13 @@ export declare function Estado({ carregando, erro, vazio, labels, }: {
76
76
  };
77
77
  }): React.JSX.Element | null;
78
78
  /** @deprecated Legacy — não usar em código novo. Ver Storybook › Legacy. */
79
- export declare function Modal({ open, onClose, title, children, wide }: {
79
+ export declare function Modal({ open, onClose, title, children, wide, fecharAoClicarFora }: {
80
80
  open: boolean;
81
81
  onClose: () => void;
82
82
  title: ReactNode;
83
83
  children: ReactNode;
84
84
  wide?: boolean;
85
+ fecharAoClicarFora?: boolean;
85
86
  }): React.JSX.Element | null;
86
87
  /**
87
88
  * Rótulo pequeno em maiúsculas por cima do campo.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@proef/common-ui",
3
- "version": "2.2.3",
3
+ "version": "2.2.4",
4
4
  "description": "",
5
5
  "main": "dist/esm/index.js",
6
6
  "module": "dist/esm/index.js",