@aurea-uds/react 0.1.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. package/README.md +34 -2
  2. package/dist/actions.d.ts +20 -0
  3. package/dist/actions.js +59 -5
  4. package/dist/agents.d.ts +209 -0
  5. package/dist/agents.js +302 -0
  6. package/dist/calendar.d.ts +6 -0
  7. package/dist/calendar.js +27 -0
  8. package/dist/chart.d.ts +10 -0
  9. package/dist/chart.js +53 -0
  10. package/dist/code-client.d.ts +6 -0
  11. package/dist/code-client.js +24 -0
  12. package/dist/code-editor.js +1 -0
  13. package/dist/code.d.ts +2 -13
  14. package/dist/code.js +5 -18
  15. package/dist/communication.js +1 -0
  16. package/dist/data-display-client.d.ts +14 -0
  17. package/dist/data-display-client.js +22 -0
  18. package/dist/data-display.d.ts +2 -23
  19. package/dist/data-display.js +5 -13
  20. package/dist/data-grid.d.ts +52 -3
  21. package/dist/data-grid.js +205 -25
  22. package/dist/feedback-client.d.ts +64 -0
  23. package/dist/feedback-client.js +110 -0
  24. package/dist/feedback.d.ts +2 -53
  25. package/dist/feedback.js +5 -71
  26. package/dist/file-input.d.ts +21 -2
  27. package/dist/file-input.js +202 -21
  28. package/dist/graph.d.ts +33 -0
  29. package/dist/graph.js +178 -0
  30. package/dist/identity-client.d.ts +9 -0
  31. package/dist/identity-client.js +12 -0
  32. package/dist/identity.d.ts +2 -8
  33. package/dist/identity.js +5 -3
  34. package/dist/index.d.ts +4 -1
  35. package/dist/index.js +33 -3
  36. package/dist/inputs-client.d.ts +105 -0
  37. package/dist/inputs-client.js +264 -0
  38. package/dist/inputs.d.ts +2 -65
  39. package/dist/inputs.js +9 -61
  40. package/dist/internal.d.ts +23 -70
  41. package/dist/internal.js +115 -23
  42. package/dist/layout-client.d.ts +9 -0
  43. package/dist/layout-client.js +72 -0
  44. package/dist/layout.d.ts +2 -14
  45. package/dist/layout.js +5 -22
  46. package/dist/markup.d.ts +72 -0
  47. package/dist/markup.js +87 -0
  48. package/dist/media-client.d.ts +36 -0
  49. package/dist/media-client.js +239 -0
  50. package/dist/media.d.ts +2 -9
  51. package/dist/media.js +5 -98
  52. package/dist/navigation-client.d.ts +94 -0
  53. package/dist/navigation-client.js +154 -0
  54. package/dist/navigation.d.ts +2 -59
  55. package/dist/navigation.js +5 -113
  56. package/dist/overlays.d.ts +28 -0
  57. package/dist/overlays.js +66 -10
  58. package/dist/pure.d.ts +196 -0
  59. package/dist/pure.js +111 -0
  60. package/dist/qrcode.d.ts +2 -1
  61. package/dist/qrcode.js +3 -2
  62. package/dist/system.d.ts +11 -1
  63. package/dist/system.js +59 -4
  64. package/package.json +40 -4
package/dist/internal.js CHANGED
@@ -1,31 +1,123 @@
1
- import { jsx as _jsx } from "react/jsx-runtime";
1
+ "use client";
2
+ // A diretiva na LINHA 1, como as referências a escrevem (Base UI e shadcn/ui, medidos em
3
+ // 06/08/2026) — Parte A do PLANO-1.0. Este módulo chama `createContext`, que é API só de
4
+ // cliente: sem a diretiva, `import {Button} from "@aurea-uds/react"` dentro de um componente
5
+ // de servidor quebrava na hora, e quebrava no consumidor.
6
+ //
7
+ // O que NÃO tem estado saiu daqui para o `pure.tsx` — está explicado lá. A reexportação abaixo
8
+ // existe para os 18 módulos de cliente seguirem importando `cx` de "./internal.js" sem mudança.
9
+ //
2
10
  // Fase 9 (achado A5): este arquivo saiu do index.tsx de 970 linhas. Um módulo por categoria
3
11
  // do registry — a taxonomia já existia e é gateada. A ordem de import entre eles é um DAG:
4
- // internal → system → actions → feedback → inputs → navigation → layout → data-display → resto.
12
+ // pure → internal → system → actions → feedback → inputs → navigation → layout → data-display → resto.
5
13
  // INTERNO: o que todo módulo precisa e ninguém publica como componente próprio.
6
14
  // NÃO é subpath — `@aurea-uds/react/internal` não existe, de propósito.
