@aurea-uds/native 0.8.12 → 0.8.14

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.js CHANGED
@@ -22,7 +22,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
22
22
  // do Lote 3. Aparência sem consumidor é a ADR-0034 ("ter a variante não é usar").
23
23
  import * as React from "react";
24
24
  import { Pressable, View } from "react-native";
25
- import { criarFolha } from "./estilos.js";
25
+ import { criarFolha, REACAO_AO_TOQUE } from "./estilos.js";
26
26
  import { Icon } from "./icon.js";
27
27
  import { Text } from "./text.js";
28
28
  import { useAureaTokens, useSobreAMarca } from "./theme.js";
@@ -114,10 +114,9 @@ const folha = criarFolha((t) => ({
114
114
  borderRadius: t.size.radiusControl, // 999 — pill, identidade INTOCÁVEL
115
115
  borderWidth: t.size.borderWidth,
116
116
  },
117
- // `.btn:active { transform:scale(.97); opacity:.9 }` no core — a mesma reação, medida lá.
118
- pressionado: { opacity: 0.9, transform: [{ scale: 0.97 }] },
119
- // `.btn:disabled { opacity:.45 }`
120
- inerte: { opacity: 0.45 },
117
+ // `.btn:active` e `.btn:disabled` do core — os números moram no `estilos.ts` desde que o
118
+ // `Card` com `onPress` passou a reagir igual (R-04, 24/09/2026).
119
+ ...REACAO_AO_TOQUE,
121
120
  }));
122
121
  /**
123
122
  * Botão. `Pressable` do RN, alvo ≥ `--target-min`, pele de token.
package/dist/busca.js CHANGED
@@ -108,7 +108,7 @@ const folha = criarFolha((t) => ({
108
108
  flexDirection: "row", alignItems: "center",
109
109
  },
110
110
  invalido: { borderColor: t.color.danger400 ?? t.color.destructive },
111
- desabilitado: { opacity: 0.5 },
111
+ desabilitado: { opacity: t.size.opacityDisabled },
112
112
  // As ações do gatilho — limpar e a seta. O `.combobox-actions` da web (`aurea.css:1673`) é
113
113
  // absoluto sobre o campo; aqui é um irmão na linha, porque não há campo por baixo.
114
114
  // `space05` mede 2 — o mesmo valor, agora com nome. O `.combobox-actions` da web
package/dist/display.d.ts CHANGED
@@ -5,6 +5,8 @@ export type AureaBadgeTone = "neutral" | "primary" | "info" | "success" | "warni
5
5
  export type AureaBadgeEmphasis = "soft" | "outline" | "solid";
6
6
  export type AureaBadgeSize = "xs" | "sm" | "md" | "lg";
7
7
  export type AureaBadgeAnchor = "top-end" | "top-start" | "bottom-end" | "bottom-start";
8
+ /** `auto` segue o recipiente (numa coluna, estica); `content` fica do tamanho do texto. */
9
+ export type AureaBadgeFit = "auto" | "content";
8
10
  /** `count > max ? `${max}+` : String(count)` — a mesma linha do `markup.tsx:140`. */
9
11
  export declare const formatarContagem: (count: number, max?: number) => string;
