@aurea-uds/react 0.30.0 → 0.32.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -146,7 +146,7 @@ function useLargura(query) {
146
146
  return () => mq.removeEventListener?.("change", avisar);
147
147
  }, () => typeof window !== "undefined" && typeof window.matchMedia === "function" && window.matchMedia(query).matches, () => false);
148
148
  }
149
- export function AppShell({ brand, navigation, navItems, currentNavId, navLabel, topbar, topbarVariant = "floating", topbarDivider, sidebarVariant, sidebarCollapsed, sidebarCollapsible, defaultSidebarCollapsed, onSidebarCollapsedChange, contentVariant = "surface", children, className, ...props }) {
149
+ export function AppShell({ brand, navigation, navItems, currentNavId, navLabel, topbar, topbarVariant = "floating", topbarDivider, sidebarVariant, sidebarScrollbar, sidebarCollapsed, sidebarCollapsible, defaultSidebarCollapsed, onSidebarCollapsedChange, contentVariant = "surface", aside, asideLabel, asideVariant, children, className, ...props }) {
150
150
  const s = useAureaStrings();
151
151
  useGavetaFechaNoDesktop();
152
152
  const controlado = sidebarCollapsed !== undefined;
@@ -180,7 +180,7 @@ export function AppShell({ brand, navigation, navItems, currentNavId, navLabel,
180
180
  // A condição vai escrita no `icon=` do botão, e não numa variável: é ali que o check 46 lê os
181
181
  // ícones que o shell desenha.
182
182
  const paraOComeco = !efetiva, direita = useDirection() === "rtl";
183
- return _jsxs("div", { className: cx("app-shell", topbarVariant === "flush" && "app-shell-flush", sidebarCollapsible && "app-shell-collapsible", className), ...props, children: [_jsx("a", { className: "skip-link", href: `#${SHELL_MAIN_ID}`, children: s.skipToContent }), _jsx(Header, { variant: topbarVariant, divider: topbarDivider, brand: _jsxs(_Fragment, { children: [_jsx(IconButton, { className: "nav-toggle", icon: "list", label: s.navigationToggle, popoverTarget: SHELL_NAV_ID }), brand] }), children: topbar }), sidebarCollapsible && _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, collapsed: efetiva, items: navItems, current: currentNavId, label: navLabel, onToggle: focoDaGaveta, children: navigation }), _jsx("main", { id: SHELL_MAIN_ID, tabIndex: -1, className: cx("content", contentVariant === "plain" && "content-plain"), children: children })] });
183
+ return _jsxs("div", { className: cx("app-shell", topbarVariant === "flush" && "app-shell-flush", sidebarCollapsible && "app-shell-collapsible", aside != null && "app-shell-with-aside", className), ...props, children: [_jsx("a", { className: "skip-link", href: `#${SHELL_MAIN_ID}`, children: s.skipToContent }), _jsx(Header, { variant: topbarVariant, divider: topbarDivider, brand: _jsxs(_Fragment, { children: [_jsx(IconButton, { className: "nav-toggle", icon: "list", label: s.navigationToggle, popoverTarget: SHELL_NAV_ID }), brand] }), children: topbar }), sidebarCollapsible && _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, items: navItems, current: currentNavId, label: navLabel, onToggle: focoDaGaveta, children: navigation }), _jsx("main", { id: SHELL_MAIN_ID, tabIndex: -1, className: cx("content", contentVariant === "plain" && "content-plain"), children: children }), aside != null && _jsx(Sidebar, { placement: "right", variant: asideVariant ?? sidebarVariant, className: "app-shell-aside", "aria-label": asideLabel ?? s.tocLabel, children: aside })] });
184
184
  }
185
185
  export function Separator({ orientation, spacing = "none", className, ...props }) {
186
186
  const ancora = useRef(null);
package/dist/markup.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import React, { type HTMLAttributes, type InputHTMLAttributes, type ReactElement, type ReactNode, type RefAttributes, type TextareaHTMLAttributes } from "react";
1
+ import React, { type AnchorHTMLAttributes, type HTMLAttributes, type InputHTMLAttributes, type ReactElement, type ReactNode, type RefAttributes, type TextareaHTMLAttributes } from "react";
2
2
  import { type Responsive } from "./pure.js";
3
3
  import type { ComponentSize } from "./actions.js";
4
4
  import type { SidebarItem } from "./navigation-client.js";
@@ -15,7 +15,7 @@ type CardBase = HTMLAttributes<HTMLDivElement> & RefAttributes<HTMLDivElement> &
15
15
  accent?: CardAccent;
16
16
  };
