@aurea-uds/react 0.1.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.
@@ -0,0 +1,31 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ // Fase 9 (achado A5): este arquivo saiu do index.tsx de 970 linhas. Um módulo por categoria
3
+ // do registry — a taxonomia já existia e é gateada. A ordem de import entre eles é um DAG:
4
+ // internal → system → actions → feedback → inputs → navigation → layout → data-display → resto.
5
+ // INTERNO: o que todo módulo precisa e ninguém publica como componente próprio.
6
+ // NÃO é subpath — `@aurea-uds/react/internal` não existe, de propósito.
7
+ // Kbd mora aqui por dependência, não por categoria: o Button precisa dele e ele não precisa de
8
+ // ninguém. A casa pública dele continua sendo `/data-display`, que o reexporta.
9
+ import { createContext, useContext } from "react";
10
+ export const cx = (...v) => v.filter(Boolean).join(" ");
11
+ export const defaultStrings = { close: "Close", paginationLabel: "Pagination", previous: "Previous", next: "Next", breadcrumbLabel: "Breadcrumb", tabsLabel: "Tabs", optionsLabel: "Options", tableLabel: "Table", commandLabel: "Command palette", commandPlaceholder: "Type a command…", dismissNotification: "Dismiss notification", toolbarLabel: "Toolbar", comboboxEmpty: "No results", comboboxClear: "Clear selection", comboboxOpen: "Open list", comboboxRemove: "Remove", comboboxLoading: "Loading…", fileDropPrompt: "Drag files here or click to select", fileAdded: "File added", fileRemoved: "File removed", fileRemove: "Remove", fileTooLarge: "File exceeds the size limit", fileWrongType: "File type not allowed", treeLabel: "Tree", notificationsLabel: "Notifications", notificationMarkAll: "Mark all as read", notificationEmpty: "No notifications", notificationUnread: "Unread", notificationNew: "new notifications", dataGridFilter: "Filter", dataGridEmpty: "No results", dataGridSelectAll: "Select all rows", dataGridSelectRow: "Select row", mediaPlayer: "Media player", mediaPlay: "Play", mediaPause: "Pause", mediaMute: "Mute", mediaUnmute: "Unmute", mediaSeek: "Seek", mediaVolume: "Volume", mediaCaptionsShow: "Show captions", mediaCaptionsHide: "Hide captions", mediaSkipBack: "Skip back 10 seconds", mediaSkipForward: "Skip forward 10 seconds", mediaFullscreenEnter: "Enter full screen", mediaFullscreenExit: "Exit full screen", uploadSending: "Uploading", uploadCancel: "Cancel upload", uploadRetry: "Retry upload", uploadError: "Upload failed", uploadCanceled: "Upload canceled", uploadComplete: "Upload complete", codeEditor: "Code editor", chatLabel: "Conversation", chatMessage: "Message", chatSend: "Send", qrCode: "QR code", copyCode: "Copy code", tocLabel: "On this page", navigationToggle: "Navigation" };
12
+ // pt-BR preservado como locale (mesmos valores que já eram o default): passe
13
+ // <AureaProvider strings={ptBR}> para restaurar português.
14
+ export const ptBR = { close: "Fechar", paginationLabel: "Paginação", previous: "Anterior", next: "Próxima", breadcrumbLabel: "Breadcrumb", tabsLabel: "Abas", optionsLabel: "Opções", tableLabel: "Tabela", commandLabel: "Paleta de comandos", commandPlaceholder: "Digite um comando…", dismissNotification: "Dispensar notificação", toolbarLabel: "Barra de ferramentas", comboboxEmpty: "Nenhum resultado", comboboxClear: "Limpar seleção", comboboxOpen: "Abrir lista", comboboxRemove: "Remover", comboboxLoading: "Carregando…", fileDropPrompt: "Arraste arquivos aqui ou clique para selecionar", fileAdded: "Arquivo adicionado", fileRemoved: "Arquivo removido", fileRemove: "Remover", fileTooLarge: "Arquivo maior que o limite", fileWrongType: "Tipo de arquivo não aceito", treeLabel: "Árvore", notificationsLabel: "Notificações", notificationMarkAll: "Marcar todas como lidas", notificationEmpty: "Nenhuma notificação", notificationUnread: "Não lida", notificationNew: "novas notificações", dataGridFilter: "Filtrar", dataGridEmpty: "Nenhum resultado", dataGridSelectAll: "Selecionar todas as linhas", dataGridSelectRow: "Selecionar linha", mediaPlayer: "Reprodutor de mídia", mediaPlay: "Reproduzir", mediaPause: "Pausar", mediaMute: "Silenciar", mediaUnmute: "Ativar som", mediaSeek: "Posição da reprodução", mediaVolume: "Volume", mediaCaptionsShow: "Ativar legendas", mediaCaptionsHide: "Desativar legendas", mediaSkipBack: "Voltar 10 segundos", mediaSkipForward: "Avançar 10 segundos", mediaFullscreenEnter: "Tela cheia", mediaFullscreenExit: "Sair da tela cheia", uploadSending: "Enviando", uploadCancel: "Cancelar envio", uploadRetry: "Tentar envio novamente", uploadError: "Falha no envio", uploadCanceled: "Envio cancelado", uploadComplete: "Envio concluído", codeEditor: "Editor de código", chatLabel: "Conversa", chatMessage: "Mensagem", chatSend: "Enviar", qrCode: "Código QR", copyCode: "Copiar código", tocLabel: "Nesta página", navigationToggle: "Navegação" };
15
+ export const StringsContext = createContext(defaultStrings);
16
+ export const useAureaStrings = () => useContext(StringsContext);
17
+ // Sprite de ícones: CONFIGURAÇÃO DE APLICAÇÃO, então entra pelo provider, uma vez.
18
+ // Antes o default absoluto "/aurea-icons.svg" vivia no Icon e 15 componentes declaravam
19
+ // e repassavam `spriteUrl` à mão — 52 sítios de código. Consequência: aplicação servida
20
+ // em subcaminho perdia TODOS os ícones, e para consertar tinha de passar a prop em cada
21
+ // componente (auditoria 26/07/2026, achado A4 — causa raiz do A3 daquela auditoria, que
22
+ // foi corrigido nos fallbacks do catálogo e não aqui). O default segue o mesmo para quem
23
+ // não configura nada; `Icon` aceita a prop como override local.
24
+ export const defaultSpriteUrl = "/aurea-icons.svg";
25
+ export const SpriteContext = createContext(defaultSpriteUrl);
26
+ export const useSpriteUrl = () => useContext(SpriteContext);
27
+ // direction informa o Base UI (lado dos popovers, setas do teclado). O CSS
28
+ // espelha sozinho por propriedade lógica, mas depende do atributo dir no DOM:
29
+ // quem consome precisa pôr dir="rtl" no <html>. Este provider não mexe no DOM.
30
+ // Kbd — representação de tecla/atalho. <kbd> é o elemento HTML certo; a pele é nossa.
31
+ export function Kbd({ children, className, ...props }) { return _jsx("kbd", { className: cx("kbd", className), ...props, children: children }); }
@@ -0,0 +1,14 @@
1
+ import { type HTMLAttributes, type RefAttributes, type ReactNode } from "react";
2
+ import { type TopbarVariant } from "./navigation.js";
3
+ export declare function Card({ variant, className, ...props }: HTMLAttributes<HTMLDivElement> & RefAttributes<HTMLDivElement> & {
4
+ variant?: "base" | "raised" | "interactive" | "inset" | "selected" | "danger";
5
+ }): import("react").JSX.Element;
6
+ export declare function Stack({ className, ...props }: HTMLAttributes<HTMLDivElement> & RefAttributes<HTMLDivElement>): import("react").JSX.Element;
7
+ export declare function Cluster({ className, ...props }: HTMLAttributes<HTMLDivElement> & RefAttributes<HTMLDivElement>): import("react").JSX.Element;
8
+ export declare function Grid({ className, ...props }: HTMLAttributes<HTMLDivElement> & RefAttributes<HTMLDivElement>): import("react").JSX.Element;
9
+ export declare function AppShell({ brand, navigation, topbar, topbarVariant, children, className, ...props }: HTMLAttributes<HTMLDivElement> & RefAttributes<HTMLDivElement> & {
10
+ brand: ReactNode;
11
+ navigation: ReactNode;
12
+ topbar?: ReactNode;
13
+ topbarVariant?: Exclude<TopbarVariant, "pill">;
14
+ }): import("react").JSX.Element;
package/dist/layout.js ADDED
@@ -0,0 +1,22 @@
1
+ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { cx, useAureaStrings } from "./internal.js";
3
+ import { IconButton } from "./actions.js";
4
+ import { Sidebar, Topbar } from "./navigation.js";
5
+ export function Card({ variant = "base", className, ...props }) { return _jsx("div", { className: cx("card", variant !== "base" && `card-${variant}`, className), ...props }); }
6
+ export function Stack({ className, ...props }) { return _jsx("div", { className: cx("stack", className), ...props }); }
7
+ export function Cluster({ className, ...props }) { return _jsx("div", { className: cx("cluster", className), ...props }); }
8
+ export function Grid({ className, ...props }) { return _jsx("div", { className: cx("grid", className), ...props }); }
9
+ // API pública intacta: as props seguem brand/navigation/topbar. O que mudou é ONDE a
10
+ // marca sai — no topo, não na lateral. `topbarVariant` escolhe a pele do topo; o shell
11
+ // precisa saber porque a lateral se encaixa abaixo dele (o `flush` não tem folga em
12
+ // cima, então ela sobe um --space-4). `pill` é cabeçalho de site — não é pra shell.
13
+ // A lateral RECOLHE em tela estreita, e sem uma linha de JavaScript: o <aside> é um popover
14
+ // nativo e o botão é o invoker. O navegador entrega Escape, clique fora, `aria-expanded` no
15
+ // disparador e a volta do foco para ele ao fechar — Baseline desde 04/2025, verificado em
16
+ // 30/07/2026. Era o achado A8: em 375px o `h1` da página começava 4,6 telas abaixo, depois
17
+ // dos 65 itens da navegação. Não-modal de propósito, como o CommandPaletteShell: o foco não
18
+ // fica preso, e por isso não declaramos modalidade que não entregamos.
19
+ // Id fixo em vez de useId: um documento tem UM AppShell (ele é dono do <main>), então não há
20
+ // colisão possível — e o CSS e o gate precisam de um alvo estável.
21
+ const SHELL_NAV_ID = "aurea-shell-nav";
22
+ export function AppShell({ brand, navigation, topbar, topbarVariant = "floating", children, className, ...props }) { const s = useAureaStrings(); return _jsxs("div", { className: cx("app-shell", topbarVariant === "flush" && "app-shell-flush", className), ...props, children: [_jsx(Topbar, { variant: topbarVariant, brand: _jsxs(_Fragment, { children: [_jsx(IconButton, { className: "nav-toggle", icon: "menu", label: s.navigationToggle, popoverTarget: SHELL_NAV_ID }), brand] }), children: topbar }), _jsx(Sidebar, { id: SHELL_NAV_ID, popover: "auto", children: navigation }), _jsx("main", { className: "content", children: children })] }); }
@@ -0,0 +1,9 @@
1
+ import React, { type HTMLAttributes, type RefAttributes, type ReactNode } from "react";
2
+ export declare function MediaPlayerShell({ children, className, ...props }: HTMLAttributes<HTMLDivElement> & RefAttributes<HTMLDivElement>): React.JSX.Element;
3
+ export declare function spokenTime(sec: number): string;
4
+ export interface MediaPlayerProps extends Omit<React.VideoHTMLAttributes<HTMLVideoElement> & RefAttributes<HTMLVideoElement>, "title"> {
5
+ kind?: "video" | "audio";
6
+ title?: ReactNode;
7
+ subtitle?: ReactNode;
8
+ }
9
+ export declare function MediaPlayer({ kind, src, poster, title, subtitle, className, children, onClick, onPlay, onPause, onEnded, onTimeUpdate, onLoadedMetadata, onDurationChange, onProgress, onVolumeChange, ...rest }: MediaPlayerProps): React.JSX.Element;
package/dist/media.js ADDED
@@ -0,0 +1,98 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ // Fase 9 (achado A5): este arquivo saiu do index.tsx de 970 linhas. Um módulo por categoria
3
+ // do registry — a taxonomia já existia e é gateada. A ordem de import entre eles é um DAG:
4
+ // internal → system → actions → feedback → inputs → navigation → layout → data-display → resto.
5
+ import React from "react";
6
+ import { cx, useAureaStrings } from "./internal.js";
7
+ import { Icon } from "./system.js";
8
+ export function MediaPlayerShell({ children, className, ...props }) { return _jsx("div", { className: cx("media-player", className), ...props, children: children }); }
9
+ // MediaPlayer (Fase 5): headless sobre <video>/<audio> nativos — o MOTOR de mídia é
10
+ // o browser; o componente só liga estado (play/tempo/buffer/volume/legenda) às
11
+ // classes .media-* já existentes. Sem CSS estrutural novo: o vídeo preenche o
12
+ // .media-viewport por style inline no elemento (não por regra no stylesheet), então
13
+ // o core e os baselines dos docs não mudam.
14
+ // A11y (o APG não fecha player; prática corrente pesquisada 07/2026): os controles
15
+ // são <button> nativos com aria-label que troca de estado (Reproduzir/Pausar,
16
+ // Silenciar/Ativar som) e a barra é <input type="range"> nativo — um slider de
17
+ // verdade, com setas/Home/End vindos do browser — cujo aria-valuetext lê o tempo por
18
+ // extenso (o número cru "243" não se entende; "4 minutes and 3 seconds" sim). Nada de
19
+ // role="slider" à mão. SEM atalho global de teclado: cada controle é focável e opera
20
+ // pelo próprio elemento nativo, então play/pause/seek/volume por teclado saem sem
21
+ // interceptar — interceptar quebraria digitação e não é padrão APG. O tempo por
22
+ // extenso é helper embutido em inglês (não i18n — como o formatSize do FileInput);
23
+ // os rótulos de botão passam pela i18n como todo controle.
24
+ function clockTime(sec) { if (!Number.isFinite(sec) || sec < 0)
25
+ sec = 0; const m = Math.floor(sec / 60), s = Math.floor(sec % 60); return `${m}:${String(s).padStart(2, "0")}`; }
26
+ export function spokenTime(sec) { if (!Number.isFinite(sec) || sec < 0)
27
+ sec = 0; const m = Math.floor(sec / 60), s = Math.floor(sec % 60); const mp = m === 1 ? "minute" : "minutes", sp = s === 1 ? "second" : "seconds"; return m && s ? `${m} ${mp} and ${s} ${sp}` : m ? `${m} ${mp}` : `${s} ${sp}`; }
28
+ // Os handlers de mídia do consumidor são COMPOSTOS com os internos (não podem
29
+ // sobrescrevê-los: um onPlay externo silenciaria o estado playing e o botão
30
+ // ficaria em "Reproduzir" — auditoria 18/07/2026, MÉDIO 2).
31
+ export function MediaPlayer({ kind = "video", src, poster, title, subtitle, className, children, onClick, onPlay, onPause, onEnded, onTimeUpdate, onLoadedMetadata, onDurationChange, onProgress, onVolumeChange, ...rest }) {
32
+ const s = useAureaStrings();
33
+ const boxRef = React.useRef(null);
34
+ const mediaRef = React.useRef(null);
35
+ const [playing, setPlaying] = React.useState(false);
36
+ const [current, setCurrent] = React.useState(0);
37
+ const [duration, setDuration] = React.useState(0);
38
+ const [buffered, setBuffered] = React.useState(0);
39
+ const [volume, setVolume] = React.useState(1);
40
+ const [muted, setMuted] = React.useState(false);
41
+ const [hasCaptions, setHasCaptions] = React.useState(false);
42
+ const [captionsOn, setCaptionsOn] = React.useState(false);
43
+ const [fullscreen, setFullscreen] = React.useState(false);
44
+ // fullscreenchange vem do document (não do elemento); é o único estado que exige
45
+ // listener — o resto sincroniza pelos eventos de mídia do próprio <video>.
46
+ React.useEffect(() => { const on = () => setFullscreen(document.fullscreenElement === boxRef.current); document.addEventListener("fullscreenchange", on); return () => document.removeEventListener("fullscreenchange", on); }, []);
47
+ const m = () => mediaRef.current;
48
+ // ramifica pelo estado sincronizado (playing), não por el.paused: playing vem dos
49
+ // eventos play/pause e não fica atrás do elemento (nem preso, como no jsdom).
50
+ const togglePlay = () => { const el = m(); if (!el)
51
+ return; if (playing)
52
+ el.pause();
53
+ else {
54
+ const p = el.play();
55
+ if (p)
56
+ p.catch(() => { });
57
+ } };
58
+ const skip = (d) => { const el = m(); if (el)
59
+ el.currentTime = Math.max(0, Math.min(el.currentTime + d, el.duration || Infinity)); };
60
+ // seek/setVol/mute atualizam o estado na hora (input controlado responsivo) e o
61
+ // elemento; os eventos de mídia reconfirmam depois (idempotente) e cobrem mudanças
62
+ // externas (controles nativos, outra aba).
63
+ const seek = (v) => { const el = m(); if (el)
64
+ el.currentTime = v; setCurrent(v); };
65
+ const setVol = (v) => { const el = m(); if (el) {
66
+ el.volume = v;
67
+ if (v > 0)
68
+ el.muted = false;
69
+ } setVolume(v); if (v > 0)
70
+ setMuted(false); };
71
+ const toggleMute = () => { const el = m(); const next = el ? !el.muted : !muted; if (el)
72
+ el.muted = next; setMuted(next); };
73
+ const toggleCaptions = () => { const el = m(); if (!el || !el.textTracks.length)
74
+ return; const show = el.textTracks[0].mode !== "showing"; el.textTracks[0].mode = show ? "showing" : "hidden"; setCaptionsOn(show); };
75
+ const toggleFullscreen = () => { if (document.fullscreenElement)
76
+ document.exitFullscreen?.();
77
+ else
78
+ boxRef.current?.requestFullscreen?.(); };
79
+ const syncVol = () => { const el = m(); if (el) {
80
+ setVolume(el.volume);
81
+ setMuted(el.muted);
82
+ } };
83
+ const syncBuf = () => { const el = m(); if (el && el.buffered.length)
84
+ setBuffered(el.buffered.end(el.buffered.length - 1)); };
85
+ const onMeta = () => { const el = m(); if (el) {
86
+ setDuration(el.duration);
87
+ setHasCaptions(el.textTracks.length > 0);
88
+ syncVol();
89
+ } };
90
+ const playedPct = duration > 0 ? Math.min(100, (current / duration) * 100) : 0;
91
+ const bufferedPct = duration > 0 ? Math.min(100, (buffered / duration) * 100) : 0;
92
+ const volPct = muted ? 0 : Math.round(volume * 100);
93
+ const valueText = duration > 0 ? `${spokenTime(current)} of ${spokenTime(duration)}` : spokenTime(current);
94
+ const MediaTag = kind === "audio" ? "audio" : "video";
95
+ return _jsxs("div", { ref: boxRef, className: cx("media-player", className), role: "group", "aria-label": typeof title === "string" ? title : s.mediaPlayer, children: [_jsxs("div", { className: "media-viewport", children: [kind === "audio" && _jsxs("div", { className: "media-placeholder", children: [_jsx(Icon, { name: "volume--up" }), title && _jsx("strong", { children: title }), subtitle && _jsx("span", { children: subtitle })] }), _jsx(MediaTag, { ref: mediaRef, src: src, ...rest, ...(kind === "video" ? { poster, playsInline: true, className: "media-fill", onClick: (e) => { onClick?.(e); togglePlay(); } } : {}), onPlay: (e) => { setPlaying(true); onPlay?.(e); }, onPause: (e) => { setPlaying(false); onPause?.(e); }, onEnded: (e) => { setPlaying(false); onEnded?.(e); }, onTimeUpdate: (e) => { const el = m(); if (el)
96
+ setCurrent(el.currentTime); onTimeUpdate?.(e); }, onLoadedMetadata: (e) => { onMeta(); onLoadedMetadata?.(e); }, onDurationChange: (e) => { const el = m(); if (el)
97
+ setDuration(el.duration); onDurationChange?.(e); }, onProgress: (e) => { syncBuf(); onProgress?.(e); }, onVolumeChange: (e) => { syncVol(); onVolumeChange?.(e); }, children: children })] }), kind === "video" && title && _jsx("div", { className: "media-overlay-title", children: _jsxs("div", { children: [_jsx("strong", { children: title }), subtitle && _jsx("span", { children: subtitle })] }) }), _jsxs("div", { className: "media-controls", children: [_jsxs("div", { className: "media-seek", style: { "--media-played": `${playedPct}%` }, children: [_jsxs("div", { className: "media-seek-track", children: [_jsx("span", { className: "media-seek-buffered", style: { width: `${bufferedPct}%` } }), _jsx("span", { className: "media-seek-played" })] }), _jsx("input", { type: "range", min: 0, max: duration > 0 ? duration : 0, step: "any", value: Math.min(current, duration || 0), "aria-label": s.mediaSeek, "aria-valuetext": valueText, onChange: e => seek(Number(e.target.value)) })] }), _jsxs("div", { className: "media-control-row", children: [_jsxs("div", { className: "media-control-group", children: [_jsx("button", { className: "media-control", type: "button", "aria-label": playing ? s.mediaPause : s.mediaPlay, onClick: togglePlay, children: _jsx(Icon, { name: playing ? "pause" : "play" }) }), _jsx("button", { className: "media-control", type: "button", "aria-label": s.mediaSkipBack, onClick: () => skip(-10), children: _jsx(Icon, { name: "rewind--10" }) }), _jsx("button", { className: "media-control", type: "button", "aria-label": s.mediaSkipForward, onClick: () => skip(10), children: _jsx(Icon, { name: "forward--10" }) }), _jsxs("span", { className: "media-time", children: [clockTime(current), " / ", clockTime(duration)] })] }), _jsxs("div", { className: "media-control-group", children: [_jsx("button", { className: "media-control", type: "button", "aria-label": muted ? s.mediaUnmute : s.mediaMute, onClick: toggleMute, children: _jsx(Icon, { name: muted || volume === 0 ? "volume--mute" : "volume--up" }) }), _jsx("input", { className: "media-volume", type: "range", min: 0, max: 100, value: volPct, "aria-label": s.mediaVolume, "aria-valuetext": `${volPct}%`, onChange: e => setVol(Number(e.target.value) / 100) }), hasCaptions && _jsx("button", { className: cx("media-control", captionsOn && "active"), type: "button", "aria-label": captionsOn ? s.mediaCaptionsHide : s.mediaCaptionsShow, "aria-pressed": captionsOn, onClick: toggleCaptions, children: _jsx(Icon, { name: "closed-caption" }) }), kind === "video" && _jsx("button", { className: "media-control", type: "button", "aria-label": fullscreen ? s.mediaFullscreenExit : s.mediaFullscreenEnter, onClick: toggleFullscreen, children: _jsx(Icon, { name: fullscreen ? "minimize" : "maximize" }) })] })] })] })] });
98
+ }
@@ -0,0 +1,59 @@
1
+ import React, { type HTMLAttributes, type RefAttributes, type ReactNode, type ReactElement } from "react";
2
+ import { type IconName } from "./system.js";
3
+ export declare function Breadcrumb({ items, label }: {
4
+ items: Array<{
5
+ label: ReactNode;
6
+ href?: string;
7
+ }>;
8
+ label?: string;
9
+ }): React.JSX.Element;
10
+ export declare function Tabs({ tabs, value, onChange, label }: {
11
+ tabs: Array<{
12
+ id: string;
13
+ label: ReactNode;
14
+ content: ReactNode;
15
+ }>;
16
+ value: string;
17
+ onChange: (id: string) => void;
18
+ label?: string;
19
+ }): React.JSX.Element;
20
+ export declare function Pagination({ page, total, onPageChange }: {
21
+ page: number;
22
+ total: number;
23
+ onPageChange: (p: number) => void;
24
+ }): React.JSX.Element;
25
+ export interface TocItem {
26
+ id: string;
27
+ label: string;
28
+ sub?: boolean;
29
+ }
30
+ export declare function TableOfContents({ items, current, label, className, ...props }: HTMLAttributes<HTMLElement> & RefAttributes<HTMLElement> & {
31
+ items: TocItem[];
32
+ current?: string;
33
+ label?: string;
34
+ }): React.JSX.Element;
35
+ export interface TreeNode {
36
+ id: string;
37
+ label: ReactNode;
38
+ icon?: IconName;
39
+ children?: TreeNode[];
40
+ }
41
+ export declare function TreeView({ items, defaultExpandedIds, onSelect, label, className }: {
42
+ items: TreeNode[];
43
+ defaultExpandedIds?: string[];
44
+ onSelect?: (node: TreeNode) => void;
45
+ label?: string;
46
+ className?: string;
47
+ }): ReactElement<unknown, string | React.JSXElementConstructor<any>>;
48
+ export declare function Sidebar({ children, className, ...props }: HTMLAttributes<HTMLElement> & RefAttributes<HTMLElement>): React.JSX.Element;
49
+ export type TopbarVariant = "floating" | "flush" | "pill";
50
+ export declare function Topbar({ variant, brand, children, className, ...props }: HTMLAttributes<HTMLElement> & RefAttributes<HTMLElement> & {
51
+ variant?: TopbarVariant;
52
+ brand?: ReactNode;
53
+ }): React.JSX.Element;
54
+ export declare function CommandPaletteShell({ open, query, onQueryChange, children }: {
55
+ open: boolean;
56
+ query: string;
57
+ onQueryChange: (v: string) => void;
58
+ children?: ReactNode;
59
+ }): React.JSX.Element | null;
@@ -0,0 +1,113 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ // Fase 9 (achado A5): este arquivo saiu do index.tsx de 970 linhas. Um módulo por categoria
3
+ // do registry — a taxonomia já existia e é gateada. A ordem de import entre eles é um DAG:
4
+ // internal → system → actions → feedback → inputs → navigation → layout → data-display → resto.
5
+ import React from "react";
6
+ import { Tabs as BaseTabs } from "@base-ui/react/tabs";
7
+ import { cx, useAureaStrings } from "./internal.js";
8
+ import { Icon } from "./system.js";
9
+ import { Button } from "./actions.js";
10
+ import { Badge } from "./feedback.js";
11
+ import { SearchField } from "./inputs.js";
12
+ export function Breadcrumb({ items, label }) { const s = useAureaStrings(); return _jsx("nav", { className: "breadcrumb", "aria-label": label ?? s.breadcrumbLabel, children: items.map((i, n) => _jsxs(React.Fragment, { children: [n > 0 && _jsx(Icon, { name: "chevron--right", size: "sm" }), " ", i.href ? _jsx("a", { href: i.href, children: i.label }) : _jsx("strong", { "aria-current": "page", children: i.label })] }, n)) }); }
13
+ export function Tabs({ tabs, value, onChange, label }) { const s = useAureaStrings(); return _jsxs(BaseTabs.Root, { value: value, onValueChange: v => onChange(String(v)), children: [_jsx(BaseTabs.List, { className: "tabs", "aria-label": label ?? s.tabsLabel, activateOnFocus: true, children: tabs.map(t => _jsx(BaseTabs.Tab, { value: t.id, className: "tab", children: t.label }, t.id)) }), tabs.map(t => _jsx(BaseTabs.Panel, { value: t.id, className: "card card-inset", tabIndex: 0, children: t.content }, t.id))] }); }
14
+ 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 })] }); }
15
+ export function TableOfContents({ items, current, label, className, ...props }) {
16
+ const s = useAureaStrings();
17
+ return _jsxs("nav", { className: cx("toc", className), "aria-label": label ?? s.tocLabel, ...props, 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 === current ? { "aria-current": "true" } : {}), children: i.label }, i.id))] });
18
+ }
19
+ function flattenVisible(nodes, expanded, level = 1, parentId, acc = []) {
20
+ for (const node of nodes) {
21
+ acc.push({ node, level, parentId });
22
+ if (node.children?.length && expanded.has(node.id))
23
+ flattenVisible(node.children, expanded, level + 1, node.id, acc);
24
+ }
25
+ return acc;
26
+ }
27
+ export function TreeView({ items, defaultExpandedIds, onSelect, label, className }) {
28
+ const s = useAureaStrings();
29
+ const baseId = React.useId();
30
+ const [expanded, setExpanded] = React.useState(() => new Set(defaultExpandedIds));
31
+ const [selected, setSelected] = React.useState();
32
+ const [active, setActive] = React.useState(() => items[0]?.id);
33
+ const rootRef = React.useRef(null);
34
+ const visible = flattenVisible(items, expanded);
35
+ // Roving tab stop derivado: se o nó ativo saiu do conjunto visível (dados
36
+ // trocados, nó removido), o primeiro visível volta a ser tabulável — senão a
37
+ // árvore inteira fica tabIndex=-1 e some da ordem do Tab (auditoria, MÉDIO 1).
38
+ const effectiveActive = active !== undefined && visible.some(v => v.node.id === active) ? active : visible[0]?.node.id;
39
+ const focusId = (id) => { setActive(id); rootRef.current?.querySelector(`[data-tree-id="${CSS.escape(id)}"]`)?.focus(); };
40
+ const toggle = (id, open) => setExpanded(prev => { const n = new Set(prev); if (open)
41
+ n.add(id);
42
+ else
43
+ n.delete(id); return n; });
44
+ const select = (node) => { setSelected(node.id); onSelect?.(node); };
45
+ const onKeyDown = (e) => {
46
+ const idx = visible.findIndex(v => v.node.id === effectiveActive);
47
+ if (idx < 0)
48
+ return;
49
+ const cur = visible[idx], hasChildren = !!cur.node.children?.length, isOpen = expanded.has(cur.node.id);
50
+ const rtl = getComputedStyle(e.currentTarget).direction === "rtl";
51
+ const expandKey = rtl ? "ArrowLeft" : "ArrowRight", collapseKey = rtl ? "ArrowRight" : "ArrowLeft";
52
+ switch (e.key) {
53
+ case "ArrowDown":
54
+ e.preventDefault();
55
+ if (idx < visible.length - 1)
56
+ focusId(visible[idx + 1].node.id);
57
+ break;
58
+ case "ArrowUp":
59
+ e.preventDefault();
60
+ if (idx > 0)
61
+ focusId(visible[idx - 1].node.id);
62
+ break;
63
+ case expandKey:
64
+ e.preventDefault();
65
+ if (hasChildren && !isOpen)
66
+ toggle(cur.node.id, true);
67
+ else if (hasChildren && isOpen)
68
+ focusId(cur.node.children[0].id);
69
+ break;
70
+ case collapseKey:
71
+ e.preventDefault();
72
+ if (hasChildren && isOpen)
73
+ toggle(cur.node.id, false);
74
+ else if (cur.parentId)
75
+ focusId(cur.parentId);
76
+ break;
77
+ case "Home":
78
+ e.preventDefault();
79
+ focusId(visible[0].node.id);
80
+ break;
81
+ case "End":
82
+ e.preventDefault();
83
+ focusId(visible[visible.length - 1].node.id);
84
+ break;
85
+ case "Enter":
86
+ case " ":
87
+ e.preventDefault();
88
+ select(cur.node);
89
+ if (hasChildren)
90
+ toggle(cur.node.id, !isOpen);
91
+ break;
92
+ }
93
+ };
94
+ const renderNodes = (nodes, level) => (_jsx("ul", { ref: level === 1 ? rootRef : undefined, className: cx(level === 1 ? "tree" : "tree-group", level === 1 && className), role: level === 1 ? "tree" : "group", "aria-label": level === 1 ? (label ?? s.treeLabel) : undefined, onKeyDown: level === 1 ? onKeyDown : undefined, children: nodes.map(node => {
95
+ const hasChildren = !!node.children?.length, isOpen = expanded.has(node.id), isSelected = selected === node.id, labelId = baseId + node.id;
96
+ return _jsxs("li", { className: "tree-item", role: "treeitem", "data-tree-id": node.id, "aria-level": level, "aria-expanded": hasChildren ? isOpen : undefined, "aria-selected": isSelected, "aria-labelledby": labelId, tabIndex: node.id === effectiveActive ? 0 : -1, children: [_jsxs("span", { className: "tree-node", "data-selected": isSelected || undefined, style: { paddingInlineStart: `calc(var(--space-3) + ${level - 1} * var(--space-4))` }, onClick: () => { focusId(node.id); select(node); if (hasChildren)
97
+ toggle(node.id, !isOpen); }, children: [hasChildren ? _jsx(Icon, { name: "chevron--right", size: "sm", className: "tree-twist" }) : _jsx("span", { className: "tree-indent", "aria-hidden": "true" }), node.icon && _jsx(Icon, { name: node.icon, size: "sm" }), _jsx("span", { id: labelId, className: "tree-label", children: node.label })] }), hasChildren && isOpen && renderNodes(node.children, level + 1)] }, node.id);
98
+ }) }));
99
+ return renderNodes(items, 1);
100
+ }
101
+ // Sidebar/Topbar: eram <aside>/<header> soltos DENTRO do AppShell; agora são componentes
102
+ // nomeados que o AppShell COMPÕE (dogfooding, AUREA.md §2.4 — "se o catálogo mostra uma
103
+ // Sidebar, a sidebar dele É a Sidebar da Aurea"). O landmark vem do elemento nativo
104
+ // (<aside>=complementary, <header>=banner); o <nav> de navegação é do consumidor, passado
105
+ // como children — por isso não embutimos <nav> aqui (aninharia landmark).
106
+ export function Sidebar({ children, className, ...props }) { return _jsx("aside", { className: cx("sidebar", className), ...props, children: children }); }
107
+ export function Topbar({ variant = "floating", brand, children, className, ...props }) { return _jsxs("header", { className: cx("topbar", `topbar-${variant}`, className), ...props, children: [brand != null && _jsx("div", { className: "brand", children: brand }), children] }); }
108
+ // Shell NÃO-modal de propósito: é presentational (o consumidor controla open) e não
109
+ // tem focus trap/inert/Escape — declarar aria-modal sem entregar a modalidade faria
110
+ // leitores ocultarem o resto da página com o teclado ainda alcançando tudo
111
+ // (auditoria 18/07/2026, ALTO 2; APG dialog-modal). Modal de verdade = BaseDialog.
112
+ export function CommandPaletteShell({ open, query, onQueryChange, children }) { const s = useAureaStrings(); if (!open)
113
+ 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] }) }); }
@@ -0,0 +1,46 @@
1
+ import React, { type ReactNode, type ReactElement } from "react";
2
+ import { type IconName } from "./system.js";
3
+ export declare function Dialog({ open, title, children, footer, onClose }: {
4
+ open: boolean;
5
+ title: ReactNode;
6
+ children: ReactNode;
7
+ footer?: ReactNode;
8
+ onClose: () => void;
9
+ }): React.JSX.Element;
10
+ export declare function Drawer({ open, title, children, onClose, side }: {
11
+ open: boolean;
12
+ title: ReactNode;
13
+ children: ReactNode;
14
+ onClose: () => void;
15
+ side?: "left" | "right";
16
+ }): React.JSX.Element;
17
+ export type OverlaySide = "top" | "right" | "bottom" | "left";
18
+ export declare function Tooltip({ children, content, side }: {
19
+ children: ReactElement;
20
+ content: ReactNode;
21
+ side?: OverlaySide;
22
+ }): React.JSX.Element;
23
+ export declare function Popover({ trigger, title, children, side }: {
24
+ trigger: ReactElement;
25
+ title?: ReactNode;
26
+ children: ReactNode;
27
+ side?: OverlaySide;
28
+ }): React.JSX.Element;
29
+ export interface MenuItemDef {
30
+ label: ReactNode;
31
+ onClick?: () => void;
32
+ disabled?: boolean;
33
+ leadingIcon?: IconName;
34
+ }
35
+ export declare function DropdownMenu({ trigger, items, side, label }: {
36
+ trigger: ReactElement;
37
+ items: Array<MenuItemDef | "separator">;
38
+ side?: OverlaySide;
39
+ label?: string;
40
+ }): React.JSX.Element;
41
+ export declare function ContextMenu({ children, items, label, className }: {
42
+ children: ReactNode;
43
+ items: Array<MenuItemDef | "separator">;
44
+ label?: string;
45
+ className?: string;
46
+ }): React.JSX.Element;
@@ -0,0 +1,36 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ // Fase 9 (achado A5): este arquivo saiu do index.tsx de 970 linhas. Um módulo por categoria
3
+ // do registry — a taxonomia já existia e é gateada. A ordem de import entre eles é um DAG:
4
+ // internal → system → actions → feedback → inputs → navigation → layout → data-display → resto.
5
+ import React from "react";
6
+ import { Dialog as BaseDialog } from "@base-ui/react/dialog";
7
+ import { Popover as BasePopover } from "@base-ui/react/popover";
8
+ import { Tooltip as BaseTooltip } from "@base-ui/react/tooltip";
9
+ import { Menu as BaseMenu } from "@base-ui/react/menu";
10
+ import { ContextMenu as BaseContextMenu } from "@base-ui/react/context-menu";
11
+ import { cx, useAureaStrings } from "./internal.js";
12
+ import { Icon } from "./system.js";
13
+ export function Dialog({ open, title, children, footer, onClose }) { const s = useAureaStrings(); return _jsx(BaseDialog.Root, { open: open, onOpenChange: o => { if (!o)
14
+ onClose(); }, children: _jsxs(BaseDialog.Portal, { children: [_jsx(BaseDialog.Backdrop, { className: "dialog-backdrop" }), _jsxs(BaseDialog.Popup, { className: "dialog", children: [_jsxs("header", { children: [_jsx(BaseDialog.Title, { render: _jsx("h2", {}), children: title }), _jsx(BaseDialog.Close, { className: "btn btn-ghost btn-icon", "aria-label": s.close, children: _jsx(Icon, { name: "close" }) })] }), _jsx("div", { className: "dialog-body", children: children }), footer && _jsx("footer", { children: footer })] })] }) }); }
15
+ export function Drawer({ open, title, children, onClose, side = "right" }) { const s = useAureaStrings(); return _jsx(BaseDialog.Root, { open: open, onOpenChange: o => { if (!o)
16
+ onClose(); }, children: _jsxs(BaseDialog.Portal, { children: [_jsx(BaseDialog.Backdrop, { className: "drawer-backdrop" }), _jsxs(BaseDialog.Popup, { className: cx("drawer", `drawer-${side}`), children: [_jsxs("header", { children: [_jsx(BaseDialog.Title, { render: _jsx("h2", {}), children: title }), _jsx(BaseDialog.Close, { className: "btn btn-ghost btn-icon", "aria-label": s.close, children: _jsx(Icon, { name: "close" }) })] }), children] })] }) }); }
17
+ // `role="tooltip"` + `aria-describedby` no disparador é o que faz a dica EXISTIR para leitor
18
+ // de tela. Medido em 30/07/2026: sem isso, o popup saía sem role e sem id, e o disparador sem
19
+ // aria-describedby — quem navega por leitor de tela ouvia só o rótulo do botão e nunca o
20
+ // conteúdo da dica. Era tooltip visual, não acessível (padrão APG Tooltip).
21
+ // O id aponta para um elemento que só existe quando aberto; referência pendente é ignorada
22
+ // pela tecnologia assistiva, então não precisa acompanhar o estado.
23
+ export function Tooltip({ children, content, side = "top" }) { const id = React.useId(); return _jsxs(BaseTooltip.Root, { children: [_jsx(BaseTooltip.Trigger, { render: children, "aria-describedby": id }), _jsx(BaseTooltip.Portal, { children: _jsx(BaseTooltip.Positioner, { side: side, sideOffset: 8, children: _jsx(BaseTooltip.Popup, { id: id, role: "tooltip", className: "tooltip", children: content }) }) })] }); }
24
+ export function Popover({ trigger, title, children, side = "bottom" }) { return _jsxs(BasePopover.Root, { children: [_jsx(BasePopover.Trigger, { render: trigger }), _jsx(BasePopover.Portal, { children: _jsx(BasePopover.Positioner, { side: side, sideOffset: 8, children: _jsxs(BasePopover.Popup, { className: "popover", children: [title && _jsx(BasePopover.Title, { render: _jsx("strong", {}), children: title }), children] }) }) })] }); }
25
+ // ContextMenu.Item/.Separator/.Popup são os MESMOS componentes de Menu.* no Base UI,
26
+ // então os itens renderizam igual nos dois menus.
27
+ const renderMenuItems = (items) => items.map((it, i) => it === "separator"
28
+ ? _jsx(BaseMenu.Separator, { className: "menu-sep" }, i)
29
+ : _jsxs(BaseMenu.Item, { className: "menu-item", disabled: it.disabled, onClick: it.onClick, children: [it.leadingIcon && _jsx(Icon, { name: it.leadingIcon }), it.label] }, i));
30
+ export function DropdownMenu({ trigger, items, side = "bottom", label }) { return _jsxs(BaseMenu.Root, { children: [_jsx(BaseMenu.Trigger, { render: trigger }), _jsx(BaseMenu.Portal, { children: _jsx(BaseMenu.Positioner, { side: side, sideOffset: 6, children: _jsx(BaseMenu.Popup, { className: "menu", "aria-label": label, children: renderMenuItems(items) }) }) })] }); }
31
+ // ContextMenu: abre no botão direito e — por teclado — em Shift+F10 / tecla Menu,
32
+ // que o browser só dispara (como evento contextmenu) sobre um elemento FOCADO. Por
33
+ // isso o gatilho é focável (tabIndex 0) e tem nome acessível (auditoria 18/07/2026,
34
+ // MÉDIO 3); sem isso o teclado não alcança o menu. Consumidor pode sobrescrever
35
+ // tabIndex via children se o próprio já for focável.
36
+ export function ContextMenu({ children, items, label, className }) { return _jsxs(BaseContextMenu.Root, { children: [_jsx(BaseContextMenu.Trigger, { className: className, tabIndex: 0, "aria-label": label, "aria-haspopup": "menu", children: children }), _jsx(BaseContextMenu.Portal, { children: _jsx(BaseContextMenu.Positioner, { children: _jsx(BaseContextMenu.Popup, { className: "menu", "aria-label": label, children: renderMenuItems(items) }) }) })] }); }
@@ -0,0 +1,10 @@
1
+ import React from "react";
2
+ import { type ErrorCorrection } from "qr";
3
+ export interface QRCodeProps extends Omit<React.SVGAttributes<SVGSVGElement>, "children"> {
4
+ value: string;
5
+ size?: number;
6
+ ecc?: ErrorCorrection;
7
+ quietZone?: number;
8
+ label?: string;
9
+ }
10
+ export declare function QRCode({ value, size, ecc, quietZone, label, className, ...props }: QRCodeProps): React.JSX.Element;
package/dist/qrcode.js ADDED
@@ -0,0 +1,34 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ // Fase 9 (achado A5): este arquivo saiu do index.tsx de 970 linhas. Um módulo por categoria
3
+ // do registry — a taxonomia já existia e é gateada. A ordem de import entre eles é um DAG:
4
+ // internal → system → actions → feedback → inputs → navigation → layout → data-display → resto.
5
+ // SUBPATH PRÓPRIO, e não a categoria Data Display: este é o único componente que precisa do
6
+ // pacote `qr`, que é peer OPCIONAL. Se ele morasse junto do Table, quem importa Table teria de
7
+ // instalar um gerador de QR — que é exatamente o achado A5 visto de perto.
8
+ import React from "react";
9
+ import encodeQR from "qr";
10
+ import { cx, useAureaStrings } from "./internal.js";
11
+ // QRCode: dado escaneável com a geometria suave da Aurea — módulos REDONDOS (dots) e
12
+ // olhos circulares, não quadrados. Miolo de alto contraste FIXO (não segue o tema: dark
13
+ // inverteria e nem todo leitor decodifica um QR invertido); override por --qr-module/
14
+ // --qr-quiet no CSS. Matriz da lib `qr` (0-dep, Apache-2.0, Paul Miller); o desenho é
15
+ // nosso. ecc default "quartile": dots cobrem menos área que quadrados, então mais
16
+ // correção de erro preserva a escaneabilidade. ponytail: um estilo (dots) só.
17
+ export function QRCode({ value, size = 160, ecc = "quartile", quietZone = 4, label, className, ...props }) {
18
+ const s = useAureaStrings();
19
+ const name = label ?? `${s.qrCode}: ${value}`;
20
+ const m = encodeQR(value, "raw", { ecc, border: quietZone });
21
+ const n = m.length;
22
+ // os 3 finder patterns (7×7) ficam nos cantos da área de dados, logo após o quiet border.
23
+ const b = quietZone, eyes = [[b, b], [n - b - 7, b], [b, n - b - 7]];
24
+ const inEye = (x, y) => eyes.some(([ex, ey]) => x >= ex && x < ex + 7 && y >= ey && y < ey + 7);
25
+ const dots = [];
26
+ for (let y = 0; y < n; y++)
27
+ for (let x = 0; x < n; x++)
28
+ if (m[y][x] && !inEye(x, y))
29
+ dots.push(_jsx("circle", { className: "qr-mod", cx: x + 0.5, cy: y + 0.5, r: 0.44 }, y * n + x));
30
+ // olho redondo: anel externo (raio 3.5) → furo (2.5, cor do fundo) → centro (1.5).
31
+ // os raios preservam a proporção 7:5:3 do finder, então o leitor ainda o reconhece.
32
+ const eyeShapes = eyes.map(([ex, ey], i) => { const cx = ex + 3.5, cy = ey + 3.5; return _jsxs(React.Fragment, { children: [_jsx("circle", { className: "qr-mod", cx: cx, cy: cy, r: 3.5 }), _jsx("circle", { className: "qr-eye-gap", cx: cx, cy: cy, r: 2.5 }), _jsx("circle", { className: "qr-mod", cx: cx, cy: cy, r: 1.5 })] }, `eye${i}`); });
33
+ return _jsxs("svg", { className: cx("qrcode", className), role: "img", "aria-label": name, viewBox: `0 0 ${n} ${n}`, width: size, height: size, ...props, children: [_jsx("title", { children: name }), eyeShapes, dots] });
34
+ }
@@ -0,0 +1,17 @@
1
+ import React, { type ReactNode } from "react";
2
+ import { type AureaStrings } from "./internal.js";
3
+ export declare function AureaProvider({ children, strings, direction, spriteUrl }: {
4
+ children: ReactNode;
5
+ strings?: Partial<AureaStrings>;
6
+ direction?: "ltr" | "rtl";
7
+ spriteUrl?: string;
8
+ }): React.JSX.Element;
9
+ export type AureaToastType = "info" | "success" | "warning" | "danger";
10
+ export declare const useToast: () => import("@base-ui/react").UseToastManagerReturnValue<any>;
11
+ export type IconName = string;
12
+ export interface IconProps extends React.SVGAttributes<SVGSVGElement> {
13
+ name: IconName;
14
+ spriteUrl?: string;
15
+ size?: "sm" | "md" | "lg" | "xl";
16
+ }
17
+ export declare function Icon({ name, spriteUrl, size, className, ...props }: IconProps): React.JSX.Element;
package/dist/system.js ADDED
@@ -0,0 +1,25 @@
1
+ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ // Fase 9 (achado A5): este arquivo saiu do index.tsx de 970 linhas. Um módulo por categoria
3
+ // do registry — a taxonomia já existia e é gateada. A ordem de import entre eles é um DAG:
4
+ // internal → system → actions → feedback → inputs → navigation → layout → data-display → resto.
5
+ import React from "react";
6
+ import { Toast as BaseToast } from "@base-ui/react/toast";
7
+ import { Tooltip as BaseTooltip } from "@base-ui/react/tooltip";
8
+ import { DirectionProvider } from "@base-ui/react/direction-provider";
9
+ import { cx, StringsContext, SpriteContext, defaultStrings, defaultSpriteUrl, useSpriteUrl, useAureaStrings } from "./internal.js";
10
+ export function AureaProvider({ children, strings, direction = "ltr", spriteUrl = defaultSpriteUrl }) {
11
+ const value = React.useMemo(() => strings ? { ...defaultStrings, ...strings } : defaultStrings, [strings]);
12
+ return _jsx(StringsContext.Provider, { value: value, children: _jsx(SpriteContext.Provider, { value: spriteUrl, children: _jsx(DirectionProvider, { direction: direction, children: _jsx(BaseToast.Provider, { children: _jsxs(BaseTooltip.Provider, { children: [children, _jsx(AureaToastViewport, {})] }) }) }) }) });
13
+ }
14
+ export const useToast = () => BaseToast.useToastManager();
15
+ function AureaToastList() {
16
+ const { toasts } = BaseToast.useToastManager();
17
+ const s = useAureaStrings();
18
+ return _jsx(_Fragment, { children: toasts.map(t => (_jsxs(BaseToast.Root, { toast: t, className: cx("toast", t.type && `toast-${t.type}`), children: [_jsxs("div", { className: "toast-text", children: [_jsx(BaseToast.Title, { render: _jsx("strong", {}) }), t.description ? _jsx(BaseToast.Description, { className: "muted" }) : null] }), _jsx(BaseToast.Close, { className: "btn btn-ghost btn-icon btn-sm", "aria-label": s.dismissNotification, children: "\u00D7" })] }, t.id))) });
19
+ }
20
+ function AureaToastViewport() {
21
+ return _jsx(BaseToast.Portal, { children: _jsx(BaseToast.Viewport, { className: "toast-stack", children: _jsx(AureaToastList, {}) }) });
22
+ }
23
+ // spriteUrl aqui é OVERRIDE local (dois sprites na mesma página, por exemplo). O normal é
24
+ // não passar nada e deixar o AureaProvider dizer de onde vêm os glifos.
25
+ export function Icon({ name, spriteUrl, size = "md", className, ...props }) { const base = useSpriteUrl(); return _jsx("svg", { "aria-hidden": "true", className: cx("icon", size !== "md" && `icon-${size}`, className), ...props, children: _jsx("use", { href: `${spriteUrl ?? base}#i-${name}` }) }); }