@aurea-uds/react 0.2.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 (49) hide show
  1. package/README.md +34 -2
  2. package/dist/actions.d.ts +10 -3
  3. package/dist/actions.js +55 -6
  4. package/dist/code-client.d.ts +6 -0
  5. package/dist/code-client.js +24 -0
  6. package/dist/code.d.ts +2 -13
  7. package/dist/code.js +5 -34
  8. package/dist/data-display-client.d.ts +14 -0
  9. package/dist/data-display-client.js +22 -0
  10. package/dist/data-display.d.ts +2 -23
  11. package/dist/data-display.js +5 -14
  12. package/dist/feedback-client.d.ts +64 -0
  13. package/dist/feedback-client.js +110 -0
  14. package/dist/feedback.d.ts +2 -63
  15. package/dist/feedback.js +5 -101
  16. package/dist/file-input.js +57 -9
  17. package/dist/identity-client.d.ts +9 -0
  18. package/dist/identity-client.js +12 -0
  19. package/dist/identity.d.ts +2 -17
  20. package/dist/identity.js +5 -25
  21. package/dist/index.d.ts +2 -1
  22. package/dist/index.js +19 -3
  23. package/dist/inputs-client.d.ts +105 -0
  24. package/dist/inputs-client.js +264 -0
  25. package/dist/inputs.d.ts +2 -91
  26. package/dist/inputs.js +9 -126
  27. package/dist/internal.d.ts +20 -5
  28. package/dist/internal.js +91 -10
  29. package/dist/layout-client.d.ts +9 -0
  30. package/dist/layout-client.js +72 -0
  31. package/dist/layout.d.ts +2 -15
  32. package/dist/layout.js +5 -76
  33. package/dist/markup.d.ts +72 -0
  34. package/dist/markup.js +87 -0
  35. package/dist/media-client.d.ts +36 -0
  36. package/dist/media-client.js +239 -0
  37. package/dist/media.d.ts +2 -9
  38. package/dist/media.js +5 -99
  39. package/dist/navigation-client.d.ts +94 -0
  40. package/dist/navigation-client.js +154 -0
  41. package/dist/navigation.d.ts +2 -85
  42. package/dist/navigation.js +5 -138
  43. package/dist/overlays.d.ts +23 -0
  44. package/dist/overlays.js +45 -1
  45. package/dist/pure.d.ts +30 -0
  46. package/dist/pure.js +26 -2
  47. package/dist/system.d.ts +9 -0
  48. package/dist/system.js +46 -0
  49. package/package.json +180 -176
package/dist/internal.js CHANGED
@@ -1,5 +1,4 @@
1
1
  "use client";
2
- import { jsx as _jsx } from "react/jsx-runtime";
3
2
  // A diretiva na LINHA 1, como as referências a escrevem (Base UI e shadcn/ui, medidos em
4
3
  // 06/08/2026) — Parte A do PLANO-1.0. Este módulo chama `createContext`, que é API só de
5
4
  // cliente: sem a diretiva, `import {Button} from "@aurea-uds/react"` dentro de um componente
@@ -13,10 +12,8 @@ import { jsx as _jsx } from "react/jsx-runtime";
13
12
  // pure → internal → system → actions → feedback → inputs → navigation → layout → data-display → resto.
14
13
  // INTERNO: o que todo módulo precisa e ninguém publica como componente próprio.
15
14
  // NÃO é subpath — `@aurea-uds/react/internal` não existe, de propósito.
16
- // Kbd mora aqui por dependência, não por categoria: o Button precisa dele e ele não precisa de
17
- // ninguém. A casa pública dele continua sendo `/data-display`, que o reexporta.
18
- import { createContext, useContext } from "react";
19
- import { cx, defaultStrings, defaultSpriteUrl } from "./pure.js";
15
+ import { createContext, useContext, useEffect, useRef, useState } from "react";
16
+ import { defaultStrings, defaultSpriteUrl } from "./pure.js";
20
17
  export { cx, defaultStrings, ptBR, defaultSpriteUrl, universalStates, stateSeverity } from "./pure.js";
21
18
  export const StringsContext = createContext(defaultStrings);
22
19
  export const useAureaStrings = () => useContext(StringsContext);
