@aurea-uds/react 0.30.0 → 0.32.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.
@@ -11,16 +11,230 @@ import { Form as BaseForm } from "@base-ui/react/form";
11
11
  import { Field as BaseField } from "@base-ui/react/field";
12
12
  import { NumberField as BaseNumberField } from "@base-ui/react/number-field";
13
13
  import { OTPField as BaseOTPField } from "@base-ui/react/otp-field";
14
+ import { Slider as BaseSlider } from "@base-ui/react/slider";
14
15
  import { RadioGroup as BaseRadioGroup } from "@base-ui/react/radio-group";
15
16
  import { Radio as BaseRadio } from "@base-ui/react/radio";
16
- import { cx, useAureaStrings, usePortalContainer, useReorder } from "./internal.js";
17
+ import { useDirection } from "@base-ui/react/direction-provider";
18
+ import { cx, useAureaStrings, usePortalContainer, useReorder, controlesRotulaveis } from "./internal.js";
19
+ import { COLOR_CHANNELS, areaBackground, channelRange, colorName, corToHex, formatChannel, getChannel, hsbToRgb, parseColor, pureHue, setChannel, spaceOf, trackBackground } from "./color.js";
20
+ import { Popover as BasePopover } from "@base-ui/react/popover";
17
21
  // Os seis controles abaixo saíram deste arquivo no item O1 e moram no `markup.tsx`, sem a
18
22
  // diretiva — são marcação, e quem escuta `onChange` é o consumidor. Voltam IMPORTADOS e não só
19
23
  // reexportados porque o `ehControle` do `Field` compara por IDENTIDADE de referência: dois
20
24
  // objetos diferentes com o mesmo nome quebrariam o rótulo do campo em silêncio.
21
- import { Textarea, Checkbox, Radio, Switch, Range, InputGroup, InputGroupAddon } from "./markup.js";
25
+ import { Textarea, Checkbox, Radio, Switch, Range, InputGroup, InputGroupAddon, ColorSwatch } from "./markup.js";
22
26
  import { Icon } from "./system.js";
23
27
  import { IconButton } from "./actions.js";