17
17
  export type CardProps = CardBase & ({
18
- variant?: "base" | "raised" | "inset" | "selected" | "danger" | "contrast";
18
+ variant?: "base" | "raised" | "inset" | "outline" | "selected" | "danger" | "contrast";
19
19
  render?: ReactElement;
20
20
  } | {
21
21
  variant: "interactive";
@@ -182,9 +182,14 @@ export type TypographyWeight = "normal" | "medium" | "semibold" | "bold" | "extr
182
182
  export type TypographyAlign = "start" | "center" | "end" | "justify";
183
183
  export type HeadingLevel = 1 | 2 | 3 | 4 | 5 | 6;
184
184
  export type ParagraphSize = "base" | "sm" | "xs";
185
+ /** A cor do texto com o NOME do nativo (DOC-11, os mesmos nomes na web e no nativo). `link` é o amarelo
186
+ * de ESCREVER (legível nos dois temas); `primary` é o de PREENCHER, e no tema claro quase some — como no
187
+ * nativo. As cores de estado são o par `-400` (ADR-0061). Vence o `color` quando os dois vêm. */
188
+ export type TypographyTone = "default" | "muted" | "subtle" | "primary" | "link" | "danger" | "success" | "warning" | "info";
185
189
  interface TypographyBase {
186
190
  align?: TypographyAlign;
187
191
  color?: TypographyColor;
192
+ tone?: TypographyTone;
188
193
  weight?: TypographyWeight;
189
194
  truncate?: boolean;
190
195
  /** GAR-16 (0.28.0): algarismos da mesma largura — para número que alinha em coluna (preço, ano,
@@ -194,18 +199,18 @@ interface TypographyBase {
194
199
  export interface TextProps extends Omit<HTMLAttributes<HTMLSpanElement>, "color">, RefAttributes<HTMLSpanElement>, TypographyBase {
195
200
  type?: TypographyType;
196
201
  }
197
- export declare function Text({ type, align, color, weight, truncate, numeric, className, ...props }: TextProps): React.JSX.Element;
202
+ export declare function Text({ type, align, color, tone, weight, truncate, numeric, className, ...props }: TextProps): React.JSX.Element;
198
203
  export interface HeadingProps extends Omit<HTMLAttributes<HTMLHeadingElement>, "color">, RefAttributes<HTMLHeadingElement>, TypographyBase {
199
204
  level?: HeadingLevel;
200
205
  }
201
- export declare function Heading({ level, align, color, weight, truncate, numeric, className, ...props }: HeadingProps): React.JSX.Element;
206
+ export declare function Heading({ level, align, color, tone, weight, truncate, numeric, className, ...props }: HeadingProps): React.JSX.Element;
202
207
  export interface ParagraphProps extends Omit<HTMLAttributes<HTMLParagraphElement>, "color">, RefAttributes<HTMLParagraphElement>, TypographyBase {
203
208
  size?: ParagraphSize;
204
209
  }
205
- export declare function Paragraph({ size, align, color, weight, truncate, numeric, className, ...props }: ParagraphProps): React.JSX.Element;
210
+ export declare function Paragraph({ size, align, color, tone, weight, truncate, numeric, className, ...props }: ParagraphProps): React.JSX.Element;
206
211
  export interface CodeProps extends Omit<HTMLAttributes<HTMLElement>, "color">, RefAttributes<HTMLElement>, TypographyBase {
207
212
  }
208
- export declare function Code({ align, color, weight, truncate, className, ...props }: CodeProps): React.JSX.Element;
213
+ export declare function Code({ align, color, tone, weight, truncate, className, ...props }: CodeProps): React.JSX.Element;
209
214
  export type BadgeEmphasis = "soft" | "solid" | "outline";
210
215
  export type BadgeSize = "xs" | "sm" | "md" | "lg";
211
216
  export type BadgePlacement = "top-end" | "top-start" | "bottom-end" | "bottom-start";
@@ -213,6 +218,9 @@ export type BadgeFit = "auto" | "content";
213
218
  export declare function formatBadgeCount(count: number, max?: number): string;
214
219
  export interface BadgeProps extends HTMLAttributes<HTMLSpanElement>, RefAttributes<HTMLSpanElement> {
215
220
  variant?: BadgeVariant;
221
+ /** A cor, com o NOME do nativo (DOC-11, 0.32.0: os mesmos nomes na web e no nativo). É o mesmo
222
+ * eixo do `variant`, e vence quando os dois vêm. */
223
+ tone?: BadgeVariant;
216
224
  /** `soft` é o tom de sempre; `solid` preenche com o acento; `outline` é só contorno. */
217
225
  emphasis?: BadgeEmphasis;
218
226
  size?: BadgeSize;
@@ -239,7 +247,7 @@ export interface BadgeProps extends HTMLAttributes<HTMLSpanElement>, RefAttribut
239
247
  * Chama-se assim, e não `content`, porque este colide com o atributo HTML de mesmo nome. */
240
248
  badgeContent?: ReactNode;
241
249
  }
242
- export declare function Badge({ variant, emphasis, size, dot, leading, trailing, image, imageAlt, count, max, showZero, fit, anchor, anchorShape, invisible, badgeContent, children, className, ...props }: BadgeProps): React.JSX.Element;
250
+ export declare function Badge({ variant: variantProp, tone, emphasis, size, dot, leading, trailing, image, imageAlt, count, max, showZero, fit, anchor, anchorShape, invisible, badgeContent, children, className, ...props }: BadgeProps): React.JSX.Element;
243
251
  /** O tom da barra: a mesma lista fechada do `ButtonTone`. Pausado é `neutral`; falha é `danger`. */
244
252
  export type ProgressTone = "brand" | "neutral" | "success" | "warning" | "danger" | "info";
245
253
  export interface ProgressProps {
@@ -252,10 +260,21 @@ export interface ProgressProps {
252
260
  * que falta, bytes. Em texto, vai junto no `aria-valuetext`. */
253
261
  detail?: ReactNode;
254
262
  tone?: ProgressTone;
263
+ /** 0.32.0: o rótulo À MOSTRA, no alto à esquerda (o da barra da referência). Sem ele, o `label` só
264
+ * nomeia a barra para o leitor de tela, como sempre. */
265
+ showLabel?: boolean;
266
+ /** 0.32.0: o valor ("60%") no alto à direita, quando não há `detail`. */
267
+ showValue?: boolean;
268
+ /** A espessura do trilho: sm 4, md 8, lg 12 — os da referência. */
269
+ size?: ProgressSize;
255
270
  className?: string;
256
271
  }
257
- export declare function Progress({ value, label, detail, tone, className }: ProgressProps): React.JSX.Element;
258
- export declare function Skeleton({ className, ...props }: HTMLAttributes<HTMLDivElement> & RefAttributes<HTMLDivElement>): React.JSX.Element;
272
+ export type ProgressSize = "sm" | "md" | "lg";
273
+ export declare function Progress({ value, label, detail, tone, showLabel, showValue, size, className }: ProgressProps): React.JSX.Element;
274
+ export type SkeletonAnimation = "pulse" | "none";
275
+ export declare function Skeleton({ animation, className, ...props }: HTMLAttributes<HTMLDivElement> & RefAttributes<HTMLDivElement> & {
276
+ animation?: SkeletonAnimation;
277
+ }): React.JSX.Element;
259
278
  export declare function AvatarGroup({ children, max, total, label, size, className }: {
260
279
  children: ReactNode;
261
280
  max?: number;
@@ -303,6 +322,8 @@ export declare function Topbar(props: HeaderProps): React.JSX.Element;
303
322
  export declare function Kbd({ children, className, ...props }: HTMLAttributes<HTMLElement> & RefAttributes<HTMLElement>): React.JSX.Element;
304
323
  export interface TextareaProps extends TextareaHTMLAttributes<HTMLTextAreaElement>, RefAttributes<HTMLTextAreaElement> {
305
324
  size?: Responsive<FieldSize>;
325
+ /** 0.32.0, a da referência: `secondary` é o fundo neutro, para o campo dentro de um cartão. */
326
+ variant?: FieldVariant;
306
327
  }
307
328
  export declare const Textarea: React.ForwardRefExoticComponent<Omit<TextareaProps, "ref"> & RefAttributes<HTMLTextAreaElement>>;
308
329
  export declare function Checkbox({ label, labelHidden, description, size, className, ...props }: Omit<InputHTMLAttributes<HTMLInputElement>, "size"> & RefAttributes<HTMLInputElement> & {
@@ -324,6 +345,7 @@ export declare function Range({ orientation, className, ...props }: InputHTMLAtt
324
345
  orientation?: Responsive<RangeOrientation>;
325
346
  }): React.JSX.Element;
326
347
  export type FieldSize = Extract<ComponentSize, "sm" | "md" | "lg">;
348
+ export type FieldVariant = "primary" | "secondary";
327
349
  export declare function Label({ htmlFor, className, children, ...props }: HTMLAttributes<HTMLLabelElement> & RefAttributes<HTMLLabelElement> & {
328
350
  htmlFor?: string;
329
351
  }): React.JSX.Element;
@@ -339,4 +361,62 @@ export declare function InputGroupAddon({ side, layout, className, children, ...
339
361
  export declare function AspectRatio({ ratio, className, style, ...props }: HTMLAttributes<HTMLDivElement> & RefAttributes<HTMLDivElement> & {
340
362
  ratio?: number;
341
363
  }): React.JSX.Element;
364
+ export interface LinkProps extends AnchorHTMLAttributes<HTMLAnchorElement>, RefAttributes<HTMLAnchorElement> {
365
+ /** Abre noutra aba e mostra a seta. */
366
+ external?: boolean;
367
+ /** Não navega: sai sem `href` e com `aria-disabled`. */
368
+ disabled?: boolean;
369
+ }
370
+ export declare function Link({ external, disabled, href, target, rel, className, children, ...props }: LinkProps): React.JSX.Element;
371
+ export type ColorSwatchSize = "xs" | "sm" | "md" | "lg" | "xl";
372
+ export interface ColorSwatchProps extends HTMLAttributes<HTMLSpanElement>, RefAttributes<HTMLSpanElement> {
373
+ /** Qualquer cor do CSS: `#f0c200`, `oklch(…)`, `var(--primary)`. */
374
+ color: string;
375
+ size?: ColorSwatchSize;
376
+ /** O nome que o leitor de tela anuncia ("Amarelo Aurea"). Sem ele, o valor da cor. */
377
+ label?: string;
378
+ }
379
+ export interface ColorSwatchOption {
380
+ color: string;
381
+ label?: string;
382
+ /** Esta cor não se escolhe (as outras sim). */
383
+ disabled?: boolean;
384
+ }
385
+ /** O que as duas formas têm em comum: a escala. (É a base que o leitor da API segue em `base & (a | b)`.) */
386
+ export interface ColorSwatchBase {
387
+ size?: ColorSwatchSize;
388
+ }
389
+ export interface ColorSwatchPickerProps {
390
+ /** As cores para escolher — `"#0485f7"` ou `{color, label}`. É o que faz da amostra um grupo de escolha. */
391
+ options: readonly (string | ColorSwatchOption)[];
392
+ /** O nome do grupo no formulário, e o que faz das amostras UM grupo (as setas andam entre elas). */
393
+ name: string;
394
+ /** O nome do grupo para o leitor de tela ("Cor de destaque"). */
395
+ label: string;
396
+ value?: string;
397
+ defaultValue?: string;
398
+ /** O evento do navegador, como no `Radio`: `event.target.value` é a cor escolhida. */
399
+ onChange?: React.ChangeEventHandler<HTMLInputElement>;
400
+ /** `"grid"` (o padrão: em linha, quebrando) ou `"stack"` (em coluna), como na referência. */
401
+ layout?: "grid" | "stack";
402
+ /** A marca da escolhida, no lugar do "visto" (um `<Icon>`, por exemplo). Ela ganha a tinta que contrasta. */
403
+ indicator?: ReactNode;
404
+ size?: ColorSwatchSize;
405
+ disabled?: boolean;
406
+ id?: string;
407
+ className?: string;
408
+ }
409
+ export declare function ColorSwatch(props: ColorSwatchBase & (ColorSwatchProps | ColorSwatchPickerProps)): React.JSX.Element;
410
+ export type ProgressCircleSize = "sm" | "md" | "lg";
411
+ export interface ProgressCircleProps extends HTMLAttributes<HTMLDivElement>, RefAttributes<HTMLDivElement> {
412
+ /** De `min` a `max`. Sem ele, o círculo gira. */
413
+ value?: number;
414
+ min?: number;
415
+ max?: number;
416
+ size?: ProgressCircleSize;
417
+ tone?: ProgressTone;
418
+ /** O nome que o leitor de tela anuncia ("Enviando o arquivo"). */
419
+ label: string;
420
+ }
421
+ export declare function ProgressCircle({ value, min, max, size, tone, label, className, ...props }: ProgressCircleProps): React.JSX.Element;
342
422
  export {};
package/dist/markup.js CHANGED
@@ -37,6 +37,8 @@ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-run
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
39
  import { cx, fundirRender, peleDoEixo } from "./pure.js";
40
+ // As contas de cor são funções puras, sem estado e sem navegador (ADR-0069): o `ColorSwatch` usa a do contraste.
41
+ import { contrastingInk, hsbToRgb, parseColor } from "./color.js";
40
42
  export function Card({ variant = "base", padding = "normal", orientation = "vertical", accent, className, render, ...props }) {
41
43
  return fundirRender(render, { className: cx("card", variant !== "base" && `card-${variant}`, accent && `card-accent card-accent-${accent}`, padding === "none" && "card-flush", orientation === "horizontal" && "card-horizontal", className), ...(variant === "contrast" ? { "data-theme": "dark" } : null), ...props });
42
44
  }
@@ -104,16 +106,17 @@ export function Timeline({ items }) { return _jsx("ol", { className: "timeline",
104
106
  // `dangerouslySetInnerHTML` tem de SANITIZAR antes. A Aurea desenha o texto; ela não pode decidir
105
107
  // o que é seguro renderizar. Está dito na ficha, em voz alta.
106
108
  export function Prose({ className, ...props }) { return _jsx("div", { className: cx("prose", className), ...props }); }
107
- const tipografia = (tipo, { align, color, weight, truncate, numeric }, className) => cx("typography", `typography-${tipo}`, color === "muted" && "typography-muted", weight && `typography-weight-${weight}`, align && `typography-align-${align}`, truncate && "typography-truncate", numeric && "typography-numeric", className);
108
- export function Text({ type = "body", align, color, weight, truncate, numeric, className, ...props }) { return _jsx("span", { className: tipografia(type, { align, color, weight, truncate, numeric }, className), ...props }); }
109
+ const tipografia = (tipo, { align, color, tone, weight, truncate, numeric }, className) => cx("typography", `typography-${tipo}`, (tone ?? color) === "muted" && "typography-muted", tone && tone !== "default" && tone !== "muted" && `typography-tone-${tone}`, weight && `typography-weight-${weight}`, align && `typography-align-${align}`, truncate && "typography-truncate", numeric && "typography-numeric", className);
110
+ export function Text({ type = "body", align, color, tone, weight, truncate, numeric, className, ...props }) { return _jsx("span", { className: tipografia(type, { align, color, tone, weight, truncate, numeric }, className), ...props }); }
109
111
  // O nível decide o elemento E o tamanho, como na referência: um `h2` tem a cara de título 2.
110
- export function Heading({ level = 1, align, color, weight, truncate, numeric, className, ...props }) { const Tag = `h${level}`; return _jsx(Tag, { className: tipografia(`h${level}`, { align, color, weight, truncate, numeric }, className), ...props }); }
111
- export function Paragraph({ size = "base", align, color, weight, truncate, numeric, className, ...props }) { return _jsx("p", { className: tipografia(size === "base" ? "body" : `body-${size}`, { align, color, weight, truncate, numeric }, className), ...props }); }
112
- export function Code({ align, color, weight, truncate, className, ...props }) { return _jsx("code", { className: tipografia("code", { align, color, weight, truncate }, className), ...props }); }
112
+ export function Heading({ level = 1, align, color, tone, weight, truncate, numeric, className, ...props }) { const Tag = `h${level}`; return _jsx(Tag, { className: tipografia(`h${level}`, { align, color, tone, weight, truncate, numeric }, className), ...props }); }
113
+ export function Paragraph({ size = "base", align, color, tone, weight, truncate, numeric, className, ...props }) { return _jsx("p", { className: tipografia(size === "base" ? "body" : `body-${size}`, { align, color, tone, weight, truncate, numeric }, className), ...props }); }
114
+ export function Code({ align, color, tone, weight, truncate, className, ...props }) { return _jsx("code", { className: tipografia("code", { align, color, tone, weight, truncate }, className), ...props }); }
113
115
  // `max` com `+` é o idioma universal do contador (três das referências o usam). Exportada porque quem
114
116
  // escreve o nome acessível precisa do MESMO texto — "99+ unread" tem de bater com o que se vê.
115
117
  export function formatBadgeCount(count, max = 99) { return count > max ? `${max}+` : String(count); }
116
- export function Badge({ variant = "neutral", emphasis = "soft", size = "md", dot, leading, trailing, image, imageAlt, count, max = 99, showZero, fit = "auto", anchor, anchorShape = "square", invisible, badgeContent, children, className, ...props }) {
118
+ export function Badge({ variant: variantProp = "neutral", tone, emphasis = "soft", size = "md", dot, leading, trailing, image, imageAlt, count, max = 99, showZero, fit = "auto", anchor, anchorShape = "square", invisible, badgeContent, children, className, ...props }) {
119
+ const variant = tone ?? variantProp;
117
120
  const numero = count != null ? formatBadgeCount(count, max) : undefined;
118
121
  const miolo = anchor ? (numero ?? badgeContent) : (numero ?? children);
119
122
  // Ponto puro: `dot` sem nada para mostrar. Aí o badge não tem conteúdo, e ganha medida própria.
@@ -131,7 +134,7 @@ export function Badge({ variant = "neutral", emphasis = "soft", size = "md", dot
131
134
  // AN-07 (03/10/2026). Tudo novo é acréscimo: com `value` e sem `detail` nem `tone`, a marcação é a
132
135
  // de antes, mais a classe da caixa de fora. A anatomia é a da barra de progresso da referência: a
133
136
  // saída no alto à direita, o trilho embaixo, e sem `aria-valuenow` quando não há total.
134
- export function Progress({ value, label, detail, tone = "brand", className }) {
137
+ export function Progress({ value, label, detail, tone = "brand", showLabel, showValue, size = "md", className }) {
135
138
  const semTotal = value == null || Number.isNaN(value);
136
139
  const pct = semTotal ? undefined : Math.max(0, Math.min(100, value));
137
140
  const temApoio = detail != null && detail !== false && detail !== "";
@@ -139,9 +142,14 @@ export function Progress({ value, label, detail, tone = "brand", className }) {
139
142
  // vezes. Apoio que não é texto (um ícone, um link) fica onde está, para o leitor achar sozinho.
140
143
  const emTexto = temApoio && (typeof detail === "string" || typeof detail === "number") ? String(detail) : undefined;
141
144
  const valuetext = emTexto == null ? undefined : pct == null ? emTexto : `${pct}%, ${emTexto}`;
142
- return _jsxs("div", { className: cx("progress-field", className), children: [temApoio && _jsx("div", { className: "progress-detail", "aria-hidden": emTexto != null || undefined, children: detail }), _jsx("div", { className: cx("progress", semTotal && "progress-indeterminate", tone !== "brand" && `progress-tone-${tone}`), role: "progressbar", "aria-label": label, "aria-valuemin": 0, "aria-valuemax": 100, "aria-valuenow": pct, "aria-valuetext": valuetext, children: _jsx("span", { style: pct == null ? undefined : { width: `${pct}%` } }) })] });
145
+ // O rótulo à mostra sai do leitor de tela (`aria-hidden`): quem nomeia a barra continua o `aria-label`,
146
+ // com o mesmo texto — sem isso, o leitor o leria duas vezes. (Este arquivo não usa hook, então nada de
147
+ // id gerado para `aria-labelledby`.)
148
+ const rotuloVisivel = !!showLabel && !!label;
149
+ const valor = !temApoio && showValue && pct != null ? `${Math.round(pct)}%` : undefined;
150
+ return _jsxs("div", { className: cx("progress-field", className), children: [rotuloVisivel && _jsx("span", { className: "progress-label", "aria-hidden": "true", children: label }), temApoio && _jsx("div", { className: "progress-detail", "aria-hidden": emTexto != null || undefined, children: detail }), valor && _jsx("div", { className: "progress-detail", "aria-hidden": "true", children: valor }), _jsx("div", { className: cx("progress", semTotal && "progress-indeterminate", tone !== "brand" && `progress-tone-${tone}`, size !== "md" && `progress-${size}`), role: "progressbar", "aria-label": label, "aria-valuemin": 0, "aria-valuemax": 100, "aria-valuenow": pct, "aria-valuetext": valuetext, children: _jsx("span", { style: pct == null ? undefined : { width: `${pct}%` } }) })] });
143
151
  }
144
- export function Skeleton({ className, ...props }) { return _jsx("div", { className: cx("skeleton", className), "aria-hidden": "true", ...props }); }
152
+ export function Skeleton({ animation = "pulse", className, ...props }) { return _jsx("div", { className: cx("skeleton", animation === "none" && "skeleton-static", className), "aria-hidden": "true", ...props }); }
145
153
  // ── Identity ─────────────────────────────────────────────────────────────────────────────────
146
154
  // `React.Children` não é hook — é utilitário de leitura de `children`, e roda no servidor.
147
155
  export function AvatarGroup({ children, max, total, label, size, className }) {
@@ -180,8 +188,12 @@ export function Topbar(props) { return _jsx(Header, { ...props }); }
180
188
  // Kbd — representação de tecla/atalho. <kbd> é o elemento HTML certo; a pele é nossa. Ele já
181
189
  // morava fora da casa dele (no `internal.tsx`, por causa do `Button`); agora mora aqui, pelo
182
190
  // mesmo motivo elevado a regra.
183
- export function Kbd({ children, className, ...props }) { return _jsx("kbd", { className: cx("kbd", className), ...props, children: children }); }
184
- export const Textarea = forwardRef(function Textarea({ className, size, ...props }, ref) { return _jsx("textarea", { ref: ref, className: cx("textarea", peleDoEixo("textarea", size), className), ...props }); });
191
+ // 0.32.0: uma tecla de UM caractere ("K", "⌘", "/") sai redonda (`kbd-single`), e uma palavra
192
+ // ("Esc", "Enter") em cápsula — o canto da Aurea sobre a medida da referência. Conta-se o que a
193
+ // pessoa vê (`Array.from` não parte um emoji ao meio); conteúdo que não é texto fica em cápsula.
194
+ const umCaractere = (c) => (typeof c === "string" || typeof c === "number") && Array.from(String(c).trim()).length === 1;
195
+ export function Kbd({ children, className, ...props }) { return _jsx("kbd", { className: cx("kbd", umCaractere(children) && "kbd-single", className), ...props, children: children }); }
196
+ export const Textarea = forwardRef(function Textarea({ className, size, variant, ...props }, ref) { return _jsx("textarea", { ref: ref, className: cx("textarea", peleDoEixo("textarea", size), variant === "secondary" && "textarea-secondary", className), ...props }); });
185
197
  // `size` nos três marcáveis pela mesma razão do campo: uma linha de formulário com um campo `sm`
186
198
  // e um checkbox de tamanho fixo ao lado desalinha. O degrau vai na MARCA, não no rótulo.
187
199
  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 })] })] })] }); }
@@ -228,3 +240,28 @@ export function InputGroupAddon({ side = "start", layout = "inline", className,
228
240
  // escreve o `padding-top: 56.25%` de 2015, ou esquece o `min-width: 0` que impede a caixa de
229
241
  // estourar dentro de um grid.
230
242
  export function AspectRatio({ ratio = 1, className, style, ...props }) { return _jsx("div", { className: cx("aspect-ratio", className), style: { aspectRatio: ratio, ...style }, ...props }); }
243
+ export function Link({ external, disabled, href, target, rel, className, children, ...props }) {
244
+ return _jsxs("a", { className: cx("link", className), href: disabled ? undefined : href, "aria-disabled": disabled || undefined, target: external ? target ?? "_blank" : target, rel: external ? rel ?? "noopener noreferrer" : rel, ...props, children: [children, external && _jsx("span", { className: "link-external", "aria-hidden": "true" })] });
245
+ }
246
+ // Transparente quando o texto diz alfa abaixo de 1 (hex de 4 ou 8 dígitos, `rgba()`, `hsla()`…). Cor de token ou
247
+ // `oklch()` a marcação não lê: fica sem xadrez.
248
+ const transparente = (cor) => { const c = parseColor(cor); return c !== null && c.a < 1; };
249
+ export function ColorSwatch(props) {
250
+ if ("options" in props) {
251
+ const { options, name, label, value, defaultValue, onChange, layout = "grid", indicator, size = "md", disabled, id, className } = props;
252
+ // O `onChange` é o do CONSUMIDOR, repassado, como no `Checkbox` e no `Radio` desta casa: nenhum manipulador nosso
253
+ // é escrito aqui (a marcação continua de servidor). Vai por espalhamento, como lá.
254
+ return _jsx("div", { role: "radiogroup", "aria-label": label, id: id, "aria-disabled": disabled || undefined, className: cx("color-swatch-picker", size !== "md" && `color-swatch-picker-${size}`, layout === "stack" && "color-swatch-picker-stack", className), children: options.map((o) => {
255
+ const op = typeof o === "string" ? { color: o } : o;
256
+ const cor = parseColor(op.color), rgb = cor && hsbToRgb(cor);
257
+ return _jsxs("label", { className: cx("color-swatch-option", rgb && contrastingInk(rgb) === "dark" && "color-swatch-option-dark"), style: { ["--color-swatch-current"]: op.color }, children: [_jsx("input", { type: "radio", className: "color-swatch-input", name: name, value: op.color, "aria-label": op.label ?? op.color, checked: value === undefined ? undefined : value === op.color, defaultChecked: value === undefined ? defaultValue === op.color : undefined, disabled: disabled || op.disabled, ...(onChange ? { onChange } : {}) }), _jsx("span", { className: cx("color-swatch", cor && cor.a < 1 && "color-swatch-alpha"), "aria-hidden": "true" }), indicator != null && _jsx("span", { className: "color-swatch-indicator", "aria-hidden": "true", children: indicator })] }, op.color);
258
+ }) });
259
+ }
260
+ const { color, size = "md", label, className, style, ...resto } = props;
261
+ return _jsx("span", { role: "img", "aria-label": label ?? color, className: cx("color-swatch", size !== "md" && `color-swatch-${size}`, transparente(color) && "color-swatch-alpha", className), style: { ...style, ["--color-swatch-current"]: color }, ...resto });
262
+ }
263
+ export function ProgressCircle({ value, min = 0, max = 100, size = "md", tone = "brand", label, className, ...props }) {
264
+ const semTotal = value == null || Number.isNaN(value);
265
+ const pct = semTotal ? 25 : Math.max(0, Math.min(100, (value - min) / (max - min) * 100));
266
+ return _jsx("div", { role: "progressbar", "aria-label": label, "aria-valuemin": semTotal ? undefined : min, "aria-valuemax": semTotal ? undefined : max, "aria-valuenow": semTotal ? undefined : value, className: cx("progress-circle", size !== "md" && `progress-circle-${size}`, tone !== "brand" && `progress-circle-${tone}`, semTotal && "progress-circle-indeterminate", className), ...props, children: _jsxs("svg", { viewBox: "0 0 36 36", "aria-hidden": "true", children: [_jsx("circle", { className: "progress-circle-track", cx: "18", cy: "18", r: "16" }), _jsx("circle", { className: "progress-circle-fill", cx: "18", cy: "18", r: "16", pathLength: 100, strokeDasharray: "100", strokeDashoffset: 100 - pct })] }) });
267
+ }
@@ -24,7 +24,8 @@ export declare function Breadcrumb({ items, label }: {
24
24
  }): React.JSX.Element;
25
25
  export type TabsOrientation = "horizontal" | "vertical";
26
26
  export type TabsPanel = "card" | "plain";
27
- export declare function Tabs({ tabs, value, onChange, label, orientation, activateOnFocus, loopFocus, panel }: {
27
+ export type TabsVariant = "primary" | "secondary";
28
+ export declare function Tabs({ tabs, value, onChange, label, orientation, activateOnFocus, loopFocus, panel, variant }: {
28
29
  tabs: Array<{
29
30
  id: string;
30
31
  label: ReactNode;
@@ -37,12 +38,26 @@ export declare function Tabs({ tabs, value, onChange, label, orientation, activa
37
38
  activateOnFocus?: boolean;
38
39
  loopFocus?: boolean;
39
40
  panel?: TabsPanel;
41
+ variant?: TabsVariant;
40
42
  }): React.JSX.Element;
41
- export declare function Pagination({ page, total, onPageChange }: {
43
+ export type PaginationVariant = "numbers" | "simple" | "compact";
44
+ export interface PaginationProps {
42
45
  page: number;
43
46
  total: number;
44
- onPageChange: (p: number) => void;
45
- }): React.JSX.Element;
47
+ onPageChange?: (p: number) => void;
48
+ variant?: PaginationVariant;
49
+ /** O resumo à esquerda — "1 a 5 de 50 pedidos". */
50
+ summary?: ReactNode;
51
+ /** O endereço de cada página. Com ele, as páginas são links. */
52
+ href?: (page: number) => string;
53
+ /** Quantas páginas aparecem de cada lado da atual. Padrão: 1. */
54
+ siblings?: number;
55
+ /** O nome do marco de navegação. Padrão: a frase `paginationLabel`. Com duas paginações na mesma
56
+ * página, cada uma diz de quê ("Páginas dos pedidos"), senão o leitor de tela ouve dois marcos iguais. */
57
+ label?: string;
58
+ className?: string;
59
+ }
60
+ export declare function Pagination({ page, total, onPageChange, variant, summary, href, siblings, label, className }: PaginationProps): React.JSX.Element;
46
61
  export interface TocItem {
47
62
  id: string;
48
63
  label: string;
@@ -80,12 +95,16 @@ export interface SidebarItem {
80
95
  /** O link do roteador do app (M-01): `<Link href="/relatorios" />`. Recebe a pele, o estado de página atual e o conteúdo do item. */
81
96
  render?: ReactElement;
82
97
  }
83
- export type SidebarVariant = "floating" | "flush";
84
- export declare function Sidebar({ items, current, collapsed, variant, label, children, className, onClick, ...props }: HTMLAttributes<HTMLElement> & RefAttributes<HTMLElement> & {
98
+ export type SidebarVariant = "floating" | "flush" | "plain";
99
+ export type SidebarScrollbar = "hover" | "visible";
100
+ export type SidebarPlacement = "left" | "right";
101
+ export declare function Sidebar({ items, current, collapsed, variant, placement, scrollbar, label, children, className, onClick, ...props }: HTMLAttributes<HTMLElement> & RefAttributes<HTMLElement> & {
85
102
  items?: SidebarItem[];
86
103
  current?: string;
87
104
  collapsed?: boolean;
88
105
  variant?: SidebarVariant;
106
+ placement?: SidebarPlacement;
107
+ scrollbar?: SidebarScrollbar;
89
108
  label?: string;
90
109
  }): React.JSX.Element;
91
110
  export type BottomNavVariant = "floating" | "edge";
@@ -135,13 +154,18 @@ export interface CommandItem {
135
154
  icon?: IconName;
136
155
  kbd?: string;
137
156
  run: () => void;
157
+ /** 0.32.0 (DOC-06): o grupo do item ("Componentes", "Páginas"). Itens com o mesmo grupo saem juntos,
158
+ * sob o título dele, na ordem em que o grupo aparece primeiro. */
159
+ group?: string;
138
160
  }
139
- export declare function CommandPalette({ open, onClose, items, placeholder, label }: {
161
+ export declare function CommandPalette({ open, onClose, items, placeholder, label, shortcuts }: {
140
162
  open: boolean;
141
163
  onClose: () => void;
142
164
  items: CommandItem[];
143
165
  placeholder?: string;
144
166
  label?: string;
167
+ /** 0.32.0 (DOC-07): a linha que ensina as teclas, embaixo — ↑ ↓ mover, Enter abrir, Esc fechar. */
168
+ shortcuts?: boolean;
145
169
  }): React.JSX.Element | null;
146
170
  export declare function CommandPaletteShell({ open, query, onQueryChange, children }: {
147
171
  open: boolean;
@@ -8,7 +8,7 @@ import { Tabs as BaseTabs } from "@base-ui/react/tabs";
8
8
  import { useDirection } from "@base-ui/react/direction-provider";
9
9
  import { Autocomplete as BaseAutocomplete } from "@base-ui/react/autocomplete";
10
10
  import { useValorResponsivo } from "./responsivo-runtime.js";
11
- import { cx, fundirRender, useAureaStrings, usePortalContainer } from "./internal.js";
11
+ import { cx, fundirRender, useAureaStrings } from "./internal.js";
12
12
  import { Kbd } from "./markup.js";
13
13
  import { Icon } from "./system.js";
14
14
  import { Button } from "./actions.js";
@@ -29,8 +29,37 @@ export function Stepper({ items, label, className }) {
29
29
  }) });
30
30
  }
31
31
  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: "caret-right", size: "sm" }), " ", i.render ? fundirRender(i.render, { children: i.label }, "a") : i.href ? _jsx("a", { href: i.href, children: i.label }) : _jsx("strong", { "aria-current": "page", children: i.label })] }, n)) }); }
32
- export function Tabs({ tabs, value, onChange, label, orientation, activateOnFocus = true, loopFocus, panel = "card" }) { 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: panel === "plain" ? "tabs-panel" : "card card-inset", tabIndex: 0, children: t.content }, t.id))] }); }
33
- 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 })] }); }
32
+ export function Tabs({ tabs, value, onChange, label, orientation, activateOnFocus = true, loopFocus, panel = "card", variant = "primary" }) { 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: cx("tabs", variant === "secondary" && "tabs-secondary"), "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: panel === "plain" ? "tabs-panel" : "card card-inset", tabIndex: 0, children: t.content }, t.id))] }); }
33
+ function paginasVisiveis(page, total, siblings) {
34
+ if (total <= 5 + siblings * 2)
35
+ return Array.from({ length: total }, (_, i) => i + 1);
36
+ const ini = Math.max(2, page - siblings), fim = Math.min(total - 1, page + siblings);
37
+ const meio = Array.from({ length: fim - ini + 1 }, (_, i) => ini + i);
38
+ return [1, ...(ini > 2 ? ["..."] : []), ...meio, ...(fim < total - 1 ? ["..."] : []), total];
39
+ }
40
+ export function Pagination({ page, total, onPageChange, variant = "numbers", summary, href, siblings = 1, label, className }) {
41
+ const s = useAureaStrings();
42
+ if (variant === "compact")
43
+ return _jsxs("nav", { className: cx("pagination", className), "aria-label": 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 })] });
44
+ // Um item: link (com `href`) ou botão. Desligado (a primeira página no "anterior"), sem endereço.
45
+ const item = (alvo, conteudo, { atual = false, nav, rotulo } = {}) => {
46
+ const fora = alvo < 1 || alvo > total;
47
+ const cls = cx("pagination-link", nav && "pagination-link-nav", atual && "active");
48
+ const comuns = { className: cls, "aria-label": rotulo, "aria-current": atual ? "page" : undefined };
49
+ if (href)
50
+ return fora ? _jsx("a", { ...comuns, "aria-disabled": "true", children: conteudo })
51
+ : _jsx("a", { ...comuns, href: href(alvo), onClick: onPageChange ? (e) => { if (!e.metaKey && !e.ctrlKey && !e.shiftKey && e.button === 0) {
52
+ e.preventDefault();
53
+ onPageChange(alvo);
54
+ } } : undefined, children: conteudo });
55
+ return _jsx("button", { type: "button", ...comuns, disabled: fora, onClick: () => onPageChange?.(alvo), children: conteudo });
56
+ };
57
+ const anterior = item(page - 1, _jsxs(_Fragment, { children: [_jsx(Icon, { name: "caret-left", className: "pagination-nav-icon" }), _jsx("span", { children: s.previous })] }), { nav: "prev" });
58
+ const proxima = item(page + 1, _jsxs(_Fragment, { children: [_jsx("span", { children: s.next }), _jsx(Icon, { name: "caret-right", className: "pagination-nav-icon" })] }), { nav: "next" });
59
+ return _jsxs("nav", { className: cx("pagination", className), "aria-label": label ?? s.paginationLabel, children: [summary != null && _jsx("span", { className: "pagination-summary", children: summary }), _jsxs("ul", { className: "pagination-content", children: [_jsx("li", { children: anterior }), variant === "numbers" && paginasVisiveis(page, total, siblings).map((p, i) => p === "..."
60
+ ? _jsx("li", { children: _jsx("span", { className: "pagination-ellipsis", "aria-hidden": "true", children: "\u2026" }) }, `e${i}`)
61
+ : _jsx("li", { children: item(p, p, { atual: p === page, rotulo: `${s.paginationPage} ${p}` }) }, p)), _jsx("li", { children: proxima })] })] });
62
+ }
34
63
  export function TableOfContents({ items, current, label, className, ...props }) {
35
64
  const s = useAureaStrings();
36
65
  const meu = React.useRef(null);
@@ -43,7 +72,7 @@ export function TableOfContents({ items, current, label, className, ...props })
43
72
  return _jsxs("nav", { ref: n => { meu.current = n; if (typeof refDele === "function")
44
73
  refDele(n);
45
74
  else if (refDele)
46
- 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))] });
75
+ refDele.current = n; }, className: cx("toc", className), "aria-label": label ?? s.tocLabel, ...resto, children: [_jsxs("p", { className: "toc-label", children: [_jsx("span", { className: "toc-label-icon", "aria-hidden": "true" }), 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))] });
47
76
  }
