@aurea-uds/native 0.8.13 → 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/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,11 @@ 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;
33
36
  /**
package/dist/estilos.js CHANGED
@@ -40,8 +40,11 @@ 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")
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, AureaBottomNavWidth, 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 é
@@ -110,7 +128,7 @@ export type CardProps = CardDaMarca | CardComum | CardTocavel;
110
128
  * ✅ **O cartão responde ao toque com `onPress`** (R-04, 24/09/2026):
111
129
  *
112
130
  * ```tsx
113
- * <Card onPress={abrir} accessibilityLabel="Moto CG 160">…</Card>
131
+ * <Card onPress={abrir} accessibilityLabel="Relatório de março">…</Card>
114
132
  * ```
115
133
  *
116
134
  * Com `onPress` a variante padrão passa a ser `interactive` — a pele de um cartão que é alvo — e
package/dist/layout.js CHANGED
@@ -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.
@@ -136,7 +153,7 @@ export function Separator({ orientation = "horizontal", label, style, ...rest })
136
153
  * ✅ **O cartão responde ao toque com `onPress`** (R-04, 24/09/2026):
137
154
  *
138
155
  * ```tsx
139
- * <Card onPress={abrir} accessibilityLabel="Moto CG 160">…</Card>
156
+ * <Card onPress={abrir} accessibilityLabel="Relatório de março">…</Card>
140
157
  * ```
141
158
  *
142
159
  * Com `onPress` a variante padrão passa a ser `interactive` — a pele de um cartão que é alvo — e
@@ -103,8 +103,17 @@ export interface BottomNavProps extends ViewProps {
103
103
  */
104
104
  export declare function BottomNav({ items, current, variant, indicator, width, label, style, ...rest }: BottomNavProps): React.JSX.Element;
105
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";
106
108
  export interface TopbarProps extends ViewProps {
107
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;
108
117
  brand?: React.ReactNode;
109
118
  children?: React.ReactNode;
110
119
  }
@@ -125,7 +134,7 @@ export interface TopbarProps extends ViewProps {
125
134
  * e **não há landmarks no RN**. Um papel inventado diria uma coisa errada; a barra fica sem papel,
126
135
  * e quem nomeia a tela é o conteúdo dela.
127
136
  */
128
- 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;
129
138
  export interface AureaNavListItem {
130
139
  id: string;
131
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,
@@ -293,9 +298,13 @@ export function BottomNav({ items, current, variant = "floating", indicator = "n
293
298
  * e **não há landmarks no RN**. Um papel inventado diria uma coisa errada; a barra fica sem papel,
294
299
  * e quem nomeia a tela é o conteúdo dela.
295
300
  */
296
- export function Topbar({ variant = "floating", brand, children, style, ...rest }) {
301
+ export function Topbar({ variant = "floating", inset = "bar", brand, children, style, ...rest }) {
297
302
  const s = folha(useAureaTokens());
298
- 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] }));
299
308
  }
300
309
  /**
301
310
  * A lista de destinos DENTRO da página — a tela de ajustes, não a moldura do app.
@@ -336,7 +345,7 @@ export function NavList({ items, chevron = "chevron--right", style, ...rest }) {
336
345
  ? _jsx(Text, { size: "xs", tone: "muted", numberOfLines: 1, children: it.description })
337
346
  : it.description)] }), valorEhTexto && _jsx(Text, { size: "sm", tone: "muted", children: it.value }), temAcao && chevron
338
347
  && _jsx(Icon, { name: chevron, size: "sm", color: t.color.mutedForeground })] }));
339
- 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));
340
349
  }) }));
341
350
  }
342
351
  /**
@@ -411,7 +420,7 @@ export function Tabs({ tabs, value, onChange, label, style, ...rest }) {
411
420
  const aberta = tabs.find((it) => it.id === value);
412
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) => {
413
422
  const ativa = it.id === value;
414
- 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"
415
424
  ? _jsx(Text, { size: "sm", weight: ativa ? 600 : 400, style: { color: ativa ? t.color.foreground : t.color.mutedForeground }, children: it.label })
416
425
  : it.label }, it.id));
417
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.13",
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.13"
50
+ "@aurea-uds/tokens": "^0.8.14"
51
51
  },
52
52
  "peerDependencies": {
53
53
  "@react-native-community/datetimepicker": ">=8",