@aurea-uds/react 0.5.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/actions.d.ts +35 -7
- package/dist/actions.js +76 -7
- package/dist/communication.js +3 -2
- package/dist/data-grid.js +149 -9
- package/dist/disclosure.d.ts +9 -0
- package/dist/disclosure.js +19 -0
- package/dist/escala.d.ts +26 -0
- package/dist/escala.js +17 -0
- package/dist/feedback-client.d.ts +5 -2
- package/dist/feedback-client.js +28 -2
- package/dist/file-input.js +2 -2
- package/dist/graph.js +8 -3
- package/dist/identity-client.d.ts +2 -1
- package/dist/identity-client.js +6 -2
- package/dist/index.d.ts +5 -3
- package/dist/index.js +11 -2
- package/dist/inputs-client.d.ts +28 -11
- package/dist/inputs-client.js +46 -18
- package/dist/inputs.d.ts +1 -0
- package/dist/inputs.js +4 -0
- package/dist/internal.d.ts +23 -2
- package/dist/internal.js +19 -1
- package/dist/layout-client.d.ts +4 -0
- package/dist/layout-client.js +41 -1
- package/dist/layout.d.ts +2 -1
- package/dist/layout.js +3 -1
- package/dist/markup.d.ts +46 -14
- package/dist/markup.js +67 -17
- package/dist/navigation-client.d.ts +8 -2
- package/dist/navigation-client.js +60 -3
- package/dist/overlays.d.ts +79 -4
- package/dist/overlays.js +65 -6
- package/dist/pure.d.ts +98 -0
- package/dist/pure.js +120 -2
- package/dist/qrcode.d.ts +2 -1
- package/dist/qrcode.js +3 -2
- package/dist/responsivo-runtime.d.ts +20 -0
- package/dist/responsivo-runtime.js +201 -0
- package/dist/system.d.ts +12 -4
- package/dist/system.js +46 -4
- package/package.json +1 -1
package/dist/markup.d.ts
CHANGED
|
@@ -1,14 +1,19 @@
|
|
|
1
|
-
import React, { type HTMLAttributes, type InputHTMLAttributes, type ReactNode, type RefAttributes, type SelectHTMLAttributes, type TextareaHTMLAttributes } from "react";
|
|
1
|
+
import React, { type HTMLAttributes, type InputHTMLAttributes, type ReactElement, type ReactNode, type RefAttributes, type SelectHTMLAttributes, type TextareaHTMLAttributes } from "react";
|
|
2
|
+
import { type Responsive } from "./pure.js";
|
|
3
|
+
import type { ComponentSize } from "./actions.js";
|
|
2
4
|
export type BadgeVariant = "neutral" | "primary" | "info" | "success" | "warning" | "danger" | "running" | "paused" | "offline" | "review";
|
|
3
5
|
export type AvatarSize = "sm" | "md" | "lg";
|
|
4
6
|
export type TopbarVariant = "floating" | "flush" | "pill";
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
7
|
+
type CardBase = HTMLAttributes<HTMLDivElement> & RefAttributes<HTMLDivElement>;
|
|
8
|
+
export type CardProps = CardBase & ({
|
|
9
|
+
variant?: "base" | "raised" | "inset" | "selected" | "danger";
|
|
10
|
+
render?: ReactElement;
|
|
11
|
+
} | {
|
|
12
|
+
variant: "interactive";
|
|
13
|
+
render: ReactElement;
|
|
14
|
+
});
|
|
15
|
+
export declare function Card({ variant, className, render, ...props }: CardProps): ReactElement<unknown, string | React.JSXElementConstructor<any>>;
|
|
8
16
|
export declare function Stack({ className, ...props }: HTMLAttributes<HTMLDivElement> & RefAttributes<HTMLDivElement>): React.JSX.Element;
|
|
9
|
-
export declare function Separator({ orientation, className, ...props }: HTMLAttributes<HTMLHRElement> & RefAttributes<HTMLHRElement> & {
|
|
10
|
-
orientation?: "horizontal" | "vertical";
|
|
11
|
-
}): React.JSX.Element;
|
|
12
17
|
export declare function Cluster({ className, ...props }: HTMLAttributes<HTMLDivElement> & RefAttributes<HTMLDivElement>): React.JSX.Element;
|
|
13
18
|
export declare function Grid({ className, ...props }: HTMLAttributes<HTMLDivElement> & RefAttributes<HTMLDivElement>): React.JSX.Element;
|
|
14
19
|
export declare function KPI({ label, value, trend, className, ...props }: HTMLAttributes<HTMLDivElement> & RefAttributes<HTMLDivElement> & {
|
|
@@ -71,7 +76,7 @@ export declare function AvatarGroup({ children, max, total, label, size, classNa
|
|
|
71
76
|
max?: number;
|
|
72
77
|
total?: number;
|
|
73
78
|
label?: string;
|
|
74
|
-
size?: AvatarSize
|
|
79
|
+
size?: Responsive<AvatarSize>;
|
|
75
80
|
className?: string;
|
|
76
81
|
}): React.JSX.Element;
|
|
77
82
|
export declare function LogStream({ lines }: {
|
|
@@ -87,17 +92,44 @@ export declare function Topbar({ variant, brand, children, className, ...props }
|
|
|
87
92
|
brand?: ReactNode;
|
|
88
93
|
}): React.JSX.Element;
|
|
89
94
|
export declare function Kbd({ children, className, ...props }: HTMLAttributes<HTMLElement> & RefAttributes<HTMLElement>): React.JSX.Element;
|
|
90
|
-
export
|
|
91
|
-
|
|
92
|
-
|
|
95
|
+
export interface SelectProps extends Omit<SelectHTMLAttributes<HTMLSelectElement>, "size">, RefAttributes<HTMLSelectElement> {
|
|
96
|
+
size?: Responsive<FieldSize>;
|
|
97
|
+
}
|
|
98
|
+
export declare const Select: React.ForwardRefExoticComponent<Omit<SelectProps, "ref"> & RefAttributes<HTMLSelectElement>>;
|
|
99
|
+
export interface TextareaProps extends TextareaHTMLAttributes<HTMLTextAreaElement>, RefAttributes<HTMLTextAreaElement> {
|
|
100
|
+
size?: Responsive<FieldSize>;
|
|
101
|
+
}
|
|
102
|
+
export declare const Textarea: React.ForwardRefExoticComponent<Omit<TextareaProps, "ref"> & RefAttributes<HTMLTextAreaElement>>;
|
|
103
|
+
export declare function Checkbox({ label, labelHidden, description, size, className, ...props }: Omit<InputHTMLAttributes<HTMLInputElement>, "size"> & RefAttributes<HTMLInputElement> & {
|
|
93
104
|
label: ReactNode;
|
|
94
105
|
labelHidden?: boolean;
|
|
95
106
|
description?: ReactNode;
|
|
107
|
+
size?: Responsive<FieldSize>;
|
|
96
108
|
}): React.JSX.Element;
|
|
97
|
-
export declare function Radio({ label, className, ...props }: InputHTMLAttributes<HTMLInputElement> & RefAttributes<HTMLInputElement> & {
|
|
109
|
+
export declare function Radio({ label, size, className, ...props }: Omit<InputHTMLAttributes<HTMLInputElement>, "size"> & RefAttributes<HTMLInputElement> & {
|
|
98
110
|
label: ReactNode;
|
|
111
|
+
size?: Responsive<FieldSize>;
|
|
99
112
|
}): React.JSX.Element;
|
|
100
|
-
export declare function Switch({ label, className, ...props }: InputHTMLAttributes<HTMLInputElement> & RefAttributes<HTMLInputElement> & {
|
|
113
|
+
export declare function Switch({ label, size, className, ...props }: Omit<InputHTMLAttributes<HTMLInputElement>, "size"> & RefAttributes<HTMLInputElement> & {
|
|
101
114
|
label: ReactNode;
|
|
115
|
+
size?: Responsive<FieldSize>;
|
|
116
|
+
}): React.JSX.Element;
|
|
117
|
+
export type RangeOrientation = "horizontal" | "vertical";
|
|
118
|
+
export declare function Range({ orientation, className, ...props }: InputHTMLAttributes<HTMLInputElement> & RefAttributes<HTMLInputElement> & {
|
|
119
|
+
orientation?: Responsive<RangeOrientation>;
|
|
120
|
+
}): React.JSX.Element;
|
|
121
|
+
export type FieldSize = Extract<ComponentSize, "sm" | "md" | "lg">;
|
|
122
|
+
export declare function Label({ htmlFor, className, children, ...props }: HTMLAttributes<HTMLLabelElement> & RefAttributes<HTMLLabelElement> & {
|
|
123
|
+
htmlFor?: string;
|
|
124
|
+
}): React.JSX.Element;
|
|
125
|
+
export type AddonSide = "start" | "end";
|
|
126
|
+
export type AddonLayout = "inline" | "block";
|
|
127
|
+
export declare function InputGroup({ className, children, ...props }: HTMLAttributes<HTMLDivElement> & RefAttributes<HTMLDivElement>): React.JSX.Element;
|
|
128
|
+
export declare function InputGroupAddon({ side, layout, className, children, ...props }: HTMLAttributes<HTMLSpanElement> & RefAttributes<HTMLSpanElement> & {
|
|
129
|
+
side?: AddonSide;
|
|
130
|
+
layout?: Responsive<AddonLayout>;
|
|
131
|
+
}): React.JSX.Element;
|
|
132
|
+
export declare function AspectRatio({ ratio, className, style, ...props }: HTMLAttributes<HTMLDivElement> & RefAttributes<HTMLDivElement> & {
|
|
133
|
+
ratio?: number;
|
|
102
134
|
}): React.JSX.Element;
|
|
103
|
-
export
|
|
135
|
+
export {};
|
package/dist/markup.js
CHANGED
|
@@ -36,9 +36,10 @@ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-run
|
|
|
36
36
|
// PARA QUEM VIER: componente novo só entra aqui se não chamar hook, não prender manipulador e não
|
|
37
37
|
// tocar motor. Na dúvida, ele NÃO entra — cliente a mais é lento, servidor a mais é quebrado.
|
|
38
38
|
import React, { forwardRef } from "react";
|
|
39
|
-
import { cx } from "./pure.js";
|
|
40
|
-
|
|
41
|
-
|
|
39
|
+
import { cx, fundirRender, peleDoEixo } from "./pure.js";
|
|
40
|
+
export function Card({ variant = "base", className, render, ...props }) {
|
|
41
|
+
return fundirRender(render, { className: cx("card", variant !== "base" && `card-${variant}`, className), ...props });
|
|
42
|
+
}
|
|
42
43
|
export function Stack({ className, ...props }) { return _jsx("div", { className: cx("stack", className), ...props }); }
|
|
43
44
|
// SEPARATOR — a régua entre coisas. Faltava: a Aurea só tinha `.prose hr` (preso à prosa) e o
|
|
44
45
|
// `ToolbarSeparator` (preso ao Toolbar). Untitled UI precisa dela para a variante de seções da
|
|
@@ -60,7 +61,13 @@ export function Stack({ className, ...props }) { return _jsx("div", { className:
|
|
|
60
61
|
// separador COM RÓTULO no meio ("or", uma data numa conversa), que eles têm em
|
|
61
62
|
// `separator__container` + `__line` + `__content`: é outra estrutura (o `<hr>` não carrega texto),
|
|
62
63
|
// ninguém pediu ainda, e fica registrado aqui como o próximo degrau se alguém precisar.
|
|
63
|
-
|
|
64
|
+
// O `Separator` SAIU daqui em 29/08/2026, e a razão é o gate que provou a decisão errada.
|
|
65
|
+
// Ele era um <hr> com orientação estática, e o `comportamental.multi-motor.spec.ts` — a prova
|
|
66
|
+
// das cinco capacidades que o Victor especificou no G-AXIS-06 — cobra dele `data-orientation`
|
|
67
|
+
// e `aria-orientation` RESOLVIDOS por largura de contêiner. Isso é eixo COMPORTAMENTAL: não
|
|
68
|
+
// sai de classe CSS, precisa de runtime, e runtime precisa de hook. Um componente que precisa
|
|
69
|
+
// de hook é de cliente, e é isso que a ADR-0026 manda — ela separa quem PRECISA do cliente de
|
|
70
|
+
// quem só caiu num módulo com a diretiva. Mora no `layout-client.tsx`.
|
|
64
71
|
export function Cluster({ className, ...props }) { return _jsx("div", { className: cx("cluster", className), ...props }); }
|
|
65
72
|
export function Grid({ className, ...props }) { return _jsx("div", { className: cx("grid", className), ...props }); }
|
|
66
73
|
// ── Data Display ─────────────────────────────────────────────────────────────────────────────
|
|
@@ -95,14 +102,22 @@ export function Progress({ value, label }) { const pct = Math.max(0, Math.min(10
|
|
|
95
102
|
export function Skeleton({ className, ...props }) { return _jsx("div", { className: cx("skeleton", className), "aria-hidden": "true", ...props }); }
|
|
96
103
|
// ── Identity ─────────────────────────────────────────────────────────────────────────────────
|
|
97
104
|
// `React.Children` não é hook — é utilitário de leitura de `children`, e roda no servidor.
|
|
98
|
-
export function AvatarGroup({ children, max, total, label, size
|
|
105
|
+
export function AvatarGroup({ children, max, total, label, size, className }) {
|
|
99
106
|
const itens = React.Children.toArray(children);
|
|
100
107
|
const mostra = max ? itens.slice(0, max) : itens;
|
|
101
108
|
const resto = (total ?? itens.length) - mostra.length;
|
|
102
|
-
return _jsxs("div", { role: "group", "aria-label": label, className: cx("avatar-group", className), children: [mostra, resto > 0 && _jsxs("span", { className: cx("avatar", "avatar-count",
|
|
109
|
+
return _jsxs("div", { role: "group", "aria-label": label, className: cx("avatar-group", className), children: [mostra, resto > 0 && _jsxs("span", { className: cx("avatar", "avatar-count", peleDoEixo("avatar", size)), "aria-hidden": "true", children: ["+", resto] })] });
|
|
103
110
|
}
|
|
104
111
|
// ── Code ─────────────────────────────────────────────────────────────────────────────────────
|
|
105
|
-
|
|
112
|
+
// O NÍVEL É MODIFICADOR DA CÉLULA, não classe na linha. O core pinta `.log-level.warn`, e a
|
|
113
|
+
// regra descendente que pintava a partir de uma classe no container (`.log-warn .log-level`)
|
|
114
|
+
// saiu do CSS de propósito — o comentário dela dizia, com todas as letras, que servia a um
|
|
115
|
+
// arquivo que não existe mais. O merge das duas linhagens ficou com o React da versão antiga
|
|
116
|
+
// contra o CSS da nova: `log-warn` era emitido e ninguém o pintava, e `.log-level` saía sem
|
|
117
|
+
// modificador nenhum. Os dois testes do `grade-do-core.test.tsx` pegaram os dois lados.
|
|
118
|
+
//
|
|
119
|
+
// `<time>` fica: é a semântica certa para a hora, e o core casa pela CLASSE, não pela tag.
|
|
120
|
+
export function LogStream({ lines }) { return _jsx("div", { className: "log-stream", role: "log", "aria-live": "polite", children: lines.map((l, n) => _jsxs("div", { className: "log-line", children: [_jsx("time", { className: "log-time", children: l.time }), _jsx("span", { className: cx("log-level", l.level && l.level.toLowerCase()), children: l.level }), _jsx("span", { children: l.text })] }, n)) }); }
|
|
106
121
|
// ── Media ────────────────────────────────────────────────────────────────────────────────────
|
|
107
122
|
export function MediaPlayerShell({ children, className, ...props }) { return _jsx("div", { className: cx("media-player", className), ...props, children: children }); }
|
|
108
123
|
// ── Navigation ───────────────────────────────────────────────────────────────────────────────
|
|
@@ -112,13 +127,48 @@ export function Topbar({ variant = "floating", brand, children, className, ...pr
|
|
|
112
127
|
// morava fora da casa dele (no `internal.tsx`, por causa do `Button`); agora mora aqui, pelo
|
|
113
128
|
// mesmo motivo elevado a regra.
|
|
114
129
|
export function Kbd({ children, className, ...props }) { return _jsx("kbd", { className: cx("kbd", className), ...props, children: children }); }
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
//
|
|
118
|
-
//
|
|
119
|
-
export
|
|
120
|
-
export
|
|
121
|
-
export function
|
|
122
|
-
export function
|
|
123
|
-
|
|
124
|
-
|
|
130
|
+
export const Select = forwardRef(function Select({ className, size, ...props }, ref) { return _jsx("select", { ref: ref, className: cx("select", peleDoEixo("select", size), className), ...props }); });
|
|
131
|
+
export const Textarea = forwardRef(function Textarea({ className, size, ...props }, ref) { return _jsx("textarea", { ref: ref, className: cx("textarea", peleDoEixo("textarea", size), className), ...props }); });
|
|
132
|
+
// `size` nos três marcáveis pela mesma razão do campo: uma linha de formulário com um campo `sm`
|
|
133
|
+
// e um checkbox de tamanho fixo ao lado desalinha. O degrau vai na MARCA, não no rótulo.
|
|
134
|
+
export function Checkbox({ label, labelHidden, description, size, className, ...props }) { return _jsxs("label", { className: cx("checkbox", labelHidden && "checkbox-bare", className), children: [_jsx("input", { type: "checkbox", ...props }), _jsx("span", { className: cx("control-mark", peleDoEixo("control-mark", size)) }), _jsxs("span", { className: labelHidden ? "sr-only" : undefined, children: [_jsx("strong", { children: label }), description && _jsxs(_Fragment, { children: [_jsx("br", {}), _jsx("span", { className: "muted", children: description })] })] })] }); }
|
|
135
|
+
export function Radio({ label, size, className, ...props }) { return _jsxs("label", { className: cx("radio", className), children: [_jsx("input", { type: "radio", ...props }), _jsx("span", { className: cx("control-mark", peleDoEixo("control-mark", size)) }), label] }); }
|
|
136
|
+
export function Switch({ label, size, className, ...props }) { return _jsxs("label", { className: cx("switch", className), children: [_jsx("input", { type: "checkbox", role: "switch", ...props }), _jsx("span", { className: cx("switch-track", peleDoEixo("switch-track", size)) }), _jsx("span", { children: label })] }); }
|
|
137
|
+
export function Range({ orientation, className, ...props }) { return _jsx("input", { className: cx("range", peleDoEixo("range", orientation, "horizontal", "range"), className), type: "range", ...props }); }
|
|
138
|
+
// `md` é a classe base — não existe `.input-md`, como não existe `.btn-md`. O prefixo é escrito
|
|
139
|
+
// literal em cada chamada (`input-${size}`, e não `${base}-${size}`) porque é assim que o check 15
|
|
140
|
+
// enxerga uma classe montada em tempo de execução; com prefixo dinâmico ele para de proteger a
|
|
141
|
+
// fronteira do core justamente nas classes novas.
|
|
142
|
+
// RÓTULO avulso. O `Field` já traz o dele, e é o caminho normal; este existe para quem monta o
|
|
143
|
+
// próprio campo — uma linha de tabela editável, um filtro que não é um `Field`. Sem ele, quem sai
|
|
144
|
+
// do `Field` escreve um `<label>` cru e perde a pele.
|
|
145
|
+
export function Label({ htmlFor, className, children, ...props }) { return _jsx("label", { htmlFor: htmlFor, className: cx("label", className), ...props, children: children }); }
|
|
146
|
+
function ladoDoAdorno(no) {
|
|
147
|
+
if (!React.isValidElement(no))
|
|
148
|
+
return null;
|
|
149
|
+
const p = no.props;
|
|
150
|
+
return no.type === InputGroupAddon ? (p.side ?? "start") : null;
|
|
151
|
+
}
|
|
152
|
+
export function InputGroup({ className, children, ...props }) {
|
|
153
|
+
const filhos = React.Children.toArray(children);
|
|
154
|
+
const inicio = filhos.filter(f => ladoDoAdorno(f) === "start");
|
|
155
|
+
const fim = filhos.filter(f => ladoDoAdorno(f) === "end");
|
|
156
|
+
const meio = filhos.filter(f => ladoDoAdorno(f) === null);
|
|
157
|
+
return _jsx("div", { className: cx("input-group", className), ...props, children: [...inicio, ...meio, ...fim] });
|
|
158
|
+
}
|
|
159
|
+
// `layout` é responsivo, e a triagem que o autorizou está no `G-AXIS-07`: com o adorno em linha,
|
|
160
|
+
// a 200px de caixa sobram 48% da largura para o campo; com ele em faixa, 99%. Não é preferência
|
|
161
|
+
// de desenho — é o campo deixar de caber.
|
|
162
|
+
//
|
|
163
|
+
// `padrao: ""` porque as duas geometrias têm regra própria: um `inline` que não emitisse classe
|
|
164
|
+
// ficaria sem o recuo, que mora na regra composta `.…-inline.…-start`.
|
|
165
|
+
export function InputGroupAddon({ side = "start", layout = "inline", className, children, ...props }) {
|
|
166
|
+
return _jsx("span", { className: cx("input-group-addon", `input-group-addon-${side}`, peleDoEixo("input-group-addon", layout, "", "input-group-addon"), className), ...props, children: children });
|
|
167
|
+
}
|
|
168
|
+
// PORTADO no merge de 28/08/2026: não existia na `main`, e a pele (`.aspect-ratio`) veio junto
|
|
169
|
+
// no core. Marcação pura — uma linha de CSS —, então é servidor.
|
|
170
|
+
// PROPORÇÃO FIXA. Quatro referências têm. Hoje é uma linha de CSS — `aspect-ratio` é suportado em
|
|
171
|
+
// todo navegador que a Aurea alcança —, e ainda assim é componente: sem ele cada consumidor
|
|
172
|
+
// escreve o `padding-top: 56.25%` de 2015, ou esquece o `min-width: 0` que impede a caixa de
|
|
173
|
+
// estourar dentro de um grid.
|
|
174
|
+
export function AspectRatio({ ratio = 1, className, style, ...props }) { return _jsx("div", { className: cx("aspect-ratio", className), style: { aspectRatio: ratio, ...style }, ...props }); }
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import React, { type HTMLAttributes, type RefAttributes, type ReactNode, type ReactElement } from "react";
|
|
2
|
+
import { type Responsive } from "./pure.js";
|
|
2
3
|
import { type IconName } from "./system.js";
|
|
3
4
|
export type StepState = "default" | "active" | "done" | "error";
|
|
4
5
|
export interface StepItem {
|
|
@@ -19,7 +20,8 @@ export declare function Breadcrumb({ items, label }: {
|
|
|
19
20
|
}>;
|
|
20
21
|
label?: string;
|
|
21
22
|
}): React.JSX.Element;
|
|
22
|
-
export
|
|
23
|
+
export type TabsOrientation = "horizontal" | "vertical";
|
|
24
|
+
export declare function Tabs({ tabs, value, onChange, label, orientation, activateOnFocus, loopFocus }: {
|
|
23
25
|
tabs: Array<{
|
|
24
26
|
id: string;
|
|
25
27
|
label: ReactNode;
|
|
@@ -28,6 +30,9 @@ export declare function Tabs({ tabs, value, onChange, label }: {
|
|
|
28
30
|
value: string;
|
|
29
31
|
onChange: (id: string) => void;
|
|
30
32
|
label?: string;
|
|
33
|
+
orientation?: Responsive<TabsOrientation>;
|
|
34
|
+
activateOnFocus?: boolean;
|
|
35
|
+
loopFocus?: boolean;
|
|
31
36
|
}): React.JSX.Element;
|
|
32
37
|
export declare function Pagination({ page, total, onPageChange }: {
|
|
33
38
|
page: number;
|
|
@@ -78,10 +83,11 @@ export type BottomNavVariant = "floating" | "edge";
|
|
|
78
83
|
export type BottomNavIndicator = "none" | "subtle" | "pill" | "circle" | "circle-raised" | "circle-bold" | "circle-outline";
|
|
79
84
|
/** @deprecated Os quatro nomes de 17/08/2026. Use `variant` + `indicator`. */
|
|
80
85
|
export type BottomNavVariantLegacy = "flat" | "surface" | "pill" | "dock";
|
|
86
|
+
export type BottomNavVariantAny = BottomNavVariant | BottomNavVariantLegacy;
|
|
81
87
|
export declare function BottomNav({ items, current, variant, indicator, label, className, ...props }: HTMLAttributes<HTMLElement> & RefAttributes<HTMLElement> & {
|
|
82
88
|
items: SidebarItem[];
|
|
83
89
|
current?: string;
|
|
84
|
-
variant?:
|
|
90
|
+
variant?: BottomNavVariantAny;
|
|
85
91
|
indicator?: BottomNavIndicator;
|
|
86
92
|
label?: string;
|
|
87
93
|
}): React.JSX.Element;
|
|
@@ -3,9 +3,10 @@ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-run
|
|
|
3
3
|
// Fase 9 (achado A5): este arquivo saiu do index.tsx de 970 linhas. Um módulo por categoria
|
|
4
4
|
// do registry — a taxonomia já existia e é gateada. A ordem de import entre eles é um DAG:
|
|
5
5
|
// internal → system → actions → feedback → inputs → navigation → layout → data-display → resto.
|
|
6
|
-
import React from "react";
|
|
6
|
+
import React, { useRef } from "react";
|
|
7
7
|
import { Tabs as BaseTabs } from "@base-ui/react/tabs";
|
|
8
8
|
import { Autocomplete as BaseAutocomplete } from "@base-ui/react/autocomplete";
|
|
9
|
+
import { useValorResponsivo } from "./responsivo-runtime.js";
|
|
9
10
|
import { cx, useAureaStrings, usePortalContainer } from "./internal.js";
|
|
10
11
|
import { Kbd } from "./markup.js";
|
|
11
12
|
import { Icon } from "./system.js";
|
|
@@ -26,11 +27,67 @@ export function Stepper({ items, label, className }) {
|
|
|
26
27
|
}) });
|
|
27
28
|
}
|
|
28
29
|
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)) }); }
|
|
29
|
-
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:
|
|
30
|
+
export function Tabs({ tabs, value, onChange, label, orientation, activateOnFocus = true, loopFocus }) { const s = useAureaStrings(); const ancora = useRef(null); const resolvida = useValorResponsivo(orientation, "horizontal", ancora); return _jsxs(BaseTabs.Root, { ref: ancora, value: value, onValueChange: v => onChange(String(v)), orientation: resolvida, className: "tabs-root", children: [_jsx(BaseTabs.List, { className: "tabs", "aria-label": label ?? s.tabsLabel, activateOnFocus: activateOnFocus, loopFocus: loopFocus, 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))] }); }
|
|
30
31
|
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 })] }); }
|
|
31
32
|
export function TableOfContents({ items, current, label, className, ...props }) {
|
|
32
33
|
const s = useAureaStrings();
|
|
33
|
-
|
|
34
|
+
const meu = React.useRef(null);
|
|
35
|
+
const observado = useSecaoEmVista(items, current === undefined, meu);
|
|
36
|
+
const atual = current ?? observado;
|
|
37
|
+
// O `ref` do consumidor continua chegando: em React 19 ele é prop comum, então espalhá-lo por
|
|
38
|
+
// `...props` depois do nosso o sobrescreveria em silêncio. Os dois são atendidos aqui.
|
|
39
|
+
const refDele = props.ref;
|
|
40
|
+
const { ref: _ignorado, ...resto } = props;
|
|
41
|
+
return _jsxs("nav", { ref: n => { meu.current = n; if (typeof refDele === "function")
|
|
42
|
+
refDele(n);
|
|
43
|
+
else if (refDele)
|
|
44
|
+
refDele.current = n; }, className: cx("toc", className), "aria-label": label ?? s.tocLabel, ...resto, children: [_jsx("p", { className: "toc-label", children: label ?? s.tocLabel }), items.map(i => _jsx("a", { href: `#${i.id}`, className: cx(i.sub && "toc-sub"), ...(i.id === atual ? { "aria-current": "true" } : {}), children: i.label }, i.id))] });
|
|
45
|
+
}
|
|
46
|
+
/** Qual dos `items` está na faixa de leitura, observando o documento. Devolve `undefined` até o
|
|
47
|
+
* primeiro cálculo — e em SSR, onde não há documento para observar.
|
|
48
|
+
*
|
|
49
|
+
* A afinação NÃO é escolha nova: é a mesma do `tocSpy` do `aurea.js`, que já estava provada em
|
|
50
|
+
* produção. Uma faixa estreita no alto da janela (`-75%` embaixo), e vence o ÚLTIMO que a cruza,
|
|
51
|
+
* não o primeiro — seção e subseção cruzam juntas, e o específico é o que interessa. Nada
|
|
52
|
+
* cruzando quer dizer topo da página, e aí o atual é o primeiro item. Reimplementar com outra
|
|
53
|
+
* régua seria criar a terceira versão divergente do mesmo comportamento, que é o defeito que
|
|
54
|
+
* este componente existe para encerrar. */
|
|
55
|
+
function useSecaoEmVista(items, ligado, nav) {
|
|
56
|
+
const [atual, setAtual] = React.useState(undefined);
|
|
57
|
+
// os ids como string estável: sem isto o efeito re-roda a cada render, porque `items` é um
|
|
58
|
+
// array novo toda vez que o pai renderiza.
|
|
59
|
+
const ids = items.map(i => i.id).join(" ");
|
|
60
|
+
React.useEffect(() => {
|
|
61
|
+
if (!ligado)
|
|
62
|
+
return;
|
|
63
|
+
if (typeof IntersectionObserver === "undefined")
|
|
64
|
+
return; // navegador antigo: fica lista de links
|
|
65
|
+
const lista = ids ? ids.split(" ") : [];
|
|
66
|
+
const alvos = lista.map(id => document.getElementById(id)).filter((e) => e !== null);
|
|
67
|
+
if (!alvos.length)
|
|
68
|
+
return;
|
|
69
|
+
// O acordo com o runtime vanilla, e ele mora no EFEITO e não no render por um motivo medido:
|
|
70
|
+
// o catálogo é gerado ESTÁTICO a partir deste mesmo componente. Marcar no render poria o
|
|
71
|
+
// atributo nas páginas todas, onde não há React vivo para observar — e o `tocSpy` do
|
|
72
|
+
// `aurea.js`, que é quem de fato marca lá, se afastaria de um substituto inexistente. Efeito
|
|
73
|
+
// só roda onde há React de verdade, que é exatamente o que a marca precisa afirmar.
|
|
74
|
+
nav.current?.setAttribute("data-toc-spy", "react");
|
|
75
|
+
setAtual(alvos[0].id);
|
|
76
|
+
const visivel = new Map();
|
|
77
|
+
const obs = new IntersectionObserver(entradas => {
|
|
78
|
+
for (const e of entradas)
|
|
79
|
+
visivel.set(e.target.id, e.isIntersecting);
|
|
80
|
+
let achado = alvos[0].id;
|
|
81
|
+
for (const alvo of alvos)
|
|
82
|
+
if (visivel.get(alvo.id))
|
|
83
|
+
achado = alvo.id;
|
|
84
|
+
setAtual(achado);
|
|
85
|
+
}, { rootMargin: "0px 0px -75% 0px" });
|
|
86
|
+
for (const alvo of alvos)
|
|
87
|
+
obs.observe(alvo);
|
|
88
|
+
return () => obs.disconnect();
|
|
89
|
+
}, [ids, ligado]);
|
|
90
|
+
return ligado ? atual : undefined;
|
|
34
91
|
}
|
|
35
92
|
function flattenVisible(nodes, expanded, level = 1, parentId, acc = []) {
|
|
36
93
|
for (const node of nodes) {
|
package/dist/overlays.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import React, { type ReactNode, type ReactElement } from "react";
|
|
2
|
+
import { type Responsive } from "./pure.js";
|
|
2
3
|
import { type IconName } from "./system.js";
|
|
3
4
|
export declare function Dialog({ open, title, children, footer, onClose }: {
|
|
4
5
|
open: boolean;
|
|
@@ -54,21 +55,95 @@ export declare function HoverCard({ trigger, children, side }: {
|
|
|
54
55
|
children: ReactNode;
|
|
55
56
|
side?: OverlaySide;
|
|
56
57
|
}): React.JSX.Element;
|
|
57
|
-
|
|
58
|
+
interface MenuItemBase {
|
|
58
59
|
label: ReactNode;
|
|
59
|
-
onClick?: () => void;
|
|
60
60
|
disabled?: boolean;
|
|
61
61
|
leadingIcon?: IconName;
|
|
62
62
|
}
|
|
63
|
+
/** Item de ação — a forma de sempre. `kind` é opcional para não quebrar quem já usa. */
|
|
64
|
+
export interface MenuActionDef extends MenuItemBase {
|
|
65
|
+
kind?: "item";
|
|
66
|
+
onClick?: () => void;
|
|
67
|
+
/** O elemento que MATERIALIZA o item, no idioma `render` da casa (o mesmo do `Card`). Para
|
|
68
|
+
* navegação prefira `href`, que usa o `LinkItem` do motor. */
|
|
69
|
+
render?: ReactElement;
|
|
70
|
+
}
|
|
71
|
+
/** Item que é um link de verdade: `<a href>`, com o `LinkItem` do motor por trás.
|
|
72
|
+
* NÃO estende `MenuItemBase`, e a ausência é a informação: **link não tem `disabled`** — o HTML
|
|
73
|
+
* não desabilita âncora, e o motor não aceita a prop. Um destino indisponível é um item de ação
|
|
74
|
+
* desabilitado, não um link apagado. */
|
|
75
|
+
export interface MenuLinkDef {
|
|
76
|
+
kind: "link";
|
|
77
|
+
label: ReactNode;
|
|
78
|
+
leadingIcon?: IconName;
|
|
79
|
+
href: string;
|
|
80
|
+
target?: string;
|
|
81
|
+
rel?: string;
|
|
82
|
+
/** Fechar o menu ao clicar. O padrão do motor é `false` (a navegação desmonta tudo de qualquer
|
|
83
|
+
* jeito); aqui o padrão é `true`, porque numa aplicação de página única o clique NÃO desmonta
|
|
84
|
+
* e o menu ficaria aberto por cima da tela nova. */
|
|
85
|
+
closeOnClick?: boolean;
|
|
86
|
+
}
|
|
87
|
+
/** Item que ALTERNA. Controlado (`checked`) ou não (`defaultChecked`), como todo par da casa. */
|
|
88
|
+
export interface MenuCheckboxDef extends MenuItemBase {
|
|
89
|
+
kind: "checkbox";
|
|
90
|
+
checked?: boolean;
|
|
91
|
+
defaultChecked?: boolean;
|
|
92
|
+
onCheckedChange?: (checked: boolean) => void;
|
|
93
|
+
}
|
|
94
|
+
/** Opções MUTUAMENTE EXCLUSIVAS. O grupo é a unidade — um rádio solto não tem sentido. */
|
|
95
|
+
export interface MenuRadioGroupDef {
|
|
96
|
+
kind: "radiogroup";
|
|
97
|
+
label?: ReactNode;
|
|
98
|
+
value?: string;
|
|
99
|
+
defaultValue?: string;
|
|
100
|
+
onValueChange?: (value: string) => void;
|
|
101
|
+
items: Array<{
|
|
102
|
+
value: string;
|
|
103
|
+
label: ReactNode;
|
|
104
|
+
disabled?: boolean;
|
|
105
|
+
leadingIcon?: IconName;
|
|
106
|
+
}>;
|
|
107
|
+
}
|
|
108
|
+
/** Submenu. `items` aninha a mesma união — um submenu pode ter submenu. */
|
|
109
|
+
export interface MenuSubmenuDef extends MenuItemBase {
|
|
110
|
+
kind: "submenu";
|
|
111
|
+
items: MenuEntry[];
|
|
112
|
+
}
|
|
113
|
+
/** Seção ROTULADA. O rótulo é do grupo, não um item: `GroupLabel` não é focável nem clicável. */
|
|
114
|
+
export interface MenuGroupDef {
|
|
115
|
+
kind: "group";
|
|
116
|
+
label?: ReactNode;
|
|
117
|
+
items: MenuEntry[];
|
|
118
|
+
}
|
|
119
|
+
export type MenuEntry = MenuActionDef | MenuLinkDef | MenuCheckboxDef | MenuRadioGroupDef | MenuSubmenuDef | MenuGroupDef | "separator";
|
|
120
|
+
/** @deprecated desde 28/08/2026 — use `MenuEntry`. Mantido porque `MenuActionDef` É a forma
|
|
121
|
+
* antiga: quem tipava com `MenuItemDef` continua compilando. */
|
|
122
|
+
export type MenuItemDef = MenuActionDef;
|
|
63
123
|
export declare function DropdownMenu({ trigger, items, side, label }: {
|
|
64
124
|
trigger: ReactElement;
|
|
65
|
-
items:
|
|
125
|
+
items: MenuEntry[];
|
|
66
126
|
side?: OverlaySide;
|
|
67
127
|
label?: string;
|
|
68
128
|
}): React.JSX.Element;
|
|
69
129
|
export declare function ContextMenu({ children, items, label, className }: {
|
|
70
130
|
children: ReactNode;
|
|
71
|
-
items:
|
|
131
|
+
items: MenuEntry[];
|
|
72
132
|
label?: string;
|
|
73
133
|
className?: string;
|
|
74
134
|
}): React.JSX.Element;
|
|
135
|
+
export type MenubarOrientation = "horizontal" | "vertical";
|
|
136
|
+
export declare function Menubar({ menus, label, modal, disabled, loopFocus, orientation, className }: {
|
|
137
|
+
menus: Array<{
|
|
138
|
+
label: ReactNode;
|
|
139
|
+
items: MenuEntry[];
|
|
140
|
+
disabled?: boolean;
|
|
141
|
+
}>;
|
|
142
|
+
label?: string;
|
|
143
|
+
modal?: boolean;
|
|
144
|
+
disabled?: boolean;
|
|
145
|
+
loopFocus?: boolean;
|
|
146
|
+
orientation?: Responsive<MenubarOrientation>;
|
|
147
|
+
className?: string;
|
|
148
|
+
}): React.JSX.Element;
|
|
149
|
+
export {};
|
package/dist/overlays.js
CHANGED
|
@@ -3,15 +3,17 @@ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-run
|
|
|
3
3
|
// Fase 9 (achado A5): este arquivo saiu do index.tsx de 970 linhas. Um módulo por categoria
|
|
4
4
|
// do registry — a taxonomia já existia e é gateada. A ordem de import entre eles é um DAG:
|
|
5
5
|
// internal → system → actions → feedback → inputs → navigation → layout → data-display → resto.
|
|
6
|
-
import React from "react";
|
|
6
|
+
import React, { useRef } from "react";
|
|
7
7
|
import { Dialog as BaseDialog } from "@base-ui/react/dialog";
|
|
8
8
|
import { AlertDialog as BaseAlertDialog } from "@base-ui/react/alert-dialog";
|
|
9
9
|
import { Popover as BasePopover } from "@base-ui/react/popover";
|
|
10
10
|
import { Tooltip as BaseTooltip } from "@base-ui/react/tooltip";
|
|
11
11
|
import { Menu as BaseMenu } from "@base-ui/react/menu";
|
|
12
|
+
import { Menubar as BaseMenubar } from "@base-ui/react/menubar";
|
|
12
13
|
import { ContextMenu as BaseContextMenu } from "@base-ui/react/context-menu";
|
|
13
14
|
import { PreviewCard as BasePreviewCard } from "@base-ui/react/preview-card";
|
|
14
15
|
import { cx, useAureaStrings, usePortalContainer } from "./internal.js";
|
|
16
|
+
import { useValorResponsivo } from "./responsivo-runtime.js";
|
|
15
17
|
import { Icon } from "./system.js";
|
|
16
18
|
import { Button } from "./actions.js";
|
|
17
19
|
export function Dialog({ open, title, children, footer, onClose }) { const s = useAureaStrings(); const portal = usePortalContainer(); return _jsx(BaseDialog.Root, { open: open, onOpenChange: o => { if (!o)
|
|
@@ -78,15 +80,72 @@ export function Popover({ trigger, title, children, side = "bottom" }) { const p
|
|
|
78
80
|
// Por depender de repouso do ponteiro, NÃO serve para informação essencial — quem navega só por
|
|
79
81
|
// teclado ou toque não abre um hover card. Conteúdo obrigatório vai em Popover.
|
|
80
82
|
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 }) }) })] }); }
|
|
83
|
+
// A MARCA de um item que alterna fica num slot de largura FIXA, e não condicional: sem ele, um
|
|
84
|
+
// menu com um item marcado e outro não desalinharia os rótulos a cada clique — o texto andaria
|
|
85
|
+
// para o lado sozinho. É a mesma razão de o `leadingIcon` ter lugar próprio.
|
|
86
|
+
const marca = (children) => _jsx("span", { className: "menu-mark", "aria-hidden": "true", children: children });
|
|
81
87
|
// ContextMenu.Item/.Separator/.Popup são os MESMOS componentes de Menu.* no Base UI,
|
|
82
88
|
// então os itens renderizam igual nos dois menus.
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
89
|
+
// `portal` viaja como PARÂMETRO e não por hook: este renderizador não é componente, e o
|
|
90
|
+
// submenu tem portal PRÓPRIO — sem passá-lo adiante, o menu de primeiro nível respeitaria o
|
|
91
|
+
// container configurado pelo provider e o submenu escaparia para o `document.body`. Duas
|
|
92
|
+
// camadas do mesmo menu em containers diferentes é o tipo de divergência que só aparece quando
|
|
93
|
+
// alguém monta a Aurea dentro de um shadow root ou de um diálogo nativo.
|
|
94
|
+
function renderMenuItems(items, portal) {
|
|
95
|
+
return items.map((it, i) => {
|
|
96
|
+
if (it === "separator")
|
|
97
|
+
return _jsx(BaseMenu.Separator, { className: "menu-sep" }, i);
|
|
98
|
+
const kind = it.kind ?? "item";
|
|
99
|
+
const dentro = (x) => _jsxs(_Fragment, { children: [x.leadingIcon && _jsx(Icon, { name: x.leadingIcon }), x.label] });
|
|
100
|
+
switch (kind) {
|
|
101
|
+
case "group": {
|
|
102
|
+
const g = it;
|
|
103
|
+
return _jsxs(BaseMenu.Group, { children: [g.label && _jsx(BaseMenu.GroupLabel, { className: "menu-label", children: g.label }), renderMenuItems(g.items, portal)] }, i);
|
|
104
|
+
}
|
|
105
|
+
case "radiogroup": {
|
|
106
|
+
const g = it;
|
|
107
|
+
return _jsxs(BaseMenu.RadioGroup, { value: g.value, defaultValue: g.defaultValue, onValueChange: v => g.onValueChange?.(String(v)), children: [g.label && _jsx(BaseMenu.GroupLabel, { className: "menu-label", children: g.label }), g.items.map((r, j) => _jsxs(BaseMenu.RadioItem, { className: "menu-item", value: r.value, disabled: r.disabled, children: [marca(_jsx(BaseMenu.RadioItemIndicator, { className: "menu-ponto" })), dentro(r)] }, j))] }, i);
|
|
108
|
+
}
|
|
109
|
+
case "submenu": {
|
|
110
|
+
const sm = it;
|
|
111
|
+
// O submenu é uma RAIZ própria com gatilho próprio — não é um item que "abre outro menu".
|
|
112
|
+
// É o que dá `aria-haspopup`, a seta para a direita e o fechamento em cascata de graça.
|
|
113
|
+
return _jsxs(BaseMenu.SubmenuRoot, { children: [_jsxs(BaseMenu.SubmenuTrigger, { className: "menu-item", disabled: sm.disabled, children: [dentro(sm), _jsx(Icon, { name: "chevron--right", size: "sm", className: "menu-seta" })] }), _jsx(BaseMenu.Portal, { container: portal, children: _jsx(BaseMenu.Positioner, { side: "inline-end", sideOffset: 4, children: _jsx(BaseMenu.Popup, { className: "menu", children: renderMenuItems(sm.items, portal) }) }) })] }, i);
|
|
114
|
+
}
|
|
115
|
+
case "checkbox": {
|
|
116
|
+
const c = it;
|
|
117
|
+
// O motor chama `onCheckedChange` com `(checked, detalhes)`. A Aurea passa SÓ o `checked`,
|
|
118
|
+
// e isso é CONTRATO: o segundo argumento é a forma interna do Base UI, e repassá-lo faria a
|
|
119
|
+
// assinatura pública da Aurea mudar junto com uma versão do motor. Mesma decisão do
|
|
120
|
+
// `onValueChange` do grupo de rádio. O teste pegou: sem o embrulho, o `vi.fn()` recebia um
|
|
121
|
+
// `PointerEvent` de brinde.
|
|
122
|
+
return _jsxs(BaseMenu.CheckboxItem, { className: "menu-item", disabled: c.disabled, checked: c.checked, defaultChecked: c.defaultChecked, onCheckedChange: v => c.onCheckedChange?.(v), children: [marca(_jsx(BaseMenu.CheckboxItemIndicator, { children: _jsx(Icon, { name: "checkmark", size: "sm" }) })), dentro(c)] }, i);
|
|
123
|
+
}
|
|
124
|
+
case "link": {
|
|
125
|
+
const l = it;
|
|
126
|
+
return _jsx(BaseMenu.LinkItem, { className: "menu-item", href: l.href, target: l.target, rel: l.rel, closeOnClick: l.closeOnClick ?? true, children: dentro(l) }, i);
|
|
127
|
+
}
|
|
128
|
+
default: {
|
|
129
|
+
const a = it;
|
|
130
|
+
return _jsx(BaseMenu.Item, { className: "menu-item", disabled: a.disabled, onClick: a.onClick, render: a.render, children: dentro(a) }, i);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
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, portal) }) }) })] }); }
|
|
87
136
|
// ContextMenu: abre no botão direito e — por teclado — em Shift+F10 / tecla Menu,
|
|
88
137
|
// que o browser só dispara (como evento contextmenu) sobre um elemento FOCADO. Por
|
|
89
138
|
// isso o gatilho é focável (tabIndex 0) e tem nome acessível (auditoria 18/07/2026,
|
|
90
139
|
// MÉDIO 3); sem isso o teclado não alcança o menu. Consumidor pode sobrescrever
|
|
91
140
|
// tabIndex via children se o próprio já for focável.
|
|
92
|
-
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) }) }) })] }); }
|
|
141
|
+
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, portal) }) }) })] }); }
|
|
142
|
+
// G-AXIS-06 — o valor responsivo RESOLVIDO é a única fonte de verdade: ele vai para o motor, o
|
|
143
|
+
// motor publica `data-orientation`/`aria-orientation` e ajusta o teclado, e a pele reage ao
|
|
144
|
+
// atributo publicado. Ver `decisions/0019-css-first-para-apresentacao-runtime-para-semantica.md`.
|
|
145
|
+
export function Menubar({ menus, label, modal, disabled, loopFocus, orientation, className }) {
|
|
146
|
+
const portal = usePortalContainer();
|
|
147
|
+
const ancora = useRef(null);
|
|
148
|
+
const resolvida = useValorResponsivo(orientation, "horizontal", ancora);
|
|
149
|
+
const vertical = resolvida === "vertical";
|
|
150
|
+
return _jsx(BaseMenubar, { ref: ancora, modal: modal, disabled: disabled, loopFocus: loopFocus, orientation: resolvida, "aria-label": label, className: cx("menubar", className), children: menus.map((m, i) => _jsxs(BaseMenu.Root, { disabled: m.disabled, children: [_jsx(BaseMenu.Trigger, { className: "menubar-trigger", children: m.label }), _jsx(BaseMenu.Portal, { container: portal, children: _jsx(BaseMenu.Positioner, { side: vertical ? "inline-end" : "bottom", align: "start", sideOffset: 4, children: _jsx(BaseMenu.Popup, { className: "menu", children: renderMenuItems(m.items) }) }) })] }, i)) });
|
|
151
|
+
}
|