28
+ // A FAMÍLIA DE COR (0.32.0, ADR-0069) — as peças INTERNAS moram aqui, no topo, antes do primeiro export (auxiliar
29
+ // entre exports entra na fatia de outro componente — regra do check 22). As públicas (`ColorArea`, `Slider` com
30
+ // `channel`, `ColorField`, `ColorPicker`) ficam com as irmãs delas, lá embaixo.
31
+ //
32
+ // A COR DE DENTRO é HSB com alfa (`Cor`, do `color.ts`), e não hex: o hex perde o matiz no cinza e no preto —
33
+ // puxar a saturação a 0 não pode trocar a cor do fundo da área. Os três espaços (RGB, HSL, HSB) e o alfa entram por
34
+ // ela, com as faixas de cada canal iguais às da biblioteca de acessibilidade em que a referência se apoia.
35
+ //
36
+ // A ÁREA (`AreaDeCor`) é o DESENHO acessível da referência, lido no código da biblioteca dela: um grupo com DOIS
37
+ // `input type="range"` escondidos, um por eixo, anunciados como controle 2D, e o valor dito com o NOME da cor. Só um
38
+ // dos dois fica na ordem do Tab (o do eixo que mexeu por último); o outro sai da árvore até a pessoa mexer nele pelo
39
+ // teclado. As teclas são as dela: setas (Shift: o salto do canal), Page Up/Down no eixo de cima, Home/End no de
40
+ // lado, invertidas na escrita da direita para a esquerda.
41
+ const no01 = (n) => Math.min(1, Math.max(0, n));
42
+ // COMO A PESSOA CHEGOU AQUI: a última interação foi teclado ou ponteiro? O anel de foco da área é só do teclado, e o
43
+ // foco que chega por código (a janela do `ColorPicker` o põe na área ao abrir; o arrasto o põe no `range`) não diz
44
+ // nada por si. É o jeito da biblioteca de acessibilidade em que a referência se apoia: ouvir o documento inteiro, uma
45
+ // vez, e lembrar a última modalidade. Medido em 13/10/2026: abrindo o seletor com o mouse, o anel acendia.
46
+ let modalidade = "teclado";
47
+ let ouvindoModalidade = false;
48
+ function ouvirModalidade() {
49
+ if (ouvindoModalidade || typeof document === "undefined")
50
+ return;
51
+ ouvindoModalidade = true;
52
+ document.addEventListener("keydown", (e) => { if (!e.metaKey && !e.ctrlKey && !e.altKey)
53
+ modalidade = "teclado"; }, true);
54
+ document.addEventListener("pointerdown", () => { modalidade = "ponteiro"; }, true);
55
+ }
56
+ /** O texto de entrada → a cor de dentro. Hex e `rgb()` não dizem o matiz do cinza: aí vale o matiz (e a saturação)
57
+ * da cor de antes. `hsl()`/`hsb()` dizem, e o que veio no texto vale. */
58
+ function corDoTexto(texto, anterior) {
59
+ const nova = texto ? parseColor(texto) : null;
60
+ if (!nova)
61
+ return anterior ?? { h: 0, s: 0, b: 1, a: 1 };
62
+ if (anterior && (nova.s === 0 || nova.b === 0) && !/^\s*hs[lb]/i.test(texto))
63
+ return { ...nova, h: anterior.h, s: nova.b === 0 ? anterior.s : nova.s };
64
+ return nova;
65
+ }
66
+ /** A cor que o `ColorPicker` reparte com as peças de dentro, quando o conteúdo da janela é montado por quem usa
67
+ * ("com campos", "com trilhas" — os exemplos da referência). */
68
+ const CorDoSeletor = React.createContext(null);
69
+ /** O estado de cor das peças: a cor de dentro por dentro, hex por fora. Controlado, se o hex que chega é o que a peça
70
+ * acabou de mandar, a cor de dentro vale (com o matiz); se é outro, vira cor guardando o matiz de antes no cinza.
71
+ * Dentro de um `ColorPicker`, a peça sem valor próprio usa a cor DELE. */
72
+ function useCor(value, defaultValue, onValueChange, onValueCommitted) {
73
+ const doSeletor = React.useContext(CorDoSeletor);
74
+ const [interno, setInterno] = React.useState(() => corDoTexto(defaultValue));
75
+ const ultimo = React.useRef(interno);
76
+ let cor = interno;
77
+ if (value !== undefined) {
78
+ const chegou = parseColor(value);
79
+ const mesmo = chegou !== null && corToHex(chegou) === corToHex(ultimo.current);
80
+ cor = mesmo ? ultimo.current : corDoTexto(value, ultimo.current);
81
+ }
82
+ ultimo.current = cor;
83
+ const avisar = (novo, fim) => { const h = corToHex(novo); onValueChange?.(h); if (fim)
84
+ onValueCommitted?.(h); };
85
+ if (doSeletor && value === undefined && defaultValue === undefined)
86
+ return [doSeletor.cor, (novo, fim) => { doSeletor.mudar(novo, fim); avisar(novo, fim); }];
87
+ const mudar = (novo, fim) => {
88
+ ultimo.current = novo;
89
+ // SEMPRE, e não só sem controle: no cinza o hex não muda com o matiz, o app devolve o MESMO valor e nada
90
+ // redesenharia — a trilha mexia e a área não (o teste do `ColorPicker` pegou). Este estado é o que redesenha;
91
+ // com controle, quem decide continua sendo o `value` (acima).
92
+ setInterno(novo);
93
+ avisar(novo, fim);
94
+ };
95
+ return [cor, mudar];
96
+ }
97
+ /** O espaço de uma peça de cor: o pedido, ou o que os canais exigem (vermelho/verde/azul no RGB, luminosidade no
98
+ * HSL, brilho no HSB). */
99
+ function espacoDosCanais(pedido, ...canais) {
100
+ if (pedido)
101
+ return pedido;
102
+ for (const ch of canais) {
103
+ const e = spaceOf(ch);
104
+ if (e !== "hsb" || ch === "brightness")
105
+ return e;
106
+ }
107
+ return "hsb";
108
+ }
109
+ function AreaDeCor({ cor, onCor, space, x: canalX, y: canalY, showDots, disabled, name, id, className, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledby, "aria-describedby": ariaDescribedby }) {
110
+ const s = useAureaStrings();
111
+ const rtl = useDirection() === "rtl";
112
+ const caixa = React.useRef(null);
113
+ const inX = React.useRef(null), inY = React.useRef(null);
114
+ const [arrastando, setArrastando] = React.useState(false);
115
+ const [focado, setFocado] = React.useState(null);
116
+ const [pelaTecla, setPelaTecla] = React.useState(false);
117
+ // O anel de foco é do TECLADO, como na referência: o foco que o arrasto põe no `range` não acende o anel.
118
+ const [peloPonteiro, setPeloPonteiro] = React.useState(false);
119
+ React.useEffect(ouvirModalidade, []);
120
+ const fx = channelRange(canalX), fy = channelRange(canalY);
121
+ const canalZ = COLOR_CHANNELS[space].find((ch) => ch !== canalX && ch !== canalY);
122
+ const vx = getChannel(cor, canalX, space), vy = getChannel(cor, canalY, space);
123
+ const px = (vx - fx.min) / (fx.max - fx.min), py = (vy - fy.min) / (fy.max - fy.min);
124
+ const hex = corToHex({ ...cor, a: 1 }), nome = colorName(hsbToRgb(cor), s);
125
+ // Antes de mexer, o leitor de tela ouve os três canais e o nome; depois de mexer pelo teclado, só o canal que
126
+ // mudou e o nome — como na referência (o resto ele já ouviu).
127
+ const fala = (ch, v) => `${s.colorChannel[ch]} ${formatChannel(ch, v)}`;
128
+ const textoDo = (eixo) => {
129
+ const a = fala(canalX, vx), b = fala(canalY, vy), c = fala(canalZ, getChannel(cor, canalZ, space));
130
+ const partes = pelaTecla ? [eixo === "x" ? a : b] : eixo === "x" ? [a, b, c] : [b, a, c];
131
+ return `${partes.join(", ")}, ${nome}`;
132
+ };
133
+ const rotulo = ariaLabel ? `${ariaLabel}, ${s.colorPicker}` : ariaLabelledby ? undefined : s.colorPicker;
134
+ // Na grade do passo do canal: somar passos em ponto flutuante acumula erro.
135
+ const naGrade = (v, f) => Math.round(Math.min(f.max, Math.max(f.min, v)) / f.step) * f.step;
136
+ const com = (nx, ny) => setChannel(setChannel(cor, canalX, nx, space), canalY, ny, space);
137
+ const pelaPosicao = (cx, cy, fim) => {
138
+ const r = caixa.current.getBoundingClientRect();
139
+ if (!r.width || !r.height)
140
+ return;
141
+ const x = no01((cx - r.left) / r.width), y = no01((cy - r.top) / r.height);
142
+ onCor(com(fx.min + (rtl ? 1 - x : x) * (fx.max - fx.min), fy.min + (1 - y) * (fy.max - fy.min)), fim);
143
+ };
144
+ const focar = (eixo) => { (eixo === "x" ? inX : inY).current?.focus({ preventScroll: true }); setFocado(eixo); };
145
+ const aoApertar = (e) => {
146
+ if (disabled || e.button !== 0)
147
+ return;
148
+ e.preventDefault();
149
+ e.currentTarget.setPointerCapture?.(e.pointerId);
150
+ setArrastando(true);
151
+ setPelaTecla(false);
152
+ setPeloPonteiro(true);
153
+ focar("x");
154
+ pelaPosicao(e.clientX, e.clientY, false);
155
+ };
156
+ const aoMover = (e) => { if (arrastando)
157
+ pelaPosicao(e.clientX, e.clientY, false); };
158
+ const aoSoltar = (e) => { if (!arrastando)
159
+ return; setArrastando(false); pelaPosicao(e.clientX, e.clientY, true); };
160
+ const aoTeclar = (e) => {
161
+ if (disabled)
162
+ return;
163
+ const frente = rtl ? -1 : 1, sx = e.shiftKey ? fx.page : fx.step, sy = e.shiftKey ? fy.page : fy.step;
164
+ let nx = vx, ny = vy, eixo;
165
+ switch (e.key) {
166
+ case "ArrowRight":
167
+ nx += sx * frente;
168
+ eixo = "x";
169
+ break;
170
+ case "ArrowLeft":
171
+ nx -= sx * frente;
172
+ eixo = "x";
173
+ break;
174
+ case "ArrowUp":
175
+ ny += sy;
176
+ eixo = "y";
177
+ break;
178
+ case "ArrowDown":
179
+ ny -= sy;
180
+ eixo = "y";
181
+ break;
182
+ case "PageUp":
183
+ ny += fy.page;
184
+ eixo = "y";
185
+ break;
186
+ case "PageDown":
187
+ ny -= fy.page;
188
+ eixo = "y";
189
+ break;
190
+ case "Home":
191
+ nx -= fx.page * frente;
192
+ eixo = "x";
193
+ break;
194
+ case "End":
195
+ nx += fx.page * frente;
196
+ eixo = "x";
197
+ break;
198
+ default: return;
199
+ }
200
+ e.preventDefault();
201
+ setPelaTecla(true);
202
+ setPeloPonteiro(false);
203
+ focar(eixo);
204
+ onCor(com(naGrade(nx, fx), naGrade(ny, fy)), true);
205
+ };
206
+ // O motor do navegador também muda o `range` (o leitor de tela pede "aumentar"): o valor chega pelo `onChange`.
207
+ const aoMudar = (eixo) => (e) => {
208
+ setPelaTecla(true);
209
+ onCor(setChannel(cor, eixo === "x" ? canalX : canalY, Number(e.target.value), space), true);
210
+ };
211
+ const escondido = (eixo) => focado === null ? eixo === "y" : focado !== eixo;
212
+ const fundo = areaBackground(cor, space, canalX, canalY, rtl);
213
+ return _jsxs("div", { ref: caixa, role: "group", id: id, "aria-label": ariaLabel ? rotulo : undefined, "aria-labelledby": ariaLabelledby, "aria-disabled": disabled || undefined, "data-disabled": disabled ? "" : undefined, "data-dragging": arrastando ? "" : undefined, className: cx("color-area", showDots && "color-area-dots", className), style: { "--color-area-fill": fundo.background, "--color-area-blend": fundo.blend ?? "normal", "--color-area-thumb": hex }, onPointerDown: aoApertar, onPointerMove: aoMover, onPointerUp: aoSoltar, onPointerCancel: aoSoltar, onKeyDown: aoTeclar, children: [_jsx("div", { className: "color-area-thumb", "data-focus-visible": focado && !peloPonteiro ? "" : undefined, style: { insetInlineStart: `${px * 100}%`, insetBlockStart: `${(1 - py) * 100}%` }, children: ["x", "y"].map((eixo) => {
214
+ const f = eixo === "x" ? fx : fy;
215
+ return _jsx("input", { ref: eixo === "x" ? inX : inY, type: "range", className: "color-area-input", min: f.min, max: f.max, step: f.step, value: naGrade(eixo === "x" ? vx : vy, f), disabled: disabled, "aria-label": rotulo, "aria-labelledby": rotulo ? undefined : ariaLabelledby, "aria-describedby": ariaDescribedby, "aria-roledescription": s.colorArea2D, "aria-valuetext": textoDo(eixo), "aria-orientation": eixo === "x" ? "horizontal" : "vertical", tabIndex: escondido(eixo) ? -1 : undefined, "aria-hidden": escondido(eixo) && !pelaTecla ? true : undefined, onChange: aoMudar(eixo), onFocus: () => { setFocado(eixo); setPeloPonteiro(modalidade === "ponteiro"); }, onBlur: (e) => { if (!caixa.current?.contains(e.relatedTarget)) {
216
+ setFocado(null);
217
+ setPeloPonteiro(false);
218
+ } } }, eixo);
219
+ }) }), name && _jsx("input", { type: "hidden", name: name, value: corToHex(cor) })] });
220
+ }
221
+ function TrilhaDeCor({ cor, onCor, channel, colorSpace, label, labelHidden, showValue = true, orientation = "horizontal", disabled, name, className, id }) {
222
+ const s = useAureaStrings();
223
+ const rtl = useDirection() === "rtl";
224
+ const space = spaceOf(channel, colorSpace);
225
+ const { min, max, step, page } = channelRange(channel);
226
+ const v = getChannel(cor, channel, space);
227
+ const idDoRotulo = React.useId();
228
+ const fundo = trackBackground(cor, channel, space, orientation === "vertical" ? "to top" : rtl ? "to left" : "to right");
229
+ const pino = channel === "hue" ? pureHue(v) : channel === "alpha" ? corToHex(cor) : corToHex({ ...cor, a: 1 });
230
+ const falado = (n) => {
231
+ const base = `${s.colorChannel[channel]} ${formatChannel(channel, n)}`;
232
+ if (channel === "alpha")
233
+ return base;
234
+ return `${base}, ${colorName(hsbToRgb(channel === "hue" ? { h: n, s: 1, b: 1 } : setChannel(cor, channel, n, space)), s)}`;
235
+ };
236
+ return _jsxs(BaseSlider.Root, { id: id, "aria-labelledby": idDoRotulo, "aria-disabled": disabled || undefined, value: v, onValueChange: (n) => onCor(setChannel(cor, channel, n, space), false), onValueCommitted: (n) => onCor(setChannel(cor, channel, n, space), true), min: min, max: max, step: step, largeStep: page, orientation: orientation, disabled: disabled, className: cx("slider", "slider-color", channel === "alpha" && "slider-alpha", className), style: { "--slider-track-fill": fundo, "--slider-thumb-color": pino }, children: [_jsx("div", { id: idDoRotulo, className: labelHidden ? "sr-only" : "slider-label", children: label }), showValue && _jsx(BaseSlider.Value, { className: "slider-output", children: (_, valores) => formatChannel(channel, valores[0] ?? v) }), _jsx(BaseSlider.Control, { className: "slider-control", children: _jsx(BaseSlider.Track, { className: "slider-track", children: _jsx(BaseSlider.Thumb, { className: "slider-thumb", "aria-labelledby": idDoRotulo, getAriaValueText: (_f, n) => falado(n) }) }) }), name && _jsx("input", { type: "hidden", name: name, value: corToHex(cor) })] });
237
+ }
24
238
  // FileInput mora em arquivo próprio (132 linhas — upload real, aborto, progresso) e é público
