@aurea-uds/react 0.8.13 → 0.9.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/dist/actions.d.ts CHANGED
@@ -24,6 +24,15 @@ export interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement>, Re
24
24
  trailingIcon?: IconName;
25
25
  href?: string;
26
26
  fullWidth?: boolean;
27
+ /** C-03 (24/09/2026): numa fila (`Cluster`), divide o espaço em partes iguais com os outros botões
28
+ * que também têm `grow`. O `fullWidth` não serve ali: ele pede a linha inteira. */
29
+ grow?: boolean;
30
+ /** C-13 (24/09/2026): atributos de LINK, que só valem com `href` — sem ele são ignorados. Antes
31
+ * chegavam ao `<a>` em tempo de execução, mas o tipo não os aceitava, e `target="_blank"` não
32
+ * compilava. */
33
+ target?: React.HTMLAttributeAnchorTarget;
34
+ rel?: string;
35
+ download?: boolean | string;
27
36
  /**
28
37
  * @deprecated Use `<Toggle>` instead. Pesquisado em 18/08/2026 nas doze referências (MUI,
29
38
  * Fluent 2, React Aria, Spectrum, Carbon, Radix/Base UI, shadcn, ReUI, PrimeReact, HeroUI,
package/dist/actions.js CHANGED
@@ -45,7 +45,7 @@ export function buttonSkin(variant, appearance, tone) {
45
45
  // type="button" por default: o default do HTML é submit, e um "Cancelar"/"Remover"
46
46
  // dentro de <form> dispararia a ação principal (auditoria 18/07/2026, ALTO 1).
47
47
  // Quem quer submeter passa type="submit" explícito (como o MessageComposer faz).
48
- export const Button = forwardRef(function Button({ variant, appearance, tone, size = "md", loading, leadingIcon, trailingIcon, className, children, disabled, type = "button", href, fullWidth, pressed, kbd, onClick, onClickCapture, "aria-disabled": ariaDisabled, ...props }, ref) {
48
+ 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, ...props }, ref) {
49
49
  // G-AXIS-04 — `size` aceita valor simples, responsivo por viewport ou adaptativo por container.
50
50
  // SOMATIVO por construção: valor simples continua emitindo `btn-sm`, byte por byte a mesma classe
51
51
  // de antes, e por isso nenhuma baseline se mexe. Só o valor responsivo entra pela camada genérica
@@ -53,7 +53,7 @@ export const Button = forwardRef(function Button({ variant, appearance, tone, si
53
53
  // Quem decide é o CSS: nada aqui observa largura, e o HTML do servidor já sai correto.
54
54
  const responsivo = ehResponsivo(size);
55
55
  const base = valorBase(size) ?? "md";
56
- const cls = cx("btn", buttonSkin(variant, appearance, tone), responsivo ? classesResponsivas("size", size) : (base !== "md" && `btn-${base}`), fullWidth && "btn-block", className);
56
+ const cls = cx("btn", buttonSkin(variant, appearance, tone), responsivo ? classesResponsivas("size", size) : (base !== "md" && `btn-${base}`), fullWidth && "btn-block", grow && "btn-grow", className);
57
57
  // kbd dentro do botão: mostra o atalho E o anuncia (aria-keyshortcuts), senão é enfeite.
58
58
  const inner = _jsxs(_Fragment, { children: [loading && _jsx("span", { className: "spinner" }), leadingIcon && _jsx(Icon, { name: leadingIcon }), _jsx("span", { children: children }), kbd && _jsx(Kbd, { children: kbd }), trailingIcon && _jsx(Icon, { name: trailingIcon })] });
59
59
  // INERTE ≠ DESABILITADO, e a diferença é medida (M4, 13/08/2026): `disabled` tira o botão da
@@ -76,7 +76,7 @@ export const Button = forwardRef(function Button({ variant, appearance, tone, si
76
76
  const bloqueia = (e) => { e.preventDefault(); e.stopPropagation(); };
77
77
  const eventos = (off || inerte) ? { onClick: bloqueia, onClickCapture: bloqueia } : { onClick: onClick, onClickCapture: onClickCapture };
78
78
  if (href !== undefined)
79
- return _jsx("a", { ref: ref, className: cls, ...shared, ...props, ...(off ? { "aria-disabled": true } : { href }), ...eventos, children: inner });
79
+ return _jsx("a", { ref: ref, className: cls, target: target, rel: rel, download: download, ...shared, ...props, ...(off ? { "aria-disabled": true } : { href }), ...eventos, children: inner });
80
80
  return _jsx("button", { ref: ref, type: type, className: cls, disabled: off, "aria-disabled": inerte || undefined, "aria-pressed": pressed, ...shared, ...(inerte ? { onClick: bloqueia, onClickCapture: bloqueia } : { onClick, onClickCapture }), ...props, children: inner });
81
81
  });
82
82
  function temConteudoVisivel(children) {
@@ -23,11 +23,14 @@ export interface InputProps extends Omit<InputHTMLAttributes<HTMLInputElement>,
23
23
  size?: Responsive<FieldSize>;
24
24
  }
25
25
  export declare const Input: React.ForwardRefExoticComponent<Omit<InputProps, "ref"> & RefAttributes<HTMLInputElement>>;
26
- export interface SearchFieldProps extends Omit<InputHTMLAttributes<HTMLInputElement>, "size">, RefAttributes<HTMLInputElement> {
26
+ export interface SearchFieldProps extends Omit<InputHTMLAttributes<HTMLInputElement>, "size" | "width">, RefAttributes<HTMLInputElement> {
27
27
  icon?: IconName;
28
28
  size?: Responsive<FieldSize>;
29
+ /** A-08 (24/09/2026): a largura do campo — `"20rem"` ou `"auto"`. Sem ela, 100% da linha, como
30
+ * sempre. O `size` muda altura e letra, nunca largura. */
31
+ width?: string;
29
32
  }
