@aurea-uds/core 0.27.0 → 0.29.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/aurea.css CHANGED
@@ -5,6 +5,7 @@
5
5
  --brand-yellow-foreground:oklch(0.421 0.095 57.708);
6
6
  --brand-yellow-text:oklch(0.516 0.105 86.047);
7
7
  --font-ui:"Atkinson Hyperlegible Next","Segoe UI",sans-serif;
8
+ --font-heading:"Atkinson Hyperlegible Next","Segoe UI",sans-serif;
8
9
  --font-editorial:"Atkinson Hyperlegible Next","Segoe UI",sans-serif;
9
10
  --font-code:"Atkinson Hyperlegible Mono",SFMono-Regular,Consolas,monospace;
10
11
  --text-xs:0.75rem;
@@ -44,6 +45,7 @@
44
45
  --radius-control:999px;
45
46
  --radius-full:var(--radius-control);
46
47
  --border-width:1px;
48
+ --accent-width:4px;
47
49
  --focus-width:2px;
48
50
  --focus-offset:2px;
49
51
  --opacity-disabled:0.5;
@@ -117,6 +119,7 @@
117
119
  --weight-medium:500;
118
120
  --weight-semibold:600;
119
121
  --weight-bold:700;
122
+ --weight-extrabold:800;
120
123
  --icon-sm:1rem;
121
124
  --icon-md:1.25rem;
122
125
  --icon-lg:1.5rem;
@@ -311,7 +314,7 @@
311
314
  --warning-foreground:#242424;
312
315
  --warning-hover:#de590b;
313
316
  }
