@aurea-uds/react 0.5.0 → 0.7.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.
@@ -274,7 +274,7 @@ export function FileInput({ accept, maxSize, multiple, onFilesChange, upload, la
274
274
  const recibo = files.filter(f => f.status === "done" || f.status === "error" || f.status === "canceled");
275
275
  const reciboTexto = recibo.map(r => [r.name, r.status, r.finishedAt ?? ""].join(TAB)).join(NL);
276
276
  return _jsxs("div", { className: cx("field", className), children: [label && _jsx("label", { className: "label", htmlFor: inputId, children: label }), _jsxs("label", { className: "dropzone", "data-dragging": dragging || undefined, onDragOver: e => { e.preventDefault(); setDragging(true); }, onDragLeave: e => { if (!e.currentTarget.contains(e.relatedTarget))
277
- setDragging(false); }, onDrop: e => { e.preventDefault(); setDragging(false); add(e.dataTransfer.files); }, children: [_jsx("input", { id: inputId, type: "file", className: "sr-only", accept: accept, multiple: multiple, onChange: e => { add(e.target.files); e.target.value = ""; } }), _jsx(Icon, { name: "cloud--upload", size: "xl", className: "dropzone-icon" }), _jsx("strong", { children: s.fileDropPrompt }), hint && _jsx("span", { className: "dropzone-hint", children: hint })] }), upload && (enviando || pausados) && _jsx("div", { className: "file-queue-actions", children: enviando ? _jsx(Button, { variant: "secondary", size: "sm", leadingIcon: "pause", onClick: pausarTudo, children: s.uploadPause })
277
+ setDragging(false); }, onDrop: e => { e.preventDefault(); setDragging(false); add(e.dataTransfer.files); }, children: [_jsx("input", { id: inputId, type: "file", className: "sr-only", accept: accept, multiple: multiple, "aria-invalid": rejected.length > 0 || undefined, "aria-describedby": rejected.length > 0 ? rejected.map((_, n) => `${inputId}-rejeitado-${n}`).join(" ") : undefined, onChange: e => { add(e.target.files); e.target.value = ""; } }), _jsx(Icon, { name: "cloud--upload", size: "xl", className: "dropzone-icon" }), _jsx("strong", { children: s.fileDropPrompt }), hint && _jsx("span", { className: "dropzone-hint", children: hint })] }), upload && (enviando || pausados) && _jsx("div", { className: "file-queue-actions", children: enviando ? _jsx(Button, { variant: "secondary", size: "sm", leadingIcon: "pause", onClick: pausarTudo, children: s.uploadPause })
278
278
  : _jsx(Button, { variant: "secondary", size: "sm", leadingIcon: "play", onClick: retomarTudo, children: s.uploadResume }) }), files.length > 0 && _jsx("ul", { className: "file-list", children: files.map(({ id, file, name, bytes, status, progress = 0, restored, checksum: soma, preview: previa, erro }) => {
279
279
  // A barra reusa .progress + .file-progress (largura fixa em classe); só o
280
280
  // preenchimento (width %) fica inline, por ser data-driven. Erro reusa
@@ -283,5 +283,5 @@ export function FileInput({ accept, maxSize, multiple, onFilesChange, upload, la
283
283
  // pelos docs não altera baseline — a justificativa antiga estava invertida).
284
284
  const pct = Math.round(progress * 100);
285
285
  return _jsxs("li", { className: "file-item", children: [previa ? _jsx("img", { className: "file-thumb", src: previa, alt: "" }) : _jsx(Icon, { name: "document" }), _jsx("span", { className: "file-name", children: name }), status === "uploading" ? _jsxs(_Fragment, { children: [_jsx("div", { className: "progress file-progress", role: "progressbar", "aria-label": `${s.uploadSending} ${name}`, "aria-valuemin": 0, "aria-valuemax": 100, "aria-valuenow": pct, children: _jsx("span", { style: { width: `${pct}%` } }) }), _jsx(IconButton, { variant: "ghost", size: "sm", icon: "pause", label: `${s.uploadPause} ${name}`, onClick: () => pausar(id) }), _jsx(IconButton, { variant: "ghost", size: "sm", icon: "close", label: `${s.uploadCancel} ${name}`, onClick: () => abortId(id) })] }) : status === "paused" ? _jsxs(_Fragment, { children: [_jsxs("span", { className: "file-size", children: [s.uploadPaused, " \u00B7 ", Math.round(progress * 100), "%"] }), _jsx(IconButton, { variant: "ghost", size: "sm", icon: "play", label: `${s.uploadResume} ${name}`, onClick: () => retomar({ id, file, name, bytes, status, progress, restored }) }), _jsx(IconButton, { variant: "ghost", size: "sm", icon: "close", label: `${s.fileRemove} ${name}`, onClick: () => remove(id) })] }) : (status === "error" || status === "canceled") ? _jsxs(_Fragment, { children: [_jsx("span", { className: "field-error", children: erro ?? (status === "error" ? s.uploadError : s.uploadCanceled) }), file && _jsx(IconButton, { variant: "ghost", size: "sm", icon: "restart", label: `${s.uploadRetry} ${name}`, onClick: () => startUpload(id, file) }), _jsx(IconButton, { variant: "ghost", size: "sm", icon: "close", label: `${s.fileRemove} ${name}`, onClick: () => remove(id) })] }) : _jsxs(_Fragment, { children: [status === "done" && _jsx(Icon, { name: "checkmark--filled" }), _jsx("span", { className: "file-size", children: formatSize(bytes) }), soma && _jsx("span", { className: "file-checksum", title: `${s.uploadChecksum}: ${soma}`, children: soma.slice(0, 8) }), _jsx(IconButton, { variant: "ghost", size: "sm", icon: "close", label: `${s.fileRemove} ${name}`, onClick: () => remove(id) })] })] }, id);
286
- }) }), conflitos.map((c, i) => _jsxs("div", { className: "file-conflict", role: "alert", children: [_jsxs("span", { children: [s.fileConflict, ": ", _jsx("strong", { children: c.file.name })] }), _jsx(Button, { size: "sm", variant: "secondary", onClick: () => resolverConflito(i, "replace"), children: s.fileReplace }), _jsx(Button, { size: "sm", variant: "secondary", onClick: () => resolverConflito(i, "both"), children: s.fileKeepBoth }), _jsx(Button, { size: "sm", variant: "ghost", onClick: () => resolverConflito(i, "skip"), children: s.fileSkip })] }, `c${i}`)), recibo.length > 0 && !emVoo && _jsxs("div", { className: "file-receipt", children: [_jsxs("div", { className: "file-receipt-head", children: [_jsx("strong", { children: s.uploadReceipt }), _jsx(Button, { size: "sm", variant: "ghost", leadingIcon: "copy", onClick: () => void navigator.clipboard?.writeText(reciboTexto), children: s.uploadReceiptCopy })] }), _jsx("ul", { children: recibo.map(r => _jsxs("li", { children: [r.name, " \u00B7 ", r.status === "done" ? s.uploadComplete : r.status === "canceled" ? s.uploadCanceled : s.uploadError, r.finishedAt ? ` · ${r.finishedAt}` : ""] }, r.id)) })] }), rejected.map((r, n) => _jsxs("span", { className: "field-error", children: [r.reason, ": ", r.file.name] }, n)), _jsx("span", { className: "sr-only", role: "status", "aria-live": "polite", children: announce })] });
286
+ }) }), conflitos.map((c, i) => _jsxs("div", { className: "file-conflict", role: "alert", children: [_jsxs("span", { children: [s.fileConflict, ": ", _jsx("strong", { children: c.file.name })] }), _jsx(Button, { size: "sm", variant: "secondary", onClick: () => resolverConflito(i, "replace"), children: s.fileReplace }), _jsx(Button, { size: "sm", variant: "secondary", onClick: () => resolverConflito(i, "both"), children: s.fileKeepBoth }), _jsx(Button, { size: "sm", variant: "ghost", onClick: () => resolverConflito(i, "skip"), children: s.fileSkip })] }, `c${i}`)), recibo.length > 0 && !emVoo && _jsxs("div", { className: "file-receipt", children: [_jsxs("div", { className: "file-receipt-head", children: [_jsx("strong", { children: s.uploadReceipt }), _jsx(Button, { size: "sm", variant: "ghost", leadingIcon: "copy", onClick: () => void navigator.clipboard?.writeText(reciboTexto), children: s.uploadReceiptCopy })] }), _jsx("ul", { children: recibo.map(r => _jsxs("li", { children: [r.name, " \u00B7 ", r.status === "done" ? s.uploadComplete : r.status === "canceled" ? s.uploadCanceled : s.uploadError, r.finishedAt ? ` · ${r.finishedAt}` : ""] }, r.id)) })] }), rejected.map((r, n) => _jsxs("span", { id: `${inputId}-rejeitado-${n}`, className: "field-error", children: [r.reason, ": ", r.file.name] }, n)), _jsx("span", { className: "sr-only", role: "status", "aria-live": "polite", children: announce })] });
287
287
  }
