@aurea-uds/native 0.31.0 → 0.32.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/dist/inputs.js CHANGED
@@ -37,6 +37,11 @@ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-run
37
37
  // .switch-track :882 largura h×7/6, altura h×2/3, raio 999, fundo muted
38
38
  // o polegar :894 inset 3, lado = 100% − space-1 − space-05, redondo
39
39
  // .segmented :922 linha, gap 3, padding 3, minH controlHMd, fundo muted
40
+ //
41
+ // ── 0.32.0: A ESCALA DE TOQUE (aprovada pelo Victor: "sim, aprovo a escala de 48 no nativo") ────
42
+ // Os campos e controles deste arquivo deixam a altura da web (32 · 36 · 40) e passam à do pacote
43
+ // nativo da referência: `touchControlH*`, 40 · 48 · 56 no confortável. As medidas de cada peça
44
+ // estão ao lado dela. O `SegmentedControl` fica na escala da web — não é desta frente.
40
45
  import * as React from "react";
41
46
  import { Animated, KeyboardAvoidingView as KeyboardAvoidingViewRN, Modal, Platform, Pressable, ScrollView, TextInput, View, } from "react-native";
42
47
  import { canto, cantosDeCima, criarFolha, estadoAcessivel, fioDoEscolhido, peleDoCartao, peleDoCartaoEscolhido } from "./estilos.js";
