@aurea-uds/react 0.54.0 → 0.56.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.
@@ -3,7 +3,7 @@ import { type Orientation, type Responsive } from "./pure.js";
3
3
  import { type IconButtonProps } from "./actions.js";
4
4
  import { type HeaderVariant, type HeaderTab, type SidebarVariant, type SidebarScrollbar, type SidebarEntry, type SidebarCollapsible, type SidebarIndicator } from "./navigation.js";
5
5
  declare function AppShellSidebarToggle(props: Omit<IconButtonProps, "icon" | "label" | "onClick" | "variant">): React.JSX.Element;
6
- export declare function AppShell({ brand, navigation, navItems, currentNavId, navLabel, topbar, topbarVariant, topbarDivider, topbarTabs, topbarCurrentTab, topbarTabsLabel, topbarTabsEnd, topbarActions, topbarBlurred, layout, sidebarVariant, sidebarScrollbar, sidebarFooter, sidebarCollapsed, sidebarCollapsible, sidebarToggle, sidebarShortcut, sidebarIndicator, sidebarPanel, sidebarContrast, defaultSidebarCollapsed, onSidebarCollapsedChange, contentVariant, aside, asideLabel, asideVariant, children, className, ...props }: HTMLAttributes<HTMLDivElement> & RefAttributes<HTMLDivElement> & {
6
+ export declare function AppShell({ brand, navigation, navItems, currentNavId, navLabel, topbar, topbarVariant, topbarDivider, topbarTabs, topbarCurrentTab, topbarTabsLabel, topbarTabsEnd, topbarActions, topbarBlurred, layout, sidebarVariant, sidebarScrollbar, sidebarFooter, sidebarCollapsed, sidebarCollapsible, sidebarToggle, sidebarShortcut, sidebarIndicator, sidebarPanel, sidebarContrast, defaultSidebarCollapsed, onSidebarCollapsedChange, contentVariant, highContrast, textSize, aside, asideLabel, asideVariant, children, className, ...props }: HTMLAttributes<HTMLDivElement> & RefAttributes<HTMLDivElement> & {
7
7
  brand: ReactNode;
8
8
  navigation?: ReactNode;
9
9
  navItems?: SidebarEntry[];
@@ -74,6 +74,13 @@ export declare function AppShell({ brand, navigation, navItems, currentNavId, na
74
74
  /** AN-01: avisa cada troca — pelo botão, ou pela largura cruzando 1280. Guardar a preferência é do app. */
75
75
  onSidebarCollapsedChange?: (collapsed: boolean) => void;
76
76
  contentVariant?: "surface" | "plain";
77
+ /** O alto contraste no app inteiro (o MNT-30, ADR-0090): contorno nos campos, nos cartões e nos botões, o texto de apoio
78
+ * mais perto do texto e o link sempre sublinhado. Marca o `AppShell` e a raiz da página (`data-contrast="high"`), para
79
+ * os menus e as janelas, que abrem fora dele, saírem iguais. */
80
+ highContrast?: boolean;
81
+ /** O texto grande no app inteiro (o MNT-30, ADR-0090): cada letra 2 acima e o controle médio a 44. `data-text="lg"` no
82
+ * `AppShell` e na raiz da página, pela mesma razão do `highContrast`. */
83
+ textSize?: "md" | "lg";
77
84
  }): React.JSX.Element;
78
85
  export declare namespace AppShell {
79
86
  export { AppShellSidebarToggle as SidebarToggle };
@@ -3,7 +3,7 @@ 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 { useEffect, useRef, useState, useSyncExternalStore } from "react";
6
+ import React, { useEffect, useRef, useState, useSyncExternalStore } from "react";
7
7
  import { Separator as BaseSeparator } from "@base-ui/react/separator";
8
8
  import { useDirection } from "@base-ui/react/direction-provider";
9
9
  import { cx, useAureaStrings } from "./internal.js";
@@ -167,8 +167,25 @@ function ProvedorDaLateral({ ligado, children, ...p }) {
167
167
  return ligado ? _jsx(Sidebar.Provider, { id: SHELL_NAV_ID, ...p, children: children }) : _jsx(_Fragment, { children: children });
168
168
  }
169
169
  function AppShellSidebarToggle(props) { return _jsx(Sidebar.Toggle, { ...props }); }
170
- export function AppShell({ brand, navigation, navItems, currentNavId, navLabel, topbar, topbarVariant = "floating", topbarDivider, topbarTabs, topbarCurrentTab, topbarTabsLabel, topbarTabsEnd, topbarActions, topbarBlurred, layout = "app", sidebarVariant, sidebarScrollbar, sidebarFooter, sidebarCollapsed, sidebarCollapsible, sidebarToggle = "edge", sidebarShortcut = true, sidebarIndicator, sidebarPanel, sidebarContrast, defaultSidebarCollapsed, onSidebarCollapsedChange, contentVariant = "surface", aside, asideLabel, asideVariant, children, className, ...props }) {
170
+ // O modo de leitura (o alto contraste, o texto grande) também na RAIZ da página (ADR-0090): menus, janelas e avisos abrem
171
+ // fora do `AppShell`, presos ao fim do documento, e sem isto sairiam sem o modo. Devolve a raiz como estava ao sair.
172
+ function useModoNaRaiz(atributo, valor) {
173
+ React.useEffect(() => {
174
+ if (valor === undefined || typeof document === "undefined")
175
+ return;
176
+ const raiz = document.documentElement;
177
+ const antes = raiz.getAttribute(atributo);
178
+ raiz.setAttribute(atributo, valor);
179
+ return () => { if (antes === null)
180
+ raiz.removeAttribute(atributo);
181
+ else
182
+ raiz.setAttribute(atributo, antes); };
183
+ }, [atributo, valor]);
184
+ }
185
+ export function AppShell({ brand, navigation, navItems, currentNavId, navLabel, topbar, topbarVariant = "floating", topbarDivider, topbarTabs, topbarCurrentTab, topbarTabsLabel, topbarTabsEnd, topbarActions, topbarBlurred, layout = "app", sidebarVariant, sidebarScrollbar, sidebarFooter, sidebarCollapsed, sidebarCollapsible, sidebarToggle = "edge", sidebarShortcut = true, sidebarIndicator, sidebarPanel, sidebarContrast, defaultSidebarCollapsed, onSidebarCollapsedChange, contentVariant = "surface", highContrast, textSize, aside, asideLabel, asideVariant, children, className, ...props }) {
171
186
  const s = useAureaStrings();
187
+ useModoNaRaiz("data-contrast", highContrast ? "high" : undefined);
188
+ useModoNaRaiz("data-text", textSize === "lg" ? "lg" : undefined);
172
189
  const docs = layout === "docs", lateral = layout === "sidebar";
173
190
  useGavetaFechaNoDesktop(docs);
174
191
  const controlado = sidebarCollapsed !== undefined;
@@ -224,7 +241,7 @@ export function AppShell({ brand, navigation, navItems, currentNavId, navLabel,
224
241
  // No arranjo, o topo é o pequeno (`size="sm"`), e o botão também: com o tamanho de sempre ele ficava 4 maior que os
225
242
  // vizinhos e fora da linha deles (medido a 375, sem toque; com toque, todos sobem para 44).
226
243
  const botaoDaGaveta = _jsx(IconButton, { className: "nav-toggle", icon: "list", label: s.navigationToggle, popoverTarget: SHELL_NAV_ID, ...(docs ? { size: "sm" } : {}) });
227
- return _jsx("div", { className: cx("app-shell", layout === "docs" && "app-shell-docs", lateral && "app-shell-lateral", !lateral && topbarVariant === "flush" && "app-shell-flush", !lateral && topbarVariant === "pill" && "app-shell-pill", recolhe && "app-shell-collapsible", aside != null && "app-shell-with-aside", className), ...props, children: _jsxs(ProvedorDaLateral, { ligado: recolhe, collapsed: efetiva, onCollapsedChange: definir, collapsible: forma, shortcut: sidebarShortcut, children: [_jsx("a", { className: "skip-link", href: `#${SHELL_MAIN_ID}`, children: s.skipToContent }), !lateral && _jsxs(Header, { variant: topbarVariant, divider: topbarDivider, tabs: topbarTabs, currentTab: topbarCurrentTab, tabsLabel: topbarTabsLabel, tabsEnd: topbarTabsEnd, ...(docs ? { maxWidth: "docs", ...(topbarVariant === "pill" ? {} : { size: "sm" }) } : topbarVariant === "pill" ? { maxWidth: "full" } : {}), blurred: topbarBlurred, brand: docs ? brand : _jsxs(_Fragment, { children: [botaoDaGaveta, brand] }), actions: topbarActions != null ? _jsxs(_Fragment, { children: [topbarActions, docs && botaoDaGaveta] }) : undefined, children: [topbar, docs && topbarActions == null && botaoDaGaveta] }), recolhe && !lateral && sidebarToggle !== "none" && _jsx("div", { className: "sidebar-toggle-slot", children: _jsx(Tooltip, { content: rotulo, side: "right", children: _jsx(IconButton, { className: "sidebar-toggle", size: "xs", icon: paraOComeco === direita ? "caret-right" : "caret-left", label: rotulo, "aria-expanded": !efetiva, "aria-controls": SHELL_NAV_ID, onClick: alternar }) }) }), _jsx(Sidebar, { id: SHELL_NAV_ID, popover: "auto", variant: sidebarVariant, scrollbar: sidebarScrollbar, collapsed: efetiva, collapsible: forma, indicator: sidebarIndicator, panel: sidebarPanel, contrast: sidebarContrast, items: navItems, current: currentNavId, label: navLabel, onToggle: focoDaGaveta, header: lateral ? brand : undefined, footer: sidebarFooter, children: navigation }), docs && aside != null && _jsx("div", { className: "docs-toc-bar", children: _jsxs("button", { type: "button", className: "docs-toc-toggle", popoverTarget: SHELL_TOC_ID, children: [_jsx(Icon, { name: "list-bullets" }), _jsx("span", { className: "docs-toc-current", "data-toc-current": "", children: asideLabel ?? s.tocLabel }), _jsx(Icon, { name: "caret-down" })] }) }), lateral
244
+ return _jsx("div", { "data-contrast": highContrast ? "high" : undefined, "data-text": textSize === "lg" ? "lg" : undefined, className: cx("app-shell", layout === "docs" && "app-shell-docs", lateral && "app-shell-lateral", !lateral && topbarVariant === "flush" && "app-shell-flush", !lateral && topbarVariant === "pill" && "app-shell-pill", recolhe && "app-shell-collapsible", aside != null && "app-shell-with-aside", className), ...props, children: _jsxs(ProvedorDaLateral, { ligado: recolhe, collapsed: efetiva, onCollapsedChange: definir, collapsible: forma, shortcut: sidebarShortcut, children: [_jsx("a", { className: "skip-link", href: `#${SHELL_MAIN_ID}`, children: s.skipToContent }), !lateral && _jsxs(Header, { variant: topbarVariant, divider: topbarDivider, tabs: topbarTabs, currentTab: topbarCurrentTab, tabsLabel: topbarTabsLabel, tabsEnd: topbarTabsEnd, ...(docs ? { maxWidth: "docs", ...(topbarVariant === "pill" ? {} : { size: "sm" }) } : topbarVariant === "pill" ? { maxWidth: "full" } : {}), blurred: topbarBlurred, brand: docs ? brand : _jsxs(_Fragment, { children: [botaoDaGaveta, brand] }), actions: topbarActions != null ? _jsxs(_Fragment, { children: [topbarActions, docs && botaoDaGaveta] }) : undefined, children: [topbar, docs && topbarActions == null && botaoDaGaveta] }), recolhe && !lateral && sidebarToggle !== "none" && _jsx("div", { className: "sidebar-toggle-slot", children: _jsx(Tooltip, { content: rotulo, side: "right", children: _jsx(IconButton, { className: "sidebar-toggle", size: "xs", icon: paraOComeco === direita ? "caret-right" : "caret-left", label: rotulo, "aria-expanded": !efetiva, "aria-controls": SHELL_NAV_ID, onClick: alternar }) }) }), _jsx(Sidebar, { id: SHELL_NAV_ID, popover: "auto", variant: sidebarVariant, scrollbar: sidebarScrollbar, collapsed: efetiva, collapsible: forma, indicator: sidebarIndicator, panel: sidebarPanel, contrast: sidebarContrast, items: navItems, current: currentNavId, label: navLabel, onToggle: focoDaGaveta, header: lateral ? brand : undefined, footer: sidebarFooter, children: navigation }), docs && aside != null && _jsx("div", { className: "docs-toc-bar", children: _jsxs("button", { type: "button", className: "docs-toc-toggle", popoverTarget: SHELL_TOC_ID, children: [_jsx(Icon, { name: "list-bullets" }), _jsx("span", { className: "docs-toc-current", "data-toc-current": "", children: asideLabel ?? s.tocLabel }), _jsx(Icon, { name: "caret-down" })] }) }), lateral
228
245
  ? _jsxs("div", { className: "app-shell-column", children: [topbar != null || topbarActions != null
229
246
  ? _jsx(Header, { variant: "flush", size: "sm", divider: topbarDivider, brand: _jsxs(_Fragment, { children: [botaoDaGaveta, sidebarToggle !== "none" && _jsx(AppShellSidebarToggle, {})] }), actions: topbarActions, children: topbar })
230
247
  : _jsxs("div", { className: "app-shell-narrow-bar", children: [botaoDaGaveta, brand] }), _jsx("main", { id: SHELL_MAIN_ID, tabIndex: -1, className: cx("content", contentVariant === "plain" && "content-plain"), children: children })] })
@@ -508,7 +508,7 @@ export function NavList({ items, size = "md", currentLabel, className, ...props
508
508
  }) });
509
509
  }
510
510
  // `size="lg"` (o site B — a busca nova, aprovada pela bancada em 01/11/2026, o item 2 e o D-2): a janela da largura `lg`
511
- // (1024), o campo de 64 com a lupa e o "Esc" dentro, o encaixe `filters` embaixo dele, o título do grupo em mono maiúsculo
511
+ // (1024), o campo de 52 (era 64 na 0.54.0; o Victor, 03/11/2026, como a referência) com a lupa e o "Esc" dentro, o encaixe `filters` embaixo dele, o título do grupo em mono maiúsculo
512
512
  // com a contagem, o item de 50 com o ícone num círculo de 40 tingido (`tone`), o título e a descrição cortada, o "↵" na
513
513
  // linha ativa, a prévia (`preview`) do item ativo ao lado da lista — some abaixo de 1240 — e o encaixe `footer` no pé,
514
514
  // ao lado das teclas. Sem `size="lg"`, a paleta de sempre; os encaixes só valem no `lg`.
@@ -76,6 +76,10 @@ interface MenuItemBase {
76
76
  label: ReactNode;
77
77
  disabled?: boolean;
78
78
  leadingIcon?: IconName;
79
+ /** 0.55.0: a explicação embaixo do nome, na letra pequena (a anatomia do item da referência). */
80
+ description?: ReactNode;
81
+ /** 0.55.0: o ícone na ponta do item — por exemplo, o "abre fora" de um link que abre outro site. */
82
+ trailingIcon?: IconName;
79
83
  }
80
84
  /** Item de ação — a forma de sempre. `kind` é opcional para não quebrar quem já usa. */
81
85
  export interface MenuActionDef extends MenuItemBase {
@@ -93,6 +97,8 @@ export interface MenuLinkDef {
93
97
  kind: "link";
94
98
  label: ReactNode;
95
99
  leadingIcon?: IconName;
100
+ description?: ReactNode;
101
+ trailingIcon?: IconName;
96
102
  href: string;
97
103
  target?: string;
98
104
  rel?: string;
@@ -137,10 +143,12 @@ export type MenuEntry = MenuActionDef | MenuLinkDef | MenuCheckboxDef | MenuRadi
137
143
  /** @deprecated desde 28/08/2026 — use `MenuEntry`. Mantido porque `MenuActionDef` É a forma
138
144
  * antiga: quem tipava com `MenuItemDef` continua compilando. */
139
145
  export type MenuItemDef = MenuActionDef;
140
- export declare function DropdownMenu({ trigger, items, side, label }: {
146
+ export declare function DropdownMenu({ trigger, items, side, align, label }: {
141
147
  trigger: ReactElement;
142
148
  items: MenuEntry[];
143
149
  side?: OverlaySide;
150
+ /** 0.55.0: por onde o menu se alinha ao gatilho — `"end"` encosta a ponta dele na ponta do botão (a referência, no botão dividido). */
151
+ align?: "start" | "center" | "end";
144
152
  label?: string;
145
153
  }): React.JSX.Element;
146
154
  export declare function ContextMenu({ children, items, label, className }: {
package/dist/overlays.js CHANGED
@@ -150,26 +150,31 @@ const marca = (children) => _jsx("span", { className: "menu-mark", "aria-hidden"
150
150
  // container configurado pelo provider e o submenu escaparia para o `document.body`. Duas
151
151
  // camadas do mesmo menu em containers diferentes é o tipo de divergência que só aparece quando
152
152
  // alguém monta a Aurea dentro de um shadow root ou de um diálogo nativo.
153
- function renderMenuItems(items, portal) {
153
+ // O item com `description` (0.55.0) se anuncia como o da referência: o NOME é só o rótulo e a explicação é a DESCRIÇÃO
154
+ // (`aria-labelledby` + `aria-describedby`). Sem isso o leitor de tela lia os dois colados ("View as MarkdownView page as…").
155
+ // `ids` é o `useId` de quem monta o menu, mais a posição do item: estável no servidor e no navegador.
156
+ const nomeDoItem = (x, id) => x.description != null ? { "aria-labelledby": `${id}-l`, "aria-describedby": `${id}-d` } : {};
157
+ function renderMenuItems(items, portal, ids = "") {
154
158
  return items.map((it, i) => {
155
159
  if (it === "separator")
156
160
  return _jsx(BaseMenu.Separator, { className: "menu-sep" }, i);
157
161
  const kind = it.kind ?? "item";
158
- const dentro = (x) => _jsxs(_Fragment, { children: [x.leadingIcon && _jsx(Icon, { name: x.leadingIcon }), x.label] });
162
+ const id = `${ids}-${i}`;
163
+ const dentro = (x, xid = id) => _jsxs(_Fragment, { children: [x.leadingIcon && _jsx(Icon, { name: x.leadingIcon }), x.description != null ? _jsxs("span", { className: "menu-item-text", children: [_jsx("span", { id: `${xid}-l`, children: x.label }), _jsx("span", { id: `${xid}-d`, className: "menu-item-description", children: x.description })] }) : x.label, x.trailingIcon && _jsx(Icon, { name: x.trailingIcon, className: "menu-item-trailing" })] });
159
164
  switch (kind) {
160
165
  case "group": {
161
166
  const g = it;
162
- return _jsxs(BaseMenu.Group, { children: [g.label && _jsx(BaseMenu.GroupLabel, { className: "menu-label", children: g.label }), renderMenuItems(g.items, portal)] }, i);
167
+ return _jsxs(BaseMenu.Group, { children: [g.label && _jsx(BaseMenu.GroupLabel, { className: "menu-label", children: g.label }), renderMenuItems(g.items, portal, id)] }, i);
163
168
  }
164
169
  case "radiogroup": {
165
170
  const g = it;
166
- 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);
171
+ 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, ...nomeDoItem(r, `${id}-${j}`), children: [marca(_jsx(BaseMenu.RadioItemIndicator, { className: "menu-ponto" })), dentro(r, `${id}-${j}`)] }, j))] }, i);
167
172
  }
168
173
  case "submenu": {
169
174
  const sm = it;
170
175
  // O submenu é uma RAIZ própria com gatilho próprio — não é um item que "abre outro menu".
171
176
  // É o que dá `aria-haspopup`, a seta para a direita e o fechamento em cascata de graça.
172
- return _jsxs(BaseMenu.SubmenuRoot, { children: [_jsxs(BaseMenu.SubmenuTrigger, { className: "menu-item", disabled: sm.disabled, children: [dentro(sm), _jsx(Icon, { name: "caret-right", size: "sm", className: "menu-seta" })] }), _jsx(BaseMenu.Portal, { container: portal, children: _jsx(BaseMenu.Positioner, { className: "positioner", side: "inline-end", sideOffset: 4, children: _jsx(BaseMenu.Popup, { className: "menu", children: renderMenuItems(sm.items, portal) }) }) })] }, i);
177
+ return _jsxs(BaseMenu.SubmenuRoot, { children: [_jsxs(BaseMenu.SubmenuTrigger, { className: "menu-item", disabled: sm.disabled, ...nomeDoItem(sm, id), children: [dentro(sm), _jsx(Icon, { name: "caret-right", size: "sm", className: "menu-seta" })] }), _jsx(BaseMenu.Portal, { container: portal, children: _jsx(BaseMenu.Positioner, { className: "positioner", side: "inline-end", sideOffset: 4, children: _jsx(BaseMenu.Popup, { className: "menu", children: renderMenuItems(sm.items, portal, id) }) }) })] }, i);
173
178
  }
174
179
  case "checkbox": {
175
180
  const c = it;
@@ -178,26 +183,26 @@ function renderMenuItems(items, portal) {
178
183
  // assinatura pública da Aurea mudar junto com uma versão do motor. Mesma decisão do
179
184
  // `onValueChange` do grupo de rádio. O teste pegou: sem o embrulho, o `vi.fn()` recebia um
180
185
  // `PointerEvent` de brinde.
181
- 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: "check", size: "sm" }) })), dentro(c)] }, i);
186
+ return _jsxs(BaseMenu.CheckboxItem, { className: "menu-item", disabled: c.disabled, checked: c.checked, defaultChecked: c.defaultChecked, onCheckedChange: v => c.onCheckedChange?.(v), ...nomeDoItem(c, id), children: [marca(_jsx(BaseMenu.CheckboxItemIndicator, { children: _jsx(Icon, { name: "check", size: "sm" }) })), dentro(c)] }, i);
182
187
  }
183
188
  case "link": {
184
189
  const l = it;
185
- return _jsx(BaseMenu.LinkItem, { className: "menu-item", href: l.href, target: l.target, rel: l.rel, closeOnClick: l.closeOnClick ?? true, children: dentro(l) }, i);
190
+ return _jsx(BaseMenu.LinkItem, { className: "menu-item", href: l.href, target: l.target, rel: l.rel, closeOnClick: l.closeOnClick ?? true, ...nomeDoItem(l, id), children: dentro(l) }, i);
186
191
  }
187
192
  default: {
188
193
  const a = it;
189
- return _jsx(BaseMenu.Item, { className: "menu-item", disabled: a.disabled, onClick: a.onClick, render: a.render, children: dentro(a) }, i);
194
+ return _jsx(BaseMenu.Item, { className: "menu-item", disabled: a.disabled, onClick: a.onClick, render: a.render, ...nomeDoItem(a, id), children: dentro(a) }, i);
190
195
  }
191
196
  }
192
197
  });
193
198
  }
194
- 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, { className: "positioner", side: side, sideOffset: 6, children: _jsx(BaseMenu.Popup, { className: "menu", "aria-label": label, children: renderMenuItems(items, portal) }) }) })] }); }
199
+ export function DropdownMenu({ trigger, items, side = "bottom", align = "center", label }) { const portal = usePortalContainer(); const ids = React.useId(); return _jsxs(BaseMenu.Root, { children: [_jsx(BaseMenu.Trigger, { render: trigger }), _jsx(BaseMenu.Portal, { container: portal, children: _jsx(BaseMenu.Positioner, { className: "positioner", side: side, align: align, sideOffset: 6, children: _jsx(BaseMenu.Popup, { className: "menu", "aria-label": label, children: renderMenuItems(items, portal, ids) }) }) })] }); }
195
200
  // ContextMenu: abre no botão direito e — por teclado — em Shift+F10 / tecla Menu,
196
201
  // que o browser só dispara (como evento contextmenu) sobre um elemento FOCADO. Por
197
202
  // isso o gatilho é focável (tabIndex 0) e tem nome acessível (auditoria 18/07/2026,
198
203
  // MÉDIO 3); sem isso o teclado não alcança o menu. Consumidor pode sobrescrever
199
204
  // tabIndex via children se o próprio já for focável.
200
- 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, { className: "positioner", children: _jsx(BaseContextMenu.Popup, { className: "menu", "aria-label": label, children: renderMenuItems(items, portal) }) }) })] }); }
205
+ export function ContextMenu({ children, items, label, className }) { const portal = usePortalContainer(); const ids = React.useId(); 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, { className: "positioner", children: _jsx(BaseContextMenu.Popup, { className: "menu", "aria-label": label, children: renderMenuItems(items, portal, ids) }) }) })] }); }
201
206
  // G-AXIS-06 — o valor responsivo RESOLVIDO é a única fonte de verdade: ele vai para o motor, o