package/dist/graph.js CHANGED
@@ -110,9 +110,9 @@ function dispor(nodes, edges) {
110
110
  function NoAurea({ data }) {
111
111
  const d = data;
112
112
  const corpo = _jsxs(_Fragment, { children: [_jsx("span", { className: "graph-node-label", children: d.label }), d.kind && _jsx("span", { className: "graph-node-kind", children: d.kind }), d.detail && _jsx("span", { className: "hint", children: d.detail })] });
113
- return _jsxs("div", { className: "graph-node", "data-selected": d.selecionado || undefined, children: [_jsx(Handle, { type: "target", position: Position.Left, isConnectable: !!d.conectavel, className: "graph-handle" }), d.aoSelecionar
113
+ return _jsxs("div", { className: "graph-node", "data-selected": d.selecionado || undefined, children: [d.conectavel && _jsx(Handle, { type: "target", position: Position.Left, isConnectable: true, className: "graph-handle" }), d.aoSelecionar
114
114
  ? _jsx("button", { type: "button", className: "graph-node-body", "aria-pressed": !!d.selecionado, onClick: d.aoSelecionar, children: corpo })
115
- : _jsx("span", { className: "graph-node-body", children: corpo }), _jsx(Handle, { type: "source", position: Position.Right, isConnectable: !!d.conectavel, className: "graph-handle" })] });
115
+ : _jsx("span", { className: "graph-node-body", children: corpo }), d.conectavel && _jsx(Handle, { type: "source", position: Position.Right, isConnectable: true, className: "graph-handle" })] });
116
116
  }
117
117
  const TIPOS = { aurea: NoAurea };
118
118
  // DependencyGraph — quem depende de quem.
@@ -146,8 +146,13 @@ export function DependencyGraph({ nodes, edges, label, selectedId, onSelect, con
146
146
  data: { label: n.label, kind: n.kind, detail: n.detail, selecionado: selectedId === n.id,
147
147
  conectavel: !!connectable, aoSelecionar: onSelect ? () => onSelect(n.id) : undefined },
148
148
  })), [nodes, automatico, selectedId, onSelect, connectable, onNodeMove]);
149
+ // `selectable:false` na PRÓPRIA aresta, e não só no `elementsSelectable` do `<ReactFlow>`:
150
+ // medido em 29/08/2026, a prop do fluxo não tira a classe `.selectable` da aresta, que é onde
151
+ // o `base.css` do motor põe `cursor:pointer`. A mão de ponteiro prometia um clique que a API
152
+ // da Aurea não atende — `onSelect` é do NÓ, e quem o atende é o <button> do corpo dele.
149
153
  const arestasDoMotor = React.useMemo(() => edges.map(e => ({
150
154
  id: `${e.from}->${e.to}`, source: e.from, target: e.to, label: e.label, animated: e.animated,
155
+ selectable: false,
151
156
  })), [edges]);
152
157
  // ── A MEDIDA TEM DE PODER VOLTAR, e isto foi achado NO NAVEGADOR ───────────────────────
153
158
  // A primeira versão passava `nodes` e nenhum `onNodesChange`. O motor aceita calado e o
