@innovui/react-native 0.0.0-stage → 0.1.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.
Files changed (204) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +57 -2
  3. package/dist/components/_shared/anchored-overlay.js +74 -0
  4. package/dist/components/_shared/anchored-overlay.js.map +1 -0
  5. package/dist/components/_shared/backdrop.js +25 -0
  6. package/dist/components/_shared/backdrop.js.map +1 -0
  7. package/dist/components/_shared/overlay-anchor.js +31 -0
  8. package/dist/components/_shared/overlay-anchor.js.map +1 -0
  9. package/dist/components/_shared/overlay-state.js +31 -0
  10. package/dist/components/_shared/overlay-state.js.map +1 -0
  11. package/dist/components/_shared/status.d.ts +5 -0
  12. package/dist/components/_shared/status.js +98 -0
  13. package/dist/components/_shared/status.js.map +1 -0
  14. package/dist/components/_shared/use-token.js +15 -0
  15. package/dist/components/_shared/use-token.js.map +1 -0
  16. package/dist/components/accordion/accordion.d.ts +43 -0
  17. package/dist/components/accordion/accordion.js +90 -0
  18. package/dist/components/accordion/accordion.js.map +1 -0
  19. package/dist/components/accordion/index.d.ts +2 -0
  20. package/dist/components/alert/alert.d.ts +25 -0
  21. package/dist/components/alert/alert.js +61 -0
  22. package/dist/components/alert/alert.js.map +1 -0
  23. package/dist/components/alert/index.d.ts +2 -0
  24. package/dist/components/avatar/avatar.d.ts +17 -0
  25. package/dist/components/avatar/avatar.js +51 -0
  26. package/dist/components/avatar/avatar.js.map +1 -0
  27. package/dist/components/avatar/index.d.ts +2 -0
  28. package/dist/components/badge/badge.d.ts +16 -0
  29. package/dist/components/badge/badge.js +57 -0
  30. package/dist/components/badge/badge.js.map +1 -0
  31. package/dist/components/badge/index.d.ts +2 -0
  32. package/dist/components/banner/banner.d.ts +23 -0
  33. package/dist/components/banner/banner.js +50 -0
  34. package/dist/components/banner/banner.js.map +1 -0
  35. package/dist/components/banner/index.d.ts +2 -0
  36. package/dist/components/breadcrumb/breadcrumb.d.ts +22 -0
  37. package/dist/components/breadcrumb/breadcrumb.js +54 -0
  38. package/dist/components/breadcrumb/breadcrumb.js.map +1 -0
  39. package/dist/components/breadcrumb/index.d.ts +2 -0
  40. package/dist/components/button/button.d.ts +21 -0
  41. package/dist/components/button/button.js +89 -0
  42. package/dist/components/button/button.js.map +1 -0
  43. package/dist/components/button/index.d.ts +2 -0
  44. package/dist/components/card/card.d.ts +17 -0
  45. package/dist/components/card/card.js +28 -0
  46. package/dist/components/card/card.js.map +1 -0
  47. package/dist/components/card/index.d.ts +2 -0
  48. package/dist/components/checkbox/checkbox.d.ts +18 -0
  49. package/dist/components/checkbox/checkbox.js +64 -0
  50. package/dist/components/checkbox/checkbox.js.map +1 -0
  51. package/dist/components/checkbox/index.d.ts +2 -0
  52. package/dist/components/combobox/combobox.d.ts +40 -0
  53. package/dist/components/combobox/combobox.js +101 -0
  54. package/dist/components/combobox/combobox.js.map +1 -0
  55. package/dist/components/combobox/index.d.ts +2 -0
  56. package/dist/components/container/container.d.ts +17 -0
  57. package/dist/components/container/container.js +42 -0
  58. package/dist/components/container/container.js.map +1 -0
  59. package/dist/components/container/index.d.ts +2 -0
  60. package/dist/components/date-picker/calendar.d.ts +38 -0
  61. package/dist/components/date-picker/calendar.js +137 -0
  62. package/dist/components/date-picker/calendar.js.map +1 -0
  63. package/dist/components/date-picker/date-picker.d.ts +24 -0
  64. package/dist/components/date-picker/date-picker.js +52 -0
  65. package/dist/components/date-picker/date-picker.js.map +1 -0
  66. package/dist/components/date-picker/date-presets.d.ts +15 -0
  67. package/dist/components/date-picker/date-presets.js +52 -0
  68. package/dist/components/date-picker/date-presets.js.map +1 -0
  69. package/dist/components/date-picker/date-range-picker.d.ts +24 -0
  70. package/dist/components/date-picker/date-range-picker.js +78 -0
  71. package/dist/components/date-picker/date-range-picker.js.map +1 -0
  72. package/dist/components/date-picker/date-sheet.js +33 -0
  73. package/dist/components/date-picker/date-sheet.js.map +1 -0
  74. package/dist/components/date-picker/date-trigger.js +14 -0
  75. package/dist/components/date-picker/date-trigger.js.map +1 -0
  76. package/dist/components/date-picker/dates.d.ts +8 -0
  77. package/dist/components/date-picker/dates.js +40 -0
  78. package/dist/components/date-picker/dates.js.map +1 -0
  79. package/dist/components/date-picker/index.d.ts +6 -0
  80. package/dist/components/divider/divider.d.ts +11 -0
  81. package/dist/components/divider/divider.js +16 -0
  82. package/dist/components/divider/divider.js.map +1 -0
  83. package/dist/components/divider/index.d.ts +2 -0
  84. package/dist/components/drawer/drawer.d.ts +34 -0
  85. package/dist/components/drawer/drawer.js +144 -0
  86. package/dist/components/drawer/drawer.js.map +1 -0
  87. package/dist/components/drawer/index.d.ts +2 -0
  88. package/dist/components/field/field.d.ts +31 -0
  89. package/dist/components/field/field.js +52 -0
  90. package/dist/components/field/field.js.map +1 -0
  91. package/dist/components/field/index.d.ts +2 -0
  92. package/dist/components/grid/grid.d.ts +35 -0
  93. package/dist/components/grid/grid.js +64 -0
  94. package/dist/components/grid/grid.js.map +1 -0
  95. package/dist/components/grid/index.d.ts +2 -0
  96. package/dist/components/icon-button/icon-button.d.ts +23 -0
  97. package/dist/components/icon-button/icon-button.js +48 -0
  98. package/dist/components/icon-button/icon-button.js.map +1 -0
  99. package/dist/components/icon-button/index.d.ts +2 -0
  100. package/dist/components/input/index.d.ts +2 -0
  101. package/dist/components/input/input.d.ts +22 -0
  102. package/dist/components/input/input.js +86 -0
  103. package/dist/components/input/input.js.map +1 -0
  104. package/dist/components/link/index.d.ts +2 -0
  105. package/dist/components/link/link.d.ts +13 -0
  106. package/dist/components/link/link.js +20 -0
  107. package/dist/components/link/link.js.map +1 -0
  108. package/dist/components/list/index.d.ts +2 -0
  109. package/dist/components/list/list.d.ts +26 -0
  110. package/dist/components/list/list.js +45 -0
  111. package/dist/components/list/list.js.map +1 -0
  112. package/dist/components/menu/index.d.ts +2 -0
  113. package/dist/components/menu/menu.d.ts +47 -0
  114. package/dist/components/menu/menu.js +81 -0
  115. package/dist/components/menu/menu.js.map +1 -0
  116. package/dist/components/modal/index.d.ts +2 -0
  117. package/dist/components/modal/modal.d.ts +41 -0
  118. package/dist/components/modal/modal.js +92 -0
  119. package/dist/components/modal/modal.js.map +1 -0
  120. package/dist/components/pagination/index.d.ts +2 -0
  121. package/dist/components/pagination/pagination.d.ts +21 -0
  122. package/dist/components/pagination/pagination.js +99 -0
  123. package/dist/components/pagination/pagination.js.map +1 -0
  124. package/dist/components/popover/index.d.ts +2 -0
  125. package/dist/components/popover/popover.d.ts +29 -0
  126. package/dist/components/popover/popover.js +54 -0
  127. package/dist/components/popover/popover.js.map +1 -0
  128. package/dist/components/radio/index.d.ts +2 -0
  129. package/dist/components/radio/radio.d.ts +16 -0
  130. package/dist/components/radio/radio.js +37 -0
  131. package/dist/components/radio/radio.js.map +1 -0
  132. package/dist/components/segmented-control/index.d.ts +2 -0
  133. package/dist/components/segmented-control/segmented-control.d.ts +23 -0
  134. package/dist/components/segmented-control/segmented-control.js +53 -0
  135. package/dist/components/segmented-control/segmented-control.js.map +1 -0
  136. package/dist/components/select/field-trigger.d.ts +6 -0
  137. package/dist/components/select/field-trigger.js +90 -0
  138. package/dist/components/select/field-trigger.js.map +1 -0
  139. package/dist/components/select/index.d.ts +2 -0
  140. package/dist/components/select/select.d.ts +35 -0
  141. package/dist/components/select/select.js +98 -0
  142. package/dist/components/select/select.js.map +1 -0
  143. package/dist/components/skeleton/index.d.ts +2 -0
  144. package/dist/components/skeleton/skeleton.d.ts +17 -0
  145. package/dist/components/skeleton/skeleton.js +53 -0
  146. package/dist/components/skeleton/skeleton.js.map +1 -0
  147. package/dist/components/slider/index.d.ts +2 -0
  148. package/dist/components/slider/slider.d.ts +26 -0
  149. package/dist/components/slider/slider.js +102 -0
  150. package/dist/components/slider/slider.js.map +1 -0
  151. package/dist/components/spinner/index.d.ts +2 -0
  152. package/dist/components/spinner/spinner.d.ts +13 -0
  153. package/dist/components/spinner/spinner.js +47 -0
  154. package/dist/components/spinner/spinner.js.map +1 -0
  155. package/dist/components/stack/index.d.ts +2 -0
  156. package/dist/components/stack/stack.d.ts +17 -0
  157. package/dist/components/stack/stack.js +60 -0
  158. package/dist/components/stack/stack.js.map +1 -0
  159. package/dist/components/stepper/index.d.ts +2 -0
  160. package/dist/components/stepper/stepper.d.ts +20 -0
  161. package/dist/components/stepper/stepper.js +61 -0
  162. package/dist/components/stepper/stepper.js.map +1 -0
  163. package/dist/components/switch/index.d.ts +2 -0
  164. package/dist/components/switch/switch.d.ts +15 -0
  165. package/dist/components/switch/switch.js +51 -0
  166. package/dist/components/switch/switch.js.map +1 -0
  167. package/dist/components/table/index.d.ts +2 -0
  168. package/dist/components/table/table.d.ts +42 -0
  169. package/dist/components/table/table.js +83 -0
  170. package/dist/components/table/table.js.map +1 -0
  171. package/dist/components/tabs/index.d.ts +2 -0
  172. package/dist/components/tabs/tabs.d.ts +35 -0
  173. package/dist/components/tabs/tabs.js +82 -0
  174. package/dist/components/tabs/tabs.js.map +1 -0
  175. package/dist/components/tag/index.d.ts +2 -0
  176. package/dist/components/tag/tag.d.ts +18 -0
  177. package/dist/components/tag/tag.js +37 -0
  178. package/dist/components/tag/tag.js.map +1 -0
  179. package/dist/components/textarea/index.d.ts +2 -0
  180. package/dist/components/textarea/textarea.d.ts +16 -0
  181. package/dist/components/textarea/textarea.js +46 -0
  182. package/dist/components/textarea/textarea.js.map +1 -0
  183. package/dist/components/toast/index.d.ts +2 -0
  184. package/dist/components/toast/toast.d.ts +41 -0
  185. package/dist/components/toast/toast.js +167 -0
  186. package/dist/components/toast/toast.js.map +1 -0
  187. package/dist/components/tooltip/index.d.ts +2 -0
  188. package/dist/components/tooltip/tooltip.d.ts +28 -0
  189. package/dist/components/tooltip/tooltip.js +62 -0
  190. package/dist/components/tooltip/tooltip.js.map +1 -0
  191. package/dist/components/upload/index.d.ts +2 -0
  192. package/dist/components/upload/upload.d.ts +65 -0
  193. package/dist/components/upload/upload.js +130 -0
  194. package/dist/components/upload/upload.js.map +1 -0
  195. package/dist/index.d.ts +87 -0
  196. package/dist/index.js +46 -0
  197. package/dist/tokens/generated.d.ts +371 -0
  198. package/dist/tokens/generated.js +373 -0
  199. package/dist/tokens/generated.js.map +1 -0
  200. package/dist/utils/cn.d.ts +6 -0
  201. package/dist/utils/cn.js +11 -0
  202. package/dist/utils/cn.js.map +1 -0
  203. package/package.json +61 -3
  204. package/theme.css +191 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 InnovTech Developers
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,58 @@
1
- # Temporary Holding Version
1
+ # @innovui/react-native
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Componentes React Native do design system InnovUI, estilizados com [Uniwind](https://docs.uniwind.dev) (Tailwind v4 para React Native). Os mesmos componentes do `@innovui/react`, adaptados à plataforma.
4
+
5
+ ## Instalação
6
+
7
+ ```bash
8
+ pnpm add @innovui/react-native uniwind tailwindcss react-native-svg
9
+ ```
10
+
11
+ No app, siga o [quickstart do Uniwind](https://docs.uniwind.dev/quickstart) (`withUniwindConfig` no `metro.config.js`) e, no `global.css`, importe os tokens e aponte o Tailwind para as classes da lib:
12
+
13
+ ```css
14
+ @import 'tailwindcss';
15
+ @import 'uniwind';
16
+ @import '@innovui/react-native/theme.css';
17
+
18
+ @source '../node_modules/@innovui/react-native/dist';
19
+ ```
20
+
21
+ O `@source` é necessário porque o Tailwind só gera as classes que encontra nos arquivos escaneados (num monorepo pnpm, aponte para o caminho real do pacote).
22
+
23
+ ```tsx
24
+ import { Button } from '@innovui/react-native'
25
+
26
+ export function App() {
27
+ return <Button onPress={() => {}}>Salvar</Button>
28
+ }
29
+ ```
30
+
31
+ ## Tema claro e escuro
32
+
33
+ `theme.css` define todos os tokens do `tokens.css` como variáveis por tema (`light` e `dark`); cada um vira utilitário (`--color-button-primary-bg` → `bg-button-primary-bg`), inclusive para o seu código. Troque o tema com o Uniwind: `Uniwind.setTheme('light' | 'dark' | 'system')`.
34
+
35
+ ## Diferenças em relação ao `@innovui/react`
36
+
37
+ - **Texto:** no native o texto não herda cor nem tamanho; os componentes aplicam `Text` por conta própria. Onde o web aceita `ReactNode`, alguns componentes aceitam só `string` (Badge, Tag, Alert `title`, itens de Menu...).
38
+ - **Eventos e acessibilidade:** `onClick` vira `onPress`; `aria-*` vira `accessibility*`. Não existe hover nem foco por teclado.
39
+ - **Sem `asChild`:** gatilhos de Modal, Drawer, Popover, Menu e Tooltip recebem o elemento clonado com `onPress` (o Tooltip abre no `onLongPress`).
40
+ - **Overlays:** Modal, Drawer, Popover, Menu, Select e Combobox usam o `Modal` nativo do RN. Sem blur no backdrop. Envolva o app em `ToastProvider` para usar `useToast()`.
41
+ - **Upload:** o RN não tem `<input type="file">`. O `Upload` é controlado: o consumidor abre o seletor (ex. `expo-document-picker`) em `onPick` e passa a lista em `files`.
42
+ - **Grid e Table:** feitos com Views e flexbox (sem CSS grid). Breakpoints vêm de `useWindowDimensions`.
43
+ - **Sombras no Android:** `shadow-*` não gera `elevation`.
44
+
45
+ ## Decisões
46
+
47
+ - **Uniwind em vez de `StyleSheet`:** os componentes usam `className`, como no `@innovui/react`, e o consumidor pode sobrescrever classes (`<Button className="h-20" />`, resolvido com `tailwind-merge`). Exige `react-native >= 0.81` e Metro.
48
+ - **Tokens:** `theme.css` e `src/tokens/generated.ts` (para usos fora de `className`) são gerados de `packages/react/src/styles/tokens.css`. Rode `pnpm --filter @innovui/react-native generate:tokens` depois de mudar os tokens; `pnpm test` falha se estiverem defasados.
49
+ - **API:** igual à do `@innovui/react` sempre que possível (`variant`, `size`, `loading`, `leadingIcon`, `trailingIcon`). Diferenças vêm da plataforma (`onPress` no lugar de `onClick`; o hover vira `active:`).
50
+ - **Testes:** o Jest não passa pelo Metro, então os testes conferem as classes aplicadas, não os estilos resolvidos. A geração dos estilos foi verificada com um bundle real do Metro.
51
+
52
+ ## Desenvolvimento
53
+
54
+ ```bash
55
+ pnpm --filter @innovui/react-native test
56
+ pnpm --filter @innovui/react-native typecheck
57
+ pnpm --filter @innovui/react-native build
58
+ ```
@@ -0,0 +1,74 @@
1
+ import { useCallback, useRef, useState } from "react";
2
+ import { Modal, Pressable, View, useWindowDimensions } from "react-native";
3
+ import { jsx } from "react/jsx-runtime";
4
+ //#region src/components/_shared/anchored-overlay.tsx
5
+ /**
6
+ * Medição de um gatilho para ancorar um overlay (Popover, Menu, Select, Combobox, DatePicker,
7
+ * Tooltip). `open()` mede o gatilho na janela e só então abre.
8
+ *
9
+ * const { ref, rect, visible, open, close } = useAnchor()
10
+ * <View ref={ref} collapsable={false}> ...gatilho... </View>
11
+ * <AnchoredOverlay visible={visible} rect={rect} onRequestClose={close}> ... </AnchoredOverlay>
12
+ */
13
+ function useAnchor() {
14
+ const ref = useRef(null);
15
+ const [rect, setRect] = useState(null);
16
+ const [visible, setVisible] = useState(false);
17
+ return {
18
+ ref,
19
+ rect,
20
+ visible,
21
+ open: useCallback(() => {
22
+ const node = ref.current;
23
+ if (!node) return;
24
+ node.measureInWindow((x, y, width, height) => {
25
+ setRect({
26
+ x,
27
+ y,
28
+ width,
29
+ height
30
+ });
31
+ setVisible(true);
32
+ });
33
+ }, []),
34
+ close: useCallback(() => setVisible(false), [])
35
+ };
36
+ }
37
+ /** Overlay transparente, posicionado junto ao gatilho, dentro de um Modal nativo (fica acima de tudo). */
38
+ function AnchoredOverlay({ visible, rect, onRequestClose, children, placement = "bottom", offset = 4, matchAnchorWidth = false, estimatedHeight = 320, dismissOnPressOutside = true, className, testID }) {
39
+ const { width: windowWidth, height: windowHeight } = useWindowDimensions();
40
+ if (!rect) return null;
41
+ const spaceBelow = windowHeight - (rect.y + rect.height);
42
+ const spaceAbove = rect.y;
43
+ const goTop = placement === "top" ? spaceAbove >= estimatedHeight || spaceAbove >= spaceBelow : spaceBelow < estimatedHeight && spaceAbove > spaceBelow;
44
+ const left = Math.max(8, Math.min(rect.x, windowWidth - 8 - (matchAnchorWidth ? rect.width : 0)));
45
+ return /* @__PURE__ */ jsx(Modal, {
46
+ transparent: true,
47
+ visible,
48
+ animationType: "fade",
49
+ onRequestClose,
50
+ statusBarTranslucent: true,
51
+ children: /* @__PURE__ */ jsx(Pressable, {
52
+ accessible: false,
53
+ className: "flex-1",
54
+ onPress: dismissOnPressOutside ? onRequestClose : void 0,
55
+ testID: testID ? `${testID}-backdrop` : void 0,
56
+ children: /* @__PURE__ */ jsx(View, {
57
+ testID,
58
+ className,
59
+ style: {
60
+ position: "absolute",
61
+ left,
62
+ ...goTop ? { bottom: windowHeight - rect.y + offset } : { top: rect.y + rect.height + offset },
63
+ ...matchAnchorWidth ? { width: rect.width } : null
64
+ },
65
+ onStartShouldSetResponder: () => true,
66
+ children
67
+ })
68
+ })
69
+ });
70
+ }
71
+ //#endregion
72
+ export { AnchoredOverlay, useAnchor };
73
+
74
+ //# sourceMappingURL=anchored-overlay.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"anchored-overlay.js","names":[],"sources":["../../../src/components/_shared/anchored-overlay.tsx"],"sourcesContent":["/// <reference types=\"uniwind/types\" />\n\nimport { type ReactNode, type RefObject, useCallback, useRef, useState } from 'react'\nimport { Modal, Pressable, useWindowDimensions, View } from 'react-native'\n\nexport type Placement = 'bottom' | 'top'\ntype Rect = { x: number; y: number; width: number; height: number }\n\n/**\n * Medição de um gatilho para ancorar um overlay (Popover, Menu, Select, Combobox, DatePicker,\n * Tooltip). `open()` mede o gatilho na janela e só então abre.\n *\n * const { ref, rect, visible, open, close } = useAnchor()\n * <View ref={ref} collapsable={false}> ...gatilho... </View>\n * <AnchoredOverlay visible={visible} rect={rect} onRequestClose={close}> ... </AnchoredOverlay>\n */\nexport function useAnchor() {\n const ref = useRef<View>(null)\n const [rect, setRect] = useState<Rect | null>(null)\n const [visible, setVisible] = useState(false)\n\n const open = useCallback(() => {\n const node = ref.current\n if (!node) return\n node.measureInWindow((x, y, width, height) => {\n setRect({ x, y, width, height })\n setVisible(true)\n })\n }, [])\n const close = useCallback(() => setVisible(false), [])\n\n return { ref: ref as RefObject<View>, rect, visible, open, close }\n}\n\nexport type AnchoredOverlayProps = {\n visible: boolean\n rect: Rect | null\n onRequestClose: () => void\n children: ReactNode\n /** Lado preferido; inverte se não couber. Padrão: `bottom`. */\n placement?: Placement\n /** Distância do gatilho. Padrão: 4. */\n offset?: number\n /** Largura do conteúdo: `anchor` iguala à do gatilho (Select, Combobox). Padrão: automática. */\n matchAnchorWidth?: boolean\n /** Altura máxima estimada do conteúdo, para decidir a inversão. Padrão: 320. */\n estimatedHeight?: number\n /** Toque fora fecha. Padrão: true. */\n dismissOnPressOutside?: boolean\n className?: string\n testID?: string\n}\n\n/** Overlay transparente, posicionado junto ao gatilho, dentro de um Modal nativo (fica acima de tudo). */\nexport function AnchoredOverlay({\n visible,\n rect,\n onRequestClose,\n children,\n placement = 'bottom',\n offset = 4,\n matchAnchorWidth = false,\n estimatedHeight = 320,\n dismissOnPressOutside = true,\n className,\n testID,\n}: AnchoredOverlayProps) {\n const { width: windowWidth, height: windowHeight } = useWindowDimensions()\n if (!rect) return null\n\n const spaceBelow = windowHeight - (rect.y + rect.height)\n const spaceAbove = rect.y\n const wantsTop = placement === 'top'\n const goTop = wantsTop\n ? spaceAbove >= estimatedHeight || spaceAbove >= spaceBelow\n : spaceBelow < estimatedHeight && spaceAbove > spaceBelow\n\n const left = Math.max(8, Math.min(rect.x, windowWidth - 8 - (matchAnchorWidth ? rect.width : 0)))\n\n return (\n <Modal\n transparent\n visible={visible}\n animationType=\"fade\"\n onRequestClose={onRequestClose}\n statusBarTranslucent\n >\n <Pressable\n accessible={false}\n className=\"flex-1\"\n onPress={dismissOnPressOutside ? onRequestClose : undefined}\n testID={testID ? `${testID}-backdrop` : undefined}\n >\n <View\n testID={testID}\n className={className}\n style={{\n position: 'absolute',\n left,\n ...(goTop\n ? { bottom: windowHeight - rect.y + offset }\n : { top: rect.y + rect.height + offset }),\n ...(matchAnchorWidth ? { width: rect.width } : null),\n }}\n // Toques dentro do conteúdo não chegam ao backdrop.\n onStartShouldSetResponder={() => true}\n >\n {children}\n </View>\n </Pressable>\n </Modal>\n )\n}\n"],"mappings":";;;;;;;;;;;;AAgBA,SAAgB,YAAY;CAC1B,MAAM,MAAM,OAAa,IAAI;CAC7B,MAAM,CAAC,MAAM,WAAW,SAAsB,IAAI;CAClD,MAAM,CAAC,SAAS,cAAc,SAAS,KAAK;CAY5C,OAAO;EAAO;EAAwB;EAAM;EAAS,MAVxC,kBAAkB;GAC7B,MAAM,OAAO,IAAI;GACjB,IAAI,CAAC,MAAM;GACX,KAAK,iBAAiB,GAAG,GAAG,OAAO,WAAW;IAC5C,QAAQ;KAAE;KAAG;KAAG;KAAO;IAAO,CAAC;IAC/B,WAAW,IAAI;GACjB,CAAC;EACH,GAAG,CAAC,CAGoD;EAAG,OAF7C,kBAAkB,WAAW,KAAK,GAAG,CAAC,CAEW;CAAE;AACnE;;AAsBA,SAAgB,gBAAgB,EAC9B,SACA,MACA,gBACA,UACA,YAAY,UACZ,SAAS,GACT,mBAAmB,OACnB,kBAAkB,KAClB,wBAAwB,MACxB,WACA,UACuB;CACvB,MAAM,EAAE,OAAO,aAAa,QAAQ,iBAAiB,oBAAoB;CACzE,IAAI,CAAC,MAAM,OAAO;CAElB,MAAM,aAAa,gBAAgB,KAAK,IAAI,KAAK;CACjD,MAAM,aAAa,KAAK;CAExB,MAAM,QADW,cAAc,QAE3B,cAAc,mBAAmB,cAAc,aAC/C,aAAa,mBAAmB,aAAa;CAEjD,MAAM,OAAO,KAAK,IAAI,GAAG,KAAK,IAAI,KAAK,GAAG,cAAc,KAAK,mBAAmB,KAAK,QAAQ,EAAE,CAAC;CAEhG,OACE,oBAAC,OAAD;EACE,aAAA;EACS;EACT,eAAc;EACE;EAChB,sBAAA;EAEA,UAAA,oBAAC,WAAD;GACE,YAAY;GACZ,WAAU;GACV,SAAS,wBAAwB,iBAAiB,KAAA;GAClD,QAAQ,SAAS,GAAG,OAAO,aAAa,KAAA;GAExC,UAAA,oBAAC,MAAD;IACU;IACG;IACX,OAAO;KACL,UAAU;KACV;KACA,GAAI,QACA,EAAE,QAAQ,eAAe,KAAK,IAAI,OAAO,IACzC,EAAE,KAAK,KAAK,IAAI,KAAK,SAAS,OAAO;KACzC,GAAI,mBAAmB,EAAE,OAAO,KAAK,MAAM,IAAI;IACjD;IAEA,iCAAiC;IAEhC;GACG,CAAA;EACG,CAAA;CACN,CAAA;AAEX"}
@@ -0,0 +1,25 @@
1
+ import { overlayOpacity } from "../../tokens/generated.js";
2
+ import { Pressable } from "react-native";
3
+ import { jsx } from "react/jsx-runtime";
4
+ import { useUniwind } from "uniwind";
5
+ //#region src/components/_shared/backdrop.tsx
6
+ /**
7
+ * Fundo escurecido de Modal e Drawer: `surface/overlay` na opacidade do tema (50% no claro, 70% no
8
+ * escuro). Sem blur (o React Native não tem backdrop-filter sem dependência nativa extra).
9
+ * Toque no fundo chama `onPress`.
10
+ */
11
+ function Backdrop({ onPress, testID }) {
12
+ const { theme } = useUniwind();
13
+ const opacity = theme === "dark" ? overlayOpacity.dark : overlayOpacity.light;
14
+ return /* @__PURE__ */ jsx(Pressable, {
15
+ accessible: false,
16
+ testID,
17
+ onPress,
18
+ className: "absolute inset-0 bg-surface-overlay",
19
+ style: { opacity }
20
+ });
21
+ }
22
+ //#endregion
23
+ export { Backdrop };
24
+
25
+ //# sourceMappingURL=backdrop.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"backdrop.js","names":[],"sources":["../../../src/components/_shared/backdrop.tsx"],"sourcesContent":["/// <reference types=\"uniwind/types\" />\n\nimport { Pressable } from 'react-native'\nimport { useUniwind } from 'uniwind'\nimport { overlayOpacity } from '../../tokens/generated'\n\n/**\n * Fundo escurecido de Modal e Drawer: `surface/overlay` na opacidade do tema (50% no claro, 70% no\n * escuro). Sem blur (o React Native não tem backdrop-filter sem dependência nativa extra).\n * Toque no fundo chama `onPress`.\n */\nexport function Backdrop({ onPress, testID }: { onPress?: () => void; testID?: string }) {\n const { theme } = useUniwind()\n const opacity = theme === 'dark' ? overlayOpacity.dark : overlayOpacity.light\n return (\n <Pressable\n accessible={false}\n testID={testID}\n onPress={onPress}\n className=\"absolute inset-0 bg-surface-overlay\"\n style={{ opacity }}\n />\n )\n}\n"],"mappings":";;;;;;;;;;AAWA,SAAgB,SAAS,EAAE,SAAS,UAAqD;CACvF,MAAM,EAAE,UAAU,WAAW;CAC7B,MAAM,UAAU,UAAU,SAAS,eAAe,OAAO,eAAe;CACxE,OACE,oBAAC,WAAD;EACE,YAAY;EACJ;EACC;EACT,WAAU;EACV,OAAO,EAAE,QAAQ;CAClB,CAAA;AAEL"}
@@ -0,0 +1,31 @@
1
+ import { useAnchor } from "./anchored-overlay.js";
2
+ import { injectHandler } from "./overlay-state.js";
3
+ import { useEffect } from "react";
4
+ import { View } from "react-native";
5
+ import { jsx } from "react/jsx-runtime";
6
+ //#region src/components/_shared/overlay-anchor.tsx
7
+ /**
8
+ * Ancoragem controlada (uso interno de Popover, Menu e Tooltip): mede o gatilho sempre que
9
+ * `isOpen` vira `true`, venha a mudança do toque ou da prop `open`.
10
+ */
11
+ function useControlledAnchor(isOpen) {
12
+ const anchor = useAnchor();
13
+ const { open: measure } = anchor;
14
+ useEffect(() => {
15
+ if (isOpen) measure();
16
+ }, [isOpen, measure]);
17
+ return anchor;
18
+ }
19
+ /** Gatilho medível: `View` sem colapso em volta do filho, que recebe o handler. */
20
+ function AnchorTrigger({ anchorRef, children, event, onTrigger, extra }) {
21
+ return /* @__PURE__ */ jsx(View, {
22
+ ref: anchorRef,
23
+ collapsable: false,
24
+ className: "self-start",
25
+ children: injectHandler(children, event, onTrigger, extra)
26
+ });
27
+ }
28
+ //#endregion
29
+ export { AnchorTrigger, useControlledAnchor };
30
+
31
+ //# sourceMappingURL=overlay-anchor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"overlay-anchor.js","names":[],"sources":["../../../src/components/_shared/overlay-anchor.tsx"],"sourcesContent":["/// <reference types=\"uniwind/types\" />\n\nimport { type ReactNode, useEffect } from 'react'\nimport { View } from 'react-native'\nimport { useAnchor } from './anchored-overlay'\nimport { injectHandler } from './overlay-state'\n\n/**\n * Ancoragem controlada (uso interno de Popover, Menu e Tooltip): mede o gatilho sempre que\n * `isOpen` vira `true`, venha a mudança do toque ou da prop `open`.\n */\nexport function useControlledAnchor(isOpen: boolean) {\n const anchor = useAnchor()\n const { open: measure } = anchor\n useEffect(() => {\n if (isOpen) measure()\n }, [isOpen, measure])\n return anchor\n}\n\n/** Gatilho medível: `View` sem colapso em volta do filho, que recebe o handler. */\nexport function AnchorTrigger({\n anchorRef,\n children,\n event,\n onTrigger,\n extra,\n}: {\n anchorRef: ReturnType<typeof useAnchor>['ref']\n children: ReactNode\n event: 'onPress' | 'onLongPress'\n onTrigger: () => void\n extra?: Record<string, unknown>\n}) {\n return (\n <View ref={anchorRef} collapsable={false} className=\"self-start\">\n {injectHandler(children, event, onTrigger, extra)}\n </View>\n )\n}\n"],"mappings":";;;;;;;;;;AAWA,SAAgB,oBAAoB,QAAiB;CACnD,MAAM,SAAS,UAAU;CACzB,MAAM,EAAE,MAAM,YAAY;CAC1B,gBAAgB;EACd,IAAI,QAAQ,QAAQ;CACtB,GAAG,CAAC,QAAQ,OAAO,CAAC;CACpB,OAAO;AACT;;AAGA,SAAgB,cAAc,EAC5B,WACA,UACA,OACA,WACA,SAOC;CACD,OACE,oBAAC,MAAD;EAAM,KAAK;EAAW,aAAa;EAAO,WAAU;EACjD,UAAA,cAAc,UAAU,OAAO,WAAW,KAAK;CAC5C,CAAA;AAEV"}
@@ -0,0 +1,31 @@
1
+ import { cloneElement, isValidElement, useCallback, useState } from "react";
2
+ //#region src/components/_shared/overlay-state.ts
3
+ /** Estado aberto/fechado controlado (`open`) ou não (`defaultOpen`), como no Radix do web. */
4
+ function useOpenState({ open, defaultOpen = false, onOpenChange }) {
5
+ const [inner, setInner] = useState(defaultOpen);
6
+ const isControlled = open !== void 0;
7
+ return [isControlled ? open : inner, useCallback((next) => {
8
+ if (!isControlled) setInner(next);
9
+ onOpenChange?.(next);
10
+ }, [isControlled, onOpenChange])];
11
+ }
12
+ /**
13
+ * Equivalente do `asChild` do Radix: injeta um handler (`onPress`/`onLongPress`) no elemento
14
+ * filho, preservando o handler que ele já tinha.
15
+ */
16
+ function injectHandler(child, name, handler, extra) {
17
+ if (!isValidElement(child)) return child;
18
+ const element = child;
19
+ const original = element.props[name];
20
+ return cloneElement(element, {
21
+ ...extra,
22
+ [name]: (...args) => {
23
+ original?.(...args);
24
+ handler();
25
+ }
26
+ });
27
+ }
28
+ //#endregion
29
+ export { injectHandler, useOpenState };
30
+
31
+ //# sourceMappingURL=overlay-state.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"overlay-state.js","names":[],"sources":["../../../src/components/_shared/overlay-state.ts"],"sourcesContent":["import {\n cloneElement,\n isValidElement,\n type ReactElement,\n type ReactNode,\n useCallback,\n useState,\n} from 'react'\n\ntype OpenState = {\n open?: boolean\n defaultOpen?: boolean\n onOpenChange?: (open: boolean) => void\n}\n\n/** Estado aberto/fechado controlado (`open`) ou não (`defaultOpen`), como no Radix do web. */\nexport function useOpenState({ open, defaultOpen = false, onOpenChange }: OpenState) {\n const [inner, setInner] = useState(defaultOpen)\n const isControlled = open !== undefined\n const current = isControlled ? open : inner\n\n const setOpen = useCallback(\n (next: boolean) => {\n if (!isControlled) setInner(next)\n onOpenChange?.(next)\n },\n [isControlled, onOpenChange],\n )\n\n return [current, setOpen] as const\n}\n\n/**\n * Equivalente do `asChild` do Radix: injeta um handler (`onPress`/`onLongPress`) no elemento\n * filho, preservando o handler que ele já tinha.\n */\nexport function injectHandler(\n child: ReactNode,\n name: 'onPress' | 'onLongPress',\n handler: () => void,\n extra?: Record<string, unknown>,\n): ReactNode {\n if (!isValidElement(child)) return child\n const element = child as ReactElement<Record<string, unknown>>\n const original = element.props[name] as ((...args: unknown[]) => void) | undefined\n return cloneElement(element, {\n ...extra,\n [name]: (...args: unknown[]) => {\n original?.(...args)\n handler()\n },\n })\n}\n"],"mappings":";;;AAgBA,SAAgB,aAAa,EAAE,MAAM,cAAc,OAAO,gBAA2B;CACnF,MAAM,CAAC,OAAO,YAAY,SAAS,WAAW;CAC9C,MAAM,eAAe,SAAS,KAAA;CAW9B,OAAO,CAVS,eAAe,OAAO,OAEtB,aACb,SAAkB;EACjB,IAAI,CAAC,cAAc,SAAS,IAAI;EAChC,eAAe,IAAI;CACrB,GACA,CAAC,cAAc,YAAY,CAGN,CAAC;AAC1B;;;;;AAMA,SAAgB,cACd,OACA,MACA,SACA,OACW;CACX,IAAI,CAAC,eAAe,KAAK,GAAG,OAAO;CACnC,MAAM,UAAU;CAChB,MAAM,WAAW,QAAQ,MAAM;CAC/B,OAAO,aAAa,SAAS;EAC3B,GAAG;GACF,QAAQ,GAAG,SAAoB;GAC9B,WAAW,GAAG,IAAI;GAClB,QAAQ;EACV;CACF,CAAC;AACH"}
@@ -0,0 +1,5 @@
1
+ import "react-native-svg";
2
+ //#region src/components/_shared/status.d.ts
3
+ export type Status = 'success' | 'error' | 'warning' | 'info' | 'neutral';
4
+ //#endregion
5
+ //# sourceMappingURL=status.d.ts.map
@@ -0,0 +1,98 @@
1
+ import Svg, { Circle, Path } from "react-native-svg";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ //#region src/components/_shared/status.tsx
4
+ /** Só a cor de destaque (texto/ícone) por status. */
5
+ const statusText = {
6
+ success: "text-status-success-text",
7
+ error: "text-status-error-text",
8
+ warning: "text-status-warning-text",
9
+ info: "text-status-info-text",
10
+ neutral: "text-status-neutral-text"
11
+ };
12
+ /** Fundo por status, compartilhado por Alert e Banner (uso interno). */
13
+ const statusBg = {
14
+ success: "bg-status-success-bg",
15
+ error: "bg-status-error-bg",
16
+ warning: "bg-status-warning-bg",
17
+ info: "bg-status-info-bg",
18
+ neutral: "bg-status-neutral-bg"
19
+ };
20
+ /** Nome do token de cor do texto de cada status, para `useToken` (ícones SVG). */
21
+ const statusTextToken = {
22
+ success: "--color-status-success-text",
23
+ error: "--color-status-error-text",
24
+ warning: "--color-status-warning-text",
25
+ info: "--color-status-info-text",
26
+ neutral: "--color-status-neutral-text"
27
+ };
28
+ /** Erro e aviso interrompem o leitor de tela (alert); o resto é anunciado com calma. */
29
+ const statusRole = (status) => status === "error" || status === "warning" ? "alert" : void 0;
30
+ const strokeProps = {
31
+ fill: "none",
32
+ strokeWidth: 2,
33
+ strokeLinecap: "round",
34
+ strokeLinejoin: "round"
35
+ };
36
+ const statusGlyph = {
37
+ success: "M7.5 10L9.16667 11.6667L12.5 8.33333",
38
+ error: "M10 6.66667V10M10 13.3333H10.0083",
39
+ warning: "M10 6.66667V10M10 13.3333H10.0083",
40
+ info: "M10 10V13.3333M10 6.66667H10.0083",
41
+ neutral: "M10 10V13.3333M10 6.66667H10.0083"
42
+ };
43
+ /** Ícone semântico de 20px, na cor passada em `color`. */
44
+ function StatusIcon({ status, color, ...props }) {
45
+ return /* @__PURE__ */ jsxs(Svg, {
46
+ width: 20,
47
+ height: 20,
48
+ viewBox: "0 0 20 20",
49
+ stroke: color,
50
+ ...strokeProps,
51
+ ...props,
52
+ children: [/* @__PURE__ */ jsx(Circle, {
53
+ cx: "10",
54
+ cy: "10",
55
+ r: "7.3333"
56
+ }), /* @__PURE__ */ jsx(Path, { d: statusGlyph[status] })]
57
+ });
58
+ }
59
+ /** × de 16px. */
60
+ function CloseIcon({ color, ...props }) {
61
+ return /* @__PURE__ */ jsx(Svg, {
62
+ width: 16,
63
+ height: 16,
64
+ viewBox: "0 0 16 16",
65
+ stroke: color,
66
+ ...strokeProps,
67
+ ...props,
68
+ children: /* @__PURE__ */ jsx(Path, { d: "M12 4L4 12M4 4L12 12" })
69
+ });
70
+ }
71
+ /** ✓ de 16px. */
72
+ function CheckIcon({ color, ...props }) {
73
+ return /* @__PURE__ */ jsx(Svg, {
74
+ width: 16,
75
+ height: 16,
76
+ viewBox: "0 0 16 16",
77
+ stroke: color,
78
+ ...strokeProps,
79
+ ...props,
80
+ children: /* @__PURE__ */ jsx(Path, { d: "M13.3333 4L6 11.3333L2.66667 8" })
81
+ });
82
+ }
83
+ /** › de 12px. */
84
+ function ChevronRightIcon({ color, ...props }) {
85
+ return /* @__PURE__ */ jsx(Svg, {
86
+ width: 12,
87
+ height: 12,
88
+ viewBox: "0 0 12 12",
89
+ stroke: color,
90
+ ...strokeProps,
91
+ ...props,
92
+ children: /* @__PURE__ */ jsx(Path, { d: "M4.5 9L7.5 6L4.5 3" })
93
+ });
94
+ }
95
+ //#endregion
96
+ export { CheckIcon, ChevronRightIcon, CloseIcon, StatusIcon, statusBg, statusRole, statusText, statusTextToken };
97
+
98
+ //# sourceMappingURL=status.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"status.js","names":[],"sources":["../../../src/components/_shared/status.tsx"],"sourcesContent":["/// <reference types=\"uniwind/types\" />\n\nimport Svg, { Circle, Path, type SvgProps } from 'react-native-svg'\n\nexport type Status = 'success' | 'error' | 'warning' | 'info' | 'neutral'\n\n/** Só a cor de destaque (texto/ícone) por status. */\nexport const statusText: Record<Status, string> = {\n success: 'text-status-success-text',\n error: 'text-status-error-text',\n warning: 'text-status-warning-text',\n info: 'text-status-info-text',\n neutral: 'text-status-neutral-text',\n}\n\n/** Fundo por status, compartilhado por Alert e Banner (uso interno). */\nexport const statusBg: Record<Status, string> = {\n success: 'bg-status-success-bg',\n error: 'bg-status-error-bg',\n warning: 'bg-status-warning-bg',\n info: 'bg-status-info-bg',\n neutral: 'bg-status-neutral-bg',\n}\n\n/** Nome do token de cor do texto de cada status, para `useToken` (ícones SVG). */\nexport const statusTextToken = {\n success: '--color-status-success-text',\n error: '--color-status-error-text',\n warning: '--color-status-warning-text',\n info: '--color-status-info-text',\n neutral: '--color-status-neutral-text',\n} as const satisfies Record<Status, `--color-${string}`>\n\n/** Erro e aviso interrompem o leitor de tela (alert); o resto é anunciado com calma. */\nexport const statusRole = (status: Status) =>\n status === 'error' || status === 'warning' ? 'alert' : undefined\n\ntype IconProps = Omit<SvgProps, 'stroke' | 'fill' | 'width' | 'height'> & { color?: string }\n\nconst strokeProps = {\n fill: 'none',\n strokeWidth: 2,\n strokeLinecap: 'round',\n strokeLinejoin: 'round',\n} as const\n\nconst statusGlyph: Record<Status, string> = {\n success: 'M7.5 10L9.16667 11.6667L12.5 8.33333',\n error: 'M10 6.66667V10M10 13.3333H10.0083',\n warning: 'M10 6.66667V10M10 13.3333H10.0083',\n info: 'M10 10V13.3333M10 6.66667H10.0083',\n neutral: 'M10 10V13.3333M10 6.66667H10.0083',\n}\n\n/** Ícone semântico de 20px, na cor passada em `color`. */\nexport function StatusIcon({ status, color, ...props }: { status: Status } & IconProps) {\n return (\n <Svg width={20} height={20} viewBox=\"0 0 20 20\" stroke={color} {...strokeProps} {...props}>\n <Circle cx=\"10\" cy=\"10\" r=\"7.3333\" />\n <Path d={statusGlyph[status]} />\n </Svg>\n )\n}\n\n/** × de 16px. */\nexport function CloseIcon({ color, ...props }: IconProps) {\n return (\n <Svg width={16} height={16} viewBox=\"0 0 16 16\" stroke={color} {...strokeProps} {...props}>\n <Path d=\"M12 4L4 12M4 4L12 12\" />\n </Svg>\n )\n}\n\n/** ✓ de 16px. */\nexport function CheckIcon({ color, ...props }: IconProps) {\n return (\n <Svg width={16} height={16} viewBox=\"0 0 16 16\" stroke={color} {...strokeProps} {...props}>\n <Path d=\"M13.3333 4L6 11.3333L2.66667 8\" />\n </Svg>\n )\n}\n\n/** › de 12px. */\nexport function ChevronRightIcon({ color, ...props }: IconProps) {\n return (\n <Svg width={12} height={12} viewBox=\"0 0 12 12\" stroke={color} {...strokeProps} {...props}>\n <Path d=\"M4.5 9L7.5 6L4.5 3\" />\n </Svg>\n )\n}\n"],"mappings":";;;;AAOA,MAAa,aAAqC;CAChD,SAAS;CACT,OAAO;CACP,SAAS;CACT,MAAM;CACN,SAAS;AACX;;AAGA,MAAa,WAAmC;CAC9C,SAAS;CACT,OAAO;CACP,SAAS;CACT,MAAM;CACN,SAAS;AACX;;AAGA,MAAa,kBAAkB;CAC7B,SAAS;CACT,OAAO;CACP,SAAS;CACT,MAAM;CACN,SAAS;AACX;;AAGA,MAAa,cAAc,WACzB,WAAW,WAAW,WAAW,YAAY,UAAU,KAAA;AAIzD,MAAM,cAAc;CAClB,MAAM;CACN,aAAa;CACb,eAAe;CACf,gBAAgB;AAClB;AAEA,MAAM,cAAsC;CAC1C,SAAS;CACT,OAAO;CACP,SAAS;CACT,MAAM;CACN,SAAS;AACX;;AAGA,SAAgB,WAAW,EAAE,QAAQ,OAAO,GAAG,SAAyC;CACtF,OACE,qBAAC,KAAD;EAAK,OAAO;EAAI,QAAQ;EAAI,SAAQ;EAAY,QAAQ;EAAO,GAAI;EAAa,GAAI;EAApF,UAAA,CACE,oBAAC,QAAD;GAAQ,IAAG;GAAK,IAAG;GAAK,GAAE;EAAU,CAAA,GACpC,oBAAC,MAAD,EAAM,GAAG,YAAY,QAAU,CAAA,CAC5B;;AAET;;AAGA,SAAgB,UAAU,EAAE,OAAO,GAAG,SAAoB;CACxD,OACE,oBAAC,KAAD;EAAK,OAAO;EAAI,QAAQ;EAAI,SAAQ;EAAY,QAAQ;EAAO,GAAI;EAAa,GAAI;EAClF,UAAA,oBAAC,MAAD,EAAM,GAAE,uBAAwB,CAAA;CAC7B,CAAA;AAET;;AAGA,SAAgB,UAAU,EAAE,OAAO,GAAG,SAAoB;CACxD,OACE,oBAAC,KAAD;EAAK,OAAO;EAAI,QAAQ;EAAI,SAAQ;EAAY,QAAQ;EAAO,GAAI;EAAa,GAAI;EAClF,UAAA,oBAAC,MAAD,EAAM,GAAE,iCAAkC,CAAA;CACvC,CAAA;AAET;;AAGA,SAAgB,iBAAiB,EAAE,OAAO,GAAG,SAAoB;CAC/D,OACE,oBAAC,KAAD;EAAK,OAAO;EAAI,QAAQ;EAAI,SAAQ;EAAY,QAAQ;EAAO,GAAI;EAAa,GAAI;EAClF,UAAA,oBAAC,MAAD,EAAM,GAAE,qBAAsB,CAAA;CAC3B,CAAA;AAET"}
@@ -0,0 +1,15 @@
1
+ import { useCSSVariable } from "uniwind";
2
+ //#region src/components/_shared/use-token.ts
3
+ /**
4
+ * Valor resolvido de um token de cor (ex.: `--color-status-success-text`) no tema atual.
5
+ * Para o que não aceita className: cores de ícone SVG, `placeholderTextColor`, `selectionColor`,
6
+ * `trackColor` do Switch etc. Devolve `undefined` se o token não existir.
7
+ */
8
+ function useToken(name) {
9
+ const value = useCSSVariable(name);
10
+ return typeof value === "string" ? value : void 0;
11
+ }
12
+ //#endregion
13
+ export { useToken };
14
+
15
+ //# sourceMappingURL=use-token.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-token.js","names":[],"sources":["../../../src/components/_shared/use-token.ts"],"sourcesContent":["import { useCSSVariable } from 'uniwind'\n\n/**\n * Valor resolvido de um token de cor (ex.: `--color-status-success-text`) no tema atual.\n * Para o que não aceita className: cores de ícone SVG, `placeholderTextColor`, `selectionColor`,\n * `trackColor` do Switch etc. Devolve `undefined` se o token não existir.\n */\nexport function useToken(name: `--color-${string}`): string | undefined {\n const value = useCSSVariable(name)\n return typeof value === 'string' ? value : undefined\n}\n"],"mappings":";;;;;;;AAOA,SAAgB,SAAS,MAA+C;CACtE,MAAM,QAAQ,eAAe,IAAI;CACjC,OAAO,OAAO,UAAU,WAAW,QAAQ,KAAA;AAC7C"}
@@ -0,0 +1,43 @@
1
+ import { ReactNode } from "react";
2
+ import { PressableProps, ViewProps } from "react-native";
3
+ //#region src/components/accordion/accordion.d.ts
4
+ type AccordionBaseProps = Omit<ViewProps, 'children'> & {
5
+ className?: string;
6
+ children?: ReactNode;
7
+ };
8
+ export type AccordionProps = AccordionBaseProps & ({
9
+ /** Abre uma seção por vez. */
10
+ type: 'single';
11
+ /** Permite fechar a seção aberta (todas recolhidas). */
12
+ collapsible?: boolean;
13
+ value?: string;
14
+ defaultValue?: string;
15
+ /** Recebe o valor aberto, ou `''` quando tudo foi recolhido (igual ao web). */
16
+ onValueChange?: (value: string) => void;
17
+ } | {
18
+ /** Permite várias seções abertas. */
19
+ type: 'multiple';
20
+ value?: string[];
21
+ defaultValue?: string[];
22
+ onValueChange?: (value: string[]) => void;
23
+ });
24
+ /**
25
+ * Lista de seções que expandem e recolhem. `type="single"` abre uma por vez (com `collapsible`,
26
+ * dá para fechar todas); `type="multiple"` permite várias abertas. Controlado (`value`) ou não
27
+ * (`defaultValue`). A animação usa `LayoutAnimation`.
28
+ */
29
+ export declare function Accordion(props: AccordionProps): import("react").JSX.Element;
30
+ export type AccordionItemProps = Omit<ViewProps, 'children'> & {
31
+ className?: string;
32
+ /** Identifica a seção em `value`/`defaultValue` do `Accordion`. */
33
+ value: string;
34
+ /** Texto do cabeçalho: é o botão que expande a seção. */
35
+ title: ReactNode;
36
+ disabled?: boolean;
37
+ /** Repassado ao botão do cabeçalho (ex.: `testID`, `accessibilityLabel`). */
38
+ triggerProps?: Omit<PressableProps, 'children' | 'style' | 'onPress' | 'disabled'>;
39
+ children?: ReactNode;
40
+ };
41
+ export declare function AccordionItem({ className, value, title, disabled, triggerProps, children, ...props }: AccordionItemProps): import("react").JSX.Element;
42
+ //#endregion
43
+ //# sourceMappingURL=accordion.d.ts.map
@@ -0,0 +1,90 @@
1
+ import { cn } from "../../utils/cn.js";
2
+ import { ChevronRightIcon } from "../_shared/status.js";
3
+ import { useToken } from "../_shared/use-token.js";
4
+ import { createContext, useCallback, useContext, useMemo, useState } from "react";
5
+ import { LayoutAnimation, Platform, Pressable, Text, UIManager, View } from "react-native";
6
+ import { jsx, jsxs } from "react/jsx-runtime";
7
+ //#region src/components/accordion/accordion.tsx
8
+ if (Platform.OS === "android") UIManager.setLayoutAnimationEnabledExperimental?.(true);
9
+ const AccordionContext = createContext({
10
+ isOpen: () => false,
11
+ toggle: () => {}
12
+ });
13
+ const toArray = (value) => value === void 0 || value === "" ? [] : Array.isArray(value) ? value : [value];
14
+ /**
15
+ * Lista de seções que expandem e recolhem. `type="single"` abre uma por vez (com `collapsible`,
16
+ * dá para fechar todas); `type="multiple"` permite várias abertas. Controlado (`value`) ou não
17
+ * (`defaultValue`). A animação usa `LayoutAnimation`.
18
+ */
19
+ function Accordion(props) {
20
+ const { className, children, type, value, defaultValue, onValueChange, collapsible: collapsibleProp, ...rest } = props;
21
+ const collapsible = type === "single" ? Boolean(collapsibleProp) : true;
22
+ const [internal, setInternal] = useState(() => toArray(defaultValue));
23
+ const open = value !== void 0 ? toArray(value) : internal;
24
+ const toggle = useCallback((item) => {
25
+ let next;
26
+ if (open.includes(item)) {
27
+ if (type === "single" && !collapsible) return;
28
+ next = open.filter((v) => v !== item);
29
+ } else next = type === "single" ? [item] : [...open, item];
30
+ LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut);
31
+ if (value === void 0) setInternal(next);
32
+ if (type === "single") onValueChange?.(next[0] ?? "");
33
+ else onValueChange?.(next);
34
+ }, [
35
+ open,
36
+ type,
37
+ collapsible,
38
+ value,
39
+ onValueChange
40
+ ]);
41
+ const context = useMemo(() => ({
42
+ isOpen: (v) => open.includes(v),
43
+ toggle
44
+ }), [open, toggle]);
45
+ return /* @__PURE__ */ jsx(AccordionContext.Provider, {
46
+ value: context,
47
+ children: /* @__PURE__ */ jsx(View, {
48
+ className: cn("w-full", className),
49
+ ...rest,
50
+ children
51
+ })
52
+ });
53
+ }
54
+ function AccordionItem({ className, value, title, disabled = false, triggerProps, children, ...props }) {
55
+ const { isOpen, toggle } = useContext(AccordionContext);
56
+ const open = isOpen(value);
57
+ const iconColor = useToken("--color-text-secondary");
58
+ return /* @__PURE__ */ jsxs(View, {
59
+ className: cn("border-b border-border-default", className),
60
+ ...props,
61
+ children: [/* @__PURE__ */ jsxs(Pressable, {
62
+ accessibilityRole: "button",
63
+ accessibilityState: {
64
+ expanded: open,
65
+ disabled
66
+ },
67
+ disabled,
68
+ onPress: () => toggle(value),
69
+ className: "flex-row items-center justify-between gap-4 py-4",
70
+ ...triggerProps,
71
+ children: [typeof title === "string" ? /* @__PURE__ */ jsx(Text, {
72
+ className: cn("flex-1 text-sm font-medium text-text-primary", disabled && "text-text-disabled"),
73
+ children: title
74
+ }) : title, /* @__PURE__ */ jsx(View, {
75
+ style: { transform: [{ rotate: open ? "90deg" : "0deg" }] },
76
+ children: /* @__PURE__ */ jsx(ChevronRightIcon, { color: iconColor })
77
+ })]
78
+ }), open ? /* @__PURE__ */ jsx(View, {
79
+ className: "pb-4",
80
+ children: typeof children === "string" ? /* @__PURE__ */ jsx(Text, {
81
+ className: "text-sm text-text-secondary",
82
+ children
83
+ }) : children
84
+ }) : null]
85
+ });
86
+ }
87
+ //#endregion
88
+ export { Accordion, AccordionItem };
89
+
90
+ //# sourceMappingURL=accordion.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"accordion.js","names":[],"sources":["../../../src/components/accordion/accordion.tsx"],"sourcesContent":["/// <reference types=\"uniwind/types\" />\n\nimport { createContext, type ReactNode, useCallback, useContext, useMemo, useState } from 'react'\nimport {\n LayoutAnimation,\n Platform,\n Pressable,\n type PressableProps,\n Text,\n UIManager,\n View,\n type ViewProps,\n} from 'react-native'\nimport { cn } from '../../utils/cn'\nimport { ChevronRightIcon } from '../_shared/status'\nimport { useToken } from '../_shared/use-token'\n\n// No Android antigo o LayoutAnimation precisa ser habilitado. Na New Architecture a chamada é no-op.\nif (Platform.OS === 'android') {\n UIManager.setLayoutAnimationEnabledExperimental?.(true)\n}\n\ntype AccordionContextValue = {\n isOpen: (value: string) => boolean\n toggle: (value: string) => void\n}\n\nconst AccordionContext = createContext<AccordionContextValue>({\n isOpen: () => false,\n toggle: () => {},\n})\n\ntype AccordionBaseProps = Omit<ViewProps, 'children'> & {\n className?: string\n children?: ReactNode\n}\n\nexport type AccordionProps = AccordionBaseProps &\n (\n | {\n /** Abre uma seção por vez. */\n type: 'single'\n /** Permite fechar a seção aberta (todas recolhidas). */\n collapsible?: boolean\n value?: string\n defaultValue?: string\n /** Recebe o valor aberto, ou `''` quando tudo foi recolhido (igual ao web). */\n onValueChange?: (value: string) => void\n }\n | {\n /** Permite várias seções abertas. */\n type: 'multiple'\n value?: string[]\n defaultValue?: string[]\n onValueChange?: (value: string[]) => void\n }\n )\n\nconst toArray = (value: string | string[] | undefined) =>\n value === undefined || value === '' ? [] : Array.isArray(value) ? value : [value]\n\n/**\n * Lista de seções que expandem e recolhem. `type=\"single\"` abre uma por vez (com `collapsible`,\n * dá para fechar todas); `type=\"multiple\"` permite várias abertas. Controlado (`value`) ou não\n * (`defaultValue`). A animação usa `LayoutAnimation`.\n */\nexport function Accordion(props: AccordionProps) {\n // União discriminada alargada: a lógica abaixo trata os dois modos com `string[]`.\n const {\n className,\n children,\n type,\n value,\n defaultValue,\n onValueChange,\n collapsible: collapsibleProp,\n ...rest\n } = props as AccordionBaseProps & {\n type: 'single' | 'multiple'\n collapsible?: boolean\n value?: string | string[]\n defaultValue?: string | string[]\n onValueChange?: (value: never) => void\n }\n const collapsible = type === 'single' ? Boolean(collapsibleProp) : true\n\n const [internal, setInternal] = useState<string[]>(() => toArray(defaultValue))\n const open = value !== undefined ? toArray(value) : internal\n\n const toggle = useCallback(\n (item: string) => {\n let next: string[]\n if (open.includes(item)) {\n if (type === 'single' && !collapsible) return\n next = open.filter((v) => v !== item)\n } else {\n next = type === 'single' ? [item] : [...open, item]\n }\n LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut)\n if (value === undefined) setInternal(next)\n if (type === 'single') (onValueChange as ((v: string) => void) | undefined)?.(next[0] ?? '')\n else (onValueChange as ((v: string[]) => void) | undefined)?.(next)\n },\n [open, type, collapsible, value, onValueChange],\n )\n\n const context = useMemo(\n () => ({ isOpen: (v: string) => open.includes(v), toggle }),\n [open, toggle],\n )\n\n return (\n <AccordionContext.Provider value={context}>\n <View className={cn('w-full', className)} {...rest}>\n {children}\n </View>\n </AccordionContext.Provider>\n )\n}\n\nexport type AccordionItemProps = Omit<ViewProps, 'children'> & {\n className?: string\n /** Identifica a seção em `value`/`defaultValue` do `Accordion`. */\n value: string\n /** Texto do cabeçalho: é o botão que expande a seção. */\n title: ReactNode\n disabled?: boolean\n /** Repassado ao botão do cabeçalho (ex.: `testID`, `accessibilityLabel`). */\n triggerProps?: Omit<PressableProps, 'children' | 'style' | 'onPress' | 'disabled'>\n children?: ReactNode\n}\n\nexport function AccordionItem({\n className,\n value,\n title,\n disabled = false,\n triggerProps,\n children,\n ...props\n}: AccordionItemProps) {\n const { isOpen, toggle } = useContext(AccordionContext)\n const open = isOpen(value)\n const iconColor = useToken('--color-text-secondary')\n\n return (\n <View className={cn('border-b border-border-default', className)} {...props}>\n <Pressable\n accessibilityRole=\"button\"\n accessibilityState={{ expanded: open, disabled }}\n disabled={disabled}\n onPress={() => toggle(value)}\n className=\"flex-row items-center justify-between gap-4 py-4\"\n {...triggerProps}\n >\n {typeof title === 'string' ? (\n <Text\n className={cn(\n 'flex-1 text-sm font-medium text-text-primary',\n disabled && 'text-text-disabled',\n )}\n >\n {title}\n </Text>\n ) : (\n title\n )}\n {/* Valor dinâmico: a rotação depende do estado. */}\n <View style={{ transform: [{ rotate: open ? '90deg' : '0deg' }] }}>\n <ChevronRightIcon color={iconColor} />\n </View>\n </Pressable>\n {open ? (\n <View className=\"pb-4\">\n {typeof children === 'string' ? (\n <Text className=\"text-sm text-text-secondary\">{children}</Text>\n ) : (\n children\n )}\n </View>\n ) : null}\n </View>\n )\n}\n"],"mappings":";;;;;;;AAkBA,IAAI,SAAS,OAAO,WAClB,UAAU,wCAAwC,IAAI;AAQxD,MAAM,mBAAmB,cAAqC;CAC5D,cAAc;CACd,cAAc,CAAC;AACjB,CAAC;AA4BD,MAAM,WAAW,UACf,UAAU,KAAA,KAAa,UAAU,KAAK,CAAC,IAAI,MAAM,QAAQ,KAAK,IAAI,QAAQ,CAAC,KAAK;;;;;;AAOlF,SAAgB,UAAU,OAAuB;CAE/C,MAAM,EACJ,WACA,UACA,MACA,OACA,cACA,eACA,aAAa,iBACb,GAAG,SACD;CAOJ,MAAM,cAAc,SAAS,WAAW,QAAQ,eAAe,IAAI;CAEnE,MAAM,CAAC,UAAU,eAAe,eAAyB,QAAQ,YAAY,CAAC;CAC9E,MAAM,OAAO,UAAU,KAAA,IAAY,QAAQ,KAAK,IAAI;CAEpD,MAAM,SAAS,aACZ,SAAiB;EAChB,IAAI;EACJ,IAAI,KAAK,SAAS,IAAI,GAAG;GACvB,IAAI,SAAS,YAAY,CAAC,aAAa;GACvC,OAAO,KAAK,QAAQ,MAAM,MAAM,IAAI;EACtC,OACE,OAAO,SAAS,WAAW,CAAC,IAAI,IAAI,CAAC,GAAG,MAAM,IAAI;EAEpD,gBAAgB,cAAc,gBAAgB,QAAQ,aAAa;EACnE,IAAI,UAAU,KAAA,GAAW,YAAY,IAAI;EACzC,IAAI,SAAS,UAAU,gBAAuD,KAAK,MAAM,EAAE;OACtF,gBAAyD,IAAI;CACpE,GACA;EAAC;EAAM;EAAM;EAAa;EAAO;CAAa,CAChD;CAEA,MAAM,UAAU,eACP;EAAE,SAAS,MAAc,KAAK,SAAS,CAAC;EAAG;CAAO,IACzD,CAAC,MAAM,MAAM,CACf;CAEA,OACE,oBAAC,iBAAiB,UAAlB;EAA2B,OAAO;EAChC,UAAA,oBAAC,MAAD;GAAM,WAAW,GAAG,UAAU,SAAS;GAAG,GAAI;GAC3C;EACG,CAAA;CACmB,CAAA;AAE/B;AAcA,SAAgB,cAAc,EAC5B,WACA,OACA,OACA,WAAW,OACX,cACA,UACA,GAAG,SACkB;CACrB,MAAM,EAAE,QAAQ,WAAW,WAAW,gBAAgB;CACtD,MAAM,OAAO,OAAO,KAAK;CACzB,MAAM,YAAY,SAAS,wBAAwB;CAEnD,OACE,qBAAC,MAAD;EAAM,WAAW,GAAG,kCAAkC,SAAS;EAAG,GAAI;EAAtE,UAAA,CACE,qBAAC,WAAD;GACE,mBAAkB;GAClB,oBAAoB;IAAE,UAAU;IAAM;GAAS;GACrC;GACV,eAAe,OAAO,KAAK;GAC3B,WAAU;GACV,GAAI;GANN,UAAA,CAQG,OAAO,UAAU,WAChB,oBAAC,MAAD;IACE,WAAW,GACT,gDACA,YAAY,oBACd;IAEC,UAAA;GACG,CAAA,IAEN,OAGF,oBAAC,MAAD;IAAM,OAAO,EAAE,WAAW,CAAC,EAAE,QAAQ,OAAO,UAAU,OAAO,CAAC,EAAE;IAC9D,UAAA,oBAAC,kBAAD,EAAkB,OAAO,UAAY,CAAA;GACjC,CAAA,CACG;EACV,CAAA,GAAA,OACC,oBAAC,MAAD;GAAM,WAAU;GACb,UAAA,OAAO,aAAa,WACnB,oBAAC,MAAD;IAAM,WAAU;IAA+B;GAAe,CAAA,IAE9D;EAEE,CAAA,IACJ,IACA;;AAEV"}
@@ -0,0 +1,2 @@
1
+ import { Accordion, AccordionItem, AccordionItemProps, AccordionProps } from "./accordion.js";
2
+ export { Accordion, AccordionItem, type AccordionItemProps, type AccordionProps };
@@ -0,0 +1,25 @@
1
+ import { Status } from "../_shared/status.js";
2
+ import { ReactNode } from "react";
3
+ import { ViewProps } from "react-native";
4
+ //#region src/components/alert/alert.d.ts
5
+ export type AlertProps = ViewProps & {
6
+ className?: string;
7
+ status?: Status;
8
+ /** Resumo do alerta (opcional). */
9
+ title?: string;
10
+ /** Mostra o ícone semântico do status. */
11
+ showIcon?: boolean;
12
+ /** Ação contextual (ex.: um `<Button variant="ghost">`). */
13
+ action?: ReactNode;
14
+ /** Mostra o botão de fechar (×) e o chama ao tocar. */
15
+ onDismiss?: () => void;
16
+ /** Nome acessível do botão de fechar. */
17
+ dismissLabel?: string;
18
+ };
19
+ /**
20
+ * Mensagem contextual em bloco, persistente. Para avisos temporários use Toast; para globais,
21
+ * Banner. `children` string vira `Text`; outros nós são renderizados como estão.
22
+ */
23
+ export declare function Alert({ className, status, title, showIcon, action, onDismiss, dismissLabel, children, ...props }: AlertProps): import("react").JSX.Element;
24
+ //#endregion
25
+ //# sourceMappingURL=alert.d.ts.map