10
12
  export interface BadgeProps extends ViewProps {
@@ -35,6 +37,19 @@ export interface BadgeProps extends ViewProps {
35
37
  max?: number;
36
38
  /** Por padrão `count === 0` some — caixa zerada não merece um "0" no canto. */
37
39
  showZero?: boolean;
40
+ /**
41
+ * `content` põe o selo do tamanho do texto mesmo numa coluna — R-01, 24/09/2026.
42
+ *
43
+ * ⚠ **Não é o padrão, e a razão foi MEDIDA:** numa coluna o selo estica, e na web também —
44
+ * `.stack` não declara `align-items`, então o `.badge` vira item de flex e estica (300 px numa
45
+ * coluna de 300, contra 55 px solto num bloco). Os dois alvos já concordavam; o que faltava era
46
+ * como pedir o contrário.
47
+ *
48
+ * ⚠ **Serve para COLUNA.** Aqui ele vira `alignSelf: "flex-start"`, e numa fila o eixo cruzado
49
+ * é o vertical: o selo subiria para o topo em vez de ficar no meio. Numa fila o selo já tem o
50
+ * tamanho do texto, então não passe `fit` ali.
51
+ */
52
+ fit?: AureaBadgeFit;
38
53
  /** Ancora o selo no canto de `children`, em vez de desenhá-lo em linha. */
39
54
  anchor?: AureaBadgeAnchor;
40
55
  /** O que o selo mostra quando `anchor` está em uso (aí `children` é o que ele decora). */
@@ -58,7 +73,7 @@ export interface BadgeProps extends ViewProps {
58
73
  * usa selo com miniatura, e prop sem consumidor é superfície pública para manter de graça. Volta
59
74
  * quando houver tela que peça.
60
75
  */
61
- export declare function Badge({ tone, emphasis, size, dot, count, max, showZero, leading, trailing, anchor, badgeContent, invisible, children, style, ...rest }: BadgeProps): React.JSX.Element;
76
+ export declare function Badge({ tone, emphasis, size, dot, count, max, showZero, leading, trailing, fit, anchor, badgeContent, invisible, children, style, ...rest }: BadgeProps): React.JSX.Element;
62
77
  export type AureaStatusVariant = "neutral" | "online" | "offline" | "busy" | "away" | "success" | "warning" | "danger" | "info";
63
78
  export interface StatusProps extends ViewProps {
64
79
  variant?: AureaStatusVariant;
package/dist/display.js CHANGED
@@ -43,6 +43,8 @@ const folha = criarFolha((t) => ({
43
43
  selo_md: {},
44
44
  selo_lg: { minHeight: t.size.space7, paddingVertical: 0, paddingHorizontal: t.size.space3 },
45
45
  ponto: { width: t.size.space2, height: t.size.space2, borderRadius: t.size.radiusFull },
46
+ // `fit="content"` (R-01): o mesmo `alignSelf` que a âncora abaixo já usa para não esticar.
47
+ justo: { alignSelf: "flex-start" },
46
48
  // A âncora é `position:relative` na web; aqui o filho absoluto já se posiciona por ela.
47
49
  ancora: { position: "relative", alignSelf: "flex-start" },
48
50
  sobreposto: { position: "absolute", zIndex: 1, minHeight: t.size.space4, paddingHorizontal: t.size.space1 },
@@ -83,7 +85,7 @@ export const formatarContagem = (count, max = 99) => count > max ? `${max}+` : S
83
85
  * usa selo com miniatura, e prop sem consumidor é superfície pública para manter de graça. Volta
84
86
  * quando houver tela que peça.
85
87
  */
86
- export function Badge({ tone = "neutral", emphasis = "soft", size = "md", dot, count, max = 99, showZero, leading, trailing, anchor, badgeContent, invisible, children, style, ...rest }) {
88
+ export function Badge({ tone = "neutral", emphasis = "soft", size = "md", dot, count, max = 99, showZero, leading, trailing, fit = "auto", anchor, badgeContent, invisible, children, style, ...rest }) {
87
89
  const t = useAureaTokens();
88
90
  const s = folha(t);
89
91
  const acento = tone === "primary" ? t.color.primaryEmphasis
@@ -112,6 +114,7 @@ export function Badge({ tone = "neutral", emphasis = "soft", size = "md", dot, c
112
114
  const soPonto = !!dot && miolo == null;
113
115
  const selo = (_jsxs(View, { style: [
114
116
  s.selo, s[`selo_${size}`], pele,
117
+ fit === "content" && !anchor && s.justo,
115
118
  anchor && s.sobreposto, anchor && s.anel,
116
119
  anchor && posicaoDaAncora(anchor),
117
120
  soPonto && { width: t.size.space2, minWidth: t.size.space2, height: t.size.space2,
package/dist/estilos.d.ts CHANGED
@@ -26,8 +26,36 @@ export declare function criarFolha<T extends NamedStyles<T> | NamedStyles<Record
26
26
  * ⚠ **Ele morava dentro do `navigation.tsx` e mudou de casa em 17/09/2026**, quando o
27
27
  * `SegmentedControl` passou a precisar do mesmo cálculo para o fio do escolhido. Copiar a função
28
28
  * para o segundo arquivo é exatamente o defeito que o `CLAUDE.md` nomeia — *correção local é
29
- * proibida sem responder "quem mais tem esse problema?"*. **Não é exportado pela porta da frente
30
- * do pacote**: é ferramenta de dentro, e o `check 39` cobraria teste de tudo que sai por lá.
29
+ * proibida sem responder "quem mais tem esse problema?"*.
30
+ *
31
+ * ✅ **Sai pela porta da frente do pacote desde 24/09/2026 (R-07).** Até ali era ferramenta de
32
+ * dentro, e o app que precisava de um fundo translúcido sobre a cor do tema (o círculo atrás de
33
+ * um ícone) não tinha como escrevê-lo sem número à mão. O `check 39` cobra o teste dele.
31
34
  */
32
35
  export declare const comOpacidade: (cor: string, pct: number) => string;
36
+ /**
37
+ * A reação ao TOQUE de tudo que é alvo inteiro — hoje o `Button`, o `IconButton` e o `Card` com
38
+ * `onPress`. Os números são os do core: `.btn:active { transform:scale(.97); opacity:.9 }` e
39
+ * `.btn:disabled { opacity:.45 }`.
40
+ *
41
+ * ⚠ **Mora aqui desde 24/09/2026 (R-04)**, quando o `Card` passou a responder ao toque e a
42
+ * decisão foi *"a mesma reação do `Button`"*. Copiar os três números para o `layout.tsx` faria
43
+ * duas peças dizerem a mesma coisa em dois lugares — e a primeira que mudasse deixaria a outra
44
+ * para trás. Mesma razão do `comOpacidade` acima, e pelo mesmo motivo **não sai pela porta da
45
+ * frente do pacote**.
46
+ *
47
+ * ⚠ **Não é animação:** não há transição, o alvo SALTA para 0,97 enquanto o dedo está em cima.
48
+ * Por isso ele não consulta `useReduceMotion` — não há movimento para parar.
49
+ */
50
+ export declare const REACAO_AO_TOQUE: {
51
+ readonly pressionado: {
52
+ readonly opacity: 0.9;
53
+ readonly transform: readonly [{
54
+ readonly scale: 0.97;
55
+ }];
56
+ };
57
+ readonly inerte: {
58
+ readonly opacity: 0.45;
59
+ };
60
+ };
33
61
  export {};
package/dist/estilos.js CHANGED
@@ -40,9 +40,30 @@ export function criarFolha(descrever) {
40
40
  * ⚠ **Ele morava dentro do `navigation.tsx` e mudou de casa em 17/09/2026**, quando o
41
41
  * `SegmentedControl` passou a precisar do mesmo cálculo para o fio do escolhido. Copiar a função
42
42
  * para o segundo arquivo é exatamente o defeito que o `CLAUDE.md` nomeia — *correção local é
43
- * proibida sem responder "quem mais tem esse problema?"*. **Não é exportado pela porta da frente
44
- * do pacote**: é ferramenta de dentro, e o `check 39` cobraria teste de tudo que sai por lá.
43
+ * proibida sem responder "quem mais tem esse problema?"*.
44
+ *
45
+ * ✅ **Sai pela porta da frente do pacote desde 24/09/2026 (R-07).** Até ali era ferramenta de
46
+ * dentro, e o app que precisava de um fundo translúcido sobre a cor do tema (o círculo atrás de
47
+ * um ícone) não tinha como escrevê-lo sem número à mão. O `check 39` cobra o teste dele.
45
48
  */
46
49
  export const comOpacidade = (cor, pct) => /^#[0-9a-fA-F]{6}$/.test(cor)
47
50
  ? cor + Math.round(Math.max(0, Math.min(1, pct)) * 255).toString(16).padStart(2, "0")
48
51
  : cor;
52
+ /**
53
+ * A reação ao TOQUE de tudo que é alvo inteiro — hoje o `Button`, o `IconButton` e o `Card` com
54
+ * `onPress`. Os números são os do core: `.btn:active { transform:scale(.97); opacity:.9 }` e
55
+ * `.btn:disabled { opacity:.45 }`.
56
+ *
57
+ * ⚠ **Mora aqui desde 24/09/2026 (R-04)**, quando o `Card` passou a responder ao toque e a
58
+ * decisão foi *"a mesma reação do `Button`"*. Copiar os três números para o `layout.tsx` faria
59
+ * duas peças dizerem a mesma coisa em dois lugares — e a primeira que mudasse deixaria a outra
60
+ * para trás. Mesma razão do `comOpacidade` acima, e pelo mesmo motivo **não sai pela porta da
61
+ * frente do pacote**.
62
+ *
63
+ * ⚠ **Não é animação:** não há transição, o alvo SALTA para 0,97 enquanto o dedo está em cima.
64
+ * Por isso ele não consulta `useReduceMotion` — não há movimento para parar.
65
+ */
66
+ export const REACAO_AO_TOQUE = {
67
+ pressionado: { opacity: 0.9, transform: [{ scale: 0.97 }] },
68
+ inerte: { opacity: 0.45 },
69
+ };
package/dist/icon.d.ts CHANGED
@@ -13,6 +13,57 @@ export type AureaIconRegistry = Readonly<Record<IconName, AureaIconComponent>>;
13
13
  * render invalidaria o `useMemo` de todo componente que o recebe.
14
14
  */
15
15
  export declare const criarRegistroDeIcones: <T extends AureaIconRegistry>(r: T) => T;
16
+ /** Uma forma do glifo. Sem `fill`, ela pinta com a cor que o `Icon` passar (a do tema, por padrão). */
17
+ interface Pintura {
18
+ /** Cor fixa desta forma, ou `"none"` para não pintar. Sem isto, a forma segue o `color`. */
19
+ fill?: string;
20
+ }
21
+ export interface AureaGlifoCaminho extends Pintura {
22
+ d: string;
23
+ fillRule?: "nonzero" | "evenodd";
24
+ }
25
+ export interface AureaGlifoCirculo extends Pintura {
26
+ cx: number;
27
+ cy: number;
28
+ r: number;
29
+ }
30
+ export interface AureaGlifoRetangulo extends Pintura {
31
+ x: number;
32
+ y: number;
33
+ width: number;
34
+ height: number;
35
+ rx?: number;
36
+ }
37
+ export interface AureaGlifoDesenho {
38
+ /** Padrão `"0 0 32 32"`, a caixa dos glifos do Carbon. */
39
+ viewBox?: string;
40
+ rects?: readonly AureaGlifoRetangulo[];
41
+ circles?: readonly AureaGlifoCirculo[];
42
+ /** Um caminho SVG por item — a string solta vale por `{d}`. */
43
+ paths?: readonly (string | AureaGlifoCaminho)[];
44
+ }
45
+ /**
46
+ * Um glifo PRÓPRIO do app — um logotipo, por exemplo — desenhado pela Aurea a partir dos dados
47
+ * do desenho. R-05, 24/09/2026.
48
+ *
49
+ * ```tsx
50
+ * const Marca = criarGlifo({viewBox: "0 0 64 64", circles: [{cx: 32, cy: 32, r: 30}], paths: ["M…"]});
51
+ * const ICONES = criarRegistroDeIcones({...OS_DO_APP, marca: Marca});
52
+ * <Icon name="marca" size="xl" />
53
+ * ```
54
+ *
55
+ * **Por que existe:** o registro já aceitava qualquer componente, mas para ESCREVER esse
56
+ * componente o app precisava importar o `react-native-svg` — o motor que a Aurea usa por dentro,
57
+ * e que a regra "só Aurea" do app barra. Agora o app passa só números e caminhos.
58
+ *
59
+ * ⚠ **A ordem de pintura é fixa:** retângulos, depois círculos, depois caminhos — o de baixo
60
+ * primeiro. Um desenho que precise de outra ordem passa tudo como `paths`, que respeitam a ordem
61
+ * da lista.
62
+ *
63
+ * ⚠ **Não traz peso novo:** o `react-native-svg` já é dependência obrigatória, e o `Chart` o usa
64
+ * pela mesma porta.
65
+ */
66
+ export declare function criarGlifo(desenho: AureaGlifoDesenho): AureaIconComponent;
16
67
  /** Põe o registro em contexto. O `AureaProvider` já faz isto quando recebe `icons`. */
17
68
  export declare function IconRegistryProvider({ registry, children }: {
18
69
  registry: AureaIconRegistry;
@@ -45,3 +96,4 @@ export interface IconProps {
45
96
  * um aviso nomeando o ícone e o caminho do import que resolve.
46
97
  */
47
98
  export declare function Icon({ name, size, color, icons, label }: IconProps): React.JSX.Element | null;
99
+ export {};
package/dist/icon.js CHANGED
@@ -1,4 +1,4 @@
1
- import { jsx as _jsx } from "react/jsx-runtime";
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  // Aurea nativo — `Icon`, e a razão de ele existir apesar de `icons/<nome>` já desenhar.
3
3
  //
4
4
  // A **cláusula 4 da ADR-0038** manda: *"`<Icon name>` continua existindo, para a paridade de API
@@ -20,6 +20,7 @@ import { jsx as _jsx } from "react/jsx-runtime";
20
20
  // um componente — o que empurra a decisão para a tela, uma prop de cada vez.
21
21
  import * as React from "react";
22
22
  import { View } from "react-native";
23
+ import Svg, { Circle, Path, Rect } from "react-native-svg";
23
24
  import { useAureaTokens } from "./theme.js";
24
25
  /**
25
26
  * Declara o registro. É uma função de identidade tipada, e ela existe por um motivo prático:
@@ -27,6 +28,35 @@ import { useAureaTokens } from "./theme.js";
27
28
  * render invalidaria o `useMemo` de todo componente que o recebe.
28
29
  */
29
30
  export const criarRegistroDeIcones = (r) => r;
31
+ /**
32
+ * Um glifo PRÓPRIO do app — um logotipo, por exemplo — desenhado pela Aurea a partir dos dados
33
+ * do desenho. R-05, 24/09/2026.
34
+ *
35
+ * ```tsx
36
+ * const Marca = criarGlifo({viewBox: "0 0 64 64", circles: [{cx: 32, cy: 32, r: 30}], paths: ["M…"]});
37
+ * const ICONES = criarRegistroDeIcones({...OS_DO_APP, marca: Marca});
38
+ * <Icon name="marca" size="xl" />
39
+ * ```
40
+ *
41
+ * **Por que existe:** o registro já aceitava qualquer componente, mas para ESCREVER esse
42
+ * componente o app precisava importar o `react-native-svg` — o motor que a Aurea usa por dentro,
43
+ * e que a regra "só Aurea" do app barra. Agora o app passa só números e caminhos.
44
+ *
45
+ * ⚠ **A ordem de pintura é fixa:** retângulos, depois círculos, depois caminhos — o de baixo
46
+ * primeiro. Um desenho que precise de outra ordem passa tudo como `paths`, que respeitam a ordem
47
+ * da lista.
48
+ *
49
+ * ⚠ **Não traz peso novo:** o `react-native-svg` já é dependência obrigatória, e o `Chart` o usa
50
+ * pela mesma porta.
51
+ */
52
+ export function criarGlifo(desenho) {
53
+ const { viewBox = "0 0 32 32", rects = [], circles = [], paths = [] } = desenho;
54
+ const Glifo = ({ size = 32, color = "#000000" }) => (_jsxs(Svg, { width: size, height: size, viewBox: viewBox, children: [rects.map((r, i) => _jsx(Rect, { ...r, fill: r.fill ?? color }, `r${i}`)), circles.map((c, i) => _jsx(Circle, { ...c, fill: c.fill ?? color }, `c${i}`)), paths.map((p, i) => {
55
+ const c = typeof p === "string" ? { d: p } : p;
56
+ return _jsx(Path, { ...c, fill: c.fill ?? color }, `p${i}`);
57
+ })] }));
58
+ return Glifo;
59
+ }
30
60
  const Contexto = React.createContext(null);
31
61
  /** Põe o registro em contexto. O `AureaProvider` já faz isto quando recebe `icons`. */
32
62
  export function IconRegistryProvider({ registry, children }) {
package/dist/index.d.ts CHANGED
@@ -3,27 +3,27 @@ export type { AureaProviderProps, AureaThemeControl } from "./theme.js";
3
3
  export { BottomNavProvider, useBottomNavSpace } from "./barranav.js";
4
4
  export { resolverTokens } from "./tokens.js";
5
5
  export type { AureaDensity, AureaFontFamilies, AureaFontInput, AureaFontScale, AureaShadow, AureaThemeName, AureaTokens, } from "./tokens.js";
6
- export { criarFolha } from "./estilos.js";
6
+ export { criarFolha, comOpacidade } from "./estilos.js";
7
7
  export { Text } from "./text.js";
8
8
  export type { TextProps, AureaTextFont, AureaTextLeading, AureaTextSize, AureaTextTone, AureaTextWeight, } from "./text.js";
9
- export { Icon, IconRegistryProvider, criarRegistroDeIcones } from "./icon.js";
10
- export type { IconProps, IconName, AureaIconComponent, AureaIconRegistry, AureaIconSize, } from "./icon.js";
9
+ export { Icon, IconRegistryProvider, criarRegistroDeIcones, criarGlifo } from "./icon.js";
10
+ export type { IconProps, IconName, AureaIconComponent, AureaIconRegistry, 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";
14
- export type { StackProps, GridProps, CardProps, SeparatorProps, AureaCardVariant } from "./layout.js";
14
+ export type { StackProps, ClusterProps, AureaClusterAlign, AureaClusterJustify, GridProps, CardProps, SeparatorProps, AureaCardVariant, } from "./layout.js";
15
15
  export { Button, IconButton } from "./actions.js";
16
16
  export type { ButtonProps, IconButtonProps, AureaButtonAppearance, AureaButtonSize, AureaButtonTone, } from "./actions.js";
17
17
  export { Spinner, Skeleton, Progress, Alert, EmptyState, DataState, ICONE_DA_VARIANTE } from "./feedback.js";
18
18
  export type { SpinnerProps, SkeletonProps, ProgressProps, AlertProps, EmptyStateProps, DataStateProps, AureaSpinnerSize, AureaAlertVariant, AureaDataStateValue, } from "./feedback.js";
19
19
  export { Badge, Status, Avatar, KPI, formatarContagem } from "./display.js";
20
- export type { BadgeProps, StatusProps, AvatarProps, KPIProps, AureaBadgeAnchor, AureaBadgeEmphasis, AureaBadgeSize, AureaBadgeTone, AureaStatusVariant, AureaAvatarSize, } from "./display.js";
20
+ export type { BadgeProps, StatusProps, AvatarProps, KPIProps, AureaBadgeAnchor, AureaBadgeEmphasis, AureaBadgeFit, AureaBadgeSize, AureaBadgeTone, AureaStatusVariant, AureaAvatarSize, } from "./display.js";
21
21
  export { useAureaStrings } from "./theme.js";
22
22
  export { defaultStrings, ptBR, gravidadeDoEstado, AUREA_UNIVERSAL_STATES } from "./strings.js";
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, AureaTopbarVariant, TabsProps, AureaTabItem, } from "./navigation.js";
26
+ export type { BottomNavProps, TopbarProps, NavListProps, StepperProps, AureaNavItem, AureaNavListItem, AureaStepItem, AureaStepState, AureaBottomNavVariant, AureaBottomNavIndicator, AureaBottomNavWidth, AureaTopbarVariant, AureaTopbarInset, TabsProps, AureaTabItem, } from "./navigation.js";
27
27
  export { Field, Label, Input, Textarea, Select, Switch, Checkbox, Radio, SegmentedControl, Form, KeyboardAvoiding, useCampo, } from "./inputs.js";
28
28
  export type { FieldProps, LabelProps, InputProps, TextareaProps, SelectProps, SwitchProps, CheckboxProps, RadioProps, SegmentedControlProps, FormProps, KeyboardAvoidingProps, AureaFieldSize, } from "./inputs.js";
29
29
  export { InputGroup, InputGroupAddon, PasswordField, useGrupoDeCampo } from "./inputs.js";
package/dist/index.js CHANGED
@@ -30,9 +30,9 @@ export { BottomNavProvider, useBottomNavSpace } from "./barranav.js";
30
30
  export { resolverTokens } from "./tokens.js";
31
31
  // A fábrica de folhas memoizada por (tema, densidade). Pública porque o consumidor tem o mesmo
32
32
  // problema que os componentes daqui — e a medição em aparelho mostrou que ele é real.
33
- export { criarFolha } from "./estilos.js";
33
+ export { criarFolha, comOpacidade } from "./estilos.js";
34
34
  export { Text } from "./text.js";
35
- export { Icon, IconRegistryProvider, criarRegistroDeIcones } from "./icon.js";
35
+ export { Icon, IconRegistryProvider, criarRegistroDeIcones, criarGlifo } from "./icon.js";
36
36
  // A ÚNICA peça do Lote 1 com dependência de terceiro — `react-native-safe-area-context`,
37
37
  // autorizada pelo Victor em 03/09/2026 depois de o lote parar por ela (`BUILDING.md` §3).
38
38
  export { Screen } from "./screen.js";
package/dist/inputs.js CHANGED
@@ -62,7 +62,7 @@ const folha = criarFolha((t) => ({
62
62
  // pílula viraria uma cápsula, e o CSS já decidiu isso (`aurea.css:694`).
63
63
  areaDeTexto: { minHeight: 104, paddingVertical: t.size.space3, borderRadius: t.size.radiusLg },
64
64
  invalido: { borderColor: t.color.danger400 ?? t.color.destructive },
65
- desabilitado: { opacity: 0.5 },
65
+ desabilitado: { opacity: t.size.opacityDisabled },
66
66
  // O GRUPO é a mesma caixa do campo virada em LINHA, e os números são os mesmos: mesma borda,
67
67
  // mesmo raio de controle, mesmo fundo. A altura e o respiro horizontal entram por tamanho, como
68
68
  // no `Input`, porque é o grupo que passa a desenhar a caixa quando existe um.
package/dist/layout.d.ts CHANGED
@@ -5,8 +5,26 @@ export interface StackProps extends ViewProps {
5
5
  }
6
6
  /** Coluna com `--space-4` entre os filhos. **Sem prop de espaçamento, de propósito.** */
7
7
  export declare function Stack({ style, ...rest }: StackProps): React.JSX.Element;
8
- /** Linha que QUEBRA, alinhada no eixo cruzado, com `--space-3`. É a fila de chips e botões. */
9
- export declare function Cluster({ style, ...rest }: StackProps): React.JSX.Element;
8
+ export type AureaClusterAlign = "start" | "center" | "end" | "baseline";
9
+ export type AureaClusterJustify = "start" | "center" | "end" | "between";
10
+ export interface ClusterProps extends StackProps {
11
+ /** Eixo cruzado (o vertical). Padrão `center`, o `align-items:center` do `.cluster`. */
12
+ align?: AureaClusterAlign;
13
+ /** Eixo principal (o horizontal). Padrão `start`. `between` espalha o que sobrar entre os itens. */
14
+ justify?: AureaClusterJustify;
15
+ /** Padrão `true`: a fila quebra linha, como o `.cluster`. `false` mantém tudo numa linha só. */
16
+ wrap?: boolean;
17
+ }
18
+ /**
19
+ * Linha que QUEBRA, alinhada no eixo cruzado, com `--space-3`. É a fila de chips e botões.
20
+ *
21
+ * ✅ **`align`, `justify` e `wrap` — R-09, 24/09/2026.** O app centralizava três filas passando
22
+ * `style`, e a regra "só Aurea" dele barra isso. Os nomes são os que o documento dos consumidores
23
+ * propõe para a web na B-01, de propósito: quando a web ganhar os mesmos, os dois alvos dizem a
24
+ * mesma coisa com a mesma palavra. **O espaço entre os itens continua sem prop**, como na `Stack`.
25
+ * Sem nenhuma das três, nada muda.
26
+ */
27
+ export declare function Cluster({ align, justify, wrap, style, ...rest }: ClusterProps): React.JSX.Element;
10
28
  export interface GridProps extends ViewProps {
11
29
  /**
12
30
  * Largura mínima de cada coluna, em dp. O equivalente do `--grid-min` da web, cujo padrão é
@@ -81,15 +99,49 @@ interface CardDaMarca extends CardBase {
81
99
  interface CardComum extends CardBase {
82
100
  variant?: Exclude<AureaCardVariant, "brand">;
83
101
  action?: never;
102
+ onPress?: undefined;
84
103
  }
85
- export type CardProps = CardDaMarca | CardComum;
104
+ /**
105
+ * 🔴 **O cartão que É um alvo — R-04, 24/09/2026.** Até aqui a documentação mandava a tela
106
+ * embrulhar o cartão num `Pressable`, e o consumidor que segue a regra "só Aurea" ficava sem
107
+ * caminho. Agora o toque é do `Card`, e **as duas obrigações que a nota antiga deixava com a tela
108
+ * passam a ser do TIPO:** com `onPress` o nome para o leitor de tela é obrigatório, e o papel de
109
+ * botão vem de dentro. Mesma escolha do `action` do `brand` e do `label` do `IconButton`.
110
+ *
111
+ * ⚠ **E o `brand` NÃO aceita `onPress`, de propósito.** Ele exige `action`, e `action` é um botão
112
+ * DENTRO do cartão — botão dentro de botão, o defeito que o `check 43` barra. No iPhone o de
113
+ * dentro some para o VoiceOver.
114
+ */
115
+ interface CardTocavel extends CardBase {
116
+ variant?: Exclude<AureaCardVariant, "brand">;
117
+ action?: never;
118
+ /** O que acontece ao tocar no cartão. Com isto o cartão inteiro vira UM botão. */
119
+ onPress: () => void;
120
+ /** O nome do cartão para o leitor de tela. Obrigatório com `onPress`: é o que ele anuncia. */
121
+ accessibilityLabel: string;
122
+ disabled?: boolean;
123
+ }
124
+ export type CardProps = CardDaMarca | CardComum | CardTocavel;
86
125
  /**
87
126
  * Superfície flutuante, raio 22 (`--radius-card`) — identidade declarada INTOCÁVEL.
88
127
  *
89
- * ⚠ **`interactive` aqui NÃO responde ao toque sozinho.** Na web ele é a variante que responde ao
90
- * ponteiro; no nativo não há hover, e um cartão que vira alvo precisa de `Pressable` em volta,
91
- * com rótulo e papel de acessibilidade próprios. A variante existe para a **pele** de um cartão
92
- * que É um alvo — quem o torna tocável é a tela.
128
+ * ✅ **O cartão responde ao toque com `onPress`** (R-04, 24/09/2026):
129
+ *
130
+ * ```tsx
131
+ * <Card onPress={abrir} accessibilityLabel="Relatório de março">…</Card>
132
+ * ```
133
+ *
134
+ * Com `onPress` a variante padrão passa a ser `interactive` — a pele de um cartão que é alvo — e
135
+ * o toque dá a MESMA reação do `Button` (`REACAO_AO_TOQUE`). Sem `onPress`, nada muda: `interactive`
136
+ * continua sendo só a pele.
137
+ *
138
+ * 🔴 **Um cartão com `onPress` NÃO PODE ter coisa tocável dentro** — nem `Button`, nem `Switch`,
139
+ * nem link. O cartão vira UM elemento para o leitor de tela, e no iPhone quem é elemento **não
140
+ * expõe os filhos**: o botão de dentro existiria na tela e não existiria para o VoiceOver. É o
141
+ * defeito que o `check 43` guarda no nosso código, e aqui ele não alcança, porque o conteúdo é
142
+ * do app. ⚠ **O exemplo do HeroUI faz exatamente isso** (`PressableFeedback` em volta de um
143
+ * `Card` com `Button` dentro, `heroui-native@1.0.10`) — é o ponto em que a referência não serve.
144
+ * Cartão com duas ações não tem `onPress`: cada ação é um botão dentro de um cartão comum.
93
145
  */
94
146
  export declare function Card({ variant, style, ...rest }: CardProps): React.JSX.Element;
95
147
  export {};
package/dist/layout.js CHANGED
@@ -12,12 +12,12 @@ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-run
12
12
  // do `Stack` na web escreve o porquê — *"um primitivo de layout que aceita qualquer espaçamento é
13
13
  // como um sistema deixa de ter espaçamento"*. Reabrir isso no nativo criaria dois sistemas.
14
14
  import * as React from "react";
15
- import { View } from "react-native";
15
+ import { Pressable, View } from "react-native";
16
16
  // `ref` chega por PROPS, sem `forwardRef` — e' o padrao do React 19, e e' o que o
17
17
  // `@aurea-uds/react` ja' faz (o `Stack` da web e' `HTMLAttributes & RefAttributes`, sem
18
18
  // envelope). O `ViewProps`/`TextProps` do RN 0.87 ja' declaram `ref`, entao ele viaja no
19
19
  // `...rest` sem nada a mais.
20
- import { criarFolha } from "./estilos.js";
20
+ import { criarFolha, REACAO_AO_TOQUE } from "./estilos.js";
21
21
  import { Text } from "./text.js";
22
22
  import { useAureaTokens, SobreAMarca } from "./theme.js";
23
23
  const folha = criarFolha((t) => ({
@@ -34,6 +34,7 @@ const folha = criarFolha((t) => ({
34
34
  // do `react-native` 0.86/0.87 que o pacote declara como peer (>=0.76).
35
35
  stack: { flexDirection: "column", gap: t.size.space4 },
36
36
  cluster: { flexDirection: "row", alignItems: "center", flexWrap: "wrap", gap: t.size.space3 },
37
+ clusterSemQuebra: { flexWrap: "nowrap" },
37
38
  grid: { flexDirection: "row", flexWrap: "wrap", gap: t.size.space4 },
38
39
  // O raio 22 e a sombra vêm de token, e os dois são identidade declarada INTOCÁVEL no CLAUDE.md.
39
40
  // `boxShadow` é do RN 0.76+, e a Etapa 2 mediu que ele é 1:1 com o CSS — inclusive `spread`,
@@ -63,10 +64,26 @@ export function Stack({ style, ...rest }) {
63
64
  const s = folha(useAureaTokens());
64
65
  return _jsx(View, { style: [s.stack, style], ...rest });
65
66
  }
66
- /** Linha que QUEBRA, alinhada no eixo cruzado, com `--space-3`. É a fila de chips e botões. */
67
- export function Cluster({ style, ...rest }) {
67
+ const ALINHAR = { start: "flex-start", center: "center", end: "flex-end", baseline: "baseline" };
68
+ const JUSTIFICAR = { start: "flex-start", center: "center", end: "flex-end", between: "space-between" };
69
+ /**
70
+ * Linha que QUEBRA, alinhada no eixo cruzado, com `--space-3`. É a fila de chips e botões.
71
+ *
72
+ * ✅ **`align`, `justify` e `wrap` — R-09, 24/09/2026.** O app centralizava três filas passando
73
+ * `style`, e a regra "só Aurea" dele barra isso. Os nomes são os que o documento dos consumidores
74
+ * propõe para a web na B-01, de propósito: quando a web ganhar os mesmos, os dois alvos dizem a
75
+ * mesma coisa com a mesma palavra. **O espaço entre os itens continua sem prop**, como na `Stack`.
76
+ * Sem nenhuma das três, nada muda.
77
+ */
78
+ export function Cluster({ align, justify, wrap, style, ...rest }) {
68
79
  const s = folha(useAureaTokens());
69
- return _jsx(View, { style: [s.cluster, style], ...rest });
80
+ return (_jsx(View, { style: [
81
+ s.cluster,
82
+ align != null && { alignItems: ALINHAR[align] },
83
+ justify != null && { justifyContent: JUSTIFICAR[justify] },
84
+ wrap === false && s.clusterSemQuebra,
85
+ style,
86
+ ], ...rest }));
70
87
  }
71
88
  /**
72
89
  * Colunas que se acomodam sem breakpoint.
@@ -133,17 +150,38 @@ export function Separator({ orientation = "horizontal", label, style, ...rest })
133
150
  /**
134
151
  * Superfície flutuante, raio 22 (`--radius-card`) — identidade declarada INTOCÁVEL.
135
152
  *
136
- * ⚠ **`interactive` aqui NÃO responde ao toque sozinho.** Na web ele é a variante que responde ao
137
- * ponteiro; no nativo não há hover, e um cartão que vira alvo precisa de `Pressable` em volta,
138
- * com rótulo e papel de acessibilidade próprios. A variante existe para a **pele** de um cartão
139
- * que É um alvo — quem o torna tocável é a tela.
153
+ * ✅ **O cartão responde ao toque com `onPress`** (R-04, 24/09/2026):
154
+ *
155
+ * ```tsx
156
+ * <Card onPress={abrir} accessibilityLabel="Relatório de março">…</Card>
157
+ * ```
158
+ *
159
+ * Com `onPress` a variante padrão passa a ser `interactive` — a pele de um cartão que é alvo — e
160
+ * o toque dá a MESMA reação do `Button` (`REACAO_AO_TOQUE`). Sem `onPress`, nada muda: `interactive`
161
+ * continua sendo só a pele.
162
+ *
163
+ * 🔴 **Um cartão com `onPress` NÃO PODE ter coisa tocável dentro** — nem `Button`, nem `Switch`,
164
+ * nem link. O cartão vira UM elemento para o leitor de tela, e no iPhone quem é elemento **não
165
+ * expõe os filhos**: o botão de dentro existiria na tela e não existiria para o VoiceOver. É o
166
+ * defeito que o `check 43` guarda no nosso código, e aqui ele não alcança, porque o conteúdo é
167
+ * do app. ⚠ **O exemplo do HeroUI faz exatamente isso** (`PressableFeedback` em volta de um
168
+ * `Card` com `Button` dentro, `heroui-native@1.0.10`) — é o ponto em que a referência não serve.
169
+ * Cartão com duas ações não tem `onPress`: cada ação é um botão dentro de um cartão comum.
140
170
  */
141
- export function Card({ variant = "base", style, ...rest }) {
171
+ export function Card({ variant, style, ...rest }) {
142
172
  const t = useAureaTokens();
143
173
  const s = folha(t);
144
- const { action, children, ...resto } = rest;
174
+ const { action, children, onPress, disabled, ...resto } = rest;
175
+ if (onPress != null) {
176
+ const pele = variant ?? "interactive";
177
+ return (_jsx(Pressable, { onPress: onPress, disabled: disabled, accessibilityRole: "button", accessibilityState: { disabled: !!disabled }, style: ({ pressed: tocando }) => [
178
+ s.cardBase, s[`card_${pele}`],
179
+ tocando && REACAO_AO_TOQUE.pressionado, disabled && REACAO_AO_TOQUE.inerte,
180
+ style,
181
+ ], ...resto, children: children }));
182
+ }
145
183
  const corpo = (_jsxs(_Fragment, { children: [children, action != null && _jsx(View, { style: s.acaoDaMarca, children: action })] }));
146
- const pele = _jsx(View, { style: [s.cardBase, s[`card_${variant}`], style], ...resto, children: corpo });
184
+ const pele = _jsx(View, { style: [s.cardBase, s[`card_${variant ?? "base"}`], style], ...resto, children: corpo });
147
185
  // 🔴 SOBRE O AMARELO A COR DO TEXTO NÃO É ESCOLHA, É OBRIGAÇÃO — e os números decidiram isto.
148
186
  // Medido nos dois temas, texto contra o `primary`:
149
187
  // texto comum ........ 10,34 no claro · **1,83 no escuro** 🔴 ilegível
@@ -11,6 +11,8 @@ export interface AureaNavItem {
11
11
  disabled?: boolean;
12
12
  }
13
13
  export type AureaBottomNavVariant = "floating" | "edge";
14
+ /** Quanto a pílula ocupa: a tela menos a margem (`full`, o de sempre) ou só as abas (`content`). */
15
+ export type AureaBottomNavWidth = "full" | "content";
14
16
  export type AureaBottomNavIndicator = "none" | "subtle" | "pill" | "circle" | "circle-raised" | "circle-outline" | "circle-bold";
15
17
  export interface BottomNavProps extends ViewProps {
16
18
  items: AureaNavItem[];
@@ -18,6 +20,11 @@ export interface BottomNavProps extends ViewProps {
18
20
  current?: string;
19
21
  variant?: AureaBottomNavVariant;
20
22
  indicator?: AureaBottomNavIndicator;
23
+ /**
24
+ * `content` põe a pílula do tamanho das abas, no centro, em vez de ir de uma borda à outra.
25
+ * Vale só no `floating`: a `edge` encosta na borda e continua da largura da tela.
26
+ */
27
+ width?: AureaBottomNavWidth;
21
28
  /** Nome da barra para o leitor de tela. Sem ele, a frase do provider. */
22
29
  label?: string;
23
30
  }
@@ -94,10 +101,19 @@ export interface BottomNavProps extends ViewProps {
94
101
  * usa `Screen` (uma fileira de botões presa no pé, por exemplo), o número está em
95
102
  * `useBottomNavSpace()`. Fora de um provedor os dois valem zero, e nada muda.
96
103
  */
97
- export declare function BottomNav({ items, current, variant, indicator, label, style, ...rest }: BottomNavProps): React.JSX.Element;
104
+ export declare function BottomNav({ items, current, variant, indicator, width, label, style, ...rest }: BottomNavProps): React.JSX.Element;
98
105
  export type AureaTopbarVariant = "floating" | "flush" | "pill";
106
+ /** O recuo dos lados da `flush`: `bar` (20, o de sempre), `page` (16, o da `Screen`) ou `none`. */
107
+ export type AureaTopbarInset = "bar" | "page" | "none";
99
108
  export interface TopbarProps extends ViewProps {
100
109
  variant?: AureaTopbarVariant;
110
+ /**
111
+ * Só na `flush` — R-02, 24/09/2026. Por padrão a barra recua 20 dos lados e a `Screen` recua 16,
112
+ * então o título fica 4 para dentro do conteúdo. `page` alinha os dois; `none` serve para a
113
+ * barra que já vai dentro de um conteúdo com recuo. As outras duas variantes são caixas com
114
+ * margem própria e ignoram esta prop.
115
+ */
116
+ inset?: AureaTopbarInset;
101
117
  brand?: React.ReactNode;
102
118
  children?: React.ReactNode;
103
119
  }
@@ -118,7 +134,7 @@ export interface TopbarProps extends ViewProps {
118
134
  * e **não há landmarks no RN**. Um papel inventado diria uma coisa errada; a barra fica sem papel,
119
135
  * e quem nomeia a tela é o conteúdo dela.
120
136
  */
121
- export declare function Topbar({ variant, brand, children, style, ...rest }: TopbarProps): React.JSX.Element;
137
+ export declare function Topbar({ variant, inset, brand, children, style, ...rest }: TopbarProps): React.JSX.Element;
122
138
  export interface AureaNavListItem {
123
139
  id: string;
124
140
  label: React.ReactNode;
@@ -26,6 +26,11 @@ const folha = criarFolha((t) => ({
26
26
  },
27
27
  topo_flush: { paddingVertical: t.size.space2, paddingHorizontal: t.size.space5,
28
28
  backgroundColor: t.color.background },
29
+ // `inset` (R-02, 24/09/2026): o recuo dos lados da `flush`. `page` é o mesmo `space4` do
30
+ // `respiro` da `Screen` (`screen.tsx`), para o título alinhar com o conteúdo embaixo; `none` é
31
+ // para a barra que já vai dentro de um conteúdo com recuo próprio.
32
+ topo_recuo_page: { paddingHorizontal: t.size.space4 },
33
+ topo_recuo_none: { paddingHorizontal: 0 },
29
34
  topo_pill: {
30
35
  alignSelf: "center", marginTop: t.size.space4, justifyContent: "flex-start",
31
36
  gap: t.size.space2, padding: t.size.space2,
@@ -45,7 +50,7 @@ const folha = criarFolha((t) => ({
45
50
  //
46
51
  // `zSticky` é token (20), o mesmo `--z-sticky` que a web usa na mesma peça.
47
52
  barra: {
48
- position: "absolute", left: 0, right: 0, bottom: 0, zIndex: t.size.zSticky,
53
+ position: "absolute", bottom: 0, zIndex: t.size.zSticky,
49
54
  flexDirection: "row", gap: t.size.space1, margin: t.size.space4, padding: t.size.space1,
50
55
  borderWidth: t.size.borderWidth, borderColor: t.color.border,
51
56
  borderRadius: t.size.radiusControl, backgroundColor: t.color.card,
@@ -58,11 +63,33 @@ const folha = criarFolha((t) => ({
58
63
  margin: 0, paddingHorizontal: 0, borderRadius: 0, backgroundColor: t.color.background,
59
64
  borderWidth: 0, borderTopWidth: t.size.borderWidth, boxShadow: undefined,
60
65
  },
66
+ // A pílula CHEIA, de uma borda à outra menos a margem — o de sempre. Mora fora da `barra` para
67
+ // o `content` nunca precisar DESFAZER estes dois (ver abaixo).
68
+ barra_cheia: { left: 0, right: 0 },
69
+ // 🔴 `width="content"` — R-08, 24/09/2026: *"ficou super largo"*. A pílula deixa de ir de uma
70
+ // borda à outra e fica do tamanho das abas, no centro. Nenhuma conta nossa: medido no Yoga do
71
+ // `react-native@0.87.1`, uma peça `absolute` SEM `left`/`right` se mede pelo conteúdo, limitada
72
+ // à largura do pai (`AbsoluteLayout.cpp`, o ramo `FitContent` de `layoutAbsoluteChild`), e é
73
+ // posicionada pelo `alignSelf` (`alignAbsoluteChild`, o caso `Align::Center`).
74
+ // ⚠ **Por isso `left/right` NÃO podem existir aqui**, e a forma importa: escrever
75
+ // `left: undefined` por cima de um `left: 0` apaga na montagem, mas NÃO numa atualização — o
76
+ // `restoreDeletedValuesInNestedArray` do `ReactNativeAttributePayload.js` pula `undefined` e
77
+ // devolve o valor do item anterior. Trocar `width` com a tela aberta deixaria a pílula cheia.
78
+ barra_content: { alignSelf: "center" },
61
79
  aba: {
62
80
  flex: 1, minWidth: 0, alignItems: "center", justifyContent: "center",
63
81
  gap: t.size.space1, minHeight: t.size.controlHLg, padding: t.size.space1,
64
82
  borderRadius: t.size.radiusControl,
65
83
  },
84
+ // Com a pílula do tamanho do conteúdo, `flex: 1` MATA as abas: no RN ele é base ZERO, e a soma
85
+ // de zeros é uma pílula de largura zero. Aqui cada aba mede o próprio conteúdo (base `auto`) e
86
+ // só encolhe se não couber. O mínimo é o alvo de toque da própria aba, `controlHLg`, para uma
87
+ // aba de rótulo curto não virar um alvo mais estreito que alto; e o respiro dos lados é o
88
+ // `space3` que o `.bottom-nav-content` da web usa na mesma peça.
89
+ aba_content: {
90
+ flexGrow: 0, flexShrink: 1, flexBasis: "auto",
91
+ minWidth: t.size.controlHLg, paddingHorizontal: t.size.space3,
92
+ },
66
93
  // A CAIXA QUE ANCORA O CONTADOR, e ela existe por um defeito visto em tela: pendurado no ITEM,
67
94
  // o número cai no meio do RÓTULO. O `navigation-client.tsx:336` conta a história inteira — o
68
95
  // Victor viu o contador cobrir o nome em 17/08/2026, e a pesquisa (Material 3 e os guias de
@@ -188,7 +215,7 @@ const folha = criarFolha((t) => ({
188
215
  * usa `Screen` (uma fileira de botões presa no pé, por exemplo), o número está em
189
216
  * `useBottomNavSpace()`. Fora de um provedor os dois valem zero, e nada muda.
190
217
  */
191
- export function BottomNav({ items, current, variant = "floating", indicator = "none", label, style, ...rest }) {
218
+ export function BottomNav({ items, current, variant = "floating", indicator = "none", width = "full", label, style, ...rest }) {
192
219
  const t = useAureaTokens();
193
220
  const s = folha(t);
194
221
  const strings = useAureaStrings();
@@ -196,12 +223,13 @@ export function BottomNav({ items, current, variant = "floating", indicator = "n
196
223
  const redondo = indicator.startsWith("circle");
197
224
  const anotar = useAnotarBottomNav();
198
225
  const flutua = variant !== "edge";
226
+ const justa = flutua && width === "content";
199
227
  return (_jsx(View, { accessibilityLabel: label ?? strings.bottomNavLabel,
200
228
  // 🔴 A BARRA CONTA A PRÓPRIA ALTURA — é o que permite o `Screen` reservar o fim da rolagem
201
229
  // sem o app calcular nada. A conta é a pílula (o que o layout mede) mais as DUAS margens
202
230
  // mais a folga do sistema; `edge` conta zero, porque ela fica no fluxo e já encurta a tela.
203
231
  onLayout: anotar == null ? undefined : (e) => anotar(flutua ? e.nativeEvent.layout.height + t.size.space4 * 2 + inset.bottom : 0), style: [
204
- s.barra, variant === "edge" && s.barra_edge,
232
+ s.barra, justa ? s.barra_content : s.barra_cheia, variant === "edge" && s.barra_edge,
205
233
  // 🔴 NO `floating` A FOLGA DO SISTEMA VAI POR FORA — conserto de 19/09/2026, visto em
206
234
  // aparelho Samsung com os três botões (|||, ○, <).
207
235
  //
@@ -229,7 +257,7 @@ export function BottomNav({ items, current, variant = "floating", indicator = "n
229
257
  return (_jsxs(Pressable, { onPress: it.onPress, disabled: it.disabled,
230
258
  // `link` e não `tab`: uma aba troca um painel DESTA tela; isto troca de tela.
231
259
  accessibilityRole: "link", accessibilityState: { selected: ativo, disabled: !!it.disabled }, style: [
232
- s.aba,
260
+ s.aba, justa && s.aba_content,
233
261
  indicator === "subtle" && { borderRadius: t.size.radiusCard - t.size.space1 },
234
262
  ativo && (indicator === "subtle" || indicator === "pill")
235
263
  && { backgroundColor: comOpacidade(t.color.primary, 0.12) },
@@ -270,9 +298,13 @@ export function BottomNav({ items, current, variant = "floating", indicator = "n
270
298
  * e **não há landmarks no RN**. Um papel inventado diria uma coisa errada; a barra fica sem papel,
271
299
  * e quem nomeia a tela é o conteúdo dela.
272
300
  */
273
- export function Topbar({ variant = "floating", brand, children, style, ...rest }) {
301
+ export function Topbar({ variant = "floating", inset = "bar", brand, children, style, ...rest }) {
274
302
  const s = folha(useAureaTokens());
275
- return (_jsxs(View, { style: [s.topo, s[`topo_${variant}`], style], ...rest, children: [brand != null && _jsx(View, { children: brand }), children] }));
303
+ return (_jsxs(View, { style: [
304
+ s.topo, s[`topo_${variant}`],
305
+ variant === "flush" && inset !== "bar" && s[`topo_recuo_${inset}`],
306
+ style,
307
+ ], ...rest, children: [brand != null && _jsx(View, { children: brand }), children] }));
276
308
  }
277
309
  /**
278
310
  * A lista de destinos DENTRO da página — a tela de ajustes, não a moldura do app.
@@ -313,7 +345,7 @@ export function NavList({ items, chevron = "chevron--right", style, ...rest }) {
313
345
  ? _jsx(Text, { size: "xs", tone: "muted", numberOfLines: 1, children: it.description })
314
346
  : it.description)] }), valorEhTexto && _jsx(Text, { size: "sm", tone: "muted", children: it.value }), temAcao && chevron
315
347
  && _jsx(Icon, { name: chevron, size: "sm", color: t.color.mutedForeground })] }));
316
- return (_jsxs(View, { style: [s.linha, it.disabled && { opacity: 0.5 }], children: [temAcao ? (_jsx(Pressable, { onPress: it.onPress, disabled: it.disabled, accessibilityRole: "link", accessibilityState: { disabled: !!it.disabled }, style: s.linhaMiolo, children: miolo })) : (_jsx(View, { accessible: true, accessibilityState: { disabled: !!it.disabled }, style: s.linhaMiolo, children: miolo })), !valorEhTexto && it.value != null ? it.value : null] }, it.id));
348
+ return (_jsxs(View, { style: [s.linha, it.disabled && { opacity: t.size.opacityDisabled }], children: [temAcao ? (_jsx(Pressable, { onPress: it.onPress, disabled: it.disabled, accessibilityRole: "link", accessibilityState: { disabled: !!it.disabled }, style: s.linhaMiolo, children: miolo })) : (_jsx(View, { accessible: true, accessibilityState: { disabled: !!it.disabled }, style: s.linhaMiolo, children: miolo })), !valorEhTexto && it.value != null ? it.value : null] }, it.id));
317
349
  }) }));
318
350
  }
319
351
  /**
@@ -388,7 +420,7 @@ export function Tabs({ tabs, value, onChange, label, style, ...rest }) {
388
420
  const aberta = tabs.find((it) => it.id === value);
389
421
  return (_jsxs(View, { style: style, ...rest, children: [_jsx(FilaRolante, { children: _jsx(View, { accessibilityRole: "tablist", accessibilityLabel: label ?? strings.tabsLabel, style: s.abas, children: tabs.map((it) => {
390
422
  const ativa = it.id === value;
391
- return (_jsx(Pressable, { onPress: it.disabled ? undefined : () => onChange?.(it.id), disabled: it.disabled, accessibilityRole: "tab", accessibilityState: { selected: ativa, disabled: !!it.disabled }, accessibilityLabel: typeof it.label === "string" ? it.label : undefined, style: [s.abaDeTab, ativa && s.abaDeTabAtiva, it.disabled && { opacity: 0.5 }], children: typeof it.label === "string"
423
+ return (_jsx(Pressable, { onPress: it.disabled ? undefined : () => onChange?.(it.id), disabled: it.disabled, accessibilityRole: "tab", accessibilityState: { 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"
392
424
  ? _jsx(Text, { size: "sm", weight: ativa ? 600 : 400, style: { color: ativa ? t.color.foreground : t.color.mutedForeground }, children: it.label })
393
425
  : it.label }, it.id));
394
426
  }) }) }), aberta
package/dist/numero.js CHANGED
@@ -80,7 +80,7 @@ const folha = criarFolha((t) => ({
80
80
  },
81
81
  campoLargo: { width: undefined, flex: 1, minWidth: t.size.space16 },
82
82
  invalido: { borderColor: t.color.danger400 ?? t.color.destructive },
83
- desabilitado: { opacity: 0.5 },
83
+ desabilitado: { opacity: t.size.opacityDisabled },
84
84
  }));
85
85
  // ─────────────────────────────────────────────────────────────────────────────────────────────
86
86
  // O formatador, e a volta que a web não precisou escrever
package/dist/screen.d.ts CHANGED
@@ -29,6 +29,22 @@ export interface ScreenProps extends ViewProps {
29
29
  */
30
30
  onRefresh?: () => void;
31
31
  refreshing?: boolean;
32
+ /**
33
+ * **O rodapé fixo** — R-06, 24/09/2026. Fica FORA da rolagem, colado embaixo, e respeita a
34
+ * área do sistema sozinho:
35
+ *
36
+ * ```tsx
37
+ * <Screen scroll footer={<Button fullWidth>Assinar</Button>}>…</Screen>
38
+ * ```
39
+ *
40
+ * A borda de baixo dele fica `space4` acima do topo da área do sistema, a mesma conta do
41
+ * `BottomNav` flutuante. Dentro de um `BottomNavProvider`, ele sobe acima da barra de abas.
42
+ *
43
+ * ⚠ **Sem gancho de inset, de propósito.** A `Screen` não exige `SafeAreaProvider` acima, e
44
+ * `useSafeAreaInsets` exige. Quando a tela não pega a borda de baixo (`edges` sem `"bottom"`), o
45
+ * rodapé ganha a própria `SafeAreaView` só nessa borda — a mesma peça nativa que a raiz já usa.
46
+ */
47
+ footer?: React.ReactNode;
32
48
  children?: React.ReactNode;
33
49
  }
34
50
  /**
@@ -49,4 +65,4 @@ export interface ScreenProps extends ViewProps {
49
65
  * nativa e lê o inset do sistema sozinha (medido no fonte da 5.9.1: ela não consome contexto
50
66
  * nenhum). Quem exige o provider são os HOOKS — e quem usa `react-navigation` já o tem.
51
67
  */
52
- export declare function Screen({ edges, padded, scroll, background, onRefresh, refreshing, style, children, ...rest }: ScreenProps): React.JSX.Element;
68
+ export declare function Screen({ edges, padded, scroll, background, onRefresh, refreshing, footer, style, children, ...rest }: ScreenProps): React.JSX.Element;
package/dist/screen.js CHANGED
@@ -1,5 +1,5 @@
1
- import { jsx as _jsx } from "react/jsx-runtime";
2
- import { RefreshControl, ScrollView } from "react-native";
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { RefreshControl, ScrollView, View } from "react-native";
3
3
  import { SafeAreaView } from "react-native-safe-area-context";
4
4
  import { useBottomNavSpace } from "./barranav.js";
5
5
  import { criarFolha } from "./estilos.js";
@@ -16,6 +16,11 @@ const folha = criarFolha((t) => ({
16
16
  respiro: { padding: t.size.space4 },
17
17
  // Sem isto, conteúdo curto não ocupa a altura e nada consegue se centralizar na tela.
18
18
  conteudo: { flexGrow: 1 },
19
+ // O rodapé fixo (R-06): o mesmo `respiro`, e só o de cima quando a raiz já recua. O
20
+ // `marginTop: "auto"` é o que o cola no pé quando a tela não rola e o conteúdo é curto; com
21
+ // `scroll` ele não muda nada, porque o rolador já ocupa o resto.
22
+ rodape: { marginTop: "auto", padding: t.size.space4 },
23
+ rodapeNoRespiro: { marginTop: "auto", paddingTop: t.size.space4 },
19
24
  }));
20
25
  /**
21
26
  * As quatro. **Não é excesso de zelo:** em paisagem o entalhe fica à ESQUERDA ou à DIREITA, e a
@@ -40,28 +45,53 @@ const TODAS_AS_BORDAS = ["top", "right", "bottom", "left"];
40
45
  * nativa e lê o inset do sistema sozinha (medido no fonte da 5.9.1: ela não consome contexto
41
46
  * nenhum). Quem exige o provider são os HOOKS — e quem usa `react-navigation` já o tem.
42
47
  */
43
- export function Screen({ edges = TODAS_AS_BORDAS, padded = true, scroll = false, background = "background", onRefresh, refreshing, style, children, ...rest }) {
48
+ export function Screen({ edges = TODAS_AS_BORDAS, padded = true, scroll = false, background = "background", onRefresh, refreshing, footer, style, children, ...rest }) {
44
49
  const t = useAureaTokens();
45
50
  const s = folha(t);
46
51
  const fundo = s[`fundo_${background}`];
47
52
  // 🔴 O ESPAÇO DA BARRA DE ABAS, quando ela flutua por cima desta tela — ver `barranav.tsx`.
48
53
  // **Zero** fora de um `BottomNavProvider`, que é o caso de toda tela cheia: sem abas, nada muda.
49
54
  const respiroDaBarra = useBottomNavSpace();
50
- return (_jsx(SafeAreaView, { style: [s.raiz, fundo, !scroll && padded && s.respiro, style], edges: edges, ...rest, children: scroll ? (_jsx(ScrollView, { style: s.raiz,
51
- // ⚠ O respiro da barra SOMA ao do tema em vez de substituí-lo: escrever
52
- // `paddingBottom` depois de `s.respiro` apagaria os 16 do tema e colaria o último
53
- // item na pílula. A conta é explícita porque a folha não soma sozinha.
54
- contentContainerStyle: [
55
- s.conteudo, padded && s.respiro,
56
- respiroDaBarra > 0
57
- && { paddingBottom: (padded ? t.size.space4 : 0) + respiroDaBarra },
58
- ],
59
- // A rosca do sistema é a única aparência da plataforma que a Aurea aceita aqui, e a
60
- // razão é que ela NÃO É desenho nosso: é o gesto do sistema operacional, com a física
61
- // que a pessoa já conhece. O que dá para pintar, pintamos — a cor é do tema.
62
- refreshControl: onRefresh ? (_jsx(RefreshControl, { refreshing: !!refreshing, onRefresh: onRefresh, tintColor: t.color.primary, colors: [t.color.primary], progressBackgroundColor: t.color.card })) : undefined,
63
- // Sem isto, tocar um botão com o teclado aberto exige DOIS toques: o primeiro só
64
- // fecha o teclado. É um defeito conhecido do RN, e a tela é o lugar certo de absorvê-lo
65
- // — o Lote 4 é todo formulário.
66
- keyboardShouldPersistTaps: "handled", children: children })) : (children) }));
55
+ return (_jsxs(SafeAreaView, { style: [s.raiz, fundo, !scroll && padded && s.respiro, style], edges: edges, ...rest, children: [scroll ? (_jsx(ScrollView, { style: s.raiz,
56
+ // ⚠ O respiro da barra SOMA ao do tema em vez de substituí-lo: escrever
57
+ // `paddingBottom` depois de `s.respiro` apagaria os 16 do tema e colaria o último
58
+ // item na pílula. A conta é explícita porque a folha não soma sozinha.
59
+ contentContainerStyle: [
60
+ s.conteudo, padded && s.respiro,
61
+ // Com rodapé, quem sobe acima da barra de abas é ELE; a rolagem termina em cima dele.
62
+ respiroDaBarra > 0 && footer == null
63
+ && { paddingBottom: (padded ? t.size.space4 : 0) + respiroDaBarra },
64
+ ],
65
+ // A rosca do sistema é a única aparência da plataforma que a Aurea aceita aqui, e a
66
+ // razão é que ela NÃO É desenho nosso: é o gesto do sistema operacional, com a física
67
+ // que a pessoa já conhece. O que dá para pintar, pintamos — a cor é do tema.
68
+ refreshControl: onRefresh ? (_jsx(RefreshControl, { refreshing: !!refreshing, onRefresh: onRefresh, tintColor: t.color.primary, colors: [t.color.primary], progressBackgroundColor: t.color.card })) : undefined,
69
+ // Sem isto, tocar um botão com o teclado aberto exige DOIS toques: o primeiro só
70
+ // fecha o teclado. É um defeito conhecido do RN, e a tela é o lugar certo de absorvê-lo
71
+ // — o Lote 4 é todo formulário.
72
+ keyboardShouldPersistTaps: "handled", children: children })) : (children), footer != null && (_jsx(Rodape, { dentroDoRespiro: !scroll && padded, pegaABorda: edges.includes("bottom"), respiroDaBarra: respiroDaBarra, children: footer }))] }));
73
+ }
74
+ /**
75
+ * O rodapé da `Screen`. Três contas, cada uma com a sua razão:
76
+ *
77
+ * - **o recuo:** `space4`, o `respiro` da própria tela. Sem `scroll` e com `padded`, a raiz já
78
+ * recua 16 dos quatro lados, então o rodapé só afasta do conteúdo de cima;
79
+ * - **a barra de abas:** dentro de um `BottomNavProvider` ela flutua por cima; o rodapé sobe o
80
+ * número que ela informa, que já inclui a área do sistema;
81
+ * - **a área do sistema:** se a raiz pega a borda de baixo, ela já soma o inset; se não pega, o
82
+ * rodapé pega sozinho, com a mesma `SafeAreaView`.
83
+ */
84
+ function Rodape({ dentroDoRespiro, pegaABorda, respiroDaBarra, children }) {
85
+ const t = useAureaTokens();
86
+ const s = folha(t);
87
+ const recuo = [
88
+ dentroDoRespiro ? s.rodapeNoRespiro : s.rodape,
89
+ respiroDaBarra > 0
90
+ && { paddingBottom: (dentroDoRespiro ? 0 : t.size.space4) + respiroDaBarra },
91
+ ];
92
+ // Com a barra de abas, o número dela já traz a área do sistema: pegar a borda de novo somaria
93
+ // o inset duas vezes.
94
+ if (pegaABorda || respiroDaBarra > 0)
95
+ return _jsx(View, { style: recuo, children: children });
96
+ return _jsx(SafeAreaView, { edges: ["bottom"], style: recuo, children: children });
67
97
  }
package/dist/sistema.js CHANGED
@@ -49,7 +49,7 @@ const folha = criarFolha((t) => ({
49
49
  borderRadius: t.size.radiusControl, backgroundColor: t.color.fieldBg,
50
50
  },
51
51
  invalido: { borderColor: t.color.danger400 ?? t.color.destructive },
52
- desabilitado: { opacity: 0.5 },
52
+ desabilitado: { opacity: t.size.opacityDisabled },
53
53
  galeria: { flexDirection: "row", flexWrap: "wrap", gap: t.size.space2 },
54
54
  miniatura: { position: "relative" },
55
55
  remover: { position: "absolute", top: -t.size.space2, right: -t.size.space2 },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aurea-uds/native",
3
- "version": "0.8.12",
3
+ "version": "0.8.14",
4
4
  "description": "React Native target for Aurea UDS: the theme x density provider, the first components and the Carbon icons, drawn on react-native-svg.",
5
5
  "keywords": [
6
6
  "aurea",
@@ -47,7 +47,7 @@
47
47
  "NOTICE"
48
48
  ],
49
49
  "dependencies": {
50
- "@aurea-uds/tokens": "^0.8.12"
50
+ "@aurea-uds/tokens": "^0.8.14"
51
51
  },
52
52
  "peerDependencies": {
53
53
  "@react-native-community/datetimepicker": ">=8",