@@ -173,6 +178,6 @@ export function DependencyGraph({ nodes, edges, label, selectedId, onSelect, con
173
178
  // que o `fitView` usa ENQUANTO ninguém mediu. Com o prefixo `initial`, o navegador sobrescreve
174
179
  // na primeira medição — passar `width`/`height` direto no ReactFlow fixaria o tamanho e mataria
175
180
  // a fluidez. O valor é a extensão do próprio layout, então no servidor o grafo cabe exato.
176
- return _jsx("div", { className: cx("dependency-graph", className), style: height ? { blockSize: height } : undefined, role: "group", "aria-label": label ?? s.graphLabel, ...props, children: _jsx(ReactFlowProvider, { initialNodes: nosDoMotor, initialEdges: arestasDoMotor, fitView: true, initialWidth: automatico.extensao.larg, initialHeight: automatico.extensao.alt, children: _jsx(ReactFlow, { nodes: nosVivos, onNodesChange: aoMudarNos, edges: arestasDoMotor, nodeTypes: TIPOS, fitView: true, proOptions: { hideAttribution: false }, nodesDraggable: !!onNodeMove, nodesConnectable: !!connectable, elementsSelectable: !!onSelect, onNodeDragStop: onNodeMove ? (_, no) => onNodeMove(no.id, no.position) : undefined, onConnect: onConnect ? c => { if (c.source && c.target)
181
+ return _jsx("div", { className: cx("dependency-graph", className), style: height ? { blockSize: height } : undefined, role: "group", "aria-label": label ?? s.graphLabel, ...props, children: _jsx(ReactFlowProvider, { initialNodes: nosDoMotor, initialEdges: arestasDoMotor, fitView: true, initialWidth: automatico.extensao.larg, initialHeight: automatico.extensao.alt, children: _jsx(ReactFlow, { nodes: nosVivos, onNodesChange: aoMudarNos, edges: arestasDoMotor, nodeTypes: TIPOS, fitView: true, proOptions: { hideAttribution: false }, nodesDraggable: !!onNodeMove, nodesConnectable: !!connectable, elementsSelectable: false, onNodeDragStop: onNodeMove ? (_, no) => onNodeMove(no.id, no.position) : undefined, onConnect: onConnect ? c => { if (c.source && c.target)
177
182
  onConnect({ from: c.source, to: c.target }); } : undefined, children: _jsx(Background, {}) }) }) });
178
183
  }
@@ -1,9 +1,10 @@
1
+ import { type Responsive } from "./pure.js";
1
2
  import React, { type ReactNode } from "react";
2
3
  import type { AvatarSize } from "./markup.js";
3
4
  export declare function Avatar({ src, alt, fallback, size, className }: {
4
5
  src?: string;
5
6
  alt?: string;
6
7
  fallback?: ReactNode;
7
- size?: AvatarSize;
8
+ size?: Responsive<AvatarSize>;
8
9
  className?: string;
9
10
  }): React.JSX.Element;
@@ -2,11 +2,15 @@
2
2
  import { jsx as _jsx } from "react/jsx-runtime";
3
3
  // Fase 9 (achado A5): este arquivo saiu do index.tsx de 970 linhas. Um módulo por categoria
4
4
  // do registry — a taxonomia já existia e é gateada. A ordem de import entre eles é um DAG:
5
+ import { peleDoEixo } from "./pure.js";
5
6
  // internal → system → actions → feedback → inputs → navigation → layout → data-display → resto.
6
7
  import React from "react";
7
8
  import { cx } from "./internal.js";
8
- export function Avatar({ src, alt = "", fallback, size = "md", className }) {
9
+ // O DEGRAU RESPONSIVO volta (merge de 28/08/2026): a ficha declara `responsive.size` e o
10
+ // componente tinha perdido a capacidade quando este arquivo entrou da `main`. Mesma perda
11
+ // silenciosa do `Input`, do `Tabs` e do `NumberField` — a ficha documentava, o código não fazia.
12
+ export function Avatar({ src, alt = "", fallback, size, className }) {
9
13
  const [falhou, setFalhou] = React.useState(false);
10
14
  React.useEffect(() => { setFalhou(false); }, [src]);
11
- return _jsx("span", { className: cx("avatar", size !== "md" && `avatar-${size}`, className), children: src && !falhou ? _jsx("img", { src: src, alt: alt, onError: () => setFalhou(true) }) : fallback });
15
+ return _jsx("span", { className: cx("avatar", peleDoEixo("avatar", size), className), children: src && !falhou ? _jsx("img", { src: src, alt: alt, onError: () => setFalhou(true) }) : fallback });
12
16
  }
package/dist/index.d.ts CHANGED
@@ -1,6 +1,8 @@
1
- export { cx, defaultStrings, ptBR, defaultSpriteUrl, gridStateToParams, gridStateFromParams, screenStateToParams, screenStateFromParams, universalStates, stateSeverity, type AureaStrings, type GridState, type ScreenState, type UniversalState } from "./pure.js";
2
- export { useAureaStrings, useSpriteUrl } from "./internal.js";
3
- export { Card, Stack, Cluster, Grid, Separator, KPI, DataList, Timeline, Prose, Badge, Progress, Skeleton, AvatarGroup, LogStream, MediaPlayerShell, Topbar, Kbd, Select, Textarea, Checkbox, Radio, Switch, Range, formatBadgeCount, type BadgeVariant, type BadgeEmphasis, type BadgeSize, type BadgePlacement, type BadgeProps, type AvatarSize, type TopbarVariant } from "./markup.js";
1
+ export { cx, defaultStrings, ptBR, defaultSpriteUrl, gridStateToParams, gridStateFromParams, screenStateToParams, screenStateFromParams, universalStates, stateSeverity, type AureaStrings, type AureaTheme, type AureaDensity, type GridState, type ScreenState, type UniversalState } from "./pure.js";
2
+ export { useAureaStrings, useSpriteUrl, useTheme, useDensity } from "./internal.js";
3
+ export { type Responsive, type Breakpoint, type ContainerBreakpoint, type Orientation, ESCALA, ESCALA_CONTAINER } from "./pure.js";
4
+ export { ContainerScope, useValorResponsivo } from "./responsivo-runtime.js";
5
+ export { AspectRatio, InputGroup, InputGroupAddon, Label, Card, Stack, Cluster, Grid, KPI, DataList, Timeline, Prose, Badge, Progress, Skeleton, AvatarGroup, LogStream, MediaPlayerShell, Topbar, Kbd, Select, Textarea, Checkbox, Radio, Switch, Range, formatBadgeCount, type BadgeVariant, type BadgeEmphasis, type BadgeSize, type BadgePlacement, type BadgeProps, type AvatarSize, type TopbarVariant } from "./markup.js";
4
6
  export * from "./system.js";
5
7
  export * from "./actions.js";
6
8
  export * from "./feedback.js";
package/dist/index.js CHANGED
@@ -29,7 +29,16 @@
29
29
  // subpath tem a diretiva. Vindo daqui — via `pure.js`, que não tem — a função é
30
30
  // chamável dos dois lados. É a mesma razão do `cx`, uma camada acima.
31
31
  export { cx, defaultStrings, ptBR, defaultSpriteUrl, gridStateToParams, gridStateFromParams, screenStateToParams, screenStateFromParams, universalStates, stateSeverity } from "./pure.js";
32
- export { useAureaStrings, useSpriteUrl } from "./internal.js";
32
+ export { useAureaStrings, useSpriteUrl, useTheme, useDensity } from "./internal.js";
33
+ // O EIXO RESPONSIVO (G-AXIS-04/06) — os tipos e a escala saem do `pure.js`, que não tem a
34
+ // diretiva, pela mesma razão do `gridStateToParams` logo acima: resolver um valor responsivo é
35
+ // computação de string, e o servidor precisa dela para emitir a classe certa no HTML.
36
+ export { ESCALA, ESCALA_CONTAINER } from "./pure.js";
37
+ // O contêiner Aurea como PRIMITIVE, e o resolvedor genérico. `ContainerScope` é público porque
38
+ // quem monta o layout precisa declarar o contêiner; `useValorResponsivo` é público porque o
39
+ // eixo comportamental é reutilizável fora dos cinco componentes que o estrearam. Estes DOIS têm
40
+ // a diretiva de propósito: observam o documento.
41
+ export { ContainerScope, useValorResponsivo } from "./responsivo-runtime.js";
33
42
  // ── A LINHA DO ITEM O1, e ela é o item inteiro ───────────────────────────────────────────────
34
43
  // Estes 22 vêm do `markup.js`, que NÃO tem a diretiva, e por isso chegam como componentes de
35
44
  // SERVIDOR. Vinham dos módulos de categoria abaixo, e de lá chegavam como cliente: `Card` é uma
@@ -42,7 +51,7 @@ export { useAureaStrings, useSpriteUrl } from "./internal.js";
42
51
  // inteiro). O que muda é de ONDE ele vem — do módulo com a diretiva —, e aí ele volta a chegar
43
52
  // como referência de CLIENTE e os 118,5 KB voltam com ele. Não é um erro: é o ganho do item O1
44
53
  // se desfazendo em silêncio. É isso, exatamente isso, que o check 35 pega.
45
- export { Card, Stack, Cluster, Grid, Separator, KPI, DataList, Timeline, Prose, Badge, Progress, Skeleton, AvatarGroup, LogStream, MediaPlayerShell, Topbar, Kbd, Select, Textarea, Checkbox, Radio, Switch, Range, formatBadgeCount } from "./markup.js";
54
+ export { AspectRatio, InputGroup, InputGroupAddon, Label, Card, Stack, Cluster, Grid, KPI, DataList, Timeline, Prose, Badge, Progress, Skeleton, AvatarGroup, LogStream, MediaPlayerShell, Topbar, Kbd, Select, Textarea, Checkbox, Radio, Switch, Range, formatBadgeCount } from "./markup.js";
46
55
  export * from "./system.js";
47
56
  export * from "./actions.js";
48
57
  export * from "./feedback.js";
@@ -1,25 +1,32 @@
1
1
  import React, { type HTMLAttributes, type InputHTMLAttributes, type RefAttributes, type ReactNode } from "react";
2
+ import { type Responsive } from "./pure.js";
3
+ import { type FieldSize } from "./markup.js";
2
4
  import { type IconName } from "./system.js";
3
5
  export { FileInput, matchesAccept, type FileRejection, type UploadContext, type UploadFn } from "./file-input.js";
6
+ export type FieldOrientation = "vertical" | "horizontal";
4
7
  export interface FieldProps extends HTMLAttributes<HTMLDivElement>, RefAttributes<HTMLDivElement> {
5
8
  label: string;
6
9
  hint?: ReactNode;
7
10
  error?: ReactNode;
8
11
  name?: string;
12
+ orientation?: Responsive<FieldOrientation>;
9
13
  }
10
- export declare function Field({ label, hint, error, children, className, id, name, ...props }: FieldProps): React.JSX.Element;
14
+ export declare function Field({ label, hint, error, children, className, id, name, orientation, ...props }: FieldProps): React.JSX.Element;
11
15
  export declare function Form({ errors, onSubmit, children, className, ...props }: Omit<React.FormHTMLAttributes<HTMLFormElement>, "onSubmit"> & RefAttributes<HTMLFormElement> & {
12
16
  errors?: Record<string, string | string[]>;
13
17
  onSubmit?: (values: Record<string, unknown>) => void;
14
18
  }): React.JSX.Element;
15
- export interface InputProps extends InputHTMLAttributes<HTMLInputElement>, RefAttributes<HTMLInputElement> {
19
+ export interface InputProps extends Omit<InputHTMLAttributes<HTMLInputElement>, "size">, RefAttributes<HTMLInputElement> {
16
20
  formatOnBlur?: (value: string) => string;
21
+ size?: Responsive<FieldSize>;
17
22
  }
18
23
  export declare const Input: React.ForwardRefExoticComponent<Omit<InputProps, "ref"> & RefAttributes<HTMLInputElement>>;
19
- export declare function SearchField({ icon, className, ...props }: InputHTMLAttributes<HTMLInputElement> & RefAttributes<HTMLInputElement> & {
24
+ export interface SearchFieldProps extends Omit<InputHTMLAttributes<HTMLInputElement>, "size">, RefAttributes<HTMLInputElement> {
20
25
  icon?: IconName;
21
- }): React.JSX.Element;
22
- export declare function NumberField({ value, defaultValue, onValueChange, min, max, step, disabled, readOnly, required, label, id, name, format, locale, className }: {
26
+ size?: Responsive<FieldSize>;
27
+ }
28
+ export declare function SearchField({ icon, className, size, ...props }: SearchFieldProps): React.JSX.Element;
29
+ export declare function NumberField({ value, defaultValue, onValueChange, min, max, step, disabled, readOnly, required, label, id, name, format, locale, className, scrubbable, scrubDirection }: {
23
30
  value?: number | null;
24
31
  defaultValue?: number;
25
32
  onValueChange?: (v: number | null) => void;
@@ -35,6 +42,8 @@ export declare function NumberField({ value, defaultValue, onValueChange, min, m
35
42
  format?: Intl.NumberFormatOptions;
36
43
  locale?: string;
37
44
  className?: string;
45
+ scrubbable?: boolean;
46
+ scrubDirection?: "horizontal" | "vertical";
38
47
  }): React.JSX.Element;
39
48
  export declare function OTPField({ length, value, defaultValue, onValueChange, mask, disabled, required, label, id, className }: {
40
49
  length: number;
@@ -61,24 +70,27 @@ export interface ComboboxOption {
61
70
  value: string;
62
71
  label: string;
63
72
  }
64
- export declare function Combobox({ items, value, onValueChange, placeholder, label, disabled, id, className, "aria-describedby": descrito, "aria-invalid": invalido }: {
73
+ export interface ComboboxOptGroup {
74
+ label: string;
65
75
  items: ComboboxOption[];
76
+ }
77
+ export declare function Combobox({ items, value, onValueChange, placeholder, label, empty, disabled, size, id, className, "aria-describedby": descrito, "aria-invalid": invalido }: {
78
+ items: ComboboxOption[] | ComboboxOptGroup[];
79
+ empty?: ReactNode;
66
80
  value?: ComboboxOption | null;
67
81
  onValueChange?: (v: ComboboxOption | null) => void;
68
82
  placeholder?: string;
69
83
  label?: ReactNode;
70
84
  disabled?: boolean;
85
+ size?: Responsive<FieldSize>;
71
86
  id?: string;
72
87
  className?: string;
73
88
  "aria-describedby"?: string;
74
89
  "aria-invalid"?: boolean | "true" | "false";
75
90
  }): React.JSX.Element;
76
- export interface ComboboxOptGroup {
77
- label: string;
78
- items: ComboboxOption[];
79
- }
80
- export declare function MultiCombobox({ items, value, onValueChange, onInputChange, loading, placeholder, label, disabled, id, className, "aria-describedby": descrito, "aria-invalid": invalido }: {
91
+ export declare function MultiCombobox({ items, value, onValueChange, onInputChange, loading, placeholder, label, empty, disabled, id, className, "aria-describedby": descrito, "aria-invalid": invalido }: {
81
92
  items: ComboboxOption[] | ComboboxOptGroup[];
93
+ empty?: ReactNode;
82
94
  value?: ComboboxOption[];
83
95
  onValueChange?: (v: ComboboxOption[]) => void;
84
96
  onInputChange?: (query: string) => void;
@@ -103,3 +115,8 @@ export interface BlockEditorProps extends Omit<HTMLAttributes<HTMLOListElement>,
103
115
  label?: string;
104
116
  }
105
117
  export declare function BlockEditor({ blocks, onReorder, onRemove, label, className, ...props }: BlockEditorProps): React.JSX.Element;
118
+ export interface PasswordFieldProps extends Omit<InputHTMLAttributes<HTMLInputElement>, "size" | "type">, RefAttributes<HTMLInputElement> {
119
+ size?: Responsive<FieldSize>;
120
+ defaultVisible?: boolean;
121
+ }
122
+ export declare function PasswordField({ className, size, defaultVisible, id, ...props }: PasswordFieldProps): React.JSX.Element;
@@ -4,6 +4,7 @@ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-run
4
4
  // do registry — a taxonomia já existia e é gateada. A ordem de import entre eles é um DAG:
5
5
  // internal → system → actions → feedback → inputs → navigation → layout → data-display → resto.
6
6
  import React, { forwardRef } from "react";
7
+ import { valorBase, peleDoEixo, soOValor } from "./pure.js";
7
8
  import { Combobox as BaseCombobox } from "@base-ui/react/combobox";
8
9
  import { Form as BaseForm } from "@base-ui/react/form";
9
10
  import { Field as BaseField } from "@base-ui/react/field";
@@ -16,7 +17,7 @@ import { cx, useAureaStrings, usePortalContainer, useReorder } from "./internal.
16
17
  // diretiva — são marcação, e quem escuta `onChange` é o consumidor. Voltam IMPORTADOS e não só
17
18
  // reexportados porque o `ehControle` do `Field` compara por IDENTIDADE de referência: dois
18
19
  // objetos diferentes com o mesmo nome quebrariam o rótulo do campo em silêncio.
19
- import { Select, Textarea, Checkbox, Radio, Switch, Range } from "./markup.js";
20
+ import { Select, Textarea, Checkbox, Radio, Switch, Range, InputGroup, InputGroupAddon } from "./markup.js";
20
21
  import { Icon } from "./system.js";
21
22
  import { IconButton } from "./actions.js";
22
23
  // FileInput mora em arquivo próprio (132 linhas — upload real, aborto, progresso) e é público
@@ -71,9 +72,13 @@ function elementoRotulavel(tipo) {
71
72
  return TAGS_ROTULAVEIS.has(tipo);
72
73
  // Componente React arbitrário pode consumir as props no wrapper e nunca entregá-las ao nó
73
74
  // focal. Só os controles Aurea cujo encaminhamento é parte do contrato entram aqui.
74
- return [Input, Select, Textarea, SearchField, Checkbox, Radio, Switch, Range, Combobox, MultiCombobox].includes(tipo);
75
+ // `PasswordField` entrou em 29/08/2026, e entrou medido: `<Field label="Senha"><PasswordField/></Field>`
76
+ // reprovava no axe com "Form elements must have labels". Fora desta lista o Field vira GRUPO e
77
+ // não pendura `htmlFor`, então o <input> de dentro ficava sem nome. Ele qualifica pela mesma
78
+ // regra que os outros: recebe `id` e espalha `...props` no `Input`, que é o nó focal.
79
+ return [Input, Select, Textarea, SearchField, PasswordField, Checkbox, Radio, Switch, Range, Combobox, MultiCombobox].includes(tipo);
75
80
  }
76
- export function Field({ label, hint, error, children, className, id, name, ...props }) {
81
+ export function Field({ label, hint, error, children, className, id, name, orientation, ...props }) {
77
82
  const auto = React.useId();
78
83
  // Fragment não cria nó no DOM. Um Fragment com um só controle é só sintaxe; com vários,
79
84
  // Field precisa voltar para grupo em vez de pendurar `htmlFor` num id que nunca existirá.
@@ -117,7 +122,7 @@ export function Field({ label, hint, error, children, className, id, name, ...pr
117
122
  // embrulho o `Field.Root` recebe o erro e não tem a quem entregá-lo, e o campo fica válido na
118
123
  // árvore de acessibilidade enquanto a frase de erro aparece na tela. Pior que não mostrar.
119
124
  const controleFinal = name ? _jsx(BaseField.Control, { render: controle }) : controle;
120
- const corpo = _jsxs("div", { className: cx("field", className), ...(nomeAqui ? {} : { role: "group", "aria-labelledby": idRotulo, ...(rotulavel ? {} : { "aria-describedby": descrito }) }), ...props, children: [_jsxs("span", { className: "label", children: [nomeAqui ? _jsx("label", { htmlFor: idControle, children: label }) : _jsx("span", { id: idRotulo, children: label }), hint && _jsx("span", { className: "hint", id: idHint, children: hint })] }), controleFinal, error && _jsx("span", { className: "field-error", id: idErro, children: error }), name && _jsx(BaseField.Error, { className: "field-error" })] });
125
+ const corpo = _jsxs("div", { className: cx("field", peleDoEixo("field", orientation, "vertical", "field"), className), ...(nomeAqui ? {} : { role: "group", "aria-labelledby": idRotulo, ...(rotulavel ? {} : { "aria-describedby": descrito }) }), ...props, children: [_jsxs("span", { className: "label", children: [nomeAqui ? _jsx("label", { htmlFor: idControle, children: label }) : _jsx("span", { id: idRotulo, children: label }), hint && _jsx("span", { className: "hint", id: idHint, children: hint })] }), controleFinal, error && _jsx("span", { className: "field-error", id: idErro, children: error }), name && _jsx(BaseField.Error, { className: "field-error" })] });
121
126
  // `Field.Root` do motor entra SÓ quando há `name`: é ele que liga o campo à chave de `errors`
122
127
  // do formulário. Ele não desenha nada — é contexto — então a pele não muda.
123
128
  return name ? _jsx(BaseField.Root, { name: name, invalid: error ? true : undefined, render: corpo }) : corpo;
@@ -138,8 +143,8 @@ export function Field({ label, hint, error, children, className, id, name, ...pr
138
143
  export function Form({ errors, onSubmit, children, className, ...props }) {
139
144
  return _jsx(BaseForm, { errors: errors, onFormSubmit: onSubmit ? (v) => onSubmit(v) : undefined, className: cx("form", className), ...props, children: children });
140
145
  }
141
- export const Input = forwardRef(function Input({ className, formatOnBlur, onBlur, onChange, ...props }, ref) {
142
- return _jsx("input", { ref: ref, className: cx("input", className), onChange: onChange, onBlur: e => {
146
+ export const Input = forwardRef(function Input({ className, formatOnBlur, size, onBlur, onChange, ...props }, ref) {
147
+ return _jsx("input", { ref: ref, className: cx("input", peleDoEixo("input", size), className), onChange: onChange, onBlur: e => {
143
148
  if (formatOnBlur) {
144
149
  const formatado = formatOnBlur(e.currentTarget.value);
145
150
  if (formatado !== e.currentTarget.value) {
@@ -150,7 +155,12 @@ export const Input = forwardRef(function Input({ className, formatOnBlur, onBlur
150
155
  onBlur?.(e);
151
156
  }, ...props });
152
157
  });
153
- export function SearchField({ icon = "search", className, ...props }) { return _jsxs("div", { className: cx("input-wrap", className), children: [_jsx(Icon, { name: icon }), _jsx(Input, { type: "search", ...props })] }); }
158
+ // O glifo entra por `InputGroup`/`InputGroupAddon`, e não pelo `.input-wrap`: o mesmo problema
159
+ // tinha sido resolvido DUAS vezes localmente aqui — `.input-wrap` para o glifo do SearchField e
160
+ // `.input-wrap-end` para o botão do PasswordField —, e o grupo é a terceira vez virando uma. O
161
+ // merge das duas linhagens tinha trazido de volta a versão com `.input-wrap`, contra um core
162
+ // que já não a pinta.
163
+ 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 })] }); }
154
164
  // labelHidden: coluna de seleção de tabela precisa do rótulo POR LINHA para o leitor de
155
165
  // tela ("Select Analyst"), mas mostrá-lo engorda a coluna. O rótulo continua no DOM, só
156
166
  // sai da tela (.sr-only) — nunca trocar por aria-label solto num <label> visível vazio.
@@ -171,9 +181,9 @@ export function SearchField({ icon = "search", className, ...props }) { return _
171
181
  // plataforma; a Aurea não escreve formatador de dinheiro, e nem deveria.
172
182
  // `name` entra junto porque é ele que faz o motor renderizar o input escondido com o valor CRU —
173
183
  // é a trava do item ("o valor mascarado continua no DOM") entregue pelo motor, e o teste a cobra.
174
- export function NumberField({ value, defaultValue, onValueChange, min, max, step, disabled, readOnly, required, label, id, name, format, locale, className }) {
184
+ export function NumberField({ value, defaultValue, onValueChange, min, max, step, disabled, readOnly, required, label, id, name, format, locale, className, scrubbable, scrubDirection }) {
175
185
  const s = useAureaStrings();
176
- return _jsx(BaseNumberField.Root, { id: id, name: name, value: value, defaultValue: defaultValue, onValueChange: onValueChange, min: min, max: max, step: step, format: format, locale: locale, disabled: disabled, readOnly: readOnly, required: required, className: cx("number-field", className), children: _jsxs(BaseNumberField.Group, { className: "number-field-group", children: [_jsx(BaseNumberField.Decrement, { className: "btn btn-ghost btn-icon", "aria-label": s.decrement, children: _jsx(Icon, { name: "subtract" }) }), _jsx(BaseNumberField.Input, { className: "input number-field-input", "aria-label": label }), _jsx(BaseNumberField.Increment, { className: "btn btn-ghost btn-icon", "aria-label": s.increment, children: _jsx(Icon, { name: "add" }) })] }) });
186
+ return _jsx(BaseNumberField.Root, { id: id, name: name, value: value, defaultValue: defaultValue, onValueChange: soOValor(onValueChange), min: min, max: max, step: step, format: format, locale: locale, disabled: disabled, readOnly: readOnly, required: required, className: cx("number-field", className), children: _jsxs(BaseNumberField.Group, { className: "number-field-group", children: [scrubbable && _jsxs(BaseNumberField.ScrubArea, { className: cx("number-field-scrub", scrubDirection === "vertical" && "number-field-scrub-vertical"), direction: scrubDirection, "aria-hidden": "true", children: [_jsx(Icon, { name: "draggable", "aria-hidden": true }), _jsx(BaseNumberField.ScrubAreaCursor, { className: "number-field-scrub-cursor", children: _jsx(Icon, { name: "draggable", "aria-hidden": true }) })] }), _jsx(BaseNumberField.Decrement, { className: "btn btn-ghost btn-icon", "aria-label": s.decrement, children: _jsx(Icon, { name: "subtract" }) }), _jsx(BaseNumberField.Input, { className: "input number-field-input", "aria-label": label }), _jsx(BaseNumberField.Increment, { className: "btn btn-ghost btn-icon", "aria-label": s.increment, children: _jsx(Icon, { name: "add" }) })] }) });
177
187
  }
178
188
  // OTPField (Lote 1 do BUILDING.md). Um campo por dígito, com o comportamento que ninguém acerta
179
189
  // à mão: colar o código inteiro distribui pelos campos, Backspace volta um, e o
@@ -188,7 +198,7 @@ export function NumberField({ value, defaultValue, onValueChange, min, max, step
188
198
  // o código; o nome de cada caixa é o que diz onde você está.
189
199
  export function OTPField({ length, value, defaultValue, onValueChange, mask, disabled, required, label, id, className }) {
190
200
  const s = useAureaStrings();
191
- return _jsx(BaseOTPField.Root, { id: id, length: length, value: value, defaultValue: defaultValue, onValueChange: onValueChange, mask: mask, disabled: disabled, required: required, role: "group", "aria-label": label, className: cx("otp-field", className), children: Array.from({ length }, (_, i) => _jsx(BaseOTPField.Input, { render: _jsx("input", { "aria-label": `${s.otpDigit} ${i + 1}` }), className: "input otp-slot" }, i)) });
201
+ return _jsx(BaseOTPField.Root, { id: id, length: length, value: value, defaultValue: defaultValue, onValueChange: soOValor(onValueChange), mask: mask, disabled: disabled, required: required, role: "group", "aria-label": label, className: cx("otp-field", className), children: Array.from({ length }, (_, i) => _jsx(BaseOTPField.Input, { render: _jsx("input", { "aria-label": `${s.otpDigit} ${i + 1}` }), className: "input otp-slot" }, i)) });
192
202
  }
193
203
  // SegmentedControl — achado M20, fechado na Parte C do PLANO-1.0 em 07/08/2026.
194
204
  //
@@ -223,6 +233,13 @@ export function SegmentedControl({ items, value, onChange, label }) {
223
233
  const s = useAureaStrings();
224
234
  return _jsx(BaseRadioGroup, { className: "segmented", "aria-label": label ?? s.optionsLabel, value: value, onValueChange: v => onChange(String(v)), children: items.map(i => _jsx(BaseRadio.Root, { value: i.value, className: i.value === value ? "active" : undefined, nativeButton: true, render: _jsx("button", { type: "button" }), children: i.label }, i.value)) });
225
235
  }
236
+ const ehAgrupado = (items) => items != null && items.length > 0 && typeof items[0] === "object" && items[0] != null && "items" in items[0];
237
+ // UM renderizador e UMA lista para os dois combobox: duas cópias da mesma árvore é como a
238
+ // segunda fica para trás na mudança seguinte.
239
+ const renderComboboxItem = (item) => _jsxs(BaseCombobox.Item, { value: item, className: "menu-item combobox-item", children: [_jsx(BaseCombobox.ItemIndicator, { className: "combobox-check", children: _jsx(Icon, { name: "checkmark", size: "sm" }) }), _jsx("span", { children: item.label })] }, item.value);
240
+ const listaCombobox = (items) => _jsx(BaseCombobox.List, { children: ehAgrupado(items)
241
+ ? (group) => _jsxs(BaseCombobox.Group, { items: group.items, className: "combobox-section", children: [_jsx(BaseCombobox.GroupLabel, { className: "combobox-group-label", children: group.label }), _jsx(BaseCombobox.Collection, { children: renderComboboxItem })] }, group.label)
242
+ : renderComboboxItem });
226
243
  // `aria-describedby` e `aria-invalid` são explícitos e não caem em `...props` por acidente: este
227
244
  // componente destrincha uma lista fixa, então o que não está aqui é DESCARTADO em silêncio — e era
228
245
  // exatamente isso que fazia o `Field` não alcançar o input do Combobox (AUD-0001, 12/08/2026).
@@ -230,24 +247,28 @@ export function SegmentedControl({ items, value, onChange, label }) {
230
247
  // `disabled` vai na ROOT, não no Input: `Input`, `Trigger`, `Clear` e `ChipRemove` leem todos o
231
248
  // mesmo `selectors.disabled` do store do Base UI (@base-ui/react 1.6.0). Desabilitar o Input
232
249
  // deixaria os dois botões vivos — abrir a lista de um campo desabilitado é o defeito, não o texto.
233
- export function Combobox({ items, value, onValueChange, placeholder, label, disabled, id, className, "aria-describedby": descrito, "aria-invalid": invalido }) {
250
+ export function Combobox({ items, value, onValueChange, placeholder, label, empty, disabled, size, id, className, "aria-describedby": descrito, "aria-invalid": invalido }) {
234
251
  const s = useAureaStrings();
235
252
  const portal = usePortalContainer();
236
253
  const autoId = React.useId();
237
254
  const inputId = id ?? autoId;
238
- return _jsxs(BaseCombobox.Root, { items: items, value: value, onValueChange: 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: "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: s.comboboxEmpty }), _jsx(BaseCombobox.List, { children: (item) => _jsxs(BaseCombobox.Item, { value: item, className: "menu-item combobox-item", children: [_jsx(BaseCombobox.ItemIndicator, { className: "combobox-check", children: _jsx(Icon, { name: "checkmark", size: "sm" }) }), _jsx("span", { children: item.label })] }, item.value) })] }) }) })] });
255
+ 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)] }) }) })] });
239
256
  }
240
- const isGrouped = (items) => items.length > 0 && "items" in items[0];
241
- export function MultiCombobox({ items, value, onValueChange, onInputChange, loading, placeholder, label, disabled, id, className, "aria-describedby": descrito, "aria-invalid": invalido }) {
257
+ // MultiCombobox (Fase 4): multi-seleção com chips, opções agrupadas e filtro
258
+ // externo (async). Reusa .combobox-popup/-item/-empty do básico.
259
+ // - Agrupar: passar ComboboxOptGroup[] em items (label + items). Detecção pela
260
+ // presença de .items no 1º elemento — não misturar plano com agrupado.
261
+ // - Async: passar onInputChange; o filtro interno do Base UI desliga
262
+ // (filter={null}) e o consumidor troca os items conforme a busca retorna.
263
+ // loading só troca o texto do vazio para não piscar "Nenhum resultado".
264
+ export function MultiCombobox({ items, value, onValueChange, onInputChange, loading, placeholder, label, empty, disabled, id, className, "aria-describedby": descrito, "aria-invalid": invalido }) {
242
265
  const s = useAureaStrings();
243
266
  const portal = usePortalContainer();
244
267
  const autoId = React.useId();
245
268
  const inputId = id ?? autoId;
246
269
  const renderItem = (item) => _jsxs(BaseCombobox.Item, { value: item, className: "menu-item combobox-item", children: [_jsx(BaseCombobox.ItemIndicator, { className: "combobox-check", children: _jsx(Icon, { name: "checkmark", size: "sm" }) }), _jsx("span", { children: item.label })] }, item.value);
247
- return _jsxs(BaseCombobox.Root, { multiple: true, items: items, value: value, onValueChange: onValueChange, disabled: disabled, itemToStringLabel: (i) => i.label, filter: onInputChange ? null : undefined, onInputValueChange: onInputChange ? ((v, d) => { if (d.reason !== "item-press")
248
- 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 : s.comboboxEmpty }), _jsx(BaseCombobox.List, { children: isGrouped(items)
249
- ? (group) => _jsxs(BaseCombobox.Group, { items: group.items, className: "combobox-section", children: [_jsx(BaseCombobox.GroupLabel, { className: "combobox-group-label", children: group.label }), _jsx(BaseCombobox.Collection, { children: renderItem })] }, group.label)
250
- : renderItem })] }) }) })] });
270
+ 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")
271
+ 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)] }) }) })] });
251
272
  }
252
273
  export function BlockEditor({ blocks, onReorder, onRemove, label, className, ...props }) {
253
274
  const s = useAureaStrings();
@@ -262,3 +283,10 @@ export function BlockEditor({ blocks, onReorder, onRemove, label, className, ...
262
283
  return _jsxs("li", { className: "block-item", "data-grabbed": r.pego === i || undefined, children: [_jsxs("div", { className: "block-rail", children: [_jsxs("button", { type: "button", id: hid, className: "block-handle", "aria-labelledby": `${hid} ${kid}`, "aria-describedby": `${bid}ajuda`, "aria-pressed": r.pego === i, onKeyDown: e => r.teclado(e, i), onPointerDown: e => r.ponteiroBaixo(e, i), onPointerMove: r.ponteiroMove, onPointerUp: r.ponteiroSolta, onPointerCancel: r.ponteiroSolta, children: [_jsx(Icon, { name: "drag--horizontal" }), _jsx("span", { className: "sr-only", children: s.sortableHandle })] }), onRemove && _jsxs("button", { type: "button", id: rid, className: "block-remove", "aria-labelledby": `${rid} ${kid}`, onClick: () => onRemove(i), children: [_jsx(Icon, { name: "trash-can" }), _jsx("span", { className: "sr-only", children: s.blockRemove })] })] }), _jsx("div", { className: "block-body", children: b.children }), _jsxs("span", { id: kid, className: "sr-only", children: [b.kind ?? s.blockLabel, " ", i + 1, " ", s.positionOf, " ", n] })] }, b.id);
263
284
  }) }), _jsx("span", { id: `${bid}ajuda`, className: "sr-only", children: s.sortableHelp }), _jsx("div", { role: "status", "aria-live": "polite", className: "sr-only", children: r.aviso })] });
264
285
  }
286
+ export function PasswordField({ className, size, defaultVisible = false, id, ...props }) {
287
+ const s = useAureaStrings();
288
+ const [visivel, setVisivel] = React.useState(defaultVisible);
289
+ const auto = React.useId();
290
+ const campoId = id ?? auto;
291
+ return _jsxs(InputGroup, { className: className, children: [_jsx(Input, { id: campoId, type: visivel ? "text" : "password", className: peleDoEixo("input", size) || undefined, autoComplete: "current-password", ...props }), _jsx(InputGroupAddon, { side: "end", layout: "inline", children: _jsx(IconButton, { variant: "ghost", size: valorBase(size) === "lg" ? "md" : "sm", icon: visivel ? "view--off" : "view", label: visivel ? s.passwordHide : s.passwordShow, "aria-controls": campoId, onClick: () => setVisivel(v => !v) }) })] });
292
+ }
package/dist/inputs.d.ts CHANGED
@@ -1,2 +1,3 @@
1
1
  export * from "./inputs-client.js";
2
2
  export { Select, Textarea, Checkbox, Radio, Switch, Range } from "./markup.js";
3
+ export { Label, InputGroup, InputGroupAddon, type AddonSide, type AddonLayout, type FieldSize } from "./markup.js";
package/dist/inputs.js CHANGED
@@ -7,3 +7,7 @@
7
7
  // quebrariam o rótulo do campo em silêncio. Um caminho de import, um objeto.
8
8
  export * from "./inputs-client.js";
9
9
  export { Select, Textarea, Checkbox, Radio, Switch, Range } from "./markup.js";
10
+ // PORTADOS no merge de 28/08/2026, da linhagem da ATIVIDADE-2 — não existiam na `main`.
11
+ // Saem do `markup` pela mesma razão dos seis acima: nenhum usa hook, então chegam como
12
+ // componente de SERVIDOR e não arrastam a fronteira de cliente com eles.
13
+ export { Label, InputGroup, InputGroupAddon } from "./markup.js";
@@ -1,10 +1,32 @@
1
1
  import React from "react";
2
- import { type AureaStrings } from "./pure.js";
3
- export { cx, defaultStrings, ptBR, defaultSpriteUrl, universalStates, stateSeverity, type AureaStrings, type UniversalState } from "./pure.js";
2
+ import { type AureaStrings, type AureaTheme, type AureaDensity } from "./pure.js";
3
+ export { cx, fundirRender, defaultStrings, ptBR, defaultSpriteUrl, universalStates, stateSeverity, type AureaStrings, type UniversalState, type AureaTheme, type AureaDensity } from "./pure.js";
4
4
  export declare const StringsContext: React.Context<AureaStrings>;
5
5
  export declare const useAureaStrings: () => AureaStrings;
6
6
  export declare const SpriteContext: React.Context<string>;
7
7
  export declare const useSpriteUrl: () => string;
8
+ export declare const ThemeContext: React.Context<{
9
+ theme?: AureaTheme;
10
+ setTheme: (t: AureaTheme) => void;
11
+ toggleTheme: () => void;
12
+ }>;
13
+ /** Tema atual e como trocá-lo. Fora de um `AureaProvider`, devolve `undefined` e no-ops — não
14
+ * lança: um componente isolado num teste não deve morrer por falta de provider. */
15
+ export declare const useTheme: () => {
16
+ theme?: AureaTheme;
17
+ setTheme: (t: AureaTheme) => void;
18
+ toggleTheme: () => void;
19
+ };
20
+ export declare const DensityContext: React.Context<{
21
+ density?: AureaDensity;
22
+ setDensity: (d: AureaDensity) => void;
23
+ }>;
24
+ /** Densidade atual e como trocá-la. Mesma regra do `useTheme`. */
25
+ export declare const useDensity: () => {
26
+ density?: AureaDensity;
27
+ setDensity: (d: AureaDensity) => void;
28
+ };
29
+ export declare const DentroDoProviderContext: React.Context<boolean>;
8
30
  export declare const PortalContext: React.Context<HTMLElement | null | undefined>;
9
31
  export declare const usePortalContainer: () => HTMLElement | null | undefined;
10
32
  export interface Reordenavel<E extends HTMLElement> {
package/dist/internal.js CHANGED
@@ -12,13 +12,31 @@
12
12
  // pure → internal → system → actions → feedback → inputs → navigation → layout → data-display → resto.
13
13
  // INTERNO: o que todo módulo precisa e ninguém publica como componente próprio.
14
14
  // NÃO é subpath — `@aurea-uds/react/internal` não existe, de propósito.
15
+ // Kbd mora aqui por dependência, não por categoria: o Button precisa dele e ele não precisa de
16
+ // ninguém. A casa pública dele continua sendo `/data-display`, que o reexporta.
15
17
  import { createContext, useContext, useEffect, useRef, useState } from "react";
16
18
  import { defaultStrings, defaultSpriteUrl } from "./pure.js";
17
- export { cx, defaultStrings, ptBR, defaultSpriteUrl, universalStates, stateSeverity } from "./pure.js";
19
+ export { cx, fundirRender, defaultStrings, ptBR, defaultSpriteUrl, universalStates, stateSeverity } from "./pure.js";
18
20
  export const StringsContext = createContext(defaultStrings);
19
21
  export const useAureaStrings = () => useContext(StringsContext);
20
22
  export const SpriteContext = createContext(defaultSpriteUrl);
21
23
  export const useSpriteUrl = () => useContext(SpriteContext);
24
+ // Tema e densidade viajam por contexto, e não por leitura do DOM, por um motivo que já custou
25
+ // caro em outro lugar deste repositório: quem lê o DOM não é notificado quando ele muda. O
26
+ // `TableOfContents` é presentacional pelo mesmo desenho e por isso NÃO marca a seção em vista
27
+ // (`G-CAP-25`). Aqui o provider é o dono do valor, escreve no `<html>` e avisa quem depende.
28
+ //
29
+ // O valor inicial é `undefined` de propósito: significa "ninguém decidiu ainda", que é
30
+ // diferente de "escuro". Um default concreto aqui faria o provider sobrescrever, na primeira
31
+ // pintura, o `data-theme` que o documento já traz no markup — e esse atributo existe justamente
32
+ // para não haver piscada de tema ao carregar.
33
+ export const ThemeContext = createContext({ theme: undefined, setTheme: () => { }, toggleTheme: () => { } });
34
+ /** Tema atual e como trocá-lo. Fora de um `AureaProvider`, devolve `undefined` e no-ops — não
35
+ * lança: um componente isolado num teste não deve morrer por falta de provider. */
36
+ export const useTheme = () => useContext(ThemeContext);
37
+ export const DensityContext = createContext({ density: undefined, setDensity: () => { } });
38
+ /** Densidade atual e como trocá-la. Mesma regra do `useTheme`. */
39
+ export const useDensity = () => useContext(DensityContext);
22
40
  // ONDE OS POPUPS SÃO MONTADOS — achado B7, fechado na Parte C do PLANO-1.0 (07/08/2026).
23
41
  // O DEFEITO não era o portal: era o consumidor não ter como mexer nele. Todo popup da Aurea
24
42
  // (diálogo, gaveta, dica, popover, os dois menus, notificação, combobox, toast) monta num
@@ -30,6 +48,17 @@ export const useSpriteUrl = () => useContext(SpriteContext);
30
48
  // Agora tem, e entra pelo provider, UMA vez, como o sprite: é configuração de aplicação, não
31
49
  // prop de componente (foi a lição do achado A4, com 52 sítios repassando `spriteUrl` à mão).
32
50
  // `undefined` é o default e mantém o comportamento de sempre: `document.body`.
51
+ // 🔴 A SENTINELA DE "ESTOU DENTRO DE UM PROVIDER" — 09/09/2026, e ela existe por um defeito
52
+ // medido, não por precaução. O `useAureaTheme` (porta `/system`) escrevia o `data-theme` no
53
+ // documento DIRETO; o efeito do `AureaProvider` só reescreve o atributo quando o valor DELE muda,
54
+ // e não mudava. Resultado: o CSS ia para claro e o contexto continuava dizendo escuro, então todo
55
+ // componente que lê `useTheme()` renderizava do jeito errado — sem erro, sem aviso, publicado.
56
+ // ⚠ POR QUE UMA SENTINELA E NÃO "olhar o ThemeContext": o padrão dele é
57
+ // `{theme:undefined,setTheme:noop}` DE PROPÓSITO — `useTheme()` fora de um provider devolve isso
58
+ // em vez de lançar, e um componente isolado num teste depende disso. Então o valor do contexto
59
+ // NÃO distingue "sem provider" de "provider que ainda não adotou tema". Só um booleano que
60
+ // apenas o provider fornece distingue.
61
+ export const DentroDoProviderContext = createContext(false);
33
62
  export const PortalContext = createContext(undefined);
34
63
  export const usePortalContainer = () => useContext(PortalContext);
35
64
  export function useReorder({ count, order, onReorder, rowSelector, handleSelector }) {
@@ -1,4 +1,5 @@
1
1
  import { type HTMLAttributes, type RefAttributes, type ReactNode } from "react";
2
+ import { type Orientation, type Responsive } from "./pure.js";
2
3
  import { type TopbarVariant, type SidebarVariant } from "./navigation.js";
3
4
  export declare function AppShell({ brand, navigation, topbar, topbarVariant, sidebarVariant, sidebarCollapsed, children, className, ...props }: HTMLAttributes<HTMLDivElement> & RefAttributes<HTMLDivElement> & {
4
5
  brand: ReactNode;
@@ -8,3 +9,6 @@ export declare function AppShell({ brand, navigation, topbar, topbarVariant, sid
8
9
  sidebarVariant?: SidebarVariant;
9
10
  sidebarCollapsed?: boolean;
10
11
  }): import("react").JSX.Element;
12
+ export declare function Separator({ orientation, className, ...props }: HTMLAttributes<HTMLHRElement> & RefAttributes<HTMLHRElement> & {
13
+ orientation?: Responsive<Orientation>;
14
+ }): import("react").JSX.Element;
@@ -1,6 +1,12 @@
1
1
  "use client";
2
2
  import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ // Fase 9 (achado A5): este arquivo saiu do index.tsx de 970 linhas. Um módulo por categoria
4
+ // do registry — a taxonomia já existia e é gateada. A ordem de import entre eles é um DAG:
5
+ // internal → system → actions → feedback → inputs → navigation → layout → data-display → resto.
6
+ import { useRef } from "react";
7
+ import { Separator as BaseSeparator } from "@base-ui/react/separator";
3
8
  import { cx, useAureaStrings } from "./internal.js";
9
+ import { useValorResponsivo } from "./responsivo-runtime.js";
4
10
  import { IconButton } from "./actions.js";
5
11
  import { Sidebar, Topbar } from "./navigation.js";
6
12
  // ── Auxiliar de topo: mora ANTES do primeiro export, e a posição é obrigatória ───────────────
@@ -73,5 +79,39 @@ function focoDaGaveta(e) {
73
79
  // normal, e é como as 106 páginas do catálogo são montadas — recebia o padrão e ponto. A variante
74
80
  // estava documentada no CHANGELOG como saída para quem instalou a `0.3.0`, e a porta estava
75
81
  // trancada. É o par do `topbarVariant`, que já existia logo acima. Ver ADR-0034.
82
+ // O alvo do link de pular. Mesmo raciocínio do id da navegação: um documento tem UM AppShell,
83
+ // então id fixo é estável e não colide. Sem alvo estável não há como o link existir.
84
+ const SHELL_MAIN_ID = "aurea-shell-main";
76
85
  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 })] }); }
86
+ export function AppShell({ brand, navigation, topbar, topbarVariant = "floating", sidebarVariant, sidebarCollapsed, children, className, ...props }) {
87
+ const s = useAureaStrings();
88
+ 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, onToggle: focoDaGaveta, children: navigation }), _jsx("main", { id: SHELL_MAIN_ID, tabIndex: -1, className: "content", children: children })] });
89
+ }
90
+ // ── Separator ────────────────────────────────────────────────────────────────────────────────
91
+ // A LINHA DO SISTEMA, e ela é de CLIENTE por necessidade, não por vizinhança.
92
+ //
93
+ // Ela já foi um <hr> com orientação estática, no `markup.tsx`. O merge das duas linhagens ficou
94
+ // com essa versão, e o gate `comportamental.multi-motor.spec.ts` reprovou: a prova das cinco
95
+ // capacidades do `G-AXIS-06` — a lista que o Victor especificou — cobra do separador
96
+ // `data-orientation` E `aria-orientation` resolvidos pela largura do CONTÊINER, e os dois são
97
+ // atributo. Atributo não sai de classe CSS. Eixo comportamental precisa de runtime.
98
+ //
99
+ // E O ELEMENTO CONTINUA SENDO UM <hr>. As duas exigências pareciam brigar — a suíte de unidade
100
+ // cobra `<hr>` sem `role` escrito ("ARIA redundante é pior que ARIA nenhuma", aviso do próprio
101
+ // APG), e a prova de contêiner cobra atributo resolvido em runtime. `render` resolve as duas:
102
+ // o motor calcula e publica sobre o elemento que a Aurea escolhe.
103
+ //
104
+ // `role={undefined}` e o `aria-orientation` explícito são o acerto fino, e foram MEDIDOS antes
105
+ // de escrever: por padrão o motor escreve `role="separator"` no <hr> (redundante — o <hr> já É
106
+ // separator) e escreve `aria-orientation` também no HORIZONTAL, que é o default implícito do
107
+ // papel. As duas props passadas aqui vencem as do motor, e sobra exatamente o que se quer:
108
+ // vertical → <hr data-orientation="vertical" aria-orientation="vertical" class="separator separator-vertical">
109
+ // horizontal → <hr data-orientation="horizontal" class="separator">
110
+ export function Separator({ orientation, className, ...props }) {
111
+ const ancora = useRef(null);
112
+ const resolvida = useValorResponsivo(orientation, "horizontal", ancora);
113
+ // O elenco existe porque o tipo do motor descreve os manipuladores sobre `HTMLDivElement` — ele
114
+ // não sabe que o `render` troca o elemento. O tipo PÚBLICO desta função é o certo: quem passar um
115
+ // `ref` recebe o <hr> que está de fato no DOM.
116
+ 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 });
117
+ }
package/dist/layout.d.ts CHANGED
@@ -1,2 +1,3 @@
1
1
  export * from "./layout-client.js";
2
- export { Card, Stack, Cluster, Grid, Separator } from "./markup.js";
2
+ export { Card, Stack, Cluster, Grid } from "./markup.js";
3
+ export { AspectRatio } from "./markup.js";