@aurea-uds/native 0.14.1 → 0.16.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/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";
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;
@@ -289,7 +292,7 @@ export interface RadioGroupItemProps extends Omit<PressableProps, "children" | "
289
292
  * não existe aqui — escolha do Victor), vão de 12 até o texto. Na opção escolhida sai a forma
290
293
  * cheia (ADR-0053), se o registro a tiver.
291
294
  */
292
- icon?: IconName;
295
+ icon?: AureaIcon;
293
296
  style?: StyleProp<ViewStyle>;
294
297
  }
295
298
  declare function RaizDoGrupoDeRadio({ value, onValueChange, disabled, invalid, label, orientation, indicatorPlacement, style, children, ...rest }: RadioGroupProps): React.JSX.Element;
@@ -367,7 +370,7 @@ export interface SelectProps {
367
370
  disabled?: boolean;
368
371
  size?: AureaFieldSize;
369
372
  /** O glifo da seta. Registre-o, ou passe `false`. */
370
- chevron?: IconName | false;
373
+ chevron?: AureaIcon | false;
371
374
  style?: StyleProp<ViewStyle>;
372
375
  testID?: string;
373
376
  }
package/dist/inputs.js CHANGED
@@ -43,13 +43,16 @@ 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 },
@@ -457,7 +460,7 @@ export function Switch({ label, description, checked, onChange, disabled, size,
457
460
  }
458
461
  if (reduzir === null)
459
462
  return;
460
- const a = Animated.timing(pos, { toValue: destino, duration: 160, useNativeDriver: true });
463
+ const a = Animated.timing(pos, { toValue: destino, duration: 160, useNativeDriver: driverNativo() });
461
464
  a.start();
462
465
  return () => a.stop();
463
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.
package/dist/numero.d.ts CHANGED
@@ -1,6 +1,6 @@
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
5
  /**
6
6
  * Os tamanhos do `NumberField`: os três do campo e mais o `display` (R-16, 01/10/2026), o número
@@ -88,8 +88,8 @@ export interface NumberFieldProps {
88
88
  keyboardType?: "numeric" | "decimal-pad" | "number-pad" | "numbers-and-punctuation";
89
89
  /** Os glifos dos botões. Registre-os, ou passe `false` para tirar os dois. */
90
90
  icons?: {
91
- increment: IconName;
92
- decrement: IconName;
91
+ increment: AureaIcon;
92
+ decrement: AureaIcon;
93
93
  } | false;
94
94
  style?: StyleProp<ViewStyle>;
95
95
  testID?: string;
package/dist/overlays.js CHANGED
@@ -63,7 +63,7 @@ import { IconButton } from "./actions.js";
63
63
  import { RecuoDaFolha } from "./screen.js";
64
64
  import { Text } from "./text.js";
65
65
  import { useAureaStrings, useAureaTokens, ForaDaMarca } from "./theme.js";
66
- import { useReduceMotion } from "./movimento.js";
66
+ import { useReduceMotion, driverNativo } from "./movimento.js";
67
67
  const folha = criarFolha((t) => ({
68
68
  fundo: { flex: 1, backgroundColor: t.color.overlay },
69
69
  // 🔴 O FUNDO TOCÁVEL VIROU IRMÃO, NÃO ANCESTRAL — 10/09/2026, e a razão está no bloco
@@ -226,7 +226,7 @@ export function Drawer({ open, title, children, onClose, side = "right", scroll
226
226
  return;
227
227
  }
228
228
  const laco = Animated.timing(progresso, {
229
- toValue: 1, duration: DURACAO, useNativeDriver: true,
229
+ toValue: 1, duration: DURACAO, useNativeDriver: driverNativo(),
230
230
  easing: Easing.bezier(...t.easing.easeEmphasized),
231
231
  });
232
232
  laco.start();
@@ -289,7 +289,7 @@ export function BottomSheet({ open, title, children, onClose, grabber = true, dr
289
289
  return;
290
290
  }
291
291
  Animated.timing(arrasto, {
292
- toValue: 0, duration: DURACAO, useNativeDriver: true,
292
+ toValue: 0, duration: DURACAO, useNativeDriver: driverNativo(),
293
293
  easing: Easing.bezier(...t.easing.easeEmphasized),
294
294
  }).start();
295
295
  },
package/dist/sistema.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import * as React from "react";
2
2
  import { type StyleProp, type ViewStyle } from "react-native";
3
- import { type IconName } from "./icon.js";
3
+ import { type AureaIcon } from "./icon.js";
4
4
  import { type AureaFieldSize } from "./inputs.js";
5
5
  export interface DatePickerProps {
6
6
  value?: Date;
@@ -14,7 +14,7 @@ export interface DatePickerProps {
14
14
  /** Como a data vira texto no gatilho. Padrão: o formato do aparelho. */
15
15
  format?: (d: Date) => string;
16
16
  placeholder?: string;
17
- icon?: IconName | false;
17
+ icon?: AureaIcon | false;
18
18
  style?: StyleProp<ViewStyle>;
19
19
  testID?: string;
20
20
  }
@@ -56,8 +56,8 @@ export interface PhotoInputProps {
56
56
  offerSettings?: boolean;
57
57
  /** Avisado quando a permissão foi negada — o app pode querer contar uma história própria. */
58
58
  onPermissionDenied?: () => void;
59
- addIcon?: IconName | false;
60
- removeIcon?: IconName;
59
+ addIcon?: AureaIcon | false;
60
+ removeIcon?: AureaIcon;
61
61
  style?: StyleProp<ViewStyle>;
62
62
  testID?: string;
63
63
  }
@@ -78,5 +78,17 @@ export interface PhotoInputProps {
78
78
  * | **quantas** cabem? | uma | `max` |
79
79
  *
80
80
  * ⚠ **O gatilho some quando o limite é atingido**, em vez de ficar aceso e não fazer nada.
81
+ *
82
+ * **R-22 (02/10/2026), achado do app: a pessoa não conseguia OLHAR a foto que escolheu.** A
83
+ * miniatura era um `Avatar` redondo de 42, sem toque, e o X (sem fundo, só 8 para fora) cobria
84
+ * metade dela. Agora, na "B" da prancha escolhida pelo Victor:
85
+ *
86
+ * | | |
87
+ * |---|---|
88
+ * | miniatura | quadrada de 64, a `Image` da `Gallery` (o `Avatar` `lg` do HeroUI) |
89
+ * | tocar nela | abre a foto grande, no MESMO zoom da `Gallery` (`FotoAmpliada`) |
90
+ * | o X | com fundo, TODO fora da foto, no canto de cima à direita |
91
+ * | leitor de tela | *"Foto 2 de 3"* (com uma só, *"Foto"*) e *"Abre a foto"*; o X, *"Remover foto 2"* |
92
+ * | inativo | a foto ainda abre (olhar não muda nada); o X não remove |
81
93
  */
82
94
  export declare function PhotoInput({ value, onChange, max, source, disabled, offerSettings, onPermissionDenied, addIcon, removeIcon, style, testID, }: PhotoInputProps): React.JSX.Element;
package/dist/sistema.js CHANGED
@@ -35,11 +35,11 @@ import { Linking, Platform, Pressable, View } from "react-native";
35
35
  import RNDateTimePicker, { DateTimePickerAndroid } from "@react-native-community/datetimepicker";
36
36
  import * as ImagePicker from "expo-image-picker";
37
37
  import { IconButton } from "./actions.js";
38
- import { Avatar } from "./display.js";
39
38
  import { criarFolha, estadoAcessivel } from "./estilos.js";
40
39
  import { Alert } from "./feedback.js";
41
40
  import { Icon } from "./icon.js";
42
41
  import { useCampo } from "./inputs.js";
42
+ import { FotoAmpliada, Image } from "./midia.js";
43
43
  import { Text } from "./text.js";
44
44
  import { useAureaStrings, useAureaTokens } from "./theme.js";
45
45
  const folha = criarFolha((t) => ({
@@ -50,16 +50,28 @@ const folha = criarFolha((t) => ({
50
50
  },
51
51
  invalido: { borderColor: t.color.danger400 ?? t.color.destructive },
52
52
  desabilitado: { opacity: t.size.opacityDisabled },
53
- galeria: { flexDirection: "row", flexWrap: "wrap", gap: t.size.space2 },
54
- miniatura: { position: "relative" },
55
- remover: { position: "absolute", top: -t.size.space2, right: -t.size.space2 },
53
+ // R-22 (02/10/2026), a "B" da prancha: miniatura QUADRADA de 64 — o `Avatar` `lg` do HeroUI
54
+ // Native, e a medida que o botão de pôr foto passou a ter também (era 72, número à mão) — e o X
55
+ // TODO FORA da foto. O X é o `IconButton` `sm` (30) com fundo; a área de toque dele é de 44
56
+ // (`targetMin`), com o desenho no centro, então o deslocamento que põe o canto do DESENHO no
57
+ // canto da foto é (30 + 44) / 2. A grade deixa esse espaço em cima, à direita e entre as linhas,
58
+ // porque no Android o toque fora da caixa do pai não chega.
59
+ galeria: {
60
+ flexDirection: "row", flexWrap: "wrap", columnGap: t.size.space4,
61
+ rowGap: foraDoX(t) + t.size.space2,
62
+ },
63
+ comX: { paddingTop: foraDoX(t), paddingRight: foraDoX(t) },
64
+ miniatura: { position: "relative", width: t.size.space16 },
65
+ remover: { position: "absolute", top: -foraDoX(t), right: -foraDoX(t) },
56
66
  adicionar: {
57
67
  alignItems: "center", justifyContent: "center", gap: t.size.space1,
58
- width: 72, height: 72, borderRadius: t.size.radiusMd,
68
+ width: t.size.space16, height: t.size.space16, borderRadius: t.size.radiusLg,
59
69
  borderWidth: t.size.borderWidth, borderColor: t.color.borderStrong,
60
70
  borderStyle: "dashed", backgroundColor: t.color.fieldBg,
61
71
  },
62
72
  }));
73
+ /** Quanto o X do `PhotoInput` sai da foto: metade do desenho (30) mais metade do alvo (44). */
74
+ const foraDoX = (t) => (t.size.controlHSm + Math.max(t.size.controlHSm, t.size.targetMin)) / 2;
63
75
  const alturaDoTamanho = (t, s) => s === "sm" ? t.size.controlHSm : s === "lg" ? t.size.controlHLg : t.size.controlHMd;
64
76
  /**
65
77
  * O campo de data — **a nossa pele, o calendário DELES**.
@@ -133,6 +145,18 @@ export function DatePicker({ value, onChange, mode = "date", minimumDate, maximu
133
145
  * | **quantas** cabem? | uma | `max` |
134
146
  *
135
147
  * ⚠ **O gatilho some quando o limite é atingido**, em vez de ficar aceso e não fazer nada.
148
+ *
149
+ * **R-22 (02/10/2026), achado do app: a pessoa não conseguia OLHAR a foto que escolheu.** A
150
+ * miniatura era um `Avatar` redondo de 42, sem toque, e o X (sem fundo, só 8 para fora) cobria
151
+ * metade dela. Agora, na "B" da prancha escolhida pelo Victor:
152
+ *
153
+ * | | |
154
+ * |---|---|
155
+ * | miniatura | quadrada de 64, a `Image` da `Gallery` (o `Avatar` `lg` do HeroUI) |
156
+ * | tocar nela | abre a foto grande, no MESMO zoom da `Gallery` (`FotoAmpliada`) |
157
+ * | o X | com fundo, TODO fora da foto, no canto de cima à direita |
158
+ * | leitor de tela | *"Foto 2 de 3"* (com uma só, *"Foto"*) e *"Abre a foto"*; o X, *"Remover foto 2"* |
159
+ * | inativo | a foto ainda abre (olhar não muda nada); o X não remove |
136
160
  */
137
161
  export function PhotoInput({ value = [], onChange, max = 1, source = "camera", disabled, offerSettings = true, onPermissionDenied, addIcon = "camera", removeIcon = "x", style, testID, }) {
138
162
  const t = useAureaTokens();
@@ -141,7 +165,12 @@ export function PhotoInput({ value = [], onChange, max = 1, source = "camera", d
141
165
  const campo = useCampo();
142
166
  const inativo = disabled ?? campo?.disabled;
143
167
  const [negadoDeVez, setNegadoDeVez] = React.useState(false);
168
+ const [aberta, setAberta] = React.useState(null);
144
169
  const cheio = value.length >= max;
170
+ // "Foto 2 de 3": a posição entre as que EXISTEM, e não entre as que cabem (`max`).
171
+ const nomeDa = (n) => value.length > 1
172
+ ? `${strings.photo} ${n + 1} ${strings.positionOf} ${value.length}` : strings.photo;
173
+ const fotoAberta = aberta != null ? value[aberta] : undefined;
145
174
  const escolher = React.useCallback(async () => {
146
175
  if (source === "camera") {
147
176
  const atual = await ImagePicker.getCameraPermissionsAsync();
@@ -166,5 +195,7 @@ export function PhotoInput({ value = [], onChange, max = 1, source = "camera", d
166
195
  .map((a) => ({ uri: a.uri, width: a.width, height: a.height }));
167
196
  onChange?.([...value, ...novas]);
168
197
  }, [source, max, value, onChange, onPermissionDenied]);
169
- return (_jsxs(View, { testID: testID, style: style, children: [negadoDeVez && (_jsx(Alert, { variant: "warning", children: _jsxs(View, { style: { gap: t.size.space2 }, children: [_jsx(Text, { size: "sm", tone: "muted", children: strings.cameraDenied }), offerSettings && (_jsx(Pressable, { onPress: () => Linking.openSettings(), accessibilityRole: "button", accessibilityLabel: strings.openSettings, children: _jsx(Text, { size: "sm", weight: 600, tone: "primary", children: strings.openSettings }) }))] }) })), _jsxs(View, { style: s.galeria, children: [value.map((foto, n) => (_jsxs(View, { style: s.miniatura, children: [_jsx(Avatar, { source: foto.uri, size: "lg", alt: "" }), _jsx(View, { style: s.remover, children: _jsx(IconButton, { appearance: "ghost", size: "sm", name: removeIcon, label: strings.photoRemove, onPress: () => onChange?.(value.filter((_, i) => i !== n)) }) })] }, foto.uri))), !cheio && (_jsx(Pressable, { onPress: inativo ? undefined : escolher, disabled: inativo, accessibilityRole: "button", accessibilityLabel: campo?.label, ...estadoAcessivel({ disabled: !!inativo }), style: [s.adicionar, inativo && s.desabilitado], children: addIcon && _jsx(Icon, { name: addIcon, size: "lg", color: t.color.subtleForeground }) }))] })] }));
198
+ return (_jsxs(View, { testID: testID, style: style, children: [negadoDeVez && (_jsx(Alert, { variant: "warning", children: _jsxs(View, { style: { gap: t.size.space2 }, children: [_jsx(Text, { size: "sm", tone: "muted", children: strings.cameraDenied }), offerSettings && (_jsx(Pressable, { onPress: () => Linking.openSettings(), accessibilityRole: "button", accessibilityLabel: strings.openSettings, children: _jsx(Text, { size: "sm", weight: 600, tone: "primary", children: strings.openSettings }) }))] }) })), _jsxs(View, { style: [s.galeria, value.length > 0 && s.comX], children: [value.map((foto, n) => (_jsxs(View, { style: s.miniatura, children: [_jsx(Pressable, { onPress: () => setAberta(n), accessibilityRole: "imagebutton", accessibilityLabel: nomeDa(n), accessibilityHint: strings.photoOpen, children: _jsx(Image, { source: foto.uri, alt: "", ratio: 1 }) }), _jsx(View, { style: s.remover, children: _jsx(IconButton, { appearance: "solid", size: "sm", name: removeIcon, label: `${strings.photoRemove} ${n + 1}`, disabled: inativo, onPress: () => onChange?.(value.filter((_, i) => i !== n)) }) })] }, `${foto.uri}-${n}`))), !cheio && (_jsx(Pressable, { onPress: inativo ? undefined : escolher, disabled: inativo, accessibilityRole: "button", accessibilityLabel: campo?.label ?? strings.photoAdd, ...estadoAcessivel({ disabled: !!inativo }), style: [s.adicionar, inativo && s.desabilitado], children: addIcon && _jsx(Icon, { name: addIcon, size: "lg", color: t.color.subtleForeground }) }))] }), _jsx(FotoAmpliada, { source: fotoAberta?.uri, title: aberta != null ? nomeDa(aberta) : "", alt: aberta != null ? nomeDa(aberta) : "",
199
+ // A proporção da foto, quando a câmera a deu: a grande aparece inteira, sem tarja à toa.
200
+ ratio: fotoAberta?.width && fotoAberta.height ? fotoAberta.width / fotoAberta.height : 1, onClose: () => setAberta(null) })] }));
170
201
  }
package/dist/strings.d.ts CHANGED
@@ -32,8 +32,18 @@ export interface AureaStrings {
32
32
  cameraDenied: string;
33
33
  /** O botão que leva às configurações do app. */
34
34
  openSettings: string;
35
- /** O rótulo do botão que tira uma foto anexada. */
35
+ /** O rótulo do botão que tira uma foto anexada. O número da foto vem depois (R-22). */
36
36
  photoRemove: string;
37
+ /**
38
+ * O nome de cada miniatura do `PhotoInput` (R-22): *"Foto 2 de 3"* — com uma foto só, só
39
+ * *"Foto"*. O `positionOf` é a mesma chave da web.
40
+ */
41
+ photo: string;
42
+ positionOf: string;
43
+ /** O que a miniatura faz, dito pelo leitor de tela depois do nome (R-22). */
44
+ photoOpen: string;
45
+ /** O nome do botão de pôr foto quando ele não está num `Field` (R-22): sem isto, ficava mudo. */
46
+ photoAdd: string;
37
47
  /**
38
48
  * O botão que revela a senha no `PasswordField`, e o que a esconde de volta. Mesmos nomes da
39
49
  * web (`pure.tsx`), porque é a mesma peça com a mesma decisão: **o rótulo é que anuncia o
@@ -83,6 +93,17 @@ export interface AureaStrings {
83
93
  searchClear: string;
84
94
  /** O nome da grade de fotos da `Gallery`. Mesmo nome da web. */
85
95
  galleryLabel: string;
96
+ /**
97
+ * O texto do gatilho do `FileInput` (R-21). **Não existe na web**: lá a área de soltar diz
98
+ * *"Arraste arquivos aqui ou clique para selecionar"*, e no telefone não há o que arrastar.
99
+ */
100
+ fileChoose: string;
101
+ /** O botão que tira um arquivo escolhido; o nome do arquivo vem depois. Mesmo nome da web. */
102
+ fileRemove: string;
103
+ /** O aviso do arquivo maior que o `maxSize`. Mesmo nome — e mesma frase — da web. */
104
+ fileTooLarge: string;
105
+ /** O aviso do arquivo de um tipo que o `accept` não aceita. Mesmo nome e frase da web. */
106
+ fileWrongType: string;
86
107
  /** A frase de cada estado universal. */
87
108
  universalState: Record<AureaUniversalState, string>;
88
109
  }
package/dist/strings.js CHANGED
@@ -38,6 +38,10 @@ export const defaultStrings = {
38
38
  cameraDenied: "Camera access is off. Turn it on in Settings to attach a photo.",
39
39
  openSettings: "Open Settings",
40
40
  photoRemove: "Remove photo",
41
+ photo: "Photo",
42
+ positionOf: "of",
43
+ photoOpen: "Opens the photo",
44
+ photoAdd: "Add photo",
41
45
  passwordShow: "Show password",
42
46
  passwordHide: "Hide password",
43
47
  close: "Close",
@@ -56,6 +60,10 @@ export const defaultStrings = {
56
60
  comboboxSearch: "Search",
57
61
  searchClear: "Clear search",
58
62
  galleryLabel: "Gallery",
63
+ fileChoose: "Choose file",
64
+ fileRemove: "Remove",
65
+ fileTooLarge: "File exceeds the size limit",
66
+ fileWrongType: "File type not allowed",
59
67
  universalState: {
60
68
  waiting_user: "Waiting for someone to act.",
61
69
  waiting_approval: "Waiting for approval.",
@@ -78,6 +86,10 @@ export const ptBR = {
78
86
  cameraDenied: "O acesso à câmera está desligado. Ligue nas configurações para anexar uma foto.",
79
87
  openSettings: "Abrir configurações",
80
88
  photoRemove: "Remover foto",
89
+ photo: "Foto",
90
+ positionOf: "de",
91
+ photoOpen: "Abre a foto",
92
+ photoAdd: "Adicionar foto",
81
93
  passwordShow: "Mostrar senha",
82
94
  passwordHide: "Ocultar senha",
83
95
  close: "Fechar",
@@ -96,6 +108,10 @@ export const ptBR = {
96
108
  comboboxSearch: "Buscar",
97
109
  searchClear: "Limpar busca",
98
110
  galleryLabel: "Galeria",
111
+ fileChoose: "Escolher arquivo",
112
+ fileRemove: "Remover",
113
+ fileTooLarge: "Arquivo maior que o limite",
114
+ fileWrongType: "Tipo de arquivo não aceito",
99
115
  universalState: {
100
116
  waiting_user: "Esperando alguém agir.",
101
117
  waiting_approval: "Esperando aprovação.",
package/dist/toast.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import * as React from "react";
2
2
  import { type StyleProp, type ViewStyle } from "react-native";
3
- import { type IconName } from "./icon.js";
3
+ import { type AureaIcon } from "./icon.js";
4
4
  import { type AureaAlertVariant } from "./feedback.js";
5
5
  /** As mesmas quatro faces do `AureaToastType` da web (`system.tsx:65`). */
6
6
  export type AureaToastType = AureaAlertVariant;
@@ -17,7 +17,7 @@ export interface AureaToastInput {
17
17
  /** Uma ação curta — "Desfazer". Ver o aviso sobre `duration` acima. */
18
18
  action?: React.ReactNode;
19
19
  /** Deixe de fora para não desenhar ícone nenhum; sem isto vem o do tipo. */
20
- icon?: IconName | false;
20
+ icon?: AureaIcon | false;
21
21
  }
22
22
  export interface AureaToast extends AureaToastInput {
23
23
  id: string;
package/dist/toast.js CHANGED
@@ -60,7 +60,7 @@ import { IconButton } from "./actions.js";
60
60
  import { criarFolha } from "./estilos.js";
61
61
  import { Icon } from "./icon.js";
62
62
  import { ICONE_DA_VARIANTE } from "./feedback.js";
63
- import { useReduceMotion } from "./movimento.js";
63
+ import { useReduceMotion, driverNativo } from "./movimento.js";
64
64
  import { Text } from "./text.js";
65
65
  import { useAureaStrings, useAureaTokens } from "./theme.js";
66
66
  const folha = criarFolha((t) => ({
@@ -185,7 +185,7 @@ export function Toast({ toast, onClose }) {
185
185
  return;
186
186
  }
187
187
  const laco = Animated.timing(entrada, {
188
- toValue: 1, duration: ENTRADA, useNativeDriver: true,
188
+ toValue: 1, duration: ENTRADA, useNativeDriver: driverNativo(),
189
189
  easing: Easing.bezier(...t.easing.easeEmphasized),
190
190
  });
191
191
  laco.start();