@aurea-uds/react 0.31.0 → 0.33.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 CHANGED
@@ -60,12 +60,14 @@ segue sem a função — não quebra a tela.
60
60
 
61
61
  **O arrumador fora da tela principal (`layoutWorker`).** Num mapa grande, a arrumação pode prender a
62
62
  tela por alguns segundos. O mapa aceita um trabalhador (Web Worker) que o app cria — cada empacotador
63
- cria trabalhador do seu jeito, e por isso quem entrega é o app. No Vite:
63
+ cria trabalhador do seu jeito, e por isso quem entrega é o app. A Aurea publica o trabalhador por um
64
+ caminho dela, `@aurea-uds/react/graph-worker`, que traz o do `elkjs` (o peer opcional) junto — o app
65
+ não importa o motor por fora. No Vite:
64
66
 
65
67
  ```js
66
- import TrabalhadorDoElk from "elkjs/lib/elk-worker.min.js?worker";
68
+ import TrabalhadorDoMapa from "@aurea-uds/react/graph-worker?worker";
67
69
 
68
- <DependencyGraph layout="layered" layoutWorker={() => new TrabalhadorDoElk()} … />
70
+ <DependencyGraph layout="layered" layoutWorker={() => new TrabalhadorDoMapa()} … />
69
71
  ```
70
72
 
71
73
  Em qualquer outro empacotador, o jeito que sempre funciona: copie `node_modules/elkjs/lib/elk-worker.min.js`
package/dist/actions.d.ts CHANGED
@@ -1,8 +1,8 @@
1
1
  import React, { type ButtonHTMLAttributes, type HTMLAttributes, type ReactElement, type RefAttributes } from "react";
2
2
  import { type Orientation, type Responsive } from "./pure.js";
3
- import { type IconName } from "./system.js";
4
- export type ButtonVariant = "primary" | "secondary" | "outline" | "ghost" | "primary-outline" | "primary-ghost" | "danger" | "danger-outline" | "danger-ghost" | "link" | "link-primary" | "link-danger" | "nav";
5
- export type ButtonAppearance = "solid" | "outline" | "ghost" | "link" | "nav";
3
+ import { type IconName, type AureaIcon } from "./system.js";
4
+ export type ButtonVariant = "primary" | "secondary" | "tertiary" | "outline" | "ghost" | "primary-outline" | "primary-ghost" | "danger" | "danger-outline" | "danger-ghost" | "link" | "link-primary" | "link-danger" | "nav" | "danger-soft";
5
+ export type ButtonAppearance = "solid" | "outline" | "ghost" | "link" | "nav" | "soft";
6
6
  export type ButtonTone = "neutral" | "brand" | "danger" | "success" | "warning" | "info";
7
7
  export type ComponentSize = "xs" | "sm" | "md" | "lg" | "xl";
8
8
  /** Resolve os dois eixos (ou o atalho) na classe que o core pinta. Exportada porque o teste
@@ -20,10 +20,13 @@ export interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement>, Re
20
20
  tone?: ButtonTone;
21
21
  size?: Responsive<ComponentSize>;
22
22
  loading?: boolean;
23
- leadingIcon?: IconName;
24
- trailingIcon?: IconName;
23
+ leadingIcon?: AureaIcon;
24
+ trailingIcon?: AureaIcon;
25
25
  href?: string;
26
26
  fullWidth?: boolean;
27
+ /** Menos peso (15/10/2026): a letra apagada e um degrau menor (12 no `sm`) — o botão de ferramenta de um topo de
28
+ * site, como o "Theme" da referência. A caixa e o fundo são os da aparência escolhida. */
29
+ quiet?: boolean;
27
30
  /** C-03 (24/09/2026): numa fila (`Cluster`), divide o espaço em partes iguais com os outros botões
28
31
  * que também têm `grow`. O `fullWidth` não serve ali: ele pede a linha inteira. */
29
32
  grow?: boolean;
@@ -50,7 +53,9 @@ export interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement>, Re
50
53
  * quebrar, e por isso este é o momento mais barato que vai existir.
51
54
  */
52
55
  pressed?: boolean;
53
- kbd?: string;
56
+ /** O atalho, à mostra e anunciado. Texto: uma caixinha só ("⌘K"). LISTA (15/10/2026): uma caixinha por tecla, como
57
+ * a busca da referência (`["Ctrl", "K"]`), e o leitor de tela recebe a combinação (`Control+K`). */
58
+ kbd?: string | readonly string[];
54
59
  /**
55
60
  * GAR-14 (0.28.0): o botão COMPARTILHA. Ele pergunta ao navegador se sabe compartilhar este link
56
61
  * (`navigator.canShare`) — detectar o recurso, e não adivinhar o aparelho, é o padrão da web. Sabe
@@ -80,7 +85,11 @@ interface ToggleBase {
80
85
  disabled?: boolean;
81
86
  id?: string;
82
87
  className?: string;
88
+ /** 0.32.0, o `ToggleButton` da referência: `default` com o fundo neutro (o padrão), `ghost` sem fundo
89
+ * (o desenho de até a 0.31.0). O ligado é o idioma "selecionado" da Aurea nos dois. */
90
+ variant?: ToggleVariant;
83
91
  }
92
+ export type ToggleVariant = "default" | "ghost";
84
93
  type ToggleChildren = Exclude<React.ReactNode, boolean | null | undefined>;
