@aurea-uds/react 0.1.0 → 0.3.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.
Files changed (64) hide show
  1. package/README.md +34 -2
  2. package/dist/actions.d.ts +20 -0
  3. package/dist/actions.js +59 -5
  4. package/dist/agents.d.ts +209 -0
  5. package/dist/agents.js +302 -0
  6. package/dist/calendar.d.ts +6 -0
  7. package/dist/calendar.js +27 -0
  8. package/dist/chart.d.ts +10 -0
  9. package/dist/chart.js +53 -0
  10. package/dist/code-client.d.ts +6 -0
  11. package/dist/code-client.js +24 -0
  12. package/dist/code-editor.js +1 -0
  13. package/dist/code.d.ts +2 -13
  14. package/dist/code.js +5 -18
  15. package/dist/communication.js +1 -0
  16. package/dist/data-display-client.d.ts +14 -0
  17. package/dist/data-display-client.js +22 -0
  18. package/dist/data-display.d.ts +2 -23
  19. package/dist/data-display.js +5 -13
  20. package/dist/data-grid.d.ts +52 -3
  21. package/dist/data-grid.js +205 -25
  22. package/dist/feedback-client.d.ts +64 -0
  23. package/dist/feedback-client.js +110 -0
  24. package/dist/feedback.d.ts +2 -53
  25. package/dist/feedback.js +5 -71
  26. package/dist/file-input.d.ts +21 -2
  27. package/dist/file-input.js +202 -21
  28. package/dist/graph.d.ts +33 -0
  29. package/dist/graph.js +178 -0
  30. package/dist/identity-client.d.ts +9 -0
  31. package/dist/identity-client.js +12 -0
  32. package/dist/identity.d.ts +2 -8
  33. package/dist/identity.js +5 -3
  34. package/dist/index.d.ts +4 -1
  35. package/dist/index.js +33 -3
  36. package/dist/inputs-client.d.ts +105 -0
  37. package/dist/inputs-client.js +264 -0
  38. package/dist/inputs.d.ts +2 -65
  39. package/dist/inputs.js +9 -61
  40. package/dist/internal.d.ts +23 -70
  41. package/dist/internal.js +115 -23
  42. package/dist/layout-client.d.ts +9 -0
  43. package/dist/layout-client.js +72 -0
  44. package/dist/layout.d.ts +2 -14
  45. package/dist/layout.js +5 -22
  46. package/dist/markup.d.ts +72 -0
  47. package/dist/markup.js +87 -0
  48. package/dist/media-client.d.ts +36 -0
  49. package/dist/media-client.js +239 -0
  50. package/dist/media.d.ts +2 -9
  51. package/dist/media.js +5 -98
  52. package/dist/navigation-client.d.ts +94 -0
  53. package/dist/navigation-client.js +154 -0
  54. package/dist/navigation.d.ts +2 -59
  55. package/dist/navigation.js +5 -113
  56. package/dist/overlays.d.ts +28 -0
  57. package/dist/overlays.js +66 -10
  58. package/dist/pure.d.ts +196 -0
  59. package/dist/pure.js +111 -0
  60. package/dist/qrcode.d.ts +2 -1
  61. package/dist/qrcode.js +3 -2
  62. package/dist/system.d.ts +11 -1
  63. package/dist/system.js +59 -4
  64. package/package.json +40 -4
@@ -1,7 +1,18 @@
1
1
  import React from "react";