25
239
  // por aqui: a categoria dele é Inputs.
26
240
  export { FileInput, matchesAccept } from "./file-input.js";
@@ -77,9 +291,11 @@ function elementoRotulavel(tipo) {
77
291
  // reprovava no axe com "Form elements must have labels". Fora desta lista o Field vira GRUPO e
78
292
  // não pendura `htmlFor`, então o <input> de dentro ficava sem nome. Ele qualifica pela mesma
79
293
  // regra que os outros: recebe `id` e espalha `...props` no `Input`, que é o nó focal.
80
- return [Input, Select, Textarea, SearchField, PasswordField, Checkbox, Radio, Switch, Range, Combobox, MultiCombobox].includes(tipo);
294
+ return [Input, Select, Textarea, SearchField, PasswordField, Checkbox, Radio, Switch, Range, Combobox, MultiCombobox, ColorField].includes(tipo)
295
+ // os que moram fora do pacote principal e se registraram (o `DatePicker`, 0.32.0)
296
+ || (typeof tipo === "function" && controlesRotulaveis.has(tipo));
81
297
  }
82
- export function Field({ label, hint, description, error, children, className, id, name, orientation, labelWidth, style, ...props }) {
298
+ export function Field({ label, hint, description, error, children, className, id, name, orientation, labelWidth, style, required, ...props }) {
83
299
  const auto = React.useId();
84
300
  // Fragment não cria nó no DOM. Um Fragment com um só controle é só sintaxe; com vários,
85
301
  // Field precisa voltar para grupo em vez de pendurar `htmlFor` num id que nunca existirá.
@@ -116,6 +332,10 @@ export function Field({ label, hint, description, error, children, className, id
116
332
  const invalidoFinal = error ? true : unico?.props["aria-invalid"];
117
333
  if (invalidoFinal !== undefined)
118
334
  injecao["aria-invalid"] = invalidoFinal;
335
+ // o `required` do controle é o que anuncia; o do filho, se ele já disse, vence
336
+ if (required && unico?.props.required === undefined)
337
+ injecao.required = true;
338
+ const asterisco = required ? _jsx("span", { className: "label-required", "aria-hidden": "true", children: "*" }) : null;
119
339
  const controle = rotulavel ? React.cloneElement(unico, injecao) : children;
120
340
  // a descrição mora no nó FOCAL quando existe um; sem filho único, no grupo — que é o que sobra
121
341
  // para anunciar. Nos dois lugares seria anunciar duas vezes.
@@ -125,7 +345,7 @@ export function Field({ label, hint, description, error, children, className, id
125
345
  // árvore de acessibilidade enquanto a frase de erro aparece na tela. Pior que não mostrar.
126
346
  const controleFinal = name ? _jsx(BaseField.Control, { render: controle }) : controle;
127
347
  const estilo = labelWidth ? { ...style, "--field-label-width": labelWidth === "auto" ? "max-content" : labelWidth } : style;
128
- const corpo = _jsxs("div", { className: cx("field", peleDoEixo("field", orientation, "vertical", "field"), className), ...(nomeAqui ? {} : { role: "group", "aria-labelledby": idRotulo, ...(rotulavel ? {} : { "aria-describedby": descrito }) }), style: estilo, ...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, description && _jsx("span", { className: "field-description", id: idDescricao, children: description }), error && _jsx("span", { className: "field-error", id: idErro, children: error }), name && _jsx(BaseField.Error, { className: "field-error" })] });
348
+ const corpo = _jsxs("div", { className: cx("field", peleDoEixo("field", orientation, "vertical", "field"), className), ...(nomeAqui ? {} : { role: "group", "aria-labelledby": idRotulo, ...(rotulavel ? {} : { "aria-describedby": descrito }) }), style: estilo, ...props, children: [_jsxs("span", { className: "label", children: [nomeAqui ? _jsxs("label", { htmlFor: idControle, id: idRotulo, children: [label, asterisco] }) : _jsxs("span", { id: idRotulo, children: [label, asterisco] }), hint && _jsx("span", { className: "hint", id: idHint, children: hint })] }), controleFinal, description && _jsx("span", { className: "field-description", id: idDescricao, children: description }), error && _jsx("span", { className: "field-error", id: idErro, children: error }), name && _jsx(BaseField.Error, { className: "field-error" })] });
129
349
  // `Field.Root` do motor entra SÓ quando há `name`: é ele que liga o campo à chave de `errors`
130
350
  // do formulário. Ele não desenha nada — é contexto — então a pele não muda.
131
351
  return name ? _jsx(BaseField.Root, { name: name, invalid: error ? true : undefined, render: corpo }) : corpo;
@@ -146,8 +366,8 @@ export function Field({ label, hint, description, error, children, className, id
146
366
  export function Form({ errors, onSubmit, children, className, ...props }) {
147
367
  return _jsx(BaseForm, { errors: errors, onFormSubmit: onSubmit ? (v) => onSubmit(v) : undefined, className: cx("form", className), ...props, children: children });
148
368
  }
149
- export const Input = forwardRef(function Input({ className, formatOnBlur, size, onBlur, onChange, ...props }, ref) {
150
- return _jsx("input", { ref: ref, className: cx("input", peleDoEixo("input", size), className), onChange: onChange, onBlur: e => {
369
+ export const Input = forwardRef(function Input({ className, formatOnBlur, size, variant, onBlur, onChange, ...props }, ref) {
370
+ return _jsx("input", { ref: ref, className: cx("input", peleDoEixo("input", size), variant === "secondary" && "input-secondary", className), onChange: onChange, onBlur: e => {
151
371
  if (formatOnBlur) {
152
372
  const formatado = formatOnBlur(e.currentTarget.value);
153
373
  if (formatado !== e.currentTarget.value) {
@@ -186,7 +406,283 @@ export function SearchField({ icon = "magnifying-glass", className, size, width,
186
406
  // é a trava do item ("o valor mascarado continua no DOM") entregue pelo motor, e o teste a cobra.
187
407
  export function NumberField({ value, defaultValue, onValueChange, min, max, step, disabled, readOnly, required, label, id, name, format, locale, className, scrubbable, scrubDirection }) {
188
408
  const s = useAureaStrings();
189
- 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: "dots-six-vertical", "aria-hidden": true }), _jsx(BaseNumberField.ScrubAreaCursor, { className: "number-field-scrub-cursor", children: _jsx(Icon, { name: "dots-six-vertical", "aria-hidden": true }) })] }), _jsx(BaseNumberField.Decrement, { className: "btn btn-ghost btn-icon", "aria-label": s.decrement, children: _jsx(Icon, { name: "minus" }) }), _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: "plus" }) })] }) });
409
+ 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: "dots-six-vertical", "aria-hidden": true }), _jsx(BaseNumberField.ScrubAreaCursor, { className: "number-field-scrub-cursor", children: _jsx(Icon, { name: "dots-six-vertical", "aria-hidden": true }) })] }), _jsx(BaseNumberField.Decrement, { className: "number-field-button number-field-decrement", "aria-label": s.decrement, children: _jsx(Icon, { name: "minus" }) }), _jsx(BaseNumberField.Input, { className: "input number-field-input", "aria-label": label }), _jsx(BaseNumberField.Increment, { className: "number-field-button number-field-increment", "aria-label": s.increment, children: _jsx(Icon, { name: "plus" }) })] }) });
410
+ }
411
+ export function CheckboxGroup({ label, description, error, value, defaultValue, onValueChange, name, disabled, required, children, className, ...props }) {
412
+ const auto = React.useId();
413
+ const [proprio, setProprio] = React.useState(defaultValue ?? []);
414
+ const marcados = value ?? proprio;
415
+ const trocar = (v, ligado) => {
416
+ const novo = ligado ? [...marcados.filter(x => x !== v), v] : marcados.filter(x => x !== v);
417
+ if (value === undefined)
418
+ setProprio(novo);
419
+ onValueChange?.(novo);
420
+ };
421
+ const idRotulo = `${auto}-label`, idDescricao = description ? `${auto}-description` : undefined, idErro = error ? `${auto}-error` : undefined;
422
+ const caixas = React.Children.map(children, filho => {
423
+ if (!React.isValidElement(filho))
424
+ return filho;
425
+ const p = filho.props;
426
+ if (typeof p.value !== "string")
427
+ return filho;
428
+ const v = p.value;
429
+ return React.cloneElement(filho, { checked: marcados.includes(v), name: name ?? filho.props.name,
430
+ disabled: p.disabled ?? disabled, ...(error ? { "aria-invalid": true } : null),
431
+ onChange: (e) => { p.onChange?.(e); trocar(v, e.currentTarget.checked); } });
432
+ });
433
+ return _jsxs("div", { role: "group", "aria-labelledby": idRotulo, "aria-describedby": [idDescricao, idErro].filter(Boolean).join(" ") || undefined, className: cx("checkbox-group", className), ...props, children: [_jsx("span", { className: "label", id: idRotulo, children: _jsxs("span", { children: [label, required ? _jsx("span", { className: "label-required", "aria-hidden": "true", children: "*" }) : null] }) }), description && _jsx("span", { className: "field-description", id: idDescricao, children: description }), caixas, error && _jsx("span", { className: "field-error", id: idErro, children: error })] });
434
+ }
435
+ const ehSecao = (x) => "items" in x;
436
+ const textoDoItem = (it) => it.textValue ?? (typeof it.label === "string" ? it.label : "");
437
+ export function ListBox({ label, items, selectionMode = "single", value, defaultValue, onValueChange, onAction, className, ...props }) {
438
+ const auto = React.useId();
439
+ const [proprio, setProprio] = React.useState(defaultValue ?? []);
440
+ const escolhidos = value ?? proprio;
441
+ const todos = React.useMemo(() => items.flatMap(x => ehSecao(x) ? x.items : [x]), [items]);
442
+ const habilitados = todos.filter(it => !it.disabled);
443
+ const [ativo, setAtivo] = React.useState(undefined);
444
+ // O item que recebe o Tab: o ativo; senão o primeiro escolhido; senão o primeiro habilitado.
445
+ const alvo = ativo ?? habilitados.find(it => escolhidos.includes(it.id))?.id ?? habilitados[0]?.id;
446
+ const refs = React.useRef(new Map());
447
+ const focar = (id) => { if (!id)
448
+ return; setAtivo(id); refs.current.get(id)?.focus(); };
449
+ const escolher = (it) => {
450
+ if (it.disabled)
451
+ return;
452
+ if (selectionMode === "none") {
453
+ onAction?.(it.id);
454
+ return;
455
+ }
456
+ const ja = escolhidos.includes(it.id);
457
+ const novo = selectionMode === "multiple" ? (ja ? escolhidos.filter(x => x !== it.id) : [...escolhidos, it.id]) : [it.id];
458
+ if (value === undefined)
459
+ setProprio(novo);
460
+ onValueChange?.(novo);
461
+ onAction?.(it.id);
462
+ };
463
+ const aoTeclar = (e, it) => {
464
+ const i = habilitados.findIndex(x => x.id === it.id);
465
+ if (e.key === "ArrowDown") {
466
+ e.preventDefault();
467
+ focar(habilitados[Math.min(i + 1, habilitados.length - 1)]?.id);
468
+ }
469
+ else if (e.key === "ArrowUp") {
470
+ e.preventDefault();
471
+ focar(habilitados[Math.max(i - 1, 0)]?.id);
472
+ }
473
+ else if (e.key === "Home") {
474
+ e.preventDefault();
475
+ focar(habilitados[0]?.id);
476
+ }
477
+ else if (e.key === "End") {
478
+ e.preventDefault();
479
+ focar(habilitados[habilitados.length - 1]?.id);
480
+ }
481
+ else if (e.key === " " || e.key === "Enter") {
482
+ e.preventDefault();
483
+ escolher(it);
484
+ }
485
+ else if (e.key.length === 1 && !e.ctrlKey && !e.metaKey && !e.altKey) {
486
+ // a primeira letra: o próximo item, depois do atual, que começa com ela (e dá a volta)
487
+ const letra = e.key.toLocaleLowerCase();
488
+ const ordem = [...habilitados.slice(i + 1), ...habilitados.slice(0, i + 1)];
489
+ const achou = ordem.find(x => textoDoItem(x).toLocaleLowerCase().startsWith(letra));
490
+ if (achou) {
491
+ e.preventDefault();
492
+ focar(achou.id);
493
+ }
494
+ }
495
+ };
496
+ const item = (it) => {
497
+ const escolhido = selectionMode !== "none" && escolhidos.includes(it.id);
498
+ const idDesc = it.description ? `${auto}-${it.id}-d` : undefined;
499
+ return _jsxs("div", { ref: n => { if (n)
500
+ refs.current.set(it.id, n);
501
+ else
502
+ refs.current.delete(it.id); }, role: "option", "aria-selected": selectionMode === "none" ? undefined : escolhido, "aria-disabled": it.disabled || undefined, "aria-describedby": idDesc, tabIndex: it.id === alvo ? 0 : -1, className: cx("list-box-item", it.danger && "list-box-item-danger"), onClick: () => { if (!it.disabled) {
503
+ setAtivo(it.id);
504
+ escolher(it);
505
+ } }, onKeyDown: e => aoTeclar(e, it), onFocus: () => setAtivo(it.id), children: [it.icon && _jsx(Icon, { name: it.icon }), _jsxs("span", { className: "list-box-item-text", children: [_jsx("span", { className: "list-box-item-label", children: it.label }), it.description && _jsx("span", { className: "list-box-item-description", id: idDesc, children: it.description })] }), escolhido && _jsx(Icon, { name: "check", className: "list-box-item-indicator" })] }, it.id);
506
+ };
507
+ return _jsx("div", { role: "listbox", "aria-label": label, "aria-multiselectable": selectionMode === "multiple" || undefined, className: cx("list-box", className), ...props, children: items.map((x, i) => ehSecao(x)
508
+ ? _jsxs("div", { role: "group", "aria-labelledby": `${auto}-s${i}`, className: "list-box-section", children: [_jsx("span", { className: "list-box-section-title", id: `${auto}-s${i}`, role: "presentation", children: x.title }), x.items.map(item)] }, `s${i}`)
509
+ : item(x)) });
510
+ }
511
+ const textoDaEtiqueta = (it) => it.textValue ?? (typeof it.label === "string" ? it.label : "");
512
+ export function TagGroup({ label, labelHidden, description, errorMessage, items, selectionMode = "none", selectedKeys, defaultSelectedKeys, onSelectionChange, onRemove, size = "md", variant = "default", className, id }) {
513
+ const s = useAureaStrings();
514
+ const auto = React.useId();
515
+ const [proprio, setProprio] = React.useState(defaultSelectedKeys ?? []);
516
+ const escolhidos = selectedKeys ?? proprio;
517
+ const escolhe = selectionMode !== "none";
518
+ const habilitadas = items.filter(it => !it.disabled);
519
+ const [ativa, setAtiva] = React.useState(undefined);
520
+ const alvo = (ativa && habilitadas.some(it => it.id === ativa) ? ativa : undefined) ?? habilitadas[0]?.id;
521
+ const refs = React.useRef(new Map());
522
+ const focar = (id) => { if (!id)
523
+ return; setAtiva(id); refs.current.get(id)?.focus(); };
524
+ const escolher = (it) => {
525
+ if (!escolhe || it.disabled)
526
+ return;
527
+ const ja = escolhidos.includes(it.id);
528
+ const novo = selectionMode === "multiple" ? (ja ? escolhidos.filter(x => x !== it.id) : [...escolhidos, it.id]) : (ja ? [] : [it.id]);
529
+ if (selectedKeys === undefined)
530
+ setProprio(novo);
531
+ onSelectionChange?.(novo);
532
+ };
533
+ const tirar = (it) => {
534
+ if (!onRemove || it.disabled)
535
+ return;
536
+ // o foco vai para a vizinha (a seguinte; na última, a anterior), e não se perde no documento
537
+ const i = habilitadas.findIndex(x => x.id === it.id);
538
+ const vizinha = habilitadas[i + 1] ?? habilitadas[i - 1];
539
+ onRemove([it.id]);
540
+ if (vizinha)
541
+ requestAnimationFrame(() => focar(vizinha.id));
542
+ };
543
+ const aoTeclar = (e, it) => {
544
+ const i = habilitadas.findIndex(x => x.id === it.id);
545
+ const rtl = getComputedStyle(e.currentTarget).direction === "rtl";
546
+ const frente = rtl ? "ArrowLeft" : "ArrowRight", tras = rtl ? "ArrowRight" : "ArrowLeft";
547
+ if (e.key === frente || e.key === "ArrowDown") {
548
+ e.preventDefault();
549
+ focar(habilitadas[Math.min(i + 1, habilitadas.length - 1)]?.id);
550
+ }
551
+ else if (e.key === tras || e.key === "ArrowUp") {
552
+ e.preventDefault();
553
+ focar(habilitadas[Math.max(i - 1, 0)]?.id);
554
+ }
555
+ else if (e.key === "Home") {
556
+ e.preventDefault();
557
+ focar(habilitadas[0]?.id);
558
+ }
559
+ else if (e.key === "End") {
560
+ e.preventDefault();
561
+ focar(habilitadas[habilitadas.length - 1]?.id);
562
+ }
563
+ else if ((e.key === "Delete" || e.key === "Backspace") && onRemove) {
564
+ e.preventDefault();
565
+ tirar(it);
566
+ }
567
+ };
568
+ const guardar = (itId) => (n) => { if (n)
569
+ refs.current.set(itId, n);
570
+ else
571
+ refs.current.delete(itId); };
572
+ const idRotulo = `${auto}-l`, idDesc = description ? `${auto}-d` : undefined, idErro = errorMessage ? `${auto}-e` : undefined;
573
+ return _jsxs("div", { className: cx("tag-group", className), id: id, children: [_jsx("span", { className: labelHidden ? "sr-only" : "label", id: idRotulo, children: label }), _jsx("div", { role: escolhe ? "group" : "list", "aria-labelledby": idRotulo, "aria-describedby": [idDesc, idErro].filter(Boolean).join(" ") || undefined, className: "tag-group-list", children: items.map(it => {
574
+ const escolhida = escolhe && escolhidos.includes(it.id);
575
+ const conteudo = _jsxs(_Fragment, { children: [it.icon && _jsx(Icon, { name: it.icon }), _jsx("span", { className: "tag-label", children: it.label })] });
576
+ const x = onRemove && _jsx("button", { type: "button", className: "tag-remove", disabled: it.disabled, tabIndex: escolhe ? -1 : it.id === alvo ? 0 : -1, ref: escolhe ? undefined : guardar(it.id), "aria-label": `${s.comboboxRemove} ${textoDaEtiqueta(it)}`.trim(), onClick: () => tirar(it), onKeyDown: escolhe ? undefined : e => aoTeclar(e, it), onFocus: escolhe ? undefined : () => setAtiva(it.id), children: _jsx(Icon, { name: "x", weight: "bold" }) });
577
+ return _jsxs("span", { role: escolhe ? undefined : "listitem", className: cx("tag", size !== "md" && `tag-${size}`, variant !== "default" && `tag-${variant}`), "data-selected": escolhida || undefined, "data-disabled": it.disabled || undefined, children: [escolhe
578
+ ? _jsx("button", { type: "button", className: "tag-main", ref: guardar(it.id), "aria-pressed": escolhida, disabled: it.disabled, tabIndex: it.id === alvo ? 0 : -1, onClick: () => { setAtiva(it.id); escolher(it); }, onKeyDown: e => aoTeclar(e, it), onFocus: () => setAtiva(it.id), children: conteudo })
579
+ : _jsx("span", { className: "tag-main", children: conteudo }), x] }, it.id);
580
+ }) }), description && _jsx("span", { className: "field-description", id: idDesc, children: description }), errorMessage && _jsx("span", { className: "field-error", id: idErro, children: errorMessage })] });
581
+ }
582
+ export function Slider(props) {
583
+ return props.channel ? _jsx(SliderDeCor, { ...props }) : _jsx(SliderDeNumero, { ...props });
584
+ }
585
+ function SliderDeCor({ value, defaultValue, onValueChange, onValueCommitted, ...resto }) {
586
+ const [cor, mudar] = useCor(value, defaultValue, onValueChange, onValueCommitted);
587
+ return _jsx(TrilhaDeCor, { ...resto, cor: cor, onCor: mudar });
588
+ }
589
+ function SliderDeNumero({ label, labelHidden, value, defaultValue, onValueChange, onValueCommitted, min, max, step, largeStep, orientation = "horizontal", disabled, name, format, locale, showValue = true, thumbLabels, className, id }) {
590
+ // Quantas alças: tantas quantos valores houver (a faixa é uma lista).
591
+ const atual = value ?? defaultValue;
592
+ const alcas = Array.isArray(atual) ? atual.length : 1;
593
+ // O NOME DA ALÇA SAI NA MARCAÇÃO. O motor liga o `<input>` de cada alça ao rótulo por um id que ele
594
+ // só conhece DEPOIS de montar — no HTML do servidor (e no catálogo, que é HTML parado) as alças saíam
595
+ // sem nome, e o axe do catálogo acusou as cinco. O `Slider.Label` do motor não aceita id, então o
596
+ // rótulo é um `<div>` nosso, com id, que nomeia o grupo e cada alça — o nome já vem no HTML. E o desligado vai também para o grupo (`aria-disabled`): o motor só marca o `<input>`, e a
597
+ // letra esmaecida do rótulo e do valor era lida como texto de contraste baixo, e não como peça desligada.
598
+ const idDoRotulo = React.useId();
599
+ return _jsxs(BaseSlider.Root, { id: id, name: name, "aria-labelledby": idDoRotulo, "aria-disabled": disabled || undefined, value: value, defaultValue: defaultValue, onValueChange: onValueChange ? (v) => onValueChange(v) : undefined, onValueCommitted: onValueCommitted ? (v) => onValueCommitted(v) : undefined, min: min, max: max, step: step, largeStep: largeStep, orientation: orientation, disabled: disabled, format: format, locale: locale, className: cx("slider", className), children: [_jsx("div", { id: idDoRotulo, className: labelHidden ? "sr-only" : "slider-label", children: label }), showValue && _jsx(BaseSlider.Value, { className: "slider-output" }), _jsx(BaseSlider.Control, { className: "slider-control", children: _jsxs(BaseSlider.Track, { className: "slider-track", children: [_jsx(BaseSlider.Indicator, { className: "slider-fill" }), Array.from({ length: alcas }, (_, i) => _jsx(BaseSlider.Thumb, { index: i, className: "slider-thumb", getAriaLabel: thumbLabels ? () => thumbLabels[i] ?? "" : undefined, "aria-labelledby": thumbLabels ? undefined : idDoRotulo }, i))] }) })] });
600
+ }
601
+ const EIXOS_PADRAO = { hsb: ["saturation", "brightness"], hsl: ["saturation", "lightness"], rgb: ["red", "green"] };
602
+ export function ColorArea({ value, defaultValue, onValueChange, onValueCommitted, colorSpace, xChannel, yChannel, ...resto }) {
603
+ const [cor, mudar] = useCor(value, defaultValue, onValueChange, onValueCommitted);
604
+ const space = espacoDosCanais(colorSpace, ...[xChannel, yChannel].filter((c) => !!c));
605
+ const [x, y] = [xChannel ?? EIXOS_PADRAO[space][0], yChannel ?? EIXOS_PADRAO[space][1]];
606
+ return _jsx(AreaDeCor, { ...resto, cor: cor, onCor: mudar, space: space, x: x, y: y });
607
+ }
608
+ export function ColorField({ value, defaultValue, onValueChange, onValueCommitted, channel, colorSpace, swatch, size, variant, width, disabled, readOnly, required, name, placeholder, id, className, ...aria }) {
609
+ const s = useAureaStrings();
610
+ const doSeletor = React.useContext(CorDoSeletor);
611
+ const [cor, mudar] = useCor(value === "" ? undefined : value, defaultValue, onValueChange, onValueCommitted);
612
+ // Vazio: o valor "" de fora, ou sem valor nenhum e ainda não escrito (o campo da referência começa vazio).
613
+ const [escrito, setEscrito] = React.useState(defaultValue !== undefined);
614
+ const vazio = !doSeletor && (value !== undefined ? value === "" : !escrito);
615
+ const [rascunho, setRascunho] = React.useState(null);
616
+ const canal = channel;
617
+ const space = canal ? spaceOf(canal, colorSpace) : "hsb";
618
+ const faixa = canal ? channelRange(canal) : null;
619
+ const v = canal ? getChannel(cor, canal, space) : 0;
620
+ const mostrado = vazio ? "" : canal ? formatChannel(canal, v) : corToHex(cor);
621
+ const confirmar = () => {
622
+ if (rascunho === null)
623
+ return;
624
+ const t = rascunho.trim();
625
+ setRascunho(null);
626
+ if (t === "") {
627
+ if (canal)
628
+ return;
629
+ setEscrito(false);
630
+ onValueChange?.("");
631
+ onValueCommitted?.("");
632
+ return;
633
+ }
634
+ if (canal) {
635
+ const n = Number(t.replace(/[°%\s]/g, "").replace(",", "."));
636
+ if (!Number.isFinite(n))
637
+ return;
638
+ mudar(setChannel(cor, canal, canal === "alpha" && (n > 1 || /%/.test(t)) ? n / 100 : n, space), true);
639
+ return;
640
+ }
641
+ const nova = parseColor(t);
642
+ if (!nova)
643
+ return;
644
+ setEscrito(true);
645
+ mudar(corDoTexto(t, cor), true);
646
+ };
647
+ const aoTeclar = (e) => {
648
+ if (e.key === "Enter") {
649
+ e.preventDefault();
650
+ confirmar();
651
+ return;
652
+ }
653
+ if (!canal || !faixa || disabled || readOnly)
654
+ return;
655
+ const passo = e.key === "ArrowUp" ? faixa.step : e.key === "ArrowDown" ? -faixa.step : e.key === "PageUp" ? faixa.page : e.key === "PageDown" ? -faixa.page : 0;
656
+ const alvo = e.key === "Home" ? faixa.min : e.key === "End" ? faixa.max : passo ? v + passo : null;
657
+ if (alvo === null)
658
+ return;
659
+ e.preventDefault();
660
+ setRascunho(null);
661
+ mudar(setChannel(cor, canal, Math.round(alvo / faixa.step) * faixa.step, space), true);
662
+ };
663
+ const comAmostra = swatch ?? !canal;
664
+ return _jsxs(InputGroup, { className: className, width: width, children: [comAmostra && _jsx(InputGroupAddon, { children: _jsx(ColorSwatch, { color: vazio ? "transparent" : corToHex(cor), size: "sm", "aria-hidden": "true" }) }), _jsx(Input, { id: id, size: size, variant: variant, value: rascunho ?? mostrado, placeholder: placeholder, disabled: disabled, readOnly: readOnly, required: required, spellCheck: false, autoComplete: "off", inputMode: canal ? "decimal" : undefined, role: canal ? "spinbutton" : undefined, "aria-valuenow": canal ? v : undefined, "aria-valuemin": faixa?.min, "aria-valuemax": faixa?.max, "aria-valuetext": canal ? `${formatChannel(canal, v)}, ${colorName(hsbToRgb(cor), s)}` : undefined, ...aria, onChange: (e) => setRascunho(e.target.value), onBlur: confirmar, onKeyDown: aoTeclar }), name && _jsx("input", { type: "hidden", name: name, value: vazio ? "" : corToHex(cor) })] });
665
+ }
666
+ export function ColorPicker({ label, value, defaultValue, onValueChange, onValueCommitted, swatches, variant = "full", children, disabled, name, id, className }) {
667
+ const s = useAureaStrings();
668
+ const portal = usePortalContainer();
669
+ const idGrupo = React.useId();
670
+ const [cor, mudar] = useCor(value, defaultValue, onValueChange, onValueCommitted);
671
+ const hex = corToHex(cor), nome = colorName(hsbToRgb(cor), s);
672
+ const amostras = swatches?.map((o) => {
673
+ const op = typeof o === "string" ? { color: o } : o;
674
+ const c = parseColor(op.color);
675
+ return { color: op.color, label: op.label ?? (c ? colorName(hsbToRgb(c), s) : op.color) };
676
+ });
677
+ const escolhida = amostras?.find((o) => { const c = parseColor(o.color); return c !== null && corToHex(c) === hex; })?.color ?? "";
678
+ const contexto = React.useMemo(() => ({ cor, mudar }), [cor, mudar]);
679
+ // O seletor escuta o teclado e o ponteiro desde que MONTA: a área só monta ao abrir, DEPOIS do clique que abriu, e
680
+ // a primeira abertura com o mouse acendia o anel (medido na bancada em 14/10/2026; o teste abria pelo teclado antes).
681
+ React.useEffect(ouvirModalidade, []);
682
+ // O foco fica PRESO na janela (`trap-focus`), como na referência: com quatro peças dentro, o Tab que saía depois
683
+ // do código fechava a janela (medido no navegador). O motor pede um botão de fechar dentro, para o leitor de tela
684
+ // por toque sair; ele fica invisível e fora do Tab — quem usa teclado tem o Esc, e o foco nunca some na tela.
685
+ return _jsxs(BasePopover.Root, { modal: "trap-focus", children: [_jsxs(BasePopover.Trigger, { disabled: disabled, id: id, "aria-label": `${label}, ${nome}`, className: cx("color-picker-trigger", className), children: [_jsx(ColorSwatch, { color: hex, "aria-hidden": "true" }), _jsx("span", { children: label })] }), name && _jsx("input", { type: "hidden", name: name, value: hex }), _jsx(BasePopover.Portal, { container: portal, children: _jsx(BasePopover.Positioner, { side: "bottom", align: "start", sideOffset: 6, children: _jsxs(BasePopover.Popup, { className: "popover color-picker-popover", "aria-label": label, children: [_jsx(CorDoSeletor.Provider, { value: contexto, children: children ?? _jsxs(_Fragment, { children: [variant !== "swatches" && _jsxs(_Fragment, { children: [_jsx(AreaDeCor, { cor: cor, onCor: mudar, space: "hsb", x: "saturation", y: "brightness", "aria-label": label }), _jsx(TrilhaDeCor, { cor: cor, onCor: mudar, channel: "hue", label: s.colorChannel.hue, labelHidden: true, showValue: false })] }), amostras && _jsx(ColorSwatch, { options: amostras, name: `${idGrupo}-amostras`, label: s.colorSwatches, value: escolhida, onChange: (e) => mudar(corDoTexto(e.target.value, cor), true) }), _jsx(ColorField, { "aria-label": s.colorHex })] }) }), _jsx(BasePopover.Close, { className: "sr-only", tabIndex: -1, children: s.close })] }) }) })] });
190
686
  }
