@aurea-uds/native 0.19.1 → 0.22.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -4
- package/dist/actions.d.ts +10 -10
- package/dist/actions.js +10 -10
- package/dist/arquivo.js +6 -6
- package/dist/busca.d.ts +2 -2
- package/dist/busca.js +6 -5
- package/dist/chart.js +2 -2
- package/dist/data.d.ts +1 -1
- package/dist/data.js +3 -3
- package/dist/display.js +12 -12
- package/dist/estilos.d.ts +44 -0
- package/dist/estilos.js +34 -1
- package/dist/feedback.d.ts +4 -4
- package/dist/feedback.js +17 -12
- package/dist/icon.d.ts +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/inputs.d.ts +42 -17
- package/dist/inputs.js +53 -26
- package/dist/layout.d.ts +3 -3
- package/dist/layout.js +6 -11
- package/dist/midia.js +3 -3
- package/dist/moldura.js +2 -2
- package/dist/navigation.d.ts +14 -4
- package/dist/navigation.js +36 -37
- package/dist/numero.d.ts +4 -3
- package/dist/numero.js +14 -13
- package/dist/overlays.js +7 -4
- package/dist/rolagem.d.ts +8 -1
- package/dist/rolagem.js +3 -2
- package/dist/screen.js +1 -1
- package/dist/sistema.d.ts +15 -2
- package/dist/sistema.js +55 -12
- package/dist/text.d.ts +5 -5
- package/dist/text.js +8 -8
- package/dist/toast.js +4 -2
- package/package.json +2 -2
package/dist/navigation.js
CHANGED
|
@@ -3,7 +3,7 @@ import { Pressable, View } from "react-native";
|
|
|
3
3
|
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
|
4
4
|
import { useAnotarBottomNav } from "./barranav.js";
|
|
5
5
|
import { Avatar, Badge } from "./display.js";
|
|
6
|
-
import { comOpacidade, criarFolha, estadoAcessivel, fioDoEscolhido } from "./estilos.js";
|
|
6
|
+
import { canto, comOpacidade, criarFolha, estadoAcessivel, fioDoEscolhido } from "./estilos.js";
|
|
7
7
|
import { Icon } from "./icon.js";
|
|
8
8
|
import { Card } from "./layout.js";
|
|
9
9
|
import { FilaRolante } from "./rolagem.js";
|
|
@@ -22,7 +22,7 @@ const folha = criarFolha((t) => ({
|
|
|
22
22
|
marginHorizontal: t.size.space4, marginTop: t.size.space4,
|
|
23
23
|
paddingVertical: t.size.space2, paddingHorizontal: t.size.space4,
|
|
24
24
|
borderWidth: t.size.borderWidth, borderColor: t.color.border,
|
|
25
|
-
|
|
25
|
+
...canto(t.size.radiusCard), backgroundColor: t.color.card,
|
|
26
26
|
},
|
|
27
27
|
topo_flush: { paddingVertical: t.size.space2, paddingHorizontal: t.size.space5,
|
|
28
28
|
backgroundColor: t.color.background },
|
|
@@ -35,7 +35,7 @@ const folha = criarFolha((t) => ({
|
|
|
35
35
|
alignSelf: "center", marginTop: t.size.space4, justifyContent: "flex-start",
|
|
36
36
|
gap: t.size.space2, padding: t.size.space2,
|
|
37
37
|
borderWidth: t.size.borderWidth, borderColor: t.color.border,
|
|
38
|
-
|
|
38
|
+
...canto(t.size.radiusControl), backgroundColor: t.color.card,
|
|
39
39
|
},
|
|
40
40
|
// ── BottomNav ──────────────────────────────────────────────────────────────────────────────
|
|
41
41
|
// 🔴 `floating` FLUTUA POR CIMA DA TELA, e isto é conserto de 19/09/2026 — a variante prometia
|
|
@@ -53,14 +53,14 @@ const folha = criarFolha((t) => ({
|
|
|
53
53
|
position: "absolute", bottom: 0, zIndex: t.size.zSticky,
|
|
54
54
|
flexDirection: "row", gap: t.size.space1, margin: t.size.space4, padding: t.size.space1,
|
|
55
55
|
borderWidth: t.size.borderWidth, borderColor: t.color.border,
|
|
56
|
-
|
|
56
|
+
...canto(t.size.radiusControl), backgroundColor: t.color.card,
|
|
57
57
|
boxShadow: [t.shadow.shadowMd],
|
|
58
58
|
},
|
|
59
59
|
// `edge` encosta na borda: sem margem, sem raio, só a linha de cima — e continua NO FLUXO,
|
|
60
60
|
// porque uma barra encostada que flutuasse esconderia o fim da tela sem nada reservar.
|
|
61
61
|
barra_edge: {
|
|
62
62
|
position: "relative", left: undefined, right: undefined, bottom: undefined, zIndex: undefined,
|
|
63
|
-
margin: 0, paddingHorizontal: 0,
|
|
63
|
+
margin: 0, paddingHorizontal: 0, ...canto(0), backgroundColor: t.color.background,
|
|
64
64
|
borderWidth: 0, borderTopWidth: t.size.borderWidth, boxShadow: undefined,
|
|
65
65
|
},
|
|
66
66
|
// A pílula CHEIA, de uma borda à outra menos a margem — o de sempre. Mora fora da `barra` para
|
|
@@ -81,18 +81,18 @@ const folha = criarFolha((t) => ({
|
|
|
81
81
|
// `space05` e não `space1` — a barra mais baixa (30/09/2026, pedido do Victor, aprovado pela
|
|
82
82
|
// imagem). Com a moldura de 32 e o vão de 4 eram 9 entre o ícone e o rótulo; ver `marca`.
|
|
83
83
|
// O NOME COLADO NO ÍCONE — 03/10/2026, pedido do Victor (*"o texto pode ficar mais próximo do
|
|
84
|
-
// ícone"*): vão ZERO, como no
|
|
84
|
+
// ícone"*): vão ZERO, como no app de referência, em que o nome começa onde a caixa do ícone acaba. O
|
|
85
85
|
// `space05` daqui (30/09/2026) somava 2 aos 2 de folga da própria linha do nome; medido na
|
|
86
86
|
// bancada, eram 9 de tinta a tinta no `pill` e 13 no `circle-bold`.
|
|
87
87
|
gap: 0, minHeight: t.size.controlHLg,
|
|
88
|
-
// 🔴 A ALTURA
|
|
89
|
-
// comparado a bottomnav como do
|
|
90
|
-
//
|
|
88
|
+
// 🔴 A ALTURA DE REFERÊNCIA — 03/10/2026, pedido do Victor: *"ainda acho ele muito largo
|
|
89
|
+
// comparado a bottomnav como [a do app de referência]"*, e "largo" é a grossura. Medido no
|
|
90
|
+
// fonte do app de referência para Android: a
|
|
91
91
|
// pílula tem 56; o botão, 48; o ícone, 24; o nome, 12. A nossa media 73 no `circle-bold`. O
|
|
92
92
|
// recheio de cima e de baixo cai de `space1` para `space05`, e o nome sai em 12 com a
|
|
93
93
|
// entrelinha justa (ver o `Text` do rótulo). Igual na web (`aurea.css`).
|
|
94
94
|
paddingHorizontal: t.size.space1, paddingVertical: t.size.space05,
|
|
95
|
-
|
|
95
|
+
...canto(t.size.radiusControl),
|
|
96
96
|
},
|
|
97
97
|
// Com a pílula do tamanho do conteúdo, `flex: 1` MATA as abas: no RN ele é base ZERO, e a soma
|
|
98
98
|
// de zeros é uma pílula de largura zero. Aqui cada aba mede o próprio conteúdo (base `auto`) e
|
|
@@ -105,11 +105,11 @@ const folha = criarFolha((t) => ({
|
|
|
105
105
|
},
|
|
106
106
|
// A CAIXA QUE ANCORA O CONTADOR, e ela existe por um defeito visto em tela: pendurado no ITEM,
|
|
107
107
|
// o número cai no meio do RÓTULO. O `navigation-client.tsx:336` conta a história inteira — o
|
|
108
|
-
// Victor viu o contador cobrir o nome em 17/08/2026, e a pesquisa (
|
|
109
|
-
// barra de abas do iOS) diz o mesmo: canto superior do ÍCONE, nunca sobre o texto.
|
|
108
|
+
// Victor viu o contador cobrir o nome em 17/08/2026, e a pesquisa (uma referência de mercado e os
|
|
109
|
+
// guias de barra de abas do iOS) diz o mesmo: canto superior do ÍCONE, nunca sobre o texto.
|
|
110
110
|
marca: { position: "relative", alignItems: "center", justifyContent: "center",
|
|
111
|
-
width: 32, height: 32,
|
|
112
|
-
marcaRedonda: {
|
|
111
|
+
width: 32, height: 32, ...canto(t.size.radiusControl) },
|
|
112
|
+
marcaRedonda: { ...canto(t.size.radiusFull) },
|
|
113
113
|
contador: { position: "absolute", top: -t.size.space05, right: -t.size.space05 },
|
|
114
114
|
// ── A BARRA FICA PARADA QUANDO SE TROCA DE ABA — pedido do Victor, 03/10/2026 ──────────────
|
|
115
115
|
// *"me incomoda o bottom nav todo se mexer [...] quero ele estático, apenas os botões
|
|
@@ -128,13 +128,13 @@ const folha = criarFolha((t) => ({
|
|
|
128
128
|
// (56 × 48).
|
|
129
129
|
disco: {
|
|
130
130
|
alignItems: "center", justifyContent: "center", gap: 0,
|
|
131
|
-
width: 56, minHeight: t.size.controlHLg,
|
|
131
|
+
width: 56, minHeight: t.size.controlHLg, ...canto(t.size.radiusFull),
|
|
132
132
|
paddingHorizontal: t.size.space1, paddingVertical: t.size.space1,
|
|
133
133
|
},
|
|
134
|
-
// `capsule` (a "A" da bancada,
|
|
135
|
-
// 56 × 32 do indicador ativo
|
|
134
|
+
// `capsule` (a "A" da bancada, de uma referência de mercado): a moldura do ícone vira a cápsula
|
|
135
|
+
// de 56 × 32 do indicador ativo dela — os mesmos `--bottomnav-bold` e `--bottomnav-ring` do
|
|
136
136
|
// `aurea.css`, em todos os itens. O rótulo fica embaixo, fora dela.
|
|
137
|
-
marcaCapsula: { width: 56, height: 32,
|
|
137
|
+
marcaCapsula: { width: 56, height: 32, ...canto(t.size.radiusFull) },
|
|
138
138
|
// O contador fica no canto do ÍCONE, e não da cápsula: sem a cápsula pintada (item não
|
|
139
139
|
// escolhido) ele ficaria solto, longe do sino. São os (56 − 32) / 2 a mais para dentro.
|
|
140
140
|
contadorCapsula: { right: (56 - 32) / 2 - t.size.space05 },
|
|
@@ -168,7 +168,7 @@ const folha = criarFolha((t) => ({
|
|
|
168
168
|
flexDirection: "row", alignItems: "center", gap: t.size.space3, width: "100%",
|
|
169
169
|
minHeight: t.size.controlHLg,
|
|
170
170
|
paddingVertical: t.size.space2, paddingHorizontal: t.size.space3,
|
|
171
|
-
|
|
171
|
+
...canto(t.size.radiusCard - t.size.space1),
|
|
172
172
|
},
|
|
173
173
|
// O MIOLO da linha: ícone + texto (+ valor em texto) + seta. Ele é quem toca; o que vem
|
|
174
174
|
// depois dele, na moldura, é IRMÃO — ver o comentário no componente.
|
|
@@ -183,7 +183,7 @@ const folha = criarFolha((t) => ({
|
|
|
183
183
|
// lá — `gap:3px; padding:3px` — e não de mim.
|
|
184
184
|
abas: {
|
|
185
185
|
flexDirection: "row", gap: 3, padding: 3,
|
|
186
|
-
minHeight: t.size.controlHMd,
|
|
186
|
+
minHeight: t.size.controlHMd, ...canto(t.size.radiusControl),
|
|
187
187
|
backgroundColor: t.color.muted,
|
|
188
188
|
},
|
|
189
189
|
// `padding:0 14px` é literal no `.tab` (`aurea.css:1127`). A aba NÃO estica (nada de `flex`):
|
|
@@ -194,18 +194,18 @@ const folha = criarFolha((t) => ({
|
|
|
194
194
|
// silêncio, e o `tsc` só pegou porque são chaves do mesmo objeto.
|
|
195
195
|
abaDeTab: {
|
|
196
196
|
alignItems: "center", justifyContent: "center",
|
|
197
|
-
paddingHorizontal: 14,
|
|
197
|
+
paddingHorizontal: 14, ...canto(t.size.radiusControl),
|
|
198
198
|
},
|
|
199
199
|
abaDeTabAtiva: { backgroundColor: t.color.secondary },
|
|
200
|
-
// `variant="secondary"` (R-12, 01/10/2026):
|
|
201
|
-
//
|
|
202
|
-
//
|
|
203
|
-
//
|
|
204
|
-
//
|
|
200
|
+
// `variant="secondary"` (R-12, 01/10/2026): a variante secundária da referência,
|
|
201
|
+
// com o fio da casa no lugar do indicador azul dela.
|
|
202
|
+
// fila (secundária) padding 0 · border-bottom 1px, cor da borda
|
|
203
|
+
// fila gap de 1 unidade → space1
|
|
204
|
+
// aba padding-inline de 3 unidades → space3 · padding-block de 1,5
|
|
205
205
|
// → space1 + space05. Rótulo `text-base` + entrelinha 1.5 dá
|
|
206
206
|
// 36, que é o `controlHMd`: a altura da fila da cápsula, então
|
|
207
207
|
// trocar de variante não muda a altura da fila.
|
|
208
|
-
//
|
|
208
|
+
// indicador (secundária) border-bottom 2px na cor de destaque → `fioDoEscolhido`
|
|
209
209
|
abasSecundarias: {
|
|
210
210
|
flexDirection: "row", gap: t.size.space1,
|
|
211
211
|
borderBottomWidth: t.size.borderWidth, borderColor: t.color.border,
|
|
@@ -221,7 +221,7 @@ const folha = criarFolha((t) => ({
|
|
|
221
221
|
passo: { flex: 1, alignItems: "center" },
|
|
222
222
|
bolinha: {
|
|
223
223
|
width: 32, height: 32, marginBottom: t.size.space2,
|
|
224
|
-
alignItems: "center", justifyContent: "center",
|
|
224
|
+
alignItems: "center", justifyContent: "center", ...canto(t.size.radiusFull),
|
|
225
225
|
borderWidth: t.size.borderWidth, borderColor: t.color.borderStrong,
|
|
226
226
|
backgroundColor: t.color.surface1,
|
|
227
227
|
},
|
|
@@ -383,13 +383,14 @@ export function BottomNav({ items, current, variant = "floating", indicator = "n
|
|
|
383
383
|
: _jsx(Badge, { tone: "danger", emphasis: "solid", size: "xs", dot: true }) }))] }));
|
|
384
384
|
// 🔴 O PESO DO RÓTULO NÃO MUDA COM A ESCOLHA (03/10/2026). Era 500 no escolhido e 400 nos
|
|
385
385
|
// outros, e texto mais grosso é texto mais LARGO: no `content` a barra mudava de largura a
|
|
386
|
-
// cada toque, em todos os indicadores. Agora é 500 em todos — o
|
|
387
|
-
//
|
|
386
|
+
// cada toque, em todos os indicadores. Agora é 500 em todos — o rótulo médio de uma
|
|
387
|
+
// referência de mercado, que também deixou de engrossar o escolhido numa revisão posterior
|
|
388
|
+
// pela mesma razão. O que marca
|
|
388
389
|
// a escolha é a cor, o ícone cheio e a forma. No `expand` o nome é `sm`: ao lado do ícone,
|
|
389
390
|
// e não embaixo dele.
|
|
390
391
|
const rotulo = typeof it.label === "string"
|
|
391
392
|
? (nomeVisivel
|
|
392
|
-
// 12 numa linha de 16, o do
|
|
393
|
+
// 12 numa linha de 16, o do app de referência — e o `text-xs` da web. Antes era o `xs` do `Text`
|
|
393
394
|
// nativo, que sobe para 14 (ADR-0050) com linha de 21: era o que mais engrossava a
|
|
394
395
|
// barra. A linha é o tamanho mais `space1`, como na web: com a entrelinha justa (14,4)
|
|
395
396
|
// a perna do "j" passava da caixa. No `expand` o nome vai ao lado do ícone, em 14.
|
|
@@ -401,7 +402,7 @@ export function BottomNav({ items, current, variant = "floating", indicator = "n
|
|
|
401
402
|
// `link` e não `tab`: uma aba troca um painel DESTA tela; isto troca de tela.
|
|
402
403
|
accessibilityRole: "link", accessibilityLabel: !nomeVisivel && typeof it.label === "string" ? it.label : undefined, ...estadoAcessivel({ selected: ativo, disabled: !!it.disabled }), style: [
|
|
403
404
|
s.aba, justa && s.aba_content,
|
|
404
|
-
indicator === "subtle" && {
|
|
405
|
+
indicator === "subtle" && { ...canto(t.size.radiusCard - t.size.space1) },
|
|
405
406
|
ativo && (indicator === "subtle" || indicator === "pill")
|
|
406
407
|
&& { backgroundColor: comOpacidade(t.color.primary, 0.12) },
|
|
407
408
|
disco && s.aba_disco,
|
|
@@ -534,7 +535,7 @@ export function Stepper({ items, label, doneIcon = "check", errorIcon = "x-circl
|
|
|
534
535
|
*
|
|
535
536
|
* ⚠ **A fila ROLA na horizontal, e a web não rola — divergência deliberada, com fonte.** A
|
|
536
537
|
* referência máxima de desenho deste projeto tem estados de rolagem na lista de abas
|
|
537
|
-
* (
|
|
538
|
+
* (rolagem à esquerda, à direita e dos dois lados, no inventário), e num telefone de 360dp
|
|
538
539
|
* quatro rótulos já não cabem. Espremer todas seria a outra saída, e ela apaga o rótulo — que é
|
|
539
540
|
* o defeito que o app mediu no `SegmentedControl`. **A cápsula, o respiro e o raio continuam
|
|
540
541
|
* sendo os do CSS**; o que muda é o transbordo.
|
|
@@ -555,7 +556,7 @@ export function Stepper({ items, label, doneIcon = "check", errorIcon = "x-circl
|
|
|
555
556
|
* largura de tela grande. Não há tablet medido neste projeto, e no nativo vale demanda antes de
|
|
556
557
|
* cobertura — a mesma decisão da `Table` do Lote 6.
|
|
557
558
|
*/
|
|
558
|
-
export function Tabs({ tabs, value, onChange, label, justify, variant = "primary", style, ...rest }) {
|
|
559
|
+
export function Tabs({ tabs, value, onChange, label, justify, variant = "primary", panel = "card", style, ...rest }) {
|
|
559
560
|
const t = useAureaTokens();
|
|
560
561
|
const s = folha(t);
|
|
561
562
|
const strings = useAureaStrings();
|
|
@@ -570,7 +571,5 @@ export function Tabs({ tabs, value, onChange, label, justify, variant = "primary
|
|
|
570
571
|
], children: [typeof it.label === "string"
|
|
571
572
|
? _jsx(Text, { size: "sm", weight: ativa ? 600 : 400, style: { color: ativa ? t.color.foreground : t.color.mutedForeground }, children: it.label })
|
|
572
573
|
: it.label, ativa && secundaria && _jsx(View, { style: s.fioDaAba })] }, it.id));
|
|
573
|
-
}) }) }), aberta
|
|
574
|
-
? (_jsx(Card, { variant: "inset", accessibilityLabel: typeof aberta.label === "string" ? aberta.label : undefined, style: s.painel, children: aberta.content }))
|
|
575
|
-
: null] }));
|
|
574
|
+
}) }) }), aberta && panel === "plain" ? (_jsx(View, { accessibilityLabel: typeof aberta.label === "string" ? aberta.label : undefined, style: s.painel, children: aberta.content })) : aberta ? (_jsx(Card, { variant: "inset", accessibilityLabel: typeof aberta.label === "string" ? aberta.label : undefined, style: s.painel, children: aberta.content })) : null] }));
|
|
576
575
|
}
|
package/dist/numero.d.ts
CHANGED
|
@@ -4,8 +4,9 @@ import type { AureaIcon } from "./icon.js";
|
|
|
4
4
|
import { type AureaFieldSize } from "./inputs.js";
|
|
5
5
|
/**
|
|
6
6
|
* Os tamanhos do `NumberField`: os três do campo e mais o `display` (R-16, 01/10/2026), o número
|
|
7
|
-
* grande de destaque. Nenhuma referência da fila tem esse tamanho (o `NumberField`
|
|
8
|
-
* só tem `variant`;
|
|
7
|
+
* grande de destaque. Nenhuma referência da fila tem esse tamanho (o `NumberField` da principal,
|
|
8
|
+
* na web, só tem `variant`; no telefone ela nem tem a peça; a segunda também não), então ele é
|
|
9
|
+
* peça nossa —
|
|
9
10
|
* e as medidas saem de escalas que JÁ existem, nenhuma nova:
|
|
10
11
|
* letra `text3xl` (30) em seminegrito — o título 2 (`h2` do B-02). Escolha do Victor.
|
|
11
12
|
* altura `controlHXl` — o degrau seguinte da escala de altura dos controles, que muda com a
|
|
@@ -73,7 +74,7 @@ export interface NumberFieldProps {
|
|
|
73
74
|
* Ocupa a largura disponível em vez de abraçar o conteúdo. Padrão **false**.
|
|
74
75
|
*
|
|
75
76
|
* O padrão segue o `.number-field` da web, que é `inline-flex` (`aurea.css:704`) — e o mesmo
|
|
76
|
-
* eixo existe na
|
|
77
|
+
* eixo existe na referência (medido no inventário). **Ligue para moeda:** o campo em
|
|
77
78
|
* repouso tem `--space-16` (64dp), que cabe um contador e não cabe `R$ 1.234,50`.
|
|
78
79
|
*/
|
|
79
80
|
fullWidth?: boolean;
|
package/dist/numero.js
CHANGED
|
@@ -8,8 +8,9 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
8
8
|
//
|
|
9
9
|
// ── A ADR-0024 ATRAVESSA, E FOI CONFERIDA ANTES DE SE AFIRMAR ISSO ───────────────────────────
|
|
10
10
|
// A decisão da web é *"a Aurea entrega o MOMENTO, não o formato"*: formatar no **blur**, nunca
|
|
11
|
-
// enquanto se digita. As três medições que a sustentam (
|
|
12
|
-
// reprovação WCAG registrada, o abandono da máscara
|
|
11
|
+
// enquanto se digita. As três medições que a sustentam (a máscara de entrada de um design system público, publicada com
|
|
12
|
+
// reprovação WCAG registrada, o abandono da máscara por uma das bibliotecas de referência, a
|
|
13
|
+
// prática de acessibilidade)
|
|
13
14
|
// são sobre COMPORTAMENTO HUMANO, não sobre plataforma — então elas valem igual aqui, e não há
|
|
14
15
|
// evidência nova que reabra a decisão (`decisions/README.md`).
|
|
15
16
|
//
|
|
@@ -47,7 +48,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
47
48
|
import * as React from "react";
|
|
48
49
|
import { Platform, TextInput, View } from "react-native";
|
|
49
50
|
import { IconButton } from "./actions.js";
|
|
50
|
-
import { criarFolha, estadoAcessivel } from "./estilos.js";
|
|
51
|
+
import { canto, criarFolha, estadoAcessivel } from "./estilos.js";
|
|
51
52
|
import { useCampo } from "./inputs.js";
|
|
52
53
|
import { useAureaStrings, useAureaTokens, usePeleSobreAMarca } from "./theme.js";
|
|
53
54
|
const alturaDoTamanho = (t, s) => s === "sm" ? t.size.controlHSm : s === "lg" ? t.size.controlHLg
|
|
@@ -63,8 +64,8 @@ const folha = criarFolha((t) => ({
|
|
|
63
64
|
// divergência do nosso próprio CSS, entregue sem declarar, e ela só apareceu quando houve
|
|
64
65
|
// imagem para olhar.
|
|
65
66
|
//
|
|
66
|
-
// ⚠ **A saída é o eixo da
|
|
67
|
-
//
|
|
67
|
+
// ⚠ **A saída é o eixo da referência, e não uma invenção minha:** o campo numérico dela publica
|
|
68
|
+
// um eixo de largura cheia, ligado e desligado (no inventário da referência, medido em 22/08/2026).
|
|
68
69
|
// Esticar ou abraçar é DECISÃO DE USO, então vira prop — com o padrão em abraçar, que é o que
|
|
69
70
|
// o nosso CSS já dizia.
|
|
70
71
|
grupo: { flexDirection: "row", alignItems: "center", gap: t.size.space1,
|
|
@@ -76,7 +77,7 @@ const folha = criarFolha((t) => ({
|
|
|
76
77
|
campo: {
|
|
77
78
|
width: t.size.space16, textAlign: "center",
|
|
78
79
|
borderWidth: t.size.borderWidth, borderColor: t.color.borderStrong,
|
|
79
|
-
|
|
80
|
+
...canto(t.size.radiusControl), backgroundColor: t.color.fieldBg,
|
|
80
81
|
// A mesma correção de Android do `Input` do Lote 4 — ver `inputs.tsx:280-296`.
|
|
81
82
|
paddingVertical: 0, textAlignVertical: "center",
|
|
82
83
|
},
|
|
@@ -202,7 +203,7 @@ const prender = (n, min, max) => {
|
|
|
202
203
|
};
|
|
203
204
|
// O texto que o campo mostra ENQUANTO SE EDITA: o número sem grupo e sem símbolo, com o separador
|
|
204
205
|
// decimal do locale. É o oposto do que se mostra em repouso, e é a decisão da ADR-0024 — editar
|
|
205
|
-
// "R$ 1.234,50" com o cursor no meio é o defeito que
|
|
206
|
+
// "R$ 1.234,50" com o cursor no meio é o defeito que uma das bibliotecas de referência filmou.
|
|
206
207
|
const paraEdicao = (n, locale) => {
|
|
207
208
|
if (n == null)
|
|
208
209
|
return "";
|
|
@@ -342,9 +343,9 @@ export function NumberField({ value, defaultValue, onValueChange, min, max, step
|
|
|
342
343
|
return direcao === 1 ? (max != null && base >= max) : (min != null && base <= min);
|
|
343
344
|
};
|
|
344
345
|
const mostrarBotoes = icons !== false && !readOnly;
|
|
345
|
-
// 🔴 ACHADO PELA REFERÊNCIA, e não por mim — 12/09/2026. O inventário da
|
|
346
|
-
// no repositório (
|
|
347
|
-
//
|
|
346
|
+
// 🔴 ACHADO PELA REFERÊNCIA, e não por mim — 12/09/2026. O inventário da referência que já
|
|
347
|
+
// morava no repositório (medido em 22/08 sobre o pacote dela) lista os estados
|
|
348
|
+
// do campo numérico dela:
|
|
348
349
|
//
|
|
349
350
|
// disabled · focus-visible · FOCUS-WITHIN · hovered · invalid · pressed
|
|
350
351
|
//
|
|
@@ -353,10 +354,10 @@ export function NumberField({ value, defaultValue, onValueChange, min, max, step
|
|
|
353
354
|
// marca (`busca.tsx:520`). Inconsistência dentro da própria biblioteca, e invisível em teste
|
|
354
355
|
// até alguém comparar com uma referência.
|
|
355
356
|
//
|
|
356
|
-
// ⚠ **A BORDA vai no CAMPO, e não no grupo — e aqui a Aurea diverge da
|
|
357
|
-
// Lá o
|
|
357
|
+
// ⚠ **A BORDA vai no CAMPO, e não no grupo — e aqui a Aurea diverge da referência de propósito.**
|
|
358
|
+
// Lá o grupo carrega a caixa e o campo fica nu dentro dela. Aqui não: o `.number-field-group`
|
|
358
359
|
// do nosso CSS (`aurea.css:705`) é só `inline-flex` + `gap`, e quem tem borda é o `.input`
|
|
359
|
-
// (`aurea.css:685`), com os dois botões FORA dela. O estado que faltava é o
|
|
360
|
+
// (`aurea.css:685`), com os dois botões FORA dela. O estado que faltava é o dela; a geometria
|
|
360
361
|
// continua sendo a nossa.
|
|
361
362
|
const [focado, setFocado] = React.useState(false);
|
|
362
363
|
return (_jsxs(View, { testID: testID, style: [s.grupo, fullWidth && s.grupoLargo, inativo && s.desabilitado, style], children: [mostrarBotoes && (_jsx(IconButton, { name: icons.decrement, label: strings.decrement, appearance: "ghost", size: tamDoBotao, disabled: inativo || noLimite(-1), onPress: () => empurrar(-1), testID: testID ? `${testID}-menos` : undefined })), _jsx(TextInput, { testID: testID ? `${testID}-campo` : undefined, value: mostrar, onChangeText: digitar, onFocus: () => { setFocado(true); setEmEdicao(paraEdicao(numero, locale)); }, onBlur: () => { setFocado(false); confirmar(); }, editable: !inativo && !readOnly, placeholder: placeholder, placeholderTextColor: peleDaMarca?.color ?? t.color.subtleForeground, keyboardType: keyboardType ?? tecladoPadrao,
|
package/dist/overlays.js
CHANGED
|
@@ -58,7 +58,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
58
58
|
import * as React from "react";
|
|
59
59
|
import { Animated, Easing, Modal, PanResponder, Pressable, ScrollView, View, } from "react-native";
|
|
60
60
|
import { Button } from "./actions.js";
|
|
61
|
-
import { criarFolha } from "./estilos.js";
|
|
61
|
+
import { canto, cantosDeCima, criarFolha } from "./estilos.js";
|
|
62
62
|
import { IconButton } from "./actions.js";
|
|
63
63
|
import { RecuoDaFolha } from "./screen.js";
|
|
64
64
|
import { Text } from "./text.js";
|
|
@@ -74,7 +74,7 @@ const folha = criarFolha((t) => ({
|
|
|
74
74
|
superficie: {
|
|
75
75
|
width: "100%", maxWidth: 560, padding: 24,
|
|
76
76
|
borderWidth: t.size.borderWidth, borderColor: t.color.border,
|
|
77
|
-
|
|
77
|
+
...canto(t.size.radiusCard),
|
|
78
78
|
backgroundColor: t.color.popover,
|
|
79
79
|
...(t.shadow.shadowLg ? { boxShadow: [t.shadow.shadowLg] } : null),
|
|
80
80
|
},
|
|
@@ -101,12 +101,15 @@ const folha = criarFolha((t) => ({
|
|
|
101
101
|
maxHeight: "90%",
|
|
102
102
|
paddingHorizontal: 24, paddingTop: t.size.space2, paddingBottom: 24,
|
|
103
103
|
backgroundColor: t.color.popover,
|
|
104
|
-
|
|
104
|
+
// HER-03 (06/10/2026, ADR-0058): 32 e não o 22 de painel — a folha acompanha o canto da tela
|
|
105
|
+
// do telefone. Decisão do Victor, olhando as duas na bancada. A lista do `Select` e a folha do
|
|
106
|
+
// `Combobox` sobem do mesmo jeito e levam o mesmo token.
|
|
107
|
+
...cantosDeCima(t.size.radiusSheet),
|
|
105
108
|
borderTopWidth: t.size.borderWidth, borderTopColor: t.color.border,
|
|
106
109
|
...(t.shadow.shadowLg ? { boxShadow: [t.shadow.shadowLg] } : null),
|
|
107
110
|
},
|
|
108
111
|
puxadorArea: { alignItems: "center", paddingVertical: t.size.space2 },
|
|
109
|
-
puxador: { width: 40, height: 4,
|
|
112
|
+
puxador: { width: 40, height: 4, ...canto(999), backgroundColor: t.color.borderStrong },
|
|
110
113
|
}));
|
|
111
114
|
/** Duração da entrada de painel, em ms. A mesma `.28s` do `.toast` do CSS (aurea.css:1586). */
|
|
112
115
|
const DURACAO = 280;
|
package/dist/rolagem.d.ts
CHANGED
|
@@ -11,6 +11,13 @@ export interface FilaRolanteProps {
|
|
|
11
11
|
* `end`. Quando não cabe, ela rola, e o alinhamento deixa de existir: começa no início.
|
|
12
12
|
*/
|
|
13
13
|
justify?: AureaFilaJustify;
|
|
14
|
+
/**
|
|
15
|
+
* A fila OCUPA a linha quando cabe (A5, 06/10/2026 — o `fullWidth` do `SegmentedControl`). O
|
|
16
|
+
* recipiente do conteúdo cresce até a largura do rolador; quem cresce dentro dele é o filho, que
|
|
17
|
+
* também precisa de `flexGrow`. Quando não cabe, rola, como sempre. Vence o `justify`: cheia,
|
|
18
|
+
* ela não tem onde ficar.
|
|
19
|
+
*/
|
|
20
|
+
fill?: boolean;
|
|
14
21
|
}
|
|
15
22
|
/** Onde a fila fica, na linha, quando cabe. O nome é o do `Cluster` (R-09): o eixo da fileira. */
|
|
16
23
|
export type AureaFilaJustify = "start" | "center" | "end";
|
|
@@ -20,4 +27,4 @@ export type AureaFilaJustify = "start" | "center" | "end";
|
|
|
20
27
|
* O componente fica com a informação em vez de adivinhar: `onLayout` dá a largura visível e
|
|
21
28
|
* `onContentSizeChange` dá a do conteúdo. Com as duas, `transbordou` é uma conta, não um palpite.
|
|
22
29
|
*/
|
|
23
|
-
export declare function FilaRolante({ children, style, testID, justify }: FilaRolanteProps): React.JSX.Element;
|
|
30
|
+
export declare function FilaRolante({ children, style, testID, justify, fill }: FilaRolanteProps): React.JSX.Element;
|
package/dist/rolagem.js
CHANGED
|
@@ -32,7 +32,7 @@ const JUSTIFICAR = { center: "center", end: "flex-end" };
|
|
|
32
32
|
* O componente fica com a informação em vez de adivinhar: `onLayout` dá a largura visível e
|
|
33
33
|
* `onContentSizeChange` dá a do conteúdo. Com as duas, `transbordou` é uma conta, não um palpite.
|
|
34
34
|
*/
|
|
35
|
-
export function FilaRolante({ children, style, testID, justify = "start" }) {
|
|
35
|
+
export function FilaRolante({ children, style, testID, justify = "start", fill }) {
|
|
36
36
|
const [visivel, setVisivel] = React.useState(0);
|
|
37
37
|
const [conteudo, setConteudo] = React.useState(0);
|
|
38
38
|
const transbordou = conteudo > visivel && visivel > 0;
|
|
@@ -46,5 +46,6 @@ export function FilaRolante({ children, style, testID, justify = "start" }) {
|
|
|
46
46
|
// largura do rolador (`flexGrow: 1`) e POSICIONA a cápsula lá dentro. Ela não estica: na
|
|
47
47
|
// fileira, o filho tem a largura dele. Medido pelo app: rolador 335 de largura, fileira 281,
|
|
48
48
|
// encostada à esquerda, e um `Cluster justify="center"` em volta não mudava nada.
|
|
49
|
-
contentContainerStyle:
|
|
49
|
+
contentContainerStyle: fill ? { flexGrow: 1 }
|
|
50
|
+
: justify === "start" ? { flexGrow: 0 } : { flexGrow: 1, justifyContent: JUSTIFICAR[justify] }, style: style, children: children }));
|
|
50
51
|
}
|
package/dist/screen.js
CHANGED
|
@@ -146,7 +146,7 @@ function Rodape({ dentroDoRespiro, pegaABorda, respiroDaBarra, children }) {
|
|
|
146
146
|
// ganhar altura. Nunca ganhava: o recuo ficava em zero e o último item, atrás dos botões do
|
|
147
147
|
// sistema (achado E10 do app).
|
|
148
148
|
//
|
|
149
|
-
// A saída é a
|
|
149
|
+
// A saída é a da referência (`select.tsx`, `useSafeAreaInsets`): o número vem do CONTEXTO
|
|
150
150
|
// do React, que atravessa o `Modal`, e vira um espaço de altura conhecida no fim da folha. Sem
|
|
151
151
|
// `SafeAreaProvider` no app, vale a medida da abertura (`initialWindowMetrics`), que a
|
|
152
152
|
// biblioteca calcula sem provider — e o componente não quebra quem não tem provider.
|
package/dist/sistema.d.ts
CHANGED
|
@@ -40,6 +40,11 @@ export type AureaPhoto = {
|
|
|
40
40
|
uri: string;
|
|
41
41
|
width?: number;
|
|
42
42
|
height?: number;
|
|
43
|
+
/**
|
|
44
|
+
* Quando a foto foi TIRADA, lida do EXIF (R-23). Só vem com a prop `exif`, e fica vazia quando
|
|
45
|
+
* a foto não traz a data — a que passou por um aplicativo de mensagens, por exemplo, perde o EXIF no caminho.
|
|
46
|
+
*/
|
|
47
|
+
takenAt?: Date;
|
|
43
48
|
};
|
|
44
49
|
export interface PhotoInputProps {
|
|
45
50
|
value?: AureaPhoto[];
|
|
@@ -56,6 +61,12 @@ export interface PhotoInputProps {
|
|
|
56
61
|
offerSettings?: boolean;
|
|
57
62
|
/** Avisado quando a permissão foi negada — o app pode querer contar uma história própria. */
|
|
58
63
|
onPermissionDenied?: () => void;
|
|
64
|
+
/**
|
|
65
|
+
* Lê do EXIF a data em que a foto foi tirada e a entrega em `takenAt` (R-23). Desligado por
|
|
66
|
+
* padrão: ler o EXIF custa (no iPhone, a foto que está no iCloud desce inteira) e o app só
|
|
67
|
+
* paga quando precisa. Da galeria, é o que põe na ordem certa a foto que sobe dias depois.
|
|
68
|
+
*/
|
|
69
|
+
exif?: boolean;
|
|
59
70
|
addIcon?: AureaIcon | false;
|
|
60
71
|
removeIcon?: AureaIcon;
|
|
61
72
|
style?: StyleProp<ViewStyle>;
|
|
@@ -76,6 +87,8 @@ export interface PhotoInputProps {
|
|
|
76
87
|
* | **câmera ou galeria?** | câmera, que é o que o plano pede | `source="library"` |
|
|
77
88
|
* | como **remover**? | um `IconButton` no canto de cada miniatura | `removeIcon` |
|
|
78
89
|
* | **quantas** cabem? | uma | `max` |
|
|
90
|
+
* | **várias de uma vez?** | da galeria, sim, quando cabe mais de uma (R-23) | `max` |
|
|
91
|
+
* | **a data** em que foi tirada? | não lê: ler o EXIF custa | `exif` → `takenAt` (R-23) |
|
|
79
92
|
*
|
|
80
93
|
* ⚠ **O gatilho some quando o limite é atingido**, em vez de ficar aceso e não fazer nada.
|
|
81
94
|
*
|
|
@@ -85,10 +98,10 @@ export interface PhotoInputProps {
|
|
|
85
98
|
*
|
|
86
99
|
* | | |
|
|
87
100
|
* |---|---|
|
|
88
|
-
* | miniatura | quadrada de 64, a `Image` da `Gallery` (o `Avatar` `lg`
|
|
101
|
+
* | miniatura | quadrada de 64, a `Image` da `Gallery` (o `Avatar` `lg` da referência) |
|
|
89
102
|
* | tocar nela | abre a foto grande, no MESMO zoom da `Gallery` (`FotoAmpliada`) |
|
|
90
103
|
* | o X | com fundo, TODO fora da foto, no canto de cima à direita |
|
|
91
104
|
* | leitor de tela | *"Foto 2 de 3"* (com uma só, *"Foto"*) e *"Abre a foto"*; o X, *"Remover foto 2"* |
|
|
92
105
|
* | inativo | a foto ainda abre (olhar não muda nada); o X não remove |
|
|
93
106
|
*/
|
|
94
|
-
export declare function PhotoInput({ value, onChange, max, source, disabled, offerSettings, onPermissionDenied, addIcon, removeIcon, style, testID, }: PhotoInputProps): React.JSX.Element;
|
|
107
|
+
export declare function PhotoInput({ value, onChange, max, source, disabled, offerSettings, onPermissionDenied, exif, addIcon, removeIcon, style, testID, }: PhotoInputProps): React.JSX.Element;
|
package/dist/sistema.js
CHANGED
|
@@ -35,7 +35,7 @@ import { Linking, Platform, Pressable, View } from "react-native";
|
|
|
35
35
|
import RNDateTimePicker, { DateTimePickerAndroid } from "@react-native-community/datetimepicker";
|
|
36
36
|
import * as ImagePicker from "expo-image-picker";
|
|
37
37
|
import { IconButton } from "./actions.js";
|
|
38
|
-
import { criarFolha, estadoAcessivel } from "./estilos.js";
|
|
38
|
+
import { canto, criarFolha, estadoAcessivel } from "./estilos.js";
|
|
39
39
|
import { Alert } from "./feedback.js";
|
|
40
40
|
import { Icon } from "./icon.js";
|
|
41
41
|
import { useCampo } from "./inputs.js";
|
|
@@ -46,12 +46,12 @@ const folha = criarFolha((t) => ({
|
|
|
46
46
|
gatilho: {
|
|
47
47
|
flexDirection: "row", alignItems: "center", justifyContent: "space-between",
|
|
48
48
|
width: "100%", borderWidth: t.size.borderWidth, borderColor: t.color.borderStrong,
|
|
49
|
-
|
|
49
|
+
...canto(t.size.radiusControl), backgroundColor: t.color.fieldBg,
|
|
50
50
|
},
|
|
51
51
|
invalido: { borderColor: t.color.danger400 ?? t.color.destructive },
|
|
52
52
|
desabilitado: { opacity: t.size.opacityDisabled },
|
|
53
|
-
// R-22 (02/10/2026), a "B" da prancha: miniatura QUADRADA de 64 — o `Avatar` `lg`
|
|
54
|
-
//
|
|
53
|
+
// R-22 (02/10/2026), a "B" da prancha: miniatura QUADRADA de 64 — o `Avatar` `lg` da
|
|
54
|
+
// referência, e a medida que o botão de pôr foto passou a ter também (era 72, número à mão) — e o X
|
|
55
55
|
// TODO FORA da foto. O X é o `IconButton` `sm` (30) com fundo; a área de toque dele é de 44
|
|
56
56
|
// (`targetMin`), com o desenho no centro, então o deslocamento que põe o canto do DESENHO no
|
|
57
57
|
// canto da foto é (30 + 44) / 2. A grade deixa esse espaço em cima, à direita e entre as linhas,
|
|
@@ -65,7 +65,7 @@ const folha = criarFolha((t) => ({
|
|
|
65
65
|
remover: { position: "absolute", top: -foraDoX(t), right: -foraDoX(t) },
|
|
66
66
|
adicionar: {
|
|
67
67
|
alignItems: "center", justifyContent: "center", gap: t.size.space1,
|
|
68
|
-
width: t.size.space16, height: t.size.space16,
|
|
68
|
+
width: t.size.space16, height: t.size.space16, ...canto(t.size.radiusLg),
|
|
69
69
|
borderWidth: t.size.borderWidth, borderColor: t.color.borderStrong,
|
|
70
70
|
borderStyle: "dashed", backgroundColor: t.color.fieldBg,
|
|
71
71
|
},
|
|
@@ -128,6 +128,36 @@ export function DatePicker({ value, onChange, mode = "date", minimumDate, maximu
|
|
|
128
128
|
style,
|
|
129
129
|
], children: [_jsx(Text, { size: tam === "sm" ? "xs" : tam === "lg" ? "base" : "md", tone: value ? "default" : "subtle", numberOfLines: 1, children: texto }), icon && _jsx(Icon, { name: icon, size: "sm", color: t.color.subtleForeground })] }), mostrarSeletorIOS && (_jsx(RNDateTimePicker, { value: value ?? new Date(), mode: mode, minimumDate: minimumDate, maximumDate: maximumDate, onChange: receber }))] }));
|
|
130
130
|
}
|
|
131
|
+
// R-23 (06/10/2026) · a data em que a foto foi tirada, e NADA MAIS do EXIF.
|
|
132
|
+
//
|
|
133
|
+
// Lido no fonte do `expo-image-picker` 57.0.15: no iPhone o código junta o dicionário `{Exif}` no
|
|
134
|
+
// topo, então `DateTimeOriginal` chega com esse nome nos dois sistemas. O formato do EXIF é
|
|
135
|
+
// "AAAA:MM:DD HH:MM:SS", sem fuso. O iPhone pode mandar `OffsetTimeOriginal` ("-03:00"); a lista
|
|
136
|
+
// de etiquetas do Android não tem fuso, e aí vale a hora local do aparelho — que é a hora em que
|
|
137
|
+
// a pessoa estava, no caso comum de fotografar e lançar no mesmo lugar.
|
|
138
|
+
//
|
|
139
|
+
// ⚠ O EXIF de uma foto da galeria traz também a LOCALIZAÇÃO. Por isso esta função devolve uma
|
|
140
|
+
// data e o EXIF não sai daqui: o app não recebe o que não pediu.
|
|
141
|
+
function dataDaFoto(exif) {
|
|
142
|
+
const bruto = exif?.DateTimeOriginal ?? exif?.DateTimeDigitized;
|
|
143
|
+
if (typeof bruto !== "string")
|
|
144
|
+
return undefined;
|
|
145
|
+
const m = /^(\d{4}):(\d{2}):(\d{2})[ T](\d{2}):(\d{2}):(\d{2})/.exec(bruto.trim());
|
|
146
|
+
if (!m)
|
|
147
|
+
return undefined;
|
|
148
|
+
const [ano, mes, dia, hora, minuto, segundo] = m.slice(1).map(Number);
|
|
149
|
+
// "0000:00:00 00:00:00" é como câmera sem relógio escreve "sem data".
|
|
150
|
+
if (ano === 0 || mes < 1 || mes > 12 || dia < 1 || dia > 31 || hora > 23 || minuto > 59 || segundo > 59) {
|
|
151
|
+
return undefined;
|
|
152
|
+
}
|
|
153
|
+
const fuso = exif?.OffsetTimeOriginal ?? exif?.OffsetTimeDigitized;
|
|
154
|
+
const f = typeof fuso === "string" ? /^([+-])(\d{2}):(\d{2})$/.exec(fuso.trim()) : null;
|
|
155
|
+
if (f) {
|
|
156
|
+
const minutos = (f[1] === "-" ? -1 : 1) * (Number(f[2]) * 60 + Number(f[3]));
|
|
157
|
+
return new Date(Date.UTC(ano, mes - 1, dia, hora, minuto, segundo) - minutos * 60000);
|
|
158
|
+
}
|
|
159
|
+
return new Date(ano, mes - 1, dia, hora, minuto, segundo);
|
|
160
|
+
}
|
|
131
161
|
/**
|
|
132
162
|
* O anexo por foto.
|
|
133
163
|
*
|
|
@@ -143,6 +173,8 @@ export function DatePicker({ value, onChange, mode = "date", minimumDate, maximu
|
|
|
143
173
|
* | **câmera ou galeria?** | câmera, que é o que o plano pede | `source="library"` |
|
|
144
174
|
* | como **remover**? | um `IconButton` no canto de cada miniatura | `removeIcon` |
|
|
145
175
|
* | **quantas** cabem? | uma | `max` |
|
|
176
|
+
* | **várias de uma vez?** | da galeria, sim, quando cabe mais de uma (R-23) | `max` |
|
|
177
|
+
* | **a data** em que foi tirada? | não lê: ler o EXIF custa | `exif` → `takenAt` (R-23) |
|
|
146
178
|
*
|
|
147
179
|
* ⚠ **O gatilho some quando o limite é atingido**, em vez de ficar aceso e não fazer nada.
|
|
148
180
|
*
|
|
@@ -152,13 +184,13 @@ export function DatePicker({ value, onChange, mode = "date", minimumDate, maximu
|
|
|
152
184
|
*
|
|
153
185
|
* | | |
|
|
154
186
|
* |---|---|
|
|
155
|
-
* | miniatura | quadrada de 64, a `Image` da `Gallery` (o `Avatar` `lg`
|
|
187
|
+
* | miniatura | quadrada de 64, a `Image` da `Gallery` (o `Avatar` `lg` da referência) |
|
|
156
188
|
* | tocar nela | abre a foto grande, no MESMO zoom da `Gallery` (`FotoAmpliada`) |
|
|
157
189
|
* | o X | com fundo, TODO fora da foto, no canto de cima à direita |
|
|
158
190
|
* | leitor de tela | *"Foto 2 de 3"* (com uma só, *"Foto"*) e *"Abre a foto"*; o X, *"Remover foto 2"* |
|
|
159
191
|
* | inativo | a foto ainda abre (olhar não muda nada); o X não remove |
|
|
160
192
|
*/
|
|
161
|
-
export function PhotoInput({ value = [], onChange, max = 1, source = "camera", disabled, offerSettings = true, onPermissionDenied, addIcon = "camera", removeIcon = "x", style, testID, }) {
|
|
193
|
+
export function PhotoInput({ value = [], onChange, max = 1, source = "camera", disabled, offerSettings = true, onPermissionDenied, exif = false, addIcon = "camera", removeIcon = "x", style, testID, }) {
|
|
162
194
|
const t = useAureaTokens();
|
|
163
195
|
const s = folha(t);
|
|
164
196
|
const strings = useAureaStrings();
|
|
@@ -185,16 +217,27 @@ export function PhotoInput({ value = [], onChange, max = 1, source = "camera", d
|
|
|
185
217
|
return;
|
|
186
218
|
}
|
|
187
219
|
}
|
|
220
|
+
// R-23: da GALERIA, escolhe várias de uma vez quando cabe mais de uma, até o que falta para o
|
|
221
|
+
// `max`. A câmera tira uma por vez — no sistema, não há outro jeito.
|
|
222
|
+
const cabem = max - value.length;
|
|
188
223
|
const r = source === "camera"
|
|
189
|
-
? await ImagePicker.launchCameraAsync({ quality: 0.7 })
|
|
190
|
-
: await ImagePicker.launchImageLibraryAsync(
|
|
224
|
+
? await ImagePicker.launchCameraAsync({ quality: 0.7, exif })
|
|
225
|
+
: await ImagePicker.launchImageLibraryAsync(cabem > 1
|
|
226
|
+
? { quality: 0.7, exif, allowsMultipleSelection: true, selectionLimit: cabem }
|
|
227
|
+
: { quality: 0.7, exif });
|
|
191
228
|
if (r.canceled || !r.assets?.length)
|
|
192
229
|
return;
|
|
193
230
|
const novas = r.assets
|
|
194
|
-
.slice(0,
|
|
195
|
-
.map((a) =>
|
|
231
|
+
.slice(0, cabem)
|
|
232
|
+
.map((a) => {
|
|
233
|
+
const foto = { uri: a.uri, width: a.width, height: a.height };
|
|
234
|
+
const quando = exif ? dataDaFoto(a.exif) : undefined;
|
|
235
|
+
if (quando)
|
|
236
|
+
foto.takenAt = quando;
|
|
237
|
+
return foto;
|
|
238
|
+
});
|
|
196
239
|
onChange?.([...value, ...novas]);
|
|
197
|
-
}, [source, max, value, onChange, onPermissionDenied]);
|
|
240
|
+
}, [source, max, value, onChange, onPermissionDenied, exif]);
|
|
198
241
|
return (_jsxs(View, { testID: testID, style: style, children: [negadoDeVez && (_jsx(Alert, { variant: "warning", children: _jsxs(View, { style: { gap: t.size.space2 }, children: [_jsx(Text, { size: "sm", tone: "muted", children: strings.cameraDenied }), offerSettings && (_jsx(Pressable, { onPress: () => Linking.openSettings(), accessibilityRole: "button", accessibilityLabel: strings.openSettings, children: _jsx(Text, { size: "sm", weight: 600, tone: "primary", children: strings.openSettings }) }))] }) })), _jsxs(View, { style: [s.galeria, value.length > 0 && s.comX], children: [value.map((foto, n) => (_jsxs(View, { style: s.miniatura, children: [_jsx(Pressable, { onPress: () => setAberta(n), accessibilityRole: "imagebutton", accessibilityLabel: nomeDa(n), accessibilityHint: strings.photoOpen, children: _jsx(Image, { source: foto.uri, alt: "", ratio: 1 }) }), _jsx(View, { style: s.remover, children: _jsx(IconButton, { appearance: "solid", size: "sm", name: removeIcon, label: `${strings.photoRemove} ${n + 1}`, disabled: inativo, onPress: () => onChange?.(value.filter((_, i) => i !== n)) }) })] }, `${foto.uri}-${n}`))), !cheio && (_jsx(Pressable, { onPress: inativo ? undefined : escolher, disabled: inativo, accessibilityRole: "button", accessibilityLabel: campo?.label ?? strings.photoAdd, ...estadoAcessivel({ disabled: !!inativo }), style: [s.adicionar, inativo && s.desabilitado], children: addIcon && _jsx(Icon, { name: addIcon, size: "lg", color: t.color.subtleForeground }) }))] }), _jsx(FotoAmpliada, { source: fotoAberta?.uri, title: aberta != null ? nomeDa(aberta) : "", alt: aberta != null ? nomeDa(aberta) : "",
|
|
199
242
|
// A proporção da foto, quando a câmera a deu: a grande aparece inteira, sem tarja à toa.
|
|
200
243
|
ratio: fotoAberta?.width && fotoAberta.height ? fotoAberta.width / fotoAberta.height : 1, onClose: () => setAberta(null) })] }));
|
package/dist/text.d.ts
CHANGED
|
@@ -10,14 +10,14 @@ export type AureaTextFont = "ui" | "editorial" | "code";
|
|
|
10
10
|
export type AureaTextTone = "default" | "muted" | "subtle" | "primary" | "link" | "danger" | "success" | "warning" | "info";
|
|
11
11
|
export type AureaTextLeading = "none" | "tight" | "normal" | "relaxed";
|
|
12
12
|
/**
|
|
13
|
-
* O PAPEL do texto — B-02, 25/09/2026, no molde do
|
|
13
|
+
* O PAPEL do texto — B-02, 25/09/2026, no molde do componente de tipografia da referência. Uma lista
|
|
14
14
|
* fechada: título 1–6, texto, texto pequeno, texto mínimo e código.
|
|
15
15
|
*/
|
|
16
16
|
export type AureaTextType = "h1" | "h2" | "h3" | "h4" | "h5" | "h6" | "body" | "body-sm" | "body-xs" | "code";
|
|
17
17
|
export interface TextProps extends TextPropsRN {
|
|
18
18
|
/**
|
|
19
|
-
* O papel (B-02). Dá tamanho, peso, entrelinha e fonte de uma vez, com os números
|
|
20
|
-
*
|
|
19
|
+
* O papel (B-02). Dá tamanho, peso, entrelinha e fonte de uma vez, com os números da
|
|
20
|
+
* referência — os MESMOS da web, sem o degrau a mais do `size`. Uma opção solta passada junto
|
|
21
21
|
* (`size`, `weight`…) continua valendo por cima dele.
|
|
22
22
|
*/
|
|
23
23
|
type?: AureaTextType;
|
|
@@ -49,7 +49,7 @@ export type AureaTypographyAlign = "start" | "center" | "end" | "justify";
|
|
|
49
49
|
interface AureaTypographyBase extends Omit<TextPropsRN, "children"> {
|
|
50
50
|
color?: AureaTypographyColor;
|
|
51
51
|
weight?: AureaTypographyWeight;
|
|
52
|
-
/** `start`/`end` viram `left`/`right`, que o React Native já espelha em RTL (nota
|
|
52
|
+
/** `start`/`end` viram `left`/`right`, que o React Native já espelha em RTL (nota da referência). */
|
|
53
53
|
align?: AureaTypographyAlign;
|
|
54
54
|
/** Uma linha só, cortada com reticências (`numberOfLines={1}`). */
|
|
55
55
|
truncate?: boolean;
|
|
@@ -58,7 +58,7 @@ interface AureaTypographyBase extends Omit<TextPropsRN, "children"> {
|
|
|
58
58
|
export interface HeadingProps extends AureaTypographyBase {
|
|
59
59
|
type?: "h1" | "h2" | "h3" | "h4" | "h5" | "h6";
|
|
60
60
|
}
|
|
61
|
-
/** Título. Marca `accessibilityRole="header"` sozinho, como
|
|
61
|
+
/** Título. Marca `accessibilityRole="header"` sozinho, como a referência. */
|
|
62
62
|
export declare function Heading({ type, accessibilityRole, ...rest }: HeadingProps): React.JSX.Element;
|
|
63
63
|
export interface ParagraphProps extends AureaTypographyBase {
|
|
64
64
|
type?: "body" | "body-sm" | "body-xs";
|