@aurea-uds/react 0.2.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.
- package/README.md +34 -2
- package/dist/actions.d.ts +10 -3
- package/dist/actions.js +55 -6
- package/dist/code-client.d.ts +6 -0
- package/dist/code-client.js +24 -0
- package/dist/code.d.ts +2 -13
- package/dist/code.js +5 -34
- package/dist/data-display-client.d.ts +14 -0
- package/dist/data-display-client.js +22 -0
- package/dist/data-display.d.ts +2 -23
- package/dist/data-display.js +5 -14
- package/dist/feedback-client.d.ts +64 -0
- package/dist/feedback-client.js +110 -0
- package/dist/feedback.d.ts +2 -63
- package/dist/feedback.js +5 -101
- package/dist/file-input.js +57 -9
- package/dist/identity-client.d.ts +9 -0
- package/dist/identity-client.js +12 -0
- package/dist/identity.d.ts +2 -17
- package/dist/identity.js +5 -25
- package/dist/index.d.ts +2 -1
- package/dist/index.js +19 -3
- package/dist/inputs-client.d.ts +105 -0
- package/dist/inputs-client.js +264 -0
- package/dist/inputs.d.ts +2 -91
- package/dist/inputs.js +9 -126
- package/dist/internal.d.ts +20 -5
- package/dist/internal.js +91 -10
- package/dist/layout-client.d.ts +9 -0
- package/dist/layout-client.js +72 -0
- package/dist/layout.d.ts +2 -15
- package/dist/layout.js +5 -76
- package/dist/markup.d.ts +72 -0
- package/dist/markup.js +87 -0
- package/dist/media-client.d.ts +36 -0
- package/dist/media-client.js +239 -0
- package/dist/media.d.ts +2 -9
- package/dist/media.js +5 -99
- package/dist/navigation-client.d.ts +94 -0
- package/dist/navigation-client.js +154 -0
- package/dist/navigation.d.ts +2 -85
- package/dist/navigation.js +5 -138
- package/dist/overlays.d.ts +23 -0
- package/dist/overlays.js +45 -1
- package/dist/pure.d.ts +30 -0
- package/dist/pure.js +26 -2
- package/dist/system.d.ts +9 -0
- package/dist/system.js +46 -0
- package/package.json +180 -176
package/dist/feedback.js
CHANGED
|
@@ -1,101 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
//
|
|
4
|
-
|
|
5
|
-
|
|
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
|
-
export function Badge({ variant = "neutral", className, ...props }) { return _jsx("span", { className: cx("badge", variant !== "neutral" && `badge-${variant}`, className), ...props }); }
|
|
12
|
-
// Status (DIRECTION §3.6): condição OPERACIONAL — ponto + rótulo. Não é Badge: Badge é
|
|
13
|
-
// metadado curto num pill; Status diz em que estado a coisa está. Reusa o .status-dot que
|
|
14
|
-
// já existia solto. A variante colore só o PONTO (currentColor); o rótulo fica legível em
|
|
15
|
-
// --foreground. Cor não é o único sinal — quem diz o estado é o texto (WCAG 1.4.1); o
|
|
16
|
-
// ponto é decorativo e sai do leitor de tela. ponytail: rótulo é do consumidor (sem i18n
|
|
17
|
-
// nova) — a variante é só a cor.
|
|
18
|
-
// `state` (Parte J) é EIXO À PARTE de `variant`, e os dois convivem: a variante é a cor do
|
|
19
|
-
// ponto, o estado é a condição universal. Quem passa `state` e não passa `variant` recebe a
|
|
20
|
-
// cor derivada — `offline` fica com o ponto vazado que ele já tinha desde sempre, os outros
|
|
21
|
-
// seis caem na gravidade. Quem passa os dois manda, porque só o consumidor sabe se aquele
|
|
22
|
-
// "esperando" dele é grave. E o rótulo é o do consumidor, como sempre foi: a string universal
|
|
23
|
-
// entra só quando não há filho, para o componente não passar a inventar texto.
|
|
24
|
-
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) })] }); }
|
|
25
|
-
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)] }); }
|
|
26
|
-
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", {})] }); }
|
|
27
|
-
// A barra grampeava 0..100 e o `aria-valuenow` NÃO — medido ao publicar o contrato de API na
|
|
28
|
-
// Parte E: com value=150 o desenho parava em 100% e o leitor de tela anunciava "150 de 100".
|
|
29
|
-
// A causa é a de sempre: o grampo existia num lugar só. Agora é UMA expressão que serve os dois,
|
|
30
|
-
// então não há como divergirem de novo.
|
|
31
|
-
// SÓ DETERMINADA, de propósito: não há modo indeterminado nem `.progress` para ele no core. A
|
|
32
|
-
// ficha dizia que havia e era falso — corrigido junto, porque contrato que promete o que não
|
|
33
|
-
// existe custa mais que ausência.
|
|
34
|
-
export function Progress({ value, label }) { const pct = Math.max(0, Math.min(100, value)); return _jsx("div", { children: _jsx("div", { className: "progress", role: "progressbar", "aria-label": label, "aria-valuemin": 0, "aria-valuemax": 100, "aria-valuenow": pct, children: _jsx("span", { style: { width: `${pct}%` } }) }) }); }
|
|
35
|
-
export function Skeleton({ className, ...props }) { return _jsx("div", { className: cx("skeleton", className), "aria-hidden": "true", ...props }); }
|
|
36
|
-
// Spinner (Lote 1 do BUILDING.md). O glifo `.spinner` existia no core desde sempre e era usado
|
|
37
|
-
// SÓ por dentro do Button — nunca teve componente, então nunca teve papel acessível: quem
|
|
38
|
-
// esperava carregando não ouvia nada. As três referências que o têm concordam em `role="status"`
|
|
39
|
-
// com rótulo, e é o que faltava.
|
|
40
|
-
// Sem escolha de "estilo" de spinner (ponto, barra, pinwheel): é UM glifo, e um sistema com
|
|
41
|
-
// cinco spinners é um sistema sem spinner.
|
|
42
|
-
// `decorative` existe para o caso em que ele vive DENTRO de um controle que já anuncia o estado
|
|
43
|
-
// (o `Button loading` faz isso com aria-busy) — dois anúncios para o mesmo fato é ruído.
|
|
44
|
-
// O default é `sm` e não `md` de propósito: o lugar natural do spinner é DENTRO de um controle,
|
|
45
|
-
// ao lado de texto, e é o tamanho que ele sempre teve aqui. Um default maior mudaria a
|
|
46
|
-
// aparência de todo botão em carregamento sem ninguém ter pedido.
|
|
47
|
-
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 }); }
|
|
48
|
-
// titleAs: o nível do título é do DOCUMENTO, não do componente. Fixo em h3, um empty state
|
|
49
|
-
// no alto de uma página vira h1→h3 e a hierarquia quebra (axe heading-order). Default h3
|
|
50
|
-
// para não mexer em quem já consome; quem sabe o contexto passa o nível certo.
|
|
51
|
-
// `state` aqui NÃO deriva cor nenhuma — o vazio já é neutro e um empty state colorido seria
|
|
52
|
-
// alarme onde há ausência. O que ele faz é o marcador no DOM e a descrição padrão, para
|
|
53
|
-
// "sem conexão" e "resultado parcial" não serem sete frases diferentes em sete telas.
|
|
54
|
-
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] }); }
|
|
55
|
-
function groupNotifications(items) {
|
|
56
|
-
const out = [];
|
|
57
|
-
for (const it of items) {
|
|
58
|
-
const last = out[out.length - 1];
|
|
59
|
-
if (last && last.label === it.group)
|
|
60
|
-
last.items.push(it);
|
|
61
|
-
else
|
|
62
|
-
out.push({ label: it.group, items: [it] });
|
|
63
|
-
}
|
|
64
|
-
return out;
|
|
65
|
-
}
|
|
66
|
-
export function NotificationCenter({ items, onItemClick, onMarkAllRead, label, icon = "notification", side = "bottom" }) {
|
|
67
|
-
const s = useAureaStrings();
|
|
68
|
-
const portal = usePortalContainer();
|
|
69
|
-
const title = label ?? s.notificationsLabel;
|
|
70
|
-
const unread = items.filter(i => !i.read).length;
|
|
71
|
-
const groups = groupNotifications(items);
|
|
72
|
-
const baseId = React.useId();
|
|
73
|
-
const seen = React.useRef(undefined);
|
|
74
|
-
const [announce, setAnnounce] = React.useState("");
|
|
75
|
-
React.useEffect(() => {
|
|
76
|
-
const ids = new Set(items.map(i => i.id));
|
|
77
|
-
if (seen.current === undefined) {
|
|
78
|
-
seen.current = ids;
|
|
79
|
-
return;
|
|
80
|
-
}
|
|
81
|
-
const fresh = items.filter(i => !seen.current.has(i.id)).length;
|
|
82
|
-
seen.current = ids;
|
|
83
|
-
// Texto idêntico duas vezes seguidas não muta o DOM e o leitor silencia a 2ª
|
|
84
|
-
// chegada (auditoria 18/07/2026, MÉDIO 4). Um NBSP alternado no fim força a
|
|
85
|
-
// mutação sem mudar o que se ouve.
|
|
86
|
-
if (fresh)
|
|
87
|
-
setAnnounce(prev => { const text = `${fresh} ${s.notificationNew}`; return prev === text ? text + " " : text; });
|
|
88
|
-
}, [items, s.notificationNew]);
|
|
89
|
-
const renderRow = (it) => {
|
|
90
|
-
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 })] });
|
|
91
|
-
return it.onClick || onItemClick
|
|
92
|
-
? _jsx("button", { type: "button", className: "notification-item", "data-read": it.read || undefined, onClick: () => { it.onClick?.(); onItemClick?.(it); }, children: body })
|
|
93
|
-
: _jsx("div", { className: "notification-item", "data-read": it.read || undefined, children: body });
|
|
94
|
-
};
|
|
95
|
-
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
|
|
96
|
-
? _jsx("div", { className: "notification-list", children: groups.map((g, gi) => {
|
|
97
|
-
const gid = baseId + gi;
|
|
98
|
-
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);
|
|
99
|
-
}) })
|
|
100
|
-
: _jsx("p", { className: "notification-empty", children: s.notificationEmpty })] }) }) }), _jsx("span", { className: "sr-only", role: "status", "aria-live": "polite", children: announce })] });
|
|
101
|
-
}
|
|
1
|
+
// VITRINE da categoria — é este arquivo que `@aurea-uds/react/feedback` resolve. Sem
|
|
2
|
+
// `"use client"` de propósito: a diretiva contamina o módulo inteiro e faria a marcação pura
|
|
3
|
+
// chegar como cliente por vizinhança (ADR-0026; o check 26b reprova quem a puser de volta aqui).
|
|
4
|
+
export * from "./feedback-client.js";
|
|
5
|
+
export { Badge, Progress, Skeleton } from "./markup.js";
|
package/dist/file-input.js
CHANGED
|
@@ -42,6 +42,21 @@ async function somaSha256(file) {
|
|
|
42
42
|
return undefined;
|
|
43
43
|
}
|
|
44
44
|
}
|
|
45
|
+
// AUD-0006 (12/08/2026): um item restaurado NÃO tem os bytes — a plataforma não devolve `File` de
|
|
46
|
+
// um armazenamento —, então ele não está subindo e não vai subir. Persistido como
|
|
47
|
+
// `pending`/`uploading`/`paused`, ele mostrava Pausar/Retomar/Cancelar INERTES (não há controller
|
|
48
|
+
// em memória para abortar, e `retomar` exige `e.file`) e contava como "em voo", o que travava o
|
|
49
|
+
// recibo para sempre: `emVoo` nunca zerava.
|
|
50
|
+
//
|
|
51
|
+
// `canceled` é o único terminal HONESTO aqui. A transferência foi de fato abortada — pelo fim da
|
|
52
|
+
// página —, e não houve falha de servidor que justificasse `error`. `pending` seria mentira dupla:
|
|
53
|
+
// não está esperando nada e continuaria contando como em voo.
|
|
54
|
+
//
|
|
55
|
+
// E o ganho é que a marcação certa sai de graça: o ramo de `canceled` já esconde o botão de
|
|
56
|
+
// repetir quando não há `File` (era uma decisão do item G1) e já oferece só a remoção. Zero string
|
|
57
|
+
// nova, zero branch novo.
|
|
58
|
+
const ATIVOS = new Set(["pending", "uploading", "paused"]);
|
|
59
|
+
const restaurar = (q) => ({ ...q, restored: true, status: ATIVOS.has(q.status) ? "canceled" : q.status });
|
|
45
60
|
function formatSize(bytes) {
|
|
46
61
|
if (bytes < 1024)
|
|
47
62
|
return `${bytes} B`;
|
|
@@ -60,12 +75,27 @@ export function FileInput({ accept, maxSize, multiple, onFilesChange, upload, la
|
|
|
60
75
|
// G1: a fila restaurada entra como estado INICIAL, não como prop controlada — e a
|
|
61
76
|
// palavra "inicial" é literal: um item restaurado não tem `File`, então controlá-lo
|
|
62
77
|
// de fora seria prometer um round-trip que a plataforma não faz.
|
|
78
|
+
// AUD-0002 (12/08/2026): o contador SEMPRE começava em zero, e os ids restaurados vêm do
|
|
79
|
+
// consumidor — inclusive `f0`, que é exatamente o que ele gera. Restaurar `f0` e escolher um
|
|
80
|
+
// arquivo novo dava `id: "f0"` aos DOIS: remover um removia os dois (`filter` por id), `patch`
|
|
81
|
+
// atualizava os dois, e o React avisava chave duplicada. Perda de registro no fluxo oficial de
|
|
82
|
+
// persistência.
|
|
83
|
+
// O conjunto dos ids em uso faz o gerador PULAR, e por ser conjunto ele vale para id fora do
|
|
84
|
+
// padrão também — semear o contador com "o maior sufixo numérico" só resolveria `f<N>`.
|
|
63
85
|
const nextId = React.useRef(0);
|
|
64
|
-
const
|
|
86
|
+
const usados = React.useRef(null);
|
|
87
|
+
if (!usados.current)
|
|
88
|
+
usados.current = new Set((initialQueue ?? []).map(q => q.id));
|
|
89
|
+
const novoId = () => { let novo; do {
|
|
90
|
+
novo = `f${nextId.current++}`;
|
|
91
|
+
} while (usados.current.has(novo)); usados.current.add(novo); return novo; };
|
|
92
|
+
const [files, setFiles] = React.useState(() => (initialQueue ?? []).map(restaurar));
|
|
65
93
|
const [rejected, setRejected] = React.useState([]);
|
|
66
94
|
// a soma chega DEPOIS do envio começar (é assíncrona), então a comparação lê a
|
|
67
95
|
// lista atual por ref em vez da closure do início do upload.
|
|
68
|
-
|
|
96
|
+
// semeada com a MESMA lista inicial do estado, e não com `[]`: com fila restaurada as duas
|
|
97
|
+
// divergiam até a primeira emissão, e é desta ref que o AUD-0009 abaixo lê a lista anterior.
|
|
98
|
+
const filesRef = React.useRef(files);
|
|
69
99
|
// E guarda a PROMESSA, não só o valor: um arquivo pequeno com envio rápido termina
|
|
70
100
|
// antes de o hash sair, e comparar contra `undefined` pularia a conferência em
|
|
71
101
|
// silêncio — que é pior que não conferir, porque parece que conferiu. Quem achou
|
|
@@ -90,7 +120,25 @@ export function FileInput({ accept, maxSize, multiple, onFilesChange, upload, la
|
|
|
90
120
|
// consumidor guarda; onde ele guarda — armazenamento local, servidor, nada — é
|
|
91
121
|
// decisão dele, a mesma linha do estado da grade na URL (PLANO-1.0 F4).
|
|
92
122
|
const paraFila = (l) => l.map(({ id, name, bytes, status, progress, restored, checksum: soma, finishedAt }) => ({ id, name, bytes, status, progress, ...(soma ? { checksum: soma } : {}), ...(finishedAt ? { finishedAt } : {}), ...(restored ? { restored: true } : {}) }));
|
|
93
|
-
|
|
123
|
+
// AUD-0009 (12/08/2026): a lista PERDE itens por dois caminhos — a troca em modo single e o
|
|
124
|
+
// "substituir" do conflito —, e a URL de objeto da prévia deles ficava viva até o desmonte. O
|
|
125
|
+
// navegador segura o blob inteiro nesse tempo, então trocar a mesma foto dez vezes acumulava dez
|
|
126
|
+
// imagens em memória com uma na tela.
|
|
127
|
+
// Revogado AQUI, num lugar só, porque `emit` é por onde toda substituição passa. Corrigir em
|
|
128
|
+
// cada chamador é exatamente o que a regra "quem MAIS tem esse problema?" existe para impedir —
|
|
129
|
+
// e por isso o `remove` deixou de revogar por conta própria: ele passa por aqui.
|
|
130
|
+
const emit = (next) => {
|
|
131
|
+
const idsNovos = new Set(next.map(e => e.id));
|
|
132
|
+
for (const e of filesRef.current)
|
|
133
|
+
if (e.preview && !idsNovos.has(e.id)) {
|
|
134
|
+
URL.revokeObjectURL(e.preview);
|
|
135
|
+
previas.current.delete(e.preview);
|
|
136
|
+
}
|
|
137
|
+
filesRef.current = next;
|
|
138
|
+
setFiles(next);
|
|
139
|
+
onFilesChange?.(next.flatMap(f => f.file ? [f.file] : []));
|
|
140
|
+
onQueueChange?.(paraFila(next));
|
|
141
|
+
};
|
|
94
142
|
const patch = (fid, p) => setFiles(prev => { const next = prev.map(e => e.id === fid ? { ...e, ...p } : e); filesRef.current = next; onQueueChange?.(paraFila(next)); return next; });
|
|
95
143
|
const abortId = (fid) => controllers.current.get(fid)?.abort();
|
|
96
144
|
// Cada arquivo envia por conta própria (promessa independente + catch por arquivo):
|
|
@@ -149,7 +197,7 @@ export function FileInput({ accept, maxSize, multiple, onFilesChange, upload, la
|
|
|
149
197
|
novosConflitos.push({ file, existente: jaTem.id });
|
|
150
198
|
continue;
|
|
151
199
|
}
|
|
152
|
-
ok.push({ id:
|
|
200
|
+
ok.push({ id: novoId(), file, name: file.name, bytes: file.size, status: "pending", progress: 0 });
|
|
153
201
|
}
|
|
154
202
|
if (novosConflitos.length)
|
|
155
203
|
setConflitos(c => [...c, ...novosConflitos]);
|
|
@@ -192,7 +240,7 @@ export function FileInput({ accept, maxSize, multiple, onFilesChange, upload, la
|
|
|
192
240
|
// ainda está em `files` quando esta função roda — o React não re-renderizou — e ler
|
|
193
241
|
// a closure deixava os DOIS na fila. Foi o teste que achou.
|
|
194
242
|
const aceitarConflito = (file, base = files) => {
|
|
195
|
-
const e = { id:
|
|
243
|
+
const e = { id: novoId(), file, name: file.name, bytes: file.size, status: "pending", progress: 0 };
|
|
196
244
|
emit([...base, e]);
|
|
197
245
|
if (preview && ehImagem(file)) {
|
|
198
246
|
const u = URL.createObjectURL(file);
|
|
@@ -216,10 +264,10 @@ export function FileInput({ accept, maxSize, multiple, onFilesChange, upload, la
|
|
|
216
264
|
}
|
|
217
265
|
aceitarConflito(c.file);
|
|
218
266
|
};
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
267
|
+
// a revogação da prévia saiu daqui: quem revoga é o `emit`, para todo item que a lista perder.
|
|
268
|
+
// Duplicar a regra em cada chamador é como o AUD-0009 nasceu — o `remove` revogava e os outros
|
|
269
|
+
// dois caminhos de substituição, não.
|
|
270
|
+
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.name}` : ""); };
|
|
223
271
|
// G6: "terminou" é não haver mais nada em voo nem esperando — pausado conta como em
|
|
224
272
|
// voo, porque a pessoa ainda vai retomar.
|
|
225
273
|
const emVoo = files.some(f => f.status === "uploading" || f.status === "paused" || f.status === "pending");
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import React, { type ReactNode } from "react";
|
|
2
|
+
import type { AvatarSize } from "./markup.js";
|
|
3
|
+
export declare function Avatar({ src, alt, fallback, size, className }: {
|
|
4
|
+
src?: string;
|
|
5
|
+
alt?: string;
|
|
6
|
+
fallback?: ReactNode;
|
|
7
|
+
size?: AvatarSize;
|
|
8
|
+
className?: string;
|
|
9
|
+
}): React.JSX.Element;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx } 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 { cx } from "./internal.js";
|
|
8
|
+
export function Avatar({ src, alt = "", fallback, size = "md", className }) {
|
|
9
|
+
const [falhou, setFalhou] = React.useState(false);
|
|
10
|
+
React.useEffect(() => { setFalhou(false); }, [src]);
|
|
11
|
+
return _jsx("span", { className: cx("avatar", size !== "md" && `avatar-${size}`, className), children: src && !falhou ? _jsx("img", { src: src, alt: alt, onError: () => setFalhou(true) }) : fallback });
|
|
12
|
+
}
|
package/dist/identity.d.ts
CHANGED
|
@@ -1,17 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
export type AvatarSize
|
|
3
|
-
export declare function Avatar({ src, alt, fallback, size, className }: {
|
|
4
|
-
src?: string;
|
|
5
|
-
alt?: string;
|
|
6
|
-
fallback?: ReactNode;
|
|
7
|
-
size?: AvatarSize;
|
|
8
|
-
className?: string;
|
|
9
|
-
}): React.JSX.Element;
|
|
10
|
-
export declare function AvatarGroup({ children, max, total, label, size, className }: {
|
|
11
|
-
children: ReactNode;
|
|
12
|
-
max?: number;
|
|
13
|
-
total?: number;
|
|
14
|
-
label?: string;
|
|
15
|
-
size?: AvatarSize;
|
|
16
|
-
className?: string;
|
|
17
|
-
}): React.JSX.Element;
|
|
1
|
+
export * from "./identity-client.js";
|
|
2
|
+
export { AvatarGroup, type AvatarSize } from "./markup.js";
|
package/dist/identity.js
CHANGED
|
@@ -1,25 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
//
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
import React from "react";
|
|
7
|
-
import { cx } from "./internal.js";
|
|
8
|
-
export function Avatar({ src, alt = "", fallback, size = "md", className }) {
|
|
9
|
-
const [falhou, setFalhou] = React.useState(false);
|
|
10
|
-
React.useEffect(() => { setFalhou(false); }, [src]);
|
|
11
|
-
return _jsx("span", { className: cx("avatar", size !== "md" && `avatar-${size}`, className), children: src && !falhou ? _jsx("img", { src: src, alt: alt, onError: () => setFalhou(true) }) : fallback });
|
|
12
|
-
}
|
|
13
|
-
// AvatarGroup: a pilha sobreposta com o "+N" no fim. A sobreposição já existia no core desde
|
|
14
|
-
// sempre (`.avatar-group .avatar`), e o container nunca teve regra — a peça filha ficou, a mãe
|
|
15
|
-
// não, exatamente como no Stepper.
|
|
16
|
-
// `max` corta a lista e o excedente vira UM avatar de contagem, que é onde as referências
|
|
17
|
-
// convergem. Fora de propósito: o `renderSurplus` de uma delas (callback para desenhar o "+N"),
|
|
18
|
-
// que é configuração para um problema que ninguém teve ainda.
|
|
19
|
-
// `role="group"` + nome: uma fila de retratos sem nome não diz de quem é.
|
|
20
|
-
export function AvatarGroup({ children, max, total, label, size = "md", className }) {
|
|
21
|
-
const itens = React.Children.toArray(children);
|
|
22
|
-
const mostra = max ? itens.slice(0, max) : itens;
|
|
23
|
-
const resto = (total ?? itens.length) - mostra.length;
|
|
24
|
-
return _jsxs("div", { role: "group", "aria-label": label, className: cx("avatar-group", className), children: [mostra, resto > 0 && _jsxs("span", { className: cx("avatar", "avatar-count", size !== "md" && `avatar-${size}`), "aria-hidden": "true", children: ["+", resto] })] });
|
|
25
|
-
}
|
|
1
|
+
// VITRINE da categoria — é este arquivo que `@aurea-uds/react/identity` resolve. Sem
|
|
2
|
+
// `"use client"` de propósito: a diretiva contamina o módulo inteiro e faria a marcação pura
|
|
3
|
+
// chegar como cliente por vizinhança (ADR-0026; o check 26b reprova quem a puser de volta aqui).
|
|
4
|
+
export * from "./identity-client.js";
|
|
5
|
+
export { AvatarGroup } from "./markup.js";
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
export { cx, defaultStrings, ptBR, defaultSpriteUrl, gridStateToParams, gridStateFromParams, universalStates, stateSeverity, type AureaStrings, type GridState, type UniversalState } from "./pure.js";
|
|
1
|
+
export { cx, defaultStrings, ptBR, defaultSpriteUrl, gridStateToParams, gridStateFromParams, screenStateToParams, screenStateFromParams, universalStates, stateSeverity, type AureaStrings, type GridState, type ScreenState, type UniversalState } from "./pure.js";
|
|
2
2
|
export { useAureaStrings, useSpriteUrl } from "./internal.js";
|
|
3
|
+
export { Card, Stack, Cluster, Grid, KPI, DataList, Timeline, Prose, Badge, Progress, Skeleton, AvatarGroup, LogStream, MediaPlayerShell, Topbar, Kbd, Select, Textarea, Checkbox, Radio, Switch, Range, type BadgeVariant, type AvatarSize, type TopbarVariant } from "./markup.js";
|
|
3
4
|
export * from "./system.js";
|
|
4
5
|
export * from "./actions.js";
|
|
5
6
|
export * from "./feedback.js";
|
package/dist/index.js
CHANGED
|
@@ -8,11 +8,14 @@
|
|
|
8
8
|
// porta de sempre: `import {Button} from "@aurea-uds/react"` continua funcionando e traz tudo
|
|
9
9
|
// que não custa dependência.
|
|
10
10
|
//
|
|
11
|
-
//
|
|
11
|
+
// SEIS MÓDULOS DE MOTOR OPCIONAL NÃO ENTRAM AQUI:
|
|
12
12
|
// CodeEditor → "@aurea-uds/react/code-editor" (CodeMirror, 6 peers opcionais)
|
|
13
13
|
// DataGrid → "@aurea-uds/react/data-grid" (@tanstack/react-table)
|
|
14
14
|
// QRCode → "@aurea-uds/react/qrcode" (qr)
|
|
15
|
-
//
|
|
15
|
+
// Calendar → "@aurea-uds/react/calendar" (react-day-picker)
|
|
16
|
+
// Chart → "@aurea-uds/react/chart" (recharts)
|
|
17
|
+
// DependencyGraph → "@aurea-uds/react/graph" (@xyflow/react)
|
|
18
|
+
// Se estivessem neste barril, importar um Button carregaria um peer opcional — e a dependência
|
|
16
19
|
// que não estivesse instalada quebraria a importação. O peso é real, então a fronteira é real.
|
|
17
20
|
// SEM `"use client"`, e é o ponto da Parte A: este barril é um módulo de SERVIDOR. Reexportar
|
|
18
21
|
// componente de um módulo com a diretiva é o caminho suportado — quem importa daqui de um
|
|
@@ -25,8 +28,21 @@
|
|
|
25
28
|
// DataGrid, de propósito: quem lê a URL num framework de RSC é o SERVIDOR, e o
|
|
26
29
|
// subpath tem a diretiva. Vindo daqui — via `pure.js`, que não tem — a função é
|
|
27
30
|
// chamável dos dois lados. É a mesma razão do `cx`, uma camada acima.
|
|
28
|
-
export { cx, defaultStrings, ptBR, defaultSpriteUrl, gridStateToParams, gridStateFromParams, universalStates, stateSeverity } from "./pure.js";
|
|
31
|
+
export { cx, defaultStrings, ptBR, defaultSpriteUrl, gridStateToParams, gridStateFromParams, screenStateToParams, screenStateFromParams, universalStates, stateSeverity } from "./pure.js";
|
|
29
32
|
export { useAureaStrings, useSpriteUrl } from "./internal.js";
|
|
33
|
+
// ── A LINHA DO ITEM O1, e ela é o item inteiro ───────────────────────────────────────────────
|
|
34
|
+
// Estes 22 vêm do `markup.js`, que NÃO tem a diretiva, e por isso chegam como componentes de
|
|
35
|
+
// SERVIDOR. Vinham dos módulos de categoria abaixo, e de lá chegavam como cliente: `Card` é uma
|
|
36
|
+
// <div>, e importá-lo embarcava 9 módulos e 118,5 KB — `node scripts/measure-boundary.mjs`.
|
|
37
|
+
//
|
|
38
|
+
// ESTA LINHA PRECISA SER EXPLÍCITA, e o que acontece sem ela foi MEDIDO, não deduzido — a
|
|
39
|
+
// primeira versão deste comentário dizia outra coisa e a prova contra o defeito a derrubou.
|
|
40
|
+
// Tirando um nome daqui: o `tsc` fica VERDE, o componente continua existindo e os testes
|
|
41
|
+
// continuam passando, porque o módulo de categoria abaixo o reexporta (é o que mantém o subpath
|
|
42
|
+
// inteiro). O que muda é de ONDE ele vem — do módulo com a diretiva —, e aí ele volta a chegar
|
|
43
|
+
// como referência de CLIENTE e os 118,5 KB voltam com ele. Não é um erro: é o ganho do item O1
|
|
44
|
+
// se desfazendo em silêncio. É isso, exatamente isso, que o check 35 pega.
|
|
45
|
+
export { Card, Stack, Cluster, Grid, KPI, DataList, Timeline, Prose, Badge, Progress, Skeleton, AvatarGroup, LogStream, MediaPlayerShell, Topbar, Kbd, Select, Textarea, Checkbox, Radio, Switch, Range } from "./markup.js";
|
|
30
46
|
export * from "./system.js";
|
|
31
47
|
export * from "./actions.js";
|
|
32
48
|
export * from "./feedback.js";
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import React, { type HTMLAttributes, type InputHTMLAttributes, 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<HTMLDivElement>, RefAttributes<HTMLDivElement> {
|
|
5
|
+
label: string;
|
|
6
|
+
hint?: ReactNode;
|
|
7
|
+
error?: ReactNode;
|
|
8
|
+
name?: string;
|
|
9
|
+
}
|
|
10
|
+
export declare function Field({ label, hint, error, children, className, id, name, ...props }: FieldProps): React.JSX.Element;
|
|
11
|
+
export declare function Form({ errors, onSubmit, children, className, ...props }: Omit<React.FormHTMLAttributes<HTMLFormElement>, "onSubmit"> & RefAttributes<HTMLFormElement> & {
|
|
12
|
+
errors?: Record<string, string | string[]>;
|
|
13
|
+
onSubmit?: (values: Record<string, unknown>) => void;
|
|
14
|
+
}): React.JSX.Element;
|
|
15
|
+
export interface InputProps extends InputHTMLAttributes<HTMLInputElement>, RefAttributes<HTMLInputElement> {
|
|
16
|
+
formatOnBlur?: (value: string) => string;
|
|
17
|
+
}
|
|
18
|
+
export declare const Input: React.ForwardRefExoticComponent<Omit<InputProps, "ref"> & RefAttributes<HTMLInputElement>>;
|
|
19
|
+
export declare function SearchField({ icon, className, ...props }: InputHTMLAttributes<HTMLInputElement> & RefAttributes<HTMLInputElement> & {
|
|
20
|
+
icon?: IconName;
|
|
21
|
+
}): React.JSX.Element;
|
|
22
|
+
export declare function NumberField({ value, defaultValue, onValueChange, min, max, step, disabled, readOnly, required, label, id, name, format, locale, className }: {
|
|
23
|
+
value?: number | null;
|
|
24
|
+
defaultValue?: number;
|
|
25
|
+
onValueChange?: (v: number | null) => void;
|
|
26
|
+
min?: number;
|
|
27
|
+
max?: number;
|
|
28
|
+
step?: number;
|
|
29
|
+
disabled?: boolean;
|
|
30
|
+
readOnly?: boolean;
|
|
31
|
+
required?: boolean;
|
|
32
|
+
label?: string;
|
|
33
|
+
id?: string;
|
|
34
|
+
name?: string;
|
|
35
|
+
format?: Intl.NumberFormatOptions;
|
|
36
|
+
locale?: string;
|
|
37
|
+
className?: string;
|
|
38
|
+
}): React.JSX.Element;
|
|
39
|
+
export declare function OTPField({ length, value, defaultValue, onValueChange, mask, disabled, required, label, id, className }: {
|
|
40
|
+
length: number;
|
|
41
|
+
value?: string;
|
|
42
|
+
defaultValue?: string;
|
|
43
|
+
onValueChange?: (v: string) => void;
|
|
44
|
+
mask?: boolean;
|
|
45
|
+
disabled?: boolean;
|
|
46
|
+
required?: boolean;
|
|
47
|
+
label?: string;
|
|
48
|
+
id?: string;
|
|
49
|
+
className?: string;
|
|
50
|
+
}): React.JSX.Element;
|
|
51
|
+
export declare function SegmentedControl({ items, value, onChange, label }: {
|
|
52
|
+
items: Array<{
|
|
53
|
+
value: string;
|
|
54
|
+
label: ReactNode;
|
|
55
|
+
}>;
|
|
56
|
+
value: string;
|
|
57
|
+
onChange: (v: string) => void;
|
|
58
|
+
label?: string;
|
|
59
|
+
}): React.JSX.Element;
|
|
60
|
+
export interface ComboboxOption {
|
|
61
|
+
value: string;
|
|
62
|
+
label: string;
|
|
63
|
+
}
|
|
64
|
+
export declare function Combobox({ items, value, onValueChange, placeholder, label, disabled, id, className, "aria-describedby": descrito, "aria-invalid": invalido }: {
|
|
65
|
+
items: ComboboxOption[];
|
|
66
|
+
value?: ComboboxOption | null;
|
|
67
|
+
onValueChange?: (v: ComboboxOption | null) => void;
|
|
68
|
+
placeholder?: string;
|
|
69
|
+
label?: ReactNode;
|
|
70
|
+
disabled?: boolean;
|
|
71
|
+
id?: string;
|
|
72
|
+
className?: string;
|
|
73
|
+
"aria-describedby"?: string;
|
|
74
|
+
"aria-invalid"?: boolean | "true" | "false";
|
|
75
|
+
}): React.JSX.Element;
|
|
76
|
+
export interface ComboboxOptGroup {
|
|
77
|
+
label: string;
|
|
78
|
+
items: ComboboxOption[];
|
|
79
|
+
}
|
|
80
|
+
export declare function MultiCombobox({ items, value, onValueChange, onInputChange, loading, placeholder, label, disabled, id, className, "aria-describedby": descrito, "aria-invalid": invalido }: {
|
|
81
|
+
items: ComboboxOption[] | ComboboxOptGroup[];
|
|
82
|
+
value?: ComboboxOption[];
|
|
83
|
+
onValueChange?: (v: ComboboxOption[]) => void;
|
|
84
|
+
onInputChange?: (query: string) => void;
|
|
85
|
+
loading?: boolean;
|
|
86
|
+
placeholder?: string;
|
|
87
|
+
label?: ReactNode;
|
|
88
|
+
disabled?: boolean;
|
|
89
|
+
id?: string;
|
|
90
|
+
className?: string;
|
|
91
|
+
"aria-describedby"?: string;
|
|
92
|
+
"aria-invalid"?: boolean | "true" | "false";
|
|
93
|
+
}): React.JSX.Element;
|
|
94
|
+
export interface EditorBlock {
|
|
95
|
+
id: string;
|
|
96
|
+
kind?: ReactNode;
|
|
97
|
+
children: ReactNode;
|
|
98
|
+
}
|
|
99
|
+
export interface BlockEditorProps extends Omit<HTMLAttributes<HTMLOListElement>, "onReorder">, RefAttributes<HTMLOListElement> {
|
|
100
|
+
blocks: EditorBlock[];
|
|
101
|
+
onReorder: (from: number, to: number) => void;
|
|
102
|
+
onRemove?: (index: number) => void;
|
|
103
|
+
label?: string;
|
|
104
|
+
}
|
|
105
|
+
export declare function BlockEditor({ blocks, onReorder, onRemove, label, className, ...props }: BlockEditorProps): React.JSX.Element;
|