@@ -35,8 +32,92 @@ export const useSpriteUrl = () => useContext(SpriteContext);
35
32
  // `undefined` é o default e mantém o comportamento de sempre: `document.body`.
36
33
  export const PortalContext = createContext(undefined);
37
34
  export const usePortalContainer = () => useContext(PortalContext);
38
- // direction informa o Base UI (lado dos popovers, setas do teclado). O CSS
39
- // espelha sozinho por propriedade lógica, mas depende do atributo dir no DOM:
40
- // quem consome precisa pôr dir="rtl" no <html>. Este provider não mexe no DOM.
41
- // Kbd representação de tecla/atalho. <kbd> é o elemento HTML certo; a pele é nossa.
42
- export function Kbd({ children, className, ...props }) { return _jsx("kbd", { className: cx("kbd", className), ...props, children: children }); }
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,15 +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, sidebarCollapsed, children, className, ...props }: HTMLAttributes<HTMLDivElement> & RefAttributes<HTMLDivElement> & {
10
- brand: ReactNode;
11
- navigation: ReactNode;
12
- topbar?: ReactNode;
13
- topbarVariant?: Exclude<TopbarVariant, "pill">;
14
- sidebarCollapsed?: boolean;
15
- }): 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,76 +1,5 @@
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
- export function Card({ variant = "base", className, ...props }) { return _jsx("div", { className: cx("card", variant !== "base" && `card-${variant}`, className), ...props }); }
50
- export function Stack({ className, ...props }) { return _jsx("div", { className: cx("stack", className), ...props }); }
51
- export function Cluster({ className, ...props }) { return _jsx("div", { className: cx("cluster", className), ...props }); }
52
- export function Grid({ className, ...props }) { return _jsx("div", { className: cx("grid", className), ...props }); }
53
- // API pública intacta: as props seguem brand/navigation/topbar. O que mudou é ONDE a
54
- // marca sai — no topo, não na lateral. `topbarVariant` escolhe a pele do topo; o shell
55
- // precisa saber porque a lateral se encaixa abaixo dele (o `flush` não tem folga em
56
- // cima, então ela sobe um --space-4). `pill` é cabeçalho de site — não é pra shell.
57
- // A lateral RECOLHE em tela estreita: o <aside> é um popover nativo e o botão é o invoker. O
58
- // navegador entrega Escape, clique fora e o `aria-expanded` no disparador. Era o achado A8: em
59
- // 375px o `h1` da página começava 4,6 telas abaixo, depois dos 65 itens da navegação.
60
- // Não-modal de propósito, como o CommandPaletteShell: o foco não fica preso, e por isso não
61
- // declaramos modalidade que não entregamos.
62
- //
63
- // ATÉ 09/08/2026 ESTE COMENTÁRIO DIZIA "sem uma linha de JavaScript" E QUE O NAVEGADOR TAMBÉM
64
- // ENTREGAVA A VOLTA DO FOCO. Entregava — no Chromium, que era o único motor onde a suíte
65
- // rodava. O item K1 pôs Firefox e WebKit no `playwright.config.ts` e a medição derrubou a
66
- // frase: no WebKit o foco vai para o <body> ao abrir, OITO Tabs não entram na gaveta e o
67
- // Escape não devolve nada. Gaveta aberta e inalcançável por teclado é WCAG 2.1.1.
68
- // A gestão de foco agora é NOSSA, igual nos três motores — `focoDaGaveta`, abaixo.
69
- // Id fixo em vez de useId: um documento tem UM AppShell (ele é dono do <main>), então não há
70
- // colisão possível — e o CSS e o gate precisam de um alvo estável.
71
- // `sidebarCollapsed` é CONTROLADO pelo consumidor, como o `open` do CommandPaletteShell: o
72
- // shell não decide quando recolher nem desenha o botão que recolhe — quem sabe se há espaço,
73
- // e se a preferência se guarda, é a aplicação. O que o shell faz é o que só ele pode: a coluna
74
- // do grid encolhe para --sidebar-rail (a regra `:has(.sidebar-collapsed)` no core).
75
- const SHELL_NAV_ID = "aurea-shell-nav";
76
- 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 })] }); }
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;