@aurea-uds/react 0.61.0 → 0.62.1

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 CHANGED
@@ -1,68 +1,68 @@
1
1
  # @aurea-uds/react
2
2
 
3
- Os componentes React do **Aurea Universal Design System**.
3
+ The React components of the **Aurea Universal Design System**.
4
4
 
5
5
  ```bash
6
6
  pnpm add @aurea-uds/react @aurea-uds/core @aurea-uds/fonts @aurea-uds/icons
7
7
  ```
8
8
 
9
- ## Uso
9
+ ## Usage
10
10
 
11
- Carregue as fontes **antes** da folha de estilo — o core não embute fonte nenhuma.
11
+ Load the fonts **before** the stylesheet — the core does not embed any font.
12
12
 
13
13
  ```js
14
14
  import "@aurea-uds/fonts/css";
15
15
  import "@aurea-uds/core/css";
16
- import {AureaProvider, Button, ptBR} from "@aurea-uds/react";
16
+ import {AureaProvider, Button} from "@aurea-uds/react";
17
17
 
18
18
  export default () => (
19
- <AureaProvider spriteUrl="/aurea-icons.svg" strings={ptBR}>
20
- <Button variant="primary">Enviar</Button>
19
+ <AureaProvider spriteUrl="/aurea-icons.svg">
20
+ <Button variant="primary">Send</Button>
21
21
  </AureaProvider>
22
22
  );
23
23
  ```
24
24
 
25
- `spriteUrl` aponta para o arquivo do `@aurea-uds/icons` — copie-o para onde o seu app serve
26
- arquivos estáticos. É configuração, então mora no provider, e não em cada componente que desenha
27
- um ícone. As frases padrão são em inglês; `strings={ptBR}` as troca pelas em português.
25
+ `spriteUrl` points to the file from `@aurea-uds/icons` — copy it to where your app serves static files.
26
+ It is configuration, so it lives on the provider, not on each component that draws an icon. The default
27
+ strings are in English; `strings={ptBR}` swaps them for the Portuguese ones.
28
28
 
29
- ## Importar por categoria
29
+ ## Import by category
30
30
 
31
- O entry point principal funciona, mas cada categoria é também um entry point próprio, para você levar uma
32
- parte do sistema sem o resto:
31
+ The main entry point works, but each category is also an entry point of its own, so you can take one
32
+ part of the system without the rest:
33
33
 
34
34
  ```js
35
35
  import {Button} from "@aurea-uds/react/actions";
36
36
  ```
37
37
 
38
- Seis módulos trazem uma engine opcional e por isso ficam **fora do entry point principal** — importar um
39
- `Button` nunca pode puxar uma dependência que você não instalou:
38
+ Six modules bring an optional engine and therefore stay **out of the main entry point** — importing a
39
+ `Button` must never pull in a dependency you did not install:
40
40
 
41
- | Importe | Exige que você instale |
41
+ | Import | Requires you to install |
42
42
  |---|---|
43
- | `@aurea-uds/react/code-editor` | `codemirror` e os pacotes `@codemirror/*` |
43
+ | `@aurea-uds/react/code-editor` | `codemirror` and the `@codemirror/*` packages |
44
44
  | `@aurea-uds/react/data-grid` | `@tanstack/react-table` |
45
45
  | `@aurea-uds/react/qrcode` | `qr` |
46
46
  | `@aurea-uds/react/calendar` | `react-day-picker` |
47
47
  | `@aurea-uds/react/chart` | `recharts` |
48
48
  | `@aurea-uds/react/graph` | `@xyflow/react` |
49
49
 
50
- ### O mapa (`DependencyGraph`) e as três peças opcionais dele
50
+ ### The graph (`DependencyGraph`) and its three optional pieces
51
51
 
52
- Cada uma só é carregada quando a função dela é pedida. Sem ela, o mapa avisa uma vez no console e
53
- segue sem a função — não quebra a tela.
52
+ Each one is loaded only when its feature is requested. Without it, the graph warns once in the console
53
+ and carries on without the feature — it does not break the screen.
54
54
 
55
- | Para | Instale |
55
+ | For | Install |
56
56
  |---|---|
57
- | arrumar em camadas, em árvore ou em círculo (`layout="layered"`, `"tree"`, `"radial"`) | `elkjs` |
58
- | a linha que desvia de um nó depois de arrumar à mão | `@tisoap/react-flow-smart-edge` |
59
- | a foto do mapa (`toPng()`, `toSvg()`) | `modern-screenshot` |
57
+ | layered, tree or circular layout (`layout="layered"`, `"tree"`, `"radial"`) | `elkjs` |
58
+ | the edge that routes around a node after manual arranging | `@tisoap/react-flow-smart-edge` |
59
+ | an image of the graph (`toPng()`, `toSvg()`) | `modern-screenshot` |
60
60
 