48
77
  /** Qual dos `items` está na faixa de leitura, observando o documento. Devolve `undefined` até o
49
78
  * primeiro cálculo — e em SSR, onde não há documento para observar.
@@ -235,10 +264,10 @@ function fecharGavetaAoEscolher(e) {
235
264
  }
236
265
  catch { /* motor sem popover: não há gaveta */ }
237
266
  }
238
- export function Sidebar({ items, current, collapsed, variant = "floating", label, children, className, onClick, ...props }) {
267
+ export function Sidebar({ items, current, collapsed, variant = "floating", placement = "left", scrollbar = "hover", label, children, className, onClick, ...props }) {
239
268
  const s = useAureaStrings();
240
269
  const baseId = React.useId();
241
- return _jsxs("aside", { className: cx("sidebar", variant !== "floating" && `sidebar-${variant}`, collapsed && "sidebar-collapsed", className), onClick: e => { onClick?.(e); fecharGavetaAoEscolher(e); }, ...props, children: [items && items.length > 0 && _jsx("nav", { className: "sidebar-nav", "aria-label": label ?? s.sidebarLabel, children: sidebarList(items, { baseId, current, collapsed }) }), children] });
270
+ return _jsxs("aside", { className: cx("sidebar", variant !== "floating" && `sidebar-${variant}`, collapsed && "sidebar-collapsed", placement === "right" && "sidebar-right", scrollbar === "visible" && "sidebar-scrollbar-visible", className), "data-placement": placement, onClick: e => { onClick?.(e); fecharGavetaAoEscolher(e); }, ...props, children: [items && items.length > 0 && _jsx("nav", { className: "sidebar-nav", "aria-label": label ?? s.sidebarLabel, children: sidebarList(items, { baseId, current, collapsed }) }), children] });
242
271
  }
243
272
  const LEGADO = {
244
273
  flat: ["edge", "none"], surface: ["floating", "circle"], pill: ["floating", "pill"], dock: ["floating", "circle"]
@@ -282,24 +311,39 @@ export function NavList({ items, className, ...props }) {
282
311
  : _jsx("button", { type: "button", className: "nav-list-row", "aria-disabled": it.disabled || undefined, onClick: it.disabled ? undefined : it.onClick, children: miolo }) }, it.id);
283
312
  }) });
