@aurea-uds/native 0.14.1 → 0.15.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 +55 -3
- package/dist/actions.d.ts +4 -4
- package/dist/arquivo.d.ts +53 -0
- package/dist/arquivo.js +134 -0
- package/dist/busca.d.ts +4 -4
- package/dist/busca.js +2 -2
- package/dist/data.d.ts +18 -0
- package/dist/data.js +35 -10
- package/dist/display.js +4 -13
- package/dist/estilos.d.ts +12 -0
- package/dist/estilos.js +19 -0
- package/dist/feedback.d.ts +9 -4
- package/dist/feedback.js +6 -5
- package/dist/icon.d.ts +17 -1
- package/dist/icon.js +8 -1
- package/dist/index.d.ts +1 -1
- package/dist/inputs.d.ts +6 -3
- package/dist/inputs.js +7 -4
- package/dist/midia.d.ts +19 -2
- package/dist/midia.js +12 -1
- package/dist/moldura.d.ts +18 -0
- package/dist/moldura.js +15 -0
- package/dist/movimento.d.ts +12 -0
- package/dist/movimento.js +15 -1
- package/dist/navigation.d.ts +6 -6
- package/dist/numero.d.ts +3 -3
- package/dist/overlays.js +3 -3
- package/dist/sistema.d.ts +16 -4
- package/dist/sistema.js +37 -6
- package/dist/strings.d.ts +22 -1
- package/dist/strings.js +16 -0
- package/dist/toast.d.ts +2 -2
- package/dist/toast.js +2 -2
- package/package.json +11 -2
package/README.md
CHANGED
|
@@ -73,17 +73,19 @@ Para o texto sair na Atkinson Hyperlegible (a fonte da Aurea desde a ADR-0053),
|
|
|
73
73
|
O último costuma **já estar instalado**: `react-navigation` e `expo-router` o arrastam. Declará-lo
|
|
74
74
|
como peer não adiciona peso ao app — só diz o que já ia estar lá.
|
|
75
75
|
|
|
76
|
-
E **
|
|
77
|
-
câmera não instala nada e não vê aviso:
|
|
76
|
+
E **três peers OPCIONAIS**, marcados como tal em `peerDependenciesMeta` — quem não usa data,
|
|
77
|
+
câmera nem arquivo não instala nada e não vê aviso. Instale só o da peça que usar:
|
|
78
78
|
|
|
79
79
|
```sh
|
|
80
80
|
pnpm add @react-native-community/datetimepicker expo-image-picker
|
|
81
|
+
pnpm add expo-document-picker
|
|
81
82
|
```
|
|
82
83
|
|
|
83
84
|
| | | |
|
|
84
85
|
|---|---|---|
|
|
85
86
|
| `@react-native-community/datetimepicker` | ≥8 | MIT · só para o `DatePicker` |
|
|
86
87
|
| `expo-image-picker` | ≥16 | MIT · só para o `PhotoInput` |
|
|
88
|
+
| `expo-document-picker` | ≥12 | MIT · só para o `FileInput`, que sai por `@aurea-uds/native/system/file` |
|
|
87
89
|
|
|
88
90
|
## Tema × densidade
|
|
89
91
|
|
|
@@ -408,8 +410,13 @@ nega. Cada pergunta ficou com um padrão, e **cada padrão tem uma saída**:
|
|
|
408
410
|
| e se a pessoa **negar**? | pergunta de novo na próxima vez, enquanto o sistema deixar | `onPermissionDenied` |
|
|
409
411
|
| e se negar **de vez** (`canAskAgain: false`)? | um `Alert` de aviso + botão para as configurações | `offerSettings={false}` |
|
|
410
412
|
| câmera ou galeria? | **câmera** | `source="library"` |
|
|
411
|
-
| como remover? | um
|
|
413
|
+
| como remover? | um X com fundo, **todo fora** da foto, no canto de cima à direita (R-22) | `removeIcon` |
|
|
412
414
|
| quantas cabem? | **uma** | `max` |
|
|
415
|
+
| como **olhar** a foto? | tocar na miniatura abre a foto grande, no mesmo zoom da `Gallery` (R-22) | — |
|
|
416
|
+
|
|
417
|
+
**Desde a `0.15.0` (R-22)**, a miniatura é quadrada de 64 (o `Avatar` `lg` do HeroUI Native), do
|
|
418
|
+
tamanho do botão de pôr foto. O leitor de tela ouve *"Foto 2 de 3"* e *"Abre a foto"*; o X,
|
|
419
|
+
*"Remover foto 2"*. Com o campo inativo, a foto ainda abre e o X não remove.
|
|
413
420
|
|
|
414
421
|
⚠ **O gatilho some ao atingir o limite**, em vez de ficar aceso e não fazer nada — botão que
|
|
415
422
|
existe e não responde é o defeito, não a proteção.
|
|
@@ -420,6 +427,39 @@ errada é um diálogo assustador que o app não precisava mostrar.
|
|
|
420
427
|
As frases (`cameraDenied`, `openSettings`, `photoRemove`, `datePlaceholder`) saem da tabela de
|
|
421
428
|
**Frases** — ver a seção abaixo.
|
|
422
429
|
|
|
430
|
+
### `FileInput` — escolher arquivo (R-21, `0.15.0`)
|
|
431
|
+
|
|
432
|
+
Ele mora num caminho **só dele**, e não no `/system`:
|
|
433
|
+
|
|
434
|
+
```tsx
|
|
435
|
+
import {FileInput} from "@aurea-uds/native/system/file";
|
|
436
|
+
|
|
437
|
+
<Field label="Documento">
|
|
438
|
+
<FileInput value={arquivos} onChange={setArquivos} accept="application/pdf"
|
|
439
|
+
maxSize={10 * 1024 * 1024} />
|
|
440
|
+
</Field>
|
|
441
|
+
```
|
|
442
|
+
|
|
443
|
+
⚠ **O caminho próprio é o que faz "opcional" ser verdade.** Se ele morasse no `/system`, todo app
|
|
444
|
+
que usa o `DatePicker` teria de instalar o `expo-document-picker`, ou a montagem falharia. Só quem
|
|
445
|
+
importa `/system/file` precisa dele.
|
|
446
|
+
|
|
447
|
+
| a pergunta | o que ficou | como mudar |
|
|
448
|
+
|---|---|---|
|
|
449
|
+
| o que volta? | `{uri, name, size, mimeType}` — o endereço local, **não o conteúdo** | — |
|
|
450
|
+
| a pessoa **cancelou**? | nada muda | — |
|
|
451
|
+
| passou do **tamanho**? | recusado, com *"Arquivo maior que o limite: nome"* em vermelho embaixo | `maxSize` |
|
|
452
|
+
| **tipo** errado? | o seletor filtra; a peça confere de novo e recusa | `accept` |
|
|
453
|
+
| o tamanho **não veio**? | aceito: não há o que medir, e o servidor confere | — |
|
|
454
|
+
| **quantos** cabem? | um; no limite o gatilho some, como no `PhotoInput` | `max` |
|
|
455
|
+
| **nome comprido**? | corta no meio (`relat…2026.pdf`): a extensão continua à vista | — |
|
|
456
|
+
|
|
457
|
+
Para o leitor de tela: o gatilho é um botão com o nome do `Field` e o texto *"Escolher arquivo"*;
|
|
458
|
+
cada arquivo lê *"nome, tamanho"*; o X lê *"Remover nome"*; a recusa é anunciada.
|
|
459
|
+
|
|
460
|
+
As frases `fileChoose`, `fileRemove`, `fileTooLarge` e `fileWrongType` saem da tabela de **Frases**;
|
|
461
|
+
as três últimas são as mesmas da web.
|
|
462
|
+
|
|
423
463
|
## Gráfico
|
|
424
464
|
|
|
425
465
|
```tsx
|
|
@@ -555,6 +595,14 @@ botão e não faria nada.
|
|
|
555
595
|
```tsx
|
|
556
596
|
<Timeline items={[{title: "Criado", description: "pelo app", time: "08/09 14:20"}]} />
|
|
557
597
|
|
|
598
|
+
// R-18 (0.15.0): ícone em moldura, cor por tom, valor à direita e o que houve entre dois itens.
|
|
599
|
+
<Timeline items={[
|
|
600
|
+
{title: "Pedido enviado", description: "28/09", icon: "truck", tone: "success",
|
|
601
|
+
trailing: "R$ 216,00", between: "6 dias depois"},
|
|
602
|
+
{title: "Pagamento", description: "22/09", icon: "storefront", tone: "primary",
|
|
603
|
+
trailing: "R$ 460,00"},
|
|
604
|
+
]} />
|
|
605
|
+
|
|
558
606
|
<DataList items={[{term: "Quilometragem", value: "12,4 km/l"}]} />
|
|
559
607
|
```
|
|
560
608
|
|
|
@@ -567,6 +615,10 @@ num painel de oito pares perde qual valor era de qual termo.
|
|
|
567
615
|
que a regra evita: a 320px a coluna do valor resolvia em **0px** e jogava o texto para fora da
|
|
568
616
|
página. `layout="inline"` / `"stacked"` força, contra a largura.
|
|
569
617
|
|
|
618
|
+
⚠ **Basta um item com `icon` para a coluna inteira ter a largura da moldura** (40): os itens sem
|
|
619
|
+
ícone ficam com o ponto, no centro dela, e a linha passa pelo centro das molduras. O `between` não
|
|
620
|
+
aparece depois do último item, porque não há próximo.
|
|
621
|
+
|
|
570
622
|
⚠ **No `Timeline` a contagem e a posição NÃO são anunciadas**, e é escolha. Na web ele é um
|
|
571
623
|
`<ol>`, e a ficha diz que o leitor anuncia "1 de 40" de graça; aqui esse "de graça" não existe e
|
|
572
624
|
teria de ser escrito item a item. Pendurar isso em cada evento de um histórico é ruído a cada
|
package/dist/actions.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
2
|
import { type PressableProps } from "react-native";
|
|
3
|
-
import { type AureaIconRegistry, type
|
|
3
|
+
import { type AureaIconRegistry, type AureaIcon } from "./icon.js";
|
|
4
4
|
/** Quanto peso a caixa tem. */
|
|
5
5
|
export type AureaButtonAppearance = "solid" | "outline" | "ghost";
|
|
6
6
|
/** O que a cor significa. Mesmos nomes da web (ADR-0044). */
|
|
@@ -12,8 +12,8 @@ export interface ButtonProps extends Omit<PressableProps, "children" | "style">
|
|
|
12
12
|
appearance?: AureaButtonAppearance;
|
|
13
13
|
tone?: AureaButtonTone;
|
|
14
14
|
size?: AureaButtonSize;
|
|
15
|
-
leadingIcon?:
|
|
16
|
-
trailingIcon?:
|
|
15
|
+
leadingIcon?: AureaIcon;
|
|
16
|
+
trailingIcon?: AureaIcon;
|
|
17
17
|
/**
|
|
18
18
|
* Um desenho QUALQUER na frente do texto, para quando a marca não pode ser um ícone nosso.
|
|
19
19
|
*
|
|
@@ -55,7 +55,7 @@ export interface LinkButtonProps extends Omit<ButtonProps, "appearance" | "fullW
|
|
|
55
55
|
/** Botão-texto sem recuo nem caixa, alinhado com o texto em volta. O `LinkButton` do HeroUI. */
|
|
56
56
|
export declare function LinkButton(props: LinkButtonProps): React.JSX.Element;
|
|
57
57
|
export interface IconButtonProps extends Omit<ButtonProps, "children" | "leadingIcon" | "trailingIcon" | "fullWidth"> {
|
|
58
|
-
name:
|
|
58
|
+
name: AureaIcon;
|
|
59
59
|
/** **Obrigatório**: um botão que só tem glifo não tem texto para o leitor de tela anunciar. */
|
|
60
60
|
label: string;
|
|
61
61
|
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { type StyleProp, type ViewStyle } from "react-native";
|
|
3
|
+
import { type AureaIcon } from "./icon.js";
|
|
4
|
+
/** O arquivo escolhido: o que o `expo-document-picker` devolve, sem o conteúdo. */
|
|
5
|
+
export type AureaFile = {
|
|
6
|
+
/** O endereço local do arquivo — é ele que o app envia. */
|
|
7
|
+
uri: string;
|
|
8
|
+
name: string;
|
|
9
|
+
/** Em bytes. Pode faltar: nem todo provedor de arquivos do Android informa. */
|
|
10
|
+
size?: number;
|
|
11
|
+
mimeType?: string;
|
|
12
|
+
};
|
|
13
|
+
export interface FileInputProps {
|
|
14
|
+
value?: AureaFile[];
|
|
15
|
+
onChange?: (arquivos: AureaFile[]) => void;
|
|
16
|
+
/**
|
|
17
|
+
* Os tipos aceitos, em MIME: `"application/pdf"`, `["image/*", "application/pdf"]`. É o filtro
|
|
18
|
+
* do seletor do sistema, e a peça confere de novo na volta. Padrão: qualquer arquivo.
|
|
19
|
+
*/
|
|
20
|
+
accept?: string | string[];
|
|
21
|
+
/** Quantos arquivos cabem. Padrão: 1. Com o limite atingido, o gatilho some. */
|
|
22
|
+
max?: number;
|
|
23
|
+
/** O tamanho máximo, em bytes. O arquivo maior é recusado, com aviso embaixo do campo. */
|
|
24
|
+
maxSize?: number;
|
|
25
|
+
disabled?: boolean;
|
|
26
|
+
/** O glifo do gatilho. Padrão `paperclip`; `false` tira. */
|
|
27
|
+
addIcon?: AureaIcon | false;
|
|
28
|
+
/** O glifo de cada arquivo escolhido. Padrão `file-text`, o mesmo da web; `false` tira. */
|
|
29
|
+
fileIcon?: AureaIcon | false;
|
|
30
|
+
removeIcon?: AureaIcon;
|
|
31
|
+
style?: StyleProp<ViewStyle>;
|
|
32
|
+
testID?: string;
|
|
33
|
+
}
|
|
34
|
+
/** O tamanho para ler: a mesma conta do `formatSize` da web (`file-input.tsx`). */
|
|
35
|
+
export declare function formatarTamanho(bytes: number): string;
|
|
36
|
+
/** O arquivo bate com o `accept`? Sem `accept`, ou sem tipo informado, a resposta é sim. */
|
|
37
|
+
export declare function aceitaTipo(mimeType: string | undefined, accept: string | string[] | undefined): boolean;
|
|
38
|
+
/**
|
|
39
|
+
* Escolher arquivo do aparelho.
|
|
40
|
+
*
|
|
41
|
+
* | a pergunta | o que ficou |
|
|
42
|
+
* |---|---|
|
|
43
|
+
* | a pessoa **cancelou** o seletor? | nada muda: nem a lista, nem o aviso |
|
|
44
|
+
* | o arquivo passa do **`maxSize`**? | é recusado, e o aviso aparece embaixo, em vermelho |
|
|
45
|
+
* | o tipo não bate com o **`accept`**? | idem — o seletor do sistema filtra, a peça confere |
|
|
46
|
+
* | o tamanho **não veio**? | aceito: não há o que medir, e o servidor confere |
|
|
47
|
+
* | **quantos** cabem? | `max`, padrão 1; no limite o gatilho some, como no `PhotoInput` |
|
|
48
|
+
* | **nome comprido**? | corta no MEIO (`relat…2026.pdf`), para a extensão continuar à vista |
|
|
49
|
+
*
|
|
50
|
+
* Para o leitor de tela: o gatilho é um botão com o nome do `Field` e o texto dele; cada
|
|
51
|
+
* arquivo lê *"nome, tamanho"*; o X lê *"Remover nome"*; o aviso de recusa é anunciado.
|
|
52
|
+
*/
|
|
53
|
+
export declare function FileInput({ value, onChange, accept, max, maxSize, disabled, addIcon, fileIcon, removeIcon, style, testID, }: FileInputProps): React.JSX.Element;
|
package/dist/arquivo.js
ADDED
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
// Aurea nativo — o `FileInput`: escolher um arquivo do aparelho. R-21, 02/10/2026.
|
|
3
|
+
//
|
|
4
|
+
// Pedido do app: mandar um documento (PDF) ao servidor dele. A trava de importação do app proíbe
|
|
5
|
+
// o `expo-document-picker` nas telas, então a peça tem de ser da Aurea, no molde do `PhotoInput`.
|
|
6
|
+
// Proposta aprovada pelo Victor em 02/10/2026 (*"3 sim"*).
|
|
7
|
+
//
|
|
8
|
+
// ── AS REFERÊNCIAS ───────────────────────────────────────────────────────────────────────────
|
|
9
|
+
// HeroUI não tem: nem o `heroui-native` 1.0.10, nem o `@heroui/styles` 3.2.6 (medido nos pacotes).
|
|
10
|
+
// ReUI tem (`reui.io/docs/components/base/file-upload`): `accept`, `maxSize`, `maxFiles`,
|
|
11
|
+
// `multiple`, `onFilesChange`. Daqui vêm `accept` e `maxSize`, que são também os nomes
|
|
12
|
+
// do `FileInput` da web. O limite é `max`, e não `maxFiles`: é o nome do `PhotoInput`,
|
|
13
|
+
// o irmão que mora ao lado.
|
|
14
|
+
// web o `FileInput` do `@aurea-uds/react`: as frases (`fileTooLarge`, `fileWrongType`,
|
|
15
|
+
// `fileRemove`) e a conta do tamanho (`formatSize`) são as de lá.
|
|
16
|
+
//
|
|
17
|
+
// ── A DEPENDÊNCIA ────────────────────────────────────────────────────────────────────────────
|
|
18
|
+
// expo-document-picker MIT · ~57.0.1 fixado pelo Expo SDK 57 · peer OPCIONAL, `>=12`
|
|
19
|
+
// O formato de `getDocumentAsync` que este arquivo usa (`canceled`, `assets`, `multiple`, `type`
|
|
20
|
+
// como texto ou lista) é o mesmo da 12.0.1 à 57.0.3 — medido nos `types.d.ts` das duas.
|
|
21
|
+
//
|
|
22
|
+
// ── ⚠ UM CAMINHO SÓ DELE: `@aurea-uds/native/system/file` ─────────────────────────────────────
|
|
23
|
+
// Não no `/system` junto do `DatePicker` e do `PhotoInput`, e é a mesma razão da ADR-0038: o
|
|
24
|
+
// Metro resolve todo `import` do arquivo que o app importa. Se o `FileInput` morasse no
|
|
25
|
+
// `sistema.tsx`, **todo app que usa o `DatePicker` teria de instalar o `expo-document-picker`**,
|
|
26
|
+
// ou a montagem falharia — o README do pacote já descreve esse defeito. Caminho próprio, e só
|
|
27
|
+
// quem escolhe arquivo instala.
|
|
28
|
+
//
|
|
29
|
+
// ── O QUE A PEÇA NÃO FAZ ─────────────────────────────────────────────────────────────────────
|
|
30
|
+
// Não lê o arquivo e não envia nada: devolve o endereço local (`uri`), o nome, o tamanho e o
|
|
31
|
+
// tipo. Quem envia é o app, e quem confere o conteúdo é o servidor dele.
|
|
32
|
+
import * as React from "react";
|
|
33
|
+
import { Pressable, View } from "react-native";
|
|
34
|
+
import * as DocumentPicker from "expo-document-picker";
|
|
35
|
+
import { IconButton } from "./actions.js";
|
|
36
|
+
import { criarFolha, estadoAcessivel } from "./estilos.js";
|
|
37
|
+
import { Icon } from "./icon.js";
|
|
38
|
+
import { alturaDoTamanho, respiroDoTamanho, useCampo } from "./inputs.js";
|
|
39
|
+
import { Text } from "./text.js";
|
|
40
|
+
import { useAureaStrings, useAureaTokens } from "./theme.js";
|
|
41
|
+
const folha = criarFolha((t) => ({
|
|
42
|
+
pilha: { gap: t.size.space2 },
|
|
43
|
+
// O gatilho e a linha do arquivo têm a pele do campo: mesma borda, mesmo fundo, cápsula.
|
|
44
|
+
caixa: {
|
|
45
|
+
flexDirection: "row", alignItems: "center", gap: t.size.space2,
|
|
46
|
+
width: "100%", minWidth: 0, borderWidth: t.size.borderWidth, borderColor: t.color.borderStrong,
|
|
47
|
+
borderRadius: t.size.radiusControl, backgroundColor: t.color.fieldBg,
|
|
48
|
+
},
|
|
49
|
+
invalido: { borderColor: t.color.danger400 ?? t.color.destructive },
|
|
50
|
+
desabilitado: { opacity: t.size.opacityDisabled },
|
|
51
|
+
nomeETamanho: { flex: 1, minWidth: 0, flexDirection: "row", alignItems: "center", gap: t.size.space2 },
|
|
52
|
+
nome: { flexShrink: 1 },
|
|
53
|
+
}));
|
|
54
|
+
/** O tamanho para ler: a mesma conta do `formatSize` da web (`file-input.tsx`). */
|
|
55
|
+
export function formatarTamanho(bytes) {
|
|
56
|
+
if (bytes < 1024)
|
|
57
|
+
return `${bytes} B`;
|
|
58
|
+
const unidades = ["KB", "MB", "GB"];
|
|
59
|
+
let n = bytes / 1024, i = 0;
|
|
60
|
+
while (n >= 1024 && i < unidades.length - 1) {
|
|
61
|
+
n /= 1024;
|
|
62
|
+
i++;
|
|
63
|
+
}
|
|
64
|
+
return `${n < 10 ? n.toFixed(1) : Math.round(n)} ${unidades[i]}`;
|
|
65
|
+
}
|
|
66
|
+
/** O arquivo bate com o `accept`? Sem `accept`, ou sem tipo informado, a resposta é sim. */
|
|
67
|
+
export function aceitaTipo(mimeType, accept) {
|
|
68
|
+
if (accept == null || !mimeType)
|
|
69
|
+
return true;
|
|
70
|
+
const tipo = mimeType.toLowerCase();
|
|
71
|
+
return (Array.isArray(accept) ? accept : [accept]).some((padrao) => {
|
|
72
|
+
const p = padrao.trim().toLowerCase();
|
|
73
|
+
if (p === "*/*" || p === tipo)
|
|
74
|
+
return true;
|
|
75
|
+
return p.endsWith("/*") && tipo.startsWith(p.slice(0, -1));
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Escolher arquivo do aparelho.
|
|
80
|
+
*
|
|
81
|
+
* | a pergunta | o que ficou |
|
|
82
|
+
* |---|---|
|
|
83
|
+
* | a pessoa **cancelou** o seletor? | nada muda: nem a lista, nem o aviso |
|
|
84
|
+
* | o arquivo passa do **`maxSize`**? | é recusado, e o aviso aparece embaixo, em vermelho |
|
|
85
|
+
* | o tipo não bate com o **`accept`**? | idem — o seletor do sistema filtra, a peça confere |
|
|
86
|
+
* | o tamanho **não veio**? | aceito: não há o que medir, e o servidor confere |
|
|
87
|
+
* | **quantos** cabem? | `max`, padrão 1; no limite o gatilho some, como no `PhotoInput` |
|
|
88
|
+
* | **nome comprido**? | corta no MEIO (`relat…2026.pdf`), para a extensão continuar à vista |
|
|
89
|
+
*
|
|
90
|
+
* Para o leitor de tela: o gatilho é um botão com o nome do `Field` e o texto dele; cada
|
|
91
|
+
* arquivo lê *"nome, tamanho"*; o X lê *"Remover nome"*; o aviso de recusa é anunciado.
|
|
92
|
+
*/
|
|
93
|
+
export function FileInput({ value = [], onChange, accept, max = 1, maxSize, disabled, addIcon = "paperclip", fileIcon = "file-text", removeIcon = "x", style, testID, }) {
|
|
94
|
+
const t = useAureaTokens();
|
|
95
|
+
const s = folha(t);
|
|
96
|
+
const strings = useAureaStrings();
|
|
97
|
+
const campo = useCampo();
|
|
98
|
+
const tam = campo?.size ?? "md";
|
|
99
|
+
const inativo = disabled ?? campo?.disabled;
|
|
100
|
+
const [recusados, setRecusados] = React.useState([]);
|
|
101
|
+
const cheio = value.length >= max;
|
|
102
|
+
const medida = { height: alturaDoTamanho(t, tam), paddingHorizontal: respiroDoTamanho(t, tam) };
|
|
103
|
+
const escolher = React.useCallback(async () => {
|
|
104
|
+
const r = await DocumentPicker.getDocumentAsync({
|
|
105
|
+
...(accept != null ? { type: accept } : {}),
|
|
106
|
+
multiple: max - value.length > 1,
|
|
107
|
+
});
|
|
108
|
+
// Cancelar não muda nada — nem apaga o aviso de uma recusa anterior.
|
|
109
|
+
if (r.canceled || !r.assets?.length)
|
|
110
|
+
return;
|
|
111
|
+
const aceitos = [];
|
|
112
|
+
const fora = [];
|
|
113
|
+
for (const a of r.assets) {
|
|
114
|
+
if (!aceitaTipo(a.mimeType, accept)) {
|
|
115
|
+
fora.push({ nome: a.name, motivo: "tipo" });
|
|
116
|
+
continue;
|
|
117
|
+
}
|
|
118
|
+
if (maxSize != null && a.size != null && a.size > maxSize) {
|
|
119
|
+
fora.push({ nome: a.name, motivo: "tamanho" });
|
|
120
|
+
continue;
|
|
121
|
+
}
|
|
122
|
+
aceitos.push({ uri: a.uri, name: a.name, size: a.size, mimeType: a.mimeType });
|
|
123
|
+
}
|
|
124
|
+
setRecusados(fora);
|
|
125
|
+
const cabem = aceitos.slice(0, max - value.length);
|
|
126
|
+
if (cabem.length)
|
|
127
|
+
onChange?.([...value, ...cabem]);
|
|
128
|
+
}, [accept, max, maxSize, value, onChange]);
|
|
129
|
+
return (_jsxs(View, { testID: testID, style: [s.pilha, style], children: [value.map((arquivo, n) => {
|
|
130
|
+
const tamanho = arquivo.size != null ? formatarTamanho(arquivo.size) : null;
|
|
131
|
+
return (_jsxs(View, { style: [s.caixa, medida, { paddingRight: 0 }], children: [_jsxs(View, { style: s.nomeETamanho, accessible: true, accessibilityLabel: tamanho ? `${arquivo.name}, ${tamanho}` : arquivo.name, children: [fileIcon && _jsx(Icon, { name: fileIcon, size: "sm", color: t.color.subtleForeground }), _jsx(Text, { size: "sm", numberOfLines: 1, ellipsizeMode: "middle", style: s.nome, children: arquivo.name }), tamanho && _jsx(Text, { size: "xs", tone: "muted", children: tamanho })] }), _jsx(IconButton, { appearance: "ghost", size: "sm", name: removeIcon, label: `${strings.fileRemove} ${arquivo.name}`, disabled: inativo, onPress: () => onChange?.(value.filter((_, i) => i !== n)) })] }, `${arquivo.uri}-${n}`));
|
|
132
|
+
}), !cheio && (_jsxs(Pressable, { onPress: inativo ? undefined : escolher, disabled: inativo, accessibilityRole: "button", accessibilityLabel: campo?.label ? `${campo.label}, ${strings.fileChoose}` : strings.fileChoose, accessibilityHint: campo?.hint, ...estadoAcessivel({ disabled: !!inativo }), style: [s.caixa, medida, (campo?.invalido || recusados.length > 0) && s.invalido,
|
|
133
|
+
inativo && s.desabilitado], children: [addIcon && _jsx(Icon, { name: addIcon, size: "sm", color: t.color.mutedForeground }), _jsx(Text, { size: tam === "sm" ? "xs" : tam === "lg" ? "base" : "md", tone: "muted", numberOfLines: 1, children: strings.fileChoose })] })), recusados.length > 0 && (_jsx(View, { accessibilityLiveRegion: "polite", children: recusados.map((r, n) => (_jsx(Text, { size: "xs", tone: "danger", children: `${r.motivo === "tamanho" ? strings.fileTooLarge : strings.fileWrongType}: ${r.nome}` }, n))) }))] }));
|
|
134
|
+
}
|
package/dist/busca.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
2
|
import { type TextInputProps, type StyleProp, type ViewStyle } from "react-native";
|
|
3
|
-
import { type
|
|
3
|
+
import { type AureaIcon } from "./icon.js";
|
|
4
4
|
import { type AureaFieldSize } from "./inputs.js";
|
|
5
5
|
/** Uma linha do catálogo. **Mesma forma da web** (`ComboboxOption`), mais o `disabled` que o `Select` daqui já tinha. */
|
|
6
6
|
export interface AureaComboboxItem {
|
|
@@ -62,9 +62,9 @@ export interface ComboboxProps {
|
|
|
62
62
|
disabled?: boolean;
|
|
63
63
|
size?: AureaFieldSize;
|
|
64
64
|
/** O glifo da seta. Registre-o, ou passe `false`. */
|
|
65
|
-
chevron?:
|
|
65
|
+
chevron?: AureaIcon | false;
|
|
66
66
|
/** O glifo da lupa no campo da folha. Registre-o, ou passe `false`. */
|
|
67
|
-
searchIcon?:
|
|
67
|
+
searchIcon?: AureaIcon | false;
|
|
68
68
|
/**
|
|
69
69
|
* O teclado do campo de busca da folha — E6, 25/09/2026. Para buscar um ANO ou um código, passe
|
|
70
70
|
* `"number-pad"`: sem isto abre o teclado de letras. É o `keyboardType` do `TextInput`, com o
|
|
@@ -115,7 +115,7 @@ export interface SearchFieldProps {
|
|
|
115
115
|
disabled?: boolean;
|
|
116
116
|
size?: AureaFieldSize;
|
|
117
117
|
/** O glifo da lupa. Padrão `search`; `false` tira. */
|
|
118
|
-
icon?:
|
|
118
|
+
icon?: AureaIcon | false;
|
|
119
119
|
/** Mostra o botão de limpar quando há texto. Padrão **true**. */
|
|
120
120
|
clearable?: boolean;
|
|
121
121
|
onSubmit?: () => void;
|
package/dist/busca.js
CHANGED
|
@@ -73,7 +73,7 @@ import { criarFolha, estadoAcessivel } from "./estilos.js";
|
|
|
73
73
|
import { Spinner } from "./feedback.js";
|
|
74
74
|
import { Icon } from "./icon.js";
|
|
75
75
|
import { KeyboardAvoiding, useCampo } from "./inputs.js";
|
|
76
|
-
import { useReduceMotion } from "./movimento.js";
|
|
76
|
+
import { useReduceMotion, driverNativo } from "./movimento.js";
|
|
77
77
|
import { RecuoDaFolha } from "./screen.js";
|
|
78
78
|
import { Text } from "./text.js";
|
|
79
79
|
import { useAureaStrings, useAureaTokens, ForaDaMarca, usePeleSobreAMarca } from "./theme.js";
|
|
@@ -283,7 +283,7 @@ export function Combobox({ items, value, onValueChange, onSearchChange, searchDe
|
|
|
283
283
|
return;
|
|
284
284
|
}
|
|
285
285
|
Animated.timing(arrasto, {
|
|
286
|
-
toValue: 0, duration: t.size.durationSlow, useNativeDriver:
|
|
286
|
+
toValue: 0, duration: t.size.durationSlow, useNativeDriver: driverNativo(),
|
|
287
287
|
easing: Easing.bezier(...t.easing.easeEmphasized),
|
|
288
288
|
}).start();
|
|
289
289
|
},
|
package/dist/data.d.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
2
|
import { type StyleProp, type ViewStyle } from "react-native";
|
|
3
|
+
import type { AureaBadgeTone } from "./display.js";
|
|
4
|
+
import type { AureaIcon } from "./icon.js";
|
|
3
5
|
type Virtualizavel = {
|
|
4
6
|
/** Vira `FlatList`. ⚠ Então este componente é o rolador — não o ponha dentro de `<Screen scroll>`. */
|
|
5
7
|
virtualized?: boolean;
|
|
@@ -9,6 +11,22 @@ export type AureaTimelineItem = {
|
|
|
9
11
|
description?: React.ReactNode;
|
|
10
12
|
/** Nó, não string: uma data absoluta e uma relativa são igualmente bem-vindas — como na web. */
|
|
11
13
|
time?: React.ReactNode;
|
|
14
|
+
/**
|
|
15
|
+
* R-18, 02/10/2026: o glifo numa moldura redonda, no lugar do ponto. Moldura de 40 (o `Avatar`
|
|
16
|
+
* `sm` do HeroUI Native) e glifo de 20. Basta um item com `icon` para a coluna inteira ter a
|
|
17
|
+
* largura da moldura — os itens sem ícone ficam com o ponto, no centro dela.
|
|
18
|
+
*/
|
|
19
|
+
icon?: AureaIcon;
|
|
20
|
+
/** A cor da moldura, com o vocabulário do `Badge`. Sem `icon`, não faz nada. Padrão: `neutral`. */
|
|
21
|
+
tone?: AureaBadgeTone;
|
|
22
|
+
/** O que vai à DIREITA do título — um valor, um selo. */
|
|
23
|
+
trailing?: React.ReactNode;
|
|
24
|
+
/**
|
|
25
|
+
* O que aconteceu ENTRE este item e o próximo, escrito ao lado da linha: *"6 dias depois"*,
|
|
26
|
+
* *"3 h depois"*. Proposta aprovada na prancha de 02/10/2026: a linha deixa de ser enfeite e
|
|
27
|
+
* passa a dizer alguma coisa. No último item não aparece, porque não há próximo.
|
|
28
|
+
*/
|
|
29
|
+
between?: React.ReactNode;
|
|
12
30
|
};
|
|
13
31
|
export interface TimelineProps extends Virtualizavel {
|
|
14
32
|
items: readonly AureaTimelineItem[];
|
package/dist/data.js
CHANGED
|
@@ -57,6 +57,7 @@ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-run
|
|
|
57
57
|
import * as React from "react";
|
|
58
58
|
import { FlatList, Pressable, View, useWindowDimensions, } from "react-native";
|
|
59
59
|
import { criarFolha } from "./estilos.js";
|
|
60
|
+
import { IconeEmMoldura } from "./moldura.js";
|
|
60
61
|
import { Text } from "./text.js";
|
|
61
62
|
import { useAureaStrings, useAureaTokens } from "./theme.js";
|
|
62
63
|
const folha = criarFolha((t) => ({
|
|
@@ -79,6 +80,14 @@ const folha = criarFolha((t) => ({
|
|
|
79
80
|
borderWidth: t.size.space05, borderColor: t.color.surface1,
|
|
80
81
|
},
|
|
81
82
|
colunaDoEvento: { flex: 1, minWidth: 0, gap: t.size.space05 },
|
|
83
|
+
// R-18: o ponto centrado na coluna da moldura, quando a lista tem molduras.
|
|
84
|
+
colunaDoPonto: { width: t.size.space10, alignItems: "center", flexGrow: 0, flexShrink: 0 },
|
|
85
|
+
linhaDoTitulo: {
|
|
86
|
+
flexDirection: "row", alignItems: "flex-start", justifyContent: "space-between",
|
|
87
|
+
gap: t.size.space2,
|
|
88
|
+
},
|
|
89
|
+
tituloQueEncolhe: { flexShrink: 1 },
|
|
90
|
+
entre: { flexDirection: "row", alignItems: "center" },
|
|
82
91
|
// ── DataList ──
|
|
83
92
|
pares: { gap: t.size.space2 },
|
|
84
93
|
parEmpilhado: { gap: t.size.space05 },
|
|
@@ -123,23 +132,39 @@ const EMPILHA_ABAIXO_DE = 640;
|
|
|
123
132
|
export function Timeline({ items, virtualized = false, style, testID }) {
|
|
124
133
|
const t = useAureaTokens();
|
|
125
134
|
const s = folha(t);
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
135
|
+
// A coluna do marcador tem a largura da moldura quando ALGUM item tem ícone: senão os títulos
|
|
136
|
+
// ficariam em colunas diferentes conforme o item. O trilho vai ao centro dela.
|
|
137
|
+
const comMoldura = items.some((i) => i.icon != null);
|
|
138
|
+
const marcador = comMoldura ? t.size.space10 : t.size.space3;
|
|
139
|
+
const desenhar = React.useCallback((item, n) => {
|
|
140
|
+
const titulo = typeof item.title === "string"
|
|
141
|
+
? _jsx(Text, { size: "md", weight: 600, style: s.tituloQueEncolhe, children: item.title }) : item.title;
|
|
142
|
+
const ponto = item.icon != null
|
|
143
|
+
? _jsx(IconeEmMoldura, { icon: item.icon, tone: item.tone, moldura: t.size.space10, glifo: t.size.iconMd, sobre: t.color.surface1 })
|
|
144
|
+
: comMoldura ? _jsx(View, { style: s.colunaDoPonto, children: _jsx(View, { style: s.ponto }) })
|
|
145
|
+
: _jsx(View, { style: s.ponto });
|
|
146
|
+
const entre = item.between != null && n < items.length - 1 ? (_jsx(View, { style: [s.entre, { paddingLeft: marcador + t.size.space3 }], children: typeof item.between === "string"
|
|
147
|
+
? _jsx(Text, { size: "xs", tone: "muted", children: item.between }) : item.between })) : null;
|
|
148
|
+
return (_jsxs(_Fragment, { children: [_jsxs(View, { style: s.evento, children: [ponto, _jsxs(View, { style: s.colunaDoEvento, children: [item.trailing != null ? (_jsxs(View, { style: s.linhaDoTitulo, children: [titulo, typeof item.trailing === "string"
|
|
149
|
+
? _jsx(Text, { size: "md", weight: 600, children: item.trailing }) : item.trailing] })) : titulo, item.description != null
|
|
150
|
+
? (typeof item.description === "string"
|
|
151
|
+
? _jsx(Text, { size: "sm", tone: "muted", children: item.description }) : item.description)
|
|
152
|
+
: null, item.time != null
|
|
153
|
+
? (typeof item.time === "string"
|
|
154
|
+
? _jsx(Text, { size: "xs", tone: "muted", children: item.time }) : item.time)
|
|
155
|
+
: null] })] }), entre] }));
|
|
156
|
+
}, [s, t, comMoldura, marcador, items.length]);
|
|
134
157
|
if (virtualized) {
|
|
135
|
-
return (_jsx(FlatList, { accessibilityRole: "list", data: items, keyExtractor: (_, n) => String(n),
|
|
158
|
+
return (_jsx(FlatList, { accessibilityRole: "list", data: items, keyExtractor: (_, n) => String(n),
|
|
159
|
+
// O `between` mora dentro da célula, então leva o mesmo vão que o separador dá entre elas.
|
|
160
|
+
renderItem: ({ item, index }) => _jsx(View, { style: s.trilha, children: desenhar(item, index) }), ItemSeparatorComponent: () => _jsx(View, { style: { height: t.size.space5 } }),
|
|
136
161
|
// ⚠ O trilho NÃO entra no modo virtualizado, e isso é honesto em vez de quebrado: ele é
|
|
137
162
|
// uma linha absoluta do topo ao pé da lista INTEIRA, e no `FlatList` a lista inteira não
|
|
138
163
|
// existe — só a janela. Desenhá-lo daria um trilho que começa e termina no lugar errado
|
|
139
164
|
// conforme se rola. Os pontos continuam, e são eles que marcam os eventos.
|
|
140
165
|
style: style, testID: testID }));
|
|
141
166
|
}
|
|
142
|
-
return (_jsxs(View, { accessibilityRole: "list", style: [s.trilha, style], testID: testID, children: [items.length > 0 ? _jsx(View, { style: s.trilho }) : null, items.map((item, n) => _jsx(React.Fragment, { children: desenhar(item) }, n))] }));
|
|
167
|
+
return (_jsxs(View, { accessibilityRole: "list", style: [s.trilha, style], testID: testID, children: [items.length > 0 ? _jsx(View, { style: [s.trilho, comMoldura && { left: marcador / 2 }] }) : null, items.map((item, n) => _jsx(React.Fragment, { children: desenhar(item, n) }, n))] }));
|
|
143
168
|
}
|
|
144
169
|
/**
|
|
145
170
|
* Os pares termo → valor.
|
package/dist/display.js
CHANGED
|
@@ -23,7 +23,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
23
23
|
// perderia o cartão, que é metade do que ela é.
|
|
24
24
|
import * as React from "react";
|
|
25
25
|
import { Image, View } from "react-native";
|
|
26
|
-
import { criarFolha } from "./estilos.js";
|
|
26
|
+
import { acentoDoTom, criarFolha, fundoDoTom } from "./estilos.js";
|
|
27
27
|
import { Card } from "./layout.js";
|
|
28
28
|
import { gravidadeDoEstado } from "./strings.js";
|
|
29
29
|
import { Text } from "./text.js";
|
|
@@ -103,24 +103,15 @@ const LETRA_DO_SELO = (t, size) => size === "lg" ? { fontSize: t.size.textBase,
|
|
|
103
103
|
export function Badge({ tone = "neutral", emphasis = "soft", size = "md", dot, count, max = 99, showZero, leading, trailing, fit = "auto", anchor, badgeContent, invisible, children, style, ...rest }) {
|
|
104
104
|
const t = useAureaTokens();
|
|
105
105
|
const s = folha(t);
|
|
106
|
-
const acento = tone
|
|
107
|
-
|
|
108
|
-
: tone === "success" ? t.color.success400 ?? t.color.success
|
|
109
|
-
: tone === "warning" ? t.color.warning400 ?? t.color.warning
|
|
110
|
-
: tone === "danger" ? t.color.danger400 ?? t.color.destructive
|
|
111
|
-
: t.color.secondaryForeground;
|
|
112
|
-
const fundoDoTom = tone === "info" ? t.color.infoBg
|
|
113
|
-
: tone === "success" ? t.color.successBg
|
|
114
|
-
: tone === "warning" ? t.color.warningBg
|
|
115
|
-
: tone === "danger" ? t.color.dangerBg
|
|
116
|
-
: undefined;
|
|
106
|
+
const acento = acentoDoTom(t, tone);
|
|
107
|
+
const fundo = fundoDoTom(t, tone);
|
|
117
108
|
const pele = emphasis === "solid"
|
|
118
109
|
? { backgroundColor: acento, borderColor: "transparent" }
|
|
119
110
|
: emphasis === "outline"
|
|
120
111
|
? { backgroundColor: "transparent", borderColor: acento }
|
|
121
112
|
: tone === "neutral"
|
|
122
113
|
? {}
|
|
123
|
-
: { backgroundColor:
|
|
114
|
+
: { backgroundColor: fundo, borderColor: acento };
|
|
124
115
|
const corDoTexto = emphasis === "solid" ? t.color.background
|
|
125
116
|
: tone === "neutral" ? t.color.secondaryForeground : acento;
|
|
126
117
|
const numero = count != null ? formatarContagem(count, max) : undefined;
|
package/dist/estilos.d.ts
CHANGED
|
@@ -33,6 +33,18 @@ export declare function criarFolha<T extends NamedStyles<T> | NamedStyles<Record
|
|
|
33
33
|
* um ícone) não tinha como escrevê-lo sem número à mão. O `check 39` cobra o teste dele.
|
|
34
34
|
*/
|
|
35
35
|
export declare const comOpacidade: (cor: string, pct: number) => string;
|
|
36
|
+
/** O tom de cor com significado — o vocabulário do `Badge` (`AureaBadgeTone`). */
|
|
37
|
+
export type TomDeCor = "neutral" | "primary" | "info" | "success" | "warning" | "danger";
|
|
38
|
+
/**
|
|
39
|
+
* A cor FORTE de um tom: o texto do selo, o ícone da moldura.
|
|
40
|
+
*
|
|
41
|
+
* ⚠ **Morava dentro do `Badge` e mudou de casa em 02/10/2026**, quando a moldura do ícone (R-15 e
|
|
42
|
+
* R-18) passou a precisar da mesma cor por tom. Duas cópias do mapa é o defeito que o `CLAUDE.md`
|
|
43
|
+
* nomeia — e o dia em que um tom novo entra numa só.
|
|
44
|
+
*/
|
|
45
|
+
export declare const acentoDoTom: (t: AureaTokens, tom: TomDeCor) => string;
|
|
46
|
+
/** O fundo SUAVE de um tom (`--<tom>-bg`). `primary` e `neutral` não têm token: `undefined`. */
|
|
47
|
+
export declare const fundoDoTom: (t: AureaTokens, tom: TomDeCor) => string | undefined;
|
|
36
48
|
/**
|
|
37
49
|
* 🔴 O FIO AMARELO DO ESCOLHIDO — o sinal único da casa para "este é o escolhido".
|
|
38
50
|
*
|
package/dist/estilos.js
CHANGED
|
@@ -49,6 +49,25 @@ export function criarFolha(descrever) {
|
|
|
49
49
|
export const comOpacidade = (cor, pct) => /^#[0-9a-fA-F]{6}$/.test(cor)
|
|
50
50
|
? cor + Math.round(Math.max(0, Math.min(1, pct)) * 255).toString(16).padStart(2, "0")
|
|
51
51
|
: cor;
|
|
52
|
+
/**
|
|
53
|
+
* A cor FORTE de um tom: o texto do selo, o ícone da moldura.
|
|
54
|
+
*
|
|
55
|
+
* ⚠ **Morava dentro do `Badge` e mudou de casa em 02/10/2026**, quando a moldura do ícone (R-15 e
|
|
56
|
+
* R-18) passou a precisar da mesma cor por tom. Duas cópias do mapa é o defeito que o `CLAUDE.md`
|
|
57
|
+
* nomeia — e o dia em que um tom novo entra numa só.
|
|
58
|
+
*/
|
|
59
|
+
export const acentoDoTom = (t, tom) => tom === "primary" ? t.color.primaryEmphasis
|
|
60
|
+
: tom === "info" ? t.color.info400 ?? t.color.info
|
|
61
|
+
: tom === "success" ? t.color.success400 ?? t.color.success
|
|
62
|
+
: tom === "warning" ? t.color.warning400 ?? t.color.warning
|
|
63
|
+
: tom === "danger" ? t.color.danger400 ?? t.color.destructive
|
|
64
|
+
: t.color.secondaryForeground;
|
|
65
|
+
/** O fundo SUAVE de um tom (`--<tom>-bg`). `primary` e `neutral` não têm token: `undefined`. */
|
|
66
|
+
export const fundoDoTom = (t, tom) => tom === "info" ? t.color.infoBg
|
|
67
|
+
: tom === "success" ? t.color.successBg
|
|
68
|
+
: tom === "warning" ? t.color.warningBg
|
|
69
|
+
: tom === "danger" ? t.color.dangerBg
|
|
70
|
+
: undefined;
|
|
52
71
|
/**
|
|
53
72
|
* 🔴 O FIO AMARELO DO ESCOLHIDO — o sinal único da casa para "este é o escolhido".
|
|
54
73
|
*
|
package/dist/feedback.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
2
|
import { type StyleProp, type ViewProps, type ViewStyle } from "react-native";
|
|
3
|
-
import { type IconName } from "./icon.js";
|
|
3
|
+
import { type IconName, type AureaIcon } from "./icon.js";
|
|
4
4
|
import { type AureaUniversalState } from "./strings.js";
|
|
5
5
|
export type AureaSpinnerSize = "sm" | "md" | "lg";
|
|
6
6
|
export interface SpinnerProps {
|
|
@@ -65,7 +65,7 @@ export interface AlertProps extends ViewProps {
|
|
|
65
65
|
/** Um dos sete estados universais. Ele **escolhe a variante** e escreve o texto padrão. */
|
|
66
66
|
state?: AureaUniversalState;
|
|
67
67
|
title?: React.ReactNode;
|
|
68
|
-
icon?:
|
|
68
|
+
icon?: AureaIcon;
|
|
69
69
|
onDismiss?: () => void;
|
|
70
70
|
children?: React.ReactNode;
|
|
71
71
|
}
|
|
@@ -86,7 +86,12 @@ export interface AlertProps extends ViewProps {
|
|
|
86
86
|
*/
|
|
87
87
|
export declare function Alert({ variant, state, title, icon, onDismiss, children, style, accessibilityLabel, accessibilityHint, ...rest }: AlertProps): React.JSX.Element;
|
|
88
88
|
export interface EmptyStateProps {
|
|
89
|
-
|
|
89
|
+
/**
|
|
90
|
+
* O glifo, dentro de uma moldura redonda — R-15, 02/10/2026, escolhida pelo Victor na prancha
|
|
91
|
+
* (*"1 c"*). Moldura de 64 (o `Avatar` `lg` do HeroUI) e glifo de 32, o de antes. O desenho
|
|
92
|
+
* do próprio app entra aqui também (`icon={Logo}`, R-11).
|
|
93
|
+
*/
|
|
94
|
+
icon?: AureaIcon;
|
|
90
95
|
title: React.ReactNode;
|
|
91
96
|
description?: React.ReactNode;
|
|
92
97
|
action?: React.ReactNode;
|
|
@@ -110,7 +115,7 @@ export interface DataStateProps extends Omit<ViewProps, "children"> {
|
|
|
110
115
|
/** Substitui o esqueleto padrão de `loading`. */
|
|
111
116
|
skeleton?: React.ReactNode;
|
|
112
117
|
emptyTitle?: React.ReactNode;
|
|
113
|
-
emptyIcon?:
|
|
118
|
+
emptyIcon?: AureaIcon;
|
|
114
119
|
action?: React.ReactNode;
|
|
115
120
|
/** Função para o conteúdo caro não ser construído enquanto o estado o esconde. */
|
|
116
121
|
children: React.ReactNode | (() => React.ReactNode);
|
package/dist/feedback.js
CHANGED
|
@@ -24,7 +24,8 @@ import { Animated, Easing, View } from "react-native";
|
|
|
24
24
|
import { IconButton } from "./actions.js";
|
|
25
25
|
import { criarFolha, estadoAcessivel } from "./estilos.js";
|
|
26
26
|
import { Icon } from "./icon.js";
|
|
27
|
-
import {
|
|
27
|
+
import { IconeEmMoldura } from "./moldura.js";
|
|
28
|
+
import { useReduceMotion, driverNativo } from "./movimento.js";
|
|
28
29
|
import { gravidadeDoEstado } from "./strings.js";
|
|
29
30
|
import { Text } from "./text.js";
|
|
30
31
|
import { useAureaStrings, useAureaTokens } from "./theme.js";
|
|
@@ -96,7 +97,7 @@ export function Spinner({ size = "sm", label, decorative, style, testID }) {
|
|
|
96
97
|
if (reduzir !== false)
|
|
97
98
|
return;
|
|
98
99
|
const laco = Animated.loop(Animated.timing(giro, {
|
|
99
|
-
toValue: 1, duration: 700, easing: Easing.linear, useNativeDriver:
|
|
100
|
+
toValue: 1, duration: 700, easing: Easing.linear, useNativeDriver: driverNativo(),
|
|
100
101
|
}));
|
|
101
102
|
laco.start();
|
|
102
103
|
// Sem o `stop` o laço sobrevive à desmontagem e continua acordando o JS — o vazamento
|
|
@@ -137,8 +138,8 @@ export function Skeleton({ width, height, radius, style, testID }) {
|
|
|
137
138
|
// 1,35 s no total, com o vale em .55 na METADE — é o `@keyframes skeleton-pulse` do
|
|
138
139
|
// `aurea.css:1112` lido literalmente: 675 ms para descer e 675 para voltar.
|
|
139
140
|
const laco = Animated.loop(Animated.sequence([
|
|
140
|
-
Animated.timing(opacidade, { toValue: 0.55, duration: 675, easing: Easing.inOut(Easing.ease), useNativeDriver:
|
|
141
|
-
Animated.timing(opacidade, { toValue: 1, duration: 675, easing: Easing.inOut(Easing.ease), useNativeDriver:
|
|
141
|
+
Animated.timing(opacidade, { toValue: 0.55, duration: 675, easing: Easing.inOut(Easing.ease), useNativeDriver: driverNativo() }),
|
|
142
|
+
Animated.timing(opacidade, { toValue: 1, duration: 675, easing: Easing.inOut(Easing.ease), useNativeDriver: driverNativo() }),
|
|
142
143
|
]));
|
|
143
144
|
laco.start();
|
|
144
145
|
return () => laco.stop();
|
|
@@ -220,7 +221,7 @@ export function EmptyState({ icon = "file", title, description, action, state, s
|
|
|
220
221
|
const s = folha(t);
|
|
221
222
|
const strings = useAureaStrings();
|
|
222
223
|
const desc = description ?? (state ? strings.universalState[state] : null);
|
|
223
|
-
return (_jsxs(View, { testID: testID, style: [s.vazio, style], children: [_jsx(
|
|
224
|
+
return (_jsxs(View, { testID: testID, style: [s.vazio, style], children: [_jsx(IconeEmMoldura, { icon: icon, moldura: t.size.space16, glifo: t.size.iconXl }), typeof title === "string"
|
|
224
225
|
? _jsx(Text, { size: "base", weight: 600, leading: "tight", align: "center", children: title })
|
|
225
226
|
: title, desc != null && (typeof desc === "string"
|
|
226
227
|
? _jsx(Text, { size: "sm", tone: "muted", align: "center", children: desc })
|