30
- export declare function SearchField({ icon, className, size, ...props }: SearchFieldProps): React.JSX.Element;
33
+ export declare function SearchField({ icon, className, size, width, ...props }: SearchFieldProps): React.JSX.Element;
31
34
  export declare function NumberField({ value, defaultValue, onValueChange, min, max, step, disabled, readOnly, required, label, id, name, format, locale, className, scrubbable, scrubDirection }: {
32
35
  value?: number | null;
33
36
  defaultValue?: number;
@@ -76,7 +79,7 @@ export interface ComboboxOptGroup {
76
79
  label: string;
77
80
  items: ComboboxOption[];
78
81
  }
79
- export declare function Combobox({ items, value, onValueChange, placeholder, label, empty, disabled, size, id, className, "aria-describedby": descrito, "aria-invalid": invalido }: {
82
+ export declare function Combobox({ items, value, onValueChange, placeholder, label, empty, disabled, size, id, className, "aria-describedby": descrito, "aria-invalid": invalido, "aria-label": rotulo }: {
80
83
  items: ComboboxOption[] | ComboboxOptGroup[];
81
84
  empty?: ReactNode;
82
85
  value?: ComboboxOption | null;
@@ -89,8 +92,11 @@ export declare function Combobox({ items, value, onValueChange, placeholder, lab
89
92
  className?: string;
90
93
  "aria-describedby"?: string;
91
94
  "aria-invalid"?: boolean | "true" | "false";
95
+ /** C-01 (24/09/2026): nome para o leitor de tela quando não há `label` visível — um filtro numa
96
+ * grade sem rótulos. Vai direto para o campo. Com `label`, prefira o `label`. */
97
+ "aria-label"?: string;
92
98
  }): React.JSX.Element;
93
- export declare function MultiCombobox({ items, value, onValueChange, onInputChange, loading, placeholder, label, empty, disabled, id, className, "aria-describedby": descrito, "aria-invalid": invalido }: {
99
+ export declare function MultiCombobox({ items, value, onValueChange, onInputChange, loading, placeholder, label, empty, disabled, id, className, "aria-describedby": descrito, "aria-invalid": invalido, "aria-label": rotulo }: {
94
100
  items: ComboboxOption[] | ComboboxOptGroup[];
95
101
  empty?: ReactNode;
96
102
  value?: ComboboxOption[];
@@ -104,6 +110,9 @@ export declare function MultiCombobox({ items, value, onValueChange, onInputChan
104
110
  className?: string;
105
111
  "aria-describedby"?: string;
106
112
  "aria-invalid"?: boolean | "true" | "false";
113
+ /** C-01 (24/09/2026): nome para o leitor de tela quando não há `label` visível — um filtro numa
114
+ * grade sem rótulos. Vai direto para o campo. Com `label`, prefira o `label`. */
115
+ "aria-label"?: string;
107
116
  }): React.JSX.Element;
108
117
  export interface SelectOption {
109
118
  value: string;
@@ -162,7 +162,7 @@ export const Input = forwardRef(function Input({ className, formatOnBlur, size,
162
162
  // `.input-wrap-end` para o botão do PasswordField —, e o grupo é a terceira vez virando uma. O
163
163
  // merge das duas linhagens tinha trazido de volta a versão com `.input-wrap`, contra um core
164
164
  // que já não a pinta.
165
- export function SearchField({ icon = "search", className, size, ...props }) { return _jsxs(InputGroup, { className: className, children: [_jsx(InputGroupAddon, { children: _jsx(Icon, { name: icon, size: valorBase(size) === "sm" ? "sm" : undefined }) }), _jsx(Input, { type: "search", size: size, ...props })] }); }
165
+ export function SearchField({ icon = "search", className, size, width, ...props }) { return _jsxs(InputGroup, { className: className, width: width, children: [_jsx(InputGroupAddon, { children: _jsx(Icon, { name: icon, size: valorBase(size) === "sm" ? "sm" : undefined }) }), _jsx(Input, { type: "search", size: size, ...props })] }); }
166
166
  // labelHidden: coluna de seleção de tabela precisa do rótulo POR LINHA para o leitor de
167
167
  // tela ("Select Analyst"), mas mostrá-lo engorda a coluna. O rótulo continua no DOM, só
168
168
  // sai da tela (.sr-only) — nunca trocar por aria-label solto num <label> visível vazio.
@@ -253,12 +253,12 @@ const listaCombobox = (items) => _jsx(BaseCombobox.List, { children: ehAgrupado(
253
253
  // `disabled` vai na ROOT, não no Input: `Input`, `Trigger`, `Clear` e `ChipRemove` leem todos o
254
254
  // mesmo `selectors.disabled` do store do Base UI (@base-ui/react 1.6.0). Desabilitar o Input
255
255
  // deixaria os dois botões vivos — abrir a lista de um campo desabilitado é o defeito, não o texto.
256
- export function Combobox({ items, value, onValueChange, placeholder, label, empty, disabled, size, id, className, "aria-describedby": descrito, "aria-invalid": invalido }) {
256
+ export function Combobox({ items, value, onValueChange, placeholder, label, empty, disabled, size, id, className, "aria-describedby": descrito, "aria-invalid": invalido, "aria-label": rotulo }) {
257
257
  const s = useAureaStrings();
258
258
  const portal = usePortalContainer();
259
259
  const autoId = React.useId();
260
260
  const inputId = id ?? autoId;
261
- return _jsxs(BaseCombobox.Root, { items: items, value: value, onValueChange: soOValor(onValueChange), disabled: disabled, itemToStringLabel: (i) => i.label, children: [_jsxs("div", { className: cx("field", className), children: [label && _jsx("label", { className: "label", htmlFor: inputId, children: label }), _jsxs(BaseCombobox.InputGroup, { className: "combobox-group", children: [_jsx(BaseCombobox.Input, { id: inputId, placeholder: placeholder, className: cx("input", peleDoEixo("input", size)), "aria-describedby": descrito, "aria-invalid": invalido }), _jsxs("span", { className: "combobox-actions", children: [_jsx(BaseCombobox.Clear, { render: _jsx(IconButton, { variant: "ghost", size: "sm", icon: "close", label: s.comboboxClear }) }), _jsx(BaseCombobox.Trigger, { render: _jsx(IconButton, { variant: "ghost", size: "sm", icon: "chevron--down", label: s.comboboxOpen }) })] })] })] }), _jsx(BaseCombobox.Portal, { container: portal, children: _jsx(BaseCombobox.Positioner, { sideOffset: 6, children: _jsxs(BaseCombobox.Popup, { className: "menu combobox-popup", children: [_jsx(BaseCombobox.Empty, { className: "combobox-empty", children: empty ?? s.comboboxEmpty }), listaCombobox(items)] }) }) })] });
261
+ return _jsxs(BaseCombobox.Root, { items: items, value: value, onValueChange: soOValor(onValueChange), disabled: disabled, itemToStringLabel: (i) => i.label, children: [_jsxs("div", { className: cx("field", className), children: [label && _jsx("label", { className: "label", htmlFor: inputId, children: label }), _jsxs(BaseCombobox.InputGroup, { className: "combobox-group", children: [_jsx(BaseCombobox.Input, { id: inputId, placeholder: placeholder, className: cx("input", peleDoEixo("input", size)), "aria-describedby": descrito, "aria-invalid": invalido, "aria-label": rotulo }), _jsxs("span", { className: "combobox-actions", children: [_jsx(BaseCombobox.Clear, { render: _jsx(IconButton, { variant: "ghost", size: "sm", icon: "close", label: s.comboboxClear }) }), _jsx(BaseCombobox.Trigger, { render: _jsx(IconButton, { variant: "ghost", size: "sm", icon: "chevron--down", label: s.comboboxOpen }) })] })] })] }), _jsx(BaseCombobox.Portal, { container: portal, children: _jsx(BaseCombobox.Positioner, { sideOffset: 6, children: _jsxs(BaseCombobox.Popup, { className: "menu combobox-popup", children: [_jsx(BaseCombobox.Empty, { className: "combobox-empty", children: empty ?? s.comboboxEmpty }), listaCombobox(items)] }) }) })] });
262
262
  }
263
263
  // MultiCombobox (Fase 4): multi-seleção com chips, opções agrupadas e filtro
264
264
  // externo (async). Reusa .combobox-popup/-item/-empty do básico.
@@ -267,13 +267,13 @@ export function Combobox({ items, value, onValueChange, placeholder, label, empt
267
267
  // - Async: passar onInputChange; o filtro interno do Base UI desliga
268
268
  // (filter={null}) e o consumidor troca os items conforme a busca retorna.
269
269
  // loading só troca o texto do vazio para não piscar "Nenhum resultado".
270
- export function MultiCombobox({ items, value, onValueChange, onInputChange, loading, placeholder, label, empty, disabled, id, className, "aria-describedby": descrito, "aria-invalid": invalido }) {
270
+ export function MultiCombobox({ items, value, onValueChange, onInputChange, loading, placeholder, label, empty, disabled, id, className, "aria-describedby": descrito, "aria-invalid": invalido, "aria-label": rotulo }) {
271
271
  const s = useAureaStrings();
272
272
  const portal = usePortalContainer();
273
273
  const autoId = React.useId();
274
274
  const inputId = id ?? autoId;
275
275
  return _jsxs(BaseCombobox.Root, { multiple: true, items: items, value: value, onValueChange: soOValor(onValueChange), disabled: disabled, itemToStringLabel: (i) => i.label, filter: onInputChange ? null : undefined, onInputValueChange: onInputChange ? ((v, d) => { if (d.reason !== "item-press")
276
- onInputChange(v); }) : undefined, children: [_jsxs("div", { className: cx("field", className), children: [label && _jsx("label", { className: "label", htmlFor: inputId, children: label }), _jsxs(BaseCombobox.InputGroup, { className: "combobox-multi", children: [_jsx(BaseCombobox.Chips, { className: "combobox-chips", children: _jsx(BaseCombobox.Value, { children: (selected) => _jsxs(_Fragment, { children: [selected.map((item) => _jsxs(BaseCombobox.Chip, { className: "combobox-chip", children: [item.label, _jsx(BaseCombobox.ChipRemove, { className: "combobox-chip-remove", "aria-label": `${s.comboboxRemove} ${item.label}`, children: _jsx(Icon, { name: "close", size: "sm" }) })] }, item.value)), _jsx(BaseCombobox.Input, { id: inputId, placeholder: selected.length ? undefined : placeholder, className: "combobox-chip-input", "aria-describedby": descrito, "aria-invalid": invalido })] }) }) }), _jsxs("span", { className: "combobox-actions", children: [_jsx(BaseCombobox.Clear, { render: _jsx(IconButton, { variant: "ghost", size: "sm", icon: "close", label: s.comboboxClear }) }), _jsx(BaseCombobox.Trigger, { render: _jsx(IconButton, { variant: "ghost", size: "sm", icon: "chevron--down", label: s.comboboxOpen }) })] })] })] }), _jsx(BaseCombobox.Portal, { container: portal, children: _jsx(BaseCombobox.Positioner, { sideOffset: 6, children: _jsxs(BaseCombobox.Popup, { className: "menu combobox-popup", children: [_jsx(BaseCombobox.Empty, { className: "combobox-empty", children: loading ? s.comboboxLoading : (empty ?? s.comboboxEmpty) }), listaCombobox(items)] }) }) })] });
276
+ onInputChange(v); }) : undefined, children: [_jsxs("div", { className: cx("field", className), children: [label && _jsx("label", { className: "label", htmlFor: inputId, children: label }), _jsxs(BaseCombobox.InputGroup, { className: "combobox-multi", children: [_jsx(BaseCombobox.Chips, { className: "combobox-chips", children: _jsx(BaseCombobox.Value, { children: (selected) => _jsxs(_Fragment, { children: [selected.map((item) => _jsxs(BaseCombobox.Chip, { className: "combobox-chip", children: [item.label, _jsx(BaseCombobox.ChipRemove, { className: "combobox-chip-remove", "aria-label": `${s.comboboxRemove} ${item.label}`, children: _jsx(Icon, { name: "close", size: "sm" }) })] }, item.value)), _jsx(BaseCombobox.Input, { id: inputId, placeholder: selected.length ? undefined : placeholder, className: "combobox-chip-input", "aria-describedby": descrito, "aria-invalid": invalido, "aria-label": rotulo })] }) }) }), _jsxs("span", { className: "combobox-actions", children: [_jsx(BaseCombobox.Clear, { render: _jsx(IconButton, { variant: "ghost", size: "sm", icon: "close", label: s.comboboxClear }) }), _jsx(BaseCombobox.Trigger, { render: _jsx(IconButton, { variant: "ghost", size: "sm", icon: "chevron--down", label: s.comboboxOpen }) })] })] })] }), _jsx(BaseCombobox.Portal, { container: portal, children: _jsx(BaseCombobox.Positioner, { sideOffset: 6, children: _jsxs(BaseCombobox.Popup, { className: "menu combobox-popup", children: [_jsx(BaseCombobox.Empty, { className: "combobox-empty", children: loading ? s.comboboxLoading : (empty ?? s.comboboxEmpty) }), listaCombobox(items)] }) }) })] });
277
277
  }
278
278
  const ehGrupoDoSelect = (i) => typeof i === "object" && i != null && "items" in i;
279
279
  // Lê `<option>`/`<optgroup>` como o navegador lê: `value` ausente vale o TEXTO da opção.
@@ -12,6 +12,8 @@ export declare function AppShell({ brand, navigation, navItems, currentNavId, na
12
12
  sidebarVariant?: SidebarVariant;
13
13
  sidebarCollapsed?: boolean;
14
14
  }): import("react").JSX.Element;
15
- export declare function Separator({ orientation, className, ...props }: HTMLAttributes<HTMLHRElement> & RefAttributes<HTMLHRElement> & {
15
+ export type SeparatorSpacing = "none" | "tight" | "normal" | "loose";
16
+ export declare function Separator({ orientation, spacing, className, ...props }: HTMLAttributes<HTMLHRElement> & RefAttributes<HTMLHRElement> & {
16
17
  orientation?: Responsive<Orientation>;
18
+ spacing?: SeparatorSpacing;
17
19
  }): import("react").JSX.Element;
@@ -121,31 +121,11 @@ export function AppShell({ brand, navigation, navItems, currentNavId, navLabel,
121
121
  useGavetaFechaNoDesktop();
122
122
  return _jsxs("div", { className: cx("app-shell", topbarVariant === "flush" && "app-shell-flush", className), ...props, children: [_jsx("a", { className: "skip-link", href: `#${SHELL_MAIN_ID}`, children: s.skipToContent }), _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, items: navItems, current: currentNavId, label: navLabel, onToggle: focoDaGaveta, children: navigation }), _jsx("main", { id: SHELL_MAIN_ID, tabIndex: -1, className: "content", children: children })] });
123
123
  }
124
- // ── Separator ────────────────────────────────────────────────────────────────────────────────
125
- // A LINHA DO SISTEMA, e ela é de CLIENTE por necessidade, não por vizinhança.
126
- //
127
- // Ela já foi um <hr> com orientação estática, no `markup.tsx`. O merge das duas linhagens ficou
128
- // com essa versão, e o gate `comportamental.multi-motor.spec.ts` reprovou: a prova das cinco
129
- // capacidades do `G-AXIS-06` — a lista que o Victor especificou — cobra do separador
130
- // `data-orientation` E `aria-orientation` resolvidos pela largura do CONTÊINER, e os dois são
131
- // atributo. Atributo não sai de classe CSS. Eixo comportamental precisa de runtime.
132
- //
133
- // E O ELEMENTO CONTINUA SENDO UM <hr>. As duas exigências pareciam brigar — a suíte de unidade
134
- // cobra `<hr>` sem `role` escrito ("ARIA redundante é pior que ARIA nenhuma", aviso do próprio
135
- // APG), e a prova de contêiner cobra atributo resolvido em runtime. `render` resolve as duas:
136
- // o motor calcula e publica sobre o elemento que a Aurea escolhe.
137
- //
138
- // `role={undefined}` e o `aria-orientation` explícito são o acerto fino, e foram MEDIDOS antes
139
- // de escrever: por padrão o motor escreve `role="separator"` no <hr> (redundante — o <hr> já É
140
- // separator) e escreve `aria-orientation` também no HORIZONTAL, que é o default implícito do
141
- // papel. As duas props passadas aqui vencem as do motor, e sobra exatamente o que se quer:
142
- // vertical → <hr data-orientation="vertical" aria-orientation="vertical" class="separator separator-vertical">
143
- // horizontal → <hr data-orientation="horizontal" class="separator">
144
- export function Separator({ orientation, className, ...props }) {
124
+ export function Separator({ orientation, spacing = "none", className, ...props }) {
145
125
  const ancora = useRef(null);
146
126
  const resolvida = useValorResponsivo(orientation, "horizontal", ancora);
147
127
  // O elenco existe porque o tipo do motor descreve os manipuladores sobre `HTMLDivElement` — ele
148
128
  // não sabe que o `render` troca o elemento. O tipo PÚBLICO desta função é o certo: quem passar um
149
129
  // `ref` recebe o <hr> que está de fato no DOM.
150
- return _jsx(BaseSeparator, { ref: ancora, orientation: resolvida, render: _jsx("hr", {}), role: undefined, "aria-orientation": resolvida === "vertical" ? "vertical" : undefined, className: cx("separator", resolvida === "vertical" && "separator-vertical", className), ...props });
130
+ return _jsx(BaseSeparator, { ref: ancora, orientation: resolvida, render: _jsx("hr", {}), role: undefined, "aria-orientation": resolvida === "vertical" ? "vertical" : undefined, className: cx("separator", resolvida === "vertical" && "separator-vertical", spacing !== "none" && `separator-space-${spacing}`, className), ...props });
151
131
  }
package/dist/markup.d.ts CHANGED
@@ -4,7 +4,9 @@ import type { ComponentSize } from "./actions.js";
4
4
  export type BadgeVariant = "neutral" | "primary" | "info" | "success" | "warning" | "danger" | "running" | "paused" | "offline" | "review";
5
5
  export type AvatarSize = "sm" | "md" | "lg";
6
6
  export type TopbarVariant = "floating" | "flush" | "pill";
7
- type CardBase = HTMLAttributes<HTMLDivElement> & RefAttributes<HTMLDivElement>;
7
+ type CardBase = HTMLAttributes<HTMLDivElement> & RefAttributes<HTMLDivElement> & {
8
+ padding?: "normal" | "none";
9
+ };
8
10
  export type CardProps = CardBase & ({
9
11
  variant?: "base" | "raised" | "inset" | "selected" | "danger";
10
12
  render?: ReactElement;
@@ -12,10 +14,42 @@ export type CardProps = CardBase & ({
12
14
  variant: "interactive";
13
15
  render: ReactElement;
14
16
  });
15
- export declare function Card({ variant, className, render, ...props }: CardProps): ReactElement<unknown, string | React.JSXElementConstructor<any>>;
16
- export declare function Stack({ className, ...props }: HTMLAttributes<HTMLDivElement> & RefAttributes<HTMLDivElement>): React.JSX.Element;
17
- export declare function Cluster({ className, ...props }: HTMLAttributes<HTMLDivElement> & RefAttributes<HTMLDivElement>): React.JSX.Element;
18
- export declare function Grid({ className, ...props }: HTMLAttributes<HTMLDivElement> & RefAttributes<HTMLDivElement>): React.JSX.Element;
17
+ export declare function Card({ variant, padding, className, render, ...props }: CardProps): ReactElement<unknown, string | React.JSXElementConstructor<any>>;
18
+ export type LayoutGap = "tight" | "normal" | "loose";
19
+ export type StackAlign = "start" | "center" | "end" | "stretch";
20
+ export type ClusterAlign = "start" | "center" | "end" | "baseline";
21
+ export type ClusterJustify = "start" | "center" | "end" | "between";
22
+ type DivProps = HTMLAttributes<HTMLDivElement> & RefAttributes<HTMLDivElement>;
23
+ export interface StackProps extends DivProps {
24
+ /** `tight` (--space-2), `normal` (o de sempre, --space-4) ou `loose` (--space-6). */
25
+ gap?: LayoutGap;
26
+ /** Eixo cruzado. Padrão `stretch`: os filhos ocupam a largura, como sempre. */
27
+ align?: StackAlign;
28
+ }
29
+ export declare function Stack({ gap, align, className, ...props }: StackProps): React.JSX.Element;
30
+ export interface ClusterProps extends DivProps {
31
+ /** `tight` (--space-2), `normal` (o de sempre, --space-3) ou `loose` (--space-6). */
32
+ gap?: LayoutGap;
33
+ /** Eixo cruzado (o vertical). Padrão `center`, como sempre. */
34
+ align?: ClusterAlign;
35
+ /** Eixo principal. `between` espalha o que sobrar: o nome à esquerda, o botão à direita. */
36
+ justify?: ClusterJustify;
37
+ /** Padrão `true`: a fila quebra linha. `false` mantém tudo numa linha só. */
38
+ wrap?: boolean;
39
+ }
40
+ export declare function Cluster({ gap, align, justify, wrap, className, ...props }: ClusterProps): React.JSX.Element;
41
+ export interface GridProps extends DivProps {
42
+ /** `tight` (--space-2), `normal` (o de sempre, --space-4) ou `loose` (--space-6). */
43
+ gap?: LayoutGap;
44
+ /**
45
+ * Largura mínima de cada coluna, em unidade de CSS (`"10rem"`, `"155px"`). Padrão `15rem`. É o
46
+ * `--grid-min` que o CSS sempre leu e que nenhum tipo mostrava.
47
+ */
48
+ min?: string;
49
+ /** Número fixo de colunas, iguais. Com ele, `min` deixa de valer. */
50
+ columns?: number;
51
+ }
52
+ export declare function Grid({ gap, min, columns, className, style, ...props }: GridProps): React.JSX.Element;
19
53
  export declare function KPI({ label, value, trend, className, ...props }: HTMLAttributes<HTMLDivElement> & RefAttributes<HTMLDivElement> & {
20
54
  label: ReactNode;
21
55
  value: ReactNode;
@@ -38,6 +72,7 @@ export declare function Prose({ className, ...props }: HTMLAttributes<HTMLDivEle
38
72
  export type BadgeEmphasis = "soft" | "solid" | "outline";
39
73
  export type BadgeSize = "xs" | "sm" | "md" | "lg";
40
74
  export type BadgePlacement = "top-end" | "top-start" | "bottom-end" | "bottom-start";
75
+ export type BadgeFit = "auto" | "content";
41
76
  export declare function formatBadgeCount(count: number, max?: number): string;
42
77
  export interface BadgeProps extends HTMLAttributes<HTMLSpanElement>, RefAttributes<HTMLSpanElement> {
43
78
  variant?: BadgeVariant;
@@ -55,6 +90,8 @@ export interface BadgeProps extends HTMLAttributes<HTMLSpanElement>, RefAttribut
55
90
  count?: number;
56
91
  max?: number;
57
92
  showZero?: boolean;
93
+ /** `content`: do tamanho do texto mesmo numa coluna. Padrão `auto`, que segue o recipiente. */
94
+ fit?: BadgeFit;
58
95
  /** Liga o modo SOBREPOSTO e escolhe o canto. `children` passa a ser o que se decora. */
59
96
  anchor?: BadgePlacement;
60
97
  /** `circle` recolhe o canto em 14% — é o `overlap` da MUI, para avatar redondo. */
@@ -65,7 +102,7 @@ export interface BadgeProps extends HTMLAttributes<HTMLSpanElement>, RefAttribut
65
102
  * Nome da MUI, e não `content`: este colide com o atributo HTML de mesmo nome. */
66
103
  badgeContent?: ReactNode;
67
104
  }
68
- 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;
105
+ export declare function Badge({ variant, emphasis, size, dot, leading, trailing, image, imageAlt, count, max, showZero, fit, anchor, anchorShape, invisible, badgeContent, children, className, ...props }: BadgeProps): React.JSX.Element;
69
106
  export declare function Progress({ value, label }: {
70
107
  value: number;
71
108
  label?: string;
@@ -120,7 +157,9 @@ export declare function Label({ htmlFor, className, children, ...props }: HTMLAt
120
157
  }): React.JSX.Element;
121
158
  export type AddonSide = "start" | "end";
122
159
  export type AddonLayout = "inline" | "block";
123
- export declare function InputGroup({ className, children, ...props }: HTMLAttributes<HTMLDivElement> & RefAttributes<HTMLDivElement>): React.JSX.Element;
160
+ export declare function InputGroup({ className, children, width, style, ...props }: HTMLAttributes<HTMLDivElement> & RefAttributes<HTMLDivElement> & {
161
+ width?: string;
162
+ }): React.JSX.Element;
124
163
  export declare function InputGroupAddon({ side, layout, className, children, ...props }: HTMLAttributes<HTMLSpanElement> & RefAttributes<HTMLSpanElement> & {
125
164
  side?: AddonSide;
126
165
  layout?: Responsive<AddonLayout>;
package/dist/markup.js CHANGED
@@ -37,39 +37,17 @@ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-run
37
37
  // tocar motor. Na dúvida, ele NÃO entra — cliente a mais é lento, servidor a mais é quebrado.
38
38
  import React, { forwardRef } from "react";
39
39
  import { cx, fundirRender, peleDoEixo } from "./pure.js";
40
- export function Card({ variant = "base", className, render, ...props }) {
41
- return fundirRender(render, { className: cx("card", variant !== "base" && `card-${variant}`, className), ...props });
40
+ export function Card({ variant = "base", padding = "normal", className, render, ...props }) {
41
+ return fundirRender(render, { className: cx("card", variant !== "base" && `card-${variant}`, padding === "none" && "card-flush", className), ...props });
42
+ }
43
+ export function Stack({ gap, align, className, ...props }) { return _jsx("div", { className: cx("stack", gap && gap !== "normal" && `stack-gap-${gap}`, align && align !== "stretch" && `stack-align-${align}`, className), ...props }); }
44
+ export function Cluster({ gap, align, justify, wrap, className, ...props }) { return _jsx("div", { className: cx("cluster", gap && gap !== "normal" && `cluster-gap-${gap}`, align && align !== "center" && `cluster-align-${align}`, justify && justify !== "start" && `cluster-justify-${justify}`, wrap === false && "cluster-nowrap", className), ...props }); }
45
+ export function Grid({ gap, min, columns, className, style, ...props }) {
46
+ const vars = min != null || columns != null
47
+ ? { ...(min != null ? { "--grid-min": min } : {}), ...(columns != null ? { "--grid-cols": String(columns) } : {}), ...style }
48
+ : style;
49
+ return _jsx("div", { className: cx("grid", gap && gap !== "normal" && `grid-gap-${gap}`, columns != null && "grid-fixed", className), style: vars, ...props });
42
50
  }
43
- export function Stack({ className, ...props }) { return _jsx("div", { className: cx("stack", className), ...props }); }
44
- // SEPARATOR — a régua entre coisas. Faltava: a Aurea só tinha `.prose hr` (preso à prosa) e o
45
- // `ToolbarSeparator` (preso ao Toolbar). Untitled UI precisa dela para a variante de seções da
46
- // lateral, e ela serve em todo lugar.
47
- //
48
- // É `<hr>`, e isso é decisão medida em 18/08/2026. O motor que já usamos ENTREGA um `Separator` —
49
- // mas ele é `'use client'`, usa hook (`useRenderElement`) e o que produz são DOIS atributos:
50
- // `role="separator"` e `aria-orientation`. Importá-lo arrastaria este módulo para o lado cliente,
51
- // que é o defeito da ADR-0026 (o `Card` custou 118,5 KB atravessando essa fronteira). Aqui a regra
52
- // "se o motor entrega, não escrevemos" (BUILDING.md passo 2) PERDE para a fronteira — e o registro
53
- // existe para a próxima sessão não "corrigir" isto de volta.
54
- //
55
- // `<hr>` nativo JÁ TEM `role="separator"` implícito, então escrever o papel seria ARIA redundante —
56
- // e o APG avisa que ARIA a mais é pior que ARIA nenhuma. O papel `separator` também tem orientação
57
- // HORIZONTAL por padrão: por isso `aria-orientation` só aparece quando é vertical.
58
- //
59
- // O que NÃO entrou, pelo passo 5: as três tonalidades do HeroUI (`secondary`/`tertiary`) — a Aurea
60
- // tem um `--border` e inventar escala de tom aqui seria copiar o sistema de token deles. E o
61
- // separador COM RÓTULO no meio ("or", uma data numa conversa), que eles têm em
62
- // `separator__container` + `__line` + `__content`: é outra estrutura (o `<hr>` não carrega texto),
63
- // ninguém pediu ainda, e fica registrado aqui como o próximo degrau se alguém precisar.
64
- // O `Separator` SAIU daqui em 29/08/2026, e a razão é o gate que provou a decisão errada.
65
- // Ele era um <hr> com orientação estática, e o `comportamental.multi-motor.spec.ts` — a prova
66
- // das cinco capacidades que o Victor especificou no G-AXIS-06 — cobra dele `data-orientation`
67
- // e `aria-orientation` RESOLVIDOS por largura de contêiner. Isso é eixo COMPORTAMENTAL: não
68
- // sai de classe CSS, precisa de runtime, e runtime precisa de hook. Um componente que precisa
69
- // de hook é de cliente, e é isso que a ADR-0026 manda — ela separa quem PRECISA do cliente de
70
- // quem só caiu num módulo com a diretiva. Mora no `layout-client.tsx`.
71
- export function Cluster({ className, ...props }) { return _jsx("div", { className: cx("cluster", className), ...props }); }
72
- export function Grid({ className, ...props }) { return _jsx("div", { className: cx("grid", className), ...props }); }
73
51
  // ── Data Display ─────────────────────────────────────────────────────────────────────────────
74
52
  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 })] }); }
75
53
  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)) }); }
@@ -83,12 +61,12 @@ export function Prose({ className, ...props }) { return _jsx("div", { className:
83
61
  // `max` com `+` é o idioma universal do contador (MUI, Ant, Material 3). Exportada porque quem
84
62
  // escreve o nome acessível precisa do MESMO texto — "99+ unread" tem de bater com o que se vê.
85
63
  export function formatBadgeCount(count, max = 99) { return count > max ? `${max}+` : String(count); }
86
- 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 }) {
64
+ export function Badge({ variant = "neutral", emphasis = "soft", size = "md", dot, leading, trailing, image, imageAlt, count, max = 99, showZero, fit = "auto", anchor, anchorShape = "square", invisible, badgeContent, children, className, ...props }) {
87
65
  const numero = count != null ? formatBadgeCount(count, max) : undefined;
88
66
  const miolo = anchor ? (numero ?? badgeContent) : (numero ?? children);
89
67
  // Ponto puro: `dot` sem nada para mostrar. Aí o badge não tem conteúdo, e ganha medida própria.
90
68
  const soPonto = !!dot && miolo == null;
91
- 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] });
69
+ const chip = _jsxs("span", { className: cx("badge", variant !== "neutral" && `badge-${variant}`, emphasis !== "soft" && `badge-${emphasis}`, size !== "md" && `badge-${size}`, fit === "content" && !anchor && "badge-fit", 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] });
92
70
  if (!anchor)
93
71
  return chip;
94
72
  // Some em zero por padrão, como a MUI: caixa de entrada zerada não merece um "0" no canto.
@@ -148,12 +126,15 @@ function ladoDoAdorno(no) {
148
126
  const p = no.props;
149
127
  return no.type === InputGroupAddon ? (p.side ?? "start") : null;
150
128
  }
151
- export function InputGroup({ className, children, ...props }) {
129
+ // A-08 (24/09/2026): `width` tira o grupo dos 100% — `"20rem"` para uma medida, `"auto"` para o
130
+ // tamanho do conteúdo. Sem ela, 100% como sempre. Com ela, o grupo também deixa de crescer numa
131
+ // fila (`flex:none`): o `flex:1` que as barras dão a ele desfaria a medida pedida.
132
+ export function InputGroup({ className, children, width, style, ...props }) {
152
133
  const filhos = React.Children.toArray(children);
153
134
  const inicio = filhos.filter(f => ladoDoAdorno(f) === "start");
154
135
  const fim = filhos.filter(f => ladoDoAdorno(f) === "end");
155
136
  const meio = filhos.filter(f => ladoDoAdorno(f) === null);
156
- return _jsx("div", { className: cx("input-group", className), ...props, children: [...inicio, ...meio, ...fim] });
137
+ return _jsx("div", { className: cx("input-group", className), style: width != null ? { inlineSize: width, flex: "none", ...style } : style, ...props, children: [...inicio, ...meio, ...fim] });
157
138
  }
158
139
  // `layout` é responsivo, e a triagem que o autorizou está no `G-AXIS-07`: com o adorno em linha,
159
140
  // a 200px de caixa sobram 48% da largura para o campo; com ele em faixa, 99%. Não é preferência
@@ -1,12 +1,15 @@
1
1
  import React, { type ReactNode, type ReactElement } from "react";
2
2
  import { type Responsive } from "./pure.js";
3
3
  import { type IconName } from "./system.js";
4
- export declare function Dialog({ open, title, children, footer, onClose }: {
4
+ export type DialogSize = "xs" | "sm" | "md" | "lg" | "xl" | "full";
5
+ export declare function Dialog({ open, title, children, footer, onClose, size, dismissible }: {
5
6
  open: boolean;
6
7
  title: ReactNode;
7
8
  children: ReactNode;
8
9
  footer?: ReactNode;
9
10
  onClose: () => void;
11
+ size?: DialogSize;
12
+ dismissible?: boolean;
10
13
  }): React.JSX.Element;
11
14
  export declare function ConfirmDialog({ open, title, description, confirmLabel, cancelLabel, destructive, onConfirm, onCancel }: {
12
15
  open: boolean;
package/dist/overlays.js CHANGED
@@ -16,8 +16,12 @@ import { cx, useAureaStrings, usePortalContainer } from "./internal.js";
16
16
  import { useValorResponsivo } from "./responsivo-runtime.js";
17
17
  import { Icon } from "./system.js";
18
18
  import { Button } from "./actions.js";
19
- export function Dialog({ open, title, children, footer, onClose }) { const s = useAureaStrings(); const portal = usePortalContainer(); return _jsx(BaseDialog.Root, { open: open, onOpenChange: o => { if (!o)
20
- onClose(); }, children: _jsxs(BaseDialog.Portal, { container: portal, children: [_jsx(BaseDialog.Backdrop, { className: "dialog-backdrop" }), _jsxs(BaseDialog.Popup, { className: "dialog", children: [_jsxs("header", { children: [_jsx(BaseDialog.Title, { render: _jsx("h2", {}), children: title }), _jsx(BaseDialog.Close, { className: "btn btn-ghost btn-icon", "aria-label": s.close, children: _jsx(Icon, { name: "close" }) })] }), _jsx("div", { className: "dialog-body", children: children }), footer && _jsx("footer", { children: footer })] })] }) }); }
19
+ export function Dialog({ open, title, children, footer, onClose, size = "md", dismissible = true }) {
20
+ const s = useAureaStrings();
21
+ const portal = usePortalContainer();
22
+ return _jsx(BaseDialog.Root, { open: open, disablePointerDismissal: !dismissible, onOpenChange: o => { if (!o && dismissible)
23
+ onClose(); }, children: _jsxs(BaseDialog.Portal, { container: portal, children: [_jsx(BaseDialog.Backdrop, { className: "dialog-backdrop" }), _jsxs(BaseDialog.Popup, { className: cx("dialog", size !== "md" && `dialog-${size}`), children: [_jsxs("header", { children: [_jsx(BaseDialog.Title, { render: _jsx("h2", {}), children: title }), _jsx(BaseDialog.Close, { className: "btn btn-ghost btn-icon", "aria-label": s.close, disabled: !dismissible, children: _jsx(Icon, { name: "close" }) })] }), _jsx("div", { className: "dialog-body", children: children }), footer && _jsx("footer", { children: footer })] })] }) });
24
+ }
21
25
  // ConfirmDialog (M5): a decisão que não se fecha por engano. NÃO é um Dialog com dois botões —
22
26
  // é `role="alertdialog"`, e a diferença é de comportamento, não de aparência: o Base UI tira do
23
27
  // AlertDialog.Root as props `modal` e `disablePointerDismissal` (medido no d.ts de 1.6.0), ou
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aurea-uds/react",
3
- "version": "0.8.13",
3
+ "version": "0.9.0",
4
4
  "description": "React components for Aurea UDS. Import @aurea-uds/fonts/css and @aurea-uds/core/css alongside.",
5
5
  "keywords": [
6
6
  "aurea",