284
313
  }
285
- export function CommandPalette({ open, onClose, items, placeholder, label }) {
314
+ export function CommandPalette({ open, onClose, items, placeholder, label, shortcuts = true }) {
286
315
  const s = useAureaStrings();
287
- const portal = usePortalContainer();
316
+ const idAtalhos = React.useId();
317
+ // GRUPOS (0.32.0, DOC-06). Em 13/08/2026 a Aurea ficou SEM grupo, e a medição decidiu: dar a cada
318
+ // `Group` a sua fatia de itens passava POR CIMA do filtro do motor (digitar "the" devolvia os três).
319
+ // O caminho certo é o da documentação do motor (1.6.0, a paleta de exemplo dele): `items` agrupados
320
+ // (`{value, items}`), e a lista recebe os grupos JÁ FILTRADOS. Sem `group` em nenhum item, a lista é
321
+ // a de sempre.
322
+ const grupos = React.useMemo(() => {
323
+ if (!items.some(i => i.group))
324
+ return null;
325
+ const mapa = new Map();
326
+ for (const i of items) {
327
+ const g = i.group ?? "";
328
+ if (!mapa.has(g))
329
+ mapa.set(g, []);
330
+ mapa.get(g).push(i);
331
+ }
332
+ return [...mapa].map(([value, itens]) => ({ value, items: itens }));
333
+ }, [items]);
288
334
  if (!open)
289
335
  return null;
290
- // SEM AGRUPAMENTO na v1, e a medição é que decidiu (13/08/2026). O `Autocomplete.Root` não
291
- // consome a estrutura agrupada como o `Combobox.Root` consome — e o caminho alternativo, dar a
292
- // cada `Group` a sua fatia de itens, PASSA POR CIMA do filtro do motor: digitar "the" devolvia
293
- // os três comandos. Entre agrupar e filtrar, filtrar é o ponto de uma paleta de comandos.
294
- // Fica registrado como limite, não como esquecimento: quando alguém precisar de grupo aqui, o
295
- // caminho é o `Combobox.Root`, e isso é troca de motor, não ajuste.
296
336
  const executa = (item) => { onClose(); item.run(); };
297
337
  const linha = (item) => _jsxs(BaseAutocomplete.Item, { value: item, className: "menu-item command-item", onClick: () => executa(item), children: [item.icon && _jsx(Icon, { name: item.icon }), _jsx("span", { className: "command-item-label", children: item.label }), item.kbd && _jsx(Kbd, { children: item.kbd })] }, item.id);
298
338
  // Escape fecha, e é o teclado que a pessoa tenta primeiro. O motor não fecha sozinho porque
299
339
  // quem é dono do `open` é o consumidor — mesma regra do Dialog e do Drawer daqui.
300
340
  return _jsx("div", { className: "command-overlay", onKeyDown: e => { if (e.key === "Escape")
301
- onClose(); }, children: _jsx("div", { className: "command-palette", role: "dialog", "aria-label": label ?? s.commandLabel, children: _jsxs(BaseAutocomplete.Root, { items: items, itemToStringValue: (i) => i.label, mode: "list", open: true, children: [_jsx(BaseAutocomplete.Input, { autoFocus: true, className: "input", onKeyDown: e => { if (e.key === "Escape")
302
- onClose(); }, "aria-label": placeholder ?? s.commandPlaceholder, placeholder: placeholder ?? s.commandPlaceholder }), _jsx(BaseAutocomplete.Portal, { container: portal, children: _jsx(BaseAutocomplete.Positioner, { sideOffset: 6, className: "command-positioner", children: _jsxs(BaseAutocomplete.Popup, { className: "menu command-list", children: [_jsx(BaseAutocomplete.Empty, { className: "combobox-empty", children: s.comboboxEmpty }), _jsx(BaseAutocomplete.List, { children: _jsx(BaseAutocomplete.Collection, { children: linha }) })] }) }) })] }) }) });
341
+ onClose(); }, children: _jsxs("div", { className: "command-palette", role: "dialog", "aria-label": label ?? s.commandLabel, children: [_jsxs(BaseAutocomplete.Root, { items: (grupos ?? items), itemToStringValue: (i) => i.label, mode: "list", open: true, inline: true, autoHighlight: "always", keepHighlight: true, children: [_jsx(BaseAutocomplete.Input, { autoFocus: true, className: "input", onKeyDown: e => { if (e.key === "Escape") {
342
+ e.stopPropagation();
343
+ onClose();
344
+ } }, "aria-label": placeholder ?? s.commandPlaceholder, placeholder: placeholder ?? s.commandPlaceholder, "aria-expanded": true, "aria-describedby": shortcuts ? idAtalhos : undefined }), _jsxs("div", { className: "menu command-list", children: [_jsx(BaseAutocomplete.Empty, { className: "combobox-empty", children: s.comboboxEmpty }), _jsx(BaseAutocomplete.List, { children: grupos
345
+ ? (g) => _jsxs(BaseAutocomplete.Group, { items: g.items, className: "command-group", children: [g.value && _jsx(BaseAutocomplete.GroupLabel, { className: "command-group-label", children: g.value }), _jsx(BaseAutocomplete.Collection, { children: linha })] }, g.value)
346
+ : _jsx(BaseAutocomplete.Collection, { children: linha }) })] })] }), shortcuts && _jsxs("p", { className: "command-shortcuts", id: idAtalhos, children: [_jsxs("span", { children: [_jsx(Kbd, { children: "\u2191" }), _jsx(Kbd, { children: "\u2193" }), " ", s.commandHintMove] }), _jsxs("span", { children: [_jsx(Kbd, { children: "Enter" }), " ", s.commandHintSelect] }), _jsxs("span", { children: [_jsx(Kbd, { children: "Esc" }), " ", s.commandHintClose] })] })] }) });
303
347
  }
