@aurea-uds/react 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/actions.d.ts +13 -0
- package/dist/actions.js +5 -0
- package/dist/agents.d.ts +209 -0
- package/dist/agents.js +302 -0
- package/dist/calendar.d.ts +6 -0
- package/dist/calendar.js +27 -0
- package/dist/chart.d.ts +10 -0
- package/dist/chart.js +53 -0
- package/dist/code-editor.js +1 -0
- package/dist/code.js +18 -2
- package/dist/communication.js +1 -0
- package/dist/data-display.js +1 -0
- package/dist/data-grid.d.ts +52 -3
- package/dist/data-grid.js +205 -25
- package/dist/feedback.d.ts +14 -4
- package/dist/feedback.js +37 -7
- package/dist/file-input.d.ts +21 -2
- package/dist/file-input.js +154 -21
- package/dist/graph.d.ts +33 -0
- package/dist/graph.js +178 -0
- package/dist/identity.d.ts +12 -3
- package/dist/identity.js +24 -2
- package/dist/index.d.ts +3 -1
- package/dist/index.js +15 -1
- package/dist/inputs.d.ts +26 -0
- package/dist/inputs.js +69 -4
- package/dist/internal.d.ts +4 -66
- package/dist/internal.js +25 -14
- package/dist/layout.d.ts +2 -1
- package/dist/layout.js +61 -7
- package/dist/media.js +1 -0
- package/dist/navigation.d.ts +27 -1
- package/dist/navigation.js +32 -7
- package/dist/overlays.d.ts +5 -0
- package/dist/overlays.js +21 -9
- package/dist/pure.d.ts +166 -0
- package/dist/pure.js +87 -0
- package/dist/qrcode.d.ts +2 -1
- package/dist/qrcode.js +3 -2
- package/dist/system.d.ts +2 -1
- package/dist/system.js +13 -4
- package/package.json +176 -144
package/dist/inputs.d.ts
CHANGED
|
@@ -26,6 +26,32 @@ export declare function Switch({ label, className, ...props }: InputHTMLAttribut
|
|
|
26
26
|
label: ReactNode;
|
|
27
27
|
}): React.JSX.Element;
|
|
28
28
|
export declare function Range(props: InputHTMLAttributes<HTMLInputElement> & RefAttributes<HTMLInputElement>): React.JSX.Element;
|
|
29
|
+
export declare function NumberField({ value, defaultValue, onValueChange, min, max, step, disabled, readOnly, required, label, id, className }: {
|
|
30
|
+
value?: number | null;
|
|
31
|
+
defaultValue?: number;
|
|
32
|
+
onValueChange?: (v: number | null) => void;
|
|
33
|
+
min?: number;
|
|
34
|
+
max?: number;
|
|
35
|
+
step?: number;
|
|
36
|
+
disabled?: boolean;
|
|
37
|
+
readOnly?: boolean;
|
|
38
|
+
required?: boolean;
|
|
39
|
+
label?: string;
|
|
40
|
+
id?: string;
|
|
41
|
+
className?: string;
|
|
42
|
+
}): React.JSX.Element;
|
|
43
|
+
export declare function OTPField({ length, value, defaultValue, onValueChange, mask, disabled, required, label, id, className }: {
|
|
44
|
+
length: number;
|
|
45
|
+
value?: string;
|
|
46
|
+
defaultValue?: string;
|
|
47
|
+
onValueChange?: (v: string) => void;
|
|
48
|
+
mask?: boolean;
|
|
49
|
+
disabled?: boolean;
|
|
50
|
+
required?: boolean;
|
|
51
|
+
label?: string;
|
|
52
|
+
id?: string;
|
|
53
|
+
className?: string;
|
|
54
|
+
}): React.JSX.Element;
|
|
29
55
|
export declare function SegmentedControl({ items, value, onChange, label }: {
|
|
30
56
|
items: Array<{
|
|
31
57
|
value: string;
|
package/dist/inputs.js
CHANGED
|
@@ -1,10 +1,15 @@
|
|
|
1
|
+
"use client";
|
|
1
2
|
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
3
|
// Fase 9 (achado A5): este arquivo saiu do index.tsx de 970 linhas. Um módulo por categoria
|
|
3
4
|
// do registry — a taxonomia já existia e é gateada. A ordem de import entre eles é um DAG:
|
|
4
5
|
// internal → system → actions → feedback → inputs → navigation → layout → data-display → resto.
|
|
5
6
|
import React, { forwardRef } from "react";
|
|
6
7
|
import { Combobox as BaseCombobox } from "@base-ui/react/combobox";
|
|
7
|
-
import {
|
|
8
|
+
import { NumberField as BaseNumberField } from "@base-ui/react/number-field";
|
|
9
|
+
import { OTPField as BaseOTPField } from "@base-ui/react/otp-field";
|
|
10
|
+
import { RadioGroup as BaseRadioGroup } from "@base-ui/react/radio-group";
|
|
11
|
+
import { Radio as BaseRadio } from "@base-ui/react/radio";
|
|
12
|
+
import { cx, useAureaStrings, usePortalContainer } from "./internal.js";
|
|
8
13
|
import { Icon } from "./system.js";
|
|
9
14
|
import { IconButton } from "./actions.js";
|
|
10
15
|
// FileInput mora em arquivo próprio (132 linhas — upload real, aborto, progresso) e é público
|
|
@@ -41,21 +46,81 @@ export function Checkbox({ label, labelHidden, description, className, ...props
|
|
|
41
46
|
export function Radio({ label, className, ...props }) { return _jsxs("label", { className: cx("radio", className), children: [_jsx("input", { type: "radio", ...props }), _jsx("span", { className: "control-mark" }), label] }); }
|
|
42
47
|
export function Switch({ label, className, ...props }) { return _jsxs("label", { className: cx("switch", className), children: [_jsx("input", { type: "checkbox", role: "switch", ...props }), _jsx("span", { className: "switch-track" }), _jsx("span", { children: label })] }); }
|
|
43
48
|
export function Range(props) { return _jsx("input", { className: cx("range", props.className), type: "range", ...props }); }
|
|
44
|
-
|
|
49
|
+
// NumberField (Lote 1 do BUILDING.md). Um `<input type="number">` cru tem três problemas que
|
|
50
|
+
// este resolve, e todos vieram do motor, não de nós: as setas nativas são alvos minúsculos e
|
|
51
|
+
// somem no Safari; a roda do mouse altera o valor por acidente sobre o campo focado; e o valor
|
|
52
|
+
// digitado não é formatado por locale. O Base UI trata os três.
|
|
53
|
+
// A anatomia (menos · campo · mais numa peça só) é a que as referências convergem.
|
|
54
|
+
// Fora de propósito: o ScrubArea do Base UI — arrastar o rótulo para variar o número. É gesto
|
|
55
|
+
// que ninguém descobre sozinho e que não tem equivalente por teclado.
|
|
56
|
+
export function NumberField({ value, defaultValue, onValueChange, min, max, step, disabled, readOnly, required, label, id, className }) {
|
|
57
|
+
const s = useAureaStrings();
|
|
58
|
+
return _jsx(BaseNumberField.Root, { id: id, value: value, defaultValue: defaultValue, onValueChange: onValueChange, min: min, max: max, step: step, disabled: disabled, readOnly: readOnly, required: required, className: cx("number-field", className), children: _jsxs(BaseNumberField.Group, { className: "number-field-group", children: [_jsx(BaseNumberField.Decrement, { className: "btn btn-ghost btn-icon", "aria-label": s.decrement, children: _jsx(Icon, { name: "subtract" }) }), _jsx(BaseNumberField.Input, { className: "input number-field-input", "aria-label": label }), _jsx(BaseNumberField.Increment, { className: "btn btn-ghost btn-icon", "aria-label": s.increment, children: _jsx(Icon, { name: "add" }) })] }) });
|
|
59
|
+
}
|
|
60
|
+
// OTPField (Lote 1 do BUILDING.md). Um campo por dígito, com o comportamento que ninguém acerta
|
|
61
|
+
// à mão: colar o código inteiro distribui pelos campos, Backspace volta um, e o
|
|
62
|
+
// `autocomplete="one-time-code"` deixa o iOS oferecer o código do SMS. Tudo do motor.
|
|
63
|
+
// Uma referência resolve isto com um pacote npm separado; aqui não entra dependência nova —
|
|
64
|
+
// o `@base-ui/react`, que já é a única dependência de runtime da biblioteca, tem `otp-field`.
|
|
65
|
+
// `length` é obrigatório de propósito: sem ele o campo não sabe quando está completo.
|
|
66
|
+
// CADA caixa leva nome próprio, e não só o grupo. O `catalog-sweep` pegou isto no primeiro
|
|
67
|
+
// lote: com `aria-label` só na raiz, o axe acusou `label` em todas as seis — seis campos de
|
|
68
|
+
// formulário anônimos. Um leitor de tela anunciaria "editar texto" seis vezes seguidas sem
|
|
69
|
+
// dizer qual é qual. O grupo continua nomeado (`role="group"`), que é o que diz para que serve
|
|
70
|
+
// o código; o nome de cada caixa é o que diz onde você está.
|
|
71
|
+
export function OTPField({ length, value, defaultValue, onValueChange, mask, disabled, required, label, id, className }) {
|
|
72
|
+
const s = useAureaStrings();
|
|
73
|
+
return _jsx(BaseOTPField.Root, { id: id, length: length, value: value, defaultValue: defaultValue, onValueChange: onValueChange, mask: mask, disabled: disabled, required: required, role: "group", "aria-label": label, className: cx("otp-field", className), children: Array.from({ length }, (_, i) => _jsx(BaseOTPField.Input, { render: _jsx("input", { "aria-label": `${s.otpDigit} ${i + 1}` }), className: "input otp-slot" }, i)) });
|
|
74
|
+
}
|
|
75
|
+
// SegmentedControl — achado M20, fechado na Parte C do PLANO-1.0 em 07/08/2026.
|
|
76
|
+
//
|
|
77
|
+
// O DEFEITO: era `role="group"` com N botões de `aria-pressed`. Isso descreve N alternâncias
|
|
78
|
+
// INDEPENDENTES — cada botão anuncia "pressionado/não pressionado", nada diz que só um pode
|
|
79
|
+
// valer, e nunca se ouve "1 de 2". Para escolha única entre poucas opções o padrão APG é
|
|
80
|
+
// `radiogroup`, e a diferença é o que o leitor de tela consegue prometer.
|
|
81
|
+
// A troca mudou a semântica de um componente publicado, então foi registrada: ADR-0016.
|
|
82
|
+
//
|
|
83
|
+
// O MOTOR ENTREGA, ENTÃO NÃO ESCREVEMOS (BUILDING.md §1). O `RadioGroup` do Base UI 1.6 traz o
|
|
84
|
+
// padrão inteiro: roving tabindex, setas que movem E selecionam, Home/End, e `aria-checked`.
|
|
85
|
+
// Escrever isso à mão seria reimplementar um composite que já está testado.
|
|
86
|
+
//
|
|
87
|
+
// `render={<button type="button"/>}` não é enfeite, e a medição de 07/08 é que disse: o
|
|
88
|
+
// `Radio.Root` renderiza um `<span>` por padrão, e a pele da Aurea é `.segmented button`. Sem
|
|
89
|
+
// isso o componente perderia a pele inteira — e o check 18 não veria, porque ele olha CLASSE.
|
|
90
|
+
// O motor também emite um `<input type="radio">` escondido por item (para envio de formulário);
|
|
91
|
+
// ele é `position:fixed` e `aria-hidden`, então não entra no flex nem na árvore de acessibilidade.
|
|
92
|
+
//
|
|
93
|
+
// E `nativeButton` é o par obrigatório do `render` acima — medido em 11/08/2026. O `Radio.Root`
|
|
94
|
+
// declara `nativeButton = false` por padrão (é o valor certo para o `<span>` que ele renderiza
|
|
95
|
+
// sozinho), e o `useButton` do motor CONFERE isso no DOM montado, dentro de um efeito: sem esta
|
|
96
|
+
// palavra sai um `console.error` do Base UI a cada montagem de CLIENTE. Não no catálogo, que é
|
|
97
|
+
// HTML estático — no console de quem INSTALA.
|
|
98
|
+
// O DOM foi medido nos dois lados antes da troca. O `role="button"` que a mensagem ameaça NÃO
|
|
99
|
+
// aparecia (o `role="radio"` do motor já vencia a fusão); o que muda de fato é o `<input>`
|
|
100
|
+
// escondido perder o `id`, que migra para este botão — e migra para o lado certo, porque o botão
|
|
101
|
+
// é o rádio e o input é `aria-hidden`. Comportamento idêntico: Enter não seleciona, Espaço
|
|
102
|
+
// seleciona, a seta move E seleciona. O que sai é a camada sintética que o motor punha por cima
|
|
103
|
+
// da ativação nativa do `<button>`.
|
|
104
|
+
export function SegmentedControl({ items, value, onChange, label }) {
|
|
105
|
+
const s = useAureaStrings();
|
|
106
|
+
return _jsx(BaseRadioGroup, { className: "segmented", "aria-label": label ?? s.optionsLabel, value: value, onValueChange: v => onChange(String(v)), children: items.map(i => _jsx(BaseRadio.Root, { value: i.value, className: i.value === value ? "active" : undefined, nativeButton: true, render: _jsx("button", { type: "button" }), children: i.label }, i.value)) });
|
|
107
|
+
}
|
|
45
108
|
export function Combobox({ items, value, onValueChange, placeholder, label, id, className }) {
|
|
46
109
|
const s = useAureaStrings();
|
|
110
|
+
const portal = usePortalContainer();
|
|
47
111
|
const autoId = React.useId();
|
|
48
112
|
const inputId = id ?? autoId;
|
|
49
|
-
return _jsxs(BaseCombobox.Root, { items: items, value: value, onValueChange: onValueChange, itemToStringLabel: (i) => i.label, children: [_jsxs("div", { className: cx("field", className), children: [label && _jsx("label", { className: "label", htmlFor: inputId, children: label }), _jsxs(BaseCombobox.InputGroup, { className: "combobox-group", children: [_jsx(BaseCombobox.Input, { id: inputId, placeholder: placeholder, className: "input" }), _jsxs("span", { className: "combobox-actions", children: [_jsx(BaseCombobox.Clear, { render: _jsx(IconButton, { variant: "ghost", size: "sm", icon: "close", label: s.comboboxClear }) }), _jsx(BaseCombobox.Trigger, { render: _jsx(IconButton, { variant: "ghost", size: "sm", icon: "chevron--down", label: s.comboboxOpen }) })] })] })] }), _jsx(BaseCombobox.Portal, { children: _jsx(BaseCombobox.Positioner, { sideOffset: 6, children: _jsxs(BaseCombobox.Popup, { className: "menu combobox-popup", children: [_jsx(BaseCombobox.Empty, { className: "combobox-empty", children: s.comboboxEmpty }), _jsx(BaseCombobox.List, { children: (item) => _jsxs(BaseCombobox.Item, { value: item, className: "menu-item combobox-item", children: [_jsx(BaseCombobox.ItemIndicator, { className: "combobox-check", children: _jsx(Icon, { name: "checkmark", size: "sm" }) }), _jsx("span", { children: item.label })] }, item.value) })] }) }) })] });
|
|
113
|
+
return _jsxs(BaseCombobox.Root, { items: items, value: value, onValueChange: onValueChange, itemToStringLabel: (i) => i.label, children: [_jsxs("div", { className: cx("field", className), children: [label && _jsx("label", { className: "label", htmlFor: inputId, children: label }), _jsxs(BaseCombobox.InputGroup, { className: "combobox-group", children: [_jsx(BaseCombobox.Input, { id: inputId, placeholder: placeholder, className: "input" }), _jsxs("span", { className: "combobox-actions", children: [_jsx(BaseCombobox.Clear, { render: _jsx(IconButton, { variant: "ghost", size: "sm", icon: "close", label: s.comboboxClear }) }), _jsx(BaseCombobox.Trigger, { render: _jsx(IconButton, { variant: "ghost", size: "sm", icon: "chevron--down", label: s.comboboxOpen }) })] })] })] }), _jsx(BaseCombobox.Portal, { container: portal, children: _jsx(BaseCombobox.Positioner, { sideOffset: 6, children: _jsxs(BaseCombobox.Popup, { className: "menu combobox-popup", children: [_jsx(BaseCombobox.Empty, { className: "combobox-empty", children: s.comboboxEmpty }), _jsx(BaseCombobox.List, { children: (item) => _jsxs(BaseCombobox.Item, { value: item, className: "menu-item combobox-item", children: [_jsx(BaseCombobox.ItemIndicator, { className: "combobox-check", children: _jsx(Icon, { name: "checkmark", size: "sm" }) }), _jsx("span", { children: item.label })] }, item.value) })] }) }) })] });
|
|
50
114
|
}
|
|
51
115
|
const isGrouped = (items) => items.length > 0 && "items" in items[0];
|
|
52
116
|
export function MultiCombobox({ items, value, onValueChange, onInputChange, loading, placeholder, label, id, className }) {
|
|
53
117
|
const s = useAureaStrings();
|
|
118
|
+
const portal = usePortalContainer();
|
|
54
119
|
const autoId = React.useId();
|
|
55
120
|
const inputId = id ?? autoId;
|
|
56
121
|
const renderItem = (item) => _jsxs(BaseCombobox.Item, { value: item, className: "menu-item combobox-item", children: [_jsx(BaseCombobox.ItemIndicator, { className: "combobox-check", children: _jsx(Icon, { name: "checkmark", size: "sm" }) }), _jsx("span", { children: item.label })] }, item.value);
|
|
57
122
|
return _jsxs(BaseCombobox.Root, { multiple: true, items: items, value: value, onValueChange: onValueChange, itemToStringLabel: (i) => i.label, filter: onInputChange ? null : undefined, onInputValueChange: onInputChange ? ((v, d) => { if (d.reason !== "item-press")
|
|
58
|
-
onInputChange(v); }) : undefined, children: [_jsxs("div", { className: cx("field", className), children: [label && _jsx("label", { className: "label", htmlFor: inputId, children: label }), _jsxs(BaseCombobox.InputGroup, { className: "combobox-multi", children: [_jsx(BaseCombobox.Chips, { className: "combobox-chips", children: _jsx(BaseCombobox.Value, { children: (selected) => _jsxs(_Fragment, { children: [selected.map((item) => _jsxs(BaseCombobox.Chip, { className: "combobox-chip", children: [item.label, _jsx(BaseCombobox.ChipRemove, { className: "combobox-chip-remove", "aria-label": `${s.comboboxRemove} ${item.label}`, children: _jsx(Icon, { name: "close", size: "sm" }) })] }, item.value)), _jsx(BaseCombobox.Input, { id: inputId, placeholder: selected.length ? undefined : placeholder, className: "combobox-chip-input" })] }) }) }), _jsxs("span", { className: "combobox-actions", children: [_jsx(BaseCombobox.Clear, { render: _jsx(IconButton, { variant: "ghost", size: "sm", icon: "close", label: s.comboboxClear }) }), _jsx(BaseCombobox.Trigger, { render: _jsx(IconButton, { variant: "ghost", size: "sm", icon: "chevron--down", label: s.comboboxOpen }) })] })] })] }), _jsx(BaseCombobox.Portal, { children: _jsx(BaseCombobox.Positioner, { sideOffset: 6, children: _jsxs(BaseCombobox.Popup, { className: "menu combobox-popup", children: [_jsx(BaseCombobox.Empty, { className: "combobox-empty", children: loading ? s.comboboxLoading : s.comboboxEmpty }), _jsx(BaseCombobox.List, { children: isGrouped(items)
|
|
123
|
+
onInputChange(v); }) : undefined, children: [_jsxs("div", { className: cx("field", className), children: [label && _jsx("label", { className: "label", htmlFor: inputId, children: label }), _jsxs(BaseCombobox.InputGroup, { className: "combobox-multi", children: [_jsx(BaseCombobox.Chips, { className: "combobox-chips", children: _jsx(BaseCombobox.Value, { children: (selected) => _jsxs(_Fragment, { children: [selected.map((item) => _jsxs(BaseCombobox.Chip, { className: "combobox-chip", children: [item.label, _jsx(BaseCombobox.ChipRemove, { className: "combobox-chip-remove", "aria-label": `${s.comboboxRemove} ${item.label}`, children: _jsx(Icon, { name: "close", size: "sm" }) })] }, item.value)), _jsx(BaseCombobox.Input, { id: inputId, placeholder: selected.length ? undefined : placeholder, className: "combobox-chip-input" })] }) }) }), _jsxs("span", { className: "combobox-actions", children: [_jsx(BaseCombobox.Clear, { render: _jsx(IconButton, { variant: "ghost", size: "sm", icon: "close", label: s.comboboxClear }) }), _jsx(BaseCombobox.Trigger, { render: _jsx(IconButton, { variant: "ghost", size: "sm", icon: "chevron--down", label: s.comboboxOpen }) })] })] })] }), _jsx(BaseCombobox.Portal, { container: portal, children: _jsx(BaseCombobox.Positioner, { sideOffset: 6, children: _jsxs(BaseCombobox.Popup, { className: "menu combobox-popup", children: [_jsx(BaseCombobox.Empty, { className: "combobox-empty", children: loading ? s.comboboxLoading : s.comboboxEmpty }), _jsx(BaseCombobox.List, { children: isGrouped(items)
|
|
59
124
|
? (group) => _jsxs(BaseCombobox.Group, { items: group.items, className: "combobox-section", children: [_jsx(BaseCombobox.GroupLabel, { className: "combobox-group-label", children: group.label }), _jsx(BaseCombobox.Collection, { children: renderItem })] }, group.label)
|
|
60
125
|
: renderItem })] }) }) })] });
|
|
61
126
|
}
|
package/dist/internal.d.ts
CHANGED
|
@@ -1,72 +1,10 @@
|
|
|
1
1
|
import { type HTMLAttributes, type RefAttributes } from "react";
|
|
2
|
-
|
|
3
|
-
export
|
|
4
|
-
close: string;
|
|
5
|
-
paginationLabel: string;
|
|
6
|
-
previous: string;
|
|
7
|
-
next: string;
|
|
8
|
-
breadcrumbLabel: string;
|
|
9
|
-
tabsLabel: string;
|
|
10
|
-
optionsLabel: string;
|
|
11
|
-
tableLabel: string;
|
|
12
|
-
commandLabel: string;
|
|
13
|
-
commandPlaceholder: string;
|
|
14
|
-
dismissNotification: string;
|
|
15
|
-
toolbarLabel: string;
|
|
16
|
-
comboboxEmpty: string;
|
|
17
|
-
comboboxClear: string;
|
|
18
|
-
comboboxOpen: string;
|
|
19
|
-
comboboxRemove: string;
|
|
20
|
-
comboboxLoading: string;
|
|
21
|
-
fileDropPrompt: string;
|
|
22
|
-
fileAdded: string;
|
|
23
|
-
fileRemoved: string;
|
|
24
|
-
fileRemove: string;
|
|
25
|
-
fileTooLarge: string;
|
|
26
|
-
fileWrongType: string;
|
|
27
|
-
treeLabel: string;
|
|
28
|
-
notificationsLabel: string;
|
|
29
|
-
notificationMarkAll: string;
|
|
30
|
-
notificationEmpty: string;
|
|
31
|
-
notificationUnread: string;
|
|
32
|
-
notificationNew: string;
|
|
33
|
-
dataGridFilter: string;
|
|
34
|
-
dataGridEmpty: string;
|
|
35
|
-
dataGridSelectAll: string;
|
|
36
|
-
dataGridSelectRow: string;
|
|
37
|
-
mediaPlayer: string;
|
|
38
|
-
mediaPlay: string;
|
|
39
|
-
mediaPause: string;
|
|
40
|
-
mediaMute: string;
|
|
41
|
-
mediaUnmute: string;
|
|
42
|
-
mediaSeek: string;
|
|
43
|
-
mediaVolume: string;
|
|
44
|
-
mediaCaptionsShow: string;
|
|
45
|
-
mediaCaptionsHide: string;
|
|
46
|
-
mediaSkipBack: string;
|
|
47
|
-
mediaSkipForward: string;
|
|
48
|
-
mediaFullscreenEnter: string;
|
|
49
|
-
mediaFullscreenExit: string;
|
|
50
|
-
uploadSending: string;
|
|
51
|
-
uploadCancel: string;
|
|
52
|
-
uploadRetry: string;
|
|
53
|
-
uploadError: string;
|
|
54
|
-
uploadCanceled: string;
|
|
55
|
-
uploadComplete: string;
|
|
56
|
-
codeEditor: string;
|
|
57
|
-
chatLabel: string;
|
|
58
|
-
chatMessage: string;
|
|
59
|
-
chatSend: string;
|
|
60
|
-
qrCode: string;
|
|
61
|
-
copyCode: string;
|
|
62
|
-
tocLabel: string;
|
|
63
|
-
navigationToggle: string;
|
|
64
|
-
}
|
|
65
|
-
export declare const defaultStrings: AureaStrings;
|
|
66
|
-
export declare const ptBR: AureaStrings;
|
|
2
|
+
import { type AureaStrings } from "./pure.js";
|
|
3
|
+
export { cx, defaultStrings, ptBR, defaultSpriteUrl, universalStates, stateSeverity, type AureaStrings, type UniversalState } from "./pure.js";
|
|
67
4
|
export declare const StringsContext: import("react").Context<AureaStrings>;
|
|
68
5
|
export declare const useAureaStrings: () => AureaStrings;
|
|
69
|
-
export declare const defaultSpriteUrl = "/aurea-icons.svg";
|
|
70
6
|
export declare const SpriteContext: import("react").Context<string>;
|
|
71
7
|
export declare const useSpriteUrl: () => string;
|
|
8
|
+
export declare const PortalContext: import("react").Context<HTMLElement | null | undefined>;
|
|
9
|
+
export declare const usePortalContainer: () => HTMLElement | null | undefined;
|
|
72
10
|
export declare function Kbd({ children, className, ...props }: HTMLAttributes<HTMLElement> & RefAttributes<HTMLElement>): import("react").JSX.Element;
|
package/dist/internal.js
CHANGED
|
@@ -1,29 +1,40 @@
|
|
|
1
|
+
"use client";
|
|
1
2
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
+
// A diretiva na LINHA 1, como as referências a escrevem (Base UI e shadcn/ui, medidos em
|
|
4
|
+
// 06/08/2026) — Parte A do PLANO-1.0. Este módulo chama `createContext`, que é API só de
|
|
5
|
+
// cliente: sem a diretiva, `import {Button} from "@aurea-uds/react"` dentro de um componente
|
|
6
|
+
// de servidor quebrava na hora, e quebrava no consumidor.
|
|
7
|
+
//
|
|
8
|
+
// O que NÃO tem estado saiu daqui para o `pure.tsx` — está explicado lá. A reexportação abaixo
|
|
9
|
+
// existe para os 18 módulos de cliente seguirem importando `cx` de "./internal.js" sem mudança.
|
|
10
|
+
//
|
|
2
11
|
// Fase 9 (achado A5): este arquivo saiu do index.tsx de 970 linhas. Um módulo por categoria
|
|
3
12
|
// do registry — a taxonomia já existia e é gateada. A ordem de import entre eles é um DAG:
|
|
4
|
-
// internal → system → actions → feedback → inputs → navigation → layout → data-display → resto.
|
|
13
|
+
// pure → internal → system → actions → feedback → inputs → navigation → layout → data-display → resto.
|
|
5
14
|
// INTERNO: o que todo módulo precisa e ninguém publica como componente próprio.
|
|
6
15
|
// NÃO é subpath — `@aurea-uds/react/internal` não existe, de propósito.
|
|
7
16
|
// Kbd mora aqui por dependência, não por categoria: o Button precisa dele e ele não precisa de
|
|
8
17
|
// ninguém. A casa pública dele continua sendo `/data-display`, que o reexporta.
|
|
9
18
|
import { createContext, useContext } from "react";
|
|
10
|
-
|
|
11
|
-
export
|
|
12
|
-
// pt-BR preservado como locale (mesmos valores que já eram o default): passe
|
|
13
|
-
// <AureaProvider strings={ptBR}> para restaurar português.
|
|
14
|
-
export const ptBR = { close: "Fechar", paginationLabel: "Paginação", previous: "Anterior", next: "Próxima", breadcrumbLabel: "Breadcrumb", tabsLabel: "Abas", optionsLabel: "Opções", tableLabel: "Tabela", commandLabel: "Paleta de comandos", commandPlaceholder: "Digite um comando…", dismissNotification: "Dispensar notificação", toolbarLabel: "Barra de ferramentas", comboboxEmpty: "Nenhum resultado", comboboxClear: "Limpar seleção", comboboxOpen: "Abrir lista", comboboxRemove: "Remover", comboboxLoading: "Carregando…", fileDropPrompt: "Arraste arquivos aqui ou clique para selecionar", fileAdded: "Arquivo adicionado", fileRemoved: "Arquivo removido", fileRemove: "Remover", fileTooLarge: "Arquivo maior que o limite", fileWrongType: "Tipo de arquivo não aceito", treeLabel: "Árvore", notificationsLabel: "Notificações", notificationMarkAll: "Marcar todas como lidas", notificationEmpty: "Nenhuma notificação", notificationUnread: "Não lida", notificationNew: "novas notificações", dataGridFilter: "Filtrar", dataGridEmpty: "Nenhum resultado", dataGridSelectAll: "Selecionar todas as linhas", dataGridSelectRow: "Selecionar linha", mediaPlayer: "Reprodutor de mídia", mediaPlay: "Reproduzir", mediaPause: "Pausar", mediaMute: "Silenciar", mediaUnmute: "Ativar som", mediaSeek: "Posição da reprodução", mediaVolume: "Volume", mediaCaptionsShow: "Ativar legendas", mediaCaptionsHide: "Desativar legendas", mediaSkipBack: "Voltar 10 segundos", mediaSkipForward: "Avançar 10 segundos", mediaFullscreenEnter: "Tela cheia", mediaFullscreenExit: "Sair da tela cheia", uploadSending: "Enviando", uploadCancel: "Cancelar envio", uploadRetry: "Tentar envio novamente", uploadError: "Falha no envio", uploadCanceled: "Envio cancelado", uploadComplete: "Envio concluído", codeEditor: "Editor de código", chatLabel: "Conversa", chatMessage: "Mensagem", chatSend: "Enviar", qrCode: "Código QR", copyCode: "Copiar código", tocLabel: "Nesta página", navigationToggle: "Navegação" };
|
|
19
|
+
import { cx, defaultStrings, defaultSpriteUrl } from "./pure.js";
|
|
20
|
+
export { cx, defaultStrings, ptBR, defaultSpriteUrl, universalStates, stateSeverity } from "./pure.js";
|
|
15
21
|
export const StringsContext = createContext(defaultStrings);
|
|
16
22
|
export const useAureaStrings = () => useContext(StringsContext);
|
|
17
|
-
// Sprite de ícones: CONFIGURAÇÃO DE APLICAÇÃO, então entra pelo provider, uma vez.
|
|
18
|
-
// Antes o default absoluto "/aurea-icons.svg" vivia no Icon e 15 componentes declaravam
|
|
19
|
-
// e repassavam `spriteUrl` à mão — 52 sítios de código. Consequência: aplicação servida
|
|
20
|
-
// em subcaminho perdia TODOS os ícones, e para consertar tinha de passar a prop em cada
|
|
21
|
-
// componente (auditoria 26/07/2026, achado A4 — causa raiz do A3 daquela auditoria, que
|
|
22
|
-
// foi corrigido nos fallbacks do catálogo e não aqui). O default segue o mesmo para quem
|
|
23
|
-
// não configura nada; `Icon` aceita a prop como override local.
|
|
24
|
-
export const defaultSpriteUrl = "/aurea-icons.svg";
|
|
25
23
|
export const SpriteContext = createContext(defaultSpriteUrl);
|
|
26
24
|
export const useSpriteUrl = () => useContext(SpriteContext);
|
|
25
|
+
// ONDE OS POPUPS SÃO MONTADOS — achado B7, fechado na Parte C do PLANO-1.0 (07/08/2026).
|
|
26
|
+
// O DEFEITO não era o portal: era o consumidor não ter como mexer nele. Todo popup da Aurea
|
|
27
|
+
// (diálogo, gaveta, dica, popover, os dois menus, notificação, combobox, toast) monta num
|
|
28
|
+
// portal no nível do `body` — é o que permite empilhar e posicionar sem herdar `overflow` nem
|
|
29
|
+
// `transform` de ninguém. A consequência é que o conteúdo dele fica FORA de qualquer landmark,
|
|
30
|
+
// e o axe acusa `region` ("todo conteúdo num landmark"). A auditoria registrou, e escreveu que
|
|
31
|
+
// "a única forma de satisfazer seria o consumidor montar o portal dentro do landmark dele" —
|
|
32
|
+
// só que ele não tinha essa forma, porque o `container` do Base UI nunca foi exposto.
|
|
33
|
+
// Agora tem, e entra pelo provider, UMA vez, como o sprite: é configuração de aplicação, não
|
|
34
|
+
// prop de componente (foi a lição do achado A4, com 52 sítios repassando `spriteUrl` à mão).
|
|
35
|
+
// `undefined` é o default e mantém o comportamento de sempre: `document.body`.
|
|
36
|
+
export const PortalContext = createContext(undefined);
|
|
37
|
+
export const usePortalContainer = () => useContext(PortalContext);
|
|
27
38
|
// direction informa o Base UI (lado dos popovers, setas do teclado). O CSS
|
|
28
39
|
// espelha sozinho por propriedade lógica, mas depende do atributo dir no DOM:
|
|
29
40
|
// quem consome precisa pôr dir="rtl" no <html>. Este provider não mexe no DOM.
|
package/dist/layout.d.ts
CHANGED
|
@@ -6,9 +6,10 @@ export declare function Card({ variant, className, ...props }: HTMLAttributes<HT
|
|
|
6
6
|
export declare function Stack({ className, ...props }: HTMLAttributes<HTMLDivElement> & RefAttributes<HTMLDivElement>): import("react").JSX.Element;
|
|
7
7
|
export declare function Cluster({ className, ...props }: HTMLAttributes<HTMLDivElement> & RefAttributes<HTMLDivElement>): import("react").JSX.Element;
|
|
8
8
|
export declare function Grid({ className, ...props }: HTMLAttributes<HTMLDivElement> & RefAttributes<HTMLDivElement>): import("react").JSX.Element;
|
|
9
|
-
export declare function AppShell({ brand, navigation, topbar, topbarVariant, children, className, ...props }: HTMLAttributes<HTMLDivElement> & RefAttributes<HTMLDivElement> & {
|
|
9
|
+
export declare function AppShell({ brand, navigation, topbar, topbarVariant, sidebarCollapsed, children, className, ...props }: HTMLAttributes<HTMLDivElement> & RefAttributes<HTMLDivElement> & {
|
|
10
10
|
brand: ReactNode;
|
|
11
11
|
navigation: ReactNode;
|
|
12
12
|
topbar?: ReactNode;
|
|
13
13
|
topbarVariant?: Exclude<TopbarVariant, "pill">;
|
|
14
|
+
sidebarCollapsed?: boolean;
|
|
14
15
|
}): import("react").JSX.Element;
|
package/dist/layout.js
CHANGED
|
@@ -1,7 +1,51 @@
|
|
|
1
|
+
"use client";
|
|
1
2
|
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
3
|
import { cx, useAureaStrings } from "./internal.js";
|
|
3
4
|
import { IconButton } from "./actions.js";
|
|
4
5
|
import { Sidebar, Topbar } from "./navigation.js";
|
|
6
|
+
// ── Auxiliar de topo: mora ANTES do primeiro export, e a posição é obrigatória ───────────────
|
|
7
|
+
// O check 22 mede o corpo de um componente do `export` dele até o PRÓXIMO export — fatia longa
|
|
8
|
+
// de propósito, porque a implementação de vários componentes daqui continua num auxiliar não
|
|
9
|
+
// exportado logo abaixo (a `Sidebar` é o caso: o `aria-current` dela mora num). Encurtar a fatia
|
|
10
|
+
// foi tentado em 09/08/2026 e a prova contra o defeito recusou: a `Sidebar` com `states: []`
|
|
11
|
+
// deixava de ser acusada.
|
|
12
|
+
// Consequência: auxiliar escrito ENTRE dois exports é lido como parte do componente anterior.
|
|
13
|
+
// Esta função tem `button:not([disabled])` num seletor, e o `\bdisabled\b` da regra acusou o
|
|
14
|
+
// `Grid` de emitir estado que ele não emite. Escrita aqui em cima, não pertence à fatia de
|
|
15
|
+
// ninguém. **Auxiliar de topo em módulo de componente vem antes dos exports.**
|
|
16
|
+
//
|
|
17
|
+
// O que ela faz e por que existe: o popover nativo não entrega a mesma gestão de foco nos três
|
|
18
|
+
// motores, e a tabela medida está no `aurea.js` do core. As mesmas cinco linhas moram lá, e a
|
|
19
|
+
// repetição é deliberada porque são DOIS RUNTIMES, não dois desenhos — o do core é delegado e
|
|
20
|
+
// serve página estática e consumo vanilla (é o que o catálogo carrega); este serve quem instala
|
|
21
|
+
// só o `@aurea-uds/react`, que importa o CSS do core e nunca o JS dele. Rodar os dois é
|
|
22
|
+
// inofensivo: focar o mesmo elemento duas vezes não faz nada.
|
|
23
|
+
//
|
|
24
|
+
// A comparação é com `"closed"` e NÃO com `"open"`, e isso é cicatriz — não simplifique de
|
|
25
|
+
// volta. O `ToggleEvent.newState` só tem esses dois valores, então as duas formas são idênticas
|
|
26
|
+
// em efeito; a diferença é no GATE. O check 15 trata qualquer string do fonte como possível nome
|
|
27
|
+
// de classe (de propósito: a versão estrita não vê `badge-${variant}` e quase fez o D1 apagar
|
|
28
|
+
// classes vivas). Com o literal `"open"` aqui, a `.drawer-wrap.open` do core — resto morto da
|
|
29
|
+
// página vanilla removida na Parte D — passava a parecer PRODUZIDA pela biblioteca, e a catraca
|
|
30
|
+
// queria travar um ganho falso.
|
|
31
|
+
function focoDaGaveta(e) {
|
|
32
|
+
const gaveta = e.currentTarget;
|
|
33
|
+
// Primeiro focável de DENTRO, não a gaveta: o WebKit não entra no conteúdo do popover pelo
|
|
34
|
+
// Tab nem com o foco no elemento dele — medido no K1.
|
|
35
|
+
if (e.newState !== "closed") {
|
|
36
|
+
const primeiro = gaveta.querySelector('a[href], button:not([disabled]), input:not([disabled]), select, textarea, [tabindex]:not([tabindex="-1"])');
|
|
37
|
+
if (primeiro)
|
|
38
|
+
primeiro.focus();
|
|
39
|
+
else {
|
|
40
|
+
gaveta.tabIndex = -1;
|
|
41
|
+
gaveta.focus();
|
|
42
|
+
}
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
// Só devolve o foco se ele ficou órfão; se a pessoa já o moveu, puxá-lo de volta seria roubo.
|
|
46
|
+
if (document.activeElement === document.body || gaveta.contains(document.activeElement))
|
|
47
|
+
document.querySelector(`[popovertarget="${gaveta.id}"]`)?.focus();
|
|
48
|
+
}
|
|
5
49
|
export function Card({ variant = "base", className, ...props }) { return _jsx("div", { className: cx("card", variant !== "base" && `card-${variant}`, className), ...props }); }
|
|
6
50
|
export function Stack({ className, ...props }) { return _jsx("div", { className: cx("stack", className), ...props }); }
|
|
7
51
|
export function Cluster({ className, ...props }) { return _jsx("div", { className: cx("cluster", className), ...props }); }
|
|
@@ -10,13 +54,23 @@ export function Grid({ className, ...props }) { return _jsx("div", { className:
|
|
|
10
54
|
// marca sai — no topo, não na lateral. `topbarVariant` escolhe a pele do topo; o shell
|
|
11
55
|
// precisa saber porque a lateral se encaixa abaixo dele (o `flush` não tem folga em
|
|
12
56
|
// cima, então ela sobe um --space-4). `pill` é cabeçalho de site — não é pra shell.
|
|
13
|
-
// A lateral RECOLHE em tela estreita
|
|
14
|
-
//
|
|
15
|
-
//
|
|
16
|
-
//
|
|
17
|
-
//
|
|
18
|
-
//
|
|
57
|
+
// A lateral RECOLHE em tela estreita: o <aside> é um popover nativo e o botão é o invoker. O
|
|
58
|
+
// navegador entrega Escape, clique fora e o `aria-expanded` no disparador. Era o achado A8: em
|
|
59
|
+
// 375px o `h1` da página começava 4,6 telas abaixo, depois dos 65 itens da navegação.
|
|
60
|
+
// Não-modal de propósito, como o CommandPaletteShell: o foco não fica preso, e por isso não
|
|
61
|
+
// declaramos modalidade que não entregamos.
|
|
62
|
+
//
|
|
63
|
+
// ATÉ 09/08/2026 ESTE COMENTÁRIO DIZIA "sem uma linha de JavaScript" E QUE O NAVEGADOR TAMBÉM
|
|
64
|
+
// ENTREGAVA A VOLTA DO FOCO. Entregava — no Chromium, que era o único motor onde a suíte
|
|
65
|
+
// rodava. O item K1 pôs Firefox e WebKit no `playwright.config.ts` e a medição derrubou a
|
|
66
|
+
// frase: no WebKit o foco vai para o <body> ao abrir, OITO Tabs não entram na gaveta e o
|
|
67
|
+
// Escape não devolve nada. Gaveta aberta e inalcançável por teclado é WCAG 2.1.1.
|
|
68
|
+
// A gestão de foco agora é NOSSA, igual nos três motores — `focoDaGaveta`, abaixo.
|
|
19
69
|
// Id fixo em vez de useId: um documento tem UM AppShell (ele é dono do <main>), então não há
|
|
20
70
|
// colisão possível — e o CSS e o gate precisam de um alvo estável.
|
|
71
|
+
// `sidebarCollapsed` é CONTROLADO pelo consumidor, como o `open` do CommandPaletteShell: o
|
|
72
|
+
// shell não decide quando recolher nem desenha o botão que recolhe — quem sabe se há espaço,
|
|
73
|
+
// e se a preferência se guarda, é a aplicação. O que o shell faz é o que só ele pode: a coluna
|
|
74
|
+
// do grid encolhe para --sidebar-rail (a regra `:has(.sidebar-collapsed)` no core).
|
|
21
75
|
const SHELL_NAV_ID = "aurea-shell-nav";
|
|
22
|
-
export function AppShell({ brand, navigation, topbar, topbarVariant = "floating", children, className, ...props }) { const s = useAureaStrings(); return _jsxs("div", { className: cx("app-shell", topbarVariant === "flush" && "app-shell-flush", className), ...props, children: [_jsx(Topbar, { variant: topbarVariant, brand: _jsxs(_Fragment, { children: [_jsx(IconButton, { className: "nav-toggle", icon: "menu", label: s.navigationToggle, popoverTarget: SHELL_NAV_ID }), brand] }), children: topbar }), _jsx(Sidebar, { id: SHELL_NAV_ID, popover: "auto", children: navigation }), _jsx("main", { className: "content", children: children })] }); }
|
|
76
|
+
export function AppShell({ brand, navigation, topbar, topbarVariant = "floating", sidebarCollapsed, children, className, ...props }) { const s = useAureaStrings(); return _jsxs("div", { className: cx("app-shell", topbarVariant === "flush" && "app-shell-flush", className), ...props, children: [_jsx(Topbar, { variant: topbarVariant, brand: _jsxs(_Fragment, { children: [_jsx(IconButton, { className: "nav-toggle", icon: "menu", label: s.navigationToggle, popoverTarget: SHELL_NAV_ID }), brand] }), children: topbar }), _jsx(Sidebar, { id: SHELL_NAV_ID, popover: "auto", collapsed: sidebarCollapsed, onToggle: focoDaGaveta, children: navigation }), _jsx("main", { className: "content", children: children })] }); }
|
package/dist/media.js
CHANGED
package/dist/navigation.d.ts
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
import React, { type HTMLAttributes, type RefAttributes, type ReactNode, type ReactElement } from "react";
|
|
2
2
|
import { type IconName } from "./system.js";
|
|
3
|
+
export type StepState = "default" | "active" | "done" | "error";
|
|
4
|
+
export interface StepItem {
|
|
5
|
+
label: ReactNode;
|
|
6
|
+
state?: StepState;
|
|
7
|
+
optional?: ReactNode;
|
|
8
|
+
onClick?: () => void;
|
|
9
|
+
}
|
|
10
|
+
export declare function Stepper({ items, label, className }: {
|
|
11
|
+
items: StepItem[];
|
|
12
|
+
label?: string;
|
|
13
|
+
className?: string;
|
|
14
|
+
}): React.JSX.Element;
|
|
3
15
|
export declare function Breadcrumb({ items, label }: {
|
|
4
16
|
items: Array<{
|
|
5
17
|
label: ReactNode;
|
|
@@ -45,7 +57,21 @@ export declare function TreeView({ items, defaultExpandedIds, onSelect, label, c
|
|
|
45
57
|
label?: string;
|
|
46
58
|
className?: string;
|
|
47
59
|
}): ReactElement<unknown, string | React.JSXElementConstructor<any>>;
|
|
48
|
-
export
|
|
60
|
+
export interface SidebarItem {
|
|
61
|
+
id: string;
|
|
62
|
+
label: ReactNode;
|
|
63
|
+
href?: string;
|
|
64
|
+
icon?: IconName;
|
|
65
|
+
badge?: ReactNode;
|
|
66
|
+
onClick?: () => void;
|
|
67
|
+
items?: SidebarItem[];
|
|
68
|
+
}
|
|
69
|
+
export declare function Sidebar({ items, current, collapsed, label, children, className, ...props }: HTMLAttributes<HTMLElement> & RefAttributes<HTMLElement> & {
|
|
70
|
+
items?: SidebarItem[];
|
|
71
|
+
current?: string;
|
|
72
|
+
collapsed?: boolean;
|
|
73
|
+
label?: string;
|
|
74
|
+
}): React.JSX.Element;
|
|
49
75
|
export type TopbarVariant = "floating" | "flush" | "pill";
|
|
50
76
|
export declare function Topbar({ variant, brand, children, className, ...props }: HTMLAttributes<HTMLElement> & RefAttributes<HTMLElement> & {
|
|
51
77
|
variant?: TopbarVariant;
|
package/dist/navigation.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
|
|
1
|
+
"use client";
|
|
2
|
+
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
3
|
// Fase 9 (achado A5): este arquivo saiu do index.tsx de 970 linhas. Um módulo por categoria
|
|
3
4
|
// do registry — a taxonomia já existia e é gateada. A ordem de import entre eles é um DAG:
|
|
4
5
|
// internal → system → actions → feedback → inputs → navigation → layout → data-display → resto.
|
|
@@ -9,6 +10,15 @@ import { Icon } from "./system.js";
|
|
|
9
10
|
import { Button } from "./actions.js";
|
|
10
11
|
import { Badge } from "./feedback.js";
|
|
11
12
|
import { SearchField } from "./inputs.js";
|
|
13
|
+
export function Stepper({ items, label, className }) {
|
|
14
|
+
const s = useAureaStrings();
|
|
15
|
+
return _jsx("div", { role: "list", "aria-label": label ?? s.stepperLabel, className: cx("stepper", className), children: items.map((it, n) => {
|
|
16
|
+
const st = it.state ?? "default";
|
|
17
|
+
const marca = st === "done" ? _jsx(Icon, { name: "checkmark" }) : st === "error" ? _jsx(Icon, { name: "error" }) : n + 1;
|
|
18
|
+
const miolo = _jsxs(_Fragment, { children: [_jsx("span", { className: "step-dot", children: marca }), _jsx("strong", { children: it.label }), it.optional && _jsx("small", { className: "step-optional", children: it.optional })] });
|
|
19
|
+
return _jsx("div", { role: "listitem", className: cx("step", st !== "default" && `step-${st}`), "aria-current": st === "active" ? "step" : undefined, children: it.onClick ? _jsx("button", { type: "button", className: "step-trigger", onClick: it.onClick, children: miolo }) : miolo }, n);
|
|
20
|
+
}) });
|
|
21
|
+
}
|
|
12
22
|
export function Breadcrumb({ items, label }) { const s = useAureaStrings(); return _jsx("nav", { className: "breadcrumb", "aria-label": label ?? s.breadcrumbLabel, children: items.map((i, n) => _jsxs(React.Fragment, { children: [n > 0 && _jsx(Icon, { name: "chevron--right", size: "sm" }), " ", i.href ? _jsx("a", { href: i.href, children: i.label }) : _jsx("strong", { "aria-current": "page", children: i.label })] }, n)) }); }
|
|
13
23
|
export function Tabs({ tabs, value, onChange, label }) { const s = useAureaStrings(); return _jsxs(BaseTabs.Root, { value: value, onValueChange: v => onChange(String(v)), children: [_jsx(BaseTabs.List, { className: "tabs", "aria-label": label ?? s.tabsLabel, activateOnFocus: true, children: tabs.map(t => _jsx(BaseTabs.Tab, { value: t.id, className: "tab", children: t.label }, t.id)) }), tabs.map(t => _jsx(BaseTabs.Panel, { value: t.id, className: "card card-inset", tabIndex: 0, children: t.content }, t.id))] }); }
|
|
14
24
|
export function Pagination({ page, total, onPageChange }) { const s = useAureaStrings(); return _jsxs("nav", { className: "pagination", "aria-label": s.paginationLabel, children: [_jsx(Button, { variant: "ghost", size: "sm", disabled: page <= 1, onClick: () => onPageChange(page - 1), children: s.previous }), _jsxs(Badge, { variant: "primary", children: [page, " / ", total] }), _jsx(Button, { variant: "ghost", size: "sm", disabled: page >= total, onClick: () => onPageChange(page + 1), children: s.next })] }); }
|
|
@@ -98,12 +108,27 @@ export function TreeView({ items, defaultExpandedIds, onSelect, label, className
|
|
|
98
108
|
}) }));
|
|
99
109
|
return renderNodes(items, 1);
|
|
100
110
|
}
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
//
|
|
106
|
-
|
|
111
|
+
function sidebarList(items, ctx, sub, labelledBy) {
|
|
112
|
+
return _jsx("ul", { className: cx("sidebar-list", sub && "sidebar-sub"), "aria-labelledby": labelledBy, children: items.map(it => {
|
|
113
|
+
const lid = ctx.baseId + it.id;
|
|
114
|
+
const filhos = it.items?.length ? it.items : undefined;
|
|
115
|
+
// Rótulo escondido vira `.sr-only` em vez de sumir do DOM: na lateral recolhida o item
|
|
116
|
+
// continua tendo nome para quem usa leitor de tela. Ícone sozinho não nomeia nada.
|
|
117
|
+
const oculto = (no) => ctx.collapsed ? _jsx("span", { className: "sr-only", children: no }) : no;
|
|
118
|
+
if (filhos && !it.href && !it.onClick)
|
|
119
|
+
return _jsxs("li", { children: [_jsx("p", { id: lid, className: cx("sidebar-group-label", ctx.collapsed && "sr-only"), children: it.label }), sidebarList(filhos, ctx, false, lid)] }, it.id);
|
|
120
|
+
const ativo = it.id === ctx.current;
|
|
121
|
+
const miolo = _jsxs(_Fragment, { children: [it.icon && _jsx(Icon, { name: it.icon, size: "sm" }), _jsx("span", { className: cx("sidebar-label", ctx.collapsed && "sr-only"), children: it.label }), it.badge != null && oculto(it.badge)] });
|
|
122
|
+
return _jsxs("li", { children: [it.href
|
|
123
|
+
? _jsx("a", { id: lid, href: it.href, className: "sidebar-item", "aria-current": ativo ? "page" : undefined, onClick: it.onClick, children: miolo })
|
|
124
|
+
: _jsx("button", { id: lid, type: "button", className: "sidebar-item", "aria-current": ativo ? "page" : undefined, onClick: it.onClick, children: miolo }), filhos && sidebarList(filhos, ctx, true, lid)] }, it.id);
|
|
125
|
+
}) });
|
|
126
|
+
}
|
|
127
|
+
export function Sidebar({ items, current, collapsed, label, children, className, ...props }) {
|
|
128
|
+
const s = useAureaStrings();
|
|
129
|
+
const baseId = React.useId();
|
|
130
|
+
return _jsxs("aside", { className: cx("sidebar", collapsed && "sidebar-collapsed", className), ...props, children: [items && items.length > 0 && _jsx("nav", { className: "sidebar-nav", "aria-label": label ?? s.sidebarLabel, children: sidebarList(items, { baseId, current, collapsed }) }), children] });
|
|
131
|
+
}
|
|
107
132
|
export function Topbar({ variant = "floating", brand, children, className, ...props }) { return _jsxs("header", { className: cx("topbar", `topbar-${variant}`, className), ...props, children: [brand != null && _jsx("div", { className: "brand", children: brand }), children] }); }
|
|
108
133
|
// Shell NÃO-modal de propósito: é presentational (o consumidor controla open) e não
|
|
109
134
|
// tem focus trap/inert/Escape — declarar aria-modal sem entregar a modalidade faria
|
package/dist/overlays.d.ts
CHANGED
|
@@ -26,6 +26,11 @@ export declare function Popover({ trigger, title, children, side }: {
|
|
|
26
26
|
children: ReactNode;
|
|
27
27
|
side?: OverlaySide;
|
|
28
28
|
}): React.JSX.Element;
|
|
29
|
+
export declare function HoverCard({ trigger, children, side }: {
|
|
30
|
+
trigger: ReactElement;
|
|
31
|
+
children: ReactNode;
|
|
32
|
+
side?: OverlaySide;
|
|
33
|
+
}): React.JSX.Element;
|
|
29
34
|
export interface MenuItemDef {
|
|
30
35
|
label: ReactNode;
|
|
31
36
|
onClick?: () => void;
|
package/dist/overlays.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
"use client";
|
|
1
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
3
|
// Fase 9 (achado A5): este arquivo saiu do index.tsx de 970 linhas. Um módulo por categoria
|
|
3
4
|
// do registry — a taxonomia já existia e é gateada. A ordem de import entre eles é um DAG:
|
|
@@ -8,29 +9,40 @@ import { Popover as BasePopover } from "@base-ui/react/popover";
|
|
|
8
9
|
import { Tooltip as BaseTooltip } from "@base-ui/react/tooltip";
|
|
9
10
|
import { Menu as BaseMenu } from "@base-ui/react/menu";
|
|
10
11
|
import { ContextMenu as BaseContextMenu } from "@base-ui/react/context-menu";
|
|
11
|
-
import {
|
|
12
|
+
import { PreviewCard as BasePreviewCard } from "@base-ui/react/preview-card";
|
|
13
|
+
import { cx, useAureaStrings, usePortalContainer } from "./internal.js";
|
|
12
14
|
import { Icon } from "./system.js";
|
|
13
|
-
export function Dialog({ open, title, children, footer, onClose }) { const s = useAureaStrings(); return _jsx(BaseDialog.Root, { open: open, onOpenChange: o => { if (!o)
|
|
14
|
-
onClose(); }, children: _jsxs(BaseDialog.Portal, { children: [_jsx(BaseDialog.Backdrop, { className: "dialog-backdrop" }), _jsxs(BaseDialog.Popup, { className: "dialog", children: [_jsxs("header", { children: [_jsx(BaseDialog.Title, { render: _jsx("h2", {}), children: title }), _jsx(BaseDialog.Close, { className: "btn btn-ghost btn-icon", "aria-label": s.close, children: _jsx(Icon, { name: "close" }) })] }), _jsx("div", { className: "dialog-body", children: children }), footer && _jsx("footer", { children: footer })] })] }) }); }
|
|
15
|
-
export function Drawer({ open, title, children, onClose, side = "right" }) { const s = useAureaStrings(); return _jsx(BaseDialog.Root, { open: open, onOpenChange: o => { if (!o)
|
|
16
|
-
onClose(); }, children: _jsxs(BaseDialog.Portal, { children: [_jsx(BaseDialog.Backdrop, { className: "drawer-backdrop" }), _jsxs(BaseDialog.Popup, { className: cx("drawer", `drawer-${side}`), children: [_jsxs("header", { children: [_jsx(BaseDialog.Title, { render: _jsx("h2", {}), children: title }), _jsx(BaseDialog.Close, { className: "btn btn-ghost btn-icon", "aria-label": s.close, children: _jsx(Icon, { name: "close" }) })] }), children] })] }) }); }
|
|
15
|
+
export function Dialog({ open, title, children, footer, onClose }) { const s = useAureaStrings(); const portal = usePortalContainer(); return _jsx(BaseDialog.Root, { open: open, onOpenChange: o => { if (!o)
|
|
16
|
+
onClose(); }, children: _jsxs(BaseDialog.Portal, { container: portal, children: [_jsx(BaseDialog.Backdrop, { className: "dialog-backdrop" }), _jsxs(BaseDialog.Popup, { className: "dialog", children: [_jsxs("header", { children: [_jsx(BaseDialog.Title, { render: _jsx("h2", {}), children: title }), _jsx(BaseDialog.Close, { className: "btn btn-ghost btn-icon", "aria-label": s.close, children: _jsx(Icon, { name: "close" }) })] }), _jsx("div", { className: "dialog-body", children: children }), footer && _jsx("footer", { children: footer })] })] }) }); }
|
|
17
|
+
export function Drawer({ open, title, children, onClose, side = "right" }) { const s = useAureaStrings(); const portal = usePortalContainer(); return _jsx(BaseDialog.Root, { open: open, onOpenChange: o => { if (!o)
|
|
18
|
+
onClose(); }, children: _jsxs(BaseDialog.Portal, { container: portal, children: [_jsx(BaseDialog.Backdrop, { className: "drawer-backdrop" }), _jsxs(BaseDialog.Popup, { className: cx("drawer", `drawer-${side}`), children: [_jsxs("header", { children: [_jsx(BaseDialog.Title, { render: _jsx("h2", {}), children: title }), _jsx(BaseDialog.Close, { className: "btn btn-ghost btn-icon", "aria-label": s.close, children: _jsx(Icon, { name: "close" }) })] }), children] })] }) }); }
|
|
17
19
|
// `role="tooltip"` + `aria-describedby` no disparador é o que faz a dica EXISTIR para leitor
|
|
18
20
|
// de tela. Medido em 30/07/2026: sem isso, o popup saía sem role e sem id, e o disparador sem
|
|
19
21
|
// aria-describedby — quem navega por leitor de tela ouvia só o rótulo do botão e nunca o
|
|
20
22
|
// conteúdo da dica. Era tooltip visual, não acessível (padrão APG Tooltip).
|
|
21
23
|
// O id aponta para um elemento que só existe quando aberto; referência pendente é ignorada
|
|
22
24
|
// pela tecnologia assistiva, então não precisa acompanhar o estado.
|
|
23
|
-
export function Tooltip({ children, content, side = "top" }) { const id = React.useId(); return _jsxs(BaseTooltip.Root, { children: [_jsx(BaseTooltip.Trigger, { render: children, "aria-describedby": id }), _jsx(BaseTooltip.Portal, { children: _jsx(BaseTooltip.Positioner, { side: side, sideOffset: 8, children: _jsx(BaseTooltip.Popup, { id: id, role: "tooltip", className: "tooltip", children: content }) }) })] }); }
|
|
24
|
-
export function Popover({ trigger, title, children, side = "bottom" }) { return _jsxs(BasePopover.Root, { children: [_jsx(BasePopover.Trigger, { render: trigger }), _jsx(BasePopover.Portal, { children: _jsx(BasePopover.Positioner, { side: side, sideOffset: 8, children: _jsxs(BasePopover.Popup, { className: "popover", children: [title && _jsx(BasePopover.Title, { render: _jsx("strong", {}), children: title }), children] }) }) })] }); }
|
|
25
|
+
export function Tooltip({ children, content, side = "top" }) { const id = React.useId(); const portal = usePortalContainer(); return _jsxs(BaseTooltip.Root, { children: [_jsx(BaseTooltip.Trigger, { render: children, "aria-describedby": id }), _jsx(BaseTooltip.Portal, { container: portal, children: _jsx(BaseTooltip.Positioner, { side: side, sideOffset: 8, children: _jsx(BaseTooltip.Popup, { id: id, role: "tooltip", className: "tooltip", children: content }) }) })] }); }
|
|
26
|
+
export function Popover({ trigger, title, children, side = "bottom" }) { const portal = usePortalContainer(); return _jsxs(BasePopover.Root, { children: [_jsx(BasePopover.Trigger, { render: trigger }), _jsx(BasePopover.Portal, { container: portal, children: _jsx(BasePopover.Positioner, { side: side, sideOffset: 8, children: _jsxs(BasePopover.Popup, { className: "popover", children: [title && _jsx(BasePopover.Title, { render: _jsx("strong", {}), children: title }), children] }) }) })] }); }
|
|
27
|
+
// HoverCard (Lote 1 do BUILDING.md). Não é Tooltip nem Popover, e a diferença é de propósito,
|
|
28
|
+
// não de aparência: a Tooltip é um RÓTULO curto (`role="tooltip"`, some ao mover o mouse); o
|
|
29
|
+
// Popover abre por CLIQUE e pode conter foco; este é uma PRÉVIA rica que aparece ao repousar o
|
|
30
|
+
// ponteiro sobre um link e cujo conteúdo é alcançável — o cartão de perfil ao passar sobre um
|
|
31
|
+
// nome. As três referências que o têm chamam de hover-card ou preview-card e concordam nisso.
|
|
32
|
+
// Superfície reusa `.popover` de propósito: é a mesma camada flutuante do sistema, e dar a ela
|
|
33
|
+
// um segundo nome criaria duas peles para a mesma coisa.
|
|
34
|
+
// Por depender de repouso do ponteiro, NÃO serve para informação essencial — quem navega só por
|
|
35
|
+
// teclado ou toque não abre um hover card. Conteúdo obrigatório vai em Popover.
|
|
36
|
+
export function HoverCard({ trigger, children, side = "bottom" }) { const portal = usePortalContainer(); return _jsxs(BasePreviewCard.Root, { children: [_jsx(BasePreviewCard.Trigger, { render: trigger }), _jsx(BasePreviewCard.Portal, { container: portal, children: _jsx(BasePreviewCard.Positioner, { side: side, sideOffset: 8, children: _jsx(BasePreviewCard.Popup, { className: "popover hover-card", children: children }) }) })] }); }
|
|
25
37
|
// ContextMenu.Item/.Separator/.Popup são os MESMOS componentes de Menu.* no Base UI,
|
|
26
38
|
// então os itens renderizam igual nos dois menus.
|
|
27
39
|
const renderMenuItems = (items) => items.map((it, i) => it === "separator"
|
|
28
40
|
? _jsx(BaseMenu.Separator, { className: "menu-sep" }, i)
|
|
29
41
|
: _jsxs(BaseMenu.Item, { className: "menu-item", disabled: it.disabled, onClick: it.onClick, children: [it.leadingIcon && _jsx(Icon, { name: it.leadingIcon }), it.label] }, i));
|
|
30
|
-
export function DropdownMenu({ trigger, items, side = "bottom", label }) { return _jsxs(BaseMenu.Root, { children: [_jsx(BaseMenu.Trigger, { render: trigger }), _jsx(BaseMenu.Portal, { children: _jsx(BaseMenu.Positioner, { side: side, sideOffset: 6, children: _jsx(BaseMenu.Popup, { className: "menu", "aria-label": label, children: renderMenuItems(items) }) }) })] }); }
|
|
42
|
+
export function DropdownMenu({ trigger, items, side = "bottom", label }) { const portal = usePortalContainer(); return _jsxs(BaseMenu.Root, { children: [_jsx(BaseMenu.Trigger, { render: trigger }), _jsx(BaseMenu.Portal, { container: portal, children: _jsx(BaseMenu.Positioner, { side: side, sideOffset: 6, children: _jsx(BaseMenu.Popup, { className: "menu", "aria-label": label, children: renderMenuItems(items) }) }) })] }); }
|
|
31
43
|
// ContextMenu: abre no botão direito e — por teclado — em Shift+F10 / tecla Menu,
|
|
32
44
|
// que o browser só dispara (como evento contextmenu) sobre um elemento FOCADO. Por
|
|
33
45
|
// isso o gatilho é focável (tabIndex 0) e tem nome acessível (auditoria 18/07/2026,
|
|
34
46
|
// MÉDIO 3); sem isso o teclado não alcança o menu. Consumidor pode sobrescrever
|
|
35
47
|
// tabIndex via children se o próprio já for focável.
|
|
36
|
-
export function ContextMenu({ children, items, label, className }) { return _jsxs(BaseContextMenu.Root, { children: [_jsx(BaseContextMenu.Trigger, { className: className, tabIndex: 0, "aria-label": label, "aria-haspopup": "menu", children: children }), _jsx(BaseContextMenu.Portal, { children: _jsx(BaseContextMenu.Positioner, { children: _jsx(BaseContextMenu.Popup, { className: "menu", "aria-label": label, children: renderMenuItems(items) }) }) })] }); }
|
|
48
|
+
export function ContextMenu({ children, items, label, className }) { const portal = usePortalContainer(); return _jsxs(BaseContextMenu.Root, { children: [_jsx(BaseContextMenu.Trigger, { className: className, tabIndex: 0, "aria-label": label, "aria-haspopup": "menu", children: children }), _jsx(BaseContextMenu.Portal, { container: portal, children: _jsx(BaseContextMenu.Positioner, { children: _jsx(BaseContextMenu.Popup, { className: "menu", "aria-label": label, children: renderMenuItems(items) }) }) })] }); }
|