2
- import { type ColumnDef } from "@tanstack/react-table";
3
- export type { ColumnDef } from "@tanstack/react-table";
4
- export declare function DataGrid<T>({ data, columns, label, filterable, pageSize, selectable, onSelectionChange, getRowId, className }: {
2
+ import { type ColumnDef, type SortingState, type RowSelectionState, type ColumnFiltersState, type VisibilityState, type ColumnSizingState } from "@tanstack/react-table";
3
+ import { type UniversalState } from "./internal.js";
4
+ import { type IconName } from "./system.js";
5
+ export type { ColumnDef, SortingState, RowSelectionState, ColumnFiltersState, VisibilityState, ColumnSizingState } from "@tanstack/react-table";
6
+ export interface GridFilterSpec {
7
+ column: string;
8
+ label: string;
9
+ facet?: boolean;
10
+ options?: Array<{
11
+ value: string;
12
+ label: string;
13
+ }>;
14
+ }
15
+ export declare function DataGrid<T>({ data, columns, label, filterable, pageSize, selectable, onSelectionChange, getRowId, className, sorting: sortingProp, onSortingChange, globalFilter: globalFilterProp, onGlobalFilterChange, page, onPageChange, rowSelection: rowSelectionProp, onRowSelectionChange, manualSorting, manualFiltering, manualPagination, rowCount, filters, columnFilters: columnFiltersProp, onColumnFiltersChange, bulkActions, stickyHeader, hideableColumns, columnVisibility: columnVisibilityProp, onColumnVisibilityChange, resizableColumns, columnSizing: columnSizingProp, onColumnSizingChange, state, stateMessage, renderDetail, detailRowId: detailRowIdProp, onDetailRowIdChange, onExport }: {
5
16
  data: T[];
6
17
  columns: Array<ColumnDef<T, any>>;
7
18
  label?: string;
@@ -11,4 +22,42 @@ export declare function DataGrid<T>({ data, columns, label, filterable, pageSize
11
22
  onSelectionChange?: (rows: T[]) => void;
12
23
  getRowId?: (row: T, index: number) => string;
13
24
  className?: string;
25
+ sorting?: SortingState;
26
+ onSortingChange?: (sorting: SortingState) => void;
27
+ globalFilter?: string;
28
+ onGlobalFilterChange?: (filter: string) => void;
29
+ page?: number;
30
+ onPageChange?: (page: number) => void;
31
+ rowSelection?: RowSelectionState;
32
+ onRowSelectionChange?: (selection: RowSelectionState) => void;
33
+ manualSorting?: boolean;
34
+ manualFiltering?: boolean;
35
+ manualPagination?: boolean;
36
+ rowCount?: number;
37
+ filters?: GridFilterSpec[];
38
+ columnFilters?: ColumnFiltersState;
39
+ onColumnFiltersChange?: (filters: ColumnFiltersState) => void;
40
+ bulkActions?: Array<{
41
+ id: string;
42
+ label: string;
43
+ icon?: IconName;
44
+ onAction: (rows: T[], clear: () => void) => void;
45
+ }>;
46
+ stickyHeader?: boolean;
47
+ hideableColumns?: boolean;
48
+ columnVisibility?: VisibilityState;
49
+ onColumnVisibilityChange?: (v: VisibilityState) => void;
50
+ resizableColumns?: boolean;
51
+ columnSizing?: ColumnSizingState;
52
+ onColumnSizingChange?: (s: ColumnSizingState) => void;
53
+ state?: "loading" | "error" | UniversalState;
54
+ stateMessage?: string;
55
+ renderDetail?: (row: T) => React.ReactNode;
56
+ detailRowId?: string | null;
57
+ onDetailRowIdChange?: (id: string | null) => void;
58
+ onExport?: (rows: T[], scope: {
59
+ count: number;
60
+ filtered: boolean;
61
+ selected: boolean;
62
+ }) => void;
14
63
  }): React.JSX.Element;
package/dist/data-grid.js CHANGED
@@ -1,3 +1,4 @@
1
+ "use client";
1
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
3
  // Fase 9 (achado A5): este arquivo saiu do index.tsx de 970 linhas. Um módulo por categoria
3
4
  // do registry — a taxonomia já existia e é gateada. A ordem de import entre eles é um DAG:
@@ -5,15 +6,35 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
5
6
  // SUBPATH PRÓPRIO (ver ./qrcode): é o único componente que precisa do @tanstack/react-table,
6
7
  // que é peer OPCIONAL. Table, DataList, KPI e Timeline ficam em /data-display sem essa conta.
7
8
  import React from "react";
8
- import { useReactTable, getCoreRowModel, getSortedRowModel, getFilteredRowModel, getPaginationRowModel, flexRender } from "@tanstack/react-table";
9
- import { cx, useAureaStrings } from "./internal.js";
9
+ import { useReactTable, getCoreRowModel, getSortedRowModel, getFilteredRowModel, getPaginationRowModel, getFacetedRowModel, getFacetedUniqueValues, flexRender } from "@tanstack/react-table";
10
+ import { cx, useAureaStrings, stateSeverity } from "./internal.js";
10
11
  import { Icon } from "./system.js";
11
- import { SearchField } from "./inputs.js";
12
+ import { Button, Toolbar, ToolbarButton, ToolbarSeparator } from "./actions.js";
13
+ import { Input, SearchField, MultiCombobox } from "./inputs.js";
12
14
  import { Pagination } from "./navigation.js";
15
+ import { Cluster } from "./layout.js";
16
+ import { Alert, Skeleton } from "./feedback.js";
17
+ import { IconButton } from "./actions.js";
13
18
  // 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 quando houver demanda real).
19
+ // filtro global, paginação e seleção por checkbox. A pele reusa .table-wrap/th/td.
20
+ //
21
+ // PLANO-1.0 Parte F, itens F1 e F2 (08/08/2026): o estado deixou de ser SÓ interno.
22
+ // Cada eixo aceita valor + callback de fora, e o interno segue sendo o default —
23
+ // nenhuma chamada existente muda. E os quatro `manual*` dizem ao motor que quem
24
+ // ordena/filtra/pagina é o servidor, não ele.
25
+ //
26
+ // Por que os nomes `manualSorting`/`manualFiltering`/`manualPagination` são os DO
27
+ // MOTOR e não inventados aqui: as três referências de tabela foram medidas em
28
+ // 08/08/2026 e NENHUMA expõe API controlada — o shadcn guarda tudo em useState
29
+ // dentro do exemplo, o Kibo põe a ordenação num átomo global (jotai) e o Untitled
30
+ // é apresentação. Não havia anatomia para copiar; o vocabulário veio do contrato
31
+ // do motor, que é o que o consumidor já lê na documentação dele.
32
+ //
33
+ // LIMITE de `manualPagination`: `data` passa a ser UMA página, então `rowCount`
34
+ // (total no servidor) é obrigatório — sem ele o motor devolve pageCount -1 e a
35
+ // paginação some da tela. E `onSelectionChange` emite as linhas da página atual,
36
+ // porque é só o que existe em memória; para seleção que atravessa páginas, use
37
+ // `rowSelection`/`onRowSelectionChange`, que são ids.
17
38
  // A11y: aria-sort fica SÓ no th ordenado (padrão APG) e o cabeçalho ordenável é
18
39
  // um <button> de verdade; a linha selecionada usa data-selected apenas para
19
40
  // estilo — aria-selected é inválido em role=table, o estado acessível é o
@@ -22,11 +43,55 @@ function GridCheck({ label, indeterminate, ...props }) {
22
43
  return _jsxs("label", { className: "checkbox", children: [_jsx("input", { type: "checkbox", ref: el => { if (el)
23
44
  el.indeterminate = !!indeterminate; }, ...props }), _jsx("span", { className: "control-mark" }), _jsx("span", { className: "sr-only", children: label })] });
24
45
  }
25
- export function DataGrid({ data, columns, label, filterable, pageSize, selectable, onSelectionChange, getRowId, className }) {
46
+ // A faceta guarda um ARRAY de valores e a célula é ESCALAR — e nenhum filterFn de
47
+ // fábrica faz "valor da célula ∈ selecionados": `arrIncludes`/`arrIncludesSome`
48
+ // esperam a célula array (medido em filterFns.ts:45,67). É uma linha, e mora aqui
49
+ // para o consumidor não repeti-la em cada coluna, que é o que a referência obriga.
50
+ const facetFilterFn = (row, columnId, value) => !Array.isArray(value) || !value.length || value.includes(String(row.getValue(columnId)));
51
+ function GridFilter({ col, spec, name }) {
52
+ if (!spec.facet)
53
+ return _jsx(Input, { value: col.getFilterValue() ?? "", onChange: e => col.setFilterValue(e.target.value || undefined), "aria-label": name });
54
+ const counts = new Map([...col.getFacetedUniqueValues()].map(([k, n]) => [String(k), n]));
55
+ // `options` explícitas mantêm na lista um valor que NENHUMA linha tem agora —
56
+ // é a diferença entre "não há nenhum cancelado" e "cancelado não existe".
57
+ const base = spec.options ?? [...counts.keys()].filter(v => v !== "" && v !== "null" && v !== "undefined").sort();
58
+ const items = (typeof base[0] === "string" ? base.map(v => ({ value: v, label: v })) : base)
59
+ .map(o => ({ value: o.value, label: counts.has(o.value) ? `${o.label} (${counts.get(o.value)})` : o.label }));
60
+ const picked = col.getFilterValue() ?? [];
61
+ return _jsx(MultiCombobox, { items: items, value: items.filter(i => picked.includes(i.value)), onValueChange: v => col.setFilterValue(v.length ? v.map(i => i.value) : undefined), label: _jsx("span", { className: "sr-only", children: name }) });
62
+ }
63
+ // F1: um só lugar decide entre "o valor vem de fora" e "eu guardo". Escrever o
64
+ // ternário em cada eixo é como se erra em um — e o erro típico (esquecer o
65
+ // onChange no modo controlado) trava a tela do consumidor, não a nossa.
66
+ function useMaybe(outer, onChange, initial) {
67
+ const [inner, setInner] = React.useState(initial);
68
+ const controlled = outer !== undefined;
69
+ const value = controlled ? outer : inner;
70
+ return [value, u => { const next = typeof u === "function" ? u(value) : u; if (!controlled)
71
+ setInner(next); onChange?.(next); }];
72
+ }
73
+ export function DataGrid({ data, columns, label, filterable, pageSize, selectable, onSelectionChange, getRowId, className, sorting: sortingProp, onSortingChange, globalFilter: globalFilterProp, onGlobalFilterChange, page, onPageChange, rowSelection: rowSelectionProp, onRowSelectionChange, manualSorting, manualFiltering, manualPagination, rowCount, filters, columnFilters: columnFiltersProp, onColumnFiltersChange, bulkActions, stickyHeader, hideableColumns, columnVisibility: columnVisibilityProp, onColumnVisibilityChange, resizableColumns, columnSizing: columnSizingProp, onColumnSizingChange, state, stateMessage, renderDetail, detailRowId: detailRowIdProp, onDetailRowIdChange, onExport }) {
26
74
  const s = useAureaStrings();
27
- const [sorting, setSorting] = React.useState([]);
28
- const [globalFilter, setGlobalFilter] = React.useState("");
29
- const [rowSelection, setRowSelection] = React.useState({});
75
+ const [sorting, setSorting] = useMaybe(sortingProp, onSortingChange, []);
76
+ const [globalFilter, setGlobalFilter] = useMaybe(globalFilterProp, onGlobalFilterChange, "");
77
+ const [columnFilters, setColumnFilters] = useMaybe(columnFiltersProp, onColumnFiltersChange, []);
78
+ // F7: os dois são MAPAS serializáveis — é isso que torna a escolha persistível,
79
+ // e por isso a persistência não é nossa: quem decide entre localStorage, perfil
80
+ // do usuário ou URL é o consumidor, exatamente como no F4.
81
+ const [columnVisibility, setColumnVisibility] = useMaybe(columnVisibilityProp, onColumnVisibilityChange, {});
82
+ const [columnSizing, setColumnSizing] = useMaybe(columnSizingProp, onColumnSizingChange, {});
83
+ const [detailRowId, setDetailRowId] = useMaybe(detailRowIdProp, onDetailRowIdChange, null);
84
+ const [rowSelection, setRowSelection] = useMaybe(rowSelectionProp, onRowSelectionChange, {});
85
+ // `page` é 1-based porque o nosso Pagination é 1-based e é ele que aparece na
86
+ // tela; o motor é 0-based. A conversão fica AQUI, uma vez. Só o índice é
87
+ // estado: o tamanho vem sempre da prop, então mudar `pageSize` não deixa um
88
+ // valor velho preso em useState.
89
+ const paginated = pageSize != null;
90
+ const [innerIndex, setInnerIndex] = React.useState(0);
91
+ const pageIndex = page !== undefined ? page - 1 : innerIndex;
92
+ const pagination = React.useMemo(() => ({ pageIndex, pageSize: pageSize ?? 10 }), [pageIndex, pageSize]);
93
+ const setPagination = (u) => { const next = typeof u === "function" ? u(pagination) : u; if (page === undefined)
94
+ setInnerIndex(next.pageIndex); onPageChange?.(next.pageIndex + 1); };
30
95
  // Sem getRowId a seleção do TanStack é por ÍNDICE: trocar/reordenar data faria o
31
96
  // checkbox "seguir" a posição e marcar OUTRO registro (auditoria 18/07/2026,
32
97
  // ALTO 3). Sem id estável a seleção não sobrevive à mudança de dados — limpa.
@@ -41,16 +106,40 @@ export function DataGrid({ data, columns, label, filterable, pageSize, selectabl
41
106
  onSelectionChange?.([]);
42
107
  }
43
108
  }, [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]);
109
+ const facetIds = React.useMemo(() => new Set((filters ?? []).filter(f => f.facet).map(f => f.column)), [filters]);
110
+ const allColumns = React.useMemo(() => {
111
+ const comFn = columns.map(c => {
112
+ const id = c.id ?? c.accessorKey;
113
+ return id && facetIds.has(id) ? { ...c, filterFn: facetFilterFn } : c;
114
+ });
115
+ return selectable ? [{
116
+ id: "select", enableSorting: false,
117
+ header: ({ table }) => _jsx(GridCheck, { label: s.dataGridSelectAll, checked: table.getIsAllRowsSelected(), indeterminate: table.getIsSomeRowsSelected(), onChange: table.getToggleAllRowsSelectedHandler() }),
118
+ cell: ({ row }) => _jsx(GridCheck, { label: s.dataGridSelectRow, checked: row.getIsSelected(), disabled: !row.getCanSelect(), onChange: row.getToggleSelectedHandler() }),
119
+ }, ...comFn] : comFn;
120
+ }, [selectable, columns, s, facetIds]);
121
+ // F9: o gatilho do detalhe é uma COLUNA com botão por linha, no molde da coluna de
122
+ // seleção — e não um <tr> clicável. Linha não é foco de teclado, e transformá-la em
123
+ // alvo exige inventar papel, tabindex e tecla; um <button> já é tudo isso.
124
+ const colunas = React.useMemo(() => renderDetail ? [...allColumns, {
125
+ id: "detail", enableSorting: false, enableHiding: false, header: () => null,
126
+ cell: ({ row }) => _jsx(IconButton, { icon: "chevron--right", label: s.dataGridDetails, size: "sm", variant: "ghost", "aria-expanded": detailRowId === row.id, onClick: () => setDetailRowId(detailRowId === row.id ? null : row.id) }),
127
+ }] : allColumns, [allColumns, renderDetail, detailRowId, s]);
49
128
  const table = useReactTable({
50
- data, columns: allColumns, getRowId,
51
- state: { sorting, globalFilter, rowSelection },
129
+ data, columns: colunas, getRowId,
130
+ state: { sorting, globalFilter, rowSelection, columnFilters, columnVisibility, columnSizing, ...(paginated ? { pagination } : {}) },
52
131
  onSortingChange: setSorting,
53
132
  onGlobalFilterChange: setGlobalFilter,
133
+ onColumnFiltersChange: setColumnFilters,
134
+ onColumnVisibilityChange: setColumnVisibility,
135
+ onColumnSizingChange: setColumnSizing,
136
+ enableColumnResizing: !!resizableColumns, columnResizeMode: "onChange",
137
+ onPaginationChange: setPagination,
138
+ // F2: os três curto-circuitam o modelo de linha correspondente dentro do
139
+ // motor — medido no fonte instalado (RowSorting.ts:535, ColumnFiltering.ts:408,
140
+ // RowPagination.ts:376). Por isso os getters abaixo seguem ligados sem
141
+ // condição: sob `manual` o motor simplesmente não os chama.
142
+ manualSorting, manualFiltering, manualPagination, rowCount,
54
143
  // emite as linhas ORIGINAIS já aqui (não em effect): getPreFilteredRowModel
55
144
  // ignora filtro/página, então a seleção sobrevive a ambos.
56
145
  onRowSelectionChange: updater => {
@@ -62,15 +151,106 @@ export function DataGrid({ data, columns, label, filterable, pageSize, selectabl
62
151
  getSortedRowModel: getSortedRowModel(),
63
152
  getFilteredRowModel: getFilteredRowModel(),
64
153
  enableRowSelection: !!selectable,
65
- ...(pageSize ? { getPaginationRowModel: getPaginationRowModel(), initialState: { pagination: { pageSize } } } : {}),
154
+ ...(paginated ? { getPaginationRowModel: getPaginationRowModel() } : {}),
155
+ // só quando há faceta: os dois varrem os dados para montar o mapa de valores
156
+ // únicos, e ninguém paga por isso sem ter pedido.
157
+ // <T> explícito: chamados sem argumento de tipo, os dois fixam TData em
158
+ // `unknown` e derrubam a inferência do useReactTable inteiro — o erro sai
159
+ // longe daqui, em `columns` e em `row.original`.
160
+ ...(facetIds.size ? { getFacetedRowModel: getFacetedRowModel(), getFacetedUniqueValues: getFacetedUniqueValues() } : {}),
66
161
  });
67
162
  const rows = table.getRowModel().rows;
68
163
  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) })] });
164
+ // F5: a barra de lote. Duas referências independentes (Activepieces e Kaneo) chegam
165
+ // à MESMA anatomia — contagem, divisória, ações, e um jeito de limpar —, então é
166
+ // ela que entra. O que não entra é o resto das duas: barra `position:fixed` sobre a
167
+ // janela inteira (decisão da APLICAÇÃO, não de um componente que o consumidor põe
168
+ // onde quer, e briga de z-index de graça) e animação por biblioteca de movimento.
169
+ //
170
+ // E ela não tem CSS NENHUM: é `Toolbar` + `ToolbarButton` + `ToolbarSeparator`, que
171
+ // já são superfície flutuante com pele e teclado de setas do Base UI, mais `.hint`
172
+ // para a contagem. A referência desenha a mesma barra à mão em 57 linhas.
173
+ //
174
+ // As linhas vão como as ORIGINAIS e de antes do filtro, igual ao onSelectionChange —
175
+ // agir em lote sobre o que o filtro escondeu é o que o consumidor pediu ao marcar.
176
+ const emLote = bulkActions?.length ? table.getPreFilteredRowModel().flatRows.filter(r => rowSelection[r.id]).map(r => r.original) : [];
177
+ const limparSelecao = () => table.resetRowSelection();
178
+ // F10: o botão DIZ o escopo, e é essa a exigência do item — "exportar" sozinho
179
+ // esconde a pergunta que importa: exportar o quê, tudo ou o que está na tela?
180
+ // A precedência é a que a pessoa acabou de fazer: se marcou, é o marcado; se
181
+ // filtrou, é o filtrado; senão é tudo.
182
+ //
183
+ // FRONTEIRA, e ela não se move: a grade não gera arquivo nem baixa nada. Formato,
184
+ // codificação e transporte são do consumidor — é a mesma linha que a Parte G traça
185
+ // para o envio de arquivo. O que entra é a peça de interface e o escopo certo.
186
+ const selecionadasExp = onExport ? table.getPreFilteredRowModel().flatRows.filter(r => rowSelection[r.id]).map(r => r.original) : [];
187
+ const filtradas = onExport ? table.getFilteredRowModel().rows.map(r => r.original) : [];
188
+ const escopo = selecionadasExp.length ? { linhas: selecionadasExp, filtered: false, selected: true, sufixo: s.dataGridExportSelected }
189
+ : filtradas.length !== data.length ? { linhas: filtradas, filtered: true, selected: false, sufixo: s.dataGridExportFiltered }
190
+ : { linhas: filtradas, filtered: false, selected: false, sufixo: s.dataGridExportRows };
191
+ const exportar = onExport ? _jsx(Button, { variant: "secondary", leadingIcon: "download", onClick: () => onExport(escopo.linhas, { count: escopo.linhas.length, filtered: escopo.filtered, selected: escopo.selected }), children: `${s.dataGridExport} ${escopo.linhas.length} ${escopo.sufixo}` }) : null;
192
+ const busca = filterable ? _jsx(SearchField, { value: globalFilter, onChange: e => setGlobalFilter(e.target.value), placeholder: s.dataGridFilter, "aria-label": s.dataGridFilter }) : null;
193
+ // F7: esconder coluna é escolher várias de uma lista — o mesmo `MultiCombobox` da
194
+ // faceta do F3. Terceira peça desta parte que entra por reuso em vez de construção.
195
+ const ocultaveis = hideableColumns ? table.getAllLeafColumns().filter(c => c.getCanHide() && c.id !== "select") : [];
196
+ const itensColuna = ocultaveis.map(c => ({ value: c.id, label: String(c.columnDef.header ?? c.id) }));
197
+ const seletorColunasEl = hideableColumns ? _jsx(MultiCombobox, { items: itensColuna, value: itensColuna.filter(i => table.getColumn(i.value)?.getIsVisible()), onValueChange: v => setColumnVisibility(Object.fromEntries(ocultaveis.map(c => [c.id, v.some(i => i.value === c.id)]))), label: _jsx("span", { className: "sr-only", children: s.dataGridColumns }), placeholder: s.dataGridColumns }) : null;
198
+ // O Cluster só entra com DOIS ou mais controles: sozinho, cada um continua item de
199
+ // grade e ocupa a largura toda, como antes desta parte. Medido no F7.
200
+ const controles = [busca, seletorColunasEl, exportar].filter(Boolean).map((c, i) => _jsx(React.Fragment, { children: c }, i));
201
+ // F8: dado velho sem aviso é pior que tela vazia — então o aviso é TEXTO, e não uma
202
+ // cor. `Alert` já resolve os dois lados: `danger` vira role="alert" (interrompe),
203
+ // os outros viram role="status" (entra na próxima pausa do leitor de tela).
204
+ //
205
+ // `loading` NÃO apaga o que já está na tela: recarregar não é motivo para o
206
+ // consumidor perder o que estava lendo. Só quando não há linha nenhuma é que
207
+ // entram os esqueletos. O `aria-busy` diz o resto.
208
+ //
209
+ // Os nomes daqui são os DESTA grade. A Parte J é que vai nomear os estados
210
+ // universais uma vez só — antecipá-la aqui criaria o segundo vocabulário que ela
211
+ // existe para impedir.
212
+ //
213
+ // A Parte J (09/08/2026) nomeou os estados universais, e este `state` passou a ACEITÁ-LOS —
214
+ // era a previsão escrita no parágrafo acima e ela se cumpriu sem renomear nada: `stale` e
215
+ // `partial` já se chamavam assim. `loading` e `error` continuam sendo DESTA grade e de
216
+ // propósito: nos sete universais a tela AINDA SERVE, e sem linha nenhuma ela não serve.
217
+ //
218
+ // As três frases da grade ficam, e não é apego — elas falam de LINHAS ("algumas linhas não
219
+ // puderam ser carregadas") onde a universal fala do genérico ("parte disto"). Mais específico
220
+ // ganha de mais geral; o universal entra para os quatro estados que a grade não tinha.
221
+ const carregando = state === "loading";
222
+ const universal = state && state !== "loading" && state !== "error" ? state : null;
223
+ const recado = state && !carregando ? (stateMessage ?? (state === "stale" ? s.dataGridStale : state === "partial" ? s.dataGridPartial : state === "error" ? s.dataGridError : s.universalState[state])) : null;
224
+ // O marcador vai na GRADE, não no recado: quem está obsoleto é a tabela, e o `Alert` é só
225
+ // como ela conta isso. Achado pelo check 30 na primeira execução dele — eu tinha marcado o
226
+ // recado, e com `state="loading"` não há recado nenhum, então o estado não chegava ao DOM.
227
+ return _jsxs("div", { className: cx("datagrid", stickyHeader && "datagrid-sticky", className), "data-state": state, children: [emLote.length > 0 && _jsxs(Toolbar, { label: s.dataGridBulkLabel, children: [_jsxs("span", { className: "hint", children: [emLote.length, " ", s.dataGridSelected] }), _jsx(ToolbarSeparator, {}), bulkActions?.map(a => _jsx(ToolbarButton, { size: "sm", leadingIcon: a.icon, onClick: () => a.onAction(emLote, limparSelecao), children: a.label }, a.id)), _jsx(ToolbarButton, { size: "sm", onClick: limparSelecao, children: s.dataGridClearSelection })] }), controles.length > 1 ? _jsx(Cluster, { children: controles }) : controles[0] ?? null, recado && _jsx(Alert, { variant: state === "error" ? "danger" : universal ? stateSeverity(universal) : "warning", children: recado }), (() => {
228
+ // F9: o embrulho de duas colunas só existe COM o painel aberto — fechado, a
229
+ // marcação é a de sempre. É a lição do F7: não mudar a estrutura de quem não
230
+ // pediu nada. A largura do painel é `--datagrid-detail-w`, e quem decide se
231
+ // ele cabe é a largura da GRADE, não a da janela: por isso `@container`, no
232
+ // precedente do MediaPlayer.
233
+ const linhaAberta = detailRowId != null ? table.getRowModel().rows.find(r => r.id === detailRowId) : undefined;
234
+ const tabela = (_jsx("div", { className: "table-wrap", role: "region", "aria-label": label ?? s.tableLabel, tabIndex: 0, "aria-busy": carregando || undefined, children: _jsxs("table", { children: [_jsxs("thead", { children: [table.getHeaderGroups().map(hg => _jsx("tr", { children: hg.headers.map(h => {
235
+ const dir = h.column.getIsSorted();
236
+ return _jsxs("th", { colSpan: h.colSpan, className: h.column.id === "select" ? "datagrid-selcol" : undefined, "aria-sort": dir === "asc" ? "ascending" : dir === "desc" ? "descending" : undefined, style: resizableColumns ? { width: h.getSize() } : undefined, children: [h.isPlaceholder ? null : h.column.getCanSort()
237
+ ? _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" })] })
238
+ : flexRender(h.column.columnDef.header, h.getContext()), resizableColumns && h.column.getCanResize() && _jsx("button", { type: "button", className: "datagrid-resizer", "aria-label": `${s.dataGridResize}: ${String(h.column.columnDef.header ?? h.column.id)}`, onMouseDown: h.getResizeHandler(), onTouchStart: h.getResizeHandler(), onKeyDown: e => {
239
+ const d = e.key === "ArrowLeft" ? -16 : e.key === "ArrowRight" ? 16 : 0;
240
+ if (!d)
241
+ return;
242
+ e.preventDefault();
243
+ setColumnSizing({ ...columnSizing, [h.column.id]: Math.max(40, h.column.getSize() + d) });
244
+ } })] }, h.id);
245
+ }) }, hg.id)), !!filters?.length && _jsx("tr", { className: "datagrid-filters", children: table.getVisibleLeafColumns().map(col => {
246
+ const spec = filters.find(f => f.column === col.id);
247
+ return _jsx("th", { className: col.id === "select" ? "datagrid-selcol" : undefined, children: spec ? _jsx(GridFilter, { col: col, spec: spec, name: `${s.dataGridFilter} ${spec.label}` }) : null }, col.id);
248
+ }) })] }), _jsx("tbody", { children: carregando && !rows.length
249
+ ? Array.from({ length: pageSize ?? 3 }, (_, i) => _jsx("tr", { children: table.getVisibleLeafColumns().map(c => _jsx("td", { children: _jsx(Skeleton, { className: "datagrid-skeleton" }) }, c.id)) }, `sk${i}`))
250
+ : 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))
251
+ : _jsx("tr", { children: _jsx("td", { colSpan: table.getVisibleLeafColumns().length, className: "datagrid-empty", children: s.dataGridEmpty }) }) })] }) }));
252
+ if (!renderDetail || !linhaAberta)
253
+ return tabela;
254
+ return _jsxs("div", { className: "datagrid-split", children: [tabela, _jsxs("aside", { className: "datagrid-detail", "aria-label": s.dataGridDetailPanel, children: [_jsx("div", { className: "datagrid-detail-head", children: _jsx(IconButton, { icon: "close", label: s.close, size: "sm", variant: "ghost", onClick: () => setDetailRowId(null) }) }), renderDetail(linhaAberta.original)] })] });
255
+ })(), paginated && table.getPageCount() > 1 && _jsx(Pagination, { page: pageIndex + 1, total: table.getPageCount(), onPageChange: p => table.setPageIndex(p - 1) })] });
76
256
  }