191
687
  // OTPField (Lote 1 do BUILDING.md). Um campo por dígito, com o comportamento que ninguém acerta
192
688
  // à mão: colar o código inteiro distribui pelos campos, Backspace volta um, e o
package/dist/inputs.d.ts CHANGED
@@ -1,3 +1,3 @@
1
1
  export * from "./inputs-client.js";
2
2
  export { Textarea, Checkbox, Radio, Switch, Range } from "./markup.js";
3
- export { Label, InputGroup, InputGroupAddon, type AddonSide, type AddonLayout, type FieldSize } from "./markup.js";
3
+ export { Label, InputGroup, InputGroupAddon, type AddonSide, type AddonLayout, type FieldSize, type FieldVariant } from "./markup.js";
@@ -1,6 +1,7 @@
1
1
  import React from "react";
2
2
  import { type AureaStrings, type AureaTheme, type AureaDensity } from "./pure.js";
3
3
  export { cx, fundirRender, defaultStrings, ptBR, defaultSpriteUrl, universalStates, stateSeverity, type AureaStrings, type UniversalState, type AureaTheme, type AureaDensity } from "./pure.js";
4
+ export declare const controlesRotulaveis: WeakSet<object>;
4
5
  export declare const StringsContext: React.Context<AureaStrings>;