7
- // Kbd mora aqui por dependência, não por categoria: o Button precisa dele e ele não precisa de
8
- // ninguém. A casa pública dele continua sendo `/data-display`, que o reexporta.
9
- import { createContext, useContext } from "react";
10
- export const cx = (...v) => v.filter(Boolean).join(" ");
11
- export const defaultStrings = { close: "Close", paginationLabel: "Pagination", previous: "Previous", next: "Next", breadcrumbLabel: "Breadcrumb", tabsLabel: "Tabs", optionsLabel: "Options", tableLabel: "Table", commandLabel: "Command palette", commandPlaceholder: "Type a command…", dismissNotification: "Dismiss notification", toolbarLabel: "Toolbar", comboboxEmpty: "No results", comboboxClear: "Clear selection", comboboxOpen: "Open list", comboboxRemove: "Remove", comboboxLoading: "Loading…", fileDropPrompt: "Drag files here or click to select", fileAdded: "File added", fileRemoved: "File removed", fileRemove: "Remove", fileTooLarge: "File exceeds the size limit", fileWrongType: "File type not allowed", treeLabel: "Tree", notificationsLabel: "Notifications", notificationMarkAll: "Mark all as read", notificationEmpty: "No notifications", notificationUnread: "Unread", notificationNew: "new notifications", dataGridFilter: "Filter", dataGridEmpty: "No results", dataGridSelectAll: "Select all rows", dataGridSelectRow: "Select row", mediaPlayer: "Media player", mediaPlay: "Play", mediaPause: "Pause", mediaMute: "Mute", mediaUnmute: "Unmute", mediaSeek: "Seek", mediaVolume: "Volume", mediaCaptionsShow: "Show captions", mediaCaptionsHide: "Hide captions", mediaSkipBack: "Skip back 10 seconds", mediaSkipForward: "Skip forward 10 seconds", mediaFullscreenEnter: "Enter full screen", mediaFullscreenExit: "Exit full screen", uploadSending: "Uploading", uploadCancel: "Cancel upload", uploadRetry: "Retry upload", uploadError: "Upload failed", uploadCanceled: "Upload canceled", uploadComplete: "Upload complete", codeEditor: "Code editor", chatLabel: "Conversation", chatMessage: "Message", chatSend: "Send", qrCode: "QR code", copyCode: "Copy code", tocLabel: "On this page", navigationToggle: "Navigation" };
12
- // pt-BR preservado como locale (mesmos valores que já eram o default): passe
13
- // <AureaProvider strings={ptBR}> para restaurar português.
14
- export const ptBR = { close: "Fechar", paginationLabel: "Paginação", previous: "Anterior", next: "Próxima", breadcrumbLabel: "Breadcrumb", tabsLabel: "Abas", optionsLabel: "Opções", tableLabel: "Tabela", commandLabel: "Paleta de comandos", commandPlaceholder: "Digite um comando…", dismissNotification: "Dispensar notificação", toolbarLabel: "Barra de ferramentas", comboboxEmpty: "Nenhum resultado", comboboxClear: "Limpar seleção", comboboxOpen: "Abrir lista", comboboxRemove: "Remover", comboboxLoading: "Carregando…", fileDropPrompt: "Arraste arquivos aqui ou clique para selecionar", fileAdded: "Arquivo adicionado", fileRemoved: "Arquivo removido", fileRemove: "Remover", fileTooLarge: "Arquivo maior que o limite", fileWrongType: "Tipo de arquivo não aceito", treeLabel: "Árvore", notificationsLabel: "Notificações", notificationMarkAll: "Marcar todas como lidas", notificationEmpty: "Nenhuma notificação", notificationUnread: "Não lida", notificationNew: "novas notificações", dataGridFilter: "Filtrar", dataGridEmpty: "Nenhum resultado", dataGridSelectAll: "Selecionar todas as linhas", dataGridSelectRow: "Selecionar linha", mediaPlayer: "Reprodutor de mídia", mediaPlay: "Reproduzir", mediaPause: "Pausar", mediaMute: "Silenciar", mediaUnmute: "Ativar som", mediaSeek: "Posição da reprodução", mediaVolume: "Volume", mediaCaptionsShow: "Ativar legendas", mediaCaptionsHide: "Desativar legendas", mediaSkipBack: "Voltar 10 segundos", mediaSkipForward: "Avançar 10 segundos", mediaFullscreenEnter: "Tela cheia", mediaFullscreenExit: "Sair da tela cheia", uploadSending: "Enviando", uploadCancel: "Cancelar envio", uploadRetry: "Tentar envio novamente", uploadError: "Falha no envio", uploadCanceled: "Envio cancelado", uploadComplete: "Envio concluído", codeEditor: "Editor de código", chatLabel: "Conversa", chatMessage: "Mensagem", chatSend: "Enviar", qrCode: "Código QR", copyCode: "Copiar código", tocLabel: "Nesta página", navigationToggle: "Navegação" };
15
+ import { createContext, useContext, useEffect, useRef, useState } from "react";
16
+ import { defaultStrings, defaultSpriteUrl } from "./pure.js";
17
+ export { cx, defaultStrings, ptBR, defaultSpriteUrl, universalStates, stateSeverity } from "./pure.js";
15
18
  export const StringsContext = createContext(defaultStrings);
16
19
  export const useAureaStrings = () => useContext(StringsContext);
17
- // Sprite de ícones: CONFIGURAÇÃO DE APLICAÇÃO, então entra pelo provider, uma vez.
18
- // Antes o default absoluto "/aurea-icons.svg" vivia no Icon e 15 componentes declaravam
19
- // e repassavam `spriteUrl` à mão — 52 sítios de código. Consequência: aplicação servida
20
- // em subcaminho perdia TODOS os ícones, e para consertar tinha de passar a prop em cada
21
- // componente (auditoria 26/07/2026, achado A4 — causa raiz do A3 daquela auditoria, que
22
- // foi corrigido nos fallbacks do catálogo e não aqui). O default segue o mesmo para quem
23
- // não configura nada; `Icon` aceita a prop como override local.
24
- export const defaultSpriteUrl = "/aurea-icons.svg";
25
20
  export const SpriteContext = createContext(defaultSpriteUrl);
26
21
  export const useSpriteUrl = () => useContext(SpriteContext);
