@aurea-uds/native 0.24.0 → 0.24.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/dist/actions.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import * as React from "react";
2
2
  import { type PressableProps } from "react-native";
3
3
  import { type AureaIconRegistry, type AureaIcon } from "./icon.js";
4
+ import type { AureaTokens } from "./tokens.js";
4
5
  /** Quanto peso a caixa tem. */
5
6
  export type AureaButtonAppearance = "solid" | "outline" | "ghost";
6
7
  /** O que a cor significa. Mesmos nomes da web (ADR-0044). */
@@ -70,6 +71,13 @@ export interface IconButtonProps extends Omit<ButtonProps, "children" | "leading
70
71
  * sozinho não diz nada a quem não o vê, e deixar isso opcional é o mesmo que deixá-lo vazio.
71
72
  */
72
73
  export declare function IconButton(props: IconButtonProps): React.JSX.Element;
74
+ /**
75
+ * Quanto a ÁREA DE TOQUE do `IconButton` passa do círculo desenhado, de cada lado — R-24
76
+ * (08/10/2026). O alvo é o maior entre o desenho e o `targetMin` (44), centrado; o círculo de
77
+ * `md` tem 36, então sobram 4 de cada lado. O `Badge` preso usa isto para se prender ao CÍRCULO,
78
+ * e não ao canto da área de toque — medido: preso à área de toque, o selo ficava 4 para fora.
79
+ */
80
+ export declare function folgaDoToque(t: AureaTokens, size?: AureaButtonSize): number;
73
81
  /** Fechado: sem `appearance` e sem `tone`, porque a cor é a do glifo. */
74
82
  export interface ThemeToggleProps extends Omit<IconButtonProps, "name" | "label" | "onPress" | "appearance" | "tone"> {
75
83
  }
