@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.
- package/dist/actions.d.ts +35 -7
- package/dist/actions.js +76 -7
- package/dist/communication.js +3 -2
- package/dist/data-grid.js +149 -9
- package/dist/disclosure.d.ts +9 -0
- package/dist/disclosure.js +19 -0
- package/dist/escala.d.ts +26 -0
- package/dist/escala.js +17 -0
- package/dist/feedback-client.d.ts +5 -2
- package/dist/feedback-client.js +28 -2
- package/dist/file-input.js +2 -2
- package/dist/graph.js +8 -3
- package/dist/identity-client.d.ts +2 -1
- package/dist/identity-client.js +6 -2
- package/dist/index.d.ts +5 -3
- package/dist/index.js +11 -2
- package/dist/inputs-client.d.ts +28 -11
- package/dist/inputs-client.js +46 -18
- package/dist/inputs.d.ts +1 -0
- package/dist/inputs.js +4 -0
- package/dist/internal.d.ts +24 -2
- package/dist/internal.js +30 -1
- package/dist/layout-client.d.ts +4 -0
- package/dist/layout-client.js +41 -1
- package/dist/layout.d.ts +2 -1
- package/dist/layout.js +3 -1
- package/dist/markup.d.ts +46 -14
- package/dist/markup.js +67 -17
- package/dist/navigation-client.d.ts +8 -2
- package/dist/navigation-client.js +60 -3
- package/dist/overlays.d.ts +79 -4
- package/dist/overlays.js +65 -6
- package/dist/pure.d.ts +98 -0
- package/dist/pure.js +120 -2
- package/dist/qrcode.d.ts +2 -1
- package/dist/qrcode.js +3 -2
- package/dist/responsivo-runtime.d.ts +20 -0
- package/dist/responsivo-runtime.js +201 -0
- package/dist/system.d.ts +12 -4
- package/dist/system.js +86 -8
- package/package.json +5 -5
package/dist/actions.d.ts
CHANGED
|
@@ -1,10 +1,24 @@
|
|
|
1
1
|
import React, { type ButtonHTMLAttributes, type HTMLAttributes, type RefAttributes } from "react";
|
|
2
|
+
import { type Orientation, type Responsive } from "./pure.js";
|
|
2
3
|
import { type IconName } from "./system.js";
|
|
3
|
-
export type ButtonVariant = "primary" | "secondary" | "outline" | "ghost" | "danger" | "danger-outline" | "danger-ghost" | "link" | "link-primary" | "link-danger" | "nav";
|
|
4
|
+
export type ButtonVariant = "primary" | "secondary" | "outline" | "ghost" | "primary-outline" | "primary-ghost" | "danger" | "danger-outline" | "danger-ghost" | "link" | "link-primary" | "link-danger" | "nav";
|
|
5
|
+
export type ButtonAppearance = "solid" | "outline" | "ghost" | "link" | "nav";
|
|
6
|
+
export type ButtonTone = "neutral" | "brand" | "danger" | "success" | "warning" | "info";
|
|
4
7
|
export type ComponentSize = "xs" | "sm" | "md" | "lg" | "xl";
|
|
8
|
+
/** Resolve os dois eixos (ou o atalho) na classe que o core pinta. Exportada porque o teste
|
|
9
|
+
* cobra a matriz inteira, e porque quem monta um botão à mão precisa da mesma conta.
|
|
10
|
+
*
|
|
11
|
+
* Não há degradação: toda célula dos dois eixos tem regra própria no core. A versão de
|
|
12
|
+
* 21/08 rebaixava `solid` de success/info para `outline` porque faltava o par de token — o
|
|
13
|
+
* Victor recusou a degradação silenciosa e mandou consertar a infraestrutura, que é o que
|
|
14
|
+
* `--success`/`--success-foreground` e os irmãos são. `outline` voltou a ser só uma aparência
|
|
15
|
+
* que se pede, nunca o que sobra quando a nossa paleta não dá conta. */
|
|
16
|
+
export declare function buttonSkin(variant?: ButtonVariant, appearance?: ButtonAppearance, tone?: ButtonTone): string;
|
|
5
17
|
export interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement>, RefAttributes<HTMLButtonElement> {
|
|
6
18
|
variant?: ButtonVariant;
|
|
7
|
-
|
|
19
|
+
appearance?: ButtonAppearance;
|
|
20
|
+
tone?: ButtonTone;
|
|
21
|
+
size?: Responsive<ComponentSize>;
|
|
8
22
|
loading?: boolean;
|
|
9
23
|
leadingIcon?: IconName;
|
|
10
24
|
trailingIcon?: IconName;
|
|
@@ -29,9 +43,11 @@ export declare const Button: React.ForwardRefExoticComponent<Omit<ButtonProps, "
|
|
|
29
43
|
interface ToggleBase {
|
|
30
44
|
pressed?: boolean;
|
|
31
45
|
defaultPressed?: boolean;
|
|
32
|
-
onPressedChange?: (pressed: boolean) => void;
|
|
46
|
+
onPressedChange?: (pressed: boolean) => void; /** O que identifica este toggle DENTRO de um ToggleGroup — sem ele o grupo não sabe qual botão
|
|
47
|
+
* mudou, e a composição fica bonita e morta. Solto, não faz diferença nenhuma. */
|
|
48
|
+
value?: string;
|
|
33
49
|
icon?: IconName;
|
|
34
|
-
size?: Extract<ComponentSize, "sm" | "md" | "lg"
|
|
50
|
+
size?: Responsive<Extract<ComponentSize, "sm" | "md" | "lg">>;
|
|
35
51
|
disabled?: boolean;
|
|
36
52
|
id?: string;
|
|
37
53
|
className?: string;
|
|
@@ -44,17 +60,18 @@ export type ToggleProps = ToggleBase & ({
|
|
|
44
60
|
children?: never;
|
|
45
61
|
label: string;
|
|
46
62
|
});
|
|
47
|
-
export declare function Toggle({ pressed, defaultPressed, onPressedChange, icon, label, size, disabled, id, className, children }: ToggleProps): React.JSX.Element;
|
|
63
|
+
export declare function Toggle({ pressed, defaultPressed, onPressedChange, value, icon, label, size, disabled, id, className, children }: ToggleProps): React.JSX.Element;
|
|
48
64
|
export interface IconButtonProps extends Omit<ButtonProps, "children"> {
|
|
49
65
|
label: string;
|
|
50
66
|
icon: IconName;
|
|
51
67
|
}
|
|
52
68
|
export declare const IconButton: React.ForwardRefExoticComponent<Omit<IconButtonProps, "ref"> & RefAttributes<HTMLButtonElement>>;
|
|
53
|
-
export declare function ButtonGroup({ label, className, ...props }: HTMLAttributes<HTMLDivElement> & RefAttributes<HTMLDivElement> & {
|
|
69
|
+
export declare function ButtonGroup({ label, orientation, className, ...props }: HTMLAttributes<HTMLDivElement> & RefAttributes<HTMLDivElement> & {
|
|
54
70
|
label?: string;
|
|
71
|
+
orientation?: Responsive<Orientation>;
|
|
55
72
|
}): React.JSX.Element;
|
|
56
73
|
export declare function Toolbar({ orientation, label, className, ...props }: HTMLAttributes<HTMLDivElement> & RefAttributes<HTMLDivElement> & {
|
|
57
|
-
orientation?:
|
|
74
|
+
orientation?: Responsive<Orientation>;
|
|
58
75
|
label?: string;
|
|
59
76
|
}): React.JSX.Element;
|
|
60
77
|
export declare function ToolbarGroup({ label, className, ...props }: HTMLAttributes<HTMLDivElement> & RefAttributes<HTMLDivElement> & {
|
|
@@ -62,4 +79,15 @@ export declare function ToolbarGroup({ label, className, ...props }: HTMLAttribu
|
|
|
62
79
|
}): React.JSX.Element;
|
|
63
80
|
export declare function ToolbarSeparator({ className, ...props }: HTMLAttributes<HTMLDivElement> & RefAttributes<HTMLDivElement>): React.JSX.Element;
|
|
64
81
|
export declare const ToolbarButton: React.ForwardRefExoticComponent<Omit<ButtonProps, "ref"> & RefAttributes<HTMLButtonElement>>;
|
|
82
|
+
export declare function ToggleGroup({ value, defaultValue, onValueChange, multiple, orientation, disabled, label, className, children }: {
|
|
83
|
+
value?: string[];
|
|
84
|
+
defaultValue?: string[];
|
|
85
|
+
onValueChange?: (value: string[]) => void;
|
|
86
|
+
multiple?: boolean;
|
|
87
|
+
orientation?: Responsive<Orientation>;
|
|
88
|
+
disabled?: boolean;
|
|
89
|
+
label?: string;
|
|
90
|
+
className?: string;
|
|
91
|
+
children: React.ReactNode;
|
|
92
|
+
}): React.JSX.Element;
|
|
65
93
|
export {};
|
package/dist/actions.js
CHANGED
|
@@ -3,17 +3,57 @@ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-run
|
|
|
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
5
|
// internal → system → actions → feedback → inputs → navigation → layout → data-display → resto.
|
|
6
|
-
import React, { forwardRef } from "react";
|
|
6
|
+
import React, { useRef, forwardRef } from "react";
|
|
7
|
+
import { classesResponsivas, ehResponsivo, peleDoEixo, soOValor, valorBase } from "./pure.js";
|
|
8
|
+
import { useValorResponsivo } from "./responsivo-runtime.js";
|
|
7
9
|
import { Toolbar as BaseToolbar } from "@base-ui/react/toolbar";
|
|
8
10
|
import { Toggle as BaseToggle } from "@base-ui/react/toggle";
|
|
11
|
+
import { ToggleGroup as BaseToggleGroup } from "@base-ui/react/toggle-group";
|
|
9
12
|
import { cx, useAureaStrings } from "./internal.js";
|
|
10
13
|
import { Kbd } from "./markup.js";
|
|
11
14
|
import { Icon } from "./system.js";
|
|
15
|
+
// O dicionário que desachata o enum. É a ÚNICA fonte da correspondência: o gate do
|
|
16
|
+
// `validate.py` lê daqui para provar que todo par ou tem regra no core ou está declarado
|
|
17
|
+
// ausente com motivo, e a ficha do registry declara os mesmos dois eixos.
|
|
18
|
+
const EIXOS = {
|
|
19
|
+
primary: ["solid", "brand"], secondary: ["solid", "neutral"],
|
|
20
|
+
outline: ["outline", "neutral"], ghost: ["ghost", "neutral"],
|
|
21
|
+
"primary-outline": ["outline", "brand"], "primary-ghost": ["ghost", "brand"],
|
|
22
|
+
danger: ["solid", "danger"], "danger-outline": ["outline", "danger"], "danger-ghost": ["ghost", "danger"],
|
|
23
|
+
link: ["link", "neutral"], "link-primary": ["link", "brand"], "link-danger": ["link", "danger"],
|
|
24
|
+
nav: ["nav", "neutral"]
|
|
25
|
+
};
|
|
26
|
+
// caminho de volta: par → nome antigo. Existir aqui significa "o core já pinta esta célula com
|
|
27
|
+
// uma classe própria", e é o que garante que NENHUMA baseline se mexe: `appearance="outline"`
|
|
28
|
+
// + `tone="danger"` emite `.btn-danger-outline`, o mesmo DOM de sempre.
|
|
29
|
+
const CLASSE_ANTIGA = new Map(Object.entries(EIXOS)
|
|
30
|
+
.map(([v, [a, t]]) => [`${a}/${t}`, v]));
|
|
31
|
+
/** Resolve os dois eixos (ou o atalho) na classe que o core pinta. Exportada porque o teste
|
|
32
|
+
* cobra a matriz inteira, e porque quem monta um botão à mão precisa da mesma conta.
|
|
33
|
+
*
|
|
34
|
+
* Não há degradação: toda célula dos dois eixos tem regra própria no core. A versão de
|
|
35
|
+
* 21/08 rebaixava `solid` de success/info para `outline` porque faltava o par de token — o
|
|
36
|
+
* Victor recusou a degradação silenciosa e mandou consertar a infraestrutura, que é o que
|
|
37
|
+
* `--success`/`--success-foreground` e os irmãos são. `outline` voltou a ser só uma aparência
|
|
38
|
+
* que se pede, nunca o que sobra quando a nossa paleta não dá conta. */
|
|
39
|
+
export function buttonSkin(variant, appearance, tone) {
|
|
40
|
+
const [a0, t0] = EIXOS[variant ?? "secondary"];
|
|
41
|
+
const a = appearance ?? a0, t = tone ?? t0;
|
|
42
|
+
const antiga = CLASSE_ANTIGA.get(`${a}/${t}`);
|
|
43
|
+
return antiga ? `btn-${antiga}` : cx(`btn-${a}`, `btn-tone-${t}`);
|
|
44
|
+
}
|
|
12
45
|
// type="button" por default: o default do HTML é submit, e um "Cancelar"/"Remover"
|
|
13
46
|
// dentro de <form> dispararia a ação principal (auditoria 18/07/2026, ALTO 1).
|
|
14
47
|
// Quem quer submeter passa type="submit" explícito (como o MessageComposer faz).
|
|
15
|
-
export const Button = forwardRef(function Button({ variant
|
|
16
|
-
|
|
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) {
|
|
49
|
+
// G-AXIS-04 — `size` aceita valor simples, responsivo por viewport ou adaptativo por container.
|
|
50
|
+
// SOMATIVO por construção: valor simples continua emitindo `btn-sm`, byte por byte a mesma classe
|
|
51
|
+
// de antes, e por isso nenhuma baseline se mexe. Só o valor responsivo entra pela camada genérica
|
|
52
|
+
// (`size-*` + `vp-*:`/`ct-*:`), que existe uma vez no core para todo o sistema.
|
|
53
|
+
// Quem decide é o CSS: nada aqui observa largura, e o HTML do servidor já sai correto.
|
|
54
|
+
const responsivo = ehResponsivo(size);
|
|
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);
|
|
17
57
|
// kbd dentro do botão: mostra o atalho E o anuncia (aria-keyshortcuts), senão é enfeite.
|
|
18
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 })] });
|
|
19
59
|
// INERTE ≠ DESABILITADO, e a diferença é medida (M4, 13/08/2026): `disabled` tira o botão da
|
|
@@ -67,7 +107,7 @@ function temConteudoVisivel(children) {
|
|
|
67
107
|
// entre os estados**. Se o texto vira "Mute"/"Unmute" ou "Play"/"Pause", não é toggle — é
|
|
68
108
|
// botão de ação, porque quem lê tela ouve o rótulo NOVO e o estado ao mesmo tempo e não sabe
|
|
69
109
|
// se o botão descreve o que é ou o que fará.
|
|
70
|
-
export function Toggle({ pressed, defaultPressed, onPressedChange, icon, label, size
|
|
110
|
+
export function Toggle({ pressed, defaultPressed, onPressedChange, value, icon, label, size, disabled, id, className, children }) {
|
|
71
111
|
// O tipo barra o consumidor TypeScript; este aviso barra o de JavaScript, que não tem tipo nenhum.
|
|
72
112
|
// Sem gate por NODE_ENV de propósito: o `dist` é saída de `tsc`, então `process` não existe no
|
|
73
113
|
// navegador e a referência quebraria o render — e um controle sem nome merece aparecer em produção
|
|
@@ -78,16 +118,45 @@ export function Toggle({ pressed, defaultPressed, onPressedChange, icon, label,
|
|
|
78
118
|
const rotuloVisivel = typeof label === "string" && label.trim().length > 0;
|
|
79
119
|
if (!textoVisivel && !rotuloVisivel)
|
|
80
120
|
console.error("Aurea: <Toggle> sem `children` visível e sem `label` não tem nome acessível — quem usa leitor de tela encontra um botão anônimo. Passe `label` quando o toggle for só ícone.");
|
|
81
|
-
return _jsxs(BaseToggle, { id: id, disabled: disabled, pressed: pressed, defaultPressed: defaultPressed, onPressedChange: onPressedChange, "aria-label": textoVisivel ? undefined : (rotuloVisivel ? label : undefined), className: cx("btn", "btn-ghost", size !== "md" && `btn-${size}`, !textoVisivel && "btn-icon", "toggle", className), children: [icon && _jsx(Icon, { name: icon }), children] });
|
|
121
|
+
return _jsxs(BaseToggle, { id: id, value: value, disabled: disabled, pressed: pressed, defaultPressed: defaultPressed, onPressedChange: soOValor(onPressedChange), "aria-label": textoVisivel ? undefined : (rotuloVisivel ? label : undefined), className: cx("btn", "btn-ghost", size !== "md" && `btn-${size}`, !textoVisivel && "btn-icon", "toggle", className), children: [icon && _jsx(Icon, { name: icon }), children] });
|
|
82
122
|
}
|
|
83
123
|
// default ghost (não secondary): um ícone-ação solto — hambúrguer, tema, fechar — é sem
|
|
84
124
|
// caixa por convenção (pedido do Victor: hambúrguer sem borda). Quem quer a caixa passa
|
|
85
125
|
// variant. Alinha o React ao HTML dos docs, onde .btn-icon já é transparente.
|
|
86
126
|
export const IconButton = forwardRef(function IconButton({ label, icon, variant = "ghost", className, ...props }, ref) { return _jsx(Button, { ref: ref, variant: variant, className: cx("btn-icon", className), "aria-label": label, ...props, children: _jsx(Icon, { name: icon }) }); });
|
|
87
127
|
// ButtonGroup: agrupamento semântico. Sem roving tabindex — cada botão continua tabulável (use Toolbar para roving).
|
|
88
|
-
|
|
128
|
+
// `orientation` (G-AXIS-01) muda só o EIXO do layout, não a semântica: `role="group"` não tem
|
|
129
|
+
// noção de direção, e por isso — ao contrário do `Toolbar` e do `ToggleGroup`, que navegam por
|
|
130
|
+
// seta e precisam saber para que lado a seta anda — aqui não há `aria-orientation`. Anunciar
|
|
131
|
+
// orientação num grupo que não navega seria prometer teclado que não existe.
|
|
132
|
+
export function ButtonGroup({ label, orientation, className, ...props }) { const s = useAureaStrings(); return _jsx("div", { role: "group", "aria-label": label ?? s.optionsLabel, className: cx("btn-group", peleDoEixo("btn-group", orientation, "horizontal", "btn-group"), className), ...props }); }
|
|
89
133
|
// Toolbar: roving tabindex (setas navegam, Tab entra/sai) via Base UI.
|
|
90
|
-
|
|
134
|
+
// G-AXIS-06 — o valor responsivo RESOLVIDO é a única fonte de verdade: ele vai para o motor, o
|
|
135
|
+
// motor publica `data-orientation`/`aria-orientation` e ajusta o teclado, e a pele reage ao
|
|
136
|
+
// atributo publicado. Nada de o CSS decidir a orientação por breakpoint enquanto o atributo
|
|
137
|
+
// diz outra coisa — seriam duas fontes de verdade e uma janela em que visual e comportamento
|
|
138
|
+
// divergem. Ver `decisions/0019-css-first-para-apresentacao-runtime-para-semantica.md`.
|
|
139
|
+
export function Toolbar({ orientation, label, className, ...props }) {
|
|
140
|
+
const s = useAureaStrings();
|
|
141
|
+
const ancora = useRef(null);
|
|
142
|
+
const resolvida = useValorResponsivo(orientation, "horizontal", ancora);
|
|
143
|
+
return _jsx(BaseToolbar.Root, { ref: ancora, orientation: resolvida, "aria-label": label ?? s.toolbarLabel, className: cx("toolbar", className), ...props });
|
|
144
|
+
}
|
|
91
145
|
export function ToolbarGroup({ label, className, ...props }) { return _jsx(BaseToolbar.Group, { "aria-label": label, className: cx("toolbar-group", className), ...props }); }
|
|
92
146
|
export function ToolbarSeparator({ className, ...props }) { return _jsx(BaseToolbar.Separator, { className: cx("toolbar-sep", className), ...props }); }
|
|
93
147
|
export const ToolbarButton = forwardRef(function ToolbarButton({ variant = "ghost", ...props }, ref) { return _jsx(BaseToolbar.Button, { ref: ref, disabled: props.disabled, render: _jsx(Button, { variant: variant, ...props }) }); });
|
|
148
|
+
// TOGGLE GROUP — vários Toggle com estado compartilhado e navegação por seta entre eles.
|
|
149
|
+
//
|
|
150
|
+
// Não é o SegmentedControl, e confundir os dois constrói a coisa errada com o nome certo: o
|
|
151
|
+
// `SegmentedControl` é escolha ÚNICA e obrigatória entre alternativas de uma mesma dimensão
|
|
152
|
+
// ("dia / semana / mês") e se parece com uma cápsula; aqui cada botão liga e desliga por conta
|
|
153
|
+
// própria, `multiple` permite vários ao mesmo tempo, e zero selecionado é estado legítimo. É a
|
|
154
|
+
// barra de formatação de um editor, não um filtro de período.
|
|
155
|
+
//
|
|
156
|
+
// O motor dá o roving tabindex, o `loopFocus` e o contrato de pressionado; a pele é a do
|
|
157
|
+
// `.btn-group`, porque um grupo de botões é o que isto é.
|
|
158
|
+
export function ToggleGroup({ value, defaultValue, onValueChange, multiple, orientation, disabled, label, className, children }) {
|
|
159
|
+
const ancora = useRef(null);
|
|
160
|
+
const resolvida = useValorResponsivo(orientation, "horizontal", ancora);
|
|
161
|
+
return _jsx(BaseToggleGroup, { ref: ancora, value: value, defaultValue: defaultValue, onValueChange: soOValor(onValueChange), multiple: multiple, orientation: resolvida, disabled: disabled, "aria-label": label, className: cx("toggle-group", className), children: children });
|
|
162
|
+
}
|
package/dist/communication.js
CHANGED
|
@@ -5,6 +5,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
5
5
|
// internal → system → actions → feedback → inputs → navigation → layout → data-display → resto.
|
|
6
6
|
import React from "react";
|
|
7
7
|
import { cx, useAureaStrings } from "./internal.js";
|
|
8
|
+
import { InputGroup, InputGroupAddon } from "./inputs.js";
|
|
8
9
|
import { Icon } from "./system.js";
|
|
9
10
|
import { IconButton } from "./actions.js";
|
|
10
11
|
import { Avatar } from "./identity.js";
|
|
@@ -13,7 +14,7 @@ export function MessageList({ messages, label, className }) {
|
|
|
13
14
|
const s = useAureaStrings();
|
|
14
15
|
return _jsx("div", { className: cx("stack", className), role: "log", "aria-live": "polite", "aria-label": label ?? s.chatLabel, children: messages.map(m => _jsxs("div", { className: "message", children: [m.avatar ? _jsx(Avatar, { src: m.avatar.src, fallback: m.avatar.fallback }) : _jsx("span", {}), _jsxs("div", { className: "message-bubble", children: [(m.author || m.time) && _jsxs("div", { className: "label", children: [_jsx("span", { children: m.author }), m.time && _jsx("span", { className: "hint", children: m.time })] }), m.body, m.status && _jsx("div", { className: "message-status", children: _jsxs(Badge, { variant: m.status.variant, children: [_jsx("i", { className: "status-dot" }), m.status.label] }) })] })] }, m.id)) });
|
|
15
16
|
}
|
|
16
|
-
// Composer: <form> sobre
|
|
17
|
+
// Composer: <form> sobre InputGroup + .input; Enter envia (submit nativo) e limpa.
|
|
17
18
|
// Texto é estado interno (não controlado — controlar de fora só com demanda real,
|
|
18
19
|
// precedente MediaPlayer/FileInput). Não envia texto vazio/só-espaço; o botão de
|
|
19
20
|
// envio desabilita enquanto vazio. O input sempre tem nome acessível (aria-label),
|
|
@@ -24,5 +25,5 @@ export function MessageComposer({ onSend, placeholder, label, icon, sendLabel, d
|
|
|
24
25
|
const [text, setText] = React.useState("");
|
|
25
26
|
const submit = (e) => { e.preventDefault(); const t = text.trim(); if (!t)
|
|
26
27
|
return; onSend(t); setText(""); };
|
|
27
|
-
return _jsxs("form", { className: cx("message-composer", className), onSubmit: submit, children: [_jsxs(
|
|
28
|
+
return _jsxs("form", { className: cx("message-composer", className), onSubmit: submit, children: [_jsxs(InputGroup, { children: [icon && _jsx(InputGroupAddon, { children: _jsx(Icon, { name: icon }) }), _jsx("input", { className: "input", value: text, disabled: disabled, placeholder: placeholder, "aria-label": label ?? s.chatMessage, onChange: e => setText(e.target.value) })] }), _jsx(IconButton, { type: "submit", variant: "primary", icon: "send", label: sendLabel ?? s.chatSend, disabled: disabled || !text.trim() })] });
|
|
28
29
|
}
|
package/dist/data-grid.js
CHANGED
|
@@ -36,11 +36,35 @@ import { IconButton } from "./actions.js";
|
|
|
36
36
|
// porque é só o que existe em memória; para seleção que atravessa páginas, use
|
|
37
37
|
// `rowSelection`/`onRowSelectionChange`, que são ids.
|
|
38
38
|
// A11y: aria-sort fica SÓ no th ordenado (padrão APG) e o cabeçalho ordenável é
|
|
39
|
-
// um <button> de verdade
|
|
40
|
-
//
|
|
41
|
-
//
|
|
39
|
+
// um <button> de verdade. A paginação reusa o componente Pagination.
|
|
40
|
+
//
|
|
41
|
+
// G-A11Y-07 — ELE É UM `grid`, E ATÉ 28/08/2026 SÓ DIZIA QUE ERA.
|
|
42
|
+
//
|
|
43
|
+
// A ficha declarava `role: "grid"`, `apg: "grid"` e quatro setas. Medido no navegador em
|
|
44
|
+
// 27/08, as quatro eram INERTES: `<table>` puro, nenhum `onKeyDown`, nenhum `tabIndex` em
|
|
45
|
+
// célula. Prometer acessibilidade que não existe é pior que não ter — quem lê o contrato para
|
|
46
|
+
// decidir se adota confia nele. A ficha foi corrigida naquele dia para o que se media
|
|
47
|
+
// (`role: "table"`); hoje o componente passa a merecer o que prometia.
|
|
48
|
+
//
|
|
49
|
+
// POR QUE `grid` E NÃO `table`, agora de verdade: a APG manda usar `grid` quando as células
|
|
50
|
+
// contêm widgets operáveis, e as deste contêm — cabeçalho ordenável é `<button>`, seleção é
|
|
51
|
+
// `<input type=checkbox>`. E vem de brinde a correção de uma limitação que o comentário antigo
|
|
52
|
+
// deste arquivo registrava como fato da vida: **`aria-selected` é inválido em `role=table`, e é
|
|
53
|
+
// VÁLIDO em `role=grid`**. A seleção deixou de ser só `data-selected` (estilo) e passou a ser
|
|
54
|
+
// estado ACESSÍVEL na linha, que é o que o leitor de tela anuncia.
|
|
55
|
+
//
|
|
56
|
+
// O MODELO DE FOCO é o da APG: um tab stop para a grade inteira (roving tabindex pelas
|
|
57
|
+
// CÉLULAS, cabeçalho incluído), setas navegam, `Enter`/`F2` entram no widget da célula e
|
|
58
|
+
// `Escape` volta para a célula. Duas teclas para marcar um checkbox é o preço de a grade ter
|
|
59
|
+
// navegação — e é exatamente o que o exemplo "Data Grid" da APG faz.
|
|
60
|
+
//
|
|
61
|
+
// A conta de posição é feita contra o DOM (`table.rows[r].cells[c]`), não contra os dados: a
|
|
62
|
+
// linha de "vazio" tem uma célula só com `colSpan`, e qualquer aritmética baseada no número de
|
|
63
|
+
// colunas dos dados sairia da caixa ali. Quem sabe quantas células a linha tem é a linha.
|
|
42
64
|
function GridCheck({ label, indeterminate, ...props }) {
|
|
43
|
-
|
|
65
|
+
// `tabIndex={-1}`: numa grade APG a parada de Tab é UMA, a grade inteira. O checkbox continua
|
|
66
|
+
// focável por programa — é o que o `Enter`/`F2` da célula faz —, e o `Space` marca dali.
|
|
67
|
+
return _jsxs("label", { className: "checkbox", children: [_jsx("input", { type: "checkbox", tabIndex: -1, ref: el => { if (el)
|
|
44
68
|
el.indeterminate = !!indeterminate; }, ...props }), _jsx("span", { className: "control-mark" }), _jsx("span", { className: "sr-only", children: label })] });
|
|
45
69
|
}
|
|
46
70
|
// A faceta guarda um ARRAY de valores e a célula é ESCALAR — e nenhum filterFn de
|
|
@@ -160,6 +184,117 @@ export function DataGrid({ data, columns, label, filterable, pageSize, selectabl
|
|
|
160
184
|
...(facetIds.size ? { getFacetedRowModel: getFacetedRowModel(), getFacetedUniqueValues: getFacetedUniqueValues() } : {}),
|
|
161
185
|
});
|
|
162
186
|
const rows = table.getRowModel().rows;
|
|
187
|
+
// O TAB STOP ROVING, e o `clamp` não é zelo: filtrar, ordenar ou paginar troca as linhas sob
|
|
188
|
+
// o foco. Sem ele a grade inteira ficaria `tabIndex=-1` e sumiria da ordem do Tab — é o mesmo
|
|
189
|
+
// defeito que o `TreeView` pagou (MÉDIO 1 da auditoria) e resolve com `effectiveActive`.
|
|
190
|
+
const grade = React.useRef(null);
|
|
191
|
+
const [foco, setFoco] = React.useState({ r: 0, c: 0 });
|
|
192
|
+
const nColunas = table.getVisibleLeafColumns().length;
|
|
193
|
+
// A LINHA DE "VAZIO" CONTA, e conta com UMA coluna só.
|
|
194
|
+
//
|
|
195
|
+
// O defeito que isto corrige foi MEDIDO, e não é o que eu tinha escrito aqui na primeira
|
|
196
|
+
// versão. Eu havia afirmado que sem o clamp a grade "sumia da ordem do Tab"; a medição
|
|
197
|
+
// desmentiu — ela continua alcançável. O que quebra é mais sutil e mais chato: com o `clamp`
|
|
198
|
+
// de linha dizendo "só existe o cabeçalho" enquanto o DOM tem a linha do vazio, **a parada de
|
|
199
|
+
// Tab fica numa célula e o foco em outra**. Filtrando até não sobrar nada e descendo para a
|
|
200
|
+
// linha do vazio, o DOM ficava assim:
|
|
201
|
+
//
|
|
202
|
+
// sem o clamp TH:0 TH:-1 TD:-1 ← foco no TD, parada de Tab no TH
|
|
203
|
+
// com o clamp TH:-1 TH:-1 TD:0 ← a parada acompanha o foco
|
|
204
|
+
//
|
|
205
|
+
// Ou seja: sair da grade e voltar devolvia a pessoa ao cabeçalho, e não a onde ela estava. É
|
|
206
|
+
// a invariante do roving tabindex — a célula focada É a parada — e o teste que a cobra assere
|
|
207
|
+
// exatamente isso, porque contar quantas células têm `tabindex=0` passava verde com o defeito.
|
|
208
|
+
const nLinhas = (rows.length || 1) + 1; // +1: a linha de cabeçalho é navegável
|
|
209
|
+
const r = Math.min(foco.r, nLinhas - 1);
|
|
210
|
+
const colunasNaLinha = rows.length === 0 && r === 1 ? 1 : nColunas;
|
|
211
|
+
const eff = { r, c: Math.min(foco.c, colunasNaLinha - 1) };
|
|
212
|
+
// Mover é reposicionar NO DOM e focar. `setFoco` só existe para o `tabIndex` da próxima
|
|
213
|
+
// renderização; quem devolve o foco de verdade é o `.focus()` aqui.
|
|
214
|
+
const irPara = (r, c) => {
|
|
215
|
+
const linhas = grade.current?.rows;
|
|
216
|
+
if (!linhas?.length)
|
|
217
|
+
return;
|
|
218
|
+
const nr = Math.max(0, Math.min(r, linhas.length - 1));
|
|
219
|
+
const nc = Math.max(0, Math.min(c, linhas[nr].cells.length - 1));
|
|
220
|
+
setFoco({ r: nr, c: nc });
|
|
221
|
+
linhas[nr].cells[nc].focus();
|
|
222
|
+
};
|
|
223
|
+
const teclado = (e) => {
|
|
224
|
+
const alvo = e.target;
|
|
225
|
+
const celula = alvo.closest("th,td");
|
|
226
|
+
if (!celula || !grade.current?.contains(celula))
|
|
227
|
+
return;
|
|
228
|
+
const r = celula.parentElement.rowIndex, c = celula.cellIndex;
|
|
229
|
+
// DENTRO de um widget da célula, o teclado é DELE. Interceptar as setas aqui quebraria o
|
|
230
|
+
// campo de texto que um dia entre numa célula, e é o defeito que o probe de teclado pegou
|
|
231
|
+
// no `Space` dos bancos com <input> (`digitaTexto`).
|
|
232
|
+
const dentro = alvo !== celula;
|
|
233
|
+
if (dentro) {
|
|
234
|
+
if (e.key === "Escape") {
|
|
235
|
+
e.preventDefault();
|
|
236
|
+
irPara(r, c);
|
|
237
|
+
}
|
|
238
|
+
return;
|
|
239
|
+
}
|
|
240
|
+
// O SALTO DE PÁGINA é de 10 linhas e é ARBITRÁRIO com aval: a APG diz "um número de linhas
|
|
241
|
+
// definido pelo autor". 10 é o que cabe numa tela sem passar do fim na maioria das grades.
|
|
242
|
+
const salto = 10;
|
|
243
|
+
switch (e.key) {
|
|
244
|
+
case "ArrowRight":
|
|
245
|
+
e.preventDefault();
|
|
246
|
+
irPara(r, c + 1);
|
|
247
|
+
break;
|
|
248
|
+
case "ArrowLeft":
|
|
249
|
+
e.preventDefault();
|
|
250
|
+
irPara(r, c - 1);
|
|
251
|
+
break;
|
|
252
|
+
case "ArrowDown":
|
|
253
|
+
e.preventDefault();
|
|
254
|
+
irPara(r + 1, c);
|
|
255
|
+
break;
|
|
256
|
+
case "ArrowUp":
|
|
257
|
+
e.preventDefault();
|
|
258
|
+
irPara(r - 1, c);
|
|
259
|
+
break;
|
|
260
|
+
case "PageDown":
|
|
261
|
+
e.preventDefault();
|
|
262
|
+
irPara(r + salto, c);
|
|
263
|
+
break;
|
|
264
|
+
case "PageUp":
|
|
265
|
+
e.preventDefault();
|
|
266
|
+
irPara(r - salto, c);
|
|
267
|
+
break;
|
|
268
|
+
case "Home":
|
|
269
|
+
e.preventDefault();
|
|
270
|
+
irPara(e.ctrlKey ? 0 : r, 0);
|
|
271
|
+
break;
|
|
272
|
+
case "End":
|
|
273
|
+
e.preventDefault();
|
|
274
|
+
irPara(e.ctrlKey ? (grade.current.rows.length - 1) : r, nColunas - 1);
|
|
275
|
+
break;
|
|
276
|
+
// ENTRAR NA CÉLULA. Se ela não tem widget, não há o que fazer e a tecla segue seu caminho
|
|
277
|
+
// (um `Enter` numa célula de texto não deve engolir nada).
|
|
278
|
+
case "Enter":
|
|
279
|
+
case "F2": {
|
|
280
|
+
const w = celula.querySelector("button:not([disabled]),a[href],input:not([disabled]),select:not([disabled]),textarea:not([disabled])");
|
|
281
|
+
if (w) {
|
|
282
|
+
e.preventDefault();
|
|
283
|
+
w.focus();
|
|
284
|
+
}
|
|
285
|
+
break;
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
};
|
|
289
|
+
// O FOCO QUE CHEGA POR CLIQUE OU POR TAB também reposiciona o roving. Sem isto, sair da grade
|
|
290
|
+
// e voltar devolveria o foco à célula ANTIGA, e clicar numa célula deixaria o tab stop noutra.
|
|
291
|
+
const focoEntrou = (e) => {
|
|
292
|
+
const celula = e.target.closest("th,td");
|
|
293
|
+
if (celula && grade.current?.contains(celula)) {
|
|
294
|
+
setFoco({ r: celula.parentElement.rowIndex, c: celula.cellIndex });
|
|
295
|
+
}
|
|
296
|
+
};
|
|
297
|
+
const rove = (r, c) => eff.r === r && eff.c === c ? 0 : -1;
|
|
163
298
|
const sortIcon = (dir) => dir === "asc" ? "chevron--sort--up" : dir === "desc" ? "chevron--sort--down" : "chevron--sort";
|
|
164
299
|
// F5: a barra de lote. Duas referências independentes (Activepieces e Kaneo) chegam
|
|
165
300
|
// à MESMA anatomia — contagem, divisória, ações, e um jeito de limpar —, então é
|
|
@@ -231,10 +366,15 @@ export function DataGrid({ data, columns, label, filterable, pageSize, selectabl
|
|
|
231
366
|
// ele cabe é a largura da GRADE, não a da janela: por isso `@container`, no
|
|
232
367
|
// precedente do MediaPlayer.
|
|
233
368
|
const linhaAberta = detailRowId != null ? table.getRowModel().rows.find(r => r.id === detailRowId) : undefined;
|
|
234
|
-
|
|
369
|
+
// A REGIÃO DE ROLAGEM PERDEU O `tabIndex={0}` que o `Table` mantém, e a diferença é o
|
|
370
|
+
// motivo de a regra existir: `scrollable-region-focusable` do axe cobra que um contêiner
|
|
371
|
+
// rolável tenha COMO ser alcançado pelo teclado, e a grade agora tem células focáveis
|
|
372
|
+
// dentro — inclusive a de "vazio". Manter o `tabIndex` daria uma parada de Tab a mais
|
|
373
|
+
// ANTES da grade, e a APG pede que a grade inteira seja UMA parada.
|
|
374
|
+
const tabela = (_jsx("div", { className: "table-wrap", role: "region", "aria-label": label ?? s.tableLabel, "aria-busy": carregando || undefined, children: _jsxs("table", { ref: grade, role: "grid", "aria-label": label ?? s.tableLabel, onKeyDown: teclado, onFocus: focoEntrou, "aria-rowcount": pageSize != null ? table.getPreFilteredRowModel().rows.length + 1 : undefined, "aria-colcount": pageSize != null ? nColunas : undefined, children: [_jsxs("thead", { children: [table.getHeaderGroups().map(hg => _jsx("tr", { children: hg.headers.map((h, ci) => {
|
|
235
375
|
const dir = h.column.getIsSorted();
|
|
236
|
-
return _jsxs("th", { colSpan: h.colSpan, className: h.column.id === "select" ? "datagrid-selcol" : undefined, "aria-sort": dir === "asc" ? "ascending" : dir === "desc" ? "descending" : undefined, style: resizableColumns ? { width: h.getSize() } : undefined, children: [h.isPlaceholder ? null : h.column.getCanSort()
|
|
237
|
-
? _jsxs("button", { type: "button", className: "datagrid-sort", onClick: h.column.getToggleSortingHandler(), children: [flexRender(h.column.columnDef.header, h.getContext()), _jsx(Icon, { name: sortIcon(dir), size: "sm", className: "datagrid-sort-icon" })] })
|
|
376
|
+
return _jsxs("th", { colSpan: h.colSpan, tabIndex: rove(0, ci), className: h.column.id === "select" ? "datagrid-selcol" : undefined, "aria-sort": dir === "asc" ? "ascending" : dir === "desc" ? "descending" : undefined, style: resizableColumns ? { width: h.getSize() } : undefined, children: [h.isPlaceholder ? null : h.column.getCanSort()
|
|
377
|
+
? _jsxs("button", { type: "button", tabIndex: -1, className: "datagrid-sort", onClick: h.column.getToggleSortingHandler(), children: [flexRender(h.column.columnDef.header, h.getContext()), _jsx(Icon, { name: sortIcon(dir), size: "sm", className: "datagrid-sort-icon" })] })
|
|
238
378
|
: flexRender(h.column.columnDef.header, h.getContext()), resizableColumns && h.column.getCanResize() && _jsx("button", { type: "button", className: "datagrid-resizer", "aria-label": `${s.dataGridResize}: ${String(h.column.columnDef.header ?? h.column.id)}`, onMouseDown: h.getResizeHandler(), onTouchStart: h.getResizeHandler(), onKeyDown: e => {
|
|
239
379
|
const d = e.key === "ArrowLeft" ? -16 : e.key === "ArrowRight" ? 16 : 0;
|
|
240
380
|
if (!d)
|
|
@@ -247,8 +387,8 @@ export function DataGrid({ data, columns, label, filterable, pageSize, selectabl
|
|
|
247
387
|
return _jsx("th", { className: col.id === "select" ? "datagrid-selcol" : undefined, children: spec ? _jsx(GridFilter, { col: col, spec: spec, name: `${s.dataGridFilter} ${spec.label}` }) : null }, col.id);
|
|
248
388
|
}) })] }), _jsx("tbody", { children: carregando && !rows.length
|
|
249
389
|
? Array.from({ length: pageSize ?? 3 }, (_, i) => _jsx("tr", { children: table.getVisibleLeafColumns().map(c => _jsx("td", { children: _jsx(Skeleton, { className: "datagrid-skeleton" }) }, c.id)) }, `sk${i}`))
|
|
250
|
-
: rows.length ? rows.map(row => _jsx("tr", { "data-selected": row.getIsSelected() || undefined, children: row.getVisibleCells().map(cell => _jsx("td", { className: cell.column.id === "select" ? "datagrid-selcol" : undefined, children: flexRender(cell.column.columnDef.cell, cell.getContext()) }, cell.id)) }, row.id))
|
|
251
|
-
: _jsx("tr", { children: _jsx("td", { colSpan:
|
|
390
|
+
: rows.length ? rows.map((row, ri) => _jsx("tr", { "aria-selected": selectable ? row.getIsSelected() : undefined, "data-selected": row.getIsSelected() || undefined, children: row.getVisibleCells().map((cell, ci) => _jsx("td", { tabIndex: rove(ri + 1, ci), className: cell.column.id === "select" ? "datagrid-selcol" : undefined, children: flexRender(cell.column.columnDef.cell, cell.getContext()) }, cell.id)) }, row.id))
|
|
391
|
+
: _jsx("tr", { children: _jsx("td", { colSpan: nColunas, tabIndex: rove(1, 0), className: "datagrid-empty", children: s.dataGridEmpty }) }) })] }) }));
|
|
252
392
|
if (!renderDetail || !linhaAberta)
|
|
253
393
|
return tabela;
|
|
254
394
|
return _jsxs("div", { className: "datagrid-split", children: [tabela, _jsxs("aside", { className: "datagrid-detail", "aria-label": s.dataGridDetailPanel, children: [_jsx("div", { className: "datagrid-detail-head", children: _jsx(IconButton, { icon: "close", label: s.close, size: "sm", variant: "ghost", onClick: () => setDetailRowId(null) }) }), renderDetail(linhaAberta.original)] })] });
|
package/dist/disclosure.d.ts
CHANGED
|
@@ -6,3 +6,12 @@ export declare function Accordion({ items }: {
|
|
|
6
6
|
content: ReactNode;
|
|
7
7
|
}>;
|
|
8
8
|
}): import("react").JSX.Element;
|
|
9
|
+
export declare function Collapsible({ trigger, children, open, defaultOpen, onOpenChange, disabled, className }: {
|
|
10
|
+
trigger: ReactNode;
|
|
11
|
+
children: ReactNode;
|
|
12
|
+
open?: boolean;
|
|
13
|
+
defaultOpen?: boolean;
|
|
14
|
+
onOpenChange?: (open: boolean) => void;
|
|
15
|
+
disabled?: boolean;
|
|
16
|
+
className?: string;
|
|
17
|
+
}): import("react").JSX.Element;
|
package/dist/disclosure.js
CHANGED
|
@@ -1,2 +1,21 @@
|
|
|
1
|
+
"use client";
|
|
1
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { Collapsible as BaseCollapsible } from "@base-ui/react/collapsible";
|
|
4
|
+
import { cx } from "./internal.js";
|
|
5
|
+
import { soOValor } from "./pure.js";
|
|
6
|
+
import { Icon } from "./system.js";
|
|
2
7
|
export function Accordion({ items }) { return _jsx("div", { className: "accordion", children: items.map(i => _jsxs("details", { children: [_jsx("summary", { children: i.title }), _jsx("div", { className: "accordion-content", children: i.content })] }, i.id)) }); }
|
|
8
|
+
// COLLAPSIBLE — uma seção que abre e fecha, com o estado do lado de quem usa.
|
|
9
|
+
//
|
|
10
|
+
// Não é um Accordion de um item só, e a diferença é o motivo de existir: o `Accordion` é um
|
|
11
|
+
// CONJUNTO de seções sobre `<details>` nativo, que abre sem JS e guarda o próprio estado. Aqui a
|
|
12
|
+
// aplicação é a dona do estado — um grupo da lateral que lembra se estava aberto, um painel de
|
|
13
|
+
// filtros que abre por ação de outro lugar, um "mostrar mais" que fecha ao trocar de página.
|
|
14
|
+
// `<details>` controlado é possível e desconfortável; e cinco das nove referências tratam os dois
|
|
15
|
+
// como componentes distintos.
|
|
16
|
+
//
|
|
17
|
+
// A API é a daqui: `trigger` é dado, o conteúdo são os filhos. Não é a API composta da referência
|
|
18
|
+
// (Root/Trigger/Panel) — quem quiser compor esse nível usa o motor direto.
|
|
19
|
+
export function Collapsible({ trigger, children, open, defaultOpen, onOpenChange, disabled, className }) {
|
|
20
|
+
return _jsxs(BaseCollapsible.Root, { open: open, defaultOpen: defaultOpen, onOpenChange: soOValor(onOpenChange), disabled: disabled, className: cx("collapsible", className), children: [_jsxs(BaseCollapsible.Trigger, { className: "collapsible-trigger", children: [_jsx(Icon, { name: "chevron--down", size: "sm", className: "collapsible-chevron" }), trigger] }), _jsx(BaseCollapsible.Panel, { className: "collapsible-panel", children: children })] });
|
|
21
|
+
}
|
package/dist/escala.d.ts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/** Os pontos da escala, em pixels. UMA escala para os dois mensuráveis — o prefixo da classe
|
|
2
|
+
* (`vp-` / `ct-`) é que diz de quê são os pixels. */
|
|
3
|
+
export declare const ESCALA: {
|
|
4
|
+
readonly "2xs": 360;
|
|
5
|
+
readonly xs: 480;
|
|
6
|
+
readonly sm: 640;
|
|
7
|
+
readonly md: 768;
|
|
8
|
+
readonly lg: 1024;
|
|
9
|
+
readonly xl: 1280;
|
|
10
|
+
readonly "2xl": 1536;
|
|
11
|
+
};
|
|
12
|
+
/** O subconjunto que o CONTAINER consulta. Contêiner de 1280px é raro o bastante para não valer
|
|
13
|
+
* regra, e ponto que ninguém usa é peso morto na folha. Cresce por medição. */
|
|
14
|
+
export declare const ESCALA_CONTAINER: {
|
|
15
|
+
readonly "2xs": 360;
|
|
16
|
+
readonly xs: 480;
|
|
17
|
+
readonly sm: 640;
|
|
18
|
+
readonly md: 768;
|
|
19
|
+
readonly lg: 1024;
|
|
20
|
+
};
|
|
21
|
+
export type Breakpoint = "2xs" | "xs" | "sm" | "md" | "lg" | "xl" | "2xl";
|
|
22
|
+
export type ContainerBreakpoint = "2xs" | "xs" | "sm" | "md" | "lg";
|
|
23
|
+
/** Os pontos do maior para o menor. A resolução percorre nesta ordem e para no primeiro que
|
|
24
|
+
* couber, que é a semântica de `min-width`: o último ponto atingido vence. */
|
|
25
|
+
export declare const PONTOS_DESC: ReadonlyArray<Breakpoint>;
|
|
26
|
+
export declare const PONTOS_CONTAINER_DESC: ReadonlyArray<ContainerBreakpoint>;
|
package/dist/escala.js
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
// GERADO por scripts/build-responsive-layer.mjs — não editar à mão.
|
|
2
|
+
//
|
|
3
|
+
// A escala canônica, lida de `packages/tokens/dist/aurea.tokens.css`. É a MESMA leitura que
|
|
4
|
+
// gera as `@media`/`@container` do core, e é por isso que o resolvedor de runtime e o CSS
|
|
5
|
+
// não podem discordar: não há dois números, há um.
|
|
6
|
+
//
|
|
7
|
+
// Sem `"use client"`: é dado, e um componente de servidor precisa dos tipos.
|
|
8
|
+
/** Os pontos da escala, em pixels. UMA escala para os dois mensuráveis — o prefixo da classe
|
|
9
|
+
* (`vp-` / `ct-`) é que diz de quê são os pixels. */
|
|
10
|
+
export const ESCALA = { "2xs": 360, "xs": 480, "sm": 640, "md": 768, "lg": 1024, "xl": 1280, "2xl": 1536 };
|
|
11
|
+
/** O subconjunto que o CONTAINER consulta. Contêiner de 1280px é raro o bastante para não valer
|
|
12
|
+
* regra, e ponto que ninguém usa é peso morto na folha. Cresce por medição. */
|
|
13
|
+
export const ESCALA_CONTAINER = { "2xs": 360, "xs": 480, "sm": 640, "md": 768, "lg": 1024 };
|
|
14
|
+
/** Os pontos do maior para o menor. A resolução percorre nesta ordem e para no primeiro que
|
|
15
|
+
* couber, que é a semântica de `min-width`: o último ponto atingido vence. */
|
|
16
|
+
export const PONTOS_DESC = ["2xl", "xl", "lg", "md", "sm", "xs", "2xs"];
|
|
17
|
+
export const PONTOS_CONTAINER_DESC = ["lg", "md", "sm", "xs", "2xs"];
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type Responsive } from "./pure.js";
|
|
1
2
|
import React, { type HTMLAttributes, type RefAttributes, type ReactNode } from "react";
|
|
2
3
|
import { type UniversalState } from "./internal.js";
|
|
3
4
|
import { type IconName } from "./system.js";
|
|
@@ -8,10 +9,12 @@ export declare function Status({ variant, state, children, className, ...props }
|
|
|
8
9
|
state?: UniversalState;
|
|
9
10
|
}): React.JSX.Element;
|
|
10
11
|
export type AlertVariant = "info" | "success" | "warning" | "danger";
|
|
11
|
-
export declare function Alert({ variant, state, title, children, className, ...props }: HTMLAttributes<HTMLDivElement> & RefAttributes<HTMLDivElement> & {
|
|
12
|
+
export declare function Alert({ variant, state, title, icon, onDismiss, children, className, ...props }: HTMLAttributes<HTMLDivElement> & RefAttributes<HTMLDivElement> & {
|
|
12
13
|
variant?: AlertVariant;
|
|
13
14
|
state?: UniversalState;
|
|
14
15
|
title?: ReactNode;
|
|
16
|
+
icon?: IconName;
|
|
17
|
+
onDismiss?: () => void;
|
|
15
18
|
}): React.JSX.Element;
|
|
16
19
|
export type BannerVariant = AlertVariant;
|
|
17
20
|
export declare function Banner({ variant, state, title, icon, onDismiss, children, className, ...props }: HTMLAttributes<HTMLDivElement> & RefAttributes<HTMLDivElement> & {
|
|
@@ -22,7 +25,7 @@ export declare function Banner({ variant, state, title, icon, onDismiss, childre
|
|
|
22
25
|
onDismiss?: () => void;
|
|
23
26
|
}): React.JSX.Element;
|
|
24
27
|
export declare function Spinner({ size, label, decorative, className, ...props }: HTMLAttributes<HTMLSpanElement> & RefAttributes<HTMLSpanElement> & {
|
|
25
|
-
size?: "sm" | "md" | "lg"
|
|
28
|
+
size?: Responsive<"sm" | "md" | "lg">;
|
|
26
29
|
label?: string;
|
|
27
30
|
decorative?: boolean;
|
|
28
31
|
}): React.JSX.Element;
|
package/dist/feedback-client.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } 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 { Popover as BasePopover } from "@base-ui/react/popover";
|
|
@@ -28,7 +29,29 @@ import { Skeleton } from "./markup.js";
|
|
|
28
29
|
// "esperando" dele é grave. E o rótulo é o do consumidor, como sempre foi: a string universal
|
|
29
30
|
// entra só quando não há filho, para o componente não passar a inventar texto.
|
|
30
31
|
export function Status({ variant, state, children, className, ...props }) { const s = useAureaStrings(); const v = variant ?? (state ? (state === "offline" ? "offline" : stateSeverity(state)) : "neutral"); return _jsxs("span", { className: cx("status", v !== "neutral" && `status-${v}`, className), "data-state": state, ...props, children: [_jsx("i", { className: "status-dot", "aria-hidden": "true" }), _jsx("span", { className: "status-label", children: children ?? (state ? s.universalState[state] : null) })] }); }
|
|
31
|
-
|
|
32
|
+
// O ícone é FUNÇÃO da variante, como o `role` já era. A referência congelada desenha o alerta
|
|
33
|
+
// com ele em toda variante, e um sistema que faz o consumidor lembrar qual glifo significa
|
|
34
|
+
// "perigo" acaba com quatro respostas diferentes para a mesma pergunta. `icon` sobrepõe quando o
|
|
35
|
+
// caso é específico. O Banner NÃO ganha o mesmo padrão de propósito: ele não existe na
|
|
36
|
+
// referência e o aviso de largura de página nem sempre quer glifo — a assimetria é decidida,
|
|
37
|
+
// não herdada.
|
|
38
|
+
const ICONE_DA_VARIANTE = { info: "information--filled",
|
|
39
|
+
success: "checkmark--filled", warning: "warning--alt--filled", danger: "error--filled" };
|
|
40
|
+
// A grade do `.alert` tem TRÊS trilhas (auto 1fr auto): ícone, corpo, ação. Uma versão anterior
|
|
41
|
+
// emitia o título solto na trilha 1 — a do ícone — e o corpo como item anônimo. Medido no
|
|
42
|
+
// navegador em 21/08/2026: o título ocupava a coluna do ícone e crescia com o próprio texto,
|
|
43
|
+
// enquanto a referência dá 300px de trilha ao ícone e 544px ao corpo. Título e corpo vão JUNTOS
|
|
44
|
+
// na trilha 2, dentro de um <div>, que é o que a referência faz.
|
|
45
|
+
// `<strong>` e não `<h4>`: um alerta aparece em qualquer profundidade da página e um h4 fixo
|
|
46
|
+
// quebra a ordem de cabeçalhos onde ele cair (é a razão de o EmptyState ter `titleAs`).
|
|
47
|
+
//
|
|
48
|
+
// A anatomia das três trilhas voltou em 29/08/2026: o merge das duas linhagens ficou com a
|
|
49
|
+
// versão sem ícone, e os oito testes do `grade-do-core.test.tsx` foram o que pegou. `state` é da
|
|
50
|
+
// outra linhagem e fica: é EIXO À PARTE de `variant` — a variante é a cor, o estado é a condição
|
|
51
|
+
// universal. Quem passa só `state` recebe a cor derivada da gravidade e o rótulo universal
|
|
52
|
+
// quando não há filho; quem passa os dois manda, porque só o consumidor sabe se aquele
|
|
53
|
+
// "esperando" dele é grave.
|
|
54
|
+
export function Alert({ variant, state, title, icon, onDismiss, children, className, ...props }) { const s = useAureaStrings(); const v = variant ?? (state ? stateSeverity(state) : "info"); return _jsxs("div", { className: cx("alert", `alert-${v}`, className), role: v === "danger" ? "alert" : "status", "data-state": state, ...props, children: [_jsx(Icon, { name: icon ?? ICONE_DA_VARIANTE[v] }), _jsxs("div", { children: [title && _jsx("strong", { children: title }), children ?? (state ? s.universalState[state] : null)] }), onDismiss ? _jsx(IconButton, { variant: "ghost", size: "sm", icon: "close", label: s.close, onClick: onDismiss }) : _jsx("span", {})] }); }
|
|
32
55
|
export function Banner({ variant, state, title, icon, onDismiss, children, className, ...props }) { const s = useAureaStrings(); const v = variant ?? (state ? stateSeverity(state) : "info"); return _jsxs("div", { className: cx("banner", `banner-${v}`, className), role: v === "danger" ? "alert" : "status", "data-state": state, ...props, children: [icon ? _jsx(Icon, { name: icon }) : _jsx("span", {}), _jsxs("div", { children: [title && _jsx("strong", { children: title }), children ?? (state ? s.universalState[state] : null)] }), onDismiss ? _jsx(IconButton, { variant: "ghost", size: "sm", icon: "close", label: s.close, onClick: onDismiss }) : _jsx("span", {})] }); }
|
|
33
56
|
// A barra grampeava 0..100 e o `aria-valuenow` NÃO — medido ao publicar o contrato de API na
|
|
34
57
|
// Parte E: com value=150 o desenho parava em 100% e o leitor de tela anunciava "150 de 100".
|
|
@@ -37,7 +60,10 @@ export function Banner({ variant, state, title, icon, onDismiss, children, class
|
|
|
37
60
|
// SÓ DETERMINADA, de propósito: não há modo indeterminado nem `.progress` para ele no core. A
|
|
38
61
|
// ficha dizia que havia e era falso — corrigido junto, porque contrato que promete o que não
|
|
39
62
|
// existe custa mais que ausência.
|
|
40
|
-
|
|
63
|
+
// O DEGRAU RESPONSIVO volta (merge de 28/08/2026): a ficha declara `responsive.size` e o
|
|
64
|
+
// componente tinha perdido a capacidade quando este arquivo entrou da `main`. Mesma perda
|
|
65
|
+
// silenciosa do `Input`, do `Tabs` e do `NumberField` — a ficha documentava, o código não fazia.
|
|
66
|
+
export function Spinner({ size, label, decorative, className, ...props }) { const s = useAureaStrings(); return _jsx("span", { className: cx("spinner", peleDoEixo("spinner", size, "sm", "spinner"), className), ...(decorative ? { "aria-hidden": true } : { role: "status", "aria-label": label ?? s.loading }), ...props }); }
|
|
41
67
|
// `children` como FUNÇÃO é de propósito para o caso `loading`: assim o consumidor não paga o
|
|
42
68
|
// render do conteúdo enquanto ele não existe. Aceita nó também, porque a maioria das telas já
|
|
43
69
|
// tem o conteúdo pronto e obrigar função seria cerimônia.
|