@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.
- package/README.md +34 -2
- package/dist/actions.d.ts +10 -3
- package/dist/actions.js +55 -6
- package/dist/code-client.d.ts +6 -0
- package/dist/code-client.js +24 -0
- package/dist/code.d.ts +2 -13
- package/dist/code.js +5 -34
- package/dist/data-display-client.d.ts +14 -0
- package/dist/data-display-client.js +22 -0
- package/dist/data-display.d.ts +2 -23
- package/dist/data-display.js +5 -14
- package/dist/feedback-client.d.ts +64 -0
- package/dist/feedback-client.js +110 -0
- package/dist/feedback.d.ts +2 -63
- package/dist/feedback.js +5 -101
- package/dist/file-input.js +57 -9
- package/dist/identity-client.d.ts +9 -0
- package/dist/identity-client.js +12 -0
- package/dist/identity.d.ts +2 -17
- package/dist/identity.js +5 -25
- package/dist/index.d.ts +2 -1
- package/dist/index.js +19 -3
- package/dist/inputs-client.d.ts +105 -0
- package/dist/inputs-client.js +264 -0
- package/dist/inputs.d.ts +2 -91
- package/dist/inputs.js +9 -126
- package/dist/internal.d.ts +20 -5
- package/dist/internal.js +91 -10
- package/dist/layout-client.d.ts +9 -0
- package/dist/layout-client.js +72 -0
- package/dist/layout.d.ts +2 -15
- package/dist/layout.js +5 -76
- package/dist/markup.d.ts +72 -0
- package/dist/markup.js +87 -0
- package/dist/media-client.d.ts +36 -0
- package/dist/media-client.js +239 -0
- package/dist/media.d.ts +2 -9
- package/dist/media.js +5 -99
- package/dist/navigation-client.d.ts +94 -0
- package/dist/navigation-client.js +154 -0
- package/dist/navigation.d.ts +2 -85
- package/dist/navigation.js +5 -138
- package/dist/overlays.d.ts +23 -0
- package/dist/overlays.js +45 -1
- package/dist/pure.d.ts +30 -0
- package/dist/pure.js +26 -2
- package/dist/system.d.ts +9 -0
- package/dist/system.js +46 -0
- 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
|
-
|
|
17
|
-
|
|
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
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
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
|
-
|
|
2
|
-
|
|
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
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
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";
|
package/dist/markup.d.ts
ADDED
|
@@ -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;
|