304
348
  export function CommandPaletteShell({ open, query, onQueryChange, children }) { const s = useAureaStrings(); if (!open)
305
349
  return null; return _jsx("div", { className: "command-overlay", children: _jsxs("div", { className: "command-palette", role: "dialog", "aria-label": s.commandLabel, children: [_jsx(SearchField, { autoFocus: true, "aria-label": s.commandPlaceholder, value: query, onChange: e => onQueryChange(e.target.value), placeholder: s.commandPlaceholder }), children] }) }); }
@@ -2,7 +2,9 @@ import React, { type ReactNode, type ReactElement } from "react";
2
2
  import { type Responsive } from "./pure.js";
3
3
  import { type IconName } from "./system.js";
4
4
  export type DialogSize = "xs" | "sm" | "md" | "lg" | "xl" | "full";
5
- export declare function Dialog({ open, title, children, footer, onClose, size, dismissible }: {
5
+ export type DialogPlacement = "auto" | "center" | "top" | "bottom";
6
+ export type DialogIconTone = "neutral" | "brand" | "info" | "success" | "warning" | "danger";
7
+ export declare function Dialog({ open, title, children, footer, onClose, size, dismissible, icon, iconTone, placement }: {
6
8
  open: boolean;
7
9
  title: ReactNode;
8
10
  children: ReactNode;
@@ -10,8 +12,11 @@ export declare function Dialog({ open, title, children, footer, onClose, size, d
10
12
  onClose: () => void;
11
13
  size?: DialogSize;
12
14
  dismissible?: boolean;
15
+ icon?: IconName;
16
+ iconTone?: DialogIconTone;
17
+ placement?: DialogPlacement;
13
18
  }): React.JSX.Element;
14
- export declare function ConfirmDialog({ open, title, description, confirmLabel, cancelLabel, destructive, onConfirm, onCancel }: {
19
+ export declare function ConfirmDialog({ open, title, description, confirmLabel, cancelLabel, destructive, onConfirm, onCancel, icon, iconTone, placement }: {
15
20
  open: boolean;
16
21
  title: ReactNode;
17
22
  description: ReactNode;
@@ -20,13 +25,18 @@ export declare function ConfirmDialog({ open, title, description, confirmLabel,
20
25
  destructive?: boolean;
21
26
  onConfirm: () => void;
22
27
  onCancel: () => void;
28
+ icon?: IconName;
29
+ iconTone?: DialogIconTone;
30
+ placement?: DialogPlacement;
23
31
  }): React.JSX.Element;
24
- export declare function Drawer({ open, title, children, onClose, side }: {
32
+ export type DrawerSide = "left" | "right" | "top" | "bottom";
33
+ export declare function Drawer({ open, title, children, footer, onClose, side }: {
25
34
  open: boolean;
26
35
  title: ReactNode;
27
36
  children: ReactNode;
37
+ footer?: ReactNode;
28
38
  onClose: () => void;
29
- side?: "left" | "right";
39
+ side?: DrawerSide;
30
40
  }): React.JSX.Element;
31
41
  export type AccessGateProps = {
32
42
  allowed: boolean;
@@ -42,16 +52,20 @@ export type AccessGateProps = {
42
52
  });
43
53
  export declare function AccessGate({ allowed, children, ...resto }: AccessGateProps): React.JSX.Element;
44
54
  export type OverlaySide = "top" | "right" | "bottom" | "left";
45
- export declare function Tooltip({ children, content, side }: {
55
+ export declare function Tooltip({ children, content, side, arrow, delay, closeDelay }: {
46
56
  children: ReactElement;
47
57
  content: ReactNode;
48
58
  side?: OverlaySide;
59
+ arrow?: boolean;
60
+ delay?: number;
61
+ closeDelay?: number;
49
62
  }): React.JSX.Element;
50
- export declare function Popover({ trigger, title, children, side }: {
63
+ export declare function Popover({ trigger, title, children, side, arrow }: {
51
64
  trigger: ReactElement;
52
65
  title?: ReactNode;
53
66
  children: ReactNode;
54
67
  side?: OverlaySide;
68
+ arrow?: boolean;
55
69
  }): React.JSX.Element;
56
70
  export declare function HoverCard({ trigger, children, side }: {
57
71
  trigger: ReactElement;