314
- [data-brand="lory"][data-theme="dark"]{
317
+ [data-brand="lory"][data-theme="dark"],[data-brand="lory"] [data-theme="dark"]{
315
318
  --background:oklch(0.16 0.018 250);
316
319
  --foreground:oklch(0.96 0.006 250);
317
320
  --foreground-strong:oklch(1 0 0);
@@ -374,7 +377,7 @@
374
377
  --danger-bg:oklch(0.34 0.09 25);
375
378
  --info-bg:oklch(0.32 0.06 235);
376
379
  }
377
- [data-brand="lory"][data-theme="light"]{
380
+ [data-brand="lory"][data-theme="light"],[data-brand="lory"] [data-theme="light"]{
378
381
  --background:oklch(0.984 0.004 70);
379
382
  --foreground:oklch(0.24 0.022 250);
380
383
  --foreground-strong:oklch(0.16 0.02 250);
@@ -1233,7 +1236,11 @@ img,svg { display:block; }
1233
1236
  /* A base do botão carrega as VARIÁVEIS DE PASSO da camada responsiva (`--step-*`), e cada
1234
1237
  fallback é exatamente o valor fixo de antes — então o botão sem valor responsivo desenha o
1235
1238
  mesmo pixel. É o que permite a camada `size-*` repor a grandeza sem tocar no componente. */
1236
- .btn { --btn-bg:transparent; --btn-fg:var(--foreground); --btn-border:transparent; display:inline-flex; align-items:center; justify-content:center; gap:var(--step-gap,8px); block-size:var(--step-h,var(--control-h-md)); padding-inline:var(--step-px,15px); border:1px solid var(--btn-border); border-radius:var(--radius-control); background:var(--btn-bg); color:var(--btn-fg); font-size:var(--step-fs,var(--text-sm)); font-weight:var(--weight-medium); line-height:var(--leading-none); text-decoration:none; white-space:nowrap; cursor:pointer; transition:transform var(--duration-fast) var(--ease-standard),background var(--duration-fast),border-color var(--duration-fast),color var(--duration-fast),opacity var(--duration-fast); }
1239
+ .btn { --btn-bg:transparent; --btn-fg:var(--foreground); --btn-border:transparent; display:inline-flex; align-items:center; justify-content:center; gap:var(--step-gap,8px); block-size:var(--step-h,var(--control-h-md)); border:1px solid var(--btn-border); border-radius:var(--radius-control); background:var(--btn-bg); color:var(--btn-fg); font-size:var(--step-fs,var(--text-sm)); font-weight:var(--weight-medium); line-height:var(--leading-none); text-decoration:none; white-space:nowrap; cursor:pointer; transition:transform var(--duration-fast) var(--ease-standard),background var(--duration-fast),border-color var(--duration-fast),color var(--duration-fast),opacity var(--duration-fast); }
1240
+ /* GAR-14 (0.28.0): o recheio do botão vale também para o aviso preso a ele (`.toast-anchored`),
1241
+ que tem a MEDIDA do botão — pedido do Victor na bancada. Uma regra só, para os dois nunca
1242
+ divergirem e para o número não se repetir. */
1243
+ .btn,.toast-anchored { padding-inline:var(--step-px,15px); }
1237
1244
  .btn:hover:not(:disabled):not([aria-disabled="true"]) { transform:none; } .btn:active:not(:disabled) { transform:scale(.97); opacity:.9; }
1238
1245
  /* Duas formas de "não dá para fazer isto", e a diferença é QUEM alcança a explicação.
1239
1246
  `:disabled` tira o controle da ordem de foco — medido em 13/08/2026: quem usa teclado nunca
@@ -1479,7 +1486,7 @@ img,svg { display:block; }
1479
1486
  Largura em token para caber de 3 a 4 dígitos sem virar caixa de texto. */
1480
1487
  .number-field { display:inline-flex; }
1481
1488
  .number-field-group { display:inline-flex; align-items:center; gap:var(--space-1); }
1482
- .number-field-input { width:var(--space-16); text-align:center; }
1489
+ .number-field-input { width:var(--space-16); text-align:center; font-variant-numeric:tabular-nums; }
1483
1490
  /* A ALÇA DE ARRASTO (G-API-02), e só quando `scrubbable` está ligada.
1484
1491
  O motor já põe por ESTILO EM LINHA tudo o que é mecânica — `touch-action:none` e
1485
1492
  `user-select:none` na alça, e `position:fixed` mais o `transform` no cursor virtual, que ele
@@ -1915,6 +1922,22 @@ img,svg { display:block; }
1915
1922
  .card-interactive:hover { transform:translateY(-2px); border-color:var(--border-strong); background:var(--surface-2); }
1916
1923
  .card-selected { border-color:var(--primary-outline); box-shadow:inset 0 0 0 1px var(--primary-outline); }
1917
1924
  .card-danger { border-color:color-mix(in srgb,var(--danger-400) 50%,var(--border)); background:var(--danger-bg); }
1925
+ /* MT-01 (10/10/2026, ADR-0063): o cartão `contrast` traz `data-theme="dark"` no próprio elemento —
1926
+ o fundo, a letra e todo filho já saem do tema escuro. Falta só a linha em volta: no claro, nenhuma
1927
+ (o cartão escuro já se separa da tela, 13,3:1); no escuro, o amarelo, com a borda de sempre (1).
1928
+ O "está num pedaço escuro?" é o do ANCESTRAL, com a guarda do GAR-05: o atributo do próprio
1929
+ cartão não conta, e uma faixa clara dentro da página escura não ganha o contorno. */
1930
+ .card-contrast { border-color:transparent; }
1931
+ [data-theme="dark"] .card-contrast:where(:not([data-theme="light"] *),[data-theme="light"] [data-theme="dark"] *) { border-color:var(--primary); }
1932
+ /* MT-02 (10/10/2026, ADR-0063): o traço no topo, `--accent-width` (4), na cor CHEIA do tom e igual
1933
+ nos dois temas — a exceção que a ADR-0063 abriu na ADR-0061 para enfeite. O navegador desenha a
1934
+ borda grossa do topo afinando na curva do canto, até a de 1 dos lados. `.card.card-accent` tem a
1935
+ força de duas classes e vem depois: o escolhido, o de perigo e o contorno do `contrast` mudam os
1936
+ outros lados, e o topo continua o traço. */
1937
+ .card.card-accent { border-top:var(--accent-width) solid var(--card-accent); }
1938
+ .card-accent-brand { --card-accent:var(--primary); } .card-accent-success { --card-accent:var(--success); }
1939
+ .card-accent-info { --card-accent:var(--info); } .card-accent-warning { --card-accent:var(--warning); }
1940
+ .card-accent-danger { --card-accent:var(--destructive); }
1918
1941
  .card-header { display:flex; align-items:flex-start; justify-content:space-between; gap:16px; margin-bottom:16px; }
1919
1942
  .card-title { margin:0; font-size:var(--text-sm); font-weight:var(--weight-medium); } .card-subtitle { margin:3px 0 0; color:var(--muted-foreground); font-size:var(--text-sm); }
1920
1943
 
@@ -2044,7 +2067,9 @@ img,svg { display:block; }
2044
2067
  `tabIndex=0` (região rolável precisa ser alcançável pelo teclado) e o `.table-wrap` não é. */
2045
2068
  .table-wrap,.table-region { overflow:auto; border:1px solid var(--border); border-radius:var(--radius-card); background:var(--card); }
2046
2069
  .table,.table-wrap table { width:100%; border-collapse:collapse; min-width:720px; }
2047
- .table th,.table td,.table-wrap th,.table-wrap td { height:var(--row-h); padding:0 14px; text-align:start; border-bottom:1px solid var(--border); }
2070
+ /* GAR-16 (0.28.0): a tabela inteira com algarismos da mesma largura — a coluna de preço e de ano
2071
+ alinha. Só os algarismos mudam; o texto das outras colunas fica igual. */
2072
+ .table th,.table td,.table-wrap th,.table-wrap td { height:var(--row-h); padding:0 14px; text-align:start; border-bottom:1px solid var(--border); font-variant-numeric:tabular-nums; }
2048
2073
  .table th,.table-wrap th { color:var(--subtle-foreground); background:var(--surface-2); font-size:var(--text-xs); letter-spacing:var(--tracking-wider); text-transform:uppercase; }
2049
2074
  .table tr:last-child td,.table-wrap tr:last-child td { border-bottom:0; }
2050
2075
  .table tbody tr:hover,.table-wrap tbody tr:hover { background:var(--surface-2); }
@@ -2393,6 +2418,18 @@ img,svg { display:block; }
2393
2418
  .image { display:block; width:100%; height:auto; object-fit:cover; border-radius:var(--radius-lg); background:var(--surface-3); }
2394
2419
  .image-contain { object-fit:contain; background:transparent; }
2395
2420
  .image-broken { display:grid; place-items:center; color:var(--muted-foreground); }
2421
+ /* GAR-15 (0.28.0): a moldura de celular do `Image frame="phone"`. GENÉRICA de propósito — sem ilha,
2422
+ sem entalhe, sem botões: as regras de marketing de um fabricante proíbem simular o aparelho dele,
2423
+ e a Aurea não leva marca de terceiro. A moldura segue o tema (a superfície do cartão, a borda e a
2424
+ sombra de sempre) e o canto é o da folha de baixo, o maior que a Aurea tem; o de dentro acompanha. */
2425
+ .image-frame { display:block; }
2426
+ .image-frame-phone { padding:var(--space-2); border:var(--border-width) solid var(--border); border-radius:var(--radius-sheet); background:var(--card); box-shadow:var(--shadow-md); }
2427
+ .image-frame-phone > .image { aspect-ratio:9 / 19.5; object-position:top; border-radius:calc(var(--radius-sheet) - var(--space-2)); }
2428
+ /* A captura de cada tema (`srcDark`). Sem `data-theme` na página, vale a clara. A guarda é a do
2429
+ GAR-05 espelhada: uma faixa CLARA dentro da página escura volta a mostrar a captura clara. */
2430
+ .image-theme-dark { display:none; }
2431
+ [data-theme="dark"] .image-theme-dark:where(:not([data-theme="light"] *),[data-theme="light"] [data-theme="dark"] *) { display:block; }
2432
+ [data-theme="dark"] .image-theme-light:where(:not([data-theme="light"] *),[data-theme="light"] [data-theme="dark"] *) { display:none; }
2396
2433
 
2397
2434
  /* VÍDEO INCORPORADO (achado N-10, 24/09/2026). A caixa é a mesma superfície do `.media-player` —
2398
2435
  raio de cartão, borda e o fundo `--media-canvas` —, então um trailer do YouTube e um vídeo
@@ -2458,7 +2495,7 @@ img,svg { display:block; }
2458
2495
  A margem vai a zero porque `h1`…`h6` e `p` trazem a do navegador; o espaço entre blocos é de
2459
2496
  quem os empilha (`Stack`), não do texto. */
2460
2497
  .typography { margin:0; color:var(--foreground); }
2461
- .typography-h1,.typography-h2,.typography-h3,.typography-h4,.typography-h5,.typography-h6 { font-weight:var(--weight-semibold); line-height:var(--leading-tight); letter-spacing:var(--tracking-tight); }
2498
+ .typography-h1,.typography-h2,.typography-h3,.typography-h4,.typography-h5,.typography-h6 { font-family:var(--font-heading); font-weight:var(--weight-semibold); line-height:var(--leading-tight); letter-spacing:var(--tracking-tight); }
2462
2499
  .typography-h1 { font-size:var(--text-4xl); }
2463
2500
  .typography-h2 { font-size:var(--text-3xl); }
2464
2501
  .typography-h3 { font-size:var(--text-2xl); }
@@ -2474,6 +2511,10 @@ img,svg { display:block; }
2474
2511
  .typography-weight-medium { font-weight:var(--weight-medium); }
2475
2512
  .typography-weight-semibold { font-weight:var(--weight-semibold); }
2476
2513
  .typography-weight-bold { font-weight:var(--weight-bold); }
2514
+ .typography-weight-extrabold { font-weight:var(--weight-extrabold); }
2515
+ /* GAR-16 (0.28.0): algarismos da mesma largura, para número que alinha em coluna ou muda na tela.
2516
+ Ligado só onde se pede — no texto corrido o "1" estreito é o certo. */
2517
+ .typography-numeric { font-variant-numeric:tabular-nums; }
2477
2518
  .typography-align-start { text-align:start; }
2478
2519
  .typography-align-center { text-align:center; }
2479
2520
  .typography-align-end { text-align:end; }
@@ -2627,6 +2668,20 @@ img,svg { display:block; }
2627
2668
  .toast-success { border-color:color-mix(in srgb,var(--success-400) 38%,var(--border)); background:var(--success-bg); }
2628
2669
  .toast-warning { border-color:color-mix(in srgb,var(--warning-400) 38%,var(--border)); background:var(--warning-bg); }
2629
2670
  .toast-danger { border-color:color-mix(in srgb,var(--danger-400) 38%,var(--border)); background:var(--danger-bg); }
2671
+ /* GAR-14 (0.28.0): o aviso do `Button share`, preso ao botão — "Link copiado", ou o erro com o link à
2672
+ mostra. Tem a medida do botão (a altura, o recheio da regra de cima, a letra e a cápsula) e a pele
2673
+ do aviso de sempre. O ícone é desenho de máscara, como a seta do `KPI`: a mesma marcação serve ao
2674
+ React (o motor posiciona) e ao HTML puro (o `aurea.js` posiciona, com `.toast-anchored-fixed`). */
2675
+ .toast-anchored { display:inline-flex; align-items:center; gap:var(--space-2); min-block-size:var(--step-h,var(--control-h-md)); max-inline-size:calc(100vw - 2 * var(--space-4)); padding-block:var(--space-1); border:var(--border-width) solid var(--border); border-radius:var(--radius-control); background:var(--popover); color:var(--popover-foreground); box-shadow:var(--shadow-md); font-size:var(--step-fs,var(--text-sm)); font-weight:var(--weight-medium); line-height:var(--leading-tight); transition:opacity var(--duration-fast) var(--ease-standard); }
2676
+ .toast-anchored[data-starting-style],.toast-anchored[data-ending-style] { opacity:0; }
2677
+ .toast-anchored-icon { --toast-glifo:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 256 256'%3E%3Cpath d='M229.66,77.66l-128,128a8,8,0,0,1-11.32,0l-56-56a8,8,0,0,1,11.32-11.32L96,188.69,218.34,66.34a8,8,0,0,1,11.32,11.32Z'/%3E%3C/svg%3E"); flex:none; inline-size:var(--icon-sm); block-size:var(--icon-sm); background:currentColor; color:var(--success-400); -webkit-mask:var(--toast-glifo) center/contain no-repeat; mask:var(--toast-glifo) center/contain no-repeat; }
2678
+ .toast-anchored-danger { border-color:color-mix(in srgb,var(--danger-400) 38%,var(--border)); background:var(--danger-bg); }
2679
+ .toast-anchored-danger .toast-anchored-icon { --toast-glifo:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 256 256'%3E%3Cpath d='M128,24A104,104,0,1,0,232,128,104.11,104.11,0,0,0,128,24Zm0,192a88,88,0,1,1,88-88A88.1,88.1,0,0,1,128,216Zm-8-80V80a8,8,0,0,1,16,0v56a8,8,0,0,1-16,0Zm20,36a12,12,0,1,1-12-12A12,12,0,0,1,140,172Z'/%3E%3C/svg%3E"); color:var(--danger-400); }
2680
+ /* O erro leva o link logo abaixo do aviso, já selecionado, para a pessoa copiar com o teclado. */
2681
+ .toast-anchored-stack { display:flex; flex-direction:column; align-items:flex-start; gap:var(--space-2); inline-size:max-content; max-inline-size:calc(100vw - 2 * var(--space-4)); }
2682
+ .toast-anchored-link { inline-size:100%; }
2683
+ .toast-anchored-fixed { position:fixed; z-index:var(--z-toast); }
2684
+ .toast-anchored-positioner { z-index:var(--z-toast); }
2630
2685
  /* CommandPaletteShell (Fase 11, achado A13). A superfície já existia aqui sob o nome
2631
2686
  `.command` — e `.command` é chrome da página de docs manual: nenhum componente React a
2632
2687
  emite. Ela passava no check 15 por ACIDENTE, porque o rótulo padrão "Type a command…" põe
package/dist/aurea.js CHANGED
@@ -47,15 +47,48 @@
47
47
  activateTab(tabs[next]);
48
48
  });
49
49
 
50
+ // O texto padrão dos avisos que este script escreve sozinho, pela língua da página. Quem emite a
51
+ // marcação (o React, o catálogo) pode mandar o seu pelo atributo `data-aurea-*` de cada gatilho.
52
+ var PT = /^pt/i.test(root.lang || "");
53
+ var TEXTO = PT
54
+ ? {copied: "Copiado", linkCopied: "Link copiado", linkCopyFailed: "Não deu para copiar. Copie o link abaixo."}
55
+ : {copied: "Copied", linkCopied: "Link copied", linkCopyFailed: "Could not copy. Copy the link below."};
56
+
57
+ // GAR-14 (0.28.0) · UMA região de status, montada cedo, para os avisos que não têm outra voz.
58
+ // Região que nasce junto com o texto nem sempre é lida; a que já existe, é (WCAG 4.1.3, a
59
+ // mensagem de status). Para repetir o mesmo texto, ela é esvaziada e preenchida de novo um
60
+ // instante depois.
61
+ var regiao = null;
62
+ function regiaoDeStatus() {
63
+ if (regiao && regiao.isConnected) return regiao;
64
+ regiao = document.createElement("div");
65
+ regiao.className = "sr-only";
66
+ regiao.setAttribute("role", "status");
67
+ regiao.setAttribute("aria-live", "polite");
68
+ regiao.setAttribute("data-aurea-status", "");
69
+ document.body.appendChild(regiao);
70
+ return regiao;
71
+ }
72
+ function anunciar(texto) {
73
+ var r = regiaoDeStatus();
74
+ r.textContent = "";
75
+ setTimeout(function () { r.textContent = texto; }, 100);
76
+ }
77
+ if (document.body) regiaoDeStatus();
78
+ else document.addEventListener("DOMContentLoaded", regiaoDeStatus);
79
+
50
80
  // Copiar código: comportamento do CORE, não do catálogo. Quem marca o gatilho com
51
81
  // [data-aurea-copy] ganha o copiar de graça — o CodeBlock do React emite esse mesmo
52
82
  // markup. Sem permissão de área de transferência, seleciona o texto pro Ctrl+C.
83
+ // 0.28.0: o "copiado" também é dito ao leitor de tela — antes só o ícone mudava, e quem não vê
84
+ // não sabia se tinha copiado.
53
85
  function copy(trigger) {
54
86
  var scope = trigger.closest("[data-aurea-copy-scope]") || trigger.parentElement;
55
87
  var source = scope && scope.querySelector("code, pre, [data-aurea-copy-source]");
56
88
  if (!source) return;
57
89
  function done() {
58
90
  trigger.classList.add("is-done");
91
+ anunciar(trigger.getAttribute("data-aurea-copy-done") || TEXTO.copied);
59
92
  setTimeout(function () { trigger.classList.remove("is-done"); }, 1500);
60
93
  }
61
94
  function select() {
@@ -75,6 +108,121 @@
75
108
  if (trigger) copy(trigger);
76
109
  });
77
110
 
111
+ // GAR-14 (0.28.0) · COMPARTILHAR. O gatilho marcado com [data-aurea-share] (o `Button share` do
112
+ // React emite isto) pergunta ao navegador se ele sabe compartilhar ESTE link — detectar o recurso,
113
+ // e não adivinhar o aparelho, é o padrão da web. Sabe: abre a janela do aparelho; a pessoa
114
+ // cancelar (`AbortError`) não é erro e não diz nada. Não sabe: copia o link e mostra "Link
115
+ // copiado" preso ao botão. A cópia falhou: avisa e mostra o link selecionado, para o Ctrl+C.
116
+ // O aviso tem a medida do botão (`.toast-anchored`, no aurea.css).
117
+ var avisoAberto = null;
118
+ function fecharAviso(devolverFoco) {
119
+ if (!avisoAberto) return;
120
+ var a = avisoAberto;
121
+ avisoAberto = null;
122
+ clearTimeout(a.timer);
123
+ window.removeEventListener("scroll", a.fechar, true);
124
+ window.removeEventListener("resize", a.fechar);
125
+ document.removeEventListener("keydown", a.tecla, true);
126
+ document.removeEventListener("pointerdown", a.fora, true);
127
+ a.el.remove();
128
+ if (devolverFoco && a.gatilho.isConnected) a.gatilho.focus();
129
+ }
130
+ function posicionar(el, gatilho) {
131
+ var r = gatilho.getBoundingClientRect();
132
+ // A distância é o token, lido da página — não um número escrito aqui.
133
+ var rem = parseFloat(getComputedStyle(root).fontSize) || 16;
134
+ var espaco = (parseFloat(getComputedStyle(root).getPropertyValue("--space-2")) || 0) * rem;
135
+ el.style.top = (r.bottom + espaco) + "px";
136
+ var largura = el.getBoundingClientRect().width;
137
+ var rtl = getComputedStyle(gatilho).direction === "rtl";
138
+ var x = rtl ? r.right - largura : r.left;
139
+ el.style.left = Math.max(espaco, Math.min(x, window.innerWidth - largura - espaco)) + "px";
140
+ }
141
+ function avisoAncorado(gatilho, falhou, texto, url) {
142
+ fecharAviso(false);
143
+ var aviso = document.createElement("div");
144
+ aviso.className = "toast-anchored" + (falhou ? " toast-anchored-danger" : "");
145
+ var icone = document.createElement("span");
146
+ icone.className = "toast-anchored-icon";
147
+ icone.setAttribute("aria-hidden", "true");
148
+ var rotulo = document.createElement("span");
149
+ rotulo.textContent = texto;
150
+ aviso.appendChild(icone);
151
+ aviso.appendChild(rotulo);
152
+ var el = aviso, campo = null;
153
+ if (falhou) {
154
+ el = document.createElement("div");
155
+ el.className = "toast-anchored-stack toast-anchored-fixed";
156
+ el.appendChild(aviso);
157
+ campo = document.createElement("input");
158
+ campo.className = "input toast-anchored-link";
159
+ campo.readOnly = true;
160
+ campo.value = url;
161
+ campo.setAttribute("aria-label", texto);
162
+ el.appendChild(campo);
163
+ } else {
164
+ aviso.className += " toast-anchored-fixed";
165
+ // O texto vai pela região de status; o desenho é só para quem vê, e não fala duas vezes.
166
+ aviso.setAttribute("aria-hidden", "true");
167
+ }
168
+ document.body.appendChild(el);
169
+ posicionar(el, gatilho);
170
+ var estado = {el: el, gatilho: gatilho, timer: 0};
171
+ // Rolar a PÁGINA solta o aviso do botão, e ele fecha. Mas o campo do link rola o próprio texto
172
+ // quando é selecionado — rolagem de dentro do aviso não conta (medido no navegador: sem esta
173
+ // guarda, o aviso de erro fechava no mesmo instante em que abria).
174
+ estado.fechar = function (e) { if (e && e.target && e.target.nodeType === 1 && el.contains(e.target)) return; fecharAviso(false); };
175
+ estado.tecla = function (e) { if (e.key === "Escape") fecharAviso(true); };
176
+ estado.fora = function (e) { if (!el.contains(e.target) && e.target !== gatilho) fecharAviso(false); };
177
+ avisoAberto = estado;
178
+ window.addEventListener("scroll", estado.fechar, true);
179
+ window.addEventListener("resize", estado.fechar);
180
+ if (falhou) {
181
+ // O erro fica até a pessoa resolver: Esc devolve o foco ao botão, clicar fora fecha.
182
+ document.addEventListener("keydown", estado.tecla, true);
183
+ document.addEventListener("pointerdown", estado.fora, true);
184
+ campo.focus();
185
+ campo.select();
186
+ } else {
187
+ estado.timer = setTimeout(estado.fechar, 1500);
188
+ }
189
+ anunciar(texto);
190
+ }
191
+ function share(trigger) {
192
+ var url = trigger.getAttribute("data-aurea-share-url") || location.href;
193
+ var dados = {url: url};
194
+ var titulo = trigger.getAttribute("data-aurea-share-title");
195
+ var texto = trigger.getAttribute("data-aurea-share-text");
196
+ if (titulo) dados.title = titulo;
197
+ if (texto) dados.text = texto;
198
+ var copiado = trigger.getAttribute("data-aurea-share-copied") || TEXTO.linkCopied;
199
+ var falhou = trigger.getAttribute("data-aurea-share-failed") || TEXTO.linkCopyFailed;
200
+ function copiar() {
201
+ if (!navigator.clipboard || !navigator.clipboard.writeText) {
202
+ avisoAncorado(trigger, true, falhou, url);
203
+ return Promise.resolve();
204
+ }
205
+ return navigator.clipboard.writeText(url).then(
206
+ function () { avisoAncorado(trigger, false, copiado); },
207
+ function () { avisoAncorado(trigger, true, falhou, url); });
208
+ }
209
+ if (typeof navigator.share === "function" && (!navigator.canShare || navigator.canShare(dados))) {
210
+ return navigator.share(dados).catch(function (e) {
211
+ if (!e || e.name !== "AbortError") return copiar();
212
+ });
213
+ }
214
+ return copiar();
215
+ }
216
+
217
+ document.addEventListener("click", function (event) {
218
+ // O `Button` do React faz o mesmo no próprio clique e marca o evento com `preventDefault`,
219
+ // para não compartilhar duas vezes quando os dois estão na página.
220
+ if (event.defaultPrevented) return;
221
+ var trigger = event.target.closest && event.target.closest("[data-aurea-share]");
222
+ if (!trigger || trigger.disabled || trigger.getAttribute("aria-disabled") === "true") return;
223
+ share(trigger);
224
+ });
225
+
78
226
  // TableOfContents: marca sozinho a seção em vista. IntersectionObserver com uma faixa
79
227
  // estreita no alto da janela — a seção "atual" é a que cruza essa faixa, não a maior
80
228
  // visível, senão o item pisca entre duas ao rolar. Sem observer (browser antigo), o
@@ -258,6 +406,7 @@
258
406
  root.dataset.theme = root.dataset.theme === "dark" ? "light" : "dark";
259
407
  },
260
408
  showToast: showToast,
261
- copy: copy
409
+ copy: copy,
410
+ share: share
262
411
  };
263
412
  })();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aurea-uds/core",
3
- "version": "0.27.0",
3
+ "version": "0.29.0",
4
4
  "description": "O CSS da Aurea UDS e o comportamento em JavaScript puro. Importe @aurea-uds/fonts/css antes deste.",
5
5
  "keywords": [
6
6
  "aurea",