27
- // direction informa o Base UI (lado dos popovers, setas do teclado). O CSS
28
- // espelha sozinho por propriedade lógica, mas depende do atributo dir no DOM:
29
- // quem consome precisa pôr dir="rtl" no <html>. Este provider não mexe no DOM.
30
- // Kbd representação de tecla/atalho. <kbd> é o elemento HTML certo; a pele é nossa.
31
- export function Kbd({ children, className, ...props }) { return _jsx("kbd", { className: cx("kbd", className), ...props, children: children }); }
22
+ // ONDE OS POPUPS SÃO MONTADOS achado B7, fechado na Parte C do PLANO-1.0 (07/08/2026).
23
+ // O DEFEITO não era o portal: era o consumidor não ter como mexer nele. Todo popup da Aurea
24
+ // (diálogo, gaveta, dica, popover, os dois menus, notificação, combobox, toast) monta num
25
+ // portal no nível do `body` é o que permite empilhar e posicionar sem herdar `overflow` nem
26
+ // `transform` de ninguém. A consequência é que o conteúdo dele fica FORA de qualquer landmark,
27
+ // e o axe acusa `region` ("todo conteúdo num landmark"). A auditoria registrou, e escreveu que
28
+ // "a única forma de satisfazer seria o consumidor montar o portal dentro do landmark dele" —
29
+ // só que ele não tinha essa forma, porque o `container` do Base UI nunca foi exposto.
30
+ // Agora tem, e entra pelo provider, UMA vez, como o sprite: é configuração de aplicação, não
31
+ // prop de componente (foi a lição do achado A4, com 52 sítios repassando `spriteUrl` à mão).
32
+ // `undefined` é o default e mantém o comportamento de sempre: `document.body`.
33
+ export const PortalContext = createContext(undefined);
34
+ export const usePortalContainer = () => useContext(PortalContext);
35
+ export function useReorder({ count, order, onReorder, rowSelector, handleSelector }) {
36
+ const s = useAureaStrings();
37
+ const ref = useRef(null);
38
+ const [pego, setPego] = useState(null);
39
+ const [foco, setFoco] = useState(null);
40
+ const [aviso, setAviso] = useState("");
41
+ const origem = useRef(null);
42
+ const arrasto = useRef(null);
43
+ const anuncia = (verbo, i) => setAviso(`${verbo}: ${i + 1} ${s.positionOf} ${count}`);
44
+ const alcas = () => [...(ref.current?.querySelectorAll(handleSelector) ?? [])];
45
+ // Vai por efeito e não por `requestAnimationFrame`: o React precisa ter commitado a ordem nova
46
+ // antes de procurar a alça.
47
+ useEffect(() => { if (foco !== null) {
48
+ alcas()[foco]?.focus();
49
+ setFoco(null);
50
+ } }, [foco, order]);
51
+ const mover = (de, para) => { if (para >= 0 && para < count && de !== para)
52
+ onReorder(de, para); };
53
+ const teclado = (e, i) => {
54
+ if (e.key === " " || e.key === "Enter") {
55
+ e.preventDefault();
56
+ if (pego === null) {
57
+ origem.current = i;
58
+ setPego(i);
59
+ anuncia(s.sortableGrabbed, i);
60
+ }
61
+ else {
62
+ setPego(null);
63
+ origem.current = null;
64
+ anuncia(s.sortableDropped, i);
65
+ }
66
+ return;
67
+ }
68
+ // As setas não podem fazer NADA com o item solto, senão o componente sequestra a navegação.
69
+ if (pego === null)
70
+ return;
71
+ if (e.key === "ArrowUp" || e.key === "ArrowDown") {
72
+ e.preventDefault();
73
+ const para = i + (e.key === "ArrowUp" ? -1 : 1);
74
+ if (para < 0 || para >= count)
75
+ return;
76
+ mover(i, para);
77
+ setPego(para);
78
+ setFoco(para);
79
+ anuncia(s.sortableMoved, para);
80
+ }
81
+ else if (e.key === "Escape") {
82
+ e.preventDefault();
83
+ const volta = origem.current;
84
+ if (volta !== null && volta !== i) {
85
+ mover(i, volta);
86
+ setFoco(volta);
87
+ }
88
+ setPego(null);
89
+ origem.current = null;
90
+ anuncia(s.sortableCanceled, volta ?? i);
91
+ }
92
+ };
93
+ // O alvo é a primeira linha cujo MEIO ainda está abaixo do ponteiro — a conta que faz o item
94
+ // trocar de lugar ao passar da metade da linha vizinha, e não ao encostar nela.
95
+ const ponteiroMove = (e) => {
96
+ const de = arrasto.current;
97
+ if (de === null)
98
+ return;
99
+ const linhas = [...(ref.current?.querySelectorAll(rowSelector) ?? [])];
100
+ const achado = linhas.findIndex(l => { const r = l.getBoundingClientRect(); return e.clientY < r.top + r.height / 2; });
101
+ const para = achado === -1 ? linhas.length - 1 : achado;
102
+ if (para !== de) {
103
+ mover(de, para);
104
+ arrasto.current = para;
105
+ setPego(para);
106
+ }
107
+ };
108
+ const ponteiroSolta = () => {
109
+ if (arrasto.current === null)
110
+ return;
111
+ anuncia(s.sortableDropped, arrasto.current);
112
+ arrasto.current = null;
113
+ setPego(null);
114
+ };
115
+ const ponteiroBaixo = (e, i) => {
116
+ if (e.pointerType === "mouse" && e.button !== 0)
117
+ return;
118
+ e.currentTarget.setPointerCapture(e.pointerId);
119
+ arrasto.current = i;
120
+ setPego(i);
121
+ };
122
+ return { ref, pego, aviso, teclado, ponteiroBaixo, ponteiroMove, ponteiroSolta };
123
+ }
@@ -0,0 +1,9 @@
1
+ import { type HTMLAttributes, type RefAttributes, type ReactNode } from "react";
2
+ import { type TopbarVariant } from "./navigation.js";
3
+ export declare function AppShell({ brand, navigation, topbar, topbarVariant, sidebarCollapsed, children, className, ...props }: HTMLAttributes<HTMLDivElement> & RefAttributes<HTMLDivElement> & {
4
+ brand: ReactNode;
5
+ navigation: ReactNode;
6
+ topbar?: ReactNode;
7
+ topbarVariant?: Exclude<TopbarVariant, "pill">;
8
+ sidebarCollapsed?: boolean;
9
+ }): import("react").JSX.Element;
@@ -0,0 +1,72 @@
1
+ "use client";
2
+ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { cx, useAureaStrings } from "./internal.js";
4
+ import { IconButton } from "./actions.js";
5
+ import { Sidebar, Topbar } from "./navigation.js";
6
+ // ── Auxiliar de topo: mora ANTES do primeiro export, e a posição é obrigatória ───────────────
7
+ // O check 22 mede o corpo de um componente do `export` dele até o PRÓXIMO export — fatia longa
8
+ // de propósito, porque a implementação de vários componentes daqui continua num auxiliar não
9
+ // exportado logo abaixo (a `Sidebar` é o caso: o `aria-current` dela mora num). Encurtar a fatia
10
+ // foi tentado em 09/08/2026 e a prova contra o defeito recusou: a `Sidebar` com `states: []`
11
+ // deixava de ser acusada.
12
+ // Consequência: auxiliar escrito ENTRE dois exports é lido como parte do componente anterior.
13
+ // Esta função tem `button:not([disabled])` num seletor, e o `\bdisabled\b` da regra acusou o
14
+ // `Grid` de emitir estado que ele não emite. Escrita aqui em cima, não pertence à fatia de
15
+ // ninguém. **Auxiliar de topo em módulo de componente vem antes dos exports.**
16
+ //
17
+ // O que ela faz e por que existe: o popover nativo não entrega a mesma gestão de foco nos três
18
+ // motores, e a tabela medida está no `aurea.js` do core. As mesmas cinco linhas moram lá, e a
19
+ // repetição é deliberada porque são DOIS RUNTIMES, não dois desenhos — o do core é delegado e
20
+ // serve página estática e consumo vanilla (é o que o catálogo carrega); este serve quem instala
21
+ // só o `@aurea-uds/react`, que importa o CSS do core e nunca o JS dele. Rodar os dois é
22
+ // inofensivo: focar o mesmo elemento duas vezes não faz nada.
23
+ //
24
+ // A comparação é com `"closed"` e NÃO com `"open"`, e isso é cicatriz — não simplifique de
25
+ // volta. O `ToggleEvent.newState` só tem esses dois valores, então as duas formas são idênticas
26
+ // em efeito; a diferença é no GATE. O check 15 trata qualquer string do fonte como possível nome
27
+ // de classe (de propósito: a versão estrita não vê `badge-${variant}` e quase fez o D1 apagar
28
+ // classes vivas). Com o literal `"open"` aqui, a `.drawer-wrap.open` do core — resto morto da
29
+ // página vanilla removida na Parte D — passava a parecer PRODUZIDA pela biblioteca, e a catraca
30
+ // queria travar um ganho falso.
31
+ function focoDaGaveta(e) {
32
+ const gaveta = e.currentTarget;
33
+ // Primeiro focável de DENTRO, não a gaveta: o WebKit não entra no conteúdo do popover pelo
34
+ // Tab nem com o foco no elemento dele — medido no K1.
35
+ if (e.newState !== "closed") {
36
+ const primeiro = gaveta.querySelector('a[href], button:not([disabled]), input:not([disabled]), select, textarea, [tabindex]:not([tabindex="-1"])');
37
+ if (primeiro)
38
+ primeiro.focus();
39
+ else {
40
+ gaveta.tabIndex = -1;
41
+ gaveta.focus();
42
+ }
43
+ return;
44
+ }
45
+ // Só devolve o foco se ele ficou órfão; se a pessoa já o moveu, puxá-lo de volta seria roubo.
46
+ if (document.activeElement === document.body || gaveta.contains(document.activeElement))
47
+ document.querySelector(`[popovertarget="${gaveta.id}"]`)?.focus();
48
+ }
49
+ // API pública intacta: as props seguem brand/navigation/topbar. O que mudou é ONDE a
50
+ // marca sai — no topo, não na lateral. `topbarVariant` escolhe a pele do topo; o shell
51
+ // precisa saber porque a lateral se encaixa abaixo dele (o `flush` não tem folga em
52
+ // cima, então ela sobe um --space-4). `pill` é cabeçalho de site — não é pra shell.
53
+ // A lateral RECOLHE em tela estreita: o <aside> é um popover nativo e o botão é o invoker. O
54
+ // navegador entrega Escape, clique fora e o `aria-expanded` no disparador. Era o achado A8: em
55
+ // 375px o `h1` da página começava 4,6 telas abaixo, depois dos 65 itens da navegação.
56
+ // Não-modal de propósito, como o CommandPaletteShell: o foco não fica preso, e por isso não
57
+ // declaramos modalidade que não entregamos.
58
+ //
59
+ // ATÉ 09/08/2026 ESTE COMENTÁRIO DIZIA "sem uma linha de JavaScript" E QUE O NAVEGADOR TAMBÉM
60
+ // ENTREGAVA A VOLTA DO FOCO. Entregava — no Chromium, que era o único motor onde a suíte
61
+ // rodava. O item K1 pôs Firefox e WebKit no `playwright.config.ts` e a medição derrubou a
62
+ // frase: no WebKit o foco vai para o <body> ao abrir, OITO Tabs não entram na gaveta e o
63
+ // Escape não devolve nada. Gaveta aberta e inalcançável por teclado é WCAG 2.1.1.
64
+ // A gestão de foco agora é NOSSA, igual nos três motores — `focoDaGaveta`, abaixo.
65
+ // Id fixo em vez de useId: um documento tem UM AppShell (ele é dono do <main>), então não há
66
+ // colisão possível — e o CSS e o gate precisam de um alvo estável.
67
+ // `sidebarCollapsed` é CONTROLADO pelo consumidor, como o `open` do CommandPaletteShell: o
68
+ // shell não decide quando recolher nem desenha o botão que recolhe — quem sabe se há espaço,
69
+ // e se a preferência se guarda, é a aplicação. O que o shell faz é o que só ele pode: a coluna
70
+ // do grid encolhe para --sidebar-rail (a regra `:has(.sidebar-collapsed)` no core).
71
+ const SHELL_NAV_ID = "aurea-shell-nav";
72
+ export function AppShell({ brand, navigation, topbar, topbarVariant = "floating", sidebarCollapsed, children, className, ...props }) { const s = useAureaStrings(); return _jsxs("div", { className: cx("app-shell", topbarVariant === "flush" && "app-shell-flush", className), ...props, children: [_jsx(Topbar, { variant: topbarVariant, brand: _jsxs(_Fragment, { children: [_jsx(IconButton, { className: "nav-toggle", icon: "menu", label: s.navigationToggle, popoverTarget: SHELL_NAV_ID }), brand] }), children: topbar }), _jsx(Sidebar, { id: SHELL_NAV_ID, popover: "auto", collapsed: sidebarCollapsed, onToggle: focoDaGaveta, children: navigation }), _jsx("main", { className: "content", children: children })] }); }
package/dist/layout.d.ts CHANGED
@@ -1,14 +1,2 @@
1
- import { type HTMLAttributes, type RefAttributes, type ReactNode } from "react";
2
- import { type TopbarVariant } from "./navigation.js";
3
- export declare function Card({ variant, className, ...props }: HTMLAttributes<HTMLDivElement> & RefAttributes<HTMLDivElement> & {
4
- variant?: "base" | "raised" | "interactive" | "inset" | "selected" | "danger";
5
- }): import("react").JSX.Element;
6
- export declare function Stack({ className, ...props }: HTMLAttributes<HTMLDivElement> & RefAttributes<HTMLDivElement>): import("react").JSX.Element;
7
- export declare function Cluster({ className, ...props }: HTMLAttributes<HTMLDivElement> & RefAttributes<HTMLDivElement>): import("react").JSX.Element;
8
- export declare function Grid({ className, ...props }: HTMLAttributes<HTMLDivElement> & RefAttributes<HTMLDivElement>): import("react").JSX.Element;
9
- export declare function AppShell({ brand, navigation, topbar, topbarVariant, children, className, ...props }: HTMLAttributes<HTMLDivElement> & RefAttributes<HTMLDivElement> & {
10
- brand: ReactNode;
11
- navigation: ReactNode;
12
- topbar?: ReactNode;
13
- topbarVariant?: Exclude<TopbarVariant, "pill">;
14
- }): import("react").JSX.Element;
1
+ export * from "./layout-client.js";
2
+ export { Card, Stack, Cluster, Grid } from "./markup.js";
package/dist/layout.js CHANGED
@@ -1,22 +1,5 @@
1
- import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { cx, useAureaStrings } from "./internal.js";
3
- import { IconButton } from "./actions.js";
4
- import { Sidebar, Topbar } from "./navigation.js";
5
- export function Card({ variant = "base", className, ...props }) { return _jsx("div", { className: cx("card", variant !== "base" && `card-${variant}`, className), ...props }); }
6
- export function Stack({ className, ...props }) { return _jsx("div", { className: cx("stack", className), ...props }); }
7
- export function Cluster({ className, ...props }) { return _jsx("div", { className: cx("cluster", className), ...props }); }
8
- export function Grid({ className, ...props }) { return _jsx("div", { className: cx("grid", className), ...props }); }
9
- // API pública intacta: as props seguem brand/navigation/topbar. O que mudou é ONDE a
10
- // marca sai — no topo, não na lateral. `topbarVariant` escolhe a pele do topo; o shell
11
- // precisa saber porque a lateral se encaixa abaixo dele (o `flush` não tem folga em
12
- // cima, então ela sobe um --space-4). `pill` é cabeçalho de site — não é pra shell.
13
- // A lateral RECOLHE em tela estreita, e sem uma linha de JavaScript: o <aside> é um popover
14
- // nativo e o botão é o invoker. O navegador entrega Escape, clique fora, `aria-expanded` no
15
- // disparador e a volta do foco para ele ao fechar — Baseline desde 04/2025, verificado em
16
- // 30/07/2026. Era o achado A8: em 375px o `h1` da página começava 4,6 telas abaixo, depois
17
- // dos 65 itens da navegação. Não-modal de propósito, como o CommandPaletteShell: o foco não
18
- // fica preso, e por isso não declaramos modalidade que não entregamos.
19
- // Id fixo em vez de useId: um documento tem UM AppShell (ele é dono do <main>), então não há
20
- // colisão possível — e o CSS e o gate precisam de um alvo estável.
21
- const SHELL_NAV_ID = "aurea-shell-nav";
22
- export function AppShell({ brand, navigation, topbar, topbarVariant = "floating", children, className, ...props }) { const s = useAureaStrings(); return _jsxs("div", { className: cx("app-shell", topbarVariant === "flush" && "app-shell-flush", className), ...props, children: [_jsx(Topbar, { variant: topbarVariant, brand: _jsxs(_Fragment, { children: [_jsx(IconButton, { className: "nav-toggle", icon: "menu", label: s.navigationToggle, popoverTarget: SHELL_NAV_ID }), brand] }), children: topbar }), _jsx(Sidebar, { id: SHELL_NAV_ID, popover: "auto", children: navigation }), _jsx("main", { className: "content", children: children })] }); }
1
+ // VITRINE da categoria é este arquivo que `@aurea-uds/react/layout` resolve. Sem `"use client"`
2
+ // de propósito: a diretiva contamina o módulo inteiro e faria a marcação pura chegar como cliente
3
+ // por vizinhança (ADR-0026; o check 26b reprova quem a puser de volta aqui).
4
+ export * from "./layout-client.js";
5
+ export { Card, Stack, Cluster, Grid } from "./markup.js";
@@ -0,0 +1,72 @@
1
+ import React, { type HTMLAttributes, type InputHTMLAttributes, type ReactNode, type RefAttributes, type SelectHTMLAttributes, type TextareaHTMLAttributes } from "react";
2
+ export type BadgeVariant = "neutral" | "primary" | "info" | "success" | "warning" | "danger" | "running" | "paused" | "offline" | "review";
3
+ export type AvatarSize = "sm" | "md" | "lg";
4
+ export type TopbarVariant = "floating" | "flush" | "pill";
5
+ export declare function Card({ variant, className, ...props }: HTMLAttributes<HTMLDivElement> & RefAttributes<HTMLDivElement> & {
6
+ variant?: "base" | "raised" | "interactive" | "inset" | "selected" | "danger";
7
+ }): React.JSX.Element;
8
+ export declare function Stack({ className, ...props }: HTMLAttributes<HTMLDivElement> & RefAttributes<HTMLDivElement>): React.JSX.Element;
9
+ export declare function Cluster({ className, ...props }: HTMLAttributes<HTMLDivElement> & RefAttributes<HTMLDivElement>): React.JSX.Element;
10
+ export declare function Grid({ className, ...props }: HTMLAttributes<HTMLDivElement> & RefAttributes<HTMLDivElement>): React.JSX.Element;
11
+ export declare function KPI({ label, value, trend, className, ...props }: HTMLAttributes<HTMLDivElement> & RefAttributes<HTMLDivElement> & {
12
+ label: ReactNode;
13
+ value: ReactNode;
14
+ trend?: ReactNode;
15
+ }): React.JSX.Element;
16
+ export declare function DataList({ items }: {
17
+ items: Array<{
18
+ term: ReactNode;
19
+ value: ReactNode;
20
+ }>;
21
+ }): React.JSX.Element;
22
+ export declare function Timeline({ items }: {
23
+ items: Array<{
24
+ title: ReactNode;
25
+ description?: ReactNode;
26
+ time?: ReactNode;
27
+ }>;
28
+ }): React.JSX.Element;
29
+ export declare function Prose({ className, ...props }: HTMLAttributes<HTMLDivElement> & RefAttributes<HTMLDivElement>): React.JSX.Element;
30
+ export declare function Badge({ variant, className, ...props }: HTMLAttributes<HTMLSpanElement> & RefAttributes<HTMLSpanElement> & {
31
+ variant?: BadgeVariant;
32
+ }): React.JSX.Element;
33
+ export declare function Progress({ value, label }: {
34
+ value: number;
35
+ label?: string;
36
+ }): React.JSX.Element;
37
+ export declare function Skeleton({ className, ...props }: HTMLAttributes<HTMLDivElement> & RefAttributes<HTMLDivElement>): React.JSX.Element;
38
+ export declare function AvatarGroup({ children, max, total, label, size, className }: {
39
+ children: ReactNode;
40
+ max?: number;
41
+ total?: number;
42
+ label?: string;
43
+ size?: AvatarSize;
44
+ className?: string;
45
+ }): React.JSX.Element;
46
+ export declare function LogStream({ lines }: {
47
+ lines: Array<{
48
+ time?: string;
49
+ level?: string;
50
+ text: string;
51
+ }>;
52
+ }): React.JSX.Element;
53
+ export declare function MediaPlayerShell({ children, className, ...props }: HTMLAttributes<HTMLDivElement> & RefAttributes<HTMLDivElement>): React.JSX.Element;
54
+ export declare function Topbar({ variant, brand, children, className, ...props }: HTMLAttributes<HTMLElement> & RefAttributes<HTMLElement> & {
55
+ variant?: TopbarVariant;
56
+ brand?: ReactNode;
57
+ }): React.JSX.Element;
58
+ export declare function Kbd({ children, className, ...props }: HTMLAttributes<HTMLElement> & RefAttributes<HTMLElement>): React.JSX.Element;
59
+ export declare const Select: React.ForwardRefExoticComponent<Omit<SelectHTMLAttributes<HTMLSelectElement> & RefAttributes<HTMLSelectElement>, "ref"> & RefAttributes<HTMLSelectElement>>;
60
+ export declare const Textarea: React.ForwardRefExoticComponent<Omit<TextareaHTMLAttributes<HTMLTextAreaElement> & RefAttributes<HTMLTextAreaElement>, "ref"> & RefAttributes<HTMLTextAreaElement>>;
61
+ export declare function Checkbox({ label, labelHidden, description, className, ...props }: InputHTMLAttributes<HTMLInputElement> & RefAttributes<HTMLInputElement> & {
62
+ label: ReactNode;
63
+ labelHidden?: boolean;
64
+ description?: ReactNode;
65
+ }): React.JSX.Element;
66
+ export declare function Radio({ label, className, ...props }: InputHTMLAttributes<HTMLInputElement> & RefAttributes<HTMLInputElement> & {
67
+ label: ReactNode;
68
+ }): React.JSX.Element;
69
+ export declare function Switch({ label, className, ...props }: InputHTMLAttributes<HTMLInputElement> & RefAttributes<HTMLInputElement> & {
70
+ label: ReactNode;
71
+ }): React.JSX.Element;
72
+ export declare function Range(props: InputHTMLAttributes<HTMLInputElement> & RefAttributes<HTMLInputElement>): React.JSX.Element;
package/dist/markup.js ADDED
@@ -0,0 +1,87 @@
1
+ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ // SEM `"use client"`, e a ausência é o ponto deste arquivo — item O1 do PLANO-1.0, decidido na
3
+ // [ADR-0026](../../../decisions/0026-marcacao-pura-e-de-servidor.md).
4
+ //
5
+ // O QUE ESTÁ AQUI: os componentes que não fazem NADA que exija o navegador. Sem hook, sem
6
+ // manipulador preso em JSX, sem motor de terceiro. `cx` mais marcação. A medição está em
7
+ // `node scripts/measure-boundary.mjs`, e ela é o motivo de o arquivo existir:
8
+ //
9
+ // importar `Card` — uma <div> com uma classe — embarcava 9 módulos e 118,5 KB de JavaScript,
10
+ // porque `layout.js` tem a diretiva e num framework de RSC a diretiva contamina o módulo
11
+ // INTEIRO e o fecho transitivo dele. `Prose`, que também é uma <div>, custava 123,5 KB.
12
+ //
13
+ // A PROVA DE QUE ISSO SE RESOLVE JÁ ESTAVA NO REPOSITÓRIO: o `Accordion` mora no
14
+ // `disclosure.tsx`, que nunca teve a diretiva, e importá-lo custa **0,3 KB**. Mesma natureza do
15
+ // `Card`, três ordens de grandeza de diferença — e a única distinção era o arquivo em que cada um
16
+ // tinha caído na Fase 9.
17
+ //
18
+ // POR QUE UM MÓDULO NOVO E NÃO UM `.pure` POR CATEGORIA: o precedente é o `Kbd`, e ele já estava
19
+ // escrito no `MAP.md`. Ele mora no `internal.tsx` **por dependência** (o `Button` precisa dele) e
20
+ // a casa PÚBLICA dele é `/data-display`, que o reexporta. Aqui é a mesma regra em escala: o
21
+ // arquivo é onde a coisa mora, a categoria é o que a ficha declara, e as duas não precisam
22
+ // coincidir. A taxonomia do registry não muda, e nenhuma ficha muda.
23
+ //
24
+ // A API NÃO QUEBRA. Cada módulo de categoria reexporta os seus, então
25
+ // `@aurea-uds/react/layout` continua entregando `Card`. O que muda é o `index.tsx`: ele exporta
26
+ // estes nomes EXPLICITAMENTE daqui, e é isso — e só isso — que faz
27
+ // `import {Card} from "@aurea-uds/react"` chegar como componente de SERVIDOR. Sem a linha
28
+ // explícita o nome ainda existe, vindo do módulo de categoria, e volta a ser cliente sem que
29
+ // nada reprove. O check 35 cobra a linha, e foi provado contra esse defeito exato.
30
+ //
31
+ // LIMITE DECLARADO: pelo subpath da categoria (`@aurea-uds/react/layout`) o `Card` continua
32
+ // vindo de um módulo com a diretiva, porque o `AppShell` mora lá e a diretiva é do ARQUIVO. Quem
33
+ // renderiza no servidor por SEO deve importar do barril. Separar cada categoria em duas entradas
34
+ // resolveria também esse caminho, e é mudança de fronteira pública — não entra sem o Victor.
35
+ //
36
+ // PARA QUEM VIER: componente novo só entra aqui se não chamar hook, não prender manipulador e não
37
+ // tocar motor. Na dúvida, ele NÃO entra — cliente a mais é lento, servidor a mais é quebrado.
38
+ import React, { forwardRef } from "react";
39
+ import { cx } from "./pure.js";
40
+ // ── Layout ───────────────────────────────────────────────────────────────────────────────────
41
+ export function Card({ variant = "base", className, ...props }) { return _jsx("div", { className: cx("card", variant !== "base" && `card-${variant}`, className), ...props }); }
42
+ export function Stack({ className, ...props }) { return _jsx("div", { className: cx("stack", className), ...props }); }
43
+ export function Cluster({ className, ...props }) { return _jsx("div", { className: cx("cluster", className), ...props }); }
44
+ export function Grid({ className, ...props }) { return _jsx("div", { className: cx("grid", className), ...props }); }
45
+ // ── Data Display ─────────────────────────────────────────────────────────────────────────────
46
+ export function KPI({ label, value, trend, className, ...props }) { return _jsxs(Card, { className: cx("kpi", className), ...props, children: [_jsx("span", { className: "muted", children: label }), _jsx("strong", { children: value }), trend && _jsx("small", { children: trend })] }); }
47
+ export function DataList({ items }) { return _jsx("dl", { className: "data-list", children: items.map((i, n) => _jsxs(React.Fragment, { children: [_jsx("dt", { children: i.term }), _jsx("dd", { children: i.value })] }, n)) }); }
48
+ export function Timeline({ items }) { return _jsx("ol", { className: "timeline", children: items.map((i, n) => _jsxs("li", { children: [_jsx("span", { className: "timeline-dot" }), _jsxs("div", { children: [_jsx("strong", { children: i.title }), i.description && _jsx("p", { children: i.description }), i.time && _jsx("small", { className: "muted", children: i.time })] })] }, n)) }); }
49
+ // Prose (item L5): UMA LINHA, e é para ser mesmo — quem transforma Markdown em elementos é o
50
+ // consumidor. A trava do item é no CSS, escopada em `.prose`, e há prova dela no `skin.spec`.
51
+ // Segurança, porque é o caminho de uso mais provável: quem entrega HTML de terceiro por
52
+ // `dangerouslySetInnerHTML` tem de SANITIZAR antes. A Aurea desenha o texto; ela não pode decidir
53
+ // o que é seguro renderizar. Está dito na ficha, em voz alta.
54
+ export function Prose({ className, ...props }) { return _jsx("div", { className: cx("prose", className), ...props }); }
55
+ // ── Feedback ─────────────────────────────────────────────────────────────────────────────────
56
+ export function Badge({ variant = "neutral", className, ...props }) { return _jsx("span", { className: cx("badge", variant !== "neutral" && `badge-${variant}`, className), ...props }); }
57
+ export function Progress({ value, label }) { const pct = Math.max(0, Math.min(100, value)); return _jsx("div", { children: _jsx("div", { className: "progress", role: "progressbar", "aria-label": label, "aria-valuemin": 0, "aria-valuemax": 100, "aria-valuenow": pct, children: _jsx("span", { style: { width: `${pct}%` } }) }) }); }
58
+ export function Skeleton({ className, ...props }) { return _jsx("div", { className: cx("skeleton", className), "aria-hidden": "true", ...props }); }
59
+ // ── Identity ─────────────────────────────────────────────────────────────────────────────────
60
+ // `React.Children` não é hook — é utilitário de leitura de `children`, e roda no servidor.
61
+ export function AvatarGroup({ children, max, total, label, size = "md", className }) {
62
+ const itens = React.Children.toArray(children);
63
+ const mostra = max ? itens.slice(0, max) : itens;
64
+ const resto = (total ?? itens.length) - mostra.length;
65
+ return _jsxs("div", { role: "group", "aria-label": label, className: cx("avatar-group", className), children: [mostra, resto > 0 && _jsxs("span", { className: cx("avatar", "avatar-count", size !== "md" && `avatar-${size}`), "aria-hidden": "true", children: ["+", resto] })] });
66
+ }
67
+ // ── Code ─────────────────────────────────────────────────────────────────────────────────────
68
+ export function LogStream({ lines }) { return _jsx("div", { className: "log-stream", role: "log", "aria-live": "polite", children: lines.map((l, n) => _jsxs("div", { className: cx("log-line", l.level && `log-${l.level}`), children: [_jsx("time", { className: "log-time", children: l.time }), _jsx("span", { className: "log-level", children: l.level }), _jsx("span", { children: l.text })] }, n)) }); }
69
+ // ── Media ────────────────────────────────────────────────────────────────────────────────────
70
+ export function MediaPlayerShell({ children, className, ...props }) { return _jsx("div", { className: cx("media-player", className), ...props, children: children }); }
71
+ // ── Navigation ───────────────────────────────────────────────────────────────────────────────
72
+ export function Topbar({ variant = "floating", brand, children, className, ...props }) { return _jsxs("header", { className: cx("topbar", `topbar-${variant}`, className), ...props, children: [brand != null && _jsx("div", { className: "brand", children: brand }), children] }); }
73
+ // ── System / interno ─────────────────────────────────────────────────────────────────────────
74
+ // Kbd — representação de tecla/atalho. <kbd> é o elemento HTML certo; a pele é nossa. Ele já
75
+ // morava fora da casa dele (no `internal.tsx`, por causa do `Button`); agora mora aqui, pelo
76
+ // mesmo motivo elevado a regra.
77
+ export function Kbd({ children, className, ...props }) { return _jsx("kbd", { className: cx("kbd", className), ...props, children: children }); }
78
+ // ── Inputs ───────────────────────────────────────────────────────────────────────────────────
79
+ // Os seis controles abaixo NÃO prendem manipulador nenhum: quem escuta `onChange` é o consumidor,
80
+ // e por isso eles são marcação. O `Field`, o `Input` e o `Combobox` ficaram no `inputs.tsx` —
81
+ // aqueles chamam hook de verdade.
82
+ export const Select = forwardRef(function Select({ className, ...props }, ref) { return _jsx("select", { ref: ref, className: cx("select", className), ...props }); });
83
+ export const Textarea = forwardRef(function Textarea({ className, ...props }, ref) { return _jsx("textarea", { ref: ref, className: cx("textarea", className), ...props }); });
84
+ export function Checkbox({ label, labelHidden, description, className, ...props }) { return _jsxs("label", { className: cx("checkbox", labelHidden && "checkbox-bare", className), children: [_jsx("input", { type: "checkbox", ...props }), _jsx("span", { className: "control-mark" }), _jsxs("span", { className: labelHidden ? "sr-only" : undefined, children: [_jsx("strong", { children: label }), description && _jsxs(_Fragment, { children: [_jsx("br", {}), _jsx("span", { className: "muted", children: description })] })] })] }); }
85
+ export function Radio({ label, className, ...props }) { return _jsxs("label", { className: cx("radio", className), children: [_jsx("input", { type: "radio", ...props }), _jsx("span", { className: "control-mark" }), label] }); }
86
+ export function Switch({ label, className, ...props }) { return _jsxs("label", { className: cx("switch", className), children: [_jsx("input", { type: "checkbox", role: "switch", ...props }), _jsx("span", { className: "switch-track" }), _jsx("span", { children: label })] }); }
87
+ export function Range(props) { return _jsx("input", { className: cx("range", props.className), type: "range", ...props }); }
@@ -0,0 +1,36 @@
1
+ import React, { type HTMLAttributes, type RefAttributes, type ReactNode } from "react";
2
+ export declare function spokenTime(sec: number): string;
3
+ export interface MediaPlayerProps extends Omit<React.VideoHTMLAttributes<HTMLVideoElement> & RefAttributes<HTMLVideoElement>, "title"> {
4
+ kind?: "video" | "audio";
5
+ title?: ReactNode;
6
+ subtitle?: ReactNode;
7
+ }
8
+ export declare function MediaPlayer({ kind, src, poster, title, subtitle, className, children, onClick, onPlay, onPause, onEnded, onTimeUpdate, onLoadedMetadata, onDurationChange, onProgress, onVolumeChange, ...rest }: MediaPlayerProps): React.JSX.Element;
9
+ export interface ImageProps extends Omit<React.ImgHTMLAttributes<HTMLImageElement>, "children"> {
10
+ ratio?: string;
11
+ fit?: "cover" | "contain";
12
+ render?: React.ReactElement;
13
+ }
14
+ export declare function Image({ ratio, fit, alt, className, style, render, onError, ...props }: ImageProps): React.JSX.Element;
15
+ export interface GalleryItem {
16
+ id: string;
17
+ src: string;
18
+ alt: string;
19
+ caption?: ReactNode;
20
+ }
21
+ export interface GalleryProps extends Omit<HTMLAttributes<HTMLUListElement>, "onSelect">, RefAttributes<HTMLUListElement> {
22
+ items: GalleryItem[];
23
+ label?: string;
24
+ selected?: string;
25
+ onSelect?: (id: string) => void;
26
+ zoom?: boolean;
27
+ ratio?: string;
28
+ }
29
+ export declare function Gallery({ items, label, selected, onSelect, zoom, ratio, className, ...props }: GalleryProps): React.JSX.Element;
30
+ export interface CarouselProps extends Omit<HTMLAttributes<HTMLDivElement>, "children">, RefAttributes<HTMLDivElement> {
31
+ children?: ReactNode;
32
+ label?: string;
33
+ controls?: boolean;
34
+ indicators?: boolean;
35
+ }
36
+ export declare function Carousel({ children, label, controls, indicators, className, ...props }: CarouselProps): React.JSX.Element;