85
94
  export type ToggleProps = ToggleBase & ({
86
95
  children: ToggleChildren;
@@ -89,19 +98,38 @@ export type ToggleProps = ToggleBase & ({
89
98
  children?: never;
90
99
  label: string;
91
100
  });
92
- export declare function Toggle({ pressed, defaultPressed, onPressedChange, value, icon, label, size, disabled, id, className, children }: ToggleProps): React.JSX.Element;
101
+ export declare function Toggle({ pressed, defaultPressed, onPressedChange, value, icon, label, size: sizePedido, disabled, id, className, children, variant }: ToggleProps): React.JSX.Element;
93
102
  export interface IconButtonProps extends Omit<ButtonProps, "children"> {
94
103
  label: string;
95
- icon: IconName;
104
+ icon: AureaIcon;
96
105
  }
97
106
  export declare const IconButton: React.ForwardRefExoticComponent<Omit<IconButtonProps, "ref"> & RefAttributes<HTMLButtonElement>>;
98
- export interface ThemeToggleProps extends Omit<IconButtonProps, "icon" | "label" | "onClick" | "variant" | "appearance" | "tone" | "href" | "target" | "rel" | "download" | "render" | "kbd" | "loading" | "leadingIcon" | "trailingIcon" | "fullWidth" | "share"> {
107
+ export interface CloseButtonProps extends Omit<ButtonProps, "quiet" | "variant" | "appearance" | "tone" | "size" | "loading" | "leadingIcon" | "trailingIcon" | "href" | "target" | "rel" | "download" | "render" | "kbd" | "fullWidth" | "grow" | "pressed" | "share"> {
108
+ /** O nome que o leitor de tela anuncia. Padrão: a frase `close` ("Close"/"Fechar"). */
109
+ label?: string;
110
+ }
111
+ export declare const CloseButton: React.ForwardRefExoticComponent<Omit<CloseButtonProps, "ref"> & RefAttributes<HTMLButtonElement>>;
112
+ export interface ThemeToggleProps extends Omit<IconButtonProps, "quiet" | "icon" | "label" | "onClick" | "variant" | "appearance" | "tone" | "href" | "target" | "rel" | "download" | "render" | "kbd" | "loading" | "leadingIcon" | "trailingIcon" | "fullWidth" | "share"> {
113
+ /** `"light-dark"` (o padrão): o botão que alterna. `"light-dark-system"` (15/10/2026): a chave de três posições do
114
+ * topo da referência — claro, escuro e sistema (seguir o do computador, e mudar junto). */
115
+ mode?: "light-dark" | "light-dark-system";
99
116
  }
100
117
  export declare const ThemeToggle: React.ForwardRefExoticComponent<Omit<ThemeToggleProps, "ref"> & RefAttributes<HTMLButtonElement>>;
101
- export declare function ButtonGroup({ label, orientation, className, ...props }: HTMLAttributes<HTMLDivElement> & RefAttributes<HTMLDivElement> & {
118
+ export interface ButtonGroupProps extends HTMLAttributes<HTMLDivElement>, RefAttributes<HTMLDivElement> {
102
119
  label?: string;
103
120
  orientation?: Responsive<Orientation>;
104
- }): React.JSX.Element;
121
+ /** O desenho de todos os botões de dentro; cada um pode trocar o seu. */
122
+ variant?: ButtonVariant;
123
+ /** O tamanho de todos os botões de dentro; cada um pode trocar o seu. */
124
+ size?: Responsive<ComponentSize>;
125
+ /** Ocupa a largura do pai, e os botões repartem em partes iguais. */
126
+ fullWidth?: boolean;
127
+ /** Desliga todos os botões de dentro. */
128
+ disabled?: boolean;
129
+ /** Botões soltos (cápsulas com vão de 4), em vez de colados. */
130
+ detached?: boolean;
131
+ }
132
+ export declare function ButtonGroup({ label, orientation, variant, size, fullWidth, disabled, detached, className, ...props }: ButtonGroupProps): React.JSX.Element;
105
133
  export declare function Toolbar({ orientation, label, className, ...props }: HTMLAttributes<HTMLDivElement> & RefAttributes<HTMLDivElement> & {
106
134
  orientation?: Responsive<Orientation>;
107
135
  label?: string;
@@ -113,7 +141,7 @@ export declare function ToolbarSeparator({ className, ...props }: HTMLAttributes
113
141
  export interface ToolbarButtonProps extends Omit<ButtonProps, "share"> {
114
142
  }
115
143
  export declare const ToolbarButton: React.ForwardRefExoticComponent<Omit<ToolbarButtonProps, "ref"> & RefAttributes<HTMLButtonElement>>;
116
- export declare function ToggleGroup({ value, defaultValue, onValueChange, multiple, orientation, disabled, label, className, children }: {
144
+ export interface ToggleGroupProps {
117
145
  value?: string[];
118
146
  defaultValue?: string[];
119
147
  onValueChange?: (value: string[]) => void;
@@ -123,5 +151,12 @@ export declare function ToggleGroup({ value, defaultValue, onValueChange, multip
123
151
  label?: string;
124
152
  className?: string;
125
153
  children: React.ReactNode;
126
- }): React.JSX.Element;
154
+ /** O tamanho de todos os botões de dentro; cada um pode trocar o seu (valor simples, como o dela). */
155
+ size?: Extract<ComponentSize, "sm" | "md" | "lg">;
156
+ /** Ocupa a largura do pai, e os botões repartem em partes iguais. */
157
+ fullWidth?: boolean;
158
+ /** Botões soltos (vão de 4), em vez de colados. */
159
+ detached?: boolean;
160
+ }
161
+ export declare function ToggleGroup({ value, defaultValue, onValueChange, multiple, orientation, disabled, label, className, children, size, fullWidth, detached }: ToggleGroupProps): React.JSX.Element;
127
162
  export {};
package/dist/actions.js CHANGED
@@ -15,19 +15,25 @@ import { Icon, useAureaTheme } from "./system.js";
15
15
  // O dicionário que desachata o enum. É a ÚNICA fonte da correspondência: o gate do
16
16
  // `validate.py` lê daqui para provar que todo par ou tem regra no core ou está declarado
17
17
  // ausente com motivo, e a ficha do registry declara os mesmos dois eixos.
18
+ // O `secondary` não tem célula própria nos dois eixos (a letra da marca no fundo neutro não é nenhum
19
+ // par aparência × tom): os eixos dele são os do `tertiary`, que valem só quando quem chama troca a
20
+ // aparência ou o tom — aí sai a célula pedida, como em todo atalho.
18
21
  const EIXOS = {
19
- primary: ["solid", "brand"], secondary: ["solid", "neutral"],
22
+ primary: ["solid", "brand"], secondary: ["solid", "neutral"], tertiary: ["solid", "neutral"],
20
23
  outline: ["outline", "neutral"], ghost: ["ghost", "neutral"],
21
24
  "primary-outline": ["outline", "brand"], "primary-ghost": ["ghost", "brand"],
22
25
  danger: ["solid", "danger"], "danger-outline": ["outline", "danger"], "danger-ghost": ["ghost", "danger"],
23
26
  link: ["link", "neutral"], "link-primary": ["link", "brand"], "link-danger": ["link", "danger"],
24
- nav: ["nav", "neutral"]
27
+ nav: ["nav", "neutral"], "danger-soft": ["soft", "danger"]
25
28
  };
29
+ // Atalhos que NÃO têm classe própria (15/10/2026): saem como os dois eixos (`btn-soft btn-tone-danger`), e herdam as
30
+ // regras de cor do tom nos dois temas em vez de repeti-las numa classe nova.
31
+ const SO_EIXOS = new Set(["danger-soft"]);
26
32
  // caminho de volta: par → nome antigo. Existir aqui significa "o core já pinta esta célula com
27
33
  // uma classe própria", e é o que garante que NENHUMA baseline se mexe: `appearance="outline"`
28
34
  // + `tone="danger"` emite `.btn-danger-outline`, o mesmo DOM de sempre.
29
35
  const CLASSE_ANTIGA = new Map(Object.entries(EIXOS)
30
- .map(([v, [a, t]]) => [`${a}/${t}`, v]));
36
+ .filter(([v]) => v !== "secondary" && !SO_EIXOS.has(v)).map(([v, [a, t]]) => [`${a}/${t}`, v]));
31
37
  /** Resolve os dois eixos (ou o atalho) na classe que o core pinta. Exportada porque o teste
32
38
  * cobra a matriz inteira, e porque quem monta um botão à mão precisa da mesma conta.
33
39
  *
@@ -37,7 +43,10 @@ const CLASSE_ANTIGA = new Map(Object.entries(EIXOS)
37
43
  * `--success`/`--success-foreground` e os irmãos são. `outline` voltou a ser só uma aparência
38
44
  * que se pede, nunca o que sobra quando a nossa paleta não dá conta. */
39
45
  export function buttonSkin(variant, appearance, tone) {
40
- const [a0, t0] = EIXOS[variant ?? "secondary"];
46
+ // A variação sem eixo pedido é a própria classe — é o caminho do `secondary`, que não tem célula.
47
+ if (appearance === undefined && tone === undefined && !(variant && SO_EIXOS.has(variant)))
48
+ return `btn-${variant ?? "tertiary"}`;
49
+ const [a0, t0] = EIXOS[variant ?? "tertiary"];
41
50
  const a = appearance ?? a0, t = tone ?? t0;
42
51
  const antiga = CLASSE_ANTIGA.get(`${a}/${t}`);
43
52
  return antiga ? `btn-${antiga}` : cx(`btn-${a}`, `btn-tone-${t}`);
@@ -90,10 +99,21 @@ async function compartilhar(botao, dados, s, dentroDoProvider) {
90
99
  avisar(true);
91
100
  }
92
101
  }
102
+ const GrupoDeBotoesContext = React.createContext(null);
93
103
  // type="button" por default: o default do HTML é submit, e um "Cancelar"/"Remover"
94
104
  // dentro de <form> dispararia a ação principal (auditoria 18/07/2026, ALTO 1).
95
105
  // Quem quer submeter passa type="submit" explícito (como o MessageComposer faz).
96
- export const Button = forwardRef(function Button({ variant, appearance, tone, size = "md", loading, leadingIcon, trailingIcon, className, children, disabled, type = "button", href, fullWidth, grow, target, rel, download, pressed, kbd, onClick, onClickCapture, "aria-disabled": ariaDisabled, render, share, ...props }, ref) {
106
+ // O nome da combinação para o `aria-keyshortcuts`: as teclas juntas por "+", com os nomes da especificação (o ESPAÇO
107
+ // separa atalhos ALTERNATIVOS — "Ctrl K" quer dizer "Ctrl" ou "K"). O texto solto passa como veio, como antes.
108
+ const NOME_DA_TECLA = { ctrl: "Control", control: "Control", "⌃": "Control", cmd: "Meta", command: "Meta", "⌘": "Meta", meta: "Meta",
109
+ shift: "Shift", "⇧": "Shift", alt: "Alt", option: "Alt", "⌥": "Alt", enter: "Enter", "↵": "Enter", esc: "Escape", escape: "Escape" };
110
+ const atalhoAnunciado = (kbd) => typeof kbd === "string" ? (kbd || undefined)
111
+ : kbd && kbd.length > 0 ? kbd.map(k => NOME_DA_TECLA[k.toLowerCase()] ?? k).join("+") : undefined;
112
+ export const Button = forwardRef(function Button({ variant: variantPedida, appearance, tone, size: sizePedido, quiet, loading, leadingIcon, trailingIcon, className, children, disabled: disabledPedido, type = "button", href, fullWidth, grow, target, rel, download, pressed, kbd, onClick, onClickCapture, "aria-disabled": ariaDisabled, render, share, ...props }, ref) {
113
+ const grupo = React.useContext(GrupoDeBotoesContext);
114
+ const variant = variantPedida ?? grupo?.variant;
115
+ const size = sizePedido ?? grupo?.size ?? "md";
116
+ const disabled = disabledPedido ?? grupo?.disabled;
97
117
  // G-AXIS-04 — `size` aceita valor simples, responsivo por viewport ou adaptativo por container.
98
118
  // SOMATIVO por construção: valor simples continua emitindo `btn-sm`, byte por byte a mesma classe
99
119
  // de antes, e por isso nenhuma baseline se mexe. Só o valor responsivo entra pela camada genérica
@@ -101,7 +121,7 @@ export const Button = forwardRef(function Button({ variant, appearance, tone, si
101
121
  // Quem decide é o CSS: nada aqui observa largura, e o HTML do servidor já sai correto.
102
122
  const responsivo = ehResponsivo(size);
103
123
  const base = valorBase(size) ?? "md";
104
- const cls = cx("btn", buttonSkin(variant, appearance, tone), responsivo ? classesResponsivas("size", size) : (base !== "md" && `btn-${base}`), fullWidth && "btn-block", grow && "btn-grow", className);
124
+ const cls = cx("btn", buttonSkin(variant, appearance, tone), responsivo ? classesResponsivas("size", size) : (base !== "md" && `btn-${base}`), fullWidth && "btn-block", grow && "btn-grow", quiet && "btn-quiet", className);
105
125
  // kbd dentro do botão: mostra o atalho E o anuncia (aria-keyshortcuts), senão é enfeite.
106
126
  // 🔴 OCUPADO TROCA, NÃO ACRESCENTA (01/10/2026, achado pelo Victor no aureauds.dev): a rodinha
107
127
  // entrava ANTES do ícone, e no `IconButton` — 36 de largura fixa — rodinha e glifo não cabiam:
@@ -111,7 +131,7 @@ export const Button = forwardRef(function Button({ variant, appearance, tone, si
111
131
  // da frente; e sem conteúdo (o `IconButton` ocupado) o invólucro vazio some, senão o `gap` dele
112
132
  // empurraria a rodinha para fora do centro. Fora do estado ocupado o HTML é o mesmo de antes.
113
133
  const temConteudo = children != null && children !== false;
114
- const inner = _jsxs(_Fragment, { children: [loading ? _jsx("span", { className: "spinner" }) : leadingIcon && _jsx(Icon, { name: leadingIcon }), (temConteudo || !loading) && _jsx("span", { children: children }), kbd && _jsx(Kbd, { children: kbd }), trailingIcon && _jsx(Icon, { name: trailingIcon })] });
134
+ const inner = _jsxs(_Fragment, { children: [loading ? _jsx("span", { className: "spinner" }) : leadingIcon && _jsx(Icon, { name: leadingIcon }), (temConteudo || !loading) && _jsx("span", { children: children }), typeof kbd === "string" ? (kbd && _jsx(Kbd, { className: "btn-kbd", children: kbd })) : kbd && kbd.length > 0 && _jsx("span", { className: "btn-kbds", children: kbd.map((k, i) => _jsx(Kbd, { className: "btn-kbd", children: k }, i)) }), trailingIcon && _jsx(Icon, { name: trailingIcon })] });
115
135
  // INERTE ≠ DESABILITADO, e a diferença é medida (M4, 13/08/2026): `disabled` tira o botão da
116
136
  // ordem de foco, então quem navega por teclado nunca alcança a explicação de POR QUE não dá — e
117
137
  // "não dá porque você não tem permissão" é justamente o caso em que a explicação é tudo. O
@@ -121,7 +141,7 @@ export const Button = forwardRef(function Button({ variant, appearance, tone, si
121
141
  // Mesmo remendo do AUD-0004, que já barrava o link desabilitado; aqui ele alcança o <button>.
122
142
  const inerte = ariaDisabled === true || ariaDisabled === "true";
123
143
  const off = disabled || loading;
124
- const shared = { "aria-keyshortcuts": kbd || undefined, "aria-busy": loading || undefined };
144
+ const shared = { "aria-keyshortcuts": atalhoAnunciado(kbd), "aria-busy": loading || undefined };
125
145
  // GAR-14: o `share` emite a mesma marcação que o `aurea.js` lê ([data-aurea-share]) — o catálogo
126
146
  // estático não hidrata, e é ela que faz o botão funcionar lá. Os textos vão junto, na língua do app.
127
147
  const s = useAureaStrings();
@@ -185,7 +205,9 @@ function temConteudoVisivel(children) {
185
205
  // entre os estados**. Se o texto vira "Mute"/"Unmute" ou "Play"/"Pause", não é toggle — é
186
206
  // botão de ação, porque quem lê tela ouve o rótulo NOVO e o estado ao mesmo tempo e não sabe
187
207
  // se o botão descreve o que é ou o que fará.
188
- export function Toggle({ pressed, defaultPressed, onPressedChange, value, icon, label, size, disabled, id, className, children }) {
208
+ export function Toggle({ pressed, defaultPressed, onPressedChange, value, icon, label, size: sizePedido, disabled, id, className, children, variant = "default" }) {
209
+ const grupo = React.useContext(GrupoDeBotoesContext);
210
+ const size = sizePedido ?? grupo?.size;
189
211
  // O tipo barra o consumidor TypeScript; este aviso barra o de JavaScript, que não tem tipo nenhum.
190
212
  // Sem gate por NODE_ENV de propósito: o `dist` é saída de `tsc`, então `process` não existe no
191
213
  // navegador e a referência quebraria o render — e um controle sem nome merece aparecer em produção
@@ -196,28 +218,48 @@ export function Toggle({ pressed, defaultPressed, onPressedChange, value, icon,
196
218
  const rotuloVisivel = typeof label === "string" && label.trim().length > 0;
197
219
  if (!textoVisivel && !rotuloVisivel)
198
220
  console.error("Aurea: <Toggle> sem `children` visível e sem `label` não tem nome acessível — quem usa leitor de tela encontra um botão anônimo. Passe `label` quando o toggle for só ícone.");
199
- return _jsxs(BaseToggle, { id: id, value: value, disabled: disabled, pressed: pressed, defaultPressed: defaultPressed, onPressedChange: soOValor(onPressedChange), "aria-label": textoVisivel ? undefined : (rotuloVisivel ? label : undefined), className: cx("btn", "btn-ghost", size !== "md" && `btn-${size}`, !textoVisivel && "btn-icon", "toggle", className), children: [icon && _jsx(Icon, { name: icon }), children] });
221
+ // O tamanho pela MESMA conta do `Button` (0.32.0): até a 0.31.0 era `size!=="md"&&\`btn-${size}\``, e
222
+ // sem `size` saía a classe `btn-undefined` (medido no catálogo, nos três botões do `ToggleGroup`); com
223
+ // valor responsivo sairia `btn-[object Object]`.
224
+ const tamanho = ehResponsivo(size) ? classesResponsivas("size", size) : ((valorBase(size) ?? "md") !== "md" && `btn-${valorBase(size)}`);
225
+ return _jsxs(BaseToggle, { id: id, value: value, disabled: disabled, pressed: pressed, defaultPressed: defaultPressed, onPressedChange: soOValor(onPressedChange), "aria-label": textoVisivel ? undefined : (rotuloVisivel ? label : undefined), className: cx("btn", variant === "ghost" ? "btn-ghost" : "btn-tertiary", tamanho, !textoVisivel && "btn-icon", "toggle", className), children: [icon && _jsx(Icon, { name: icon }), children] });
200
226
  }
201
227
  // default ghost (não secondary): um ícone-ação solto — hambúrguer, tema, fechar — é sem
202
228
  // caixa por convenção (pedido do Victor: hambúrguer sem borda). Quem quer a caixa passa
203
229
  // variant. Alinha o React ao HTML dos docs, onde .btn-icon já é transparente.
204
230
  // Ocupado, o glifo SAI e a rodinha fica sozinha no centro — ver o `inner` do `Button`.
205
- export const IconButton = forwardRef(function IconButton({ label, icon, variant = "ghost", className, loading, ...props }, ref) { return _jsx(Button, { ref: ref, variant: variant, className: cx("btn-icon", className), "aria-label": label, loading: loading, ...props, children: loading ? null : _jsx(Icon, { name: icon }) }); });
206
- export const ThemeToggle = forwardRef(function ThemeToggle({ className, ...props }, ref) {
207
- const { theme, toggleTheme } = useAureaTheme();
231
+ // Dentro de um grupo com `variant`, o do grupo vale (0.32.0); solto, o padrão continua o fantasma.
232
+ export const IconButton = forwardRef(function IconButton({ label, icon, variant, className, loading, ...props }, ref) { const grupo = React.useContext(GrupoDeBotoesContext); return _jsx(Button, { ref: ref, variant: variant ?? (grupo?.variant ? undefined : "ghost"), className: cx("btn-icon", className), "aria-label": label, loading: loading, ...props, children: loading ? null : _jsx(Icon, { name: icon }) }); });
233
+ export const CloseButton = forwardRef(function CloseButton({ label, className, children, ...props }, ref) {
234
+ const s = useAureaStrings();
235
+ return _jsx(Button, { ref: ref, variant: "tertiary", className: cx("close-button", className), "aria-label": label ?? s.close, ...props, children: children ?? _jsx(Icon, { name: "x", weight: "bold" }) });
236
+ });
237
+ export const ThemeToggle = forwardRef(function ThemeToggle({ className, mode = "light-dark", ...props }, ref) {
238
+ const { theme, preference, toggleTheme, setTheme } = useAureaTheme();
208
239
  const s = useAureaStrings();
209
240
  const escuro = theme === "dark";
241
+ const nome = React.useId();
242
+ // A CHAVE DE TRÊS POSIÇÕES (15/10/2026, o Victor: "A, B, C, pode"): a do topo da referência, medida nela — a
243
+ // cápsula de 34 com as três opções de 26 e o glifo de 16. São botões de opção NATIVOS, como as amostras do
244
+ // `ColorSwatch`: a escolha única e as setas são do navegador. A escolhida tem o glifo cheio (ADR-0053) e a pele da
245
+ // aba escolhida (ADR-0059). Quem escolhe é o provider (`"system"` segue o sistema).
246
+ if (mode === "light-dark-system") {
247
+ const opcoes = [["light", "sun", s.themeLight], ["dark", "moon", s.themeDark], ["system", "monitor", s.themeSystem]];
248
+ return _jsx("div", { role: "radiogroup", "aria-label": s.themeChoice, className: cx("theme-switch", className), children: opcoes.map(([v, icone, rotulo]) => {
249
+ const marcada = preference === v;
250
+ return _jsxs("label", { className: "theme-switch-option", children: [_jsx("input", { type: "radio", className: "theme-switch-input", name: nome, value: v, checked: marcada, disabled: props.disabled, "aria-label": rotulo, onChange: () => setTheme(v) }), _jsx(Icon, { name: icone, size: "sm", weight: marcada ? "fill" : "regular" })] }, v);
251
+ }) });
252
+ }
210
253
  // O corpo do `IconButton`, escrito aqui só para o glifo sair na forma CHEIA (ADR-0053: a lua e o
211
254
  // sol cheios, como os do conjunto de ícones anterior, aprovados pela imagem em 25/09/2026). O `IconButton` público não
212
255
  // tem peso de ícone.
213
256
  return _jsx(Button, { ref: ref, variant: "ghost", className: cx("btn-icon", escuro ? "theme-toggle-sun" : "theme-toggle-moon", className), "aria-label": escuro ? s.themeToLight : s.themeToDark, onClick: toggleTheme, ...props, children: _jsx(Icon, { name: escuro ? "sun" : "moon", weight: "fill" }) });
214
257
  });
215
- // ButtonGroup: agrupamento semântico. Sem roving tabindex — cada botão continua tabulável (use Toolbar para roving).
216
- // `orientation` (G-AXIS-01) muda só o EIXO do layout, não a semântica: `role="group"` não tem
217
- // noção de direção, e por isso — ao contrário do `Toolbar` e do `ToggleGroup`, que navegam por
218
- // seta e precisam saber para que lado a seta anda — aqui não há `aria-orientation`. Anunciar
219
- // orientação num grupo que não navega seria prometer teclado que não existe.
220
- export function ButtonGroup({ label, orientation, className, ...props }) { const s = useAureaStrings(); return _jsx("div", { role: "group", "aria-label": label ?? s.optionsLabel, className: cx("btn-group", peleDoEixo("btn-group", orientation, "horizontal", "btn-group"), className), ...props }); }
258
+ export function ButtonGroup({ label, orientation, variant, size, fullWidth, disabled, detached, className, ...props }) {
259
+ const s = useAureaStrings();
260
+ const grupo = React.useMemo(() => ({ variant, size, disabled }), [variant, size, disabled]);
261
+ return _jsx(GrupoDeBotoesContext.Provider, { value: grupo, children: _jsx("div", { role: "group", "aria-label": label ?? s.optionsLabel, className: cx("btn-group", peleDoEixo("btn-group", orientation, "horizontal", "btn-group"), detached && "btn-group-detached", fullWidth && "btn-group-full", className), ...props }) });
262
+ }
221
263
  // Toolbar: roving tabindex (setas navegam, Tab entra/sai) via Base UI.
222
264
  // G-AXIS-06 — o valor responsivo RESOLVIDO é a única fonte de verdade: ele vai para o motor, o
223
265
  // motor publica `data-orientation`/`aria-orientation` e ajusta o teclado, e a pele reage ao
@@ -233,18 +275,9 @@ export function Toolbar({ orientation, label, className, ...props }) {
233
275
  export function ToolbarGroup({ label, className, ...props }) { return _jsx(BaseToolbar.Group, { "aria-label": label, className: cx("toolbar-group", className), ...props }); }
234
276
  export function ToolbarSeparator({ className, ...props }) { return _jsx(BaseToolbar.Separator, { className: cx("toolbar-sep", className), ...props }); }
235
277
  export const ToolbarButton = forwardRef(function ToolbarButton({ variant = "ghost", ...props }, ref) { return _jsx(BaseToolbar.Button, { ref: ref, disabled: props.disabled, render: _jsx(Button, { variant: variant, ...props }) }); });
236
- // TOGGLE GROUP — vários Toggle com estado compartilhado e navegação por seta entre eles.
237
- //
238
- // Não é o SegmentedControl, e confundir os dois constrói a coisa errada com o nome certo: o
239
- // `SegmentedControl` é escolha ÚNICA e obrigatória entre alternativas de uma mesma dimensão
240
- // ("dia / semana / mês") e se parece com uma cápsula; aqui cada botão liga e desliga por conta
241
- // própria, `multiple` permite vários ao mesmo tempo, e zero selecionado é estado legítimo. É a
242
- // barra de formatação de um editor, não um filtro de período.
243
- //
244
- // O motor dá o roving tabindex, o `loopFocus` e o contrato de pressionado; a pele é a do
245
- // `.btn-group`, porque um grupo de botões é o que isto é.
246
- export function ToggleGroup({ value, defaultValue, onValueChange, multiple, orientation, disabled, label, className, children }) {
278
+ export function ToggleGroup({ value, defaultValue, onValueChange, multiple, orientation, disabled, label, className, children, size, fullWidth, detached }) {
247
279
  const ancora = useRef(null);
248
280
  const resolvida = useValorResponsivo(orientation, "horizontal", ancora);
249
- return _jsx(BaseToggleGroup, { ref: ancora, value: value, defaultValue: defaultValue, onValueChange: soOValor(onValueChange), multiple: multiple, orientation: resolvida, disabled: disabled, "aria-label": label, className: cx("toggle-group", className), children: children });
281
+ const grupo = React.useMemo(() => ({ size }), [size]);
282
+ return _jsx(GrupoDeBotoesContext.Provider, { value: grupo, children: _jsx(BaseToggleGroup, { ref: ancora, value: value, defaultValue: defaultValue, onValueChange: soOValor(onValueChange), multiple: multiple, orientation: resolvida, disabled: disabled, "aria-label": label, className: cx("toggle-group", detached && "toggle-group-detached", fullWidth && "toggle-group-full", className), children: children }) });
250
283
  }
package/dist/agents.js CHANGED
@@ -100,7 +100,7 @@ export function HumanApproval({ title, description, details, risk, reasoning, de
100
100
  const s = useAureaStrings();
101
101
  return _jsxs("section", { className: cx("card", "approval", className), "aria-label": typeof title === "string" ? title : s.approvalLabel, ...props, children: [_jsxs("header", { className: "approval-head", children: [_jsx("strong", { children: title }), risk && _jsx(Badge, { variant: risk === "high" ? "danger" : risk === "medium" ? "warning" : "neutral", children: s.approvalRisk[risk] })] }), description && _jsx("p", { className: "approval-desc", children: description }), !!details?.length && _jsx(DataList, { items: details }), reasoning && _jsx("p", { className: "approval-reason", children: reasoning }), deadline && _jsxs("p", { className: "hint", children: [s.approvalDeadline, ": ", deadline] }), decision
102
102
  ? _jsx(Alert, { variant: decision === "approved" ? "success" : "warning", children: decision === "approved" ? s.approvalApproved : s.approvalDenied })
103
- : _jsxs("footer", { className: "approval-actions", children: [_jsx(Button, { variant: "secondary", onClick: onDeny, children: s.approvalDeny }), _jsx(Button, { variant: "primary", onClick: onApprove, children: s.approvalApprove })] })] });
103
+ : _jsxs("footer", { className: "approval-actions", children: [_jsx(Button, { variant: "tertiary", onClick: onDeny, children: s.approvalDeny }), _jsx(Button, { variant: "primary", onClick: onApprove, children: s.approvalApprove })] })] });
104
104
  }
105
105
  // ToolPermission — o que cada ferramenta pode, sem perguntar de novo.
106
106
  //
@@ -1,6 +1,43 @@
1
1
  import React from "react";
2
- import { DayPicker } from "react-day-picker";
2
+ import { DayPicker, type DateRange, type DayPickerLocale } from "react-day-picker";
3
+ import { type Responsive } from "./pure.js";
4
+ import { type IconName } from "./system.js";
5
+ import type { FieldSize } from "./markup.js";
3
6
  export type CalendarProps = React.ComponentProps<typeof DayPicker> & {
4
7
  label?: string;
5
8
  };
6
9
  export declare function Calendar({ label, className, classNames, ...props }: CalendarProps): React.JSX.Element;
10
+ export interface DatePickerBase {
11
+ minimumDate?: Date;
12
+ maximumDate?: Date;
13
+ disabled?: boolean;
14
+ size?: Responsive<FieldSize>;
15
+ /** A língua do campo E do calendário: o objeto de `react-day-picker/locale` (`import {ptBR} from
16
+ * "react-day-picker/locale"`), o mesmo que o `Calendar` recebe. Padrão: inglês, o padrão do motor. */
17
+ locale?: DayPickerLocale;
18
+ /** Como a data vira texto no gatilho. Padrão: dia, mês curto e ano na língua do `locale` (sem ele, inglês). */
19
+ format?: (d: Date) => string;
20
+ /** O texto do gatilho vazio. Padrão: a frase `datePlaceholder` (ou `dateRangePlaceholder` no intervalo). */
21
+ placeholder?: string;
22
+ /** O ícone no fim do gatilho. Padrão: `calendar`; `false` tira. */
23
+ icon?: IconName | false;
24
+ /** Vai no formulário como `aaaa-mm-dd`; no intervalo, `aaaa-mm-dd/aaaa-mm-dd` (o intervalo da ISO 8601). */
25
+ name?: string;
26
+ id?: string;
27
+ className?: string;
28
+ "aria-label"?: string;
29
+ "aria-labelledby"?: string;
30
+ "aria-describedby"?: string;
31
+ }
32
+ export type DatePickerProps = DatePickerBase & ({
33
+ range?: false;
34
+ value?: Date;
35
+ defaultValue?: Date;
36
+ onChange?: (d: Date) => void;
37
+ } | {
38
+ range: true;
39
+ value?: DateRange;
40
+ defaultValue?: DateRange;
41
+ onChange?: (r: DateRange) => void;
42
+ });
43
+ export declare function DatePicker(props: DatePickerProps): React.JSX.Element;
package/dist/calendar.js CHANGED
@@ -1,7 +1,44 @@
1
1
  "use client";
2
- import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ // A diretiva aqui NÃO vem de um hook nosso — este arquivo não chama nenhum. Vem do motor: o
4
+ // `react-day-picker` usa estado e NÃO publica `"use client"` no dist dele (medido em
5
+ // 06/08/2026, junto com `recharts` e `@tanstack/react-table`, que também não publicam; o
6
+ // `@base-ui/react` publica). Sem a diretiva, o módulo seria de servidor e o motor quebraria
7
+ // dentro dele. É o caso que a regra "tem hook? leva diretiva" não enxerga, e por isso o
8
+ // check 26 do validate.py também olha para essas três importações.
9
+ //
10
+ // Lote 4 do BUILDING.md (01/08/2026). SUBPATH PRÓPRIO, como ./chart, ./data-grid e ./qrcode: o
11
+ // único componente que precisa do `react-day-picker`, que é peer OPCIONAL.
12
+ //
13
+ // O MOTOR, e por que este: o Base UI — que já é dependência dura daqui — **não tem** calendário
14
+ // nem campo de data. Medido em 01/08/2026 na lista de exports do 1.6.0, que é a versão mais
15
+ // recente. Então o motor era decisão aberta, e a resposta é a mesma de uma das referências: react-day-picker.
16
+ //
17
+ // O que foi medido antes de escolher (o registro completo está no REFERENCES.md):
18
+ // • renderiza no SERVIDOR — 8618 bytes, <table> de verdade, 42 células. Ao contrário do
19
+ // Recharts, então o catálogo não precisa de prerender nenhum;
20
+ // • a semântica já vem certa: role="grid"/"gridcell", aria-selected, <button> por dia,
21
+ // o mês em role="status" aria-live, e <thead aria-hidden> com <th scope="col">;
22
+ // • tem API de tema PRÓPRIA (`classNames`), que é o que permite manter todo nome `rdp-*`
23
+ // fora do nosso CSS — mesma saída da pele do CodeEditor, que vem pelo EditorView.theme.
24
+ //
25
+ // O que NÃO entrou: o motor headless que outra das referências escolheu. Trazer um segundo motor
26
+ // headless para conviver com o Base UI é criar padrão paralelo, que o protocolo proíbe.
27
+ //
28
+ // ~~E o que NÃO se constrói aqui: um `DatePicker`. Aquela mesma referência não tem componente-raiz para isso e
29
+ // diz por quê — seletor de data é composição de Popover com calendário, e a Aurea já tem os
30
+ // dois. A composição está registrada como PATTERN, que é o veículo da casa para isso.~~
31
+ // 13/10/2026 (ADR-0068): o `DatePicker` ENTRA, e mora aqui. A referência principal tem a peça (a frase
32
+ // acima falava de outra referência), o nativo da Aurea já tem um `DatePicker`, e quase todo sistema do
33
+ // mercado entrega o seletor de data como componente — deixado como padrão, cada app refazia o foco, a
34
+ // janela e a leitura da data, e cada um saía diferente. Escolha do Victor: *"data B"*. Mora neste arquivo
35
+ // pelo mesmo motivo do `Calendar`: o motor de calendário é dependência OPCIONAL, e só este caminho o puxa.
36
+ import React from "react";
3
37
  import { DayPicker } from "react-day-picker";
4
- import { cx } from "./internal.js";
38
+ import { Popover as BasePopover } from "@base-ui/react/popover";
39
+ import { cx, useAureaStrings, usePortalContainer, controlesRotulaveis } from "./internal.js";
40
+ import { peleDoEixo } from "./pure.js";
41
+ import { Icon } from "./system.js";
5
42
  // Só TRÊS nomes são renomeados, e é de propósito. Todo o resto da pele é escrito contra ELEMENTO
6
43
  // (`.calendar table`, `.calendar th`, `.calendar td button`) e contra os `data-*` que o motor já
7
44
  // emite (`data-selected`, `data-today`, `data-outside`, `data-disabled`, `data-hidden`) — e
@@ -25,3 +62,79 @@ export function Calendar({ label, className, classNames, ...props }) {
25
62
  // motor não deixa a raiz dele ser um `group`, e nenhum dos dois papéis dele serve aqui.
26
63
  return label ? _jsx("div", { role: "group", "aria-label": label, children: grade }) : grade;
27
64
  }
65
+ // A data do formulário pela data LOCAL: `toISOString` passa por UTC e, à noite no Brasil, daria o dia seguinte.
66
+ const dataIso = (d) => `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-${String(d.getDate()).padStart(2, "0")}`;
67
+ // A língua vem do `locale`, a MESMA que o calendário usa, e nunca do ambiente: com a língua do ambiente, o
68
+ // servidor escrevia "12 de ago. de 2026" e o navegador "Aug 12, 2026" — medido em 13/10/2026, erro de
69
+ // hidratação 418 no catálogo. Sem `locale`, inglês, como o motor (a regra da casa do `agents.tsx`: padrão fixo).
70
+ const formatoPadrao = (d, locale) => d.toLocaleDateString(locale?.code ?? "en-US", { day: "numeric", month: "short", year: "numeric" });
71
+ export function DatePicker(props) {
72
+ const { minimumDate, maximumDate, disabled, size, locale, format, placeholder, icon = "calendar", name, id, className, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledby, "aria-describedby": ariaDescribedby } = props;
73
+ const s = useAureaStrings();
74
+ const portal = usePortalContainer();
75
+ const [aberta, setAberta] = React.useState(false);
76
+ const cliques = React.useRef(0);
77
+ const gatilho = React.useRef(null);
78
+ const [idDoRotulo, setIdDoRotulo] = React.useState();
79
+ const [interno, setInterno] = React.useState(props.defaultValue);
80
+ const valor = props.value !== undefined ? props.value : interno;
81
+ const formatar = format ?? ((d) => formatoPadrao(d, locale));
82
+ let texto;
83
+ let campo;
84
+ if (props.range) {
85
+ const r = valor;
86
+ if (r?.from)
87
+ texto = r.to && r.to.getTime() !== r.from.getTime() ? `${formatar(r.from)} – ${formatar(r.to)}` : formatar(r.from);
88
+ if (r?.from)
89
+ campo = `${dataIso(r.from)}/${dataIso(r.to ?? r.from)}`;
90
+ }
91
+ else {
92
+ const d = valor;
93
+ if (d) {
94
+ texto = formatar(d);
95
+ campo = dataIso(d);
96
+ }
97
+ }
98
+ const vazio = texto === undefined;
99
+ const limites = [minimumDate && { before: minimumDate }, maximumDate && { after: maximumDate }].filter(Boolean);
100
+ const comum = {
101
+ disabled: limites.length ? limites : undefined,
102
+ startMonth: minimumDate, endMonth: maximumDate, locale,
103
+ defaultMonth: props.range ? valor?.from : valor,
104
+ autoFocus: true,
105
+ };
106
+ // O intervalo é NOSSO, pelo dia clicado (o 2º argumento do motor), e não o que o motor calcula: o dele
107
+ // ESTENDE o intervalo que já existe — com 1 a 3 escolhido, clicar 15 e 20 dava 1 a 20. Aqui, cada
108
+ // abertura começa um intervalo novo: o 1º clique é uma ponta, o 2º a outra, em qualquer ordem.
109
+ const calendario = props.range
110
+ ? _jsx(Calendar, { ...comum, mode: "range", selected: valor, onSelect: (_, dia) => {
111
+ cliques.current += 1;
112
+ const inicio = valor?.from;
113
+ const r = cliques.current === 1 || !inicio ? { from: dia, to: undefined }
114
+ : dia < inicio ? { from: dia, to: inicio } : { from: inicio, to: dia };
115
+ if (props.value === undefined)
116
+ setInterno(r);
117
+ props.onChange?.(r);
118
+ if (cliques.current >= 2)
119
+ setAberta(false);
120
+ } })
121
+ : _jsx(Calendar, { ...comum, mode: "single", selected: valor, onSelect: (d) => {
122
+ if (!d)
123
+ return;
124
+ if (props.value === undefined)
125
+ setInterno(d);
126
+ props.onChange?.(d);
127
+ setAberta(false);
128
+ } });
129
+ return _jsxs(BasePopover.Root, { open: aberta, onOpenChange: (o) => {
130
+ // O `<label for>` que nomeia o gatilho (o do `Field` tem `id`), pela ligação do próprio navegador.
131
+ if (o) {
132
+ cliques.current = 0;
133
+ setIdDoRotulo(gatilho.current?.labels?.[0]?.id || undefined);
134
+ }
135
+ setAberta(o);
136
+ }, children: [_jsxs(BasePopover.Trigger, { ref: gatilho, disabled: disabled, id: id, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledby, "aria-describedby": ariaDescribedby, className: cx("select", "select-trigger", "date-picker-trigger", peleDoEixo("select", size), className), children: [_jsx("span", { className: "select-value", "data-placeholder": vazio ? "" : undefined, lang: vazio ? undefined : locale?.code, children: texto ?? placeholder ?? (props.range ? s.dateRangePlaceholder : s.datePlaceholder) }), icon !== false && _jsx(Icon, { name: icon })] }), name && _jsx("input", { type: "hidden", name: name, value: campo ?? "" }), _jsx(BasePopover.Portal, { container: portal, children: _jsx(BasePopover.Positioner, { className: "positioner", side: "bottom", align: "start", sideOffset: 6, children: _jsx(BasePopover.Popup, { className: "popover date-picker-popover", "aria-labelledby": ariaLabelledby ?? idDoRotulo, "aria-label": (ariaLabelledby ?? idDoRotulo) ? undefined
137
+ : ariaLabel ?? (props.range ? s.dateRangePlaceholder : s.datePlaceholder), children: calendario }) }) })] });
138
+ }
139
+ // O `Field` nomeia o gatilho com `<label for>`: o `id` que ele injeta vai para o botão (ver `internal.tsx`).
140
+ controlesRotulaveis.add(DatePicker);
@@ -1,7 +1,9 @@
1
1
  import React from "react";
2
- export declare function CodeBlock({ children, language, copyable, className }: {
2
+ export declare function CodeBlock({ children, language, copyable, preview, className }: {
3
3
  children: string;
4
4
  language?: string;
5
5
  copyable?: boolean;
6
+ /** A prévia viva do código: com ela, o bloco vira a caixa de exemplo, com as abas Prévia | Código. */
7
+ preview?: React.ReactNode;
6
8
  className?: string;
7
9
  }): React.JSX.Element;
@@ -9,7 +9,29 @@ import { Icon } from "./system.js";
9
9
  // copyable: o gatilho é marcado com data-aurea-copy e o COMPORTAMENTO mora no aurea.js do
10
10
  // core (uma implementação para React e para HTML puro). O onClick daqui atende quem só
11
11
  // carrega o pacote React; ele para a propagação pro core não copiar duas vezes.
12
- export function CodeBlock({ children, language = "text", copyable, className }) {
12
+ // DOC-02 (0.32.0): a CAIXA DE EXEMPLO do site da Aurea virou o `preview` do `CodeBlock` — as abas
13
+ // Prévia | Código (o controle segmentado ocupando a largura), a prévia no fundo afundado e o código sem
14
+ // moldura. É a caixa do site de hoje (decisão do Victor, 06/10/2026: a da Aurea, não a da referência),
15
+ // agora no pacote, para o site usar só peças da Aurea. O teclado é o das abas do APG: setas, Home e End.
16
+ // Sem o `aurea.js` na página — quem troca a aba é o React.
17
+ function CaixaDeExemplo({ preview, codigo, className }) {
18
+ const s = useAureaStrings();
19
+ const id = React.useId();
20
+ const [aba, setAba] = React.useState("preview");
21
+ const refPrevia = React.useRef(null), refCodigo = React.useRef(null);
22
+ const ir = (a) => { setAba(a); (a === "preview" ? refPrevia : refCodigo).current?.focus(); };
23
+ const tecla = (e) => {
24
+ if (!["ArrowRight", "ArrowLeft", "Home", "End"].includes(e.key))
25
+ return;
26
+ e.preventDefault();
27
+ ir(e.key === "Home" ? "preview" : e.key === "End" ? "code" : aba === "preview" ? "code" : "preview");
28
+ };
29
+ const botao = (a, rotulo) => _jsx("button", { type: "button", role: "tab", id: `${id}-${a}`, ref: a === "preview" ? refPrevia : refCodigo, className: aba === a ? "active" : undefined, "aria-selected": aba === a, "aria-controls": `${id}-${a}-panel`, tabIndex: aba === a ? 0 : -1, onClick: () => setAba(a), onKeyDown: tecla, children: rotulo });
30
+ return _jsxs("div", { className: cx("demo code-preview", className), children: [_jsx("div", { className: "demo-tabs", children: _jsxs("div", { className: "segmented", role: "tablist", children: [botao("preview", s.codeBlockPreview), botao("code", s.codeBlockCode)] }) }), _jsx("div", { className: "demo-panel", "data-panel": "preview", role: "tabpanel", id: `${id}-preview-panel`, "aria-labelledby": `${id}-preview`, tabIndex: 0, hidden: aba !== "preview", children: preview }), _jsx("div", { className: "demo-panel demo-code", "data-panel": "code", role: "tabpanel", id: `${id}-code-panel`, "aria-labelledby": `${id}-code`, tabIndex: 0, hidden: aba !== "code", children: codigo })] });
31
+ }
32
+ export function CodeBlock({ children, language = "text", copyable, preview, className }) {
33
+ if (preview !== undefined)
34
+ return _jsx(CaixaDeExemplo, { preview: preview, className: className, codigo: _jsx(CodeBlock, { language: language, copyable: copyable, children: children }) });
13
35
  const s = useAureaStrings();
14
36
  // tabIndex no <pre>: `.code-block` é `overflow:auto` por construção, então é REGIÃO ROLÁVEL
15
37
  // e não tem nada focável dentro — quem navega por teclado não alcança o código que passa da