202
207
  // motor publica `data-orientation`/`aria-orientation` e ajusta o teclado, e a pele reage ao
203
208
  // atributo publicado. Ver `decisions/0047-css-first-para-apresentacao-runtime-para-semantica.md`.
@@ -206,5 +211,6 @@ export function Menubar({ menus, label, modal, disabled, loopFocus, orientation,
206
211
  const ancora = useRef(null);
207
212
  const resolvida = useValorResponsivo(orientation, "horizontal", ancora);
208
213
  const vertical = resolvida === "vertical";
209
- 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, { className: "positioner", side: vertical ? "inline-end" : "bottom", align: "start", sideOffset: 4, children: _jsx(BaseMenu.Popup, { className: "menu", children: renderMenuItems(m.items) }) }) })] }, i)) });
214
+ const ids = React.useId();
215
+ 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, { className: "positioner", side: vertical ? "inline-end" : "bottom", align: "start", sideOffset: 4, children: _jsx(BaseMenu.Popup, { className: "menu", children: renderMenuItems(m.items, undefined, `${ids}-${i}`) }) }) })] }, i)) });
210
216
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aurea-uds/react",
3
- "version": "0.54.0",
3
+ "version": "0.56.0",
4
4
  "description": "Os componentes React da Aurea UDS. Importe @aurea-uds/fonts/css e @aurea-uds/core/css junto.",
5
5
  "keywords": [
6
6
  "aurea",