61
- **O arrumador fora da tela principal (`layoutWorker`).** Num mapa grande, a arrumação pode prender a
62
- tela por alguns segundos. O mapa aceita um trabalhador (Web Worker) que o app cria — cada empacotador
63
- cria trabalhador do seu jeito, e por isso quem entrega é o app. A Aurea publica o trabalhador por um
64
- caminho dela, `@aurea-uds/react/graph-worker`, que traz o do `elkjs` (o peer opcional) junto — o app
65
- não importa o motor por fora. No Vite:
61
+ **Layout off the main thread (`layoutWorker`).** On a large graph, the layout can freeze the screen for a
62
+ few seconds. The graph accepts a Web Worker that the app creates — every bundler creates workers its own
63
+ way, so the app is the one that supplies it. Aurea publishes the worker on its own entry point,
64
+ `@aurea-uds/react/graph-worker`, which brings the `elkjs` worker (the optional peer) along — the app does
65
+ not import the engine separately. In Vite:
66
66
 
67
67
  ```js
68
68
  import TrabalhadorDoMapa from "@aurea-uds/react/graph-worker?worker";
@@ -70,42 +70,42 @@ import TrabalhadorDoMapa from "@aurea-uds/react/graph-worker?worker";
70
70
  <DependencyGraph layout="layered" layoutWorker={() => new TrabalhadorDoMapa()} … />
71
71
  ```
72
72
 
73
- Em qualquer outro empacotador, o jeito que sempre funciona: copie `node_modules/elkjs/lib/elk-worker.min.js`
74
- para a pasta pública do app e passe `layoutWorker={() => new Worker("/elk-worker.min.js")}`.
73
+ In any other bundler, the way that always works: copy `node_modules/elkjs/lib/elk-worker.min.js` to the
74
+ app's public folder and pass `layoutWorker={() => new Worker("/elk-worker.min.js")}`.
75
75
 
76
76
  ## Hooks
77
77
 
78
- Quatro são públicos, e eles são a parte da API que **não tem página no catálogo** — o catálogo
79
- documenta componentes, e hook não é componente. Enquanto isso não mudar, é aqui que eles estão
80
- escritos.
78
+ Four are public, and they are the part of the API that **has no page in the catalog** — the catalog
79
+ documents components, and a hook is not a component. Until that changes, this is where they are written
80
+ down.
81
81
 
82
82
  ```js
83
83
  import {AureaProvider, useToast} from "@aurea-uds/react";
84
84
 
85
85
  function BotaoSalvar() {
86
- const toast = useToast(); // dentro do AureaProvider
86
+ const toast = useToast(); // inside AureaProvider
87
87
  return <Button onClick={() => toast.add({
88
- title: "Salvo",
89
- description: "Dois arquivos enviados.",
88
+ title: "Saved",
89
+ description: "Two files uploaded.",
90
90
  type: "success", // info | success | warning | danger
91
- })}>Salvar</Button>;
91
+ })}>Save</Button>;
92
92
  }
93
93
  ```
94
94
 
95
- A pilha se desenha sozinha: o `AureaProvider` já monta o viewport dos toasts, então não há
96
- `<Toaster/>` para posicionar nem um segundo provider para instalar.
95
+ The stack draws itself: `AureaProvider` already mounts the toast viewport, so there is no `<Toaster/>` to
96
+ place and no second provider to install.
97
97
 
98
- | Hook | O que entrega |
98
+ | Hook | What it returns |
99
99
  |---|---|
100
- | `useToast()` | `add({title, description, type})`, mais `close(id)` e a lista viva. A fila é a do Base UI |
101
- | `useAureaTheme()` | lê e muda **os dois** eixos no `<html>`: `theme` (`dark`/`light`) e `density`. `theme` é `null` no servidor — não desenhe o que depende do tema até ele deixar de ser |
102
- | `useAureaStrings()` | o dicionário de frases, somado ao que você passou ao provider |
103
- | `useSpriteUrl()` | de onde o sprite de ícones está sendo carregado |
100
+ | `useToast()` | `add({title, description, type})`, plus `close(id)` and the live list. The queue is Base UI's |
101
+ | `useAureaTheme()` | reads and changes **both** axes on `<html>`: `theme` (`dark`/`light`) and `density`. `theme` is `null` on the server — do not render what depends on the theme until it stops being `null` |
102
+ | `useAureaStrings()` | the string dictionary, merged with what you passed to the provider |
103
+ | `useSpriteUrl()` | where the icon sprite is being loaded from |
104
104
 
105
- ## Requisitos
105
+ ## Requirements
106
106
 
107
- React 19 ou mais novo. No React 19 `ref` é prop comum, e os tipos dependem disso.
107
+ React 19 or newer. In React 19 `ref` is a regular prop, and the types depend on that.
108
108
 