@@ -50,9 +55,16 @@ import { useAureaStrings, useAureaTokens, useTokensDoApp, usePeleSobreAMarca, Fo
50
55
  // `alturaDoTamanho` e `respiroDoTamanho` saem do arquivo (não do pacote) desde 02/10/2026: o
51
56
  // `FileInput` (R-21) desenha um gatilho com a medida do campo, e copiar os números para lá é o
52
57
  // defeito que o `CLAUDE.md` nomeia.
53
- export const alturaDoTamanho = (t, s) => s === "sm" ? t.size.controlHSm : s === "lg" ? t.size.controlHLg : t.size.controlHMd;
58
+ // 0.32.0: a altura é a de TOQUE (40 · 48 · 56; a do campo da referência é 48), e anda com a
59
+ // densidade como a da web andava.
60
+ export const alturaDoTamanho = (t, s) => s === "sm" ? t.size.touchControlHSm : s === "lg" ? t.size.touchControlHLg : t.size.touchControlHMd;
54
61
  const fonteDoTamanho = (t, s) => s === "sm" ? t.size.textSm : s === "lg" ? t.size.textLg : t.size.textBase; // ADR-0050: um degrau acima da web
55
- export const respiroDoTamanho = (t, s) => s === "sm" ? t.size.space3 : s === "lg" ? t.size.space4 : 13;
62
+ // 0.32.0: o `md` é o 12 do campo da referência (`space3`). Era 13, o `padding-inline` cru do
63
+ // `.input` da web, que não tinha token.
64
+ export const respiroDoTamanho = (t, s) => s === "lg" ? t.size.space4 : t.size.space3;
65
+ // O gatilho de LISTA (`Select`, e o `Combobox`, que é igual a ele) tem 16 dos lados no `md`, o da
66
+ // referência; o `sm` e o `lg` ficam com o do campo.
67
+ export const respiroDoGatilho = (t, s) => s === "sm" ? respiroDoTamanho(t, s) : t.size.space4;
56
68
  const folha = criarFolha((t) => ({
57
69
  campo: { gap: 7 },
58
70
  rotulo: { flexDirection: "row", justifyContent: "space-between", gap: 12 },
@@ -64,7 +76,9 @@ const folha = criarFolha((t) => ({
64
76
  },
65
77
  // O `.textarea` é o único da família com raio LG em vez do de controle: uma caixa alta em
66
78
  // pílula viraria uma cápsula, e o CSS já decidiu isso (`aurea.css:694`).
67
- areaDeTexto: { minHeight: 104, paddingVertical: t.size.space3, ...canto(t.size.radiusLg) },
79
+ // 0.32.0: a caixa alta da referência — 128 de altura (era 104) e 8 em cima e embaixo (era 12).
80
+ areaDeTexto: { minHeight: t.size.space24 + t.size.space8, paddingVertical: t.size.space2,
81
+ ...canto(t.size.radiusLg) },
68
82
  invalido: { borderColor: t.color.danger400 ?? t.color.destructive },
69
83
  desabilitado: { opacity: t.size.opacityDisabled },
70
84
  // O GRUPO é a mesma caixa do campo virada em LINHA, e os números são os mesmos: mesma borda,
@@ -89,7 +103,8 @@ const folha = criarFolha((t) => ({
89
103
  },
90
104
  // E5: a marca no MEIO da altura do texto, como a referência. Era `flex-start` com um
91
105
  // `marginTop: 1` fixo, e a bolinha ficava presa no topo do rótulo.
92
- linhaDeControle: { flexDirection: "row", alignItems: "center", gap: 9 },
106
+ // 0.32.0: 12 entre a marca e o texto, o vão do campo de controle da referência (era 9).
107
+ linhaDeControle: { flexDirection: "row", alignItems: "center", gap: t.size.space3 },
93
108
  // CHK-01: o item do `RadioGroup variant="card"` — a pele do `Card` (`estilos.ts`), com a marca
94
109
  // no alto, na linha do título.
95
110
  itemCartao: { ...peleDoCartao(t), alignItems: "flex-start" },
@@ -97,7 +112,9 @@ const folha = criarFolha((t) => ({
97
112
  linhaNoTopo: { alignItems: "flex-start" },
98
113
  trilho: { ...canto(t.size.radiusFull), backgroundColor: t.color.muted,
99
114
  borderWidth: t.size.borderWidth, borderColor: t.color.border, justifyContent: "center" },
100
- polegar: { position: "absolute", left: 3, ...canto(t.size.radiusFull) },
115
+ // 0.32.0: a 2 da borda de FORA, como a bolinha da referência — o `left` conta de dentro da borda
116
+ // do trilho, então é `space05 − borderWidth` (1), a mesma conta do `.switch-track:after` da web.
117
+ polegar: { position: "absolute", left: t.size.space05 - t.size.borderWidth, ...canto(t.size.radiusFull) },
101
118
  segmentada: {
102
119
  flexDirection: "row", alignSelf: "flex-start", gap: 3, padding: 3,
103
120
  minHeight: t.size.controlHMd, ...canto(t.size.radiusControl),
@@ -138,8 +155,13 @@ const folha = criarFolha((t) => ({
138
155
  ...cantosDeCima(t.size.radiusSheet),
139
156
  backgroundColor: t.color.popover, borderWidth: t.size.borderWidth, borderColor: t.color.border,
140
157
  },
141
- opcao: { minHeight: t.size.controlHLg, justifyContent: "center",
142
- paddingHorizontal: t.size.space4 },
158
+ // 0.32.0: a opção da lista da referência — 12 em cima e embaixo, 8 dos lados, 8 entre o rótulo e
159
+ // o indicador. Com a linha de 24 do rótulo dá 48, a altura de toque (era `controlHLg`, 40).
160
+ opcao: { minHeight: t.size.touchControlHMd, flexDirection: "row", alignItems: "center",
161
+ gap: t.size.space2, paddingVertical: t.size.space3, paddingHorizontal: t.size.space2 },
162
+ // O indicador da escolhida: a caixa de 20 (`iconMd`) com o `check` de 16 no meio, como o da
163
+ // referência e a coluna `.combobox-check` da web.
164
+ indicador: { width: t.size.iconMd, height: t.size.iconMd, alignItems: "center", justifyContent: "center" },
143
165
  }));
144
166
  const CampoCtx = React.createContext(null);
145
167
  /** O que os controles leem do `Field` acima deles. Público porque um controle novo vai precisar. */
@@ -193,7 +215,7 @@ export function Field({ label, hint, error, disabled, size = "md", children, sty
193
215
  export function Label({ children, trailing, style, ...rest }) {
194
216
  const s = folha(useAureaTokens());
195
217
  return (_jsxs(View, { style: [s.rotulo, style], ...rest, children: [typeof children === "string"
196
- ? _jsx(Text, { size: "sm", weight: 600, children: children }) : children, trailing] }));
218
+ ? _jsx(Text, { size: "sm", weight: 500, children: children }) : children, trailing] }));
197
219
  }
198
220
  /**
199
221
  * O campo de uma linha.
@@ -278,7 +300,7 @@ export function Input({ value, defaultValue, onChangeText, placeholder, disabled
278
300
  style,
279
301
  ] }));
280
302
  }
281
- /** A caixa alta. Mesmo campo, `multiline`, raio LG e altura mínima de 104 (medida no CSS). */
303
+ /** A caixa alta. Mesmo campo, `multiline`, raio LG e altura mínima de 128 (a da referência, 0.32.0). */
282
304
  export function Textarea({ rows, size, style, ...rest }) {
283
305
  const t = useAureaTokens();
284
306
  const s = folha(t);
@@ -356,7 +378,8 @@ function ControleMarcado({ papel, label, description, checked, onChange, disable
356
378
  // largura que sobra dentro de uma fileira e apagaria o que estivesse ao lado.
357
379
  const temTexto = label != null || description != null;
358
380
  // A marca é METADE da altura do controle — `aurea.css:871`, e é o que faz ela crescer junto
359
- // com a densidade sem token próprio.
381
+ // com a densidade sem token próprio. 0.32.0: da altura de TOQUE, então o `md` dá os 24 da caixa
382
+ // da referência (era 18).
360
383
  const lado = alturaDoTamanho(t, tam) / 2;
361
384
  // `start`: o meio da marca no meio da PRIMEIRA linha do rótulo. A linha é a do `Text size="sm"`
362
385
  // do rótulo (entrelinha normal), então a conta sai dos mesmos tokens que desenham o texto.
@@ -368,14 +391,16 @@ function ControleMarcado({ papel, label, description, checked, onChange, disable
368
391
  // aparece escrito duas vezes, ou o controle sobe MUDO para quem usa leitor de tela.
369
392
  accessibilityLabel: accessibilityLabel ?? (typeof label === "string" ? label : campo?.label), accessibilityHint: typeof description === "string" ? description : campo?.hint, style: [s.linhaDeControle, align === "start" && s.linhaNoTopo, inativo && s.desabilitado, style], children: [_jsx(View, { style: [
370
393
  s.marcaBase, noTopo,
394
+ // 0.32.0: o canto da caixa é o `radiusXs` (6), o mesmo do `.checkbox .control-mark` da
395
+ // web; era 5 cru.
371
396
  { width: lado, height: lado,
372
- ...canto(papel === "radio" ? t.size.radiusFull : 5) },
397
+ ...canto(papel === "radio" ? t.size.radiusFull : t.size.radiusXs) },
373
398
  checked && { backgroundColor: t.color.controlSelected, borderColor: t.color.controlSelected },
374
399
  ], children: checked && (papel === "radio"
375
- // O ponto do rádio é 8×8 e da cor SELECIONADA, não da cor de primeiro plano — no CSS
376
- // ele é `background: var(--control-selected)` sobre a marca já pintada.
377
- ? _jsx(View, { style: { width: 8, height: 8, ...canto(t.size.radiusFull),
378
- backgroundColor: t.color.controlSelectedForeground } })
400
+ // O ponto do rádio é da cor de primeiro plano da seleção, sobre a marca já pintada.
401
+ // 0.32.0: 10, o ponto da referência e o do `RadioGroup` (`space2 + space05`); era 8.
402
+ ? _jsx(View, { style: { width: t.size.space2 + t.size.space05, height: t.size.space2 + t.size.space05,
403
+ ...canto(t.size.radiusFull), backgroundColor: t.color.controlSelectedForeground } })
379
404
  // O visto da web é desenhado com DUAS BORDAS de um retângulo 8×4 girado −45°. Isso
380
405
  // atravessa inteiro: `transform: rotate` e `borderLeftWidth`/`borderBottomWidth`
381
406
  // existem no RN. Desenhá-lo assim, em vez de trazer um glifo, mantém a peça sem
@@ -463,8 +488,8 @@ export const RadioGroup = Object.assign(RaizDoGrupoDeRadio, { Item: ItemDoGrupoD
463
488
  *
464
489
  * ⚠ **NÃO é o `Switch` do React Native**, pela mesma razão que o `Spinner` não é o
465
490
  * `ActivityIndicator`: aquele desenha o interruptor do sistema no Android e o do iOS no iOS —
466
- * duas aparências que o `CLAUDE.md` proíbe em voz alta. As medidas aqui são as do CSS: trilho de
467
- * `altura × 7/6` por `altura × 2/3`, polegar com 3 de folga.
491
+ * duas aparências que o `CLAUDE.md` proíbe em voz alta. As medidas são as do interruptor da
492
+ * referência (0.32.0): trilho de 48 × 24 e bolinha em cápsula de 28 × 20, a 2 da borda.
468
493
  *
469
494
  * ⚠ **E ele respeita "menos movimento"**: com a preferência ligada, o polegar salta em vez de
470
495
  * deslizar. O estado continua o mesmo — o que some é a animação, não a informação.
@@ -477,11 +502,17 @@ export function Switch({ label, description, checked, onChange, disabled, size,
477
502
  const inativo = disabled ?? campo?.disabled;
478
503
  const reduzir = useReduceMotion();
479
504
  const temTexto = label != null || description != null;
505
+ // 0.32.0: o interruptor da referência — trilho 48 × 24 e bolinha em CÁPSULA de 28 × 20, a 2 da
506
+ // borda de fora (era trilho 42 × 24 e bolinha redonda de 18). Na altura de toque: o trilho é a
507
+ // altura do controle por METADE dela (a largura é o dobro da altura, como na web); a bolinha é a
508
+ // altura do trilho menos 4 e 8 mais larga que alta. sm 40 × 20 / 24 × 16 · lg 56 × 28 / 32 × 24.
480
509
  const h = alturaDoTamanho(t, tam);
481
- const larguraDoTrilho = h * 7 / 6;
482
- const alturaDoTrilho = h * 2 / 3;
483
- const ladoDoPolegar = alturaDoTrilho - t.size.space1 - t.size.space05;
484
- const curso = larguraDoTrilho - ladoDoPolegar - 6;
510
+ const larguraDoTrilho = h;
511
+ const alturaDoTrilho = h / 2;
512
+ const alturaDoPolegar = alturaDoTrilho - 2 * t.size.space05;
513
+ const larguraDoPolegar = alturaDoPolegar + t.size.space2;
514
+ // O curso deixa os mesmos 2 da borda de fora na outra ponta (a conta do `--switch-x` da web).
515
+ const curso = larguraDoTrilho - larguraDoPolegar - 2 * t.size.space05;
485
516
  const pos = React.useRef(new Animated.Value(checked ? 1 : 0)).current;
486
517
  React.useEffect(() => {
487
518
  const destino = checked ? 1 : 0;
@@ -514,7 +545,8 @@ export function Switch({ label, description, checked, onChange, disabled, size,
514
545
  // `Input`: o que a cascata resolve de graça, aqui é conta explícita.
515
546
  style: [
516
547
  { flexDirection: "row", alignItems: "center" },
517
- temTexto && { gap: 10 },
548
+ // 0.32.0: 12 até o texto, o vão do campo de controle da referência e o do `Checkbox` (era 10).
549
+ temTexto && { gap: t.size.space3 },
518
550
  inativo && s.desabilitado, style,
519
551
  ], children: [_jsx(View, { style: [
520
552
  s.trilho,
@@ -522,7 +554,7 @@ export function Switch({ label, description, checked, onChange, disabled, size,
522
554
  checked && { backgroundColor: t.color.controlSelected, borderColor: t.color.controlSelected },
523
555
  ], children: _jsx(Animated.View, { style: [
524
556
  s.polegar,
525
- { width: ladoDoPolegar, height: ladoDoPolegar,
557
+ { width: larguraDoPolegar, height: alturaDoPolegar,
526
558
  backgroundColor: checked ? t.color.controlSelectedForeground : t.color.mutedForeground,
527
559
  transform: [{ translateX: pos.interpolate({ inputRange: [0, 1], outputRange: [0, curso] }) }] },
528
560
  ] }) }), temTexto && (_jsxs(View, { style: { flex: 1 }, children: [typeof label === "string" ? _jsx(Text, { size: "sm", children: label }) : label, description != null && (typeof description === "string"
@@ -566,6 +598,17 @@ export function SegmentedControl({ items, value, onChange, label, disabled, just
566
598
  : it.label, ativo && _jsx(View, { style: s.fioDoSegmento })] }, it.value));
567
599
  }) }) }));
568
600
  }
601
+ // ─────────────────────────────────────────────────────────────────────────────────────────────
602
+ // Select — o "motor novo sobre `Modal`" que a §5.2.4 previu
603
+ // ─────────────────────────────────────────────────────────────────────────────────────────────
604
+ /**
605
+ * O indicador da opção escolhida, nas listas do `Select` e do `Combobox` (0.32.0): o da referência
606
+ * — a caixa de 20 com o `check` de 16 — e o mesmo glifo da coluna `.combobox-check` da web. A cor
607
+ * é a do destaque que se lê como ícone nos dois temas (`primaryEmphasis`, ADR-0054). Sai do
608
+ * arquivo, não do pacote — e é função, não componente, para não contar como peça nova. ⚠ Pede o
609
+ * `check` no registro de ícones do app; sem ele, o aviso de desenvolvimento do `Icon` diz o import.
610
+ */
611
+ export const indicadorDaEscolhida = (t) => (_jsx(View, { style: folha(t).indicador, children: _jsx(Icon, { name: "check", size: "sm", color: t.color.primaryEmphasis }) }));
569
612
  /**
570
613
  * A escolha de um de muitos.
571
614
  *
@@ -599,14 +642,17 @@ export function Select({ items, value, onChange, placeholder, disabled, size, ch
599
642
  const escolhido = items.find((i) => i.value === value);
600
643
  return (_jsxs(_Fragment, { children: [_jsxs(Pressable, { testID: testID, onPress: inativo ? undefined : () => setAberto(true), disabled: inativo, accessibilityRole: "button", accessibilityLabel: campo?.label, accessibilityHint: campo?.hint, accessibilityValue: { text: escolhido?.label }, ...estadoAcessivel({ disabled: !!inativo, expanded: aberto }), style: [
601
644
  s.caixa,
645
+ // 0.32.0: o gatilho da referência — 16 dos lados (era 13) e 12 entre o valor e a seta.
646
+ // Os 12 de cima e de baixo dela são a altura de toque: 48 menos a linha de 24 do valor.
602
647
  { flexDirection: "row", alignItems: "center", justifyContent: "space-between",
603
- height: alturaDoTamanho(t, tam), paddingHorizontal: respiroDoTamanho(t, tam) },
648
+ gap: t.size.space3, height: alturaDoTamanho(t, tam),
649
+ paddingHorizontal: respiroDoGatilho(t, tam) },
604
650
  campo?.invalido && s.invalido,
605
651
  // Sobre a marca a tinta vence inválido — ver `usePeleSobreAMarca`.
606
652
  peleDaMarca,
607
653
  inativo && s.desabilitado,
608
654
  style,
609
- ], children: [_jsx(Text, { size: tam === "sm" ? "xs" : tam === "lg" ? "base" : "md", tone: escolhido ? "default" : "subtle", numberOfLines: 1, children: escolhido?.label ?? placeholder ?? "" }), chevron && _jsx(Icon, { name: chevron, size: "sm", color: peleDaMarca?.color ?? t.color.subtleForeground })] }), _jsx(ForaDaMarca, { children: _jsx(Modal, { visible: aberto, transparent: true, animationType: "slide", statusBarTranslucent: true, navigationBarTranslucent: true, onRequestClose: () => setAberto(false), children: _jsxs(View, { style: sf.fundoDaLista, children: [_jsx(Pressable, { style: sf.fundoDeToque, onPress: () => setAberto(false), accessible: false }), _jsxs(View, { style: sf.lista, children: [_jsx(ScrollView, { children: items.map((it) => (_jsx(Pressable, { disabled: it.disabled, onPress: () => { onChange?.(it.value); setAberto(false); }, accessibilityRole: "menuitem", ...estadoAcessivel({ selected: it.value === value, disabled: !!it.disabled }), style: [sf.opcao, it.disabled && sf.desabilitado], children: _jsx(Text, { size: "md", weight: it.value === value ? 600 : 400, children: it.label }) }, it.value))) }), _jsx(RecuoDaFolha, {})] })] }) }) })] }));
655
+ ], children: [_jsx(Text, { size: tam === "sm" ? "xs" : tam === "lg" ? "base" : "md", tone: escolhido ? "default" : "subtle", numberOfLines: 1, children: escolhido?.label ?? placeholder ?? "" }), chevron && _jsx(Icon, { name: chevron, size: "sm", color: peleDaMarca?.color ?? t.color.subtleForeground })] }), _jsx(ForaDaMarca, { children: _jsx(Modal, { visible: aberto, transparent: true, animationType: "slide", statusBarTranslucent: true, navigationBarTranslucent: true, onRequestClose: () => setAberto(false), children: _jsxs(View, { style: sf.fundoDaLista, children: [_jsx(Pressable, { style: sf.fundoDeToque, onPress: () => setAberto(false), accessible: false }), _jsxs(View, { style: sf.lista, children: [_jsx(ScrollView, { children: items.map((it) => (_jsxs(Pressable, { disabled: it.disabled, onPress: () => { onChange?.(it.value); setAberto(false); }, accessibilityRole: "menuitem", ...estadoAcessivel({ selected: it.value === value, disabled: !!it.disabled }), style: [sf.opcao, it.disabled && sf.desabilitado], children: [_jsx(Text, { size: "md", weight: 500, style: { flex: 1, minWidth: 0 }, children: it.label }), it.value === value && indicadorDaEscolhida(tf)] }, it.value))) }), _jsx(RecuoDaFolha, {})] })] }) }) })] }));
610
656
  }
611
657
  /**
612
658
  * A pilha de campos, com o respiro do `--space-5`.
package/dist/layout.d.ts CHANGED
@@ -92,7 +92,7 @@ export interface SeparatorProps extends ViewProps {
92
92
  * comportamento certo.
93
93
  */
94
94
  export declare function Separator({ orientation, label, style, ...rest }: SeparatorProps): React.JSX.Element;
95
- export type AureaCardVariant = "base" | "raised" | "interactive" | "inset" | "selected" | "danger" | "brand" | "contrast";
95
+ export type AureaCardVariant = "base" | "raised" | "interactive" | "inset" | "outline" | "selected" | "danger" | "brand" | "contrast";
96
96
  /** O tom do traço no topo (`accent`, MT-02, ADR-0063) — os MESMOS cinco da web. */
97
97
  export type AureaCardAccent = "brand" | "success" | "info" | "warning" | "danger";
98
98
  interface CardBase extends ViewProps {
package/dist/layout.js CHANGED
@@ -46,6 +46,9 @@ const folha = criarFolha((t) => ({
46
46
  card_interactive: { borderColor: t.color.borderStrong },
47
47
  // `inset` recessa em vez de flutuar — é o painel DENTRO de um card, e a ficha da web diz isso.
48
48
  card_inset: { backgroundColor: t.color.surfaceInset, borderColor: "transparent" },
49
+ // 0.32.0: `outline` é o cartão da cor de onde ele está, só com a linha em volta — o mesmo nome da
50
+ // web. Sem fundo; a borda, o raio e o recheio continuam os do `cardBase`.
51
+ card_outline: { backgroundColor: "transparent" },
49
52
  card_selected: peleDoCartaoEscolhido(t),
50
53
  card_danger: { borderColor: t.color.danger400 ?? t.color.destructive, backgroundColor: t.color.dangerBg },
51
54
  // O amarelo é o DE PREENCHER — `primary`, o mesmo do botão sólido. A borda some: uma superfície
package/dist/menu.d.ts ADDED
@@ -0,0 +1,99 @@
1
+ import * as React from "react";
2
+ import { type AureaIcon } from "./icon.js";
3
+ /** O lado do gatilho onde a camada abre. FÍSICO, como o `OverlaySide` da web. */
4
+ export type AureaOverlaySide = "top" | "right" | "bottom" | "left";
5
+ export interface PopoverProps {
6
+ /** O elemento que abre — um `Button`, um `IconButton`. O toque dele continua valendo. */
7
+ trigger: React.ReactElement;
8
+ /** O título: 18 em peso médio, anunciado como cabeçalho. */
9
+ title?: React.ReactNode;
10
+ /** O corpo. Texto solto sai como a descrição da referência: 16, esmaecido. */
11
+ children: React.ReactNode;
12
+ /** O lado do gatilho. Vira sozinho quando não cabe. */
13
+ side?: AureaOverlaySide;
14
+ /** A seta apontando para o gatilho. */
15
+ arrow?: boolean;
16
+ /** Só do nativo, como nos outros sobrepostos: `-fundo` e `-painel` marcam as duas peças. */
17
+ testID?: string;
18
+ }
19
+ /**
20
+ * A janela pequena presa ao gatilho: abre no toque, fecha no toque fora e no VOLTAR.
21
+ *
22
+ * ⚠ **Sem papel de diálogo**, pela razão medida no `overlays.tsx`: `role="dialog"` não mapeia em
23
+ * plataforma nenhuma. O confinamento do leitor de tela vem do `Modal`, que é uma janela do sistema;
24
+ * o nome vem do título, que é um cabeçalho. No iOS, o gesto de saída do VoiceOver fecha.
25
+ */
26
+ export declare function Popover({ trigger, title, children, side, arrow, testID }: PopoverProps): React.JSX.Element;
27
+ interface MenuItemBase {
28
+ label: React.ReactNode;
29
+ disabled?: boolean;
30
+ leadingIcon?: AureaIcon;
31
+ }
32
+ /** Item de ação. `kind` é opcional, como na web. */
33
+ export interface MenuActionDef extends MenuItemBase {
34
+ kind?: "item";
35
+ onClick?: () => void;
36
+ /**
37
+ * Só do nativo: o item que DESTRÓI dado, com o texto e o toque no tom de perigo — o `danger` do
38
+ * item da referência. A web ainda não tem o campo.
39
+ */
40
+ destructive?: boolean;
41
+ }
42
+ /** Item que abre um endereço — no aparelho, pelo `Linking`. Sem `disabled`, como na web. */
43
+ export interface MenuLinkDef {
44
+ kind: "link";
45
+ label: React.ReactNode;
46
+ leadingIcon?: AureaIcon;
47
+ href: string;
48
+ /** Fechar ao tocar. Padrão `true`, como na web. */
49
+ closeOnClick?: boolean;
50
+ }
51
+ /** Item que ALTERNA. Controlado (`checked`) ou não (`defaultChecked`). O menu fica aberto. */
52
+ export interface MenuCheckboxDef extends MenuItemBase {
53
+ kind: "checkbox";
54
+ checked?: boolean;
55
+ defaultChecked?: boolean;
56
+ onCheckedChange?: (checked: boolean) => void;
57
+ }
58
+ /** Opções mutuamente exclusivas. Escolher fecha o menu, como a escolha única da referência. */
59
+ export interface MenuRadioGroupDef {
60
+ kind: "radiogroup";
61
+ label?: React.ReactNode;
62
+ value?: string;
63
+ defaultValue?: string;
64
+ onValueChange?: (value: string) => void;
65
+ items: Array<{
66
+ value: string;
67
+ label: React.ReactNode;
68
+ disabled?: boolean;
69
+ leadingIcon?: AureaIcon;
70
+ }>;
71
+ }
72
+ /** Submenu: abre NO LUGAR, dentro do painel. `items` aninha a mesma união. */
73
+ export interface MenuSubmenuDef extends MenuItemBase {
74
+ kind: "submenu";
75
+ items: MenuEntry[];
76
+ }
77
+ /** Seção rotulada. O rótulo não é item: não se toca. */
78
+ export interface MenuGroupDef {
79
+ kind: "group";
80
+ label?: React.ReactNode;
81
+ items: MenuEntry[];
82
+ }
83
+ export type MenuEntry = MenuActionDef | MenuLinkDef | MenuCheckboxDef | MenuRadioGroupDef | MenuSubmenuDef | MenuGroupDef | "separator";
84
+ export interface DropdownMenuProps {
85
+ trigger: React.ReactElement;
86
+ items: MenuEntry[];
87
+ side?: AureaOverlaySide;
88
+ /** O nome do menu para o leitor de tela. */
89
+ label?: string;
90
+ testID?: string;
91
+ }
92
+ /**
93
+ * O menu de comandos preso ao gatilho.
94
+ *
95
+ * O estado de quem alterna sem ser controlado (`defaultChecked`, `defaultValue`) mora AQUI, e não no
96
+ * painel: o painel desmonta ao fechar, e o item marcado tem de continuar marcado na próxima vez.
97
+ */
98
+ export declare function DropdownMenu({ trigger, items, side, label, testID }: DropdownMenuProps): React.JSX.Element;
99
+ export {};