@aurea-uds/react 0.31.0 → 0.33.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -3
- package/dist/actions.d.ts +48 -13
- package/dist/actions.js +64 -31
- package/dist/agents.js +1 -1
- package/dist/calendar.d.ts +38 -1
- package/dist/calendar.js +115 -2
- package/dist/code-client.d.ts +3 -1
- package/dist/code-client.js +23 -1
- package/dist/color.d.ts +92 -0
- package/dist/color.js +293 -0
- package/dist/disclosure.d.ts +19 -9
- package/dist/disclosure.js +8 -1
- package/dist/feedback-client.d.ts +14 -6
- package/dist/feedback-client.js +29 -16
- package/dist/graph-worker.d.ts +1 -0
- package/dist/graph-worker.js +17 -0
- package/dist/graph.d.ts +11 -1
- package/dist/graph.js +214 -19
- package/dist/icon-names.d.ts +7 -1
- package/dist/icon-names.js +2 -0
- package/dist/identity-client.d.ts +6 -1
- package/dist/identity-client.js +2 -5
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/inputs-client.d.ts +245 -3
- package/dist/inputs-client.js +541 -14
- package/dist/inputs.d.ts +1 -1
- package/dist/internal.d.ts +7 -4
- package/dist/internal.js +6 -1
- package/dist/layout-client.d.ts +14 -2
- package/dist/layout-client.js +2 -2
- package/dist/markup.d.ts +135 -15
- package/dist/markup.js +87 -17
- package/dist/navigation-client.d.ts +38 -10
- package/dist/navigation-client.js +77 -21
- package/dist/overlays.d.ts +20 -6
- package/dist/overlays.js +70 -15
- package/dist/pure.d.ts +27 -0
- package/dist/pure.js +2 -2
- package/dist/system.d.ts +12 -6
- package/dist/system.js +52 -13
- package/package.json +8 -2
package/dist/inputs-client.js
CHANGED
|
@@ -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 {
|
|
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, Kbd } 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 ?
|
|
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) {
|
|
@@ -163,7 +383,8 @@ export const Input = forwardRef(function Input({ className, formatOnBlur, size,
|
|
|
163
383
|
// `.input-wrap-end` para o botão do PasswordField —, e o grupo é a terceira vez virando uma. O
|
|
164
384
|
// merge das duas linhagens tinha trazido de volta a versão com `.input-wrap`, contra um core
|
|
165
385
|
// que já não a pinta.
|
|
166
|
-
|
|
386
|
+
// `search-field` (15/10/2026): a busca tem a medida própria da referência — 12, a lupa, 8 (ver o core).
|
|
387
|
+
export function SearchField({ icon = "magnifying-glass", className, size, width, ...props }) { return _jsxs(InputGroup, { className: cx("search-field", className), width: width, children: [_jsx(InputGroupAddon, { children: _jsx(Icon, { name: icon, size: valorBase(size) === "sm" ? "sm" : undefined }) }), _jsx(Input, { type: "search", size: size, ...props })] }); }
|
|
167
388
|
// labelHidden: coluna de seleção de tabela precisa do rótulo POR LINHA para o leitor de
|
|
168
389
|
// tela ("Select Analyst"), mas mostrá-lo engorda a coluna. O rótulo continua no DOM, só
|
|
169
390
|
// sai da tela (.sr-only) — nunca trocar por aria-label solto num <label> visível vazio.
|
|
@@ -186,7 +407,310 @@ export function SearchField({ icon = "magnifying-glass", className, size, width,
|
|
|
186
407
|
// é a trava do item ("o valor mascarado continua no DOM") entregue pelo motor, e o teste a cobra.
|
|
187
408
|
export function NumberField({ value, defaultValue, onValueChange, min, max, step, disabled, readOnly, required, label, id, name, format, locale, className, scrubbable, scrubDirection }) {
|
|
188
409
|
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: "
|
|
410
|
+
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" }) })] }) });
|
|
411
|
+
}
|
|
412
|
+
export function CheckboxGroup({ label, description, error, value, defaultValue, onValueChange, name, disabled, required, children, className, ...props }) {
|
|
413
|
+
const auto = React.useId();
|
|
414
|
+
const [proprio, setProprio] = React.useState(defaultValue ?? []);
|
|
415
|
+
const marcados = value ?? proprio;
|
|
416
|
+
const trocar = (v, ligado) => {
|
|
417
|
+
const novo = ligado ? [...marcados.filter(x => x !== v), v] : marcados.filter(x => x !== v);
|
|
418
|
+
if (value === undefined)
|
|
419
|
+
setProprio(novo);
|
|
420
|
+
onValueChange?.(novo);
|
|
421
|
+
};
|
|
422
|
+
const idRotulo = `${auto}-label`, idDescricao = description ? `${auto}-description` : undefined, idErro = error ? `${auto}-error` : undefined;
|
|
423
|
+
const caixas = React.Children.map(children, filho => {
|
|
424
|
+
if (!React.isValidElement(filho))
|
|
425
|
+
return filho;
|
|
426
|
+
const p = filho.props;
|
|
427
|
+
if (typeof p.value !== "string")
|
|
428
|
+
return filho;
|
|
429
|
+
const v = p.value;
|
|
430
|
+
return React.cloneElement(filho, { checked: marcados.includes(v), name: name ?? filho.props.name,
|
|
431
|
+
disabled: p.disabled ?? disabled, ...(error ? { "aria-invalid": true } : null),
|
|
432
|
+
onChange: (e) => { p.onChange?.(e); trocar(v, e.currentTarget.checked); } });
|
|
433
|
+
});
|
|
434
|
+
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 })] });
|
|
435
|
+
}
|
|
436
|
+
export function RadioGroup({ label, labelHidden, description, error, value, defaultValue, onValueChange, name, disabled, invalid, orientation = "vertical", required, children, className, ...props }) {
|
|
437
|
+
const auto = React.useId();
|
|
438
|
+
const [proprio, setProprio] = React.useState(defaultValue);
|
|
439
|
+
const escolhido = value ?? proprio;
|
|
440
|
+
const idRotulo = `${auto}-label`, idDescricao = description ? `${auto}-description` : undefined, idErro = error ? `${auto}-error` : undefined;
|
|
441
|
+
const opcoes = React.Children.map(children, filho => {
|
|
442
|
+
if (!React.isValidElement(filho))
|
|
443
|
+
return filho;
|
|
444
|
+
const p = filho.props;
|
|
445
|
+
if (typeof p.value !== "string")
|
|
446
|
+
return filho;
|
|
447
|
+
const v = p.value;
|
|
448
|
+
return React.cloneElement(filho, { checked: escolhido === v, name: name ?? auto, required,
|
|
449
|
+
disabled: p.disabled ?? disabled, ...(error || invalid ? { "aria-invalid": true } : null),
|
|
450
|
+
onChange: (e) => {
|
|
451
|
+
p.onChange?.(e);
|
|
452
|
+
if (!e.currentTarget.checked)
|
|
453
|
+
return;
|
|
454
|
+
if (value === undefined)
|
|
455
|
+
setProprio(v);
|
|
456
|
+
onValueChange?.(v);
|
|
457
|
+
} });
|
|
458
|
+
});
|
|
459
|
+
return _jsxs("div", { role: "radiogroup", "aria-labelledby": idRotulo, "aria-describedby": [idDescricao, idErro].filter(Boolean).join(" ") || undefined, "aria-orientation": orientation, "aria-required": required || undefined, "aria-invalid": error || invalid ? true : undefined, className: cx("radio-group", orientation === "horizontal" && "radio-group-horizontal", labelHidden && "radio-group-bare", className), ...props, children: [_jsx("span", { className: cx("label", labelHidden && "sr-only"), 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 }), orientation === "horizontal" ? _jsx("div", { className: "radio-group-items", children: opcoes }) : opcoes, error && _jsx("span", { className: "field-error", id: idErro, children: error })] });
|
|
460
|
+
}
|
|
461
|
+
/** A opção do grupo: o `Radio`, como o `RadioGroup.Item` do nativo. */
|
|
462
|
+
RadioGroup.Item = Radio;
|
|
463
|
+
const ehSecao = (x) => "items" in x;
|
|
464
|
+
const textoDoItem = (it) => it.textValue ?? (typeof it.label === "string" ? it.label : "");
|
|
465
|
+
export function ListBox({ label, items, selectionMode = "single", value, defaultValue, onValueChange, onAction, className, ...props }) {
|
|
466
|
+
const auto = React.useId();
|
|
467
|
+
const [proprio, setProprio] = React.useState(defaultValue ?? []);
|
|
468
|
+
const escolhidos = value ?? proprio;
|
|
469
|
+
const todos = React.useMemo(() => items.flatMap(x => ehSecao(x) ? x.items : [x]), [items]);
|
|
470
|
+
const habilitados = todos.filter(it => !it.disabled);
|
|
471
|
+
const [ativo, setAtivo] = React.useState(undefined);
|
|
472
|
+
// O item que recebe o Tab: o ativo; senão o primeiro escolhido; senão o primeiro habilitado.
|
|
473
|
+
const alvo = ativo ?? habilitados.find(it => escolhidos.includes(it.id))?.id ?? habilitados[0]?.id;
|
|
474
|
+
const refs = React.useRef(new Map());
|
|
475
|
+
const focar = (id) => { if (!id)
|
|
476
|
+
return; setAtivo(id); refs.current.get(id)?.focus(); };
|
|
477
|
+
const escolher = (it) => {
|
|
478
|
+
if (it.disabled)
|
|
479
|
+
return;
|
|
480
|
+
if (selectionMode === "none") {
|
|
481
|
+
onAction?.(it.id);
|
|
482
|
+
return;
|
|
483
|
+
}
|
|
484
|
+
const ja = escolhidos.includes(it.id);
|
|
485
|
+
const novo = selectionMode === "multiple" ? (ja ? escolhidos.filter(x => x !== it.id) : [...escolhidos, it.id]) : [it.id];
|
|
486
|
+
if (value === undefined)
|
|
487
|
+
setProprio(novo);
|
|
488
|
+
onValueChange?.(novo);
|
|
489
|
+
onAction?.(it.id);
|
|
490
|
+
};
|
|
491
|
+
const aoTeclar = (e, it) => {
|
|
492
|
+
const i = habilitados.findIndex(x => x.id === it.id);
|
|
493
|
+
if (e.key === "ArrowDown") {
|
|
494
|
+
e.preventDefault();
|
|
495
|
+
focar(habilitados[Math.min(i + 1, habilitados.length - 1)]?.id);
|
|
496
|
+
}
|
|
497
|
+
else if (e.key === "ArrowUp") {
|
|
498
|
+
e.preventDefault();
|
|
499
|
+
focar(habilitados[Math.max(i - 1, 0)]?.id);
|
|
500
|
+
}
|
|
501
|
+
else if (e.key === "Home") {
|
|
502
|
+
e.preventDefault();
|
|
503
|
+
focar(habilitados[0]?.id);
|
|
504
|
+
}
|
|
505
|
+
else if (e.key === "End") {
|
|
506
|
+
e.preventDefault();
|
|
507
|
+
focar(habilitados[habilitados.length - 1]?.id);
|
|
508
|
+
}
|
|
509
|
+
else if (e.key === " " || e.key === "Enter") {
|
|
510
|
+
e.preventDefault();
|
|
511
|
+
escolher(it);
|
|
512
|
+
}
|
|
513
|
+
else if (e.key.length === 1 && !e.ctrlKey && !e.metaKey && !e.altKey) {
|
|
514
|
+
// a primeira letra: o próximo item, depois do atual, que começa com ela (e dá a volta)
|
|
515
|
+
const letra = e.key.toLocaleLowerCase();
|
|
516
|
+
const ordem = [...habilitados.slice(i + 1), ...habilitados.slice(0, i + 1)];
|
|
517
|
+
const achou = ordem.find(x => textoDoItem(x).toLocaleLowerCase().startsWith(letra));
|
|
518
|
+
if (achou) {
|
|
519
|
+
e.preventDefault();
|
|
520
|
+
focar(achou.id);
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
};
|
|
524
|
+
const item = (it) => {
|
|
525
|
+
const escolhido = selectionMode !== "none" && escolhidos.includes(it.id);
|
|
526
|
+
const idDesc = it.description ? `${auto}-${it.id}-d` : undefined;
|
|
527
|
+
return _jsxs("div", { ref: n => { if (n)
|
|
528
|
+
refs.current.set(it.id, n);
|
|
529
|
+
else
|
|
530
|
+
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) {
|
|
531
|
+
setAtivo(it.id);
|
|
532
|
+
escolher(it);
|
|
533
|
+
} }, 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 })] }), it.kbd && _jsx(Kbd, { className: "list-box-item-kbd", children: it.kbd }), escolhido && _jsx(Icon, { name: "check", className: "list-box-item-indicator" })] }, it.id);
|
|
534
|
+
};
|
|
535
|
+
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)
|
|
536
|
+
? _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}`)
|
|
537
|
+
: item(x)) });
|
|
538
|
+
}
|
|
539
|
+
const textoDaEtiqueta = (it) => it.textValue ?? (typeof it.label === "string" ? it.label : "");
|
|
540
|
+
export function TagGroup({ label, labelHidden, description, errorMessage, items, selectionMode = "none", selectedKeys, defaultSelectedKeys, onSelectionChange, onRemove, size = "md", variant = "default", className, id }) {
|
|
541
|
+
const s = useAureaStrings();
|
|
542
|
+
const auto = React.useId();
|
|
543
|
+
const [proprio, setProprio] = React.useState(defaultSelectedKeys ?? []);
|
|
544
|
+
const escolhidos = selectedKeys ?? proprio;
|
|
545
|
+
const escolhe = selectionMode !== "none";
|
|
546
|
+
const habilitadas = items.filter(it => !it.disabled);
|
|
547
|
+
const [ativa, setAtiva] = React.useState(undefined);
|
|
548
|
+
const alvo = (ativa && habilitadas.some(it => it.id === ativa) ? ativa : undefined) ?? habilitadas[0]?.id;
|
|
549
|
+
const refs = React.useRef(new Map());
|
|
550
|
+
const focar = (id) => { if (!id)
|
|
551
|
+
return; setAtiva(id); refs.current.get(id)?.focus(); };
|
|
552
|
+
const escolher = (it) => {
|
|
553
|
+
if (!escolhe || it.disabled)
|
|
554
|
+
return;
|
|
555
|
+
const ja = escolhidos.includes(it.id);
|
|
556
|
+
const novo = selectionMode === "multiple" ? (ja ? escolhidos.filter(x => x !== it.id) : [...escolhidos, it.id]) : (ja ? [] : [it.id]);
|
|
557
|
+
if (selectedKeys === undefined)
|
|
558
|
+
setProprio(novo);
|
|
559
|
+
onSelectionChange?.(novo);
|
|
560
|
+
};
|
|
561
|
+
const tirar = (it) => {
|
|
562
|
+
if (!onRemove || it.disabled)
|
|
563
|
+
return;
|
|
564
|
+
// o foco vai para a vizinha (a seguinte; na última, a anterior), e não se perde no documento
|
|
565
|
+
const i = habilitadas.findIndex(x => x.id === it.id);
|
|
566
|
+
const vizinha = habilitadas[i + 1] ?? habilitadas[i - 1];
|
|
567
|
+
onRemove([it.id]);
|
|
568
|
+
if (vizinha)
|
|
569
|
+
requestAnimationFrame(() => focar(vizinha.id));
|
|
570
|
+
};
|
|
571
|
+
const aoTeclar = (e, it) => {
|
|
572
|
+
const i = habilitadas.findIndex(x => x.id === it.id);
|
|
573
|
+
const rtl = getComputedStyle(e.currentTarget).direction === "rtl";
|
|
574
|
+
const frente = rtl ? "ArrowLeft" : "ArrowRight", tras = rtl ? "ArrowRight" : "ArrowLeft";
|
|
575
|
+
if (e.key === frente || e.key === "ArrowDown") {
|
|
576
|
+
e.preventDefault();
|
|
577
|
+
focar(habilitadas[Math.min(i + 1, habilitadas.length - 1)]?.id);
|
|
578
|
+
}
|
|
579
|
+
else if (e.key === tras || e.key === "ArrowUp") {
|
|
580
|
+
e.preventDefault();
|
|
581
|
+
focar(habilitadas[Math.max(i - 1, 0)]?.id);
|
|
582
|
+
}
|
|
583
|
+
else if (e.key === "Home") {
|
|
584
|
+
e.preventDefault();
|
|
585
|
+
focar(habilitadas[0]?.id);
|
|
586
|
+
}
|
|
587
|
+
else if (e.key === "End") {
|
|
588
|
+
e.preventDefault();
|
|
589
|
+
focar(habilitadas[habilitadas.length - 1]?.id);
|
|
590
|
+
}
|
|
591
|
+
else if ((e.key === "Delete" || e.key === "Backspace") && onRemove) {
|
|
592
|
+
e.preventDefault();
|
|
593
|
+
tirar(it);
|
|
594
|
+
}
|
|
595
|
+
};
|
|
596
|
+
const guardar = (itId) => (n) => { if (n)
|
|
597
|
+
refs.current.set(itId, n);
|
|
598
|
+
else
|
|
599
|
+
refs.current.delete(itId); };
|
|
600
|
+
const idRotulo = `${auto}-l`, idDesc = description ? `${auto}-d` : undefined, idErro = errorMessage ? `${auto}-e` : undefined;
|
|
601
|
+
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 => {
|
|
602
|
+
const escolhida = escolhe && escolhidos.includes(it.id);
|
|
603
|
+
const conteudo = _jsxs(_Fragment, { children: [it.icon && _jsx(Icon, { name: it.icon }), _jsx("span", { className: "tag-label", children: it.label })] });
|
|
604
|
+
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" }) });
|
|
605
|
+
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
|
|
606
|
+
? _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 })
|
|
607
|
+
: _jsx("span", { className: "tag-main", children: conteudo }), x] }, it.id);
|
|
608
|
+
}) }), description && _jsx("span", { className: "field-description", id: idDesc, children: description }), errorMessage && _jsx("span", { className: "field-error", id: idErro, children: errorMessage })] });
|
|
609
|
+
}
|
|
610
|
+
export function Slider(props) {
|
|
611
|
+
return props.channel ? _jsx(SliderDeCor, { ...props }) : _jsx(SliderDeNumero, { ...props });
|
|
612
|
+
}
|
|
613
|
+
function SliderDeCor({ value, defaultValue, onValueChange, onValueCommitted, ...resto }) {
|
|
614
|
+
const [cor, mudar] = useCor(value, defaultValue, onValueChange, onValueCommitted);
|
|
615
|
+
return _jsx(TrilhaDeCor, { ...resto, cor: cor, onCor: mudar });
|
|
616
|
+
}
|
|
617
|
+
function SliderDeNumero({ label, labelHidden, value, defaultValue, onValueChange, onValueCommitted, min, max, step, largeStep, orientation = "horizontal", disabled, name, format, locale, showValue = true, thumbLabels, className, id }) {
|
|
618
|
+
// Quantas alças: tantas quantos valores houver (a faixa é uma lista).
|
|
619
|
+
const atual = value ?? defaultValue;
|
|
620
|
+
const alcas = Array.isArray(atual) ? atual.length : 1;
|
|
621
|
+
// 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
|
|
622
|
+
// só conhece DEPOIS de montar — no HTML do servidor (e no catálogo, que é HTML parado) as alças saíam
|
|
623
|
+
// sem nome, e o axe do catálogo acusou as cinco. O `Slider.Label` do motor não aceita id, então o
|
|
624
|
+
// 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
|
|
625
|
+
// letra esmaecida do rótulo e do valor era lida como texto de contraste baixo, e não como peça desligada.
|
|
626
|
+
const idDoRotulo = React.useId();
|
|
627
|
+
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))] }) })] });
|
|
628
|
+
}
|
|
629
|
+
const EIXOS_PADRAO = { hsb: ["saturation", "brightness"], hsl: ["saturation", "lightness"], rgb: ["red", "green"] };
|
|
630
|
+
export function ColorArea({ value, defaultValue, onValueChange, onValueCommitted, colorSpace, xChannel, yChannel, ...resto }) {
|
|
631
|
+
const [cor, mudar] = useCor(value, defaultValue, onValueChange, onValueCommitted);
|
|
632
|
+
const space = espacoDosCanais(colorSpace, ...[xChannel, yChannel].filter((c) => !!c));
|
|
633
|
+
const [x, y] = [xChannel ?? EIXOS_PADRAO[space][0], yChannel ?? EIXOS_PADRAO[space][1]];
|
|
634
|
+
return _jsx(AreaDeCor, { ...resto, cor: cor, onCor: mudar, space: space, x: x, y: y });
|
|
635
|
+
}
|
|
636
|
+
export function ColorField({ value, defaultValue, onValueChange, onValueCommitted, channel, colorSpace, swatch, size, variant, width, disabled, readOnly, required, name, placeholder, id, className, ...aria }) {
|
|
637
|
+
const s = useAureaStrings();
|
|
638
|
+
const doSeletor = React.useContext(CorDoSeletor);
|
|
639
|
+
const [cor, mudar] = useCor(value === "" ? undefined : value, defaultValue, onValueChange, onValueCommitted);
|
|
640
|
+
// Vazio: o valor "" de fora, ou sem valor nenhum e ainda não escrito (o campo da referência começa vazio).
|
|
641
|
+
const [escrito, setEscrito] = React.useState(defaultValue !== undefined);
|
|
642
|
+
const vazio = !doSeletor && (value !== undefined ? value === "" : !escrito);
|
|
643
|
+
const [rascunho, setRascunho] = React.useState(null);
|
|
644
|
+
const canal = channel;
|
|
645
|
+
const space = canal ? spaceOf(canal, colorSpace) : "hsb";
|
|
646
|
+
const faixa = canal ? channelRange(canal) : null;
|
|
647
|
+
const v = canal ? getChannel(cor, canal, space) : 0;
|
|
648
|
+
const mostrado = vazio ? "" : canal ? formatChannel(canal, v) : corToHex(cor);
|
|
649
|
+
const confirmar = () => {
|
|
650
|
+
if (rascunho === null)
|
|
651
|
+
return;
|
|
652
|
+
const t = rascunho.trim();
|
|
653
|
+
setRascunho(null);
|
|
654
|
+
if (t === "") {
|
|
655
|
+
if (canal)
|
|
656
|
+
return;
|
|
657
|
+
setEscrito(false);
|
|
658
|
+
onValueChange?.("");
|
|
659
|
+
onValueCommitted?.("");
|
|
660
|
+
return;
|
|
661
|
+
}
|
|
662
|
+
if (canal) {
|
|
663
|
+
const n = Number(t.replace(/[°%\s]/g, "").replace(",", "."));
|
|
664
|
+
if (!Number.isFinite(n))
|
|
665
|
+
return;
|
|
666
|
+
mudar(setChannel(cor, canal, canal === "alpha" && (n > 1 || /%/.test(t)) ? n / 100 : n, space), true);
|
|
667
|
+
return;
|
|
668
|
+
}
|
|
669
|
+
const nova = parseColor(t);
|
|
670
|
+
if (!nova)
|
|
671
|
+
return;
|
|
672
|
+
setEscrito(true);
|
|
673
|
+
mudar(corDoTexto(t, cor), true);
|
|
674
|
+
};
|
|
675
|
+
const aoTeclar = (e) => {
|
|
676
|
+
if (e.key === "Enter") {
|
|
677
|
+
e.preventDefault();
|
|
678
|
+
confirmar();
|
|
679
|
+
return;
|
|
680
|
+
}
|
|
681
|
+
if (!canal || !faixa || disabled || readOnly)
|
|
682
|
+
return;
|
|
683
|
+
const passo = e.key === "ArrowUp" ? faixa.step : e.key === "ArrowDown" ? -faixa.step : e.key === "PageUp" ? faixa.page : e.key === "PageDown" ? -faixa.page : 0;
|
|
684
|
+
const alvo = e.key === "Home" ? faixa.min : e.key === "End" ? faixa.max : passo ? v + passo : null;
|
|
685
|
+
if (alvo === null)
|
|
686
|
+
return;
|
|
687
|
+
e.preventDefault();
|
|
688
|
+
setRascunho(null);
|
|
689
|
+
mudar(setChannel(cor, canal, Math.round(alvo / faixa.step) * faixa.step, space), true);
|
|
690
|
+
};
|
|
691
|
+
const comAmostra = swatch ?? !canal;
|
|
692
|
+
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) })] });
|
|
693
|
+
}
|
|
694
|
+
export function ColorPicker({ label, value, defaultValue, onValueChange, onValueCommitted, swatches, variant = "full", children, disabled, name, id, className }) {
|
|
695
|
+
const s = useAureaStrings();
|
|
696
|
+
const portal = usePortalContainer();
|
|
697
|
+
const idGrupo = React.useId();
|
|
698
|
+
const [cor, mudar] = useCor(value, defaultValue, onValueChange, onValueCommitted);
|
|
699
|
+
const hex = corToHex(cor), nome = colorName(hsbToRgb(cor), s);
|
|
700
|
+
const amostras = swatches?.map((o) => {
|
|
701
|
+
const op = typeof o === "string" ? { color: o } : o;
|
|
702
|
+
const c = parseColor(op.color);
|
|
703
|
+
return { color: op.color, label: op.label ?? (c ? colorName(hsbToRgb(c), s) : op.color) };
|
|
704
|
+
});
|
|
705
|
+
const escolhida = amostras?.find((o) => { const c = parseColor(o.color); return c !== null && corToHex(c) === hex; })?.color ?? "";
|
|
706
|
+
const contexto = React.useMemo(() => ({ cor, mudar }), [cor, mudar]);
|
|
707
|
+
// O seletor escuta o teclado e o ponteiro desde que MONTA: a área só monta ao abrir, DEPOIS do clique que abriu, e
|
|
708
|
+
// a primeira abertura com o mouse acendia o anel (medido na bancada em 14/10/2026; o teste abria pelo teclado antes).
|
|
709
|
+
React.useEffect(ouvirModalidade, []);
|
|
710
|
+
// O foco fica PRESO na janela (`trap-focus`), como na referência: com quatro peças dentro, o Tab que saía depois
|
|
711
|
+
// do código fechava a janela (medido no navegador). O motor pede um botão de fechar dentro, para o leitor de tela
|
|
712
|
+
// por toque sair; ele fica invisível e fora do Tab — quem usa teclado tem o Esc, e o foco nunca some na tela.
|
|
713
|
+
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, { className: "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
714
|
}
|
|
191
715
|
// OTPField (Lote 1 do BUILDING.md). Um campo por dígito, com o comportamento que ninguém acerta
|
|
192
716
|
// à mão: colar o código inteiro distribui pelos campos, Backspace volta um, e o
|
|
@@ -199,9 +723,12 @@ export function NumberField({ value, defaultValue, onValueChange, min, max, step
|
|
|
199
723
|
// formulário anônimos. Um leitor de tela anunciaria "editar texto" seis vezes seguidas sem
|
|
200
724
|
// dizer qual é qual. O grupo continua nomeado (`role="group"`), que é o que diz para que serve
|
|
201
725
|
// o código; o nome de cada caixa é o que diz onde você está.
|
|
202
|
-
|
|
726
|
+
// 15/10/2026 (o Victor: "A, B, C, pode"), como o campo de código da referência: `groupSize` põe o TRACINHO a cada N
|
|
727
|
+
// caixas (o 3 + 3 da vitrine dela; o `Separator` do motor, de 6 × 2), e `variant="secondary"` dá às caixas o fundo
|
|
728
|
+
// neutro do `Input` secundário. Sem os dois, a marcação é a de antes.
|
|
729
|
+
export function OTPField({ length, value, defaultValue, onValueChange, mask, disabled, required, label, id, className, groupSize, variant }) {
|
|
203
730
|
const s = useAureaStrings();
|
|
204
|
-
return _jsx(BaseOTPField.Root, { id: id, length: length, value: value, defaultValue: defaultValue, onValueChange: soOValor(onValueChange), mask: mask, disabled: disabled, required: required, role: "group", "aria-label": label, className: cx("otp-field", className), children: Array.from({ length }, (_, i) => _jsx(BaseOTPField.Input, { render: _jsx("input", { "aria-label": `${s.otpDigit} ${i + 1}` }), className: "input otp-slot" }, i)) });
|
|
731
|
+
return _jsx(BaseOTPField.Root, { id: id, length: length, value: value, defaultValue: defaultValue, onValueChange: soOValor(onValueChange), mask: mask, disabled: disabled, required: required, role: "group", "aria-label": label, className: cx("otp-field", className), children: Array.from({ length }, (_, i) => _jsxs(React.Fragment, { children: [_jsx(BaseOTPField.Input, { render: _jsx("input", { "aria-label": `${s.otpDigit} ${i + 1}` }), className: cx("input otp-slot", variant === "secondary" && "input-secondary") }), groupSize && groupSize > 0 && (i + 1) % groupSize === 0 && i < length - 1 ? _jsx(BaseOTPField.Separator, { className: "otp-separator" }) : null] }, i)) });
|
|
205
732
|
}
|
|
206
733
|
// SegmentedControl — achado M20, fechado na Parte C do PLANO-1.0 em 07/08/2026.
|
|
207
734
|
//
|
|
@@ -263,7 +790,7 @@ export function Combobox({ items, value, onValueChange, placeholder, label, empt
|
|
|
263
790
|
const portal = usePortalContainer();
|
|
264
791
|
const autoId = React.useId();
|
|
265
792
|
const inputId = id ?? autoId;
|
|
266
|
-
return _jsxs(BaseCombobox.Root, { items: items, value: value, onValueChange: soOValor(onValueChange), disabled: disabled, itemToStringLabel: (i) => i.label, children: [_jsxs("div", { className: cx("field", className), children: [label && _jsx("label", { className: "label", htmlFor: inputId, children: label }), _jsxs(BaseCombobox.InputGroup, { className: "combobox-group", children: [_jsx(BaseCombobox.Input, { id: inputId, placeholder: placeholder, className: cx("input", peleDoEixo("input", size)), "aria-describedby": descrito, "aria-invalid": invalido, "aria-label": rotulo }), _jsxs("span", { className: "combobox-actions", children: [_jsx(BaseCombobox.Clear, { render: _jsx(IconButton, { variant: "ghost", size: "sm", icon: "x", label: s.comboboxClear }) }), _jsx(BaseCombobox.Trigger, { render: _jsx(IconButton, { variant: "ghost", size: "sm", icon: "caret-down", label: s.comboboxOpen }) })] })] })] }), _jsx(BaseCombobox.Portal, { container: portal, children: _jsx(BaseCombobox.Positioner, { sideOffset: 6, children: _jsxs(BaseCombobox.Popup, { className: "menu combobox-popup", children: [_jsx(BaseCombobox.Empty, { className: "combobox-empty", children: empty ?? s.comboboxEmpty }), listaCombobox(items)] }) }) })] });
|
|
793
|
+
return _jsxs(BaseCombobox.Root, { items: items, value: value, onValueChange: soOValor(onValueChange), disabled: disabled, itemToStringLabel: (i) => i.label, children: [_jsxs("div", { className: cx("field", className), children: [label && _jsx("label", { className: "label", htmlFor: inputId, children: label }), _jsxs(BaseCombobox.InputGroup, { className: "combobox-group", children: [_jsx(BaseCombobox.Input, { id: inputId, placeholder: placeholder, className: cx("input", peleDoEixo("input", size)), "aria-describedby": descrito, "aria-invalid": invalido, "aria-label": rotulo }), _jsxs("span", { className: "combobox-actions", children: [_jsx(BaseCombobox.Clear, { render: _jsx(IconButton, { variant: "ghost", size: "sm", icon: "x", label: s.comboboxClear }) }), _jsx(BaseCombobox.Trigger, { render: _jsx(IconButton, { variant: "ghost", size: "sm", icon: "caret-down", label: s.comboboxOpen }) })] })] })] }), _jsx(BaseCombobox.Portal, { container: portal, children: _jsx(BaseCombobox.Positioner, { className: "positioner", sideOffset: 6, children: _jsxs(BaseCombobox.Popup, { className: "menu combobox-popup", children: [_jsx(BaseCombobox.Empty, { className: "combobox-empty", children: empty ?? s.comboboxEmpty }), listaCombobox(items)] }) }) })] });
|
|
267
794
|
}
|
|
268
795
|
// MultiCombobox (Fase 4): multi-seleção com chips, opções agrupadas e filtro
|
|
269
796
|
// externo (async). Reusa .combobox-popup/-item/-empty do básico.
|
|
@@ -278,7 +805,7 @@ export function MultiCombobox({ items, value, onValueChange, onInputChange, load
|
|
|
278
805
|
const autoId = React.useId();
|
|
279
806
|
const inputId = id ?? autoId;
|
|
280
807
|
return _jsxs(BaseCombobox.Root, { multiple: true, items: items, value: value, onValueChange: soOValor(onValueChange), disabled: disabled, itemToStringLabel: (i) => i.label, filter: onInputChange ? null : undefined, onInputValueChange: onInputChange ? ((v, d) => { if (d.reason !== "item-press")
|
|
281
|
-
onInputChange(v); }) : undefined, children: [_jsxs("div", { className: cx("field", className), children: [label && _jsx("label", { className: "label", htmlFor: inputId, children: label }), _jsxs(BaseCombobox.InputGroup, { className: "combobox-multi", children: [_jsx(BaseCombobox.Chips, { className: "combobox-chips", children: _jsx(BaseCombobox.Value, { children: (selected) => _jsxs(_Fragment, { children: [selected.map((item) => _jsxs(BaseCombobox.Chip, { className: "combobox-chip", children: [item.label, _jsx(BaseCombobox.ChipRemove, { className: "combobox-chip-remove", "aria-label": `${s.comboboxRemove} ${item.label}`, children: _jsx(Icon, { name: "x", size: "sm" }) })] }, item.value)), _jsx(BaseCombobox.Input, { id: inputId, placeholder: selected.length ? undefined : placeholder, className: "combobox-chip-input", "aria-describedby": descrito, "aria-invalid": invalido, "aria-label": rotulo })] }) }) }), _jsxs("span", { className: "combobox-actions", children: [_jsx(BaseCombobox.Clear, { render: _jsx(IconButton, { variant: "ghost", size: "sm", icon: "x", label: s.comboboxClear }) }), _jsx(BaseCombobox.Trigger, { render: _jsx(IconButton, { variant: "ghost", size: "sm", icon: "caret-down", label: s.comboboxOpen }) })] })] })] }), _jsx(BaseCombobox.Portal, { container: portal, children: _jsx(BaseCombobox.Positioner, { sideOffset: 6, children: _jsxs(BaseCombobox.Popup, { className: "menu combobox-popup", children: [_jsx(BaseCombobox.Empty, { className: "combobox-empty", children: loading ? s.comboboxLoading : (empty ?? s.comboboxEmpty) }), listaCombobox(items)] }) }) })] });
|
|
808
|
+
onInputChange(v); }) : undefined, children: [_jsxs("div", { className: cx("field", className), children: [label && _jsx("label", { className: "label", htmlFor: inputId, children: label }), _jsxs(BaseCombobox.InputGroup, { className: "combobox-multi", children: [_jsx(BaseCombobox.Chips, { className: "combobox-chips", children: _jsx(BaseCombobox.Value, { children: (selected) => _jsxs(_Fragment, { children: [selected.map((item) => _jsxs(BaseCombobox.Chip, { className: "combobox-chip", children: [item.label, _jsx(BaseCombobox.ChipRemove, { className: "combobox-chip-remove", "aria-label": `${s.comboboxRemove} ${item.label}`, children: _jsx(Icon, { name: "x", size: "sm" }) })] }, item.value)), _jsx(BaseCombobox.Input, { id: inputId, placeholder: selected.length ? undefined : placeholder, className: "combobox-chip-input", "aria-describedby": descrito, "aria-invalid": invalido, "aria-label": rotulo })] }) }) }), _jsxs("span", { className: "combobox-actions", children: [_jsx(BaseCombobox.Clear, { render: _jsx(IconButton, { variant: "ghost", size: "sm", icon: "x", label: s.comboboxClear }) }), _jsx(BaseCombobox.Trigger, { render: _jsx(IconButton, { variant: "ghost", size: "sm", icon: "caret-down", label: s.comboboxOpen }) })] })] })] }), _jsx(BaseCombobox.Portal, { container: portal, children: _jsx(BaseCombobox.Positioner, { className: "positioner", sideOffset: 6, children: _jsxs(BaseCombobox.Popup, { className: "menu combobox-popup", children: [_jsx(BaseCombobox.Empty, { className: "combobox-empty", children: loading ? s.comboboxLoading : (empty ?? s.comboboxEmpty) }), listaCombobox(items)] }) }) })] });
|
|
282
809
|
}
|
|
283
810
|
const ehGrupoDoSelect = (i) => typeof i === "object" && i != null && "items" in i;
|
|
284
811
|
// Lê `<option>`/`<optgroup>` como o navegador lê: `value` ausente vale o TEXTO da opção.
|
|
@@ -308,7 +835,7 @@ export const Select = forwardRef(function Select({ items, children, value, defau
|
|
|
308
835
|
onValueChange?.(valor);
|
|
309
836
|
onChange?.({ target: { value: valor, name }, currentTarget: { value: valor, name } });
|
|
310
837
|
};
|
|
311
|
-
return _jsxs(BaseSelect.Root, { items: planas.map(o => ({ value: o.value, label: o.label })), ...(value !== undefined ? { value } : { defaultValue: inicial }), onValueChange: v => mudar(v), name: name, required: required, disabled: disabled, children: [_jsx(BaseSelect.Trigger, { ref: ref, className: cx("select", "select-trigger", peleDoEixo("select", size), className), ...props, children: _jsx(BaseSelect.Value, { placeholder: placeholder, className: "select-value" }) }), _jsx(BaseSelect.Portal, { container: portal, children: _jsx(BaseSelect.Positioner, { sideOffset: 6, alignItemWithTrigger: false, children: _jsx(BaseSelect.Popup, { className: "menu combobox-popup", children: _jsx(BaseSelect.List, { children: lista.map((i, n) => ehGrupoDoSelect(i)
|
|
838
|
+
return _jsxs(BaseSelect.Root, { items: planas.map(o => ({ value: o.value, label: o.label })), ...(value !== undefined ? { value } : { defaultValue: inicial }), onValueChange: v => mudar(v), name: name, required: required, disabled: disabled, children: [_jsx(BaseSelect.Trigger, { ref: ref, className: cx("select", "select-trigger", peleDoEixo("select", size), className), ...props, children: _jsx(BaseSelect.Value, { placeholder: placeholder, className: "select-value" }) }), _jsx(BaseSelect.Portal, { container: portal, children: _jsx(BaseSelect.Positioner, { className: "positioner", sideOffset: 6, alignItemWithTrigger: false, children: _jsx(BaseSelect.Popup, { className: "menu combobox-popup", children: _jsx(BaseSelect.List, { children: lista.map((i, n) => ehGrupoDoSelect(i)
|
|
312
839
|
? _jsxs(BaseSelect.Group, { className: "combobox-section", children: [_jsx(BaseSelect.GroupLabel, { className: "combobox-group-label", children: i.label }), i.items.map(itemDoSelect)] }, n)
|
|
313
840
|
: itemDoSelect(i)) }) }) }) })] });
|
|
314
841
|
});
|