@@ -0,0 +1,64 @@
1
+ import React, { type HTMLAttributes, type RefAttributes, type ReactNode } from "react";
2
+ import { type UniversalState } from "./internal.js";
3
+ import { type IconName } from "./system.js";
4
+ import { type OverlaySide } from "./overlays.js";
5
+ export type StatusVariant = "neutral" | "online" | "offline" | "busy" | "away" | "running" | "success" | "warning" | "danger" | "info";
6
+ export declare function Status({ variant, state, children, className, ...props }: HTMLAttributes<HTMLSpanElement> & RefAttributes<HTMLSpanElement> & {
7
+ variant?: StatusVariant;
8
+ state?: UniversalState;
9
+ }): React.JSX.Element;
10
+ export type AlertVariant = "info" | "success" | "warning" | "danger";
11
+ export declare function Alert({ variant, state, title, children, className, ...props }: HTMLAttributes<HTMLDivElement> & RefAttributes<HTMLDivElement> & {
12
+ variant?: AlertVariant;
13
+ state?: UniversalState;
14
+ title?: ReactNode;
15
+ }): React.JSX.Element;
16
+ export type BannerVariant = AlertVariant;
17
+ export declare function Banner({ variant, state, title, icon, onDismiss, children, className, ...props }: HTMLAttributes<HTMLDivElement> & RefAttributes<HTMLDivElement> & {
18
+ variant?: BannerVariant;
19
+ state?: UniversalState;
20
+ title?: ReactNode;
21
+ icon?: IconName;
22
+ onDismiss?: () => void;
23
+ }): React.JSX.Element;
24
+ export declare function Spinner({ size, label, decorative, className, ...props }: HTMLAttributes<HTMLSpanElement> & RefAttributes<HTMLSpanElement> & {
25
+ size?: "sm" | "md" | "lg";
26
+ label?: string;
27
+ decorative?: boolean;
28
+ }): React.JSX.Element;
29
+ export type DataStateValue = "loading" | "error" | "empty" | UniversalState;
30
+ export declare function DataState({ state, message, skeleton, emptyTitle, emptyIcon, action, children, className, ...props }: HTMLAttributes<HTMLDivElement> & RefAttributes<HTMLDivElement> & {
31
+ state?: DataStateValue;
32
+ message?: ReactNode;
33
+ skeleton?: ReactNode;
34
+ emptyTitle?: ReactNode;
35
+ emptyIcon?: IconName;
36
+ action?: ReactNode;
37
+ children: ReactNode | (() => ReactNode);
38
+ }): React.JSX.Element;
39
+ export declare function EmptyState({ icon, title, titleAs: TitleTag, description, action, state }: {
40
+ icon?: IconName;
41
+ title: ReactNode;
42
+ titleAs?: "h2" | "h3" | "h4" | "p";
43
+ description?: ReactNode;
44
+ action?: ReactNode;
45
+ state?: UniversalState;
46
+ }): React.JSX.Element;
47
+ export interface NotificationItem {
48
+ id: string;
49
+ title: ReactNode;
50
+ description?: ReactNode;
51
+ time?: ReactNode;
52
+ icon?: IconName;
53
+ read?: boolean;
54
+ group?: string;
55
+ onClick?: () => void;
56
+ }
57
+ export declare function NotificationCenter({ items, onItemClick, onMarkAllRead, label, icon, side }: {
58
+ items: NotificationItem[];
59
+ onItemClick?: (item: NotificationItem) => void;
60
+ onMarkAllRead?: () => void;
61
+ label?: string;
62
+ icon?: IconName;
63
+ side?: OverlaySide;
64
+ }): React.JSX.Element;
@@ -0,0 +1,110 @@
1
+ "use client";
2
+ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ // Fase 9 (achado A5): este arquivo saiu do index.tsx de 970 linhas. Um módulo por categoria
4
+ // do registry — a taxonomia já existia e é gateada. A ordem de import entre eles é um DAG:
5
+ // internal → system → actions → feedback → inputs → navigation → layout → data-display → resto.
6
+ import React from "react";
7
+ import { Popover as BasePopover } from "@base-ui/react/popover";
8
+ import { cx, useAureaStrings, usePortalContainer, stateSeverity } from "./internal.js";
9
+ import { Icon } from "./system.js";
10
+ import { Button, IconButton } from "./actions.js";
11
+ // `oracle` saiu daqui (achado M9, 26/07/2026): estava no tipo e no CSS, e NÃO estava na
12
+ // ficha — ou seja, era superfície pública que o contrato não declarava. E é vocabulário do
13
+ // app de origem (papel de agente), não do sistema, como as classes de domínio que o achado
14
+ // A6 mapeou. As classes `.badge-oracle`/`.btn-oracle` seguem no core porque o
15
+ // `apps/docs/index.html` escrito à mão as usa; saem junto com ele, na Fase 4 do plano.
16
+ // `.btn-oracle` nunca foi alcançável pelo React — `ButtonVariant` não tem `oracle`.
17
+ import { Skeleton } from "./markup.js";
18
+ // Status (DIRECTION §3.6): condição OPERACIONAL — ponto + rótulo. Não é Badge: Badge é
19
+ // metadado curto num pill; Status diz em que estado a coisa está. Reusa o .status-dot que
20
+ // já existia solto. A variante colore só o PONTO (currentColor); o rótulo fica legível em
21
+ // --foreground. Cor não é o único sinal — quem diz o estado é o texto (WCAG 1.4.1); o
22
+ // ponto é decorativo e sai do leitor de tela. ponytail: rótulo é do consumidor (sem i18n
23
+ // nova) — a variante é só a cor.
24
+ // `state` (Parte J) é EIXO À PARTE de `variant`, e os dois convivem: a variante é a cor do
25
+ // ponto, o estado é a condição universal. Quem passa `state` e não passa `variant` recebe a
26
+ // cor derivada — `offline` fica com o ponto vazado que ele já tinha desde sempre, os outros
27
+ // seis caem na gravidade. Quem passa os dois manda, porque só o consumidor sabe se aquele
28
+ // "esperando" dele é grave. E o rótulo é o do consumidor, como sempre foi: a string universal
29
+ // entra só quando não há filho, para o componente não passar a inventar texto.
30
+ export function Status({ variant, state, children, className, ...props }) { const s = useAureaStrings(); const v = variant ?? (state ? (state === "offline" ? "offline" : stateSeverity(state)) : "neutral"); return _jsxs("span", { className: cx("status", v !== "neutral" && `status-${v}`, className), "data-state": state, ...props, children: [_jsx("i", { className: "status-dot", "aria-hidden": "true" }), _jsx("span", { className: "status-label", children: children ?? (state ? s.universalState[state] : null) })] }); }
31
+ export function Alert({ variant, state, title, children, className, ...props }) { const s = useAureaStrings(); const v = variant ?? (state ? stateSeverity(state) : "info"); return _jsxs("div", { className: cx("alert", `alert-${v}`, className), role: v === "danger" ? "alert" : "status", "data-state": state, ...props, children: [title && _jsx("strong", { children: title }), children ?? (state ? s.universalState[state] : null)] }); }
32
+ export function Banner({ variant, state, title, icon, onDismiss, children, className, ...props }) { const s = useAureaStrings(); const v = variant ?? (state ? stateSeverity(state) : "info"); return _jsxs("div", { className: cx("banner", `banner-${v}`, className), role: v === "danger" ? "alert" : "status", "data-state": state, ...props, children: [icon ? _jsx(Icon, { name: icon }) : _jsx("span", {}), _jsxs("div", { children: [title && _jsx("strong", { children: title }), children ?? (state ? s.universalState[state] : null)] }), onDismiss ? _jsx(IconButton, { variant: "ghost", size: "sm", icon: "close", label: s.close, onClick: onDismiss }) : _jsx("span", {})] }); }
33
+ // A barra grampeava 0..100 e o `aria-valuenow` NÃO — medido ao publicar o contrato de API na
34
+ // Parte E: com value=150 o desenho parava em 100% e o leitor de tela anunciava "150 de 100".
35
+ // A causa é a de sempre: o grampo existia num lugar só. Agora é UMA expressão que serve os dois,
36
+ // então não há como divergirem de novo.
37
+ // SÓ DETERMINADA, de propósito: não há modo indeterminado nem `.progress` para ele no core. A
38
+ // ficha dizia que havia e era falso — corrigido junto, porque contrato que promete o que não
39
+ // existe custa mais que ausência.
40
+ export function Spinner({ size = "sm", label, decorative, className, ...props }) { const s = useAureaStrings(); return _jsx("span", { className: cx("spinner", size !== "sm" && `spinner-${size}`, className), ...(decorative ? { "aria-hidden": true } : { role: "status", "aria-label": label ?? s.loading }), ...props }); }
41
+ // `children` como FUNÇÃO é de propósito para o caso `loading`: assim o consumidor não paga o
42
+ // render do conteúdo enquanto ele não existe. Aceita nó também, porque a maioria das telas já
43
+ // tem o conteúdo pronto e obrigar função seria cerimônia.
44
+ export function DataState({ state, message, skeleton, emptyTitle, emptyIcon, action, children, className, ...props }) {
45
+ const s = useAureaStrings();
46
+ const conteudo = () => typeof children === "function" ? children() : children;
47
+ const caixa = (inner, ocupado) => _jsx("div", { className: cx("data-state", className), "aria-busy": ocupado || undefined, "data-state": state, ...props, children: inner });
48
+ if (state === "loading")
49
+ return caixa(skeleton ?? _jsx(Skeleton, { style: { height: "var(--space-8)" } }), true);
50
+ if (state === "error")
51
+ return caixa(_jsx(Alert, { variant: "danger", children: message ?? s.dataError }));
52
+ // `titleAs="p"` e não o `h3` padrão do EmptyState: aqui o vazio é estado de uma REGIÃO, não
53
+ // seção do documento. Injetar um h3 no meio do conteúdo do consumidor salta nível de título —
54
+ // o gate de hierarquia pegou (`salto h1 → h3`) e o axe repetiu como `heading-order`.
55
+ if (state === "empty")
56
+ return caixa(_jsx(EmptyState, { icon: emptyIcon, titleAs: "p", title: emptyTitle ?? s.dataEmpty, description: message, action: action }));
57
+ // Os universais NÃO substituem o conteúdo: eles o acompanham. É a regra do DataGrid, e o
58
+ // motivo é o mesmo — a pessoa precisa do dado E do aviso, não de um no lugar do outro.
59
+ if (state)
60
+ return caixa(_jsxs(_Fragment, { children: [_jsx(Alert, { variant: stateSeverity(state), state: state, children: message }), conteudo()] }));
61
+ return caixa(conteudo());
62
+ }
63
+ export function EmptyState({ icon = "document--blank", title, titleAs: TitleTag = "h3", description, action, state }) { const s = useAureaStrings(); const desc = description ?? (state ? s.universalState[state] : null); return _jsxs("div", { className: "empty-state", "data-state": state, children: [_jsx(Icon, { name: icon, size: "xl" }), _jsx(TitleTag, { className: "empty-title", children: title }), desc && _jsx("p", { className: "muted", children: desc }), action] }); }
64
+ function groupNotifications(items) {
65
+ const out = [];
66
+ for (const it of items) {
67
+ const last = out[out.length - 1];
68
+ if (last && last.label === it.group)
69
+ last.items.push(it);
70
+ else
71
+ out.push({ label: it.group, items: [it] });
72
+ }
73
+ return out;
74
+ }
75
+ export function NotificationCenter({ items, onItemClick, onMarkAllRead, label, icon = "notification", side = "bottom" }) {
76
+ const s = useAureaStrings();
77
+ const portal = usePortalContainer();
78
+ const title = label ?? s.notificationsLabel;
79
+ const unread = items.filter(i => !i.read).length;
80
+ const groups = groupNotifications(items);
81
+ const baseId = React.useId();
82
+ const seen = React.useRef(undefined);
83
+ const [announce, setAnnounce] = React.useState("");
84
+ React.useEffect(() => {
85
+ const ids = new Set(items.map(i => i.id));
86
+ if (seen.current === undefined) {
87
+ seen.current = ids;
88
+ return;
89
+ }
90
+ const fresh = items.filter(i => !seen.current.has(i.id)).length;
91
+ seen.current = ids;
92
+ // Texto idêntico duas vezes seguidas não muta o DOM e o leitor silencia a 2ª
93
+ // chegada (auditoria 18/07/2026, MÉDIO 4). Um NBSP alternado no fim força a
94
+ // mutação sem mudar o que se ouve.
95
+ if (fresh)
96
+ setAnnounce(prev => { const text = `${fresh} ${s.notificationNew}`; return prev === text ? text + " " : text; });
97
+ }, [items, s.notificationNew]);
98
+ const renderRow = (it) => {
99
+ 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 })] });
100
+ return it.onClick || onItemClick
101
+ ? _jsx("button", { type: "button", className: "notification-item", "data-read": it.read || undefined, onClick: () => { it.onClick?.(); onItemClick?.(it); }, children: body })
102
+ : _jsx("div", { className: "notification-item", "data-read": it.read || undefined, children: body });
103
+ };
104
+ 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, { container: 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
105
+ ? _jsx("div", { className: "notification-list", children: groups.map((g, gi) => {
106
+ const gid = baseId + gi;
107
+ 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);
108
+ }) })
109
+ : _jsx("p", { className: "notification-empty", children: s.notificationEmpty })] }) }) }), _jsx("span", { className: "sr-only", role: "status", "aria-live": "polite", children: announce })] });
110
+ }
@@ -1,53 +1,2 @@
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;
1
+ export * from "./feedback-client.js";
2
+ export { Badge, Progress, Skeleton, type BadgeVariant } from "./markup.js";