@aurea-uds/react 0.2.0 → 0.4.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 +22 -3
- package/dist/actions.js +62 -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 +10 -0
- package/dist/layout-client.js +77 -0
- package/dist/layout.d.ts +2 -15
- package/dist/layout.js +5 -76
- package/dist/markup.d.ts +103 -0
- package/dist/markup.js +124 -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 +120 -0
- package/dist/navigation-client.js +196 -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 +46 -2
- package/dist/pure.d.ts +31 -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,10 @@
|
|
|
1
|
+
import { type HTMLAttributes, type RefAttributes, type ReactNode } from "react";
|
|
2
|
+
import { type TopbarVariant, type SidebarVariant } from "./navigation.js";
|
|
3
|
+
export declare function AppShell({ brand, navigation, topbar, topbarVariant, sidebarVariant, sidebarCollapsed, children, className, ...props }: HTMLAttributes<HTMLDivElement> & RefAttributes<HTMLDivElement> & {
|
|
4
|
+
brand: ReactNode;
|
|
5
|
+
navigation: ReactNode;
|
|
6
|
+
topbar?: ReactNode;
|
|
7
|
+
topbarVariant?: Exclude<TopbarVariant, "pill">;
|
|
8
|
+
sidebarVariant?: SidebarVariant;
|
|
9
|
+
sidebarCollapsed?: boolean;
|
|
10
|
+
}): import("react").JSX.Element;
|
|
@@ -0,0 +1,77 @@
|
|
|
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 shell encolhe o painel para --sidebar-rail e,
|
|
70
|
+
// quando ele flutua, preserva as duas margens na coluna (as regras `:has()` no core).
|
|
71
|
+
// `sidebarVariant` existe porque SEM ELE a variante da lateral é enfeite. Medido em 20/08/2026: o
|
|
72
|
+
// shell chamava `<Sidebar>` sem variante nenhuma, então quem monta por aqui — que é o caminho
|
|
73
|
+
// normal, e é como as 106 páginas do catálogo são montadas — recebia o padrão e ponto. A variante
|
|
74
|
+
// estava documentada no CHANGELOG como saída para quem instalou a `0.3.0`, e a porta estava
|
|
75
|
+
// trancada. É o par do `topbarVariant`, que já existia logo acima. Ver ADR-0034.
|
|
76
|
+
const SHELL_NAV_ID = "aurea-shell-nav";
|
|
77
|
+
export function AppShell({ brand, navigation, topbar, topbarVariant = "floating", sidebarVariant, 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", variant: sidebarVariant, 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, Separator } 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, Separator } from "./markup.js";
|
package/dist/markup.d.ts
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
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 Separator({ orientation, className, ...props }: HTMLAttributes<HTMLHRElement> & RefAttributes<HTMLHRElement> & {
|
|
10
|
+
orientation?: "horizontal" | "vertical";
|
|
11
|
+
}): React.JSX.Element;
|
|
12
|
+
export declare function Cluster({ className, ...props }: HTMLAttributes<HTMLDivElement> & RefAttributes<HTMLDivElement>): React.JSX.Element;
|
|
13
|
+
export declare function Grid({ className, ...props }: HTMLAttributes<HTMLDivElement> & RefAttributes<HTMLDivElement>): React.JSX.Element;
|
|
14
|
+
export declare function KPI({ label, value, trend, className, ...props }: HTMLAttributes<HTMLDivElement> & RefAttributes<HTMLDivElement> & {
|
|
15
|
+
label: ReactNode;
|
|
16
|
+
value: ReactNode;
|
|
17
|
+
trend?: ReactNode;
|
|
18
|
+
}): React.JSX.Element;
|
|
19
|
+
export declare function DataList({ items }: {
|
|
20
|
+
items: Array<{
|
|
21
|
+
term: ReactNode;
|
|
22
|
+
value: ReactNode;
|
|
23
|
+
}>;
|
|
24
|
+
}): React.JSX.Element;
|
|
25
|
+
export declare function Timeline({ items }: {
|
|
26
|
+
items: Array<{
|
|
27
|
+
title: ReactNode;
|
|
28
|
+
description?: ReactNode;
|
|
29
|
+
time?: ReactNode;
|
|
30
|
+
}>;
|
|
31
|
+
}): React.JSX.Element;
|
|
32
|
+
export declare function Prose({ className, ...props }: HTMLAttributes<HTMLDivElement> & RefAttributes<HTMLDivElement>): React.JSX.Element;
|
|
33
|
+
export type BadgeEmphasis = "soft" | "solid" | "outline";
|
|
34
|
+
export type BadgeSize = "xs" | "sm" | "md" | "lg";
|
|
35
|
+
export type BadgePlacement = "top-end" | "top-start" | "bottom-end" | "bottom-start";
|
|
36
|
+
export declare function formatBadgeCount(count: number, max?: number): string;
|
|
37
|
+
export interface BadgeProps extends HTMLAttributes<HTMLSpanElement>, RefAttributes<HTMLSpanElement> {
|
|
38
|
+
variant?: BadgeVariant;
|
|
39
|
+
/** `soft` é o tom de sempre; `solid` preenche com o acento; `outline` é só contorno. */
|
|
40
|
+
emphasis?: BadgeEmphasis;
|
|
41
|
+
size?: BadgeSize;
|
|
42
|
+
/** Ponto de estado antes do texto. Sozinho num `anchor`, ele É o badge. */
|
|
43
|
+
dot?: boolean;
|
|
44
|
+
leading?: ReactNode;
|
|
45
|
+
trailing?: ReactNode;
|
|
46
|
+
/** Foto redonda no início — o `BadgeWithImage` da referência. */
|
|
47
|
+
image?: string;
|
|
48
|
+
imageAlt?: string;
|
|
49
|
+
/** Conteúdo numérico. Passa por `max` e some em zero, salvo `showZero`. */
|
|
50
|
+
count?: number;
|
|
51
|
+
max?: number;
|
|
52
|
+
showZero?: boolean;
|
|
53
|
+
/** Liga o modo SOBREPOSTO e escolhe o canto. `children` passa a ser o que se decora. */
|
|
54
|
+
anchor?: BadgePlacement;
|
|
55
|
+
/** `circle` recolhe o canto em 14% — é o `overlap` da MUI, para avatar redondo. */
|
|
56
|
+
anchorShape?: "square" | "circle";
|
|
57
|
+
/** Esconde sem tirar o filho do lugar. */
|
|
58
|
+
invisible?: boolean;
|
|
59
|
+
/** Conteúdo do badge no modo sobreposto (no modo chip, quem manda é `children`).
|
|
60
|
+
* Nome da MUI, e não `content`: este colide com o atributo HTML de mesmo nome. */
|
|
61
|
+
badgeContent?: ReactNode;
|
|
62
|
+
}
|
|
63
|
+
export declare function Badge({ variant, emphasis, size, dot, leading, trailing, image, imageAlt, count, max, showZero, anchor, anchorShape, invisible, badgeContent, children, className, ...props }: BadgeProps): React.JSX.Element;
|
|
64
|
+
export declare function Progress({ value, label }: {
|
|
65
|
+
value: number;
|
|
66
|
+
label?: string;
|
|
67
|
+
}): React.JSX.Element;
|
|
68
|
+
export declare function Skeleton({ className, ...props }: HTMLAttributes<HTMLDivElement> & RefAttributes<HTMLDivElement>): React.JSX.Element;
|
|
69
|
+
export declare function AvatarGroup({ children, max, total, label, size, className }: {
|
|
70
|
+
children: ReactNode;
|
|
71
|
+
max?: number;
|
|
72
|
+
total?: number;
|
|
73
|
+
label?: string;
|
|
74
|
+
size?: AvatarSize;
|
|
75
|
+
className?: string;
|
|
76
|
+
}): React.JSX.Element;
|
|
77
|
+
export declare function LogStream({ lines }: {
|
|
78
|
+
lines: Array<{
|
|
79
|
+
time?: string;
|
|
80
|
+
level?: string;
|
|
81
|
+
text: string;
|
|
82
|
+
}>;
|
|
83
|
+
}): React.JSX.Element;
|
|
84
|
+
export declare function MediaPlayerShell({ children, className, ...props }: HTMLAttributes<HTMLDivElement> & RefAttributes<HTMLDivElement>): React.JSX.Element;
|
|
85
|
+
export declare function Topbar({ variant, brand, children, className, ...props }: HTMLAttributes<HTMLElement> & RefAttributes<HTMLElement> & {
|
|
86
|
+
variant?: TopbarVariant;
|
|
87
|
+
brand?: ReactNode;
|
|
88
|
+
}): React.JSX.Element;
|
|
89
|
+
export declare function Kbd({ children, className, ...props }: HTMLAttributes<HTMLElement> & RefAttributes<HTMLElement>): React.JSX.Element;
|
|
90
|
+
export declare const Select: React.ForwardRefExoticComponent<Omit<SelectHTMLAttributes<HTMLSelectElement> & RefAttributes<HTMLSelectElement>, "ref"> & RefAttributes<HTMLSelectElement>>;
|
|
91
|
+
export declare const Textarea: React.ForwardRefExoticComponent<Omit<TextareaHTMLAttributes<HTMLTextAreaElement> & RefAttributes<HTMLTextAreaElement>, "ref"> & RefAttributes<HTMLTextAreaElement>>;
|
|
92
|
+
export declare function Checkbox({ label, labelHidden, description, className, ...props }: InputHTMLAttributes<HTMLInputElement> & RefAttributes<HTMLInputElement> & {
|
|
93
|
+
label: ReactNode;
|
|
94
|
+
labelHidden?: boolean;
|
|
95
|
+
description?: ReactNode;
|
|
96
|
+
}): React.JSX.Element;
|
|
97
|
+
export declare function Radio({ label, className, ...props }: InputHTMLAttributes<HTMLInputElement> & RefAttributes<HTMLInputElement> & {
|
|
98
|
+
label: ReactNode;
|
|
99
|
+
}): React.JSX.Element;
|
|
100
|
+
export declare function Switch({ label, className, ...props }: InputHTMLAttributes<HTMLInputElement> & RefAttributes<HTMLInputElement> & {
|
|
101
|
+
label: ReactNode;
|
|
102
|
+
}): React.JSX.Element;
|
|
103
|
+
export declare function Range(props: InputHTMLAttributes<HTMLInputElement> & RefAttributes<HTMLInputElement>): React.JSX.Element;
|
package/dist/markup.js
ADDED
|
@@ -0,0 +1,124 @@
|
|
|
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
|
+
// SEPARATOR — a régua entre coisas. Faltava: a Aurea só tinha `.prose hr` (preso à prosa) e o
|
|
44
|
+
// `ToolbarSeparator` (preso ao Toolbar). Untitled UI precisa dela para a variante de seções da
|
|
45
|
+
// lateral, e ela serve em todo lugar.
|
|
46
|
+
//
|
|
47
|
+
// É `<hr>`, e isso é decisão medida em 18/08/2026. O motor que já usamos ENTREGA um `Separator` —
|
|
48
|
+
// mas ele é `'use client'`, usa hook (`useRenderElement`) e o que produz são DOIS atributos:
|
|
49
|
+
// `role="separator"` e `aria-orientation`. Importá-lo arrastaria este módulo para o lado cliente,
|
|
50
|
+
// que é o defeito da ADR-0026 (o `Card` custou 118,5 KB atravessando essa fronteira). Aqui a regra
|
|
51
|
+
// "se o motor entrega, não escrevemos" (BUILDING.md passo 2) PERDE para a fronteira — e o registro
|
|
52
|
+
// existe para a próxima sessão não "corrigir" isto de volta.
|
|
53
|
+
//
|
|
54
|
+
// `<hr>` nativo JÁ TEM `role="separator"` implícito, então escrever o papel seria ARIA redundante —
|
|
55
|
+
// e o APG avisa que ARIA a mais é pior que ARIA nenhuma. O papel `separator` também tem orientação
|
|
56
|
+
// HORIZONTAL por padrão: por isso `aria-orientation` só aparece quando é vertical.
|
|
57
|
+
//
|
|
58
|
+
// O que NÃO entrou, pelo passo 5: as três tonalidades do HeroUI (`secondary`/`tertiary`) — a Aurea
|
|
59
|
+
// tem um `--border` e inventar escala de tom aqui seria copiar o sistema de token deles. E o
|
|
60
|
+
// separador COM RÓTULO no meio ("or", uma data numa conversa), que eles têm em
|
|
61
|
+
// `separator__container` + `__line` + `__content`: é outra estrutura (o `<hr>` não carrega texto),
|
|
62
|
+
// ninguém pediu ainda, e fica registrado aqui como o próximo degrau se alguém precisar.
|
|
63
|
+
export function Separator({ orientation = "horizontal", className, ...props }) { return _jsx("hr", { "aria-orientation": orientation === "vertical" ? "vertical" : undefined, className: cx("separator", orientation === "vertical" && "separator-vertical", className), ...props }); }
|
|
64
|
+
export function Cluster({ className, ...props }) { return _jsx("div", { className: cx("cluster", className), ...props }); }
|
|
65
|
+
export function Grid({ className, ...props }) { return _jsx("div", { className: cx("grid", className), ...props }); }
|
|
66
|
+
// ── Data Display ─────────────────────────────────────────────────────────────────────────────
|
|
67
|
+
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 })] }); }
|
|
68
|
+
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)) }); }
|
|
69
|
+
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)) }); }
|
|
70
|
+
// Prose (item L5): UMA LINHA, e é para ser mesmo — quem transforma Markdown em elementos é o
|
|
71
|
+
// consumidor. A trava do item é no CSS, escopada em `.prose`, e há prova dela no `skin.spec`.
|
|
72
|
+
// Segurança, porque é o caminho de uso mais provável: quem entrega HTML de terceiro por
|
|
73
|
+
// `dangerouslySetInnerHTML` tem de SANITIZAR antes. A Aurea desenha o texto; ela não pode decidir
|
|
74
|
+
// o que é seguro renderizar. Está dito na ficha, em voz alta.
|
|
75
|
+
export function Prose({ className, ...props }) { return _jsx("div", { className: cx("prose", className), ...props }); }
|
|
76
|
+
// `max` com `+` é o idioma universal do contador (MUI, Ant, Material 3). Exportada porque quem
|
|
77
|
+
// escreve o nome acessível precisa do MESMO texto — "99+ unread" tem de bater com o que se vê.
|
|
78
|
+
export function formatBadgeCount(count, max = 99) { return count > max ? `${max}+` : String(count); }
|
|
79
|
+
export function Badge({ variant = "neutral", emphasis = "soft", size = "md", dot, leading, trailing, image, imageAlt, count, max = 99, showZero, anchor, anchorShape = "square", invisible, badgeContent, children, className, ...props }) {
|
|
80
|
+
const numero = count != null ? formatBadgeCount(count, max) : undefined;
|
|
81
|
+
const miolo = anchor ? (numero ?? badgeContent) : (numero ?? children);
|
|
82
|
+
// Ponto puro: `dot` sem nada para mostrar. Aí o badge não tem conteúdo, e ganha medida própria.
|
|
83
|
+
const soPonto = !!dot && miolo == null;
|
|
84
|
+
const chip = _jsxs("span", { className: cx("badge", variant !== "neutral" && `badge-${variant}`, emphasis !== "soft" && `badge-${emphasis}`, size !== "md" && `badge-${size}`, anchor && `badge-overlay badge-at-${anchor}`, anchor && anchorShape === "circle" && "badge-on-circle", anchor && soPonto && "badge-is-dot", className), ...props, children: [dot && !soPonto && _jsx("span", { className: "badge-dot" }), image && _jsx("img", { className: "badge-image", src: image, alt: imageAlt ?? "" }), leading, miolo, trailing] });
|
|
85
|
+
if (!anchor)
|
|
86
|
+
return chip;
|
|
87
|
+
// Some em zero por padrão, como a MUI: caixa de entrada zerada não merece um "0" no canto.
|
|
88
|
+
const escondido = invisible || (count === 0 && !showZero) || (miolo == null && !dot);
|
|
89
|
+
// `aria-hidden` no contador, e o número vai para o nome de QUEM É DECORADO — regra da própria
|
|
90
|
+
// documentação da MUI e das três fontes de acessibilidade lidas em 17/08/2026. Sem isso, o
|
|
91
|
+
// botão do sino é anunciado "sino 8" e ninguém sabe o que é o 8.
|
|
92
|
+
return _jsxs("span", { className: "badge-anchor", children: [children, !escondido && React.cloneElement(chip, { "aria-hidden": true })] });
|
|
93
|
+
}
|
|
94
|
+
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}%` } }) }) }); }
|
|
95
|
+
export function Skeleton({ className, ...props }) { return _jsx("div", { className: cx("skeleton", className), "aria-hidden": "true", ...props }); }
|
|
96
|
+
// ── Identity ─────────────────────────────────────────────────────────────────────────────────
|
|
97
|
+
// `React.Children` não é hook — é utilitário de leitura de `children`, e roda no servidor.
|
|
98
|
+
export function AvatarGroup({ children, max, total, label, size = "md", className }) {
|
|
99
|
+
const itens = React.Children.toArray(children);
|
|
100
|
+
const mostra = max ? itens.slice(0, max) : itens;
|
|
101
|
+
const resto = (total ?? itens.length) - mostra.length;
|
|
102
|
+
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] })] });
|
|
103
|
+
}
|
|
104
|
+
// ── Code ─────────────────────────────────────────────────────────────────────────────────────
|
|
105
|
+
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)) }); }
|
|
106
|
+
// ── Media ────────────────────────────────────────────────────────────────────────────────────
|
|
107
|
+
export function MediaPlayerShell({ children, className, ...props }) { return _jsx("div", { className: cx("media-player", className), ...props, children: children }); }
|
|
108
|
+
// ── Navigation ───────────────────────────────────────────────────────────────────────────────
|
|
109
|
+
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] }); }
|
|
110
|
+
// ── System / interno ─────────────────────────────────────────────────────────────────────────
|
|
111
|
+
// Kbd — representação de tecla/atalho. <kbd> é o elemento HTML certo; a pele é nossa. Ele já
|
|
112
|
+
// morava fora da casa dele (no `internal.tsx`, por causa do `Button`); agora mora aqui, pelo
|
|
113
|
+
// mesmo motivo elevado a regra.
|
|
114
|
+
export function Kbd({ children, className, ...props }) { return _jsx("kbd", { className: cx("kbd", className), ...props, children: children }); }
|
|
115
|
+
// ── Inputs ───────────────────────────────────────────────────────────────────────────────────
|
|
116
|
+
// Os seis controles abaixo NÃO prendem manipulador nenhum: quem escuta `onChange` é o consumidor,
|
|
117
|
+
// e por isso eles são marcação. O `Field`, o `Input` e o `Combobox` ficaram no `inputs.tsx` —
|
|
118
|
+
// aqueles chamam hook de verdade.
|
|
119
|
+
export const Select = forwardRef(function Select({ className, ...props }, ref) { return _jsx("select", { ref: ref, className: cx("select", className), ...props }); });
|
|
120
|
+
export const Textarea = forwardRef(function Textarea({ className, ...props }, ref) { return _jsx("textarea", { ref: ref, className: cx("textarea", className), ...props }); });
|
|
121
|
+
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 })] })] })] }); }
|
|
122
|
+
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] }); }
|
|
123
|
+
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 })] }); }
|
|
124
|
+
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;
|