109
- ## Licença
109
+ ## License
110
110
 
111
111
  Apache-2.0.
@@ -17,7 +17,12 @@ import { Icon } from "./system.js";
17
17
  export function Accordion({ items, variant = "default", multiple = false, defaultValue, indicator = "chevron", size = "md", orientation = "vertical", className }) {
18
18
  const grupo = React.useId();
19
19
  const deLado = orientation === "horizontal";
20
- const lista = _jsx("div", { className: cx("accordion", variant === "surface" && "accordion-surface", indicator === "plus" && "accordion-plus", size === "lg" && "accordion-lg", deLado && "accordion-horizontal", !deLado && className), children: items.map(i => _jsxs("details", { className: "accordion-item", name: multiple && !deLado ? undefined : grupo, open: defaultValue?.includes(i.id) || undefined, children: [_jsxs("summary", { "aria-disabled": i.disabled || undefined, tabIndex: i.disabled ? -1 : undefined, onClick: i.disabled ? (e) => e.preventDefault()
20
+ // DE LADO, SEMPRE HÁ UM À VISTA também na abertura (06/11/2026, a D4 da auditoria externa — decisão do Victor, "Abrir o
21
+ // primeiro"): sem `defaultValue`, o horizontal nascia com tudo fechado, só as tiras em pé e nenhuma área de conteúdo. Agora
22
+ // nasce com o primeiro item que não está desligado. A referência não tem o horizontal; o de pé continua como o dela,
23
+ // fechado.
24
+ const abertos = defaultValue ?? (deLado ? [items.find(i => !i.disabled)?.id].filter(Boolean) : undefined);
25
+ const lista = _jsx("div", { className: cx("accordion", variant === "surface" && "accordion-surface", indicator === "plus" && "accordion-plus", size === "lg" && "accordion-lg", deLado && "accordion-horizontal", !deLado && className), children: items.map(i => _jsxs("details", { className: "accordion-item", name: multiple && !deLado ? undefined : grupo, open: abertos?.includes(i.id) || undefined, children: [_jsxs("summary", { "aria-disabled": i.disabled || undefined, tabIndex: i.disabled ? -1 : undefined, onClick: i.disabled ? (e) => e.preventDefault()
21
26
  : deLado ? (e) => { if (e.currentTarget.parentElement.open)
22
27
  e.preventDefault(); } : undefined, children: [_jsx("span", { className: "accordion-title", children: i.title }), _jsx("span", { className: "accordion-chevron", "aria-hidden": "true" })] }), _jsx("div", { className: "accordion-content", children: i.content })] }, i.id)) });
23
28
  return deLado ? _jsx("div", { className: cx("accordion-horizontal-frame", className), children: lista }) : lista;
@@ -103,6 +103,10 @@ export function MediaPlayer({ kind = "video", src, poster, title, subtitle, clas
103
103
  setCurrent(el.currentTime); onTimeUpdate?.(e); }, onLoadedMetadata: (e) => { onMeta(); onLoadedMetadata?.(e); }, onDurationChange: (e) => { const el = m(); if (el)
104
104
  setDuration(el.duration); onDurationChange?.(e); }, onProgress: (e) => { syncBuf(); onProgress?.(e); }, onVolumeChange: (e) => { syncVol(); onVolumeChange?.(e); }, children: children })] }), kind === "video" && title && _jsx("div", { className: "media-overlay-title", children: _jsxs("div", { children: [_jsx("strong", { children: title }), subtitle && _jsx("span", { children: subtitle })] }) }), _jsxs("div", { className: "media-controls", children: [_jsxs("div", { className: "media-seek", style: { "--media-played": `${playedPct}%` }, children: [_jsxs("div", { className: "media-seek-track", children: [_jsx("span", { className: "media-seek-buffered", style: { width: `${bufferedPct}%` } }), _jsx("span", { className: "media-seek-played" })] }), _jsx("input", { type: "range", min: 0, max: duration > 0 ? duration : 0, step: "any", value: Math.min(current, duration || 0), "aria-label": s.mediaSeek, "aria-valuetext": valueText, onChange: e => seek(Number(e.target.value)) })] }), _jsxs("div", { className: "media-control-row", children: [_jsxs("div", { className: "media-control-group", children: [_jsx("button", { className: "media-control", type: "button", "aria-label": playing ? s.mediaPause : s.mediaPlay, onClick: togglePlay, children: _jsx(Icon, { name: playing ? "pause" : "play" }) }), _jsx("button", { className: "media-control", type: "button", "aria-label": s.mediaSkipBack, onClick: () => skip(-10), children: _jsx(Icon, { name: "clock-counter-clockwise" }) }), _jsx("button", { className: "media-control", type: "button", "aria-label": s.mediaSkipForward, onClick: () => skip(10), children: _jsx(Icon, { name: "clock-clockwise" }) }), _jsxs("span", { className: "media-time", children: [clockTime(current), " / ", clockTime(duration)] })] }), _jsxs("div", { className: "media-control-group", children: [_jsx("button", { className: "media-control", type: "button", "aria-label": muted ? s.mediaUnmute : s.mediaMute, onClick: toggleMute, children: _jsx(Icon, { name: muted || volume === 0 ? "speaker-slash" : "speaker-high" }) }), _jsx("input", { className: "media-volume", type: "range", min: 0, max: 100, value: volPct, "aria-label": s.mediaVolume, "aria-valuetext": `${volPct}%`, onChange: e => setVol(Number(e.target.value) / 100) }), hasCaptions && _jsx("button", { className: cx("media-control", captionsOn && "active"), type: "button", "aria-label": captionsOn ? s.mediaCaptionsHide : s.mediaCaptionsShow, "aria-pressed": captionsOn, onClick: toggleCaptions, children: _jsx(Icon, { name: "closed-captioning" }) }), kind === "video" && _jsx("button", { className: "media-control", type: "button", "aria-label": fullscreen ? s.mediaFullscreenExit : s.mediaFullscreenEnter, onClick: toggleFullscreen, children: _jsx(Icon, { name: fullscreen ? "arrows-in" : "arrows-out" }) })] })] })] })] });
105
105
  }
106
+ // SÓ `https:` (06/11/2026, a D5 da auditoria externa — decisão do Victor): a moldura aceitava qualquer endereço, sem
107
+ // `sandbox` — não rouba nada da página (medido: o `data:` roda isolado), mas pode mostrar conteúdo falso. Sem `https:`, a
108
+ // moldura fica vazia e o link de origem não aparece.
109
+ const soHttps = (u) => !!u && /^https:\/\//i.test(u.trim());
106
110
  function comAutoplay(src) { try {
107
111
  const u = new URL(src);
108
112
  u.searchParams.set("autoplay", "1");
@@ -119,9 +123,9 @@ export function MediaEmbed({ src, title, poster, ratio = "16/9", href, autoplay
119
123
  React.useEffect(() => { if (clicou)
120
124
  frameRef.current?.focus(); }, [clicou]);
121
125
  const endereco = clicou && autoplay ? comAutoplay(src) : src;
122
- return _jsxs("div", { className: cx("media-embed", className), style: style, children: [_jsx("div", { className: "media-embed-frame", style: { aspectRatio: ratio }, children: ativo
126
+ return _jsxs("div", { className: cx("media-embed", className), style: style, children: [_jsx("div", { className: "media-embed-frame", style: { aspectRatio: ratio }, children: !soHttps(src) ? null : ativo
123
127
  ? _jsx("iframe", { ref: frameRef, src: endereco, title: title, loading: "lazy", referrerPolicy: "strict-origin-when-cross-origin", allow: "autoplay; encrypted-media; fullscreen; picture-in-picture", allowFullScreen: true, ...rest })
124
- : _jsxs("button", { type: "button", className: "media-embed-facade", "aria-label": `${s.mediaPlay}: ${title}`, onClick: () => { setAtivo(true); setClicou(true); }, children: [_jsx("img", { src: poster, alt: "", loading: "lazy", decoding: "async" }), _jsx("span", { className: "media-embed-play", "aria-hidden": "true", children: _jsx(Icon, { name: "play-circle", weight: "fill" }) })] }) }), href && _jsxs("a", { className: "media-embed-link", href: href, target: "_blank", rel: "noopener", children: [_jsx(Icon, { name: "arrow-square-out" }), s.mediaEmbedOpen] })] });
128
+ : _jsxs("button", { type: "button", className: "media-embed-facade", "aria-label": `${s.mediaPlay}: ${title}`, onClick: () => { setAtivo(true); setClicou(true); }, children: [_jsx("img", { src: poster, alt: "", loading: "lazy", decoding: "async" }), _jsx("span", { className: "media-embed-play", "aria-hidden": "true", children: _jsx(Icon, { name: "play-circle", weight: "fill" }) })] }) }), soHttps(href) && _jsxs("a", { className: "media-embed-link", href: href, target: "_blank", rel: "noopener", children: [_jsx(Icon, { name: "arrow-square-out" }), s.mediaEmbedOpen] })] });
125
129
  }
126
130
  export function Image({ ratio, fit, alt, className, style, render, onError, frame, srcDark, ...props }) {
127
131
  const [quebrou, setQuebrou] = React.useState(false);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aurea-uds/react",
3
- "version": "0.61.0",
3
+ "version": "0.62.1",
4
4
  "description": "Os componentes React da Aurea UDS. Importe @aurea-uds/fonts/css e @aurea-uds/core/css junto.",
5
5
  "keywords": [
6
6
  "aurea",