@aurea-uds/native 0.8.1 → 0.8.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/actions.d.ts +22 -2
- package/dist/actions.js +4 -4
- package/dist/display.d.ts +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.js +5 -0
- package/dist/inputs.d.ts +55 -0
- package/dist/inputs.js +104 -26
- package/dist/strings.d.ts +8 -0
- package/dist/strings.js +4 -0
- package/dist/text.d.ts +1 -1
- package/package.json +2 -2
package/dist/actions.d.ts
CHANGED
|
@@ -3,7 +3,7 @@ import { type PressableProps } from "react-native";
|
|
|
3
3
|
import { type AureaIconRegistry, type IconName } from "./icon.js";
|
|
4
4
|
/** Quanto peso a caixa tem. */
|
|
5
5
|
export type AureaButtonAppearance = "solid" | "outline" | "ghost";
|
|
6
|
-
/** O que a cor significa. Mesmos nomes da web (ADR-
|
|
6
|
+
/** O que a cor significa. Mesmos nomes da web (ADR-0044). */
|
|
7
7
|
export type AureaButtonTone = "neutral" | "brand" | "danger" | "success" | "warning" | "info";
|
|
8
8
|
/** As cinco alturas de controle, todas de token de densidade. */
|
|
9
9
|
export type AureaButtonSize = "xs" | "sm" | "md" | "lg" | "xl";
|
|
@@ -14,6 +14,26 @@ export interface ButtonProps extends Omit<PressableProps, "children" | "style">
|
|
|
14
14
|
size?: AureaButtonSize;
|
|
15
15
|
leadingIcon?: IconName;
|
|
16
16
|
trailingIcon?: IconName;
|
|
17
|
+
/**
|
|
18
|
+
* Um desenho QUALQUER na frente do texto, para quando a marca não pode ser um ícone nosso.
|
|
19
|
+
*
|
|
20
|
+
* ⚠ **Ele existe por causa de MARCA REGISTRADA, não por conveniência.** O botão "Entrar com
|
|
21
|
+
* Google" e o "Entrar com Apple" exigem o desenho oficial de cada um, e nenhum dos dois pode
|
|
22
|
+
* viver dentro desta biblioteca: o Google proíbe redesenhar e manda usar o arquivo do pacote
|
|
23
|
+
* dele; a Apple proíbe usar o logo sem licença escrita. **Então a marca entra pelo app**, e o
|
|
24
|
+
* que a Aurea dá é a cápsula em volta.
|
|
25
|
+
*
|
|
26
|
+
* <Button appearance="outline" leading={<RNImage source={logoGoogle} style={{width: 18, height: 18}} />}>
|
|
27
|
+
* Entrar com Google
|
|
28
|
+
* </Button>
|
|
29
|
+
*
|
|
30
|
+
* ⚠ **A cor do texto NÃO atravessa para cá** — o que entra desenha a própria cor, e é assim
|
|
31
|
+
* que tem de ser: a marca do Google tem cor fixa, e tingi-la seria justamente o que a regra
|
|
32
|
+
* dele proíbe. Um `leadingIcon` nosso continua herdando a cor do botão.
|
|
33
|
+
*/
|
|
34
|
+
leading?: React.ReactNode;
|
|
35
|
+
/** O mesmo, do outro lado. */
|
|
36
|
+
trailing?: React.ReactNode;
|
|
17
37
|
/** Registro de ícones, quando não há um em contexto. Ver `criarRegistroDeIcones`. */
|
|
18
38
|
icons?: AureaIconRegistry;
|
|
19
39
|
fullWidth?: boolean;
|
|
@@ -29,7 +49,7 @@ export interface ButtonProps extends Omit<PressableProps, "children" | "style">
|
|
|
29
49
|
* descreve: lá, um botão desabilitado some da ordem de foco e a explicação pendurada nele não é
|
|
30
50
|
* lida por ninguém. Aqui não há o dilema, então não há o par `aria-disabled` — um só basta.
|
|
31
51
|
*/
|
|
32
|
-
export declare function Button({ children, appearance, tone, size, leadingIcon, trailingIcon, icons, fullWidth, pressed, disabled, accessibilityLabel, ...rest }: ButtonProps): React.JSX.Element;
|
|
52
|
+
export declare function Button({ children, appearance, tone, size, leadingIcon, trailingIcon, leading, trailing, icons, fullWidth, pressed, disabled, accessibilityLabel, ...rest }: ButtonProps): React.JSX.Element;
|
|
33
53
|
export interface IconButtonProps extends Omit<ButtonProps, "children" | "leadingIcon" | "trailingIcon" | "fullWidth"> {
|
|
34
54
|
name: IconName;
|
|
35
55
|
/** **Obrigatório**: um botão que só tem glifo não tem texto para o leitor de tela anunciar. */
|
package/dist/actions.js
CHANGED
|
@@ -5,7 +5,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
5
5
|
// Medida a ficha da web antes de escrever (passo 1 do BUILDING.md). Lá existem TRÊS props que
|
|
6
6
|
// pintam a mesma coisa, e a própria ficha diz qual é qual: *"`variant` é atalho para o par
|
|
7
7
|
// (appearance, tone). Os treze nomes continuam valendo e pintam exatamente o mesmo; a API de dois
|
|
8
|
-
// eixos é appearance + tone"* — a ADR-
|
|
8
|
+
// eixos é appearance + tone"* — a ADR-0044.
|
|
9
9
|
//
|
|
10
10
|
// O `variant` existe lá porque a Aurea **está publicada** e aqueles treze nomes são contrato com
|
|
11
11
|
// quem instalou. **Aqui não há nada publicado**, e nascer com o atalho seria nascer com a dívida:
|
|
@@ -88,7 +88,7 @@ const folha = criarFolha((t) => ({
|
|
|
88
88
|
* descreve: lá, um botão desabilitado some da ordem de foco e a explicação pendurada nele não é
|
|
89
89
|
* lida por ninguém. Aqui não há o dilema, então não há o par `aria-disabled` — um só basta.
|
|
90
90
|
*/
|
|
91
|
-
export function Button({ children, appearance = "solid", tone = "neutral", size = "md", leadingIcon, trailingIcon, icons, fullWidth = false, pressed, disabled, accessibilityLabel, ...rest }) {
|
|
91
|
+
export function Button({ children, appearance = "solid", tone = "neutral", size = "md", leadingIcon, trailingIcon, leading, trailing, icons, fullWidth = false, pressed, disabled, accessibilityLabel, ...rest }) {
|
|
92
92
|
const t = useAureaTokens();
|
|
93
93
|
const s = folha(t);
|
|
94
94
|
const cor = pintar(t, tone);
|
|
@@ -104,9 +104,9 @@ export function Button({ children, appearance = "solid", tone = "neutral", size
|
|
|
104
104
|
return (_jsx(Pressable, { disabled: disabled, accessibilityRole: "button", accessibilityLabel: accessibilityLabel, accessibilityState: { disabled: !!disabled, ...(pressed === undefined ? null : { checked: pressed }) }, style: ({ pressed: tocando }) => [
|
|
105
105
|
s.alvo, fullWidth && s.alvoLargura,
|
|
106
106
|
tocando && s.pressionado, disabled && s.inerte,
|
|
107
|
-
], ...rest, children: _jsxs(View, { style: [s.caixa, caixa], children: [leadingIcon ? _jsx(Icon, { name: leadingIcon, size: ICONE[size], color: corDoTexto, icons: icons }) : null, typeof children === "string"
|
|
107
|
+
], ...rest, children: _jsxs(View, { style: [s.caixa, caixa], children: [leading ?? null, leadingIcon ? _jsx(Icon, { name: leadingIcon, size: ICONE[size], color: corDoTexto, icons: icons }) : null, typeof children === "string"
|
|
108
108
|
? _jsx(Text, { size: FONTE[size], weight: 500, leading: "none", style: { color: corDoTexto }, children: children })
|
|
109
|
-
: children, trailingIcon ? _jsx(Icon, { name: trailingIcon, size: ICONE[size], color: corDoTexto, icons: icons }) : null] }) }));
|
|
109
|
+
: children, trailingIcon ? _jsx(Icon, { name: trailingIcon, size: ICONE[size], color: corDoTexto, icons: icons }) : null, trailing ?? null] }) }));
|
|
110
110
|
}
|
|
111
111
|
/**
|
|
112
112
|
* Botão quadrado, só glifo.
|
package/dist/display.d.ts
CHANGED
|
@@ -10,7 +10,7 @@ export declare const formatarContagem: (count: number, max?: number) => string;
|
|
|
10
10
|
export interface BadgeProps extends ViewProps {
|
|
11
11
|
/**
|
|
12
12
|
* O tom. Chama-se `tone` e não `variant`, como no `Button` do Lote 1 — o vocabulário de cor
|
|
13
|
-
* com significado é `tone` em todo o alvo nativo (ADR-
|
|
13
|
+
* com significado é `tone` em todo o alvo nativo (ADR-0044 é quem separa os dois na web).
|
|
14
14
|
*/
|
|
15
15
|
tone?: AureaBadgeTone;
|
|
16
16
|
emphasis?: AureaBadgeEmphasis;
|
package/dist/index.d.ts
CHANGED
|
@@ -25,6 +25,8 @@ export { BottomNav, Topbar, NavList, Stepper } from "./navigation.js";
|
|
|
25
25
|
export type { BottomNavProps, TopbarProps, NavListProps, StepperProps, AureaNavItem, AureaNavListItem, AureaStepItem, AureaStepState, AureaBottomNavVariant, AureaBottomNavIndicator, AureaTopbarVariant, } from "./navigation.js";
|
|
26
26
|
export { Field, Label, Input, Textarea, Select, Switch, Checkbox, Radio, SegmentedControl, Form, KeyboardAvoiding, useCampo, } from "./inputs.js";
|
|
27
27
|
export type { FieldProps, LabelProps, InputProps, TextareaProps, SelectProps, SwitchProps, CheckboxProps, RadioProps, SegmentedControlProps, FormProps, KeyboardAvoidingProps, AureaFieldSize, } from "./inputs.js";
|
|
28
|
+
export { InputGroup, InputGroupAddon, PasswordField, useGrupoDeCampo } from "./inputs.js";
|
|
29
|
+
export type { InputGroupProps, InputGroupAddonProps, PasswordFieldProps } from "./inputs.js";
|
|
28
30
|
export { Dialog, ConfirmDialog, Drawer, BottomSheet } from "./overlays.js";
|
|
29
31
|
export type { DialogProps, ConfirmDialogProps, DrawerProps, BottomSheetProps, AureaDrawerSide, } from "./overlays.js";
|
|
30
32
|
export { ToastHost, Toast, useToast } from "./toast.js";
|
package/dist/index.js
CHANGED
|
@@ -54,6 +54,11 @@ export { BottomNav, Topbar, NavList, Stepper } from "./navigation.js";
|
|
|
54
54
|
// nome, a dica e o estado de inválido para o controle por CONTEXTO. É a tradução que decidiu o
|
|
55
55
|
// lote — o RN não tem `id` nem `htmlFor`.
|
|
56
56
|
export { Field, Label, Input, Textarea, Select, Switch, Checkbox, Radio, SegmentedControl, Form, KeyboardAvoiding, useCampo, } from "./inputs.js";
|
|
57
|
+
// ⚠ O GRUPO e o campo de SENHA entraram depois do Lote 4, medidos pelo app na tela de entrar
|
|
58
|
+
// (15/09/2026): o campo não tinha onde encaixar glifo, e `secureTextEntry` esconde a senha sem
|
|
59
|
+
// dar botão para mostrá-la. **Os dois já existiam na web desde 29/08/2026** — a falta era só
|
|
60
|
+
// deste lado, e o desenho daqui é o de lá traduzido, não um desenho novo.
|
|
61
|
+
export { InputGroup, InputGroupAddon, PasswordField, useGrupoDeCampo } from "./inputs.js";
|
|
57
62
|
// ⚠ `DatePicker` e `PhotoInput` NÃO saem daqui, e é de propósito: eles vivem em
|
|
58
63
|
// `@aurea-uds/native/system`, pela mesma razão que os ícones vivem em `./icons/*` (ADR-0038).
|
|
59
64
|
// Sair por este barril traria os dois módulos NATIVOS ao grafo de todo app — inclusive de quem
|
package/dist/inputs.d.ts
CHANGED
|
@@ -11,6 +11,14 @@ type ContextoDeCampo = {
|
|
|
11
11
|
};
|
|
12
12
|
/** O que os controles leem do `Field` acima deles. Público porque um controle novo vai precisar. */
|
|
13
13
|
export declare function useCampo(): ContextoDeCampo | null;
|
|
14
|
+
interface ContextoDeGrupo {
|
|
15
|
+
size: AureaFieldSize;
|
|
16
|
+
disabled: boolean;
|
|
17
|
+
/** O campo avisa o grupo quando ganha e perde o foco — no RN não existe `:focus-within`. */
|
|
18
|
+
aoFocar: (v: boolean) => void;
|
|
19
|
+
}
|
|
20
|
+
/** O grupo em volta do campo, quando existe um. `null` quando o campo está sozinho. */
|
|
21
|
+
export declare function useGrupoDeCampo(): ContextoDeGrupo | null;
|
|
14
22
|
export interface FieldProps extends ViewProps {
|
|
15
23
|
label?: string;
|
|
16
24
|
hint?: React.ReactNode;
|
|
@@ -120,6 +128,53 @@ interface ControleProps {
|
|
|
120
128
|
style?: StyleProp<ViewStyle>;
|
|
121
129
|
testID?: string;
|
|
122
130
|
}
|
|
131
|
+
export interface InputGroupProps extends ViewProps {
|
|
132
|
+
/** O degrau do sistema. Cai no `Field` em volta quando não vem, como no `Input`. */
|
|
133
|
+
size?: AureaFieldSize;
|
|
134
|
+
disabled?: boolean;
|
|
135
|
+
testID?: string;
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* A caixa de campo que aceita coisas dentro — glifo na frente, botão atrás, os dois.
|
|
139
|
+
*
|
|
140
|
+
* <InputGroup>
|
|
141
|
+
* <InputGroupAddon><Icon name="email" size="sm" /></InputGroupAddon>
|
|
142
|
+
* <Input value={email} onChangeText={setEmail} keyboardType="email-address" />
|
|
143
|
+
* </InputGroup>
|
|
144
|
+
*
|
|
145
|
+
* ⚠ **O `Input` lá dentro para de desenhar a própria caixa** — quem desenha borda, fundo, altura,
|
|
146
|
+
* foco e inválido passa a ser o grupo. Isso é automático: o campo enxerga o grupo por contexto.
|
|
147
|
+
*/
|
|
148
|
+
export declare function InputGroup({ size, disabled, children, style, testID, ...rest }: InputGroupProps): React.JSX.Element;
|
|
149
|
+
export interface InputGroupAddonProps extends ViewProps {
|
|
150
|
+
testID?: string;
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Um encaixe dentro do `InputGroup`. **A posição é a do JSX** — antes do campo fica na frente,
|
|
154
|
+
* depois fica atrás. Não há prop de lado, e o porquê está no bloco do grupo lá em cima.
|
|
155
|
+
*/
|
|
156
|
+
export declare function InputGroupAddon({ children, style, testID, ...rest }: InputGroupAddonProps): React.JSX.Element;
|
|
157
|
+
/**
|
|
158
|
+
* O campo de senha, com o botão que mostra e esconde.
|
|
159
|
+
*
|
|
160
|
+
* <Field label="Senha"><PasswordField value={senha} onChangeText={setSenha} /></Field>
|
|
161
|
+
*
|
|
162
|
+
* ⚠ **`secureTextEntry` não entra**: um campo de senha que aceitasse essa prop deixaria de ser
|
|
163
|
+
* um. É a mesma razão pela qual o `type` não atravessa no `PasswordField` da web.
|
|
164
|
+
*
|
|
165
|
+
* ⚠ **O rótulo do botão é o anúncio do estado** — ele troca entre "Mostrar senha" e "Ocultar
|
|
166
|
+
* senha". Não há estado de pressionado junto, e isso é decisão copiada da web: com os dois, o
|
|
167
|
+
* leitor de tela lê a mesma informação duas vezes.
|
|
168
|
+
*
|
|
169
|
+
* ⚠ **`autoCapitalize` nasce em `none`.** No Android o teclado maiúsculiza a primeira letra por
|
|
170
|
+
* padrão, e numa senha isso é o erro de digitação mais caro que existe: o campo está escondido,
|
|
171
|
+
* então ninguém vê o que deu errado.
|
|
172
|
+
*/
|
|
173
|
+
export interface PasswordFieldProps extends Omit<InputProps, "secureTextEntry" | "multiline" | "keyboardType"> {
|
|
174
|
+
/** Começa visível. Padrão: escondida. */
|
|
175
|
+
defaultVisible?: boolean;
|
|
176
|
+
}
|
|
177
|
+
export declare function PasswordField({ defaultVisible, size, disabled, autoCapitalize, testID, style, ...rest }: PasswordFieldProps): React.JSX.Element;
|
|
123
178
|
export type CheckboxProps = ControleProps;
|
|
124
179
|
export type RadioProps = ControleProps;
|
|
125
180
|
/** A caixa que marca. `accessibilityRole="checkbox"` + `state.checked`. */
|
package/dist/inputs.js
CHANGED
|
@@ -40,10 +40,11 @@ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-run
|
|
|
40
40
|
import * as React from "react";
|
|
41
41
|
import { Animated, KeyboardAvoidingView as KeyboardAvoidingViewRN, Modal, Platform, Pressable, ScrollView, TextInput, View, } from "react-native";
|
|
42
42
|
import { criarFolha } from "./estilos.js";
|
|
43
|
+
import { IconButton } from "./actions.js";
|
|
43
44
|
import { Icon } from "./icon.js";
|
|
44
45
|
import { useReduceMotion } from "./movimento.js";
|
|
45
46
|
import { Text } from "./text.js";
|
|
46
|
-
import { useAureaTokens } from "./theme.js";
|
|
47
|
+
import { useAureaStrings, useAureaTokens } from "./theme.js";
|
|
47
48
|
const alturaDoTamanho = (t, s) => s === "sm" ? t.size.controlHSm : s === "lg" ? t.size.controlHLg : t.size.controlHMd;
|
|
48
49
|
const fonteDoTamanho = (t, s) => s === "sm" ? t.size.textXs : s === "lg" ? t.size.textBase : t.size.textMd;
|
|
49
50
|
const respiroDoTamanho = (t, s) => s === "sm" ? t.size.space3 : s === "lg" ? t.size.space4 : 13;
|
|
@@ -61,6 +62,21 @@ const folha = criarFolha((t) => ({
|
|
|
61
62
|
areaDeTexto: { minHeight: 104, paddingVertical: t.size.space3, borderRadius: t.size.radiusLg },
|
|
62
63
|
invalido: { borderColor: t.color.danger400 ?? t.color.destructive },
|
|
63
64
|
desabilitado: { opacity: 0.5 },
|
|
65
|
+
// O GRUPO é a mesma caixa do campo virada em LINHA, e os números são os mesmos: mesma borda,
|
|
66
|
+
// mesmo raio de controle, mesmo fundo. A altura e o respiro horizontal entram por tamanho, como
|
|
67
|
+
// no `Input`, porque é o grupo que passa a desenhar a caixa quando existe um.
|
|
68
|
+
grupo: {
|
|
69
|
+
flexDirection: "row", alignItems: "center", width: "100%", minWidth: 0,
|
|
70
|
+
borderWidth: t.size.borderWidth, borderColor: t.color.borderStrong,
|
|
71
|
+
borderRadius: t.size.radiusControl, backgroundColor: t.color.fieldBg,
|
|
72
|
+
gap: t.size.space1,
|
|
73
|
+
},
|
|
74
|
+
// Dentro do grupo o campo perde a caixa e vira só o texto — a borda passou a ser do grupo.
|
|
75
|
+
// ⚠ As DUAS últimas linhas continuam obrigatórias: são o conserto de Android de 09/09/2026
|
|
76
|
+
// (`paddingVertical: 0` desliga a injeção do tema, `textAlignVertical` tira o texto do topo).
|
|
77
|
+
// É o mesmo par que o `campoDeTexto` do `busca.tsx` carrega pela mesma razão.
|
|
78
|
+
campoNoGrupo: { flex: 1, minWidth: 0, paddingVertical: 0, textAlignVertical: "center" },
|
|
79
|
+
encaixe: { flexDirection: "row", alignItems: "center", flexShrink: 0, gap: t.size.space1 },
|
|
64
80
|
marcaBase: {
|
|
65
81
|
alignItems: "center", justifyContent: "center", marginTop: 1,
|
|
66
82
|
borderWidth: t.size.borderWidth, borderColor: t.color.borderStrong,
|
|
@@ -96,6 +112,11 @@ const CampoCtx = React.createContext(null);
|
|
|
96
112
|
export function useCampo() {
|
|
97
113
|
return React.useContext(CampoCtx);
|
|
98
114
|
}
|
|
115
|
+
const GrupoDeCampo = React.createContext(null);
|
|
116
|
+
/** O grupo em volta do campo, quando existe um. `null` quando o campo está sozinho. */
|
|
117
|
+
export function useGrupoDeCampo() {
|
|
118
|
+
return React.useContext(GrupoDeCampo);
|
|
119
|
+
}
|
|
99
120
|
/**
|
|
100
121
|
* Rótulo, controle, dica e erro — na ordem, com o respiro do CSS.
|
|
101
122
|
*
|
|
@@ -151,11 +172,13 @@ export function Input({ value, defaultValue, onChangeText, placeholder, disabled
|
|
|
151
172
|
const t = useAureaTokens();
|
|
152
173
|
const s = folha(t);
|
|
153
174
|
const campo = useCampo();
|
|
154
|
-
const
|
|
155
|
-
const
|
|
175
|
+
const grupo = useGrupoDeCampo();
|
|
176
|
+
const tam = size ?? grupo?.size ?? campo?.size ?? "md";
|
|
177
|
+
const inativo = disabled ?? grupo?.disabled ?? campo?.disabled;
|
|
156
178
|
const [focado, setFocado] = React.useState(false);
|
|
157
|
-
return (_jsx(TextInput, { testID: testID, value: value, defaultValue: defaultValue, onChangeText: onChangeText, placeholder: placeholder, placeholderTextColor: t.color.subtleForeground, editable: !inativo, keyboardType: keyboardType, secureTextEntry: secureTextEntry, autoCapitalize: autoCapitalize, multiline: multiline, onFocus: () => { setFocado(true); onFocus?.(); }, onBlur: () => {
|
|
179
|
+
return (_jsx(TextInput, { testID: testID, value: value, defaultValue: defaultValue, onChangeText: onChangeText, placeholder: placeholder, placeholderTextColor: t.color.subtleForeground, editable: !inativo, keyboardType: keyboardType, secureTextEntry: secureTextEntry, autoCapitalize: autoCapitalize, multiline: multiline, onFocus: () => { setFocado(true); grupo?.aoFocar(true); onFocus?.(); }, onBlur: () => {
|
|
158
180
|
setFocado(false);
|
|
181
|
+
grupo?.aoFocar(false);
|
|
159
182
|
// Só avisa quando o texto MUDOU: emitir o mesmo valor a cada saída de foco faria o app
|
|
160
183
|
// re-renderizar sem motivo, e num formulário de dez campos isso é dez renders por
|
|
161
184
|
// preenchimento. Mesma guarda da web (`inputs-client.tsx:194`).
|
|
@@ -169,30 +192,36 @@ export function Input({ value, defaultValue, onChangeText, placeholder, disabled
|
|
|
169
192
|
// O nome e a dica vêm do `Field` — ver o bloco acima sobre por que a ligação da web não
|
|
170
193
|
// atravessa. Sem isto, o campo é um nó anônimo para o leitor de tela.
|
|
171
194
|
accessibilityLabel: campo?.label, accessibilityHint: campo?.hint, accessibilityState: { disabled: !!inativo }, "aria-invalid": campo?.invalido, style: [
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
195
|
+
// DENTRO DE UM GRUPO o campo não desenha caixa nenhuma: borda, fundo, altura, respiro,
|
|
196
|
+
// inválido, foco e desabilitado passam a ser do grupo. Manter qualquer um dos dois
|
|
197
|
+
// desenhando daria borda dentro de borda — que é exatamente o defeito que o `.input-group
|
|
198
|
+
// > .input` da web zera à mão (`aurea.css:751`).
|
|
199
|
+
grupo ? s.campoNoGrupo : s.caixa,
|
|
200
|
+
grupo
|
|
201
|
+
? { fontSize: fonteDoTamanho(t, tam), fontFamily: t.font.ui[400], color: t.color.foreground }
|
|
202
|
+
: { height: alturaDoTamanho(t, tam), paddingHorizontal: respiroDoTamanho(t, tam),
|
|
203
|
+
fontSize: fonteDoTamanho(t, tam), fontFamily: t.font.ui[400], color: t.color.foreground,
|
|
204
|
+
// ⚠ AS DUAS LINHAS ABAIXO NÃO SÃO ENFEITE — sem elas o texto sobe e é CORTADO no
|
|
205
|
+
// Android, e foi assim que este campo chegou ao vidro em 09/09/2026.
|
|
206
|
+
//
|
|
207
|
+
// `padding-block:0` é LITERAL na web (`aurea.css:685`) e não atravessou. No RN a falta
|
|
208
|
+
// dele não é "sem padding": o `AndroidTextInputComponentDescriptor.h:101-107` INJETA o
|
|
209
|
+
// padding do tema do Android no nó do Yoga sempre que as props não trazem `padding`,
|
|
210
|
+
// `paddingTop`/`paddingBottom` nem `paddingVertical` — e o `hasPaddingVertical` é
|
|
211
|
+
// exatamente o que desliga a injeção. Com `height` fixa, esse padding come a altura por
|
|
212
|
+
// dentro e a caixa de texto fica menor que a linha: daí o corte.
|
|
213
|
+
//
|
|
214
|
+
// O `textAlignVertical` é o outro lado da mesma conta. Na web, um campo com
|
|
215
|
+
// `block-size` centraliza a linha sozinho; no Android o `setTextAlignVertical` do
|
|
216
|
+
// `ReactTextInputManager.kt:571` traduz `null`/`auto` para `Gravity.NO_GRAVITY`, que
|
|
217
|
+
// deixa o `EditText` no topo. `center` é o que reproduz a web — e o `Textarea` o
|
|
218
|
+
// sobrescreve para `top`, que é o que uma caixa alta faz nos dois lados.
|
|
219
|
+
paddingVertical: 0, textAlignVertical: "center" },
|
|
220
|
+
!grupo && campo?.invalido && s.invalido,
|
|
192
221
|
// O foco é o único estado que sobra do trio da web (hover/focus/active), e ele importa
|
|
193
222
|
// mais aqui: no telefone é a única pista de qual campo o teclado está alimentando.
|
|
194
|
-
focado && { borderColor: t.color.focusStrong },
|
|
195
|
-
inativo && s.desabilitado,
|
|
223
|
+
!grupo && focado && { borderColor: t.color.focusStrong },
|
|
224
|
+
!grupo && inativo && s.desabilitado,
|
|
196
225
|
style,
|
|
197
226
|
] }));
|
|
198
227
|
}
|
|
@@ -211,6 +240,55 @@ export function Textarea({ rows, size, style, ...rest }) {
|
|
|
211
240
|
style,
|
|
212
241
|
], multiline: true }));
|
|
213
242
|
}
|
|
243
|
+
/**
|
|
244
|
+
* A caixa de campo que aceita coisas dentro — glifo na frente, botão atrás, os dois.
|
|
245
|
+
*
|
|
246
|
+
* <InputGroup>
|
|
247
|
+
* <InputGroupAddon><Icon name="email" size="sm" /></InputGroupAddon>
|
|
248
|
+
* <Input value={email} onChangeText={setEmail} keyboardType="email-address" />
|
|
249
|
+
* </InputGroup>
|
|
250
|
+
*
|
|
251
|
+
* ⚠ **O `Input` lá dentro para de desenhar a própria caixa** — quem desenha borda, fundo, altura,
|
|
252
|
+
* foco e inválido passa a ser o grupo. Isso é automático: o campo enxerga o grupo por contexto.
|
|
253
|
+
*/
|
|
254
|
+
export function InputGroup({ size, disabled, children, style, testID, ...rest }) {
|
|
255
|
+
const t = useAureaTokens();
|
|
256
|
+
const s = folha(t);
|
|
257
|
+
const campo = useCampo();
|
|
258
|
+
const tam = size ?? campo?.size ?? "md";
|
|
259
|
+
const inativo = disabled ?? campo?.disabled ?? false;
|
|
260
|
+
const [focado, setFocado] = React.useState(false);
|
|
261
|
+
// O contexto muda de identidade a cada render se for objeto literal, e isso re-renderiza todo
|
|
262
|
+
// campo de todo formulário a cada tecla. Mesma memoização do `Field`.
|
|
263
|
+
const ctx = React.useMemo(() => ({ size: tam, disabled: inativo, aoFocar: setFocado }), [tam, inativo]);
|
|
264
|
+
return (_jsx(View, { testID: testID, style: [
|
|
265
|
+
s.grupo,
|
|
266
|
+
{ height: alturaDoTamanho(t, tam), paddingHorizontal: respiroDoTamanho(t, tam) },
|
|
267
|
+
campo?.invalido && s.invalido,
|
|
268
|
+
// O foco vem do campo lá dentro, por contexto: no React Native não existe `:focus-within`,
|
|
269
|
+
// e sem isto a única pista de qual campo o teclado alimenta sumiria ao entrar num grupo.
|
|
270
|
+
focado && { borderColor: t.color.focusStrong },
|
|
271
|
+
inativo && s.desabilitado,
|
|
272
|
+
style,
|
|
273
|
+
], ...rest, children: _jsx(GrupoDeCampo.Provider, { value: ctx, children: children }) }));
|
|
274
|
+
}
|
|
275
|
+
/**
|
|
276
|
+
* Um encaixe dentro do `InputGroup`. **A posição é a do JSX** — antes do campo fica na frente,
|
|
277
|
+
* depois fica atrás. Não há prop de lado, e o porquê está no bloco do grupo lá em cima.
|
|
278
|
+
*/
|
|
279
|
+
export function InputGroupAddon({ children, style, testID, ...rest }) {
|
|
280
|
+
const t = useAureaTokens();
|
|
281
|
+
const s = folha(t);
|
|
282
|
+
return _jsx(View, { testID: testID, style: [s.encaixe, style], ...rest, children: children });
|
|
283
|
+
}
|
|
284
|
+
export function PasswordField({ defaultVisible = false, size, disabled, autoCapitalize = "none", testID, style, ...rest }) {
|
|
285
|
+
const strings = useAureaStrings();
|
|
286
|
+
const campo = useCampo();
|
|
287
|
+
const [visivel, setVisivel] = React.useState(defaultVisible);
|
|
288
|
+
const tam = size ?? campo?.size ?? "md";
|
|
289
|
+
const inativo = disabled ?? campo?.disabled;
|
|
290
|
+
return (_jsxs(InputGroup, { size: tam, disabled: inativo, testID: testID, children: [_jsx(Input, { ...rest, size: tam, disabled: inativo, autoCapitalize: autoCapitalize, secureTextEntry: !visivel, style: style, testID: testID ? `${testID}-campo` : undefined }), _jsx(InputGroupAddon, { children: _jsx(IconButton, { name: visivel ? "view--off" : "view", label: visivel ? strings.passwordHide : strings.passwordShow, appearance: "ghost", size: tam === "lg" ? "md" : "sm", disabled: inativo, onPress: () => setVisivel(v => !v), testID: testID ? `${testID}-olho` : undefined }) })] }));
|
|
291
|
+
}
|
|
214
292
|
function ControleMarcado({ papel, label, description, checked, onChange, disabled, size, style, testID, }) {
|
|
215
293
|
const t = useAureaTokens();
|
|
216
294
|
const s = folha(t);
|
package/dist/strings.d.ts
CHANGED
|
@@ -32,6 +32,14 @@ export interface AureaStrings {
|
|
|
32
32
|
openSettings: string;
|
|
33
33
|
/** O rótulo do botão que tira uma foto anexada. */
|
|
34
34
|
photoRemove: string;
|
|
35
|
+
/**
|
|
36
|
+
* O botão que revela a senha no `PasswordField`, e o que a esconde de volta. Mesmos nomes da
|
|
37
|
+
* web (`pure.tsx`), porque é a mesma peça com a mesma decisão: **o rótulo é que anuncia o
|
|
38
|
+
* estado**, e não um `accessibilityState` de pressionado — com os dois, o leitor de tela diz
|
|
39
|
+
* a mesma coisa duas vezes.
|
|
40
|
+
*/
|
|
41
|
+
passwordShow: string;
|
|
42
|
+
passwordHide: string;
|
|
35
43
|
/** O botão de fechar do `Dialog` e do `Drawer`. Mesmo nome da web. */
|
|
36
44
|
close: string;
|
|
37
45
|
/** A saída do `ConfirmDialog`. Mesmo nome da web. */
|
package/dist/strings.js
CHANGED
|
@@ -37,6 +37,8 @@ export const defaultStrings = {
|
|
|
37
37
|
cameraDenied: "Camera access is off. Turn it on in Settings to attach a photo.",
|
|
38
38
|
openSettings: "Open Settings",
|
|
39
39
|
photoRemove: "Remove photo",
|
|
40
|
+
passwordShow: "Show password",
|
|
41
|
+
passwordHide: "Hide password",
|
|
40
42
|
close: "Close",
|
|
41
43
|
confirmCancel: "Cancel",
|
|
42
44
|
confirmProceed: "Continue",
|
|
@@ -72,6 +74,8 @@ export const ptBR = {
|
|
|
72
74
|
cameraDenied: "O acesso à câmera está desligado. Ligue nas configurações para anexar uma foto.",
|
|
73
75
|
openSettings: "Abrir configurações",
|
|
74
76
|
photoRemove: "Remover foto",
|
|
77
|
+
passwordShow: "Mostrar senha",
|
|
78
|
+
passwordHide: "Ocultar senha",
|
|
75
79
|
close: "Fechar",
|
|
76
80
|
confirmCancel: "Cancelar",
|
|
77
81
|
confirmProceed: "Continuar",
|
package/dist/text.d.ts
CHANGED
|
@@ -6,7 +6,7 @@ export type AureaTextSize = "xs" | "sm" | "md" | "base" | "lg" | "xl" | "2xl" |
|
|
|
6
6
|
export type AureaTextWeight = 400 | 500 | 600 | 700;
|
|
7
7
|
/** Papel de tipografia — os três `--font-*` da Aurea. */
|
|
8
8
|
export type AureaTextFont = "ui" | "editorial" | "code";
|
|
9
|
-
/** O que a cor SIGNIFICA. Mesmo vocabulário do `tone` do Button (ADR-
|
|
9
|
+
/** O que a cor SIGNIFICA. Mesmo vocabulário do `tone` do Button (ADR-0044). */
|
|
10
10
|
export type AureaTextTone = "default" | "muted" | "subtle" | "primary" | "danger" | "success" | "warning" | "info";
|
|
11
11
|
export type AureaTextLeading = "none" | "tight" | "normal" | "relaxed";
|
|
12
12
|
export interface TextProps extends TextPropsRN {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aurea-uds/native",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.2",
|
|
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.
|
|
50
|
+
"@aurea-uds/tokens": "^0.8.2"
|
|
51
51
|
},
|
|
52
52
|
"peerDependencies": {
|
|
53
53
|
"@react-native-community/datetimepicker": ">=8",
|