@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.
- package/LICENSE +202 -0
- package/README.md +53 -0
- package/dist/actions.d.ts +33 -0
- package/dist/actions.js +32 -0
- package/dist/code-editor.d.ts +18 -0
- package/dist/code-editor.js +111 -0
- package/dist/code.d.ts +13 -0
- package/dist/code.js +18 -0
- package/dist/communication.d.ts +31 -0
- package/dist/communication.js +27 -0
- package/dist/data-display.d.ts +23 -0
- package/dist/data-display.js +13 -0
- package/dist/data-grid.d.ts +14 -0
- package/dist/data-grid.js +76 -0
- package/dist/disclosure.d.ts +8 -0
- package/dist/disclosure.js +2 -0
- package/dist/feedback.d.ts +53 -0
- package/dist/feedback.js +71 -0
- package/dist/file-input.d.ts +25 -0
- package/dist/file-input.js +106 -0
- package/dist/identity.d.ts +8 -0
- package/dist/identity.js +3 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.js +30 -0
- package/dist/inputs.d.ts +65 -0
- package/dist/inputs.js +61 -0
- package/dist/internal.d.ts +72 -0
- package/dist/internal.js +31 -0
- package/dist/layout.d.ts +14 -0
- package/dist/layout.js +22 -0
- package/dist/media.d.ts +9 -0
- package/dist/media.js +98 -0
- package/dist/navigation.d.ts +59 -0
- package/dist/navigation.js +113 -0
- package/dist/overlays.d.ts +46 -0
- package/dist/overlays.js +36 -0
- package/dist/qrcode.d.ts +10 -0
- package/dist/qrcode.js +34 -0
- package/dist/system.d.ts +17 -0
- package/dist/system.js +25 -0
- package/package.json +144 -0
|
@@ -0,0 +1,76 @@
|
|
|
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 (ver ./qrcode): é o único componente que precisa do @tanstack/react-table,
|
|
6
|
+
// que é peer OPCIONAL. Table, DataList, KPI e Timeline ficam em /data-display sem essa conta.
|
|
7
|
+
import React from "react";
|
|
8
|
+
import { useReactTable, getCoreRowModel, getSortedRowModel, getFilteredRowModel, getPaginationRowModel, flexRender } from "@tanstack/react-table";
|
|
9
|
+
import { cx, useAureaStrings } from "./internal.js";
|
|
10
|
+
import { Icon } from "./system.js";
|
|
11
|
+
import { SearchField } from "./inputs.js";
|
|
12
|
+
import { Pagination } from "./navigation.js";
|
|
13
|
+
// DataGrid (Fase 4): TanStack Table v8 (8.21.3) headless + pele Aurea — sort,
|
|
14
|
+
// filtro global, paginação e seleção por checkbox. v8 porque o v9 ainda é beta
|
|
15
|
+
// (07/2026; decisão no ROADMAP). A pele reusa .table-wrap/th/td; o estado é
|
|
16
|
+
// interno (não controlado — controlar de fora só quando houver demanda real).
|
|
17
|
+
// A11y: aria-sort fica SÓ no th ordenado (padrão APG) e o cabeçalho ordenável é
|
|
18
|
+
// um <button> de verdade; a linha selecionada usa data-selected apenas para
|
|
19
|
+
// estilo — aria-selected é inválido em role=table, o estado acessível é o
|
|
20
|
+
// próprio checkbox. A paginação reusa o componente Pagination.
|
|
21
|
+
function GridCheck({ label, indeterminate, ...props }) {
|
|
22
|
+
return _jsxs("label", { className: "checkbox", children: [_jsx("input", { type: "checkbox", ref: el => { if (el)
|
|
23
|
+
el.indeterminate = !!indeterminate; }, ...props }), _jsx("span", { className: "control-mark" }), _jsx("span", { className: "sr-only", children: label })] });
|
|
24
|
+
}
|
|
25
|
+
export function DataGrid({ data, columns, label, filterable, pageSize, selectable, onSelectionChange, getRowId, className }) {
|
|
26
|
+
const s = useAureaStrings();
|
|
27
|
+
const [sorting, setSorting] = React.useState([]);
|
|
28
|
+
const [globalFilter, setGlobalFilter] = React.useState("");
|
|
29
|
+
const [rowSelection, setRowSelection] = React.useState({});
|
|
30
|
+
// Sem getRowId a seleção do TanStack é por ÍNDICE: trocar/reordenar data faria o
|
|
31
|
+
// checkbox "seguir" a posição e marcar OUTRO registro (auditoria 18/07/2026,
|
|
32
|
+
// ALTO 3). Sem id estável a seleção não sobrevive à mudança de dados — limpa.
|
|
33
|
+
// Com getRowId ela persiste corretamente; prefira passá-lo quando selectable.
|
|
34
|
+
const prevData = React.useRef(data);
|
|
35
|
+
React.useEffect(() => {
|
|
36
|
+
if (prevData.current === data)
|
|
37
|
+
return;
|
|
38
|
+
prevData.current = data;
|
|
39
|
+
if (!getRowId && Object.keys(rowSelection).length) {
|
|
40
|
+
setRowSelection({});
|
|
41
|
+
onSelectionChange?.([]);
|
|
42
|
+
}
|
|
43
|
+
}, [data]);
|
|
44
|
+
const allColumns = React.useMemo(() => selectable ? [{
|
|
45
|
+
id: "select", enableSorting: false,
|
|
46
|
+
header: ({ table }) => _jsx(GridCheck, { label: s.dataGridSelectAll, checked: table.getIsAllRowsSelected(), indeterminate: table.getIsSomeRowsSelected(), onChange: table.getToggleAllRowsSelectedHandler() }),
|
|
47
|
+
cell: ({ row }) => _jsx(GridCheck, { label: s.dataGridSelectRow, checked: row.getIsSelected(), disabled: !row.getCanSelect(), onChange: row.getToggleSelectedHandler() }),
|
|
48
|
+
}, ...columns] : columns, [selectable, columns, s]);
|
|
49
|
+
const table = useReactTable({
|
|
50
|
+
data, columns: allColumns, getRowId,
|
|
51
|
+
state: { sorting, globalFilter, rowSelection },
|
|
52
|
+
onSortingChange: setSorting,
|
|
53
|
+
onGlobalFilterChange: setGlobalFilter,
|
|
54
|
+
// emite as linhas ORIGINAIS já aqui (não em effect): getPreFilteredRowModel
|
|
55
|
+
// ignora filtro/página, então a seleção sobrevive a ambos.
|
|
56
|
+
onRowSelectionChange: updater => {
|
|
57
|
+
const next = typeof updater === "function" ? updater(rowSelection) : updater;
|
|
58
|
+
setRowSelection(next);
|
|
59
|
+
onSelectionChange?.(table.getPreFilteredRowModel().flatRows.filter(r => next[r.id]).map(r => r.original));
|
|
60
|
+
},
|
|
61
|
+
getCoreRowModel: getCoreRowModel(),
|
|
62
|
+
getSortedRowModel: getSortedRowModel(),
|
|
63
|
+
getFilteredRowModel: getFilteredRowModel(),
|
|
64
|
+
enableRowSelection: !!selectable,
|
|
65
|
+
...(pageSize ? { getPaginationRowModel: getPaginationRowModel(), initialState: { pagination: { pageSize } } } : {}),
|
|
66
|
+
});
|
|
67
|
+
const rows = table.getRowModel().rows;
|
|
68
|
+
const sortIcon = (dir) => dir === "asc" ? "chevron--sort--up" : dir === "desc" ? "chevron--sort--down" : "chevron--sort";
|
|
69
|
+
return _jsxs("div", { className: cx("datagrid", className), children: [filterable && _jsx(SearchField, { value: globalFilter, onChange: e => setGlobalFilter(e.target.value), placeholder: s.dataGridFilter, "aria-label": s.dataGridFilter }), _jsx("div", { className: "table-wrap", role: "region", "aria-label": label ?? s.tableLabel, tabIndex: 0, children: _jsxs("table", { children: [_jsx("thead", { children: table.getHeaderGroups().map(hg => _jsx("tr", { children: hg.headers.map(h => {
|
|
70
|
+
const dir = h.column.getIsSorted();
|
|
71
|
+
return _jsx("th", { colSpan: h.colSpan, className: h.column.id === "select" ? "datagrid-selcol" : undefined, "aria-sort": dir === "asc" ? "ascending" : dir === "desc" ? "descending" : undefined, children: h.isPlaceholder ? null : h.column.getCanSort()
|
|
72
|
+
? _jsxs("button", { type: "button", className: "datagrid-sort", onClick: h.column.getToggleSortingHandler(), children: [flexRender(h.column.columnDef.header, h.getContext()), _jsx(Icon, { name: sortIcon(dir), size: "sm", className: "datagrid-sort-icon" })] })
|
|
73
|
+
: flexRender(h.column.columnDef.header, h.getContext()) }, h.id);
|
|
74
|
+
}) }, hg.id)) }), _jsx("tbody", { children: rows.length ? rows.map(row => _jsx("tr", { "data-selected": row.getIsSelected() || undefined, children: row.getVisibleCells().map(cell => _jsx("td", { className: cell.column.id === "select" ? "datagrid-selcol" : undefined, children: flexRender(cell.column.columnDef.cell, cell.getContext()) }, cell.id)) }, row.id))
|
|
75
|
+
: _jsx("tr", { children: _jsx("td", { colSpan: table.getVisibleLeafColumns().length, className: "datagrid-empty", children: s.dataGridEmpty }) }) })] }) }), pageSize != null && table.getPageCount() > 1 && _jsx(Pagination, { page: table.getState().pagination.pageIndex + 1, total: table.getPageCount(), onPageChange: p => table.setPageIndex(p - 1) })] });
|
|
76
|
+
}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
export function Accordion({ items }) { return _jsx("div", { className: "accordion", children: items.map(i => _jsxs("details", { children: [_jsx("summary", { children: i.title }), _jsx("div", { className: "accordion-content", children: i.content })] }, i.id)) }); }
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import React, { type HTMLAttributes, type RefAttributes, type ReactNode } from "react";
|
|
2
|
+
import { type IconName } from "./system.js";
|
|
3
|
+
import { type OverlaySide } from "./overlays.js";
|
|
4
|
+
export type BadgeVariant = "neutral" | "primary" | "info" | "success" | "warning" | "danger" | "running" | "paused" | "offline" | "review";
|
|
5
|
+
export declare function Badge({ variant, className, ...props }: HTMLAttributes<HTMLSpanElement> & RefAttributes<HTMLSpanElement> & {
|
|
6
|
+
variant?: BadgeVariant;
|
|
7
|
+
}): React.JSX.Element;
|
|
8
|
+
export type StatusVariant = "neutral" | "online" | "offline" | "busy" | "away" | "running" | "success" | "warning" | "danger" | "info";
|
|
9
|
+
export declare function Status({ variant, children, className, ...props }: HTMLAttributes<HTMLSpanElement> & RefAttributes<HTMLSpanElement> & {
|
|
10
|
+
variant?: StatusVariant;
|
|
11
|
+
}): React.JSX.Element;
|
|
12
|
+
export type AlertVariant = "info" | "success" | "warning" | "danger";
|
|
13
|
+
export declare function Alert({ variant, title, children, className, ...props }: HTMLAttributes<HTMLDivElement> & RefAttributes<HTMLDivElement> & {
|
|
14
|
+
variant?: AlertVariant;
|
|
15
|
+
title?: ReactNode;
|
|
16
|
+
}): React.JSX.Element;
|
|
17
|
+
export type BannerVariant = AlertVariant;
|
|
18
|
+
export declare function Banner({ variant, title, icon, onDismiss, children, className, ...props }: HTMLAttributes<HTMLDivElement> & RefAttributes<HTMLDivElement> & {
|
|
19
|
+
variant?: BannerVariant;
|
|
20
|
+
title?: ReactNode;
|
|
21
|
+
icon?: IconName;
|
|
22
|
+
onDismiss?: () => void;
|
|
23
|
+
}): React.JSX.Element;
|
|
24
|
+
export declare function Progress({ value, label }: {
|
|
25
|
+
value: number;
|
|
26
|
+
label?: string;
|
|
27
|
+
}): React.JSX.Element;
|
|
28
|
+
export declare function Skeleton({ className, ...props }: HTMLAttributes<HTMLDivElement> & RefAttributes<HTMLDivElement>): React.JSX.Element;
|
|
29
|
+
export declare function EmptyState({ icon, title, titleAs: TitleTag, description, action }: {
|
|
30
|
+
icon?: IconName;
|
|
31
|
+
title: ReactNode;
|
|
32
|
+
titleAs?: "h2" | "h3" | "h4" | "p";
|
|
33
|
+
description?: ReactNode;
|
|
34
|
+
action?: ReactNode;
|
|
35
|
+
}): React.JSX.Element;
|
|
36
|
+
export interface NotificationItem {
|
|
37
|
+
id: string;
|
|
38
|
+
title: ReactNode;
|
|
39
|
+
description?: ReactNode;
|
|
40
|
+
time?: ReactNode;
|
|
41
|
+
icon?: IconName;
|
|
42
|
+
read?: boolean;
|
|
43
|
+
group?: string;
|
|
44
|
+
onClick?: () => void;
|
|
45
|
+
}
|
|
46
|
+
export declare function NotificationCenter({ items, onItemClick, onMarkAllRead, label, icon, side }: {
|
|
47
|
+
items: NotificationItem[];
|
|
48
|
+
onItemClick?: (item: NotificationItem) => void;
|
|
49
|
+
onMarkAllRead?: () => void;
|
|
50
|
+
label?: string;
|
|
51
|
+
icon?: IconName;
|
|
52
|
+
side?: OverlaySide;
|
|
53
|
+
}): React.JSX.Element;
|
package/dist/feedback.js
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
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 { Popover as BasePopover } from "@base-ui/react/popover";
|
|
7
|
+
import { cx, useAureaStrings } from "./internal.js";
|
|
8
|
+
import { Icon } from "./system.js";
|
|
9
|
+
import { Button, IconButton } from "./actions.js";
|
|
10
|
+
export function Badge({ variant = "neutral", className, ...props }) { return _jsx("span", { className: cx("badge", variant !== "neutral" && `badge-${variant}`, className), ...props }); }
|
|
11
|
+
// Status (DIRECTION §3.6): condição OPERACIONAL — ponto + rótulo. Não é Badge: Badge é
|
|
12
|
+
// metadado curto num pill; Status diz em que estado a coisa está. Reusa o .status-dot que
|
|
13
|
+
// já existia solto. A variante colore só o PONTO (currentColor); o rótulo fica legível em
|
|
14
|
+
// --foreground. Cor não é o único sinal — quem diz o estado é o texto (WCAG 1.4.1); o
|
|
15
|
+
// ponto é decorativo e sai do leitor de tela. ponytail: rótulo é do consumidor (sem i18n
|
|
16
|
+
// nova) — a variante é só a cor.
|
|
17
|
+
export function Status({ variant = "neutral", children, className, ...props }) { return _jsxs("span", { className: cx("status", variant !== "neutral" && `status-${variant}`, className), ...props, children: [_jsx("i", { className: "status-dot", "aria-hidden": "true" }), _jsx("span", { className: "status-label", children: children })] }); }
|
|
18
|
+
export function Alert({ variant = "info", title, children, className, ...props }) { return _jsxs("div", { className: cx("alert", `alert-${variant}`, className), role: variant === "danger" ? "alert" : "status", ...props, children: [title && _jsx("strong", { children: title }), children] }); }
|
|
19
|
+
export function Banner({ variant = "info", title, icon, onDismiss, children, className, ...props }) { const s = useAureaStrings(); return _jsxs("div", { className: cx("banner", `banner-${variant}`, className), role: variant === "danger" ? "alert" : "status", ...props, children: [icon ? _jsx(Icon, { name: icon }) : _jsx("span", {}), _jsxs("div", { children: [title && _jsx("strong", { children: title }), children] }), onDismiss ? _jsx(IconButton, { variant: "ghost", size: "sm", icon: "close", label: s.close, onClick: onDismiss }) : _jsx("span", {})] }); }
|
|
20
|
+
export function Progress({ value, label }) { return _jsx("div", { children: _jsx("div", { className: "progress", role: "progressbar", "aria-label": label, "aria-valuemin": 0, "aria-valuemax": 100, "aria-valuenow": value, children: _jsx("span", { style: { width: `${Math.max(0, Math.min(100, value))}%` } }) }) }); }
|
|
21
|
+
export function Skeleton({ className, ...props }) { return _jsx("div", { className: cx("skeleton", className), "aria-hidden": "true", ...props }); }
|
|
22
|
+
// titleAs: o nível do título é do DOCUMENTO, não do componente. Fixo em h3, um empty state
|
|
23
|
+
// no alto de uma página vira h1→h3 e a hierarquia quebra (axe heading-order). Default h3
|
|
24
|
+
// para não mexer em quem já consome; quem sabe o contexto passa o nível certo.
|
|
25
|
+
export function EmptyState({ icon = "document--blank", title, titleAs: TitleTag = "h3", description, action }) { return _jsxs("div", { className: "empty-state", children: [_jsx(Icon, { name: icon, size: "xl" }), _jsx(TitleTag, { className: "empty-title", children: title }), description && _jsx("p", { className: "muted", children: description }), action] }); }
|
|
26
|
+
function groupNotifications(items) {
|
|
27
|
+
const out = [];
|
|
28
|
+
for (const it of items) {
|
|
29
|
+
const last = out[out.length - 1];
|
|
30
|
+
if (last && last.label === it.group)
|
|
31
|
+
last.items.push(it);
|
|
32
|
+
else
|
|
33
|
+
out.push({ label: it.group, items: [it] });
|
|
34
|
+
}
|
|
35
|
+
return out;
|
|
36
|
+
}
|
|
37
|
+
export function NotificationCenter({ items, onItemClick, onMarkAllRead, label, icon = "notification", side = "bottom" }) {
|
|
38
|
+
const s = useAureaStrings();
|
|
39
|
+
const title = label ?? s.notificationsLabel;
|
|
40
|
+
const unread = items.filter(i => !i.read).length;
|
|
41
|
+
const groups = groupNotifications(items);
|
|
42
|
+
const baseId = React.useId();
|
|
43
|
+
const seen = React.useRef(undefined);
|
|
44
|
+
const [announce, setAnnounce] = React.useState("");
|
|
45
|
+
React.useEffect(() => {
|
|
46
|
+
const ids = new Set(items.map(i => i.id));
|
|
47
|
+
if (seen.current === undefined) {
|
|
48
|
+
seen.current = ids;
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
const fresh = items.filter(i => !seen.current.has(i.id)).length;
|
|
52
|
+
seen.current = ids;
|
|
53
|
+
// Texto idêntico duas vezes seguidas não muta o DOM e o leitor silencia a 2ª
|
|
54
|
+
// chegada (auditoria 18/07/2026, MÉDIO 4). Um NBSP alternado no fim força a
|
|
55
|
+
// mutação sem mudar o que se ouve.
|
|
56
|
+
if (fresh)
|
|
57
|
+
setAnnounce(prev => { const text = `${fresh} ${s.notificationNew}`; return prev === text ? text + " " : text; });
|
|
58
|
+
}, [items, s.notificationNew]);
|
|
59
|
+
const renderRow = (it) => {
|
|
60
|
+
const body = _jsxs(_Fragment, { children: [_jsx("span", { className: "notification-dot", "aria-hidden": "true" }), _jsxs("span", { className: "notification-item-title", children: [it.icon && _jsx(Icon, { name: it.icon, size: "sm" }), !it.read && _jsxs("span", { className: "sr-only", children: [s.notificationUnread, " "] }), it.title] }), it.time && _jsx("span", { className: "notification-time", children: it.time }), it.description && _jsx("span", { className: "notification-item-desc", children: it.description })] });
|
|
61
|
+
return it.onClick || onItemClick
|
|
62
|
+
? _jsx("button", { type: "button", className: "notification-item", "data-read": it.read || undefined, onClick: () => { it.onClick?.(); onItemClick?.(it); }, children: body })
|
|
63
|
+
: _jsx("div", { className: "notification-item", "data-read": it.read || undefined, children: body });
|
|
64
|
+
};
|
|
65
|
+
return _jsxs(BasePopover.Root, { children: [_jsxs("span", { className: "notification-trigger", children: [_jsx(BasePopover.Trigger, { render: _jsx(IconButton, { variant: "ghost", icon: icon, label: unread ? `${title} (${unread})` : title }) }), unread > 0 && _jsx("span", { className: "notification-count", "aria-hidden": "true", children: unread > 99 ? "99+" : unread })] }), _jsx(BasePopover.Portal, { children: _jsx(BasePopover.Positioner, { side: side, sideOffset: 8, children: _jsxs(BasePopover.Popup, { className: "popover notification-panel", "aria-label": title, children: [_jsxs("div", { className: "notification-head", children: [_jsx(BasePopover.Title, { render: _jsx("strong", {}), children: title }), unread > 0 && onMarkAllRead && _jsx(Button, { variant: "ghost", size: "sm", onClick: onMarkAllRead, children: s.notificationMarkAll })] }), items.length
|
|
66
|
+
? _jsx("div", { className: "notification-list", children: groups.map((g, gi) => {
|
|
67
|
+
const gid = baseId + gi;
|
|
68
|
+
return _jsxs(React.Fragment, { children: [g.label && _jsx("p", { className: "notification-group-label", id: gid, children: g.label }), _jsx("ul", { className: "notification-sublist", "aria-labelledby": g.label ? gid : undefined, children: g.items.map(it => _jsx("li", { children: renderRow(it) }, it.id)) })] }, gi);
|
|
69
|
+
}) })
|
|
70
|
+
: _jsx("p", { className: "notification-empty", children: s.notificationEmpty })] }) }) }), _jsx("span", { className: "sr-only", role: "status", "aria-live": "polite", children: announce })] });
|
|
71
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import React, { type ReactNode } from "react";
|
|
2
|
+
export interface FileRejection {
|
|
3
|
+
file: File;
|
|
4
|
+
reason: string;
|
|
5
|
+
}
|
|
6
|
+
export interface UploadContext {
|
|
7
|
+
signal: AbortSignal;
|
|
8
|
+
onProgress: (fraction: number) => void;
|
|
9
|
+
}
|
|
10
|
+
export type UploadFn = (file: File, ctx: UploadContext) => Promise<void>;
|
|
11
|
+
export declare function matchesAccept(file: {
|
|
12
|
+
name: string;
|
|
13
|
+
type: string;
|
|
14
|
+
}, accept?: string): boolean;
|
|
15
|
+
export declare function FileInput({ accept, maxSize, multiple, onFilesChange, upload, label, hint, id, className }: {
|
|
16
|
+
accept?: string;
|
|
17
|
+
maxSize?: number;
|
|
18
|
+
multiple?: boolean;
|
|
19
|
+
onFilesChange?: (files: File[]) => void;
|
|
20
|
+
upload?: UploadFn;
|
|
21
|
+
label?: ReactNode;
|
|
22
|
+
hint?: ReactNode;
|
|
23
|
+
id?: string;
|
|
24
|
+
className?: string;
|
|
25
|
+
}): React.JSX.Element;
|
|
@@ -0,0 +1,106 @@
|
|
|
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 { cx, useAureaStrings } from "./internal.js";
|
|
7
|
+
import { Icon } from "./system.js";
|
|
8
|
+
import { IconButton } from "./actions.js";
|
|
9
|
+
// matchesAccept espelha o algoritmo do atributo accept do HTML: extensão (.json),
|
|
10
|
+
// grupo de tipo (image/*) ou MIME exato (application/json). accept vazio = tudo.
|
|
11
|
+
export function matchesAccept(file, accept) {
|
|
12
|
+
const tokens = (accept ?? "").split(",").map(t => t.trim().toLowerCase()).filter(Boolean);
|
|
13
|
+
if (!tokens.length)
|
|
14
|
+
return true;
|
|
15
|
+
const name = file.name.toLowerCase(), type = file.type.toLowerCase();
|
|
16
|
+
return tokens.some(t => t.startsWith(".") ? name.endsWith(t) : t.endsWith("/*") ? type.startsWith(t.slice(0, -1)) : type === t);
|
|
17
|
+
}
|
|
18
|
+
function formatSize(bytes) {
|
|
19
|
+
if (bytes < 1024)
|
|
20
|
+
return `${bytes} B`;
|
|
21
|
+
const units = ["KB", "MB", "GB"];
|
|
22
|
+
let n = bytes / 1024, i = 0;
|
|
23
|
+
while (n >= 1024 && i < units.length - 1) {
|
|
24
|
+
n /= 1024;
|
|
25
|
+
i++;
|
|
26
|
+
}
|
|
27
|
+
return `${n < 10 ? n.toFixed(1) : Math.round(n)} ${units[i]}`;
|
|
28
|
+
}
|
|
29
|
+
export function FileInput({ accept, maxSize, multiple, onFilesChange, upload, label, hint, id, className }) {
|
|
30
|
+
const s = useAureaStrings();
|
|
31
|
+
const autoId = React.useId();
|
|
32
|
+
const inputId = id ?? autoId;
|
|
33
|
+
const nextId = React.useRef(0);
|
|
34
|
+
const [files, setFiles] = React.useState([]);
|
|
35
|
+
const [rejected, setRejected] = React.useState([]);
|
|
36
|
+
const [announce, setAnnounce] = React.useState("");
|
|
37
|
+
const [dragging, setDragging] = React.useState(false);
|
|
38
|
+
const controllers = React.useRef(new Map());
|
|
39
|
+
// Desmontar aborta tudo em voo — sem isso requisições e closures sobrevivem à
|
|
40
|
+
// tela (auditoria 18/07/2026, MÉDIO 5). Concorrência/agenda é do consumidor: o
|
|
41
|
+
// transporte é dele (UploadFn); o componente dispara um upload() por arquivo aceito.
|
|
42
|
+
React.useEffect(() => { const map = controllers.current; return () => map.forEach(c => c.abort()); }, []);
|
|
43
|
+
const emit = (next) => { setFiles(next); onFilesChange?.(next.map(f => f.file)); };
|
|
44
|
+
const patch = (fid, p) => setFiles(prev => prev.map(e => e.id === fid ? { ...e, ...p } : e));
|
|
45
|
+
const abortId = (fid) => controllers.current.get(fid)?.abort();
|
|
46
|
+
// Cada arquivo envia por conta própria (promessa independente + catch por arquivo):
|
|
47
|
+
// erro ou cancelamento de um não derruba a fila. Cancelar = abort() no controller;
|
|
48
|
+
// no catch, signal.aborted distingue "cancelado" de "falhou" seja qual for o erro
|
|
49
|
+
// que o consumidor lançou. patch é setState funcional → seguro sob closures velhas.
|
|
50
|
+
const startUpload = (fid, file) => {
|
|
51
|
+
if (!upload)
|
|
52
|
+
return;
|
|
53
|
+
const c = new AbortController();
|
|
54
|
+
controllers.current.set(fid, c);
|
|
55
|
+
patch(fid, { status: "uploading", progress: 0 });
|
|
56
|
+
(async () => {
|
|
57
|
+
try {
|
|
58
|
+
await upload(file, { signal: c.signal, onProgress: f => patch(fid, { progress: Math.max(0, Math.min(1, f)) }) });
|
|
59
|
+
patch(fid, { status: "done", progress: 1 });
|
|
60
|
+
setAnnounce(`${s.uploadComplete}: ${file.name}`);
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
const st = c.signal.aborted ? "canceled" : "error";
|
|
64
|
+
patch(fid, { status: st });
|
|
65
|
+
setAnnounce(`${st === "canceled" ? s.uploadCanceled : s.uploadError}: ${file.name}`);
|
|
66
|
+
}
|
|
67
|
+
finally {
|
|
68
|
+
controllers.current.delete(fid);
|
|
69
|
+
}
|
|
70
|
+
})();
|
|
71
|
+
};
|
|
72
|
+
const add = (list) => {
|
|
73
|
+
if (!list || !list.length)
|
|
74
|
+
return;
|
|
75
|
+
const ok = [], bad = [];
|
|
76
|
+
for (const file of Array.from(list)) {
|
|
77
|
+
const reason = !matchesAccept(file, accept) ? s.fileWrongType : (maxSize != null && file.size > maxSize) ? s.fileTooLarge : null;
|
|
78
|
+
if (reason)
|
|
79
|
+
bad.push({ file, reason });
|
|
80
|
+
else
|
|
81
|
+
ok.push({ id: nextId.current++, file });
|
|
82
|
+
}
|
|
83
|
+
// multiple acumula; single substitui (input nativo já entrega 1 arquivo).
|
|
84
|
+
// só emite se algo passou — drop 100% rejeitado não mexe na lista atual.
|
|
85
|
+
const added = multiple ? ok : ok.slice(-1);
|
|
86
|
+
if (added.length) {
|
|
87
|
+
if (!multiple)
|
|
88
|
+
files.forEach(f => abortId(f.id)); // single troca o arquivo: aborta o envio anterior
|
|
89
|
+
emit(multiple ? [...files, ...added] : added);
|
|
90
|
+
added.forEach(e => startUpload(e.id, e.file));
|
|
91
|
+
}
|
|
92
|
+
setRejected(bad);
|
|
93
|
+
setAnnounce([ok.length && `${s.fileAdded}: ${ok.map(f => f.file.name).join(", ")}`, ...bad.map(b => `${b.reason}: ${b.file.name}`)].filter(Boolean).join(". "));
|
|
94
|
+
};
|
|
95
|
+
const remove = (rid) => { const f = files.find(x => x.id === rid); abortId(rid); emit(files.filter(x => x.id !== rid)); setAnnounce(f ? `${s.fileRemoved}: ${f.file.name}` : ""); };
|
|
96
|
+
return _jsxs("div", { className: cx("field", className), children: [label && _jsx("label", { className: "label", htmlFor: inputId, children: label }), _jsxs("label", { className: "dropzone", "data-dragging": dragging || undefined, onDragOver: e => { e.preventDefault(); setDragging(true); }, onDragLeave: e => { if (!e.currentTarget.contains(e.relatedTarget))
|
|
97
|
+
setDragging(false); }, onDrop: e => { e.preventDefault(); setDragging(false); add(e.dataTransfer.files); }, children: [_jsx("input", { id: inputId, type: "file", className: "sr-only", accept: accept, multiple: multiple, onChange: e => { add(e.target.files); e.target.value = ""; } }), _jsx(Icon, { name: "cloud--upload", size: "xl", className: "dropzone-icon" }), _jsx("strong", { children: s.fileDropPrompt }), hint && _jsx("span", { className: "dropzone-hint", children: hint })] }), files.length > 0 && _jsx("ul", { className: "file-list", children: files.map(({ id, file, status, progress = 0 }) => {
|
|
98
|
+
// A barra reusa .progress + .file-progress (largura fixa em classe); só o
|
|
99
|
+
// preenchimento (width %) fica inline, por ser data-driven. Erro reusa
|
|
100
|
+
// .field-error. Como .file-progress não é usada nos docs, adicioná-la ao core
|
|
101
|
+
// não muda pixel de screenshot nenhum (auditoria 18/07, M14: CSS não usado
|
|
102
|
+
// pelos docs não altera baseline — a justificativa antiga estava invertida).
|
|
103
|
+
const pct = Math.round(progress * 100);
|
|
104
|
+
return _jsxs("li", { className: "file-item", children: [_jsx(Icon, { name: "document" }), _jsx("span", { className: "file-name", children: file.name }), status === "uploading" ? _jsxs(_Fragment, { children: [_jsx("div", { className: "progress file-progress", role: "progressbar", "aria-label": `${s.uploadSending} ${file.name}`, "aria-valuemin": 0, "aria-valuemax": 100, "aria-valuenow": pct, children: _jsx("span", { style: { width: `${pct}%` } }) }), _jsx(IconButton, { variant: "ghost", size: "sm", icon: "close", label: `${s.uploadCancel} ${file.name}`, onClick: () => abortId(id) })] }) : (status === "error" || status === "canceled") ? _jsxs(_Fragment, { children: [_jsx("span", { className: "field-error", children: status === "error" ? s.uploadError : s.uploadCanceled }), _jsx(IconButton, { variant: "ghost", size: "sm", icon: "restart", label: `${s.uploadRetry} ${file.name}`, onClick: () => startUpload(id, file) }), _jsx(IconButton, { variant: "ghost", size: "sm", icon: "close", label: `${s.fileRemove} ${file.name}`, onClick: () => remove(id) })] }) : _jsxs(_Fragment, { children: [status === "done" && _jsx(Icon, { name: "checkmark--filled" }), _jsx("span", { className: "file-size", children: formatSize(file.size) }), _jsx(IconButton, { variant: "ghost", size: "sm", icon: "close", label: `${s.fileRemove} ${file.name}`, onClick: () => remove(id) })] })] }, id);
|
|
105
|
+
}) }), rejected.map((r, n) => _jsxs("span", { className: "field-error", children: [r.reason, ": ", r.file.name] }, n)), _jsx("span", { className: "sr-only", role: "status", "aria-live": "polite", children: announce })] });
|
|
106
|
+
}
|
package/dist/identity.js
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { cx } from "./internal.js";
|
|
3
|
+
export function Avatar({ src, alt = "", fallback, size = 40, className }) { return _jsx("span", { className: cx("avatar", className), style: { width: size, height: size }, children: src ? _jsx("img", { src: src, alt: alt }) : fallback }); }
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export { cx, defaultStrings, ptBR, useAureaStrings, defaultSpriteUrl, useSpriteUrl, type AureaStrings } from "./internal.js";
|
|
2
|
+
export * from "./system.js";
|
|
3
|
+
export * from "./actions.js";
|
|
4
|
+
export * from "./feedback.js";
|
|
5
|
+
export * from "./inputs.js";
|
|
6
|
+
export * from "./navigation.js";
|
|
7
|
+
export * from "./layout.js";
|
|
8
|
+
export * from "./data-display.js";
|
|
9
|
+
export * from "./identity.js";
|
|
10
|
+
export * from "./disclosure.js";
|
|
11
|
+
export * from "./overlays.js";
|
|
12
|
+
export * from "./media.js";
|
|
13
|
+
export * from "./communication.js";
|
|
14
|
+
export * from "./code.js";
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
// @aurea-uds/react — a entrada principal.
|
|
2
|
+
//
|
|
3
|
+
// Fase 9 (achado A5). Antes: UM arquivo de 970 linhas, UM ponto de entrada, e oito dependências
|
|
4
|
+
// de runtime — das quais seis serviam o CodeEditor, uma o DataGrid e uma o QRCode. Quem
|
|
5
|
+
// instalava a biblioteca para usar um Button baixava um editor de código inteiro.
|
|
6
|
+
//
|
|
7
|
+
// Agora cada categoria do registry é um módulo, e cada módulo é um subpath. Este arquivo é a
|
|
8
|
+
// porta de sempre: `import {Button} from "@aurea-uds/react"` continua funcionando e traz tudo
|
|
9
|
+
// que não custa dependência.
|
|
10
|
+
//
|
|
11
|
+
// TRÊS COMPONENTES NÃO ENTRAM AQUI, e é o ponto da fase:
|
|
12
|
+
// CodeEditor → "@aurea-uds/react/code-editor" (CodeMirror, 6 peers opcionais)
|
|
13
|
+
// DataGrid → "@aurea-uds/react/data-grid" (@tanstack/react-table)
|
|
14
|
+
// QRCode → "@aurea-uds/react/qrcode" (qr)
|
|
15
|
+
// Se estivessem neste barril, importar um Button carregaria os três — e a dependência opcional
|
|
16
|
+
// que não estivesse instalada quebraria a importação. O peso é real, então a fronteira é real.
|
|
17
|
+
export { cx, defaultStrings, ptBR, useAureaStrings, defaultSpriteUrl, useSpriteUrl } from "./internal.js";
|
|
18
|
+
export * from "./system.js";
|
|
19
|
+
export * from "./actions.js";
|
|
20
|
+
export * from "./feedback.js";
|
|
21
|
+
export * from "./inputs.js";
|
|
22
|
+
export * from "./navigation.js";
|
|
23
|
+
export * from "./layout.js";
|
|
24
|
+
export * from "./data-display.js";
|
|
25
|
+
export * from "./identity.js";
|
|
26
|
+
export * from "./disclosure.js";
|
|
27
|
+
export * from "./overlays.js";
|
|
28
|
+
export * from "./media.js";
|
|
29
|
+
export * from "./communication.js";
|
|
30
|
+
export * from "./code.js";
|
package/dist/inputs.d.ts
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import React, { type HTMLAttributes, type InputHTMLAttributes, type SelectHTMLAttributes, type TextareaHTMLAttributes, type RefAttributes, type ReactNode } from "react";
|
|
2
|
+
import { type IconName } from "./system.js";
|
|
3
|
+
export { FileInput, matchesAccept, type FileRejection, type UploadContext, type UploadFn } from "./file-input.js";
|
|
4
|
+
export interface FieldProps extends HTMLAttributes<HTMLLabelElement>, RefAttributes<HTMLLabelElement> {
|
|
5
|
+
label: string;
|
|
6
|
+
hint?: ReactNode;
|
|
7
|
+
error?: ReactNode;
|
|
8
|
+
htmlFor?: string;
|
|
9
|
+
}
|
|
10
|
+
export declare function Field({ label, hint, error, children, className, id, ...props }: FieldProps): React.JSX.Element;
|
|
11
|
+
export declare const Input: React.ForwardRefExoticComponent<Omit<InputHTMLAttributes<HTMLInputElement> & RefAttributes<HTMLInputElement>, "ref"> & RefAttributes<HTMLInputElement>>;
|
|
12
|
+
export declare const Select: React.ForwardRefExoticComponent<Omit<SelectHTMLAttributes<HTMLSelectElement> & RefAttributes<HTMLSelectElement>, "ref"> & RefAttributes<HTMLSelectElement>>;
|
|
13
|
+
export declare const Textarea: React.ForwardRefExoticComponent<Omit<TextareaHTMLAttributes<HTMLTextAreaElement> & RefAttributes<HTMLTextAreaElement>, "ref"> & RefAttributes<HTMLTextAreaElement>>;
|
|
14
|
+
export declare function SearchField({ icon, className, ...props }: InputHTMLAttributes<HTMLInputElement> & RefAttributes<HTMLInputElement> & {
|
|
15
|
+
icon?: IconName;
|
|
16
|
+
}): React.JSX.Element;
|
|
17
|
+
export declare function Checkbox({ label, labelHidden, description, className, ...props }: InputHTMLAttributes<HTMLInputElement> & RefAttributes<HTMLInputElement> & {
|
|
18
|
+
label: ReactNode;
|
|
19
|
+
labelHidden?: boolean;
|
|
20
|
+
description?: ReactNode;
|
|
21
|
+
}): React.JSX.Element;
|
|
22
|
+
export declare function Radio({ label, className, ...props }: InputHTMLAttributes<HTMLInputElement> & RefAttributes<HTMLInputElement> & {
|
|
23
|
+
label: ReactNode;
|
|
24
|
+
}): React.JSX.Element;
|
|
25
|
+
export declare function Switch({ label, className, ...props }: InputHTMLAttributes<HTMLInputElement> & RefAttributes<HTMLInputElement> & {
|
|
26
|
+
label: ReactNode;
|
|
27
|
+
}): React.JSX.Element;
|
|
28
|
+
export declare function Range(props: InputHTMLAttributes<HTMLInputElement> & RefAttributes<HTMLInputElement>): React.JSX.Element;
|
|
29
|
+
export declare function SegmentedControl({ items, value, onChange, label }: {
|
|
30
|
+
items: Array<{
|
|
31
|
+
value: string;
|
|
32
|
+
label: ReactNode;
|
|
33
|
+
}>;
|
|
34
|
+
value: string;
|
|
35
|
+
onChange: (v: string) => void;
|
|
36
|
+
label?: string;
|
|
37
|
+
}): React.JSX.Element;
|
|
38
|
+
export interface ComboboxOption {
|
|
39
|
+
value: string;
|
|
40
|
+
label: string;
|
|
41
|
+
}
|
|
42
|
+
export declare function Combobox({ items, value, onValueChange, placeholder, label, id, className }: {
|
|
43
|
+
items: ComboboxOption[];
|
|
44
|
+
value?: ComboboxOption | null;
|
|
45
|
+
onValueChange?: (v: ComboboxOption | null) => void;
|
|
46
|
+
placeholder?: string;
|
|
47
|
+
label?: ReactNode;
|
|
48
|
+
id?: string;
|
|
49
|
+
className?: string;
|
|
50
|
+
}): React.JSX.Element;
|
|
51
|
+
export interface ComboboxOptGroup {
|
|
52
|
+
label: string;
|
|
53
|
+
items: ComboboxOption[];
|
|
54
|
+
}
|
|
55
|
+
export declare function MultiCombobox({ items, value, onValueChange, onInputChange, loading, placeholder, label, id, className }: {
|
|
56
|
+
items: ComboboxOption[] | ComboboxOptGroup[];
|
|
57
|
+
value?: ComboboxOption[];
|
|
58
|
+
onValueChange?: (v: ComboboxOption[]) => void;
|
|
59
|
+
onInputChange?: (query: string) => void;
|
|
60
|
+
loading?: boolean;
|
|
61
|
+
placeholder?: string;
|
|
62
|
+
label?: ReactNode;
|
|
63
|
+
id?: string;
|
|
64
|
+
className?: string;
|
|
65
|
+
}): React.JSX.Element;
|
package/dist/inputs.js
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
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, { forwardRef } from "react";
|
|
6
|
+
import { Combobox as BaseCombobox } from "@base-ui/react/combobox";
|
|
7
|
+
import { cx, useAureaStrings } from "./internal.js";
|
|
8
|
+
import { Icon } from "./system.js";
|
|
9
|
+
import { IconButton } from "./actions.js";
|
|
10
|
+
// FileInput mora em arquivo próprio (132 linhas — upload real, aborto, progresso) e é público
|
|
11
|
+
// por aqui: a categoria dele é Inputs.
|
|
12
|
+
export { FileInput, matchesAccept } from "./file-input.js";
|
|
13
|
+
// hint e error entram como DESCRIÇÃO (aria-describedby) e não como parte do nome. Medido em
|
|
14
|
+
// 30/07/2026: por estarem dentro do <label>, o nome acessível do campo virava "E-mail Usamos
|
|
15
|
+
// para entrar Endereço inválido" — tudo grudado — e nada dizia ao leitor de tela que o valor
|
|
16
|
+
// era inválido. Nome é o que o campo É; hint e erro são sobre o valor.
|
|
17
|
+
// O controle vem como children, então a ligação é feita clonando-o: é a única forma de pôr
|
|
18
|
+
// aria-describedby/aria-invalid no elemento certo sem obrigar o consumidor a repetir ids.
|
|
19
|
+
export function Field({ label, hint, error, children, className, id, ...props }) {
|
|
20
|
+
const auto = React.useId();
|
|
21
|
+
const base = id ?? auto;
|
|
22
|
+
const idHint = hint ? `${base}-hint` : undefined;
|
|
23
|
+
const idErro = error ? `${base}-error` : undefined;
|
|
24
|
+
const descrito = [idHint, idErro].filter(Boolean).join(" ") || undefined;
|
|
25
|
+
const controle = React.isValidElement(children)
|
|
26
|
+
? React.cloneElement(children, {
|
|
27
|
+
"aria-describedby": [children.props["aria-describedby"], descrito].filter(Boolean).join(" ") || undefined,
|
|
28
|
+
"aria-invalid": error ? true : children.props["aria-invalid"],
|
|
29
|
+
})
|
|
30
|
+
: children;
|
|
31
|
+
return _jsxs("label", { className: cx("field", className), id: id, ...props, children: [_jsxs("span", { className: "label", children: [label, hint && _jsx("span", { className: "hint", id: idHint, children: hint })] }), controle, error && _jsx("span", { className: "field-error", id: idErro, children: error })] });
|
|
32
|
+
}
|
|
33
|
+
export const Input = forwardRef(function Input({ className, ...props }, ref) { return _jsx("input", { ref: ref, className: cx("input", className), ...props }); });
|
|
34
|
+
export const Select = forwardRef(function Select({ className, ...props }, ref) { return _jsx("select", { ref: ref, className: cx("select", className), ...props }); });
|
|
35
|
+
export const Textarea = forwardRef(function Textarea({ className, ...props }, ref) { return _jsx("textarea", { ref: ref, className: cx("textarea", className), ...props }); });
|
|
36
|
+
export function SearchField({ icon = "search", className, ...props }) { return _jsxs("div", { className: cx("input-wrap", className), children: [_jsx(Icon, { name: icon }), _jsx(Input, { type: "search", ...props })] }); }
|
|
37
|
+
// labelHidden: coluna de seleção de tabela precisa do rótulo POR LINHA para o leitor de
|
|
38
|
+
// tela ("Select Analyst"), mas mostrá-lo engorda a coluna. O rótulo continua no DOM, só
|
|
39
|
+
// sai da tela (.sr-only) — nunca trocar por aria-label solto num <label> visível vazio.
|
|
40
|
+
export function Checkbox({ label, labelHidden, description, className, ...props }) { return _jsxs("label", { className: cx("checkbox", labelHidden && "checkbox-bare", className), children: [_jsx("input", { type: "checkbox", ...props }), _jsx("span", { className: "control-mark" }), _jsxs("span", { className: labelHidden ? "sr-only" : undefined, children: [_jsx("strong", { children: label }), description && _jsxs(_Fragment, { children: [_jsx("br", {}), _jsx("span", { className: "muted", children: description })] })] })] }); }
|
|
41
|
+
export function Radio({ label, className, ...props }) { return _jsxs("label", { className: cx("radio", className), children: [_jsx("input", { type: "radio", ...props }), _jsx("span", { className: "control-mark" }), label] }); }
|
|
42
|
+
export function Switch({ label, className, ...props }) { return _jsxs("label", { className: cx("switch", className), children: [_jsx("input", { type: "checkbox", role: "switch", ...props }), _jsx("span", { className: "switch-track" }), _jsx("span", { children: label })] }); }
|
|
43
|
+
export function Range(props) { return _jsx("input", { className: cx("range", props.className), type: "range", ...props }); }
|
|
44
|
+
export function SegmentedControl({ items, value, onChange, label }) { const s = useAureaStrings(); return _jsx("div", { className: "segmented", role: "group", "aria-label": label ?? s.optionsLabel, children: items.map(i => _jsx("button", { className: i.value === value ? "active" : undefined, "aria-pressed": i.value === value, onClick: () => onChange(i.value), children: i.label }, i.value)) }); }
|
|
45
|
+
export function Combobox({ items, value, onValueChange, placeholder, label, id, className }) {
|
|
46
|
+
const s = useAureaStrings();
|
|
47
|
+
const autoId = React.useId();
|
|
48
|
+
const inputId = id ?? autoId;
|
|
49
|
+
return _jsxs(BaseCombobox.Root, { items: items, value: value, onValueChange: onValueChange, itemToStringLabel: (i) => i.label, children: [_jsxs("div", { className: cx("field", className), children: [label && _jsx("label", { className: "label", htmlFor: inputId, children: label }), _jsxs(BaseCombobox.InputGroup, { className: "combobox-group", children: [_jsx(BaseCombobox.Input, { id: inputId, placeholder: placeholder, className: "input" }), _jsxs("span", { className: "combobox-actions", children: [_jsx(BaseCombobox.Clear, { render: _jsx(IconButton, { variant: "ghost", size: "sm", icon: "close", label: s.comboboxClear }) }), _jsx(BaseCombobox.Trigger, { render: _jsx(IconButton, { variant: "ghost", size: "sm", icon: "chevron--down", label: s.comboboxOpen }) })] })] })] }), _jsx(BaseCombobox.Portal, { children: _jsx(BaseCombobox.Positioner, { sideOffset: 6, children: _jsxs(BaseCombobox.Popup, { className: "menu combobox-popup", children: [_jsx(BaseCombobox.Empty, { className: "combobox-empty", children: s.comboboxEmpty }), _jsx(BaseCombobox.List, { children: (item) => _jsxs(BaseCombobox.Item, { value: item, className: "menu-item combobox-item", children: [_jsx(BaseCombobox.ItemIndicator, { className: "combobox-check", children: _jsx(Icon, { name: "checkmark", size: "sm" }) }), _jsx("span", { children: item.label })] }, item.value) })] }) }) })] });
|
|
50
|
+
}
|
|
51
|
+
const isGrouped = (items) => items.length > 0 && "items" in items[0];
|
|
52
|
+
export function MultiCombobox({ items, value, onValueChange, onInputChange, loading, placeholder, label, id, className }) {
|
|
53
|
+
const s = useAureaStrings();
|
|
54
|
+
const autoId = React.useId();
|
|
55
|
+
const inputId = id ?? autoId;
|
|
56
|
+
const renderItem = (item) => _jsxs(BaseCombobox.Item, { value: item, className: "menu-item combobox-item", children: [_jsx(BaseCombobox.ItemIndicator, { className: "combobox-check", children: _jsx(Icon, { name: "checkmark", size: "sm" }) }), _jsx("span", { children: item.label })] }, item.value);
|
|
57
|
+
return _jsxs(BaseCombobox.Root, { multiple: true, items: items, value: value, onValueChange: onValueChange, itemToStringLabel: (i) => i.label, filter: onInputChange ? null : undefined, onInputValueChange: onInputChange ? ((v, d) => { if (d.reason !== "item-press")
|
|
58
|
+
onInputChange(v); }) : undefined, children: [_jsxs("div", { className: cx("field", className), children: [label && _jsx("label", { className: "label", htmlFor: inputId, children: label }), _jsxs(BaseCombobox.InputGroup, { className: "combobox-multi", children: [_jsx(BaseCombobox.Chips, { className: "combobox-chips", children: _jsx(BaseCombobox.Value, { children: (selected) => _jsxs(_Fragment, { children: [selected.map((item) => _jsxs(BaseCombobox.Chip, { className: "combobox-chip", children: [item.label, _jsx(BaseCombobox.ChipRemove, { className: "combobox-chip-remove", "aria-label": `${s.comboboxRemove} ${item.label}`, children: _jsx(Icon, { name: "close", size: "sm" }) })] }, item.value)), _jsx(BaseCombobox.Input, { id: inputId, placeholder: selected.length ? undefined : placeholder, className: "combobox-chip-input" })] }) }) }), _jsxs("span", { className: "combobox-actions", children: [_jsx(BaseCombobox.Clear, { render: _jsx(IconButton, { variant: "ghost", size: "sm", icon: "close", label: s.comboboxClear }) }), _jsx(BaseCombobox.Trigger, { render: _jsx(IconButton, { variant: "ghost", size: "sm", icon: "chevron--down", label: s.comboboxOpen }) })] })] })] }), _jsx(BaseCombobox.Portal, { children: _jsx(BaseCombobox.Positioner, { sideOffset: 6, children: _jsxs(BaseCombobox.Popup, { className: "menu combobox-popup", children: [_jsx(BaseCombobox.Empty, { className: "combobox-empty", children: loading ? s.comboboxLoading : s.comboboxEmpty }), _jsx(BaseCombobox.List, { children: isGrouped(items)
|
|
59
|
+
? (group) => _jsxs(BaseCombobox.Group, { items: group.items, className: "combobox-section", children: [_jsx(BaseCombobox.GroupLabel, { className: "combobox-group-label", children: group.label }), _jsx(BaseCombobox.Collection, { children: renderItem })] }, group.label)
|
|
60
|
+
: renderItem })] }) }) })] });
|
|
61
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { type HTMLAttributes, type RefAttributes } from "react";
|
|
2
|
+
export declare const cx: (...v: Array<string | false | null | undefined>) => string;
|
|
3
|
+
export interface AureaStrings {
|
|
4
|
+
close: string;
|
|
5
|
+
paginationLabel: string;
|
|
6
|
+
previous: string;
|
|
7
|
+
next: string;
|
|
8
|
+
breadcrumbLabel: string;
|
|
9
|
+
tabsLabel: string;
|
|
10
|
+
optionsLabel: string;
|
|
11
|
+
tableLabel: string;
|
|
12
|
+
commandLabel: string;
|
|
13
|
+
commandPlaceholder: string;
|
|
14
|
+
dismissNotification: string;
|
|
15
|
+
toolbarLabel: string;
|
|
16
|
+
comboboxEmpty: string;
|
|
17
|
+
comboboxClear: string;
|
|
18
|
+
comboboxOpen: string;
|
|
19
|
+
comboboxRemove: string;
|
|
20
|
+
comboboxLoading: string;
|
|
21
|
+
fileDropPrompt: string;
|
|
22
|
+
fileAdded: string;
|
|
23
|
+
fileRemoved: string;
|
|
24
|
+
fileRemove: string;
|
|
25
|
+
fileTooLarge: string;
|
|
26
|
+
fileWrongType: string;
|
|
27
|
+
treeLabel: string;
|
|
28
|
+
notificationsLabel: string;
|
|
29
|
+
notificationMarkAll: string;
|
|
30
|
+
notificationEmpty: string;
|
|
31
|
+
notificationUnread: string;
|
|
32
|
+
notificationNew: string;
|
|
33
|
+
dataGridFilter: string;
|
|
34
|
+
dataGridEmpty: string;
|
|
35
|
+
dataGridSelectAll: string;
|
|
36
|
+
dataGridSelectRow: string;
|
|
37
|
+
mediaPlayer: string;
|
|
38
|
+
mediaPlay: string;
|
|
39
|
+
mediaPause: string;
|
|
40
|
+
mediaMute: string;
|
|
41
|
+
mediaUnmute: string;
|
|
42
|
+
mediaSeek: string;
|
|
43
|
+
mediaVolume: string;
|
|
44
|
+
mediaCaptionsShow: string;
|
|
45
|
+
mediaCaptionsHide: string;
|
|
46
|
+
mediaSkipBack: string;
|
|
47
|
+
mediaSkipForward: string;
|
|
48
|
+
mediaFullscreenEnter: string;
|
|
49
|
+
mediaFullscreenExit: string;
|
|
50
|
+
uploadSending: string;
|
|
51
|
+
uploadCancel: string;
|
|
52
|
+
uploadRetry: string;
|
|
53
|
+
uploadError: string;
|
|
54
|
+
uploadCanceled: string;
|
|
55
|
+
uploadComplete: string;
|
|
56
|
+
codeEditor: string;
|
|
57
|
+
chatLabel: string;
|
|
58
|
+
chatMessage: string;
|
|
59
|
+
chatSend: string;
|
|
60
|
+
qrCode: string;
|
|
61
|
+
copyCode: string;
|
|
62
|
+
tocLabel: string;
|
|
63
|
+
navigationToggle: string;
|
|
64
|
+
}
|
|
65
|
+
export declare const defaultStrings: AureaStrings;
|
|
66
|
+
export declare const ptBR: AureaStrings;
|
|
67
|
+
export declare const StringsContext: import("react").Context<AureaStrings>;
|
|
68
|
+
export declare const useAureaStrings: () => AureaStrings;
|
|
69
|
+
export declare const defaultSpriteUrl = "/aurea-icons.svg";
|
|
70
|
+
export declare const SpriteContext: import("react").Context<string>;
|
|
71
|
+
export declare const useSpriteUrl: () => string;
|
|
72
|
+
export declare function Kbd({ children, className, ...props }: HTMLAttributes<HTMLElement> & RefAttributes<HTMLElement>): import("react").JSX.Element;
|