@aurea-uds/react 0.18.0 → 0.22.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/dist/data-grid.js CHANGED
@@ -25,8 +25,8 @@ import { IconButton } from "./actions.js";
25
25
  //
26
26
  // Por que os nomes `manualSorting`/`manualFiltering`/`manualPagination` são os DO
27
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
28
+ // 08/08/2026 e NENHUMA expõe API controlada — uma guarda tudo em useState
29
+ // dentro do exemplo, outra põe a ordenação num átomo de estado global e a terceira
30
30
  // é apresentação. Não havia anatomia para copiar; o vocabulário veio do contrato
31
31
  // do motor, que é o que o consumidor já lê na documentação dele.
32
32
  //
@@ -316,7 +316,7 @@ export function DataGrid({ data, columns, label, filterable, pageSize, selectabl
316
316
  };
317
317
  const rove = (r, c) => eff.r === r && eff.c === c ? 0 : -1;
318
318
  const sortIcon = (dir) => dir === "asc" ? "caret-up" : dir === "desc" ? "caret-down" : "caret-up-down";
319
- // F5: a barra de lote. Duas referências independentes (Activepieces e Kaneo) chegam
319
+ // F5: a barra de lote. Duas referências independentes chegam
320
320
  // à MESMA anatomia — contagem, divisória, ações, e um jeito de limpar —, então é
321
321
  // ela que entra. O que não entra é o resto das duas: barra `position:fixed` sobre a
322
322
  // janela inteira (decisão da APLICAÇÃO, não de um componente que o consumidor põe
@@ -26,6 +26,7 @@ export declare function matchesAccept(file: {
26
26
  name: string;
27
27
  type: string;
28
28
  }, accept?: string): boolean;
29
+ export declare function formatSize(bytes: number): string;
29
30
  export declare function FileInput({ accept, maxSize, multiple, onFilesChange, upload, label, hint, id, className, initialQueue, onQueueChange, preview, checksum }: {
30
31
  accept?: string;
31
32
  maxSize?: number;
@@ -57,7 +57,9 @@ async function somaSha256(file) {
57
57
  // nova, zero branch novo.
58
58
  const ATIVOS = new Set(["pending", "uploading", "paused"]);
59
59
  const restaurar = (q) => ({ ...q, restored: true, status: ATIVOS.has(q.status) ? "canceled" : q.status });
60
- function formatSize(bytes) {
60
+ // Exportada para dentro do pacote (não sai no barril): o `MessageComposer` mostra o tamanho do
61
+ // anexo no mesmo formato da fila daqui (AN-03).
62
+ export function formatSize(bytes) {
61
63
  if (bytes < 1024)
62
64
  return `${bytes} B`;
63
65
  const units = ["KB", "MB", "GB"];
package/dist/graph.js CHANGED
@@ -13,14 +13,14 @@ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-run
13
13
  //
14
14
  // ── O MOTOR, e por que este ──────────────────────────────────────────────────────────────
15
15
  // Autorizado pelo Victor em 09/08/2026, e a autorização foi RECONFIRMADA por medição depois de
16
- // eu mesmo levantar dúvida. A dúvida era boa e a resposta desfez: o `trace-graph-view/` do
17
- // langfuse — a referência mais próxima do domínio — desenha `<div>` posicionados por ELK e NÃO
16
+ // eu mesmo levantar dúvida. A dúvida era boa e a resposta desfez: a vista de grafo de rastreio
17
+ // da referência mais próxima do domínio desenha `<div>` posicionados por um motor de layout de terceiro e NÃO
18
18
  // usa React Flow, e o React Flow não faz layout nenhum, então adotá-lo não entrega um grafo
19
- // pronto. O que decidiu foi o ALVO, que o Victor nomeou: uma aplicação tipo n8n. Aí a conta
19
+ // pronto. O que decidiu foi o ALVO, que o Victor nomeou: uma aplicação de automação de fluxos. Aí a conta
20
20
  // vira outra, e ela é medida:
21
- // • a `activepieces-main`, que já está em `Referencia/` e é concorrente direta do n8n, usa
22
- // `@xyflow/react` 12.3.5 — está no `package.json` dela;
23
- // • o próprio n8n usa **Vue Flow**, o irmão Vue do React Flow, da mesma equipe xyflow.
21
+ // • uma das referências, que já está em `Referencia/` e é concorrente direta do líder desse tipo de aplicação, usa
22
+ // `@xyflow/react` — está no `package.json` dela;
23
+ // • o próprio líder usa o irmão Vue do React Flow, da mesma equipe xyflow.
24
24
  // Grafo de LEITURA não precisa de motor. EDITOR precisa, e o padrão de mercado para este
25
25
  // editor é o xyflow. Registro completo no `REFERENCES.md`.
26
26
  //
@@ -50,7 +50,7 @@ import { cx, useAureaStrings } from "./internal.js";
50
50
  //
51
51
  // ponytail: sem minimização de cruzamento de arestas — grafo denso vai desenhar linhas se
52
52
  // cruzando, e isso é aceitável para dependência (dezenas de nós), não para mil. O caminho de
53
- // subida é o `elkjs`, que é o que o langfuse usa, e ele é DEPENDÊNCIA NOVA: entra com
53
+ // subida é uma biblioteca de layout automático de grafos, a que a referência usa, e ela é DEPENDÊNCIA NOVA: entra com
54
54
  // autorização, não de contrabando.
55
55
  const LARGURA = 180, ALTURA = 52, VAO_X = 90, VAO_Y = 24;
56
56
  // O VÃO ENTRE COLUNAS CRESCE COM O RÓTULO (A-09, 23/09/2026). Com o rótulo legível, a primeira
@@ -112,12 +112,12 @@ function dispor(nodes, edges) {
112
112
  return { posicoes, extensao: { larg: colunas * LARGURA + (colunas - 1) * vaoX, alt: linhas * ALTURA + (linhas - 1) * VAO_Y } };
113
113
  }
114
114
  // O nó é NOSSO, e é o ponto em que este componente deixa de ser "React Flow com outra cor".
115
- // O `GraphNode.tsx` do langfuse declara o motivo em uma linha — *"Real-HTML accessibility (the
116
- // win over the old canvas renderer)"* —: nó desenhado em canvas não existe para o teclado nem
115
+ // O componente de nó da referência declara o motivo em uma linha — a acessibilidade de HTML de
116
+ // verdade, que é a vantagem sobre o antigo desenho em canvas —: nó desenhado em canvas não existe para o teclado nem
117
117
  // para o leitor de tela. Aqui ele é `<button>` de verdade quando dá para selecionar.
118
118
  //
119
- // O que NÃO entrou: as DEZ cores por tipo de nó da referência (`AGENT` roxo, `TOOL` laranja,
120
- // `GENERATION` magenta…). É a quarta vez que esta parte recusa a mesma coisa. `kind` sai como
119
+ // O que NÃO entrou: as DEZ cores por tipo de nó da referência (agente roxo, ferramenta laranja,
120
+ // geração magenta…). É a quarta vez que esta parte recusa a mesma coisa. `kind` sai como
121
121
  // TEXTO, que é legível também por quem não separa as cores.
122
122
  function NoAurea({ data }) {
123
123
  const d = data;
package/dist/index.d.ts CHANGED
@@ -2,7 +2,7 @@ export { cx, defaultStrings, ptBR, defaultSpriteUrl, gridStateToParams, gridStat
2
2
  export { useAureaStrings, useSpriteUrl, useTheme, useDensity } from "./internal.js";
3
3
  export { type Responsive, type Breakpoint, type ContainerBreakpoint, type Orientation, ESCALA, ESCALA_CONTAINER } from "./pure.js";
4
4
  export { ContainerScope, useValorResponsivo } from "./responsivo-runtime.js";
5
- export { AspectRatio, InputGroup, InputGroupAddon, Label, Card, Stack, Cluster, Grid, KPI, DataList, Timeline, Prose, Text, Heading, Paragraph, Code, Badge, Progress, Skeleton, AvatarGroup, LogStream, MediaPlayerShell, Topbar, Kbd, Textarea, Checkbox, Radio, Switch, Range, formatBadgeCount, type BadgeVariant, type BadgeEmphasis, type BadgeSize, type BadgePlacement, type BadgeProps, type ProgressProps, type ProgressTone, type AvatarSize, type TopbarVariant, type TextProps, type HeadingProps, type ParagraphProps, type CodeProps, type TypographyType, type TypographyColor, type TypographyWeight, type TypographyAlign, type HeadingLevel, type ParagraphSize } from "./markup.js";
5
+ export { AspectRatio, InputGroup, InputGroupAddon, Label, Card, Stack, Cluster, Grid, Container, Section, KPI, DataList, Timeline, Prose, Text, Heading, Paragraph, Code, Badge, Progress, Skeleton, AvatarGroup, LogStream, MediaPlayerShell, Topbar, Kbd, Textarea, Checkbox, Radio, Switch, Range, formatBadgeCount, type BadgeVariant, type BadgeEmphasis, type BadgeSize, type BadgePlacement, type BadgeProps, type ProgressProps, type ProgressTone, type AvatarSize, type TopbarVariant, type TextProps, type HeadingProps, type ParagraphProps, type CodeProps, type TypographyType, type TypographyColor, type TypographyWeight, type TypographyAlign, type HeadingLevel, type ParagraphSize } from "./markup.js";
6
6
  export * from "./system.js";
7
7
  export * from "./actions.js";
8
8
  export * from "./feedback.js";
package/dist/index.js CHANGED
@@ -51,7 +51,7 @@ export { ContainerScope, useValorResponsivo } from "./responsivo-runtime.js";
51
51
  // inteiro). O que muda é de ONDE ele vem — do módulo com a diretiva —, e aí ele volta a chegar
52
52
  // como referência de CLIENTE e os 118,5 KB voltam com ele. Não é um erro: é o ganho do item O1
53
53
  // se desfazendo em silêncio. É isso, exatamente isso, que o check 35 pega.
54
- export { AspectRatio, InputGroup, InputGroupAddon, Label, Card, Stack, Cluster, Grid, KPI, DataList, Timeline, Prose, Text, Heading, Paragraph, Code, Badge, Progress, Skeleton, AvatarGroup, LogStream, MediaPlayerShell, Topbar, Kbd, Textarea, Checkbox, Radio, Switch, Range, formatBadgeCount } from "./markup.js";
54
+ export { AspectRatio, InputGroup, InputGroupAddon, Label, Card, Stack, Cluster, Grid, Container, Section, KPI, DataList, Timeline, Prose, Text, Heading, Paragraph, Code, Badge, Progress, Skeleton, AvatarGroup, LogStream, MediaPlayerShell, Topbar, Kbd, Textarea, Checkbox, Radio, Switch, Range, formatBadgeCount } from "./markup.js";
55
55
  export * from "./system.js";
56
56
  export * from "./actions.js";
57
57
  export * from "./feedback.js";
@@ -11,7 +11,7 @@ export interface FieldProps extends HTMLAttributes<HTMLDivElement>, RefAttribute
11
11
  error?: ReactNode;
12
12
  /** B-12 (24/09/2026): texto de ajuda que precisa de uma FRASE — mora embaixo do controle, onde
13
13
  * cabe, e não divide a linha do rótulo. O `hint` continua o que é: nota curta ao lado do rótulo
14
- * ("opcional", "em MB"). É o lugar do `Description` do HeroUI e da `description` que o
14
+ * ("opcional", "em MB"). É o lugar da descrição da referência e da `description` que o
15
15
  * `Checkbox` desta casa já tem. Entra no `aria-describedby` na ordem da tela: hint, descrição,
16
16
  * erro. */
17
17
  description?: ReactNode;
@@ -68,7 +68,7 @@ export declare function OTPField({ length, value, defaultValue, onValueChange, m
68
68
  id?: string;
69
69
  className?: string;
70
70
  }): React.JSX.Element;
71
- export declare function SegmentedControl({ items, value, onChange, label }: {
71
+ export declare function SegmentedControl({ items, value, onChange, label, fullWidth }: {
72
72
  items: Array<{
73
73
  value: string;
74
74
  label: ReactNode;
@@ -76,6 +76,7 @@ export declare function SegmentedControl({ items, value, onChange, label }: {
76
76
  value: string;
77
77
  onChange: (v: string) => void;
78
78
  label?: string;
79
+ fullWidth?: boolean;
79
80
  }): React.JSX.Element;
80
81
  export interface ComboboxOption {
81
82
  value: string;
@@ -133,9 +133,9 @@ export function Field({ label, hint, description, error, children, className, id
133
133
  // Form (M1): o formulário que EXIBE erro, e não o que decide o que é erro.
134
134
  //
135
135
  // A divisão saiu de pesquisa e da medição dos consumidores (13/08/2026). O padrão de mercado é
136
- // `react-hook-form` + `zod`, e é o que o shadcn embrulha — mas embrulhar isso aqui obrigaria TODO
137
- // projeto que usa a Aurea a usar react-hook-form, inclusive os que já validam de outro jeito (um
138
- // consumidor real valida com zod em server action, sem biblioteca de formulário no cliente).
136
+ // uma biblioteca de formulário com uma de esquema, e é o que uma das referências embrulha — mas embrulhar isso aqui obrigaria TODO
137
+ // projeto que usa a Aurea a usar essa biblioteca de formulário, inclusive os que já validam de outro jeito (um
138
+ // consumidor real valida com a biblioteca de esquema em server action, sem biblioteca de formulário no cliente).
139
139
  // Dependência nova também é decisão do Victor pelo BUILDING.md §3.3, e ela não foi necessária:
140
140
  // o `Form` do Base UI — motor que a Aurea JÁ paga desde a Fase 2 — declara no próprio tipo que
141
141
  // `errors` são "validation errors returned externally, typically after submission by a server or
@@ -232,9 +232,13 @@ export function OTPField({ length, value, defaultValue, onValueChange, mask, dis
232
232
  // é o rádio e o input é `aria-hidden`. Comportamento idêntico: Enter não seleciona, Espaço
233
233
  // seleciona, a seta move E seleciona. O que sai é a camada sintética que o motor punha por cima
234
234
  // da ativação nativa do `<button>`.
235
- export function SegmentedControl({ items, value, onChange, label }) {
235
+ //
236
+ // `fullWidth` (A5, 06/10/2026): o mesmo nome e o mesmo desenho do nativo — o controle ocupa a
237
+ // linha e cada segmento CRESCE a partir do rótulo, sem encolher abaixo dele. É como o grupo
238
+ // de botões alternáveis da referência ocupa a linha.
239
+ export function SegmentedControl({ items, value, onChange, label, fullWidth }) {
236
240
  const s = useAureaStrings();
237
- return _jsx(BaseRadioGroup, { className: "segmented", "aria-label": label ?? s.optionsLabel, value: value, onValueChange: v => onChange(String(v)), children: items.map(i => _jsx(BaseRadio.Root, { value: i.value, className: i.value === value ? "active" : undefined, nativeButton: true, render: _jsx("button", { type: "button" }), children: i.label }, i.value)) });
241
+ return _jsx(BaseRadioGroup, { className: fullWidth ? "segmented segmented-full" : "segmented", "aria-label": label ?? s.optionsLabel, value: value, onValueChange: v => onChange(String(v)), children: items.map(i => _jsx(BaseRadio.Root, { value: i.value, className: i.value === value ? "active" : undefined, nativeButton: true, render: _jsx("button", { type: "button" }), children: i.label }, i.value)) });
238
242
  }
239
243
  const ehAgrupado = (items) => items != null && items.length > 0 && typeof items[0] === "object" && items[0] != null && "items" in items[0];
240
244
  // UM renderizador e UMA lista para os dois combobox: duas cópias da mesma árvore é como a
package/dist/internal.js CHANGED
@@ -1,5 +1,5 @@
1
1
  "use client";
2
- // A diretiva na LINHA 1, como as referências a escrevem (Base UI e shadcn/ui, medidos em
2
+ // A diretiva na LINHA 1, como as referências a escrevem (o Base UI e outra delas, medidos em
3
3
  // 06/08/2026) — Parte A do PLANO-1.0. Este módulo chama `createContext`, que é API só de
4
4
  // cliente: sem a diretiva, `import {Button} from "@aurea-uds/react"` dentro de um componente
5
5
  // de servidor quebrava na hora, e quebrava no consumidor.
@@ -125,8 +125,8 @@ function useGavetaFechaNoDesktop() {
125
125
  // AN-01 (03/10/2026): O BOTÃO DE RECOLHER E A TRILHA SOZINHA. Um consumidor viu numa janela de 1508:
126
126
  // a lateral sempre aberta, sem botão, e diminuir a janela não a recolhia — só virava gaveta abaixo de
127
127
  // 1024. O modo trilha existia (`sidebarCollapsed`, `.sidebar-collapsed`), mas só controlado e sem
128
- // gatilho. A referência é a `Sidebar` do shadcn que o ReUI usa no `c-sidebar-2` (o HeroUI não tem
129
- // moldura de app): estado aberto/recolhido, controlado e não controlado, um gatilho, e o nome em
128
+ // gatilho. A referência é a lateral de uma das referências, que outra delas usa num dos seus exemplos
129
+ // (a referência principal não tem moldura de app): estado aberto/recolhido, controlado e não controlado, um gatilho, e o nome em
130
130
  // dica na trilha. O lugar do botão é o que o Victor marcou: no alto, na junção do menu com o
131
131
  // conteúdo. E a trilha sozinha em tela média vem do pedido — a referência não a tem.
132
132
  //
package/dist/layout.d.ts CHANGED
@@ -1,3 +1,5 @@
1
1
  export * from "./layout-client.js";
2
2
  export { Card, Stack, Cluster, Grid } from "./markup.js";
3
3
  export { AspectRatio } from "./markup.js";
4
+ export { Container, Section } from "./markup.js";
5
+ export type { GridSpan, GridItemProps, ContainerSize, ContainerProps, SectionSurface, SectionSpacing, SectionProps } from "./markup.js";
package/dist/layout.js CHANGED
@@ -5,3 +5,5 @@ export * from "./layout-client.js";
5
5
  export { Card, Stack, Cluster, Grid } from "./markup.js";
6
6
  // PORTADO no merge de 28/08/2026 — sai do markup (servidor), como os demais de marcação.
7
7
  export { AspectRatio } from "./markup.js";
8
+ // GAR-02 a GAR-05 (06/10/2026): a página de site — `Container`, `Section` e o `Grid.Item` com `span`.
9
+ export { Container, Section } from "./markup.js";
package/dist/markup.d.ts CHANGED
@@ -48,7 +48,7 @@ export interface ClusterProps extends DivProps {
48
48
  }
49
49
  export declare function Cluster({ gap, align, justify, wrap, className, ...props }: ClusterProps): React.JSX.Element;
50
50
  /**
51
- * A largura mínima da coluna, por nome — AN-08 (03/10/2026). O HeroUI não tem `Grid`, e nenhum dos
51
+ * A largura mínima da coluna, por nome — AN-08 (03/10/2026). A referência não tem `Grid`, e nenhum dos
52
52
  * quatro números é novo: cada um já mede uma grade ou uma caixa da Aurea (ver `.grid-min-*` no CSS).
53
53
  * `md` é o padrão de sempre.
54
54
  */
@@ -66,6 +66,56 @@ export interface GridProps extends DivProps {
66
66
  columns?: number;
67
67
  }
68
68
  export declare function Grid({ gap, min, columns, className, style, ...props }: GridProps): React.JSX.Element;
69
+ export declare namespace Grid {
70
+ export { GridItem as Item };
71
+ }
72
+ /**
73
+ * Quantas colunas da grade o item ocupa — GAR-03 (06/10/2026). Texto, e não número, porque é o
74
+ * valor de um eixo responsivo, como `size` e `orientation`.
75
+ */
76
+ export type GridSpan = "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" | "10" | "11" | "12";
77
+ export interface GridItemProps extends DivProps {
78
+ /**
79
+ * Quantas colunas o item ocupa (GAR-03). Com `<Grid columns={12}>` é o "7 de 12" de página de
80
+ * site. Responsivo, mobile-first: `{base: "12", viewport: {md: "7"}}` ocupa a linha inteira no
81
+ * estreito e 7 de 12 a partir do `md`. Sem `span`, uma coluna, como qualquer filho de grade.
82
+ */
83
+ span?: Responsive<GridSpan>;
84
+ }
85
+ /** O item da grade que sabe quantas colunas ocupa (GAR-03). Mora em `Grid.Item`, como `Card.Media`. */
86
+ declare function GridItem({ span, className, ...props }: GridItemProps): React.JSX.Element;
87
+ /** A largura máxima do `Container`, pelos nomes da escala de pontos. `xl` (1280) é o padrão. */
88
+ export type ContainerSize = "sm" | "md" | "lg" | "xl" | "2xl" | "full";
89
+ export interface ContainerProps extends HTMLAttributes<HTMLDivElement>, RefAttributes<HTMLDivElement> {
90
+ /** Até onde o conteúdo cresce: `sm` 640 · `md` 768 · `lg` 1024 · `xl` 1280 (padrão) · `2xl` 1536 · `full` sem teto. */
91
+ size?: ContainerSize;
92
+ }
93
+ /**
94
+ * A largura de leitura de uma página de SITE: centrada, com teto e o respiro dos lados que cresce
95
+ * com a tela (16 no estreito, 32 a partir do `md`). É o que vai DENTRO de uma `Section`. O painel
96
+ * de aplicativo continua sendo o `AppShell`.
97
+ */
98
+ export declare function Container({ size, className, ...props }: ContainerProps): React.JSX.Element;
99
+ /** O fundo da faixa: o da página (padrão), o de cartão ou o rebaixado. */
100
+ export type SectionSurface = "background" | "card" | "inset";
101
+ /** O respiro de cima e de baixo: `md` (padrão, 64 e 96 a partir do `md`) ou `sm` (32 e 48). */
102
+ export type SectionSpacing = "sm" | "md";
103
+ export interface SectionProps extends HTMLAttributes<HTMLElement>, RefAttributes<HTMLElement> {
104
+ surface?: SectionSurface;
105
+ spacing?: SectionSpacing;
106
+ /**
107
+ * Um tema só para esta faixa (GAR-05): `"dark"` faz uma faixa escura dentro de uma página clara,
108
+ * e o contrário. Fundo, letra e peças de dentro seguem o tema da faixa, e as regras de tema da
109
+ * página não vazam para dentro dela.
110
+ */
111
+ theme?: "light" | "dark";
112
+ }
113
+ /**
114
+ * A faixa de ponta a ponta de uma página de site (GAR-04): `<section>` com fundo e respiro de site.
115
+ * O conteúdo vai dentro de um `Container`. Dê um nome à faixa (`aria-labelledby` no título dela)
116
+ * quando ela for uma região que o leitor de tela deva listar.
117
+ */
118
+ export declare function Section({ surface, spacing, theme, className, ...props }: SectionProps): React.JSX.Element;
69
119
  export declare function KPI({ label, value, trend, className, ...props }: HTMLAttributes<HTMLDivElement> & RefAttributes<HTMLDivElement> & {
70
120
  label: ReactNode;
71
121
  value: ReactNode;
@@ -126,7 +176,7 @@ export interface BadgeProps extends HTMLAttributes<HTMLSpanElement>, RefAttribut
126
176
  dot?: boolean;
127
177
  leading?: ReactNode;
128
178
  trailing?: ReactNode;
129
- /** Foto redonda no início — o `BadgeWithImage` da referência. */
179
+ /** Foto redonda no início — o selo com imagem da referência. */
130
180
  image?: string;
131
181
  imageAlt?: string;
132
182
  /** Conteúdo numérico. Passa por `max` e some em zero, salvo `showZero`. */
@@ -137,12 +187,12 @@ export interface BadgeProps extends HTMLAttributes<HTMLSpanElement>, RefAttribut
137
187
  fit?: BadgeFit;
138
188
  /** Liga o modo SOBREPOSTO e escolhe o canto. `children` passa a ser o que se decora. */
139
189
  anchor?: BadgePlacement;
140
- /** `circle` recolhe o canto em 14% — é o `overlap` da MUI, para avatar redondo. */
190
+ /** `circle` recolhe o canto em 14% — é a sobreposição circular da referência, para avatar redondo. */
141
191
  anchorShape?: "square" | "circle";
142
192
  /** Esconde sem tirar o filho do lugar. */
143
193
  invisible?: boolean;
144
194
  /** Conteúdo do badge no modo sobreposto (no modo chip, quem manda é `children`).
145
- * Nome da MUI, e não `content`: este colide com o atributo HTML de mesmo nome. */
195
+ * Chama-se assim, e não `content`, porque este colide com o atributo HTML de mesmo nome. */
146
196
  badgeContent?: ReactNode;
147
197
  }
148
198
  export declare function Badge({ variant, emphasis, size, dot, leading, trailing, image, imageAlt, count, max, showZero, fit, anchor, anchorShape, invisible, badgeContent, children, className, ...props }: BadgeProps): React.JSX.Element;
@@ -154,7 +204,7 @@ export interface ProgressProps {
154
204
  value?: number;
155
205
  /** O nome que o leitor de tela anuncia. Não aparece. */
156
206
  label?: string;
157
- /** O texto de apoio, no alto à direita — o `ProgressBar.Output` do HeroUI: velocidade, tempo
207
+ /** O texto de apoio, no alto à direita — a saída de texto da barra de progresso da referência: velocidade, tempo
158
208
  * que falta, bytes. Em texto, vai junto no `aria-valuetext`. */
159
209
  detail?: ReactNode;
160
210
  tone?: ProgressTone;
package/dist/markup.js CHANGED
@@ -57,6 +57,27 @@ export function Grid({ gap, min, columns, className, style, ...props }) {
57
57
  : style;
58
58
  return _jsx("div", { className: cx("grid", gap && gap !== "normal" && `grid-gap-${gap}`, nome && min !== "md" && `grid-min-${min}`, columns != null && "grid-fixed", className), style: vars, ...props });
59
59
  }
60
+ /** O item da grade que sabe quantas colunas ocupa (GAR-03). Mora em `Grid.Item`, como `Card.Media`. */
61
+ function GridItem({ span, className, ...props }) {
62
+ return _jsx("div", { className: cx("grid-item", peleDoEixo("grid-span", span, "", "grid-span"), className), ...props });
63
+ }
64
+ Grid.Item = GridItem;
65
+ /**
66
+ * A largura de leitura de uma página de SITE: centrada, com teto e o respiro dos lados que cresce
67
+ * com a tela (16 no estreito, 32 a partir do `md`). É o que vai DENTRO de uma `Section`. O painel
68
+ * de aplicativo continua sendo o `AppShell`.
69
+ */
70
+ export function Container({ size = "xl", className, ...props }) {
71
+ return _jsx("div", { className: cx("container", size !== "xl" && `container-${size}`, className), ...props });
72
+ }
73
+ /**
74
+ * A faixa de ponta a ponta de uma página de site (GAR-04): `<section>` com fundo e respiro de site.
75
+ * O conteúdo vai dentro de um `Container`. Dê um nome à faixa (`aria-labelledby` no título dela)
76
+ * quando ela for uma região que o leitor de tela deva listar.
77
+ */
78
+ export function Section({ surface = "background", spacing = "md", theme, className, ...props }) {
79
+ return _jsx("section", { "data-theme": theme, className: cx("section", surface !== "background" && `section-${surface}`, spacing !== "md" && `section-spacing-${spacing}`, className), ...props });
80
+ }
60
81
  // ── Data Display ─────────────────────────────────────────────────────────────────────────────
61
82
  export function KPI({ label, value, trend, className, ...props }) { return _jsxs(Card, { className: cx("kpi", className), ...props, children: [_jsx("span", { className: "muted", children: label }), _jsx("strong", { children: value }), trend && _jsx("small", { children: trend })] }); }
62
83
  export function DataList({ items }) { return _jsx("dl", { className: "data-list", children: items.map((i, n) => _jsxs(React.Fragment, { children: [_jsx("dt", { children: i.term }), _jsx("dd", { children: i.value })] }, n)) }); }
@@ -69,11 +90,11 @@ export function Timeline({ items }) { return _jsx("ol", { className: "timeline",
69
90
  export function Prose({ className, ...props }) { return _jsx("div", { className: cx("prose", className), ...props }); }
70
91
  const tipografia = (tipo, { align, color, weight, truncate }, className) => cx("typography", `typography-${tipo}`, color === "muted" && "typography-muted", weight && `typography-weight-${weight}`, align && `typography-align-${align}`, truncate && "typography-truncate", className);
71
92
  export function Text({ type = "body", align, color, weight, truncate, className, ...props }) { return _jsx("span", { className: tipografia(type, { align, color, weight, truncate }, className), ...props }); }
72
- // O nível decide o elemento E o tamanho, como no HeroUI: um `h2` tem a cara de título 2.
93
+ // O nível decide o elemento E o tamanho, como na referência: um `h2` tem a cara de título 2.
73
94
  export function Heading({ level = 1, align, color, weight, truncate, className, ...props }) { const Tag = `h${level}`; return _jsx(Tag, { className: tipografia(`h${level}`, { align, color, weight, truncate }, className), ...props }); }
74
95
  export function Paragraph({ size = "base", align, color, weight, truncate, className, ...props }) { return _jsx("p", { className: tipografia(size === "base" ? "body" : `body-${size}`, { align, color, weight, truncate }, className), ...props }); }
75
96
  export function Code({ align, color, weight, truncate, className, ...props }) { return _jsx("code", { className: tipografia("code", { align, color, weight, truncate }, className), ...props }); }
76
- // `max` com `+` é o idioma universal do contador (MUI, Ant, Material 3). Exportada porque quem
97
+ // `max` com `+` é o idioma universal do contador (três das referências o usam). Exportada porque quem
77
98
  // escreve o nome acessível precisa do MESMO texto — "99+ unread" tem de bater com o que se vê.
78
99
  export function formatBadgeCount(count, max = 99) { return count > max ? `${max}+` : String(count); }
79
100
  export function Badge({ variant = "neutral", emphasis = "soft", size = "md", dot, leading, trailing, image, imageAlt, count, max = 99, showZero, fit = "auto", anchor, anchorShape = "square", invisible, badgeContent, children, className, ...props }) {
@@ -84,15 +105,15 @@ export function Badge({ variant = "neutral", emphasis = "soft", size = "md", dot
84
105
  const chip = _jsxs("span", { className: cx("badge", variant !== "neutral" && `badge-${variant}`, emphasis !== "soft" && `badge-${emphasis}`, size !== "md" && `badge-${size}`, fit === "content" && !anchor && "badge-fit", anchor && `badge-overlay badge-at-${anchor}`, anchor && anchorShape === "circle" && "badge-on-circle", anchor && soPonto && "badge-is-dot", className), ...props, children: [dot && !soPonto && _jsx("span", { className: "badge-dot" }), image && _jsx("img", { className: "badge-image", src: image, alt: imageAlt ?? "" }), leading, miolo, trailing] });
85
106
  if (!anchor)
86
107
  return chip;
87
- // Some em zero por padrão, como a MUI: caixa de entrada zerada não merece um "0" no canto.
108
+ // Some em zero por padrão, como na referência: caixa de entrada zerada não merece um "0" no canto.
88
109
  const escondido = invisible || (count === 0 && !showZero) || (miolo == null && !dot);
89
110
  // `aria-hidden` no contador, e o número vai para o nome de QUEM É DECORADO — regra da própria
90
- // documentação da MUI e das três fontes de acessibilidade lidas em 17/08/2026. Sem isso, o
111
+ // documentação da referência e das três fontes de acessibilidade lidas em 17/08/2026. Sem isso, o
91
112
  // botão do sino é anunciado "sino 8" e ninguém sabe o que é o 8.
92
113
  return _jsxs("span", { className: "badge-anchor", children: [children, !escondido && React.cloneElement(chip, { "aria-hidden": true })] });
93
114
  }
94
115
  // AN-07 (03/10/2026). Tudo novo é acréscimo: com `value` e sem `detail` nem `tone`, a marcação é a
95
- // de antes, mais a classe da caixa de fora. A anatomia é a do `ProgressBar` do HeroUI 3.2.6: a
116
+ // de antes, mais a classe da caixa de fora. A anatomia é a da barra de progresso da referência: a
96
117
  // saída no alto à direita, o trilho embaixo, e sem `aria-valuenow` quando não há total.
97
118
  export function Progress({ value, label, detail, tone = "brand", className }) {
98
119
  const semTotal = value == null || Number.isNaN(value);
@@ -27,11 +27,14 @@ export interface ImageProps extends Omit<React.ImgHTMLAttributes<HTMLImageElemen
27
27
  render?: React.ReactElement;
28
28
  }
29
29
  export declare function Image({ ratio, fit, alt, className, style, render, onError, ...props }: ImageProps): React.JSX.Element;
30
+ export type GalleryItemKind = "image" | "video";
30
31
  export interface GalleryItem {
31
32
  id: string;
32
33
  src: string;
33
34
  alt: string;
34
35
  caption?: ReactNode;
36
+ kind?: GalleryItemKind;
37
+ duration?: number;
35
38
  }
36
39
  export interface GalleryProps extends Omit<HTMLAttributes<HTMLUListElement>, "onSelect">, RefAttributes<HTMLUListElement> {
37
40
  items: GalleryItem[];
@@ -40,8 +43,14 @@ export interface GalleryProps extends Omit<HTMLAttributes<HTMLUListElement>, "on
40
43
  onSelect?: (id: string) => void;
41
44
  zoom?: boolean;
42
45
  ratio?: string;
46
+ selectionMode?: "single" | "multiple";
47
+ selectedIds?: string[];
48
+ onSelectionChange?: (ids: string[]) => void;
49
+ hasMore?: boolean;
50
+ loading?: boolean;
51
+ onReachEnd?: () => void;
43
52
  }
44
- export declare function Gallery({ items, label, selected, onSelect, zoom, ratio, className, ...props }: GalleryProps): React.JSX.Element;
53
+ export declare function Gallery({ items, label, selected, onSelect, zoom, ratio, selectionMode, selectedIds, onSelectionChange, hasMore, loading, onReachEnd, className, ...props }: GalleryProps): React.JSX.Element;
45
54
  export interface CarouselProps extends Omit<HTMLAttributes<HTMLDivElement>, "children">, RefAttributes<HTMLDivElement> {
46
55
  children?: ReactNode;
47
56
  label?: string;
@@ -11,6 +11,8 @@ import { IconButton } from "./actions.js";
11
11
  // A galeria AMPLIA num Dialog, e o Dialog já existe (trava do item L2). O import é para `overlays`,
12
12
  // que como este módulo mora no "resto" do DAG e não importa `media` — não há ciclo.
13
13
  import { Dialog } from "./overlays.js";
14
+ // AN-05: o tempo do vídeo e a linha de "carregando mais" reusam peças que já existem.
15
+ import { Badge, Spinner } from "./feedback.js";
14
16
  // MediaPlayer (Fase 5): headless sobre <video>/<audio> nativos — o MOTOR de mídia é
15
17
  // o browser; o componente só liga estado (play/tempo/buffer/volume/legenda) às
16
18
  // classes .media-* já existentes. Sem CSS estrutural novo: o vídeo preenche o
@@ -143,35 +145,56 @@ export function Image({ ratio, fit, alt, className, style, render, onError, ...p
143
145
  return _jsx("span", { className: cx("image", "image-broken", fit === "contain" && "image-contain", className), style: estilo, role: "img", "aria-label": alt, children: _jsx(Icon, { name: "image" }) });
144
146
  return elemento;
145
147
  }
146
- export function Gallery({ items, label, selected, onSelect, zoom, ratio = "1/1", className, ...props }) {
148
+ export function Gallery({ items, label, selected, onSelect, zoom, ratio = "1/1", selectionMode = "single", selectedIds, onSelectionChange, hasMore, loading, onReachEnd, className, ...props }) {
147
149
  const s = useAureaStrings();
148
150
  const [ampliado, setAmpliado] = React.useState(null);
149
- const interativo = !!onSelect || !!zoom;
151
+ const multipla = selectionMode === "multiple";
152
+ const interativo = !!onSelect || !!zoom || multipla;
150
153
  const aberto = items.find(i => i.id === ampliado);
151
- return _jsxs(_Fragment, { children: [_jsx("ul", { className: cx("gallery", className), "aria-label": label ?? s.galleryLabel, ...props, children: items.map(i => {
154
+ const escolhidos = new Set(selectedIds);
155
+ const alternar = (id) => onSelectionChange?.(escolhidos.has(id) ? (selectedIds ?? []).filter(x => x !== id) : [...(selectedIds ?? []), id]);
156
+ const fimRef = React.useRef(null);
157
+ const avisar = React.useRef(onReachEnd);
158
+ avisar.current = onReachEnd;
159
+ React.useEffect(() => {
160
+ const fim = fimRef.current;
161
+ if (!fim || !hasMore || loading || typeof IntersectionObserver === "undefined")
162
+ return;
163
+ const obs = new IntersectionObserver(entradas => { if (entradas.some(e => e.isIntersecting))
164
+ avisar.current?.(); });
165
+ obs.observe(fim);
166
+ return () => obs.disconnect();
167
+ }, [hasMore, loading, items.length]);
168
+ return _jsxs(_Fragment, { children: [_jsx("ul", { className: cx("gallery", className), "aria-label": label ?? s.galleryLabel, "aria-busy": loading || undefined, ...props, children: items.map(i => {
152
169
  // LEGENDA VISÍVEL TORNA A MINIATURA DECORATIVA, e quem exigiu isso foi o axe, não a
153
170
  // teoria: com `alt` e legenda dizendo a mesma coisa, ele reprova `image-redundant-alt` e o
154
171
  // leitor de tela anuncia o texto DUAS vezes seguidas. É a regra de figura com legenda do
155
172
  // WAI — quando o texto ao lado já diz, a imagem entra com `alt=""`. O `alt` de verdade não
156
173
  // se perde: ele continua nomeando a foto AMPLIADA, que é onde não há legenda ao lado.
157
- const miolo = _jsxs(_Fragment, { children: [_jsx(Image, { src: i.src, alt: i.caption != null ? "" : i.alt, ratio: ratio }), i.caption != null && _jsx("span", { className: "gallery-caption", children: i.caption })] });
174
+ const foto = _jsx(Image, { src: i.src, alt: i.caption != null ? "" : i.alt, ratio: ratio });
175
+ const video = i.kind === "video";
176
+ const miolo = _jsxs(_Fragment, { children: [video
177
+ ? _jsxs("span", { className: "gallery-media", children: [foto, _jsxs(Badge, { className: "gallery-duration", children: [_jsx(Icon, { name: "play", size: "sm" }), _jsx("span", { className: "sr-only", children: ` ${s.galleryVideo}, ` }), i.duration != null && clockTime(i.duration)] })] })
178
+ : foto, i.caption != null && _jsx("span", { className: "gallery-caption", children: i.caption })] });
179
+ const marcado = multipla ? escolhidos.has(i.id) : i.id === selected;
158
180
  return _jsx("li", { className: "gallery-item", children: interativo
159
- ? _jsx("button", { type: "button", className: cx("gallery-tile", i.id === selected && "is-selected"), "aria-current": i.id === selected ? "true" : undefined, onClick: () => { onSelect?.(i.id); if (zoom)
160
- setAmpliado(i.id); }, children: miolo })
181
+ ? (multipla
182
+ ? _jsx("button", { type: "button", className: cx("gallery-tile", marcado && "is-selected"), "aria-pressed": marcado, onClick: () => alternar(i.id), children: miolo })
183
+ : _jsx("button", { type: "button", className: cx("gallery-tile", marcado && "is-selected"), "aria-current": marcado ? "true" : undefined, onClick: () => { onSelect?.(i.id); if (zoom)
184
+ setAmpliado(i.id); }, children: miolo }))
161
185
  : miolo }, i.id);
162
- }) }), zoom && _jsx(Dialog, { open: !!aberto, title: aberto ? (aberto.caption ?? aberto.alt) : "", onClose: () => setAmpliado(null), children: aberto && _jsx(Image, { src: aberto.src, alt: aberto.alt, fit: "contain" }) })] });
186
+ }) }), hasMore && _jsx("div", { ref: fimRef, className: "gallery-more", children: loading && _jsx(Spinner, {}) }), zoom && !multipla && _jsx(Dialog, { open: !!aberto, title: aberto ? (aberto.caption ?? aberto.alt) : "", onClose: () => setAmpliado(null), children: aberto && _jsx(Image, { src: aberto.src, alt: aberto.alt, fit: "contain" }) })] });
163
187
  }
164
188
  // ── Carousel (PLANO-1.0, item L1) ────────────────────────────────────────────────────────────
165
189
  // MARCAÇÃO, NÃO MOTOR — e a pergunta que o item mandava decidir primeiro ("se `scroll-snap` do
166
190
  // CSS cobre, o componente é marcação") foi respondida MEDINDO, não preferindo:
167
191
  //
168
- // • as QUATRO referências embrulham o MESMO motor de terceiro, o `embla-carousel` — medido em
169
- // 15/08/2026 no `kibo-main/packages/shadcn-ui/.../carousel.tsx`, nas quatro bases do
170
- // `ui-main` (`aria`, `base`, `radix`, `new-york-v4`), no `carousel-base.tsx` do `react-main`
171
- // e no `activepieces-main`. Nenhuma escreve um motor; todas pagam o mesmo;
192
+ // • as QUATRO referências embrulham o MESMO motor de carrossel de terceiro — medido em
193
+ // 15/08/2026 no fonte de cada uma, nas pastas de `Referencia/` (numa delas, nas quatro bases
194
+ // que ela oferece). Nenhuma escreve um motor; todas pagam o mesmo;
172
195
  // • o `@base-ui/react` NÃO tem carrossel (medido: 46 pastas em `packages/react/src`, nenhuma
173
196
  // é carousel — a mesma medição que abriu a decisão de motor do Calendar);
174
- // • trazer o `embla` seria DEPENDÊNCIA NOVA, que pelo `BUILDING.md` §3.3 interrompe o lote e
197
+ // • trazer esse motor seria DEPENDÊNCIA NOVA, que pelo `BUILDING.md` §3.3 interrompe o lote e
175
198
  // exige o Victor — para um componente que o navegador já sabe fazer;
176
199
  // • e o caminho SEM JavaScript nenhum ainda não serve: `::scroll-button()`/`::scroll-marker()`
177
200
  // (CSS Overflow 5) não são Baseline — pesquisado em 15/08/2026: Chrome/Edge 135+ têm,
@@ -179,7 +202,7 @@ export function Gallery({ items, label, selected, onSelect, zoom, ratio = "1/1",
179
202
  // Um design system não pode entregar controle que só funciona num navegador.
180
203
  //
181
204
  // Então o motor é o CONTÊINER DE ROLAGEM nativo com `scroll-snap`, que é Baseline há anos e dá
182
- // de graça o que o `embla` reimplementa: arrasto por toque com inércia, rolagem por roda e
205
+ // de graça o que esse motor reimplementa: arrasto por toque com inércia, rolagem por roda e
183
206
  // teclado, e o encaixe no slide. O que sobra de JavaScript é o que o CSS ainda não tem — saber
184
207
  // em QUAL slide se está, para desenhar o ponto aceso e desabilitar a seta do fim.
185
208
  //
@@ -188,20 +211,20 @@ export function Gallery({ items, label, selected, onSelect, zoom, ratio = "1/1",
188
211
  // `prefers-reduced-motion` que o core já tem (`scroll-behavior:auto!important`) alcança este
189
212
  // componente sem uma linha nova. Passar `behavior:"smooth"` daqui passaria POR CIMA dela.
190
213
  //
191
- // UM componente, não sete peças: `Carousel.Root/Content/Item/PrevTrigger/NextTrigger/
192
- // IndicatorGroup/Indicator` é a decomposição da referência, e aqui ela custaria sete fichas para
214
+ // UM componente, não sete peças: raiz, conteúdo, item, seta de voltar, seta de avançar,
215
+ // grupo de indicadores e indicador é a decomposição da referência, e aqui ela custaria sete fichas para
193
216
  // desenhar uma lista que rola. É o mesmo argumento que o `Stepper`, o `TreeView` e a `Sidebar`
194
217
  // já resolveram: quem embrulha cada slide é o componente, e por isso o rótulo "Slide 3 de 8"
195
218
  // nunca fica com o consumidor — que é onde ele seria esquecido.
196
219
  //
197
220
  // LIMITES DECLARADOS (todos são escopo menor que o da referência, `BUILDING.md` §Passo 5):
198
- // • sem laço infinito, sem autoplay e sem arrasto com o MOUSE — os três são do `embla` e
221
+ // • sem laço infinito, sem autoplay e sem arrasto com o MOUSE — os três são do motor de terceiro e
199
222
  // nenhum deles apareceu na medição dos consumidores; laço, ainda por cima, não existe em
200
223
  // contêiner de rolagem nativo e voltaria a exigir motor;
201
224
  // • sem eixo vertical: não há uso medido, e o sprite não tem chevron para cima (a allowlist
202
225
  // do contrato tem `chevron--left/right/down`), então o eixo custaria glifo novo por nada;
203
226
  // • quantos slides aparecem por vez é CSS, não prop: `--carousel-slide` (default `100%`) é a
204
- // válvula no idioma do `--qr-size` e do `--datagrid-max-h`. É o caso `multiple` da
227
+ // válvula no idioma do `--qr-size` e do `--datagrid-max-h`. É o caso de vários slides por vez da
205
228
  // referência, sem API nenhuma;
206
229
  // • em RTL a rolagem vai para o lado certo (o deslocamento é medido pela borda inicial), mas
207
230
  // o GLIFO da seta não espelha — é uma linha de CSS que ninguém pediu e que nenhum teste
@@ -23,7 +23,8 @@ export declare function Breadcrumb({ items, label }: {
23
23
  label?: string;
24
24
  }): React.JSX.Element;
25
25
  export type TabsOrientation = "horizontal" | "vertical";
26
- export declare function Tabs({ tabs, value, onChange, label, orientation, activateOnFocus, loopFocus }: {
26
+ export type TabsPanel = "card" | "plain";
27
+ export declare function Tabs({ tabs, value, onChange, label, orientation, activateOnFocus, loopFocus, panel }: {
27
28
  tabs: Array<{
28
29
  id: string;
29
30
  label: ReactNode;
@@ -35,6 +36,7 @@ export declare function Tabs({ tabs, value, onChange, label, orientation, activa
35
36
  orientation?: Responsive<TabsOrientation>;
36
37
  activateOnFocus?: boolean;
37
38
  loopFocus?: boolean;
39
+ panel?: TabsPanel;
38
40
  }): React.JSX.Element;
39
41
  export declare function Pagination({ page, total, onPageChange }: {
40
42
  page: number;
@@ -56,11 +58,14 @@ export interface TreeNode {
56
58
  label: ReactNode;
57
59
  icon?: IconName;
58
60
  children?: TreeNode[];
61
+ hasChildren?: boolean;
59
62
  }
60
- export declare function TreeView({ items, defaultExpandedIds, onSelect, label, className }: {
63
+ export declare function TreeView({ items, defaultExpandedIds, onSelect, onExpand, selectedId, label, className }: {
61
64
  items: TreeNode[];
62
65
  defaultExpandedIds?: string[];
63
66
  onSelect?: (node: TreeNode) => void;
67
+ onExpand?: (node: TreeNode) => void | Promise<unknown>;
68
+ selectedId?: string | null;
64
69
  label?: string;
65
70
  className?: string;
66
71
  }): ReactElement<unknown, string | React.JSXElementConstructor<any>>;