package/dist/actions.js CHANGED
@@ -198,6 +198,16 @@ export function LinkButton(props) {
198
198
  export function IconButton(props) {
199
199
  return _jsx(BotaoDeIcone, { ...props });
200
200
  }
201
+ /**
202
+ * Quanto a ÁREA DE TOQUE do `IconButton` passa do círculo desenhado, de cada lado — R-24
203
+ * (08/10/2026). O alvo é o maior entre o desenho e o `targetMin` (44), centrado; o círculo de
204
+ * `md` tem 36, então sobram 4 de cada lado. O `Badge` preso usa isto para se prender ao CÍRCULO,
205
+ * e não ao canto da área de toque — medido: preso à área de toque, o selo ficava 4 para fora.
206
+ */
207
+ export function folgaDoToque(t, size = "md") {
208
+ const lado = t.size[ALTURA[size]];
209
+ return Math.max(0, (Math.max(lado, t.size.targetMin) - lado) / 2);
210
+ }
201
211
  /** O corpo do `IconButton`. A cor própria do ícone (`corDoIcone`) é só do `ThemeToggle`: o
202
212
  * `IconButton` público não tem cor solta, e dentro do cartão da marca ela não vale (a tinta vence). */
203
213
  function BotaoDeIcone({ name, label, appearance = "ghost", tone = "neutral", size = "md", icons, pressed, disabled, corDoIcone, pesoDoIcone, ...rest }) {
package/dist/display.js CHANGED
@@ -23,6 +23,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
23
23
  // perderia o cartão, que é metade do que ela é.
24
24
  import * as React from "react";
25
25
  import { Image, View } from "react-native";
26
+ import { IconButton, folgaDoToque } from "./actions.js";
26
27
  import { canto, acentoDoTom, criarFolha, fundoDoTom } from "./estilos.js";
27
28
  import { Card } from "./layout.js";
28
29
  import { gravidadeDoEstado } from "./strings.js";
@@ -57,11 +58,14 @@ const folha = criarFolha((t) => ({
57
58
  justo: { alignSelf: "flex-start" },
58
59
  // A âncora é `position:relative` na web; aqui o filho absoluto já se posiciona por ela.
59
60
  ancora: { position: "relative", alignSelf: "flex-start" },
60
- sobreposto: { position: "absolute", zIndex: 1, minHeight: t.size.space4, paddingHorizontal: t.size.space1 },
61
- // ⚠ A web desenha um anel da cor do FUNDO em volta do selo sobreposto
62
- // (`box-shadow:0 0 0 var(--space-05) var(--badge-ring)`, aurea.css:1010) — é o que separa o
63
- // número do ícone embaixo. `boxShadow` do RN 0.76+ aceita spread, então o anel atravessa.
64
- anel: { boxShadow: [{ offsetX: 0, offsetY: 0, blurRadius: 0, spreadDistance: t.size.space05, color: t.color.background }] },
61
+ // R-24 (08/10/2026): o selo PRESO segue a referência principal. O nativo dela não tem selo, então
62
+ // vale a web dela (o `badge.css` da 3.2.6, lido no pacote): no mínimo um quadrado do tamanho do
63
+ // selo (`MINIMO_PRESO`, abaixo), que vira círculo com um dígito; sem recheio vertical e meia
64
+ // unidade dos lados; e um fio de 1 da cor do FUNDO em volta, que separa o número do que está
65
+ // atrás. Antes sobrava o recheio vertical do tamanho — o "1" do `sm` media 14,9 × 22, esticado —
66
+ // e o anel era uma sombra de 2.
67
+ sobreposto: { position: "absolute", zIndex: 1, paddingVertical: 0, paddingHorizontal: t.size.space05,
68
+ borderWidth: t.size.borderWidth, borderColor: t.color.background },
65
69
  // ── Status ─────────────────────────────────────────────────────────────────────────────────
66
70
  estado: { flexDirection: "row", alignItems: "center", gap: t.size.space2 },
67
71
  // O halo é `box-shadow 0 0 0 3px` da cor corrente a 14% (aurea.css:1055). Sem `color-mix` no
@@ -118,37 +122,66 @@ export function Badge({ tone = "neutral", emphasis = "soft", size = "md", dot, c
118
122
  const miolo = anchor ? (numero ?? badgeContent) : (numero ?? children);
119
123
  // Ponto puro: `dot` sem nada para mostrar. Aí o selo não tem conteúdo e ganha medida própria.
120
124
  const soPonto = !!dot && miolo == null;
121
- const selo = (_jsxs(View, { style: [
125
+ // R-24: preso, o selo se prende ao DESENHO do alvo. O `IconButton` tem área de toque maior que o
126
+ // círculo (44 contra 36 no `md`); preso a ela, o selo ficava 4 para fora. A folga sai do próprio
127
+ // `IconButton` (`folgaDoToque`), e não de um número escrito aqui.
128
+ const folga = anchor && React.isValidElement(children) && children.type === IconButton
129
+ ? folgaDoToque(t, children.props.size) : 0;
130
+ // O ponto tem 8 de cor (a medida da Aurea, nos dois alvos) mais o fio de 1 do anel de cada lado.
131
+ const ladoDoPonto = t.size.space2 + 2 * t.size.borderWidth;
132
+ const minimo = soPonto ? ladoDoPonto : MINIMO_PRESO(t, size);
133
+ // O deslocamento de 25% é do TAMANHO DO SELO, que só se sabe depois de desenhar. Até a primeira
134
+ // medida vale o mínimo — o tamanho exato do selo de um dígito, o caso comum —, então o primeiro
135
+ // quadro já sai certo para ele, e o de "99+" se acerta na medida seguinte. Sem `translate` em
136
+ // porcentagem de propósito: ele só existe na Nova Arquitetura, e o pacote aceita RN desde 0.76.
137
+ const [medida, setMedida] = React.useState(null);
138
+ const aoMedir = anchor ? (e) => {
139
+ const { width: w, height: h } = e.nativeEvent.layout;
140
+ if (!medida || medida.w !== w || medida.h !== h)
141
+ setMedida({ w, h });
142
+ } : undefined;
143
+ const selo = (_jsxs(View, { onLayout: aoMedir, style: [
122
144
  s.selo, s[`selo_${size}`], pele,
123
145
  fit === "content" && !anchor && s.justo,
124
- anchor && s.sobreposto, anchor && s.anel,
125
- anchor && posicaoDaAncora(anchor),
126
- soPonto && { width: t.size.space2, minWidth: t.size.space2, height: t.size.space2,
127
- minHeight: t.size.space2, paddingHorizontal: 0, paddingVertical: 0,
128
- backgroundColor: acento, borderWidth: 0 },
146
+ anchor && s.sobreposto,
147
+ anchor && { minWidth: minimo, minHeight: minimo },
148
+ anchor && posicaoDaAncora(anchor, folga, medida?.w ?? minimo, medida?.h ?? minimo),
149
+ soPonto && { width: ladoDoPonto, height: ladoDoPonto, paddingHorizontal: 0, paddingVertical: 0,
150
+ backgroundColor: acento, borderWidth: anchor ? t.size.borderWidth : 0 },
129
151
  style,
130
152
  ], ...(anchor ? { accessibilityElementsHidden: true,
131
153
  importantForAccessibility: "no-hide-descendants" } : rest), children: [dot && !soPonto && _jsx(View, { style: [s.ponto, { backgroundColor: corDoTexto }] }), !soPonto && leading, typeof miolo === "string" || typeof miolo === "number"
132
- ? _jsx(Text, { weight: 500, style: [LETRA_DO_SELO(t, size), { color: corDoTexto }], children: miolo })
154
+ ? _jsx(Text, { weight: 500, style: [LETRA_DO_SELO(t, size),
155
+ // Preso, a linha cabe no mínimo menos o fio: a letra fica no meio do círculo de 16.
156
+ anchor && { lineHeight: Math.min(LETRA_DO_SELO(t, size).lineHeight, minimo - 2 * t.size.borderWidth) },
157
+ { color: corDoTexto }], children: miolo })
133
158
  : miolo, !soPonto && trailing] }));
134
159
  if (!anchor)
135
160
  return selo;
136
161
  const escondido = invisible || (count === 0 && !showZero) || (miolo == null && !dot);
137
162
  return _jsxs(View, { style: s.ancora, ...rest, children: [children, !escondido && selo] });
138
163
  }
164
+ /** O mínimo do selo preso, da referência principal: 16 no `xs` e no `sm`, 28 no `md`, 32 no `lg`. */
165
+ const MINIMO_PRESO = (t, size) => size === "lg" ? t.size.space8 : size === "md" ? t.size.space7 : t.size.space4;
139
166
  /**
140
- * O canto, traduzido para o RN.
167
+ * O canto, do jeito da referência principal (R-24, 08/10/2026): o selo encosta no canto do alvo e
168
+ * sai 25% do PRÓPRIO tamanho para fora — três quartos dele ficam por cima do alvo. A regra é a mesma
169
+ * para alvo redondo e quadrado, como lá.
141
170
  *
142
- * A web usa `inset-*` mais `transform: translate(±50%, ∓50%)`, o que centra o selo EM CIMA do
143
- * canto. `translate` percentual não existe no `transform` do RN — medido no contrato — então o
144
- * deslocamento sai por `inset` negativo, que é o mesmo efeito com aritmética diferente.
171
+ * Antes era um `-8` fixo, sem token, a partir do canto da ÁREA DE TOQUE: o selo do sino caía 4,5
172
+ * para fora do círculo (medido no `react-native-web`). A web da Aurea centra o selo no canto e o
173
+ * puxa 14% para dentro em alvo redondo; no sino de 36, as duas contas caem a cerca de 1 ponto uma
174
+ * da outra. `folga` é o quanto a área de toque passa do desenho (0 fora do `IconButton`).
145
175
  */
146
- function posicaoDaAncora(anchor) {
147
- const meio = -8;
148
- return anchor === "top-end" ? { top: meio, right: meio }
149
- : anchor === "top-start" ? { top: meio, left: meio }
150
- : anchor === "bottom-end" ? { bottom: meio, right: meio }
151
- : { bottom: meio, left: meio };
176
+ function posicaoDaAncora(anchor, folga, w, h) {
177
+ const fora = 0.25;
178
+ const emCima = anchor === "top-end" || anchor === "top-start";
179
+ const noFim = anchor === "top-end" || anchor === "bottom-end";
180
+ return {
181
+ ...(emCima ? { top: folga } : { bottom: folga }),
182
+ ...(noFim ? { right: folga } : { left: folga }),
183
+ transform: [{ translateX: (noFim ? 1 : -1) * w * fora }, { translateY: (emCima ? -1 : 1) * h * fora }],
184
+ };
152
185
  }
153
186
  /**
154
187
  * Um ponto e uma palavra.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aurea-uds/native",
3
- "version": "0.24.0",
3
+ "version": "0.24.1",
4
4
  "description": "A Aurea UDS para React Native: o provider de tema e densidade, os componentes e os ícones do Phosphor sobre react-native-svg.",
5
5
  "keywords": [
6
6
  "aurea",
@@ -55,7 +55,7 @@
55
55
  "NOTICE"
56
56
  ],
57
57
  "dependencies": {
58
- "@aurea-uds/tokens": "^0.24.0"
58
+ "@aurea-uds/tokens": "^0.24.1"
59
59
  },
60
60
  "peerDependencies": {
61
61
  "@react-native-community/datetimepicker": ">=8",