@aurea-uds/native 0.13.0 → 0.15.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.
@@ -1,6 +1,6 @@
1
1
  import * as React from "react";
2
2
  import { type StyleProp, type ViewProps, type ViewStyle } from "react-native";
3
- import { type IconName } from "./icon.js";
3
+ import { type IconName, type AureaIcon } from "./icon.js";
4
4
  import { type AureaUniversalState } from "./strings.js";
5
5
  export type AureaSpinnerSize = "sm" | "md" | "lg";
6
6
  export interface SpinnerProps {
@@ -65,7 +65,7 @@ export interface AlertProps extends ViewProps {
65
65
  /** Um dos sete estados universais. Ele **escolhe a variante** e escreve o texto padrão. */
66
66
  state?: AureaUniversalState;
67
67
  title?: React.ReactNode;
68
- icon?: IconName;
68
+ icon?: AureaIcon;
69
69
  onDismiss?: () => void;
70
70
  children?: React.ReactNode;
71
71
  }
@@ -86,7 +86,12 @@ export interface AlertProps extends ViewProps {
86
86
  */
87
87
  export declare function Alert({ variant, state, title, icon, onDismiss, children, style, accessibilityLabel, accessibilityHint, ...rest }: AlertProps): React.JSX.Element;
88
88
  export interface EmptyStateProps {
89
- icon?: IconName;
89
+ /**
90
+ * O glifo, dentro de uma moldura redonda — R-15, 02/10/2026, escolhida pelo Victor na prancha
91
+ * (*"1 c"*). Moldura de 64 (o `Avatar` `lg` do HeroUI) e glifo de 32, o de antes. O desenho
92
+ * do próprio app entra aqui também (`icon={Logo}`, R-11).
93
+ */
94
+ icon?: AureaIcon;
90
95
  title: React.ReactNode;
91
96
  description?: React.ReactNode;
92
97
  action?: React.ReactNode;
@@ -110,7 +115,7 @@ export interface DataStateProps extends Omit<ViewProps, "children"> {
110
115
  /** Substitui o esqueleto padrão de `loading`. */
111
116
  skeleton?: React.ReactNode;
112
117
  emptyTitle?: React.ReactNode;
113
- emptyIcon?: IconName;
118
+ emptyIcon?: AureaIcon;
114
119
  action?: React.ReactNode;
115
120
  /** Função para o conteúdo caro não ser construído enquanto o estado o esconde. */
116
121
  children: React.ReactNode | (() => React.ReactNode);
package/dist/feedback.js CHANGED
@@ -24,7 +24,8 @@ import { Animated, Easing, View } from "react-native";
24
24
  import { IconButton } from "./actions.js";
25
25
  import { criarFolha, estadoAcessivel } from "./estilos.js";
26
26
  import { Icon } from "./icon.js";
27
- import { useReduceMotion } from "./movimento.js";
27
+ import { IconeEmMoldura } from "./moldura.js";
28
+ import { useReduceMotion, driverNativo } from "./movimento.js";
28
29
  import { gravidadeDoEstado } from "./strings.js";
29
30
  import { Text } from "./text.js";
30
31
  import { useAureaStrings, useAureaTokens } from "./theme.js";
@@ -96,7 +97,7 @@ export function Spinner({ size = "sm", label, decorative, style, testID }) {
96
97
  if (reduzir !== false)
97
98
  return;
98
99
  const laco = Animated.loop(Animated.timing(giro, {
99
- toValue: 1, duration: 700, easing: Easing.linear, useNativeDriver: true,
100
+ toValue: 1, duration: 700, easing: Easing.linear, useNativeDriver: driverNativo(),
100
101
  }));
101
102
  laco.start();
102
103
  // Sem o `stop` o laço sobrevive à desmontagem e continua acordando o JS — o vazamento
@@ -137,8 +138,8 @@ export function Skeleton({ width, height, radius, style, testID }) {
137
138
  // 1,35 s no total, com o vale em .55 na METADE — é o `@keyframes skeleton-pulse` do
138
139
  // `aurea.css:1112` lido literalmente: 675 ms para descer e 675 para voltar.
139
140
  const laco = Animated.loop(Animated.sequence([
140
- Animated.timing(opacidade, { toValue: 0.55, duration: 675, easing: Easing.inOut(Easing.ease), useNativeDriver: true }),
141
- Animated.timing(opacidade, { toValue: 1, duration: 675, easing: Easing.inOut(Easing.ease), useNativeDriver: true }),
141
+ Animated.timing(opacidade, { toValue: 0.55, duration: 675, easing: Easing.inOut(Easing.ease), useNativeDriver: driverNativo() }),
142
+ Animated.timing(opacidade, { toValue: 1, duration: 675, easing: Easing.inOut(Easing.ease), useNativeDriver: driverNativo() }),
142
143
  ]));
143
144
  laco.start();
144
145
  return () => laco.stop();
@@ -220,7 +221,7 @@ export function EmptyState({ icon = "file", title, description, action, state, s
220
221
  const s = folha(t);
221
222
  const strings = useAureaStrings();
222
223
  const desc = description ?? (state ? strings.universalState[state] : null);
223
- return (_jsxs(View, { testID: testID, style: [s.vazio, style], children: [_jsx(Icon, { name: icon, size: "xl", color: t.color.subtleForeground }), typeof title === "string"
224
+ return (_jsxs(View, { testID: testID, style: [s.vazio, style], children: [_jsx(IconeEmMoldura, { icon: icon, moldura: t.size.space16, glifo: t.size.iconXl }), typeof title === "string"
224
225
  ? _jsx(Text, { size: "base", weight: 600, leading: "tight", align: "center", children: title })
225
226
  : title, desc != null && (typeof desc === "string"
226
227
  ? _jsx(Text, { size: "sm", tone: "muted", align: "center", children: desc })
package/dist/icon.d.ts CHANGED
@@ -4,6 +4,17 @@ export type AureaIconComponent = (props: {
4
4
  size?: number;
5
5
  color?: string;
6
6
  }) => React.ReactElement;
7
+ /**
8
+ * Um ícone: o NOME de um glifo do registro (`"truck"`) ou o próprio DESENHO (`Logo`) — R-11,
9
+ * 02/10/2026. É o jeito do HeroUI: lá nenhuma peça recebe nome de ícone, o app põe o componente
10
+ * dele dentro. Aqui os dois valem em toda prop de ícone, e o desenho dispensa registro e
11
+ * `declare module`: o logotipo do app entra como `icon={Logo}`, com o `Logo` de `criarGlifo` ou o
12
+ * `default` de um `icons/*`. Ele recebe o `size` e a `color` que a peça pedir.
13
+ *
14
+ * ⚠ O desenho passado direto não tem forma cheia: no item escolhido ele sai igual (a forma cheia
15
+ * mora no registro, como `<nome>-fill`).
16
+ */
17
+ export type AureaIcon = IconName | AureaIconComponent;
7
18
  export type { IconName, PhosphorIconName, AureaIconNames, IconWeight } from "./icon-names.js";
8
19
  import type { IconName, IconWeight, PhosphorIconName } from "./icon-names.js";
9
20
  /**
@@ -75,6 +86,10 @@ export interface AureaGlifoDesenho extends Pintura {
75
86
  * // a traço: a tinta no desenho inteiro, como no `<svg>` raiz
76
87
  * const Logo = criarGlifo({fill: "none", stroke: "currentColor", strokeWidth: 2,
77
88
  * strokeLinecap: "round", paths: ["M…"]});
89
+ * // o caminho curto (R-11, 02/10/2026): o desenho entra direto em qualquer prop de ícone
90
+ * <Icon name={Marca} size="xl" />
91
+ * <BottomNav items={[{id: "inicio", label: "Início", icon: Marca}]} />
92
+ * // o caminho do registro, para usar por NOME (pede `AureaIconNames`)
78
93
  * const ICONES = criarRegistroDeIcones({...OS_DO_APP, marca: Marca});
79
94
  * <Icon name="marca" size="xl" />
80
95
  * ```
@@ -99,7 +114,8 @@ export declare function IconRegistryProvider({ registry, children }: {
99
114
  /** A escala de glifo dos tokens. Começa em `sm`: **não existe `--icon-xs`** — como na web. */
100
115
  export type AureaIconSize = "sm" | "md" | "lg" | "xl";
101
116
  export interface IconProps {
102
- name: IconName;
117
+ /** O nome de um glifo do registro, ou o próprio desenho (`AureaIcon`, R-11). */
118
+ name: AureaIcon;
103
119
  size?: AureaIconSize | number;
104
120
  /** Padrão: a cor de texto do tema. Ver a nota sobre herança abaixo. */
105
121
  color?: string;
package/dist/icon.js CHANGED
@@ -52,6 +52,10 @@ function pintar(forma, desenho, color) {
52
52
  * // a traço: a tinta no desenho inteiro, como no `<svg>` raiz
53
53
  * const Logo = criarGlifo({fill: "none", stroke: "currentColor", strokeWidth: 2,
54
54
  * strokeLinecap: "round", paths: ["M…"]});
55
+ * // o caminho curto (R-11, 02/10/2026): o desenho entra direto em qualquer prop de ícone
56
+ * <Icon name={Marca} size="xl" />
57
+ * <BottomNav items={[{id: "inicio", label: "Início", icon: Marca}]} />
58
+ * // o caminho do registro, para usar por NOME (pede `AureaIconNames`)
55
59
  * const ICONES = criarRegistroDeIcones({...OS_DO_APP, marca: Marca});
56
60
  * <Icon name="marca" size="xl" />
57
61
  * ```
@@ -99,7 +103,10 @@ export function Icon({ name, size = "md", color, icons, label, weight }) {
99
103
  const doContexto = React.useContext(Contexto);
100
104
  // Lido como mapa de texto: a chave `-fill` não é um `IconName`, e o tipo do registro já a aceita.
101
105
  const registro = (icons ?? doContexto);
102
- const Glifo = (weight === "fill" ? registro?.[`${name}-fill`] : undefined) ?? registro?.[name];
106
+ // O desenho passado direto (R-11) não consulta o registro.
107
+ const Glifo = typeof name === "function"
108
+ ? name
109
+ : (weight === "fill" ? registro?.[`${name}-fill`] : undefined) ?? registro?.[name];
103
110
  if (!Glifo) {
104
111
  if (__DEV__) {
105
112
  console.warn(`Aurea Icon: "${name}" não está no registro. Importe-o pelo caminho profundo e ` +
package/dist/index.d.ts CHANGED
@@ -7,7 +7,7 @@ export { criarFolha, comOpacidade } from "./estilos.js";
7
7
  export { Text, Heading, Paragraph, Code } from "./text.js";
8
8
  export type { TextProps, AureaTextFont, AureaTextLeading, AureaTextSize, AureaTextTone, AureaTextWeight, AureaTextType, HeadingProps, ParagraphProps, CodeProps, AureaTypographyColor, AureaTypographyWeight, AureaTypographyAlign, } from "./text.js";
9
9
  export { Icon, IconRegistryProvider, criarRegistroDeIcones, criarGlifo } from "./icon.js";
10
- export type { IconProps, IconName, PhosphorIconName, AureaIconNames, IconWeight, AureaIconComponent, AureaIconRegistry, AureaIconSize, AureaGlifoDesenho, AureaGlifoCaminho, AureaGlifoCirculo, AureaGlifoRetangulo, } from "./icon.js";
10
+ export type { IconProps, IconName, PhosphorIconName, AureaIconNames, IconWeight, AureaIconComponent, AureaIconRegistry, AureaIcon, AureaIconSize, AureaGlifoDesenho, AureaGlifoCaminho, AureaGlifoCirculo, AureaGlifoRetangulo, } from "./icon.js";
11
11
  export { Screen } from "./screen.js";
12
12
  export type { ScreenProps, AureaScreenBackground, AureaScreenEdge } from "./screen.js";
13
13
  export { Stack, Cluster, Grid, Card, Separator } from "./layout.js";
@@ -23,7 +23,7 @@ export { defaultStrings, ptBR, gravidadeDoEstado, AUREA_UNIVERSAL_STATES } from
23
23
  export type { AureaStrings, AureaUniversalState } from "./strings.js";
24
24
  export { useReduceMotion } from "./movimento.js";
25
25
  export { BottomNav, Topbar, NavList, Stepper, Tabs } from "./navigation.js";
26
- export type { BottomNavProps, TopbarProps, NavListProps, StepperProps, AureaNavItem, AureaNavListItem, AureaStepItem, AureaStepState, AureaBottomNavVariant, AureaBottomNavIndicator, AureaBottomNavWidth, AureaTopbarVariant, AureaTopbarInset, TabsProps, AureaTabItem, } from "./navigation.js";
26
+ export type { BottomNavProps, TopbarProps, NavListProps, StepperProps, AureaNavItem, AureaNavListItem, AureaStepItem, AureaStepState, AureaBottomNavVariant, AureaBottomNavIndicator, AureaBottomNavWidth, AureaTopbarVariant, AureaTopbarInset, TabsProps, AureaTabItem, AureaTabsVariant, } from "./navigation.js";
27
27
  export { Field, Label, Input, Textarea, Select, Switch, Checkbox, Radio, RadioGroup, SegmentedControl, Form, KeyboardAvoiding, useCampo, } from "./inputs.js";
28
28
  export type { FieldProps, LabelProps, InputProps, TextareaProps, SelectProps, SwitchProps, CheckboxProps, RadioProps, RadioGroupProps, RadioGroupItemProps, AureaRadioIndicatorPlacement, SegmentedControlProps, FormProps, KeyboardAvoidingProps, AureaFieldSize, } from "./inputs.js";
29
29
  export { InputGroup, InputGroupAddon, PasswordField, useGrupoDeCampo } from "./inputs.js";
@@ -39,7 +39,7 @@ export type { ChartProps, ChartLegendProps, AureaSeries, AureaChartMark } from "
39
39
  export { Combobox, SearchField } from "./busca.js";
40
40
  export type { ComboboxProps, SearchFieldProps, AureaComboboxItem } from "./busca.js";
41
41
  export { NumberField, formatarNumero, lerNumero, separadoresDoLocale } from "./numero.js";
42
- export type { NumberFieldProps, AureaSeparadores } from "./numero.js";
42
+ export type { NumberFieldProps, AureaSeparadores, AureaNumberFieldSize } from "./numero.js";
43
43
  export { Image, Gallery } from "./midia.js";
44
44
  export type { ImageProps, GalleryProps, AureaGalleryItem, AureaImageSource, } from "./midia.js";
45
45
  export type { AureaFilaJustify } from "./rolagem.js";
package/dist/inputs.d.ts CHANGED
@@ -1,8 +1,11 @@
1
1
  import * as React from "react";
2
2
  import { TextInput, type KeyboardTypeOptions, type PressableProps, type StyleProp, type TextInputProps, type TextStyle, type ViewProps, type ViewStyle } from "react-native";
3
3
  import { type AureaFilaJustify } from "./rolagem.js";
4
- import { type AureaIconRegistry, type IconName } from "./icon.js";
4
+ import { type AureaIconRegistry, type AureaIcon } from "./icon.js";
5
+ import type { AureaTokens } from "./tokens.js";
5
6
  export type AureaFieldSize = "sm" | "md" | "lg";
7
+ export declare const alturaDoTamanho: (t: AureaTokens, s: AureaFieldSize) => number;
8
+ export declare const respiroDoTamanho: (t: AureaTokens, s: AureaFieldSize) => number;
6
9
  type ContextoDeCampo = {
7
10
  label?: string;
8
11
  hint?: string;
@@ -282,10 +285,18 @@ export interface RadioGroupItemProps extends Omit<PressableProps, "children" | "
282
285
  invalid?: boolean;
283
286
  /** O lado da marca só neste item; sem ele, vale o do grupo. */
284
287
  indicatorPlacement?: AureaRadioIndicatorPlacement;
288
+ /**
289
+ * Um ícone antes do texto (R-19, 01/10/2026). O HeroUI não tem esta prop: no `RadioGroup.Item`
290
+ * dele o ícone entra como mais um filho, e a Aurea traduz filho livre em nome de ícone, como em
291
+ * todo o resto. Tamanho `iconLg` (24, o mesmo da marca; o HeroUI usa 22 nos ícones de lista, que
292
+ * não existe aqui — escolha do Victor), vão de 12 até o texto. Na opção escolhida sai a forma
293
+ * cheia (ADR-0053), se o registro a tiver.
294
+ */
295
+ icon?: AureaIcon;
285
296
  style?: StyleProp<ViewStyle>;
286
297
  }
287
298
  declare function RaizDoGrupoDeRadio({ value, onValueChange, disabled, invalid, label, orientation, indicatorPlacement, style, children, ...rest }: RadioGroupProps): React.JSX.Element;
288
- declare function ItemDoGrupoDeRadio({ value, label, description, disabled, invalid, indicatorPlacement, style, accessibilityLabel, ...rest }: RadioGroupItemProps): React.JSX.Element;
299
+ declare function ItemDoGrupoDeRadio({ value, label, description, disabled, invalid, indicatorPlacement, icon, style, accessibilityLabel, ...rest }: RadioGroupItemProps): React.JSX.Element;
289
300
  /**
290
301
  * Um conjunto de opções em que só uma fica escolhida — o `RadioGroup` do HeroUI Native.
291
302
  *
@@ -359,7 +370,7 @@ export interface SelectProps {
359
370
  disabled?: boolean;
360
371
  size?: AureaFieldSize;
361
372
  /** O glifo da seta. Registre-o, ou passe `false`. */
362
- chevron?: IconName | false;
373
+ chevron?: AureaIcon | false;
363
374
  style?: StyleProp<ViewStyle>;
364
375
  testID?: string;
365
376
  }
package/dist/inputs.js CHANGED
@@ -39,17 +39,20 @@ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-run
39
39
  // .segmented :922 linha, gap 3, padding 3, minH controlHMd, fundo muted
40
40
  import * as React from "react";
41
41
  import { Animated, KeyboardAvoidingView as KeyboardAvoidingViewRN, Modal, Platform, Pressable, ScrollView, TextInput, View, } from "react-native";
42
- import { comOpacidade, criarFolha, estadoAcessivel } from "./estilos.js";
42
+ import { criarFolha, estadoAcessivel, fioDoEscolhido } from "./estilos.js";
43
43
  import { FilaRolante } from "./rolagem.js";
44
44
  import { IconButton } from "./actions.js";
45
45
  import { Icon } from "./icon.js";
46
- import { useReduceMotion } from "./movimento.js";
46
+ import { useReduceMotion, driverNativo } from "./movimento.js";
47
47
  import { RecuoDaFolha } from "./screen.js";
48
48
  import { Text } from "./text.js";
49
49
  import { useAureaStrings, useAureaTokens, usePeleSobreAMarca, ForaDaMarca } from "./theme.js";
50
- const alturaDoTamanho = (t, s) => s === "sm" ? t.size.controlHSm : s === "lg" ? t.size.controlHLg : t.size.controlHMd;
50
+ // `alturaDoTamanho` e `respiroDoTamanho` saem do arquivo (não do pacote) desde 02/10/2026: o
51
+ // `FileInput` (R-21) desenha um gatilho com a medida do campo, e copiar os números para lá é o
52
+ // defeito que o `CLAUDE.md` nomeia.
53
+ export const alturaDoTamanho = (t, s) => s === "sm" ? t.size.controlHSm : s === "lg" ? t.size.controlHLg : t.size.controlHMd;
51
54
  const fonteDoTamanho = (t, s) => s === "sm" ? t.size.textSm : s === "lg" ? t.size.textLg : t.size.textBase; // ADR-0050: um degrau acima da web
52
- const respiroDoTamanho = (t, s) => s === "sm" ? t.size.space3 : s === "lg" ? t.size.space4 : 13;
55
+ export const respiroDoTamanho = (t, s) => s === "sm" ? t.size.space3 : s === "lg" ? t.size.space4 : 13;
53
56
  const folha = criarFolha((t) => ({
54
57
  campo: { gap: 7 },
55
58
  rotulo: { flexDirection: "row", justifyContent: "space-between", gap: 12 },
@@ -99,20 +102,9 @@ const folha = criarFolha((t) => ({
99
102
  segmento: { flex: 1, alignItems: "center", justifyContent: "center",
100
103
  paddingHorizontal: 14, borderRadius: t.size.radiusControl },
101
104
  segmentoAtivo: { backgroundColor: t.color.secondary },
102
- // 🔴 O FIO AMARELO DO ESCOLHIDO — ele faltava, e a falta era metade do defeito C4.
103
- // O `aurea.css:958-960` põe este fio em TODOS os selecionados da casa (`.is-selected`, aba
104
- // ativa, botão alternado e `.segmented button.active`), e o bloco de comentário acima dele
105
- // diz por quê, com data: *"Item de nav ativo, aba ativa, item de lista lateral e botão
106
- // alternado ligado mostram o MESMO sinal (…) Um usuário aprende uma vez e reconhece em todo
107
- // lugar (pedido do Victor, 24/07)."*
108
- // Os três números saem daquela linha, não de mim: altura 2 (fração de pixel borra), recuo 15,
109
- // e o amarelo a 75% (cheio ele grita mais que o próprio rótulo).
110
- // ⚠ Em segmento estreito o recuo de 15 de cada lado zera a largura e o fio some — **e é o
111
- // mesmo comportamento da web**, onde `inset-inline:15px` num box estreito não desenha nada.
112
- fioDoSegmento: {
113
- position: "absolute", left: 15, right: 15, bottom: 0, height: 2,
114
- borderRadius: t.size.radiusFull, backgroundColor: comOpacidade(t.color.primary, 0.75),
115
- },
105
+ // 🔴 O FIO AMARELO DO ESCOLHIDO — ele faltava, e a falta era metade do defeito C4. Os números
106
+ // e a razão moram no `fioDoEscolhido` (`estilos.ts`), que a aba do `Tabs` secundário também usa.
107
+ fioDoSegmento: fioDoEscolhido(t),
116
108
  formulario: { gap: t.size.space5 },
117
109
  // A folha do `Select`: no RN não existe `<select>`, então a lista é um `Modal` — que é
118
110
  // exatamente o "motor novo sobre `Modal`" que a §5.2.4 previu.
@@ -388,7 +380,7 @@ function RaizDoGrupoDeRadio({ value, onValueChange, disabled, invalid, label, or
388
380
  const contexto = React.useMemo(() => ({ value, onValueChange, disabled, invalid, indicatorPlacement }), [value, onValueChange, disabled, invalid, indicatorPlacement]);
389
381
  return (_jsx(GrupoDeRadio.Provider, { value: contexto, children: _jsx(View, { accessibilityRole: "radiogroup", accessibilityLabel: label, ...estadoAcessivel({ disabled: !!disabled }), style: [{ gap: t.size.space3, flexDirection: orientation === "horizontal" ? "row" : "column" }, style], ...rest, children: children }) }));
390
382
  }
391
- function ItemDoGrupoDeRadio({ value, label, description, disabled, invalid, indicatorPlacement, style, accessibilityLabel, ...rest }) {
383
+ function ItemDoGrupoDeRadio({ value, label, description, disabled, invalid, indicatorPlacement, icon, style, accessibilityLabel, ...rest }) {
392
384
  const t = useAureaTokens();
393
385
  const s = folha(t);
394
386
  const grupo = React.useContext(GrupoDeRadio);
@@ -403,7 +395,7 @@ function ItemDoGrupoDeRadio({ value, label, description, disabled, invalid, indi
403
395
  const noInicio = (indicatorPlacement ?? grupo?.indicatorPlacement ?? "end") === "start";
404
396
  const texto = (label != null || description != null) && (_jsxs(View, { style: { flex: 1, minWidth: 0 }, children: [typeof label === "string"
405
397
  ? _jsx(Text, { size: "base", weight: 500, tone: erro ? "danger" : "default", children: label }) : label, description != null && (typeof description === "string"
406
- ? _jsx(Text, { size: "sm", tone: "muted", children: description }) : description)] }, "texto"));
398
+ ? _jsx(Text, { size: "xs", tone: "muted", children: description }) : description)] }, "texto"));
407
399
  const marcaDesenhada = (_jsx(View, { style: [
408
400
  s.marcaBase,
409
401
  { width: marca, height: marca, borderRadius: t.size.radiusFull },
@@ -412,11 +404,14 @@ function ItemDoGrupoDeRadio({ value, label, description, disabled, invalid, indi
412
404
  borderColor: erro ? perigo : t.color.controlSelected },
413
405
  ], children: escolhido && _jsx(View, { style: { width: ponto, height: ponto, borderRadius: t.size.radiusFull,
414
406
  backgroundColor: t.color.controlSelectedForeground } }) }, "marca"));
407
+ // O ícone é decorativo: o rótulo já diz o que a opção é, e o `Icon` sem `label` sai da árvore
408
+ // de acessibilidade. A cor acompanha a do rótulo, inclusive no erro.
409
+ const glifo = icon != null && (_jsx(Icon, { name: icon, size: "lg", weight: escolhido ? "fill" : "regular", color: erro ? perigo : t.color.foreground }, "icone"));
415
410
  return (_jsx(Pressable, { onPress: inativo || escolhido ? undefined : () => grupo?.onValueChange?.(value), disabled: inativo, accessibilityRole: "radio", ...estadoAcessivel({ checked: escolhido, disabled: !!inativo }), accessibilityLabel: accessibilityLabel ?? (typeof label === "string" ? label : undefined), accessibilityHint: typeof description === "string" ? description : undefined, style: [
416
411
  { flexDirection: "row", alignItems: "center", justifyContent: "space-between",
417
412
  gap: t.size.space3, minHeight: t.size.targetMin },
418
413
  inativo && s.desabilitado, style,
419
- ], ...rest, children: noInicio ? [marcaDesenhada, texto] : [texto, marcaDesenhada] }));
414
+ ], ...rest, children: noInicio ? [marcaDesenhada, glifo, texto] : [glifo, texto, marcaDesenhada] }));
420
415
  }
421
416
  /**
422
417
  * Um conjunto de opções em que só uma fica escolhida — o `RadioGroup` do HeroUI Native.
@@ -465,7 +460,7 @@ export function Switch({ label, description, checked, onChange, disabled, size,
465
460
  }
466
461
  if (reduzir === null)
467
462
  return;
468
- const a = Animated.timing(pos, { toValue: destino, duration: 160, useNativeDriver: true });
463
+ const a = Animated.timing(pos, { toValue: destino, duration: 160, useNativeDriver: driverNativo() });
469
464
  a.start();
470
465
  return () => a.stop();
471
466
  }, [checked, pos, reduzir]);
package/dist/midia.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import * as React from "react";
2
2
  import { type ImageSourcePropType, type StyleProp, type ViewStyle } from "react-native";
3
- import { type IconName } from "./icon.js";
3
+ import { type AureaIcon } from "./icon.js";
4
4
  /** URL ou `require()` de um asset local — as duas formas do `Image` do RN, como no `Avatar`. */
5
5
  export type AureaImageSource = ImageSourcePropType | string;
6
6
  export interface ImageProps {
@@ -17,7 +17,7 @@ export interface ImageProps {
17
17
  /** O que aparece no lugar quando o bitmap não vem. Sem ele, o glifo `image` sobre a caixa reservada. */
18
18
  fallback?: React.ReactNode;
19
19
  /** O glifo do substituto. Registre-o, ou passe `false`. */
20
- fallbackIcon?: IconName | false;
20
+ fallbackIcon?: AureaIcon | false;
21
21
  /**
22
22
  * Desenha OUTRO componente de imagem com a nossa pele — é o caso do consumidor que já usa
23
23
  * `expo-image` por cache de disco.
@@ -96,3 +96,20 @@ export interface GalleryProps {
96
96
  * um alvo que engana quem navega por leitor de tela — mesma decisão da web, mesma razão.
97
97
  */
98
98
  export declare function Gallery({ items, label, selected, onSelect, zoom, ratio, minTileWidth, style, testID, }: GalleryProps): React.JSX.Element;
99
+ /**
100
+ * A foto grande, no `Dialog` — a da `Gallery` e, desde a R-22 (02/10/2026), a do `PhotoInput`.
101
+ * Morava dentro da `Gallery`; duas cópias do zoom é o defeito que o `CLAUDE.md` nomeia, e o dia em
102
+ * que uma das duas muda e a outra não. Não sai pelo barril: é peça de dentro.
103
+ *
104
+ * ⚠ Aberta quando há `source`. O `Dialog` fica montado e fechado no resto do tempo — é o contrato
105
+ * dele, e o `Modal` do RN não desenha nada fechado.
106
+ */
107
+ export declare function FotoAmpliada({ source, title, alt, ratio, onClose, testID }: {
108
+ source?: AureaImageSource;
109
+ /** O título da janela: uma caixa sem nome anuncia só "janela". */
110
+ title: string;
111
+ alt: string;
112
+ ratio?: number | string;
113
+ onClose: () => void;
114
+ testID?: string;
115
+ }): React.JSX.Element;
package/dist/midia.js CHANGED
@@ -177,5 +177,16 @@ export function Gallery({ items, label, selected, onSelect, zoom, ratio = 1, min
177
177
  // `alt=""` acima). Sem legenda, o nome é o `alt`.
178
178
  accessibilityLabel: temLegenda && typeof item.caption === "string"
179
179
  ? item.caption : item.alt, ...estadoAcessivel({ selected: item.id === selected }), style: [s.ladrilho, item.id === selected && s.ladrilhoEscolhido], children: miolo }, item.id));
180
- }) }), zoom && (_jsx(Dialog, { open: aberto != null, title: aberto ? (typeof aberto.caption === "string" ? aberto.caption : aberto.alt) : "", onClose: () => setAmpliado(null), testID: testID ? `${testID}-ampliada` : undefined, children: aberto && _jsx(Image, { source: aberto.source, alt: aberto.alt, fit: "contain", ratio: ratio }) }))] }));
180
+ }) }), zoom && (_jsx(FotoAmpliada, { source: aberto?.source, title: aberto ? (typeof aberto.caption === "string" ? aberto.caption : aberto.alt) : "", alt: aberto?.alt ?? "", ratio: ratio, onClose: () => setAmpliado(null), testID: testID ? `${testID}-ampliada` : undefined }))] }));
181
+ }
182
+ /**
183
+ * A foto grande, no `Dialog` — a da `Gallery` e, desde a R-22 (02/10/2026), a do `PhotoInput`.
184
+ * Morava dentro da `Gallery`; duas cópias do zoom é o defeito que o `CLAUDE.md` nomeia, e o dia em
185
+ * que uma das duas muda e a outra não. Não sai pelo barril: é peça de dentro.
186
+ *
187
+ * ⚠ Aberta quando há `source`. O `Dialog` fica montado e fechado no resto do tempo — é o contrato
188
+ * dele, e o `Modal` do RN não desenha nada fechado.
189
+ */
190
+ export function FotoAmpliada({ source, title, alt, ratio, onClose, testID }) {
191
+ return (_jsx(Dialog, { open: source != null, title: title, onClose: onClose, testID: testID, children: source != null && _jsx(Image, { source: source, alt: alt, fit: "contain", ratio: ratio }) }));
181
192
  }
@@ -0,0 +1,18 @@
1
+ import * as React from "react";
2
+ import { type TomDeCor } from "./estilos.js";
3
+ import { type AureaIcon } from "./icon.js";
4
+ export interface IconeEmMolduraProps {
5
+ icon: AureaIcon;
6
+ /** O lado da moldura, em dp — sempre de um token. */
7
+ moldura: number;
8
+ /** O lado do glifo, em dp — sempre de um token. */
9
+ glifo: number;
10
+ tone?: TomDeCor;
11
+ /**
12
+ * A cor de BAIXO, quando há algo atrás. No escuro os fundos suaves são translúcidos (o
13
+ * `successBg` é `oklch(0.42 0.11 150 / 0.36)`), e o trilho da `Timeline` aparecia através da
14
+ * moldura. É o mesmo truque do ponto da linha do tempo: a cor da superfície faz o buraco.
15
+ */
16
+ sobre?: string;
17
+ }
18
+ export declare function IconeEmMoldura({ icon, moldura, glifo, tone, sobre }: IconeEmMolduraProps): React.JSX.Element;
@@ -0,0 +1,15 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { View } from "react-native";
3
+ import { acentoDoTom, comOpacidade, fundoDoTom } from "./estilos.js";
4
+ import { Icon } from "./icon.js";
5
+ import { useAureaTokens } from "./theme.js";
6
+ export function IconeEmMoldura({ icon, moldura, glifo, tone = "neutral", sobre }) {
7
+ const t = useAureaTokens();
8
+ const fundo = tone === "neutral" ? t.color.muted
9
+ : tone === "primary" ? comOpacidade(t.color.primary, 0.1)
10
+ : fundoDoTom(t, tone);
11
+ const cor = tone === "neutral" ? t.color.mutedForeground : acentoDoTom(t, tone);
12
+ const redondo = { width: moldura, height: moldura, borderRadius: t.size.radiusFull, flexGrow: 0, flexShrink: 0 };
13
+ const circulo = (_jsx(View, { style: [redondo, { alignItems: "center", justifyContent: "center", backgroundColor: fundo }], children: _jsx(Icon, { name: icon, size: glifo, color: cor }) }));
14
+ return sobre == null ? circulo : (_jsx(View, { style: [redondo, { backgroundColor: sobre }], children: circulo }));
15
+ }
@@ -15,3 +15,15 @@
15
15
  * Quem só quer um booleano escreve `useReduceMotion() === true`.
16
16
  */
17
17
  export declare function useReduceMotion(): boolean | null;
18
+ /**
19
+ * O `useNativeDriver` de toda animação da Aurea — E13, 02/10/2026.
20
+ *
21
+ * No aparelho, `true`: a animação roda fora do JS. **No navegador não existe esse motor**: o
22
+ * React Native Web (0.21.3, `NativeAnimatedHelper.js:429`) avisa no console *"`useNativeDriver`
23
+ * is not supported"* toda vez que recebe `true`, e anima pelo JS do mesmo jeito. Então lá vai
24
+ * `false`, dito com todas as letras (sem a chave, ele avisa outra coisa: *"was not specified"*).
25
+ *
26
+ * Função, e não constante, para o teste conseguir trocar a plataforma entre um caso e outro.
27
+ * Quem pega o `useNativeDriver: true` escrito à mão é o `native-e13.test.tsx`.
28
+ */
29
+ export declare function driverNativo(): boolean;
package/dist/movimento.js CHANGED
@@ -14,7 +14,7 @@
14
14
  //
15
15
  // A pergunta é do sistema: iOS "Reduzir movimento", Android "Remover animações".
16
16
  import * as React from "react";
17
- import { AccessibilityInfo } from "react-native";
17
+ import { AccessibilityInfo, Platform } from "react-native";
18
18
  /**
19
19
  * `true` quando a pessoa pediu menos movimento, `false` quando não, **`null` enquanto o sistema
20
20
  * não respondeu**.
@@ -43,3 +43,17 @@ export function useReduceMotion() {
43
43
  }, []);
44
44
  return reduzir;
45
45
  }
46
+ /**
47
+ * O `useNativeDriver` de toda animação da Aurea — E13, 02/10/2026.
48
+ *
49
+ * No aparelho, `true`: a animação roda fora do JS. **No navegador não existe esse motor**: o
50
+ * React Native Web (0.21.3, `NativeAnimatedHelper.js:429`) avisa no console *"`useNativeDriver`
51
+ * is not supported"* toda vez que recebe `true`, e anima pelo JS do mesmo jeito. Então lá vai
52
+ * `false`, dito com todas as letras (sem a chave, ele avisa outra coisa: *"was not specified"*).
53
+ *
54
+ * Função, e não constante, para o teste conseguir trocar a plataforma entre um caso e outro.
55
+ * Quem pega o `useNativeDriver: true` escrito à mão é o `native-e13.test.tsx`.
56
+ */
57
+ export function driverNativo() {
58
+ return Platform.OS !== "web";
59
+ }
@@ -1,11 +1,11 @@
1
1
  import * as React from "react";
2
2
  import { type ViewProps } from "react-native";
3
- import { type IconName } from "./icon.js";
3
+ import { type AureaIcon } from "./icon.js";
4
4
  import { type AureaFilaJustify } from "./rolagem.js";
5
5
  export interface AureaNavItem {
6
6
  id: string;
7
7
  label: React.ReactNode;
8
- icon?: IconName;
8
+ icon?: AureaIcon;
9
9
  /** O contador de não lidos. Número vira selo; `true` vira ponto. */
10
10
  badge?: number | boolean;
11
11
  onPress?: () => void;
@@ -142,14 +142,14 @@ export interface AureaNavListItem {
142
142
  description?: React.ReactNode;
143
143
  /** O valor à direita — um número, um estado, uma data. */
144
144
  value?: React.ReactNode;
145
- icon?: IconName;
145
+ icon?: AureaIcon;
146
146
  onPress?: () => void;
147
147
  disabled?: boolean;
148
148
  }
149
149
  export interface NavListProps extends ViewProps {
150
150
  items: AureaNavListItem[];
151
151
  /** O glifo de "isto abre", à direita. Registre-o, ou passe `false`. */
152
- chevron?: IconName | false;
152
+ chevron?: AureaIcon | false;
153
153
  }
154
154
  /**
155
155
  * A lista de destinos DENTRO da página — a tela de ajustes, não a moldura do app.
@@ -174,8 +174,8 @@ export interface StepperProps extends ViewProps {
174
174
  items: AureaStepItem[];
175
175
  label?: string;
176
176
  /** Glifos de `done` e `error`. Registre-os, ou o passo mostra o número. */
177
- doneIcon?: IconName | false;
178
- errorIcon?: IconName | false;
177
+ doneIcon?: AureaIcon | false;
178
+ errorIcon?: AureaIcon | false;
179
179
  }
180
180
  /**
181
181
  * A trilha de passos — o onboarding de 3 passos que o consumidor já tem.
@@ -200,7 +200,16 @@ export interface TabsProps extends ViewProps {
200
200
  label?: string;
201
201
  /** Onde a fila de abas fica quando cabe: `start` (padrão), `center` ou `end` (E3). */
202
202
  justify?: AureaFilaJustify;
203
+ /**
204
+ * O jeito da fila, com os nomes do HeroUI (R-12, 01/10/2026):
205
+ * - `primary` (padrão): a cápsula, com a aba aberta num fundo `secondary`.
206
+ * - `secondary`: sem cápsula — um fio fino embaixo da fila inteira e o **fio amarelo** da casa
207
+ * embaixo da aba aberta, o mesmo do `SegmentedControl`.
208
+ */
209
+ variant?: AureaTabsVariant;
203
210
  }
211
+ /** Os dois jeitos da fila de abas, com os nomes do `Tabs` do HeroUI Native. */
212
+ export type AureaTabsVariant = "primary" | "secondary";
204
213
  /**
205
214
  * As abas DENTRO da tela — trocar o painel, não trocar de página.
206
215
  *
@@ -234,4 +243,4 @@ export interface TabsProps extends ViewProps {
234
243
  * largura de tela grande. Não há tablet medido neste projeto, e no nativo vale demanda antes de
235
244
  * cobertura — a mesma decisão da `Table` do Lote 6.
236
245
  */
237
- export declare function Tabs({ tabs, value, onChange, label, justify, style, ...rest }: TabsProps): React.JSX.Element;
246
+ export declare function Tabs({ tabs, value, onChange, label, justify, variant, style, ...rest }: TabsProps): React.JSX.Element;
@@ -3,7 +3,7 @@ import { Pressable, View } from "react-native";
3
3
  import { useSafeAreaInsets } from "react-native-safe-area-context";
4
4
  import { useAnotarBottomNav } from "./barranav.js";
5
5
  import { Badge } from "./display.js";
6
- import { comOpacidade, criarFolha, estadoAcessivel } from "./estilos.js";
6
+ import { comOpacidade, criarFolha, estadoAcessivel, fioDoEscolhido } from "./estilos.js";
7
7
  import { Icon } from "./icon.js";
8
8
  import { Card } from "./layout.js";
9
9
  import { FilaRolante } from "./rolagem.js";
@@ -133,6 +133,24 @@ const folha = criarFolha((t) => ({
133
133
  paddingHorizontal: 14, borderRadius: t.size.radiusControl,
134
134
  },
135
135
  abaDeTabAtiva: { backgroundColor: t.color.secondary },
136
+ // `variant="secondary"` (R-12, 01/10/2026): o `secondary` do HeroUI Native 1.0.10
137
+ // (`styles/components/tabs.css`), com o fio da casa no lugar do indicador azul deles.
138
+ // .tabs__list--variant-secondary padding 0 · border-bottom 1px, cor da borda
139
+ // .tabs__list gap `* 1` → space1
140
+ // .tabs__trigger padding-inline `* 3` → space3 · padding-block `* 1.5`
141
+ // → space1 + space05. Rótulo `text-base` + entrelinha 1.5 dá
142
+ // 36, que é o `controlHMd`: a altura da fila da cápsula, então
143
+ // trocar de variante não muda a altura da fila.
144
+ // .tabs__indicator--variant-secondary border-bottom 2px na cor de destaque → `fioDoEscolhido`
145
+ abasSecundarias: {
146
+ flexDirection: "row", gap: t.size.space1,
147
+ borderBottomWidth: t.size.borderWidth, borderColor: t.color.border,
148
+ },
149
+ abaSecundaria: {
150
+ alignItems: "center", justifyContent: "center", minHeight: t.size.controlHMd,
151
+ paddingHorizontal: t.size.space3, paddingVertical: t.size.space1 + t.size.space05,
152
+ },
153
+ fioDaAba: fioDoEscolhido(t),
136
154
  painel: { marginTop: t.size.space3 },
137
155
  // ── Stepper ────────────────────────────────────────────────────────────────────────────────
138
156
  trilha: { flexDirection: "row" },
@@ -418,16 +436,21 @@ export function Stepper({ items, label, doneIcon = "check", errorIcon = "x-circl
418
436
  * largura de tela grande. Não há tablet medido neste projeto, e no nativo vale demanda antes de
419
437
  * cobertura — a mesma decisão da `Table` do Lote 6.
420
438
  */
421
- export function Tabs({ tabs, value, onChange, label, justify, style, ...rest }) {
439
+ export function Tabs({ tabs, value, onChange, label, justify, variant = "primary", style, ...rest }) {
422
440
  const t = useAureaTokens();
423
441
  const s = folha(t);
424
442
  const strings = useAureaStrings();
425
443
  const aberta = tabs.find((it) => it.id === value);
426
- return (_jsxs(View, { style: style, ...rest, children: [_jsx(FilaRolante, { justify: justify, children: _jsx(View, { accessibilityRole: "tablist", accessibilityLabel: label ?? strings.tabsLabel, style: s.abas, children: tabs.map((it) => {
444
+ const secundaria = variant === "secondary";
445
+ return (_jsxs(View, { style: style, ...rest, children: [_jsx(FilaRolante, { justify: justify, children: _jsx(View, { accessibilityRole: "tablist", accessibilityLabel: label ?? strings.tabsLabel, style: secundaria ? s.abasSecundarias : s.abas, children: tabs.map((it) => {
427
446
  const ativa = it.id === value;
428
- return (_jsx(Pressable, { onPress: it.disabled ? undefined : () => onChange?.(it.id), disabled: it.disabled, accessibilityRole: "tab", ...estadoAcessivel({ selected: ativa, disabled: !!it.disabled }), accessibilityLabel: typeof it.label === "string" ? it.label : undefined, style: [s.abaDeTab, ativa && s.abaDeTabAtiva, it.disabled && { opacity: t.size.opacityDisabled }], children: typeof it.label === "string"
429
- ? _jsx(Text, { size: "sm", weight: ativa ? 600 : 400, style: { color: ativa ? t.color.foreground : t.color.mutedForeground }, children: it.label })
430
- : it.label }, it.id));
447
+ return (_jsxs(Pressable, { onPress: it.disabled ? undefined : () => onChange?.(it.id), disabled: it.disabled, accessibilityRole: "tab", ...estadoAcessivel({ selected: ativa, disabled: !!it.disabled }), accessibilityLabel: typeof it.label === "string" ? it.label : undefined, style: [
448
+ secundaria ? s.abaSecundaria : s.abaDeTab,
449
+ ativa && !secundaria && s.abaDeTabAtiva,
450
+ it.disabled && { opacity: t.size.opacityDisabled },
451
+ ], children: [typeof it.label === "string"
452
+ ? _jsx(Text, { size: "sm", weight: ativa ? 600 : 400, style: { color: ativa ? t.color.foreground : t.color.mutedForeground }, children: it.label })
453
+ : it.label, ativa && secundaria && _jsx(View, { style: s.fioDaAba })] }, it.id));
431
454
  }) }) }), aberta
432
455
  ? (_jsx(Card, { variant: "inset", accessibilityLabel: typeof aberta.label === "string" ? aberta.label : undefined, style: s.painel, children: aberta.content }))
433
456
  : null] }));
package/dist/numero.d.ts CHANGED
@@ -1,7 +1,19 @@
1
1
  import * as React from "react";
2
2
  import { type StyleProp, type ViewStyle } from "react-native";
3
- import type { IconName } from "./icon-names.js";
3
+ import type { AureaIcon } from "./icon.js";
4
4
  import { type AureaFieldSize } from "./inputs.js";
5
+ /**
6
+ * Os tamanhos do `NumberField`: os três do campo e mais o `display` (R-16, 01/10/2026), o número
7
+ * grande de destaque. Nenhuma referência da fila tem esse tamanho (o `NumberField` do HeroUI web
8
+ * só tem `variant`; o telefone dele nem tem a peça; o ReUI também não), então ele é peça nossa —
9
+ * e as medidas saem de escalas que JÁ existem, nenhuma nova:
10
+ * letra `text3xl` (30) em seminegrito — o título 2 (`h2` do B-02). Escolha do Victor.
11
+ * altura `controlHXl` — o degrau seguinte da escala de altura dos controles, que muda com a
12
+ * densidade como as outras três.
13
+ * largura `space24` (96): o `space16` (64) do campo comum não cabe "1.234" em 30.
14
+ * botões os do `lg`, o maior do `IconButton`.
15
+ */
16
+ export type AureaNumberFieldSize = AureaFieldSize | "display";
5
17
  /** Os separadores de um locale, derivados de um número-sonda. */
6
18
  export type AureaSeparadores = {
7
19
  decimal: string;
@@ -52,7 +64,11 @@ export interface NumberFieldProps {
52
64
  disabled?: boolean;
53
65
  /** Mostra o valor e não deixa editar — os botões também somem. */
54
66
  readOnly?: boolean;
55
- size?: AureaFieldSize;
67
+ /**
68
+ * `sm` · `md` · `lg`, como os outros campos, e `display`: o número grande de destaque, com a
69
+ * letra do título 2 (30). Ver `AureaNumberFieldSize`. Moeda em `display` pede `fullWidth`.
70
+ */
71
+ size?: AureaNumberFieldSize;
56
72
  /**
57
73
  * Ocupa a largura disponível em vez de abraçar o conteúdo. Padrão **false**.
58
74
  *
@@ -72,8 +88,8 @@ export interface NumberFieldProps {
72
88
  keyboardType?: "numeric" | "decimal-pad" | "number-pad" | "numbers-and-punctuation";
73
89
  /** Os glifos dos botões. Registre-os, ou passe `false` para tirar os dois. */
74
90
  icons?: {
75
- increment: IconName;
76
- decrement: IconName;
91
+ increment: AureaIcon;
92
+ decrement: AureaIcon;
77
93
  } | false;
78
94
  style?: StyleProp<ViewStyle>;
79
95
  testID?: string;