5
6
  export declare const useAureaStrings: () => AureaStrings;
6
7
  export declare const SpriteContext: React.Context<string>;
package/dist/internal.js CHANGED
@@ -18,6 +18,10 @@ import { createContext, useContext, useEffect, useRef, useState } from "react";
18
18
  import { Toast as BaseToast } from "@base-ui/react/toast";
19
19
  import { defaultStrings, defaultSpriteUrl } from "./pure.js";
20
20
  export { cx, fundirRender, defaultStrings, ptBR, defaultSpriteUrl, universalStates, stateSeverity } from "./pure.js";
21
+ // Os controles da Aurea que moram FORA do pacote principal (o `DatePicker`, em `./calendar`, 0.32.0) e
22
+ // entregam o `id` ao nó focal. O `Field` os nomeia com `<label for>`, como os da lista dele; eles se
23
+ // registram aqui ao carregar, porque importar cada um no `Field` puxaria o motor deles para todo app.
24
+ export const controlesRotulaveis = new WeakSet();
21
25
  export const StringsContext = createContext(defaultStrings);
22
26
  export const useAureaStrings = () => useContext(StringsContext);
23
27
  export const SpriteContext = createContext(defaultSpriteUrl);
@@ -1,7 +1,7 @@
1
1
  import { type HTMLAttributes, type RefAttributes, type ReactNode } from "react";
2
2
  import { type Orientation, type Responsive } from "./pure.js";
3
- import { type HeaderVariant, type SidebarVariant, type SidebarItem } from "./navigation.js";
4
- export declare function AppShell({ brand, navigation, navItems, currentNavId, navLabel, topbar, topbarVariant, topbarDivider, sidebarVariant, sidebarCollapsed, sidebarCollapsible, defaultSidebarCollapsed, onSidebarCollapsedChange, contentVariant, children, className, ...props }: HTMLAttributes<HTMLDivElement> & RefAttributes<HTMLDivElement> & {
3
+ import { type HeaderVariant, type SidebarVariant, type SidebarScrollbar, type SidebarItem } from "./navigation.js";
4
+ export declare function AppShell({ brand, navigation, navItems, currentNavId, navLabel, topbar, topbarVariant, topbarDivider, sidebarVariant, sidebarScrollbar, sidebarCollapsed, sidebarCollapsible, defaultSidebarCollapsed, onSidebarCollapsedChange, contentVariant, aside, asideLabel, asideVariant, children, className, ...props }: HTMLAttributes<HTMLDivElement> & RefAttributes<HTMLDivElement> & {
5
5
  brand: ReactNode;
6
6
  navigation?: ReactNode;
7
7
  navItems?: SidebarItem[];
@@ -11,6 +11,18 @@ export declare function AppShell({ brand, navigation, navItems, currentNavId, na
11
11
  topbarVariant?: Exclude<HeaderVariant, "pill">;
12
12
  topbarDivider?: boolean;
13
13
  sidebarVariant?: SidebarVariant;
14
+ /** 0.32.0: a barra de rolagem da lateral da esquerda — o `scrollbar` da `Sidebar`. Padrão `hover` (clara, só com o
15
+ * ponteiro em cima); `visible` é a barra sempre à vista. */
16
+ sidebarScrollbar?: SidebarScrollbar;
17
+ /** 0.32.0 (DOC-01): a coluna da DIREITA — o "Nesta página" de uma documentação (um `TableOfContents`).
18
+ * Mora numa `Sidebar placement="right"` do mesmo desenho da da esquerda. De 1280 para cima é a
19
+ * terceira coluna; abaixo, desce para depois do conteúdo. */
20
+ aside?: ReactNode;
21
+ /** O nome da coluna da direita para o leitor de tela. Padrão: a frase `tocLabel` ("On this page"). */
22
+ asideLabel?: string;
23
+ /** 0.32.0: a variante da coluna da direita. Padrão: a mesma da esquerda (`sidebarVariant`). `plain`
24
+ * tira a caixa — os itens soltos na página, como no "On this page" da documentação da referência. */
25
+ asideVariant?: SidebarVariant;
14
26
  /** A lateral em trilha de ícones. Controlado: quem passa decide (e o `onSidebarCollapsedChange` avisa). */
15
27
  sidebarCollapsed?: boolean;
16
28
  /** AN-01: o botão de recolher na junção do menu com o conteúdo, e a trilha sozinha entre 1024 e 1279 de largura. */