@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
@@ -0,0 +1,41 @@
1
+ import { ReactNode } from "react";
2
+ import { VariantProps } from "class-variance-authority";
3
+ //#region src/components/modal/modal.d.ts
4
+ declare const modalVariants: (props?: ({
5
+ size?: "lg" | "md" | "sm" | null | undefined;
6
+ } & import("class-variance-authority/types").ClassProp) | undefined) => string;
7
+ export type ModalProps = VariantProps<typeof modalVariants> & {
8
+ open?: boolean;
9
+ defaultOpen?: boolean;
10
+ onOpenChange?: (open: boolean) => void;
11
+ /** Título do diálogo; também é o nome acessível dele. */
12
+ title: ReactNode;
13
+ /** O corpo do diálogo. */
14
+ children: ReactNode;
15
+ /** Ações do rodapé (primária à direita). Use `ModalClose` para fechar. Sem ele, não há rodapé. */
16
+ footer?: ReactNode;
17
+ /** Elemento que abre o modal (ex.: um `<Button>`); recebe `onPress`. Dispensável no modo controlado. */
18
+ trigger?: ReactNode;
19
+ /** Mostra o botão de fechar (×) no cabeçalho. */
20
+ showClose?: boolean;
21
+ /** Linha entre o cabeçalho e o corpo. Padrão: `true`. */
22
+ showHeaderDivider?: boolean;
23
+ /** Linha entre o corpo e o rodapé (só aparece com `footer`). Padrão: `true`. */
24
+ showFooterDivider?: boolean;
25
+ /** Nome acessível do botão de fechar. */
26
+ closeLabel?: string;
27
+ className?: string;
28
+ testID?: string;
29
+ };
30
+ /**
31
+ * Diálogo que bloqueia o conteúdo de trás, com backdrop. Usa o `Modal` nativo: o botão voltar do
32
+ * Android e o toque no fundo fecham. Não há foco preso nem `Esc`; `accessibilityViewIsModal`
33
+ * mantém o leitor de tela dentro do diálogo.
34
+ */
35
+ export declare function Modal({ open, defaultOpen, onOpenChange, title, children, footer, trigger, showClose, showHeaderDivider, showFooterDivider, closeLabel, size, className, testID }: ModalProps): import("react").JSX.Element;
36
+ /** Fecha o modal ao pressionar. Envolva um `<Button>` do rodapé (injeta `onPress`, como o `asChild`). */
37
+ export declare function ModalClose({ children }: {
38
+ children: ReactNode;
39
+ }): import("react").JSX.Element;
40
+ //#endregion
41
+ //# sourceMappingURL=modal.d.ts.map
@@ -0,0 +1,92 @@
1
+ import { cn } from "../../utils/cn.js";
2
+ import { CloseIcon } from "../_shared/status.js";
3
+ import { useToken } from "../_shared/use-token.js";
4
+ import { Backdrop } from "../_shared/backdrop.js";
5
+ import { injectHandler, useOpenState } from "../_shared/overlay-state.js";
6
+ import { createContext, useContext } from "react";
7
+ import { Modal, Pressable, ScrollView, Text, View } from "react-native";
8
+ import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
9
+ import { cva } from "class-variance-authority";
10
+ //#region src/components/modal/modal.tsx
11
+ const modalVariants = cva("max-h-full w-full overflow-hidden rounded-lg border border-border-default bg-surface-elevated shadow-2xl", {
12
+ variants: { size: {
13
+ sm: "max-w-[480px]",
14
+ md: "max-w-[640px]",
15
+ lg: "max-w-[800px]"
16
+ } },
17
+ defaultVariants: { size: "sm" }
18
+ });
19
+ const CloseContext = createContext(null);
20
+ /**
21
+ * Diálogo que bloqueia o conteúdo de trás, com backdrop. Usa o `Modal` nativo: o botão voltar do
22
+ * Android e o toque no fundo fecham. Não há foco preso nem `Esc`; `accessibilityViewIsModal`
23
+ * mantém o leitor de tela dentro do diálogo.
24
+ */
25
+ function Modal$1({ open, defaultOpen, onOpenChange, title, children, footer, trigger, showClose = true, showHeaderDivider = true, showFooterDivider = true, closeLabel = "Fechar", size, className, testID }) {
26
+ const [isOpen, setOpen] = useOpenState({
27
+ open,
28
+ defaultOpen,
29
+ onOpenChange
30
+ });
31
+ const close = () => setOpen(false);
32
+ const iconColor = useToken("--color-text-primary");
33
+ return /* @__PURE__ */ jsxs(Fragment$1, { children: [injectHandler(trigger, "onPress", () => setOpen(true)), /* @__PURE__ */ jsx(Modal, {
34
+ transparent: true,
35
+ statusBarTranslucent: true,
36
+ animationType: "fade",
37
+ visible: isOpen,
38
+ onRequestClose: close,
39
+ children: /* @__PURE__ */ jsx(CloseContext.Provider, {
40
+ value: close,
41
+ children: /* @__PURE__ */ jsxs(View, {
42
+ className: "flex-1 items-center justify-center p-4",
43
+ accessibilityViewIsModal: true,
44
+ children: [/* @__PURE__ */ jsx(Backdrop, {
45
+ onPress: close,
46
+ testID: testID ? `${testID}-backdrop` : void 0
47
+ }), /* @__PURE__ */ jsxs(View, {
48
+ testID,
49
+ className: cn(modalVariants({ size }), className),
50
+ children: [
51
+ /* @__PURE__ */ jsxs(View, {
52
+ className: cn("flex-row items-center gap-4 px-6 py-4", showHeaderDivider && "border-b border-border-default"),
53
+ children: [/* @__PURE__ */ jsx(Text, {
54
+ accessibilityRole: "header",
55
+ className: "flex-1 text-lg font-semibold leading-6 text-text-primary",
56
+ children: title
57
+ }), showClose && /* @__PURE__ */ jsx(Pressable, {
58
+ accessibilityRole: "button",
59
+ accessibilityLabel: closeLabel,
60
+ hitSlop: 8,
61
+ onPress: close,
62
+ className: "size-8 items-center justify-center rounded-full active:bg-button-secondary-bg-hover",
63
+ children: /* @__PURE__ */ jsx(CloseIcon, { color: iconColor })
64
+ })]
65
+ }),
66
+ /* @__PURE__ */ jsx(ScrollView, {
67
+ className: "shrink",
68
+ contentContainerClassName: "gap-4 p-6",
69
+ children: typeof children === "string" ? /* @__PURE__ */ jsx(Text, {
70
+ className: "text-sm leading-5 text-text-secondary",
71
+ children
72
+ }) : children
73
+ }),
74
+ footer && /* @__PURE__ */ jsx(View, {
75
+ className: cn("flex-row items-center justify-end gap-2 px-6 py-4", showFooterDivider && "border-t border-border-default"),
76
+ children: footer
77
+ })
78
+ ]
79
+ })]
80
+ })
81
+ })
82
+ })] });
83
+ }
84
+ /** Fecha o modal ao pressionar. Envolva um `<Button>` do rodapé (injeta `onPress`, como o `asChild`). */
85
+ function ModalClose({ children }) {
86
+ const close = useContext(CloseContext);
87
+ return /* @__PURE__ */ jsx(Fragment$1, { children: injectHandler(children, "onPress", () => close?.()) });
88
+ }
89
+ //#endregion
90
+ export { Modal$1 as Modal, ModalClose };
91
+
92
+ //# sourceMappingURL=modal.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"modal.js","names":["Modal","RNModal"],"sources":["../../../src/components/modal/modal.tsx"],"sourcesContent":["/// <reference types=\"uniwind/types\" />\n\nimport { cva, type VariantProps } from 'class-variance-authority'\nimport { createContext, type ReactNode, useContext } from 'react'\nimport { Pressable, Modal as RNModal, ScrollView, Text, View } from 'react-native'\nimport { cn } from '../../utils/cn'\nimport { Backdrop } from '../_shared/backdrop'\nimport { injectHandler, useOpenState } from '../_shared/overlay-state'\nimport { CloseIcon } from '../_shared/status'\nimport { useToken } from '../_shared/use-token'\n\nconst modalVariants = cva(\n 'max-h-full w-full overflow-hidden rounded-lg border border-border-default bg-surface-elevated shadow-2xl',\n {\n variants: {\n size: { sm: 'max-w-[480px]', md: 'max-w-[640px]', lg: 'max-w-[800px]' },\n },\n defaultVariants: { size: 'sm' },\n },\n)\n\nconst CloseContext = createContext<(() => void) | null>(null)\n\nexport type ModalProps = VariantProps<typeof modalVariants> & {\n open?: boolean\n defaultOpen?: boolean\n onOpenChange?: (open: boolean) => void\n /** Título do diálogo; também é o nome acessível dele. */\n title: ReactNode\n /** O corpo do diálogo. */\n children: ReactNode\n /** Ações do rodapé (primária à direita). Use `ModalClose` para fechar. Sem ele, não há rodapé. */\n footer?: ReactNode\n /** Elemento que abre o modal (ex.: um `<Button>`); recebe `onPress`. Dispensável no modo controlado. */\n trigger?: ReactNode\n /** Mostra o botão de fechar (×) no cabeçalho. */\n showClose?: boolean\n /** Linha entre o cabeçalho e o corpo. Padrão: `true`. */\n showHeaderDivider?: boolean\n /** Linha entre o corpo e o rodapé (só aparece com `footer`). Padrão: `true`. */\n showFooterDivider?: boolean\n /** Nome acessível do botão de fechar. */\n closeLabel?: string\n className?: string\n testID?: string\n}\n\n/**\n * Diálogo que bloqueia o conteúdo de trás, com backdrop. Usa o `Modal` nativo: o botão voltar do\n * Android e o toque no fundo fecham. Não há foco preso nem `Esc`; `accessibilityViewIsModal`\n * mantém o leitor de tela dentro do diálogo.\n */\nexport function Modal({\n open,\n defaultOpen,\n onOpenChange,\n title,\n children,\n footer,\n trigger,\n showClose = true,\n showHeaderDivider = true,\n showFooterDivider = true,\n closeLabel = 'Fechar',\n size,\n className,\n testID,\n}: ModalProps) {\n const [isOpen, setOpen] = useOpenState({ open, defaultOpen, onOpenChange })\n const close = () => setOpen(false)\n const iconColor = useToken('--color-text-primary')\n\n return (\n <>\n {injectHandler(trigger, 'onPress', () => setOpen(true))}\n <RNModal\n transparent\n statusBarTranslucent\n animationType=\"fade\"\n visible={isOpen}\n onRequestClose={close}\n >\n <CloseContext.Provider value={close}>\n <View className=\"flex-1 items-center justify-center p-4\" accessibilityViewIsModal>\n <Backdrop onPress={close} testID={testID ? `${testID}-backdrop` : undefined} />\n <View testID={testID} className={cn(modalVariants({ size }), className)}>\n <View\n className={cn(\n 'flex-row items-center gap-4 px-6 py-4',\n showHeaderDivider && 'border-b border-border-default',\n )}\n >\n <Text\n accessibilityRole=\"header\"\n className=\"flex-1 text-lg font-semibold leading-6 text-text-primary\"\n >\n {title}\n </Text>\n {showClose && (\n <Pressable\n accessibilityRole=\"button\"\n accessibilityLabel={closeLabel}\n hitSlop={8}\n onPress={close}\n className=\"size-8 items-center justify-center rounded-full active:bg-button-secondary-bg-hover\"\n >\n <CloseIcon color={iconColor} />\n </Pressable>\n )}\n </View>\n <ScrollView className=\"shrink\" contentContainerClassName=\"gap-4 p-6\">\n {typeof children === 'string' ? (\n <Text className=\"text-sm leading-5 text-text-secondary\">{children}</Text>\n ) : (\n children\n )}\n </ScrollView>\n {footer && (\n <View\n className={cn(\n 'flex-row items-center justify-end gap-2 px-6 py-4',\n showFooterDivider && 'border-t border-border-default',\n )}\n >\n {footer}\n </View>\n )}\n </View>\n </View>\n </CloseContext.Provider>\n </RNModal>\n </>\n )\n}\n\n/** Fecha o modal ao pressionar. Envolva um `<Button>` do rodapé (injeta `onPress`, como o `asChild`). */\nexport function ModalClose({ children }: { children: ReactNode }) {\n const close = useContext(CloseContext)\n return <>{injectHandler(children, 'onPress', () => close?.())}</>\n}\n"],"mappings":";;;;;;;;;;AAWA,MAAM,gBAAgB,IACpB,4GACA;CACE,UAAU,EACR,MAAM;EAAE,IAAI;EAAiB,IAAI;EAAiB,IAAI;CAAgB,EACxE;CACA,iBAAiB,EAAE,MAAM,KAAK;AAChC,CACF;AAEA,MAAM,eAAe,cAAmC,IAAI;;;;;;AA+B5D,SAAgBA,QAAM,EACpB,MACA,aACA,cACA,OACA,UACA,QACA,SACA,YAAY,MACZ,oBAAoB,MACpB,oBAAoB,MACpB,aAAa,UACb,MACA,WACA,UACa;CACb,MAAM,CAAC,QAAQ,WAAW,aAAa;EAAE;EAAM;EAAa;CAAa,CAAC;CAC1E,MAAM,cAAc,QAAQ,KAAK;CACjC,MAAM,YAAY,SAAS,sBAAsB;CAEjD,OACE,qBAAA,YAAA,EAAA,UAAA,CACG,cAAc,SAAS,iBAAiB,QAAQ,IAAI,CAAC,GACtD,oBAACC,OAAD;EACE,aAAA;EACA,sBAAA;EACA,eAAc;EACd,SAAS;EACT,gBAAgB;EAEhB,UAAA,oBAAC,aAAa,UAAd;GAAuB,OAAO;GAC5B,UAAA,qBAAC,MAAD;IAAM,WAAU;IAAyC,0BAAA;IAAzD,UAAA,CACE,oBAAC,UAAD;KAAU,SAAS;KAAO,QAAQ,SAAS,GAAG,OAAO,aAAa,KAAA;IAAY,CAAA,GAC9E,qBAAC,MAAD;KAAc;KAAQ,WAAW,GAAG,cAAc,EAAE,KAAK,CAAC,GAAG,SAAS;KAAtE,UAAA;MACE,qBAAC,MAAD;OACE,WAAW,GACT,yCACA,qBAAqB,gCACvB;OAJF,UAAA,CAME,oBAAC,MAAD;QACE,mBAAkB;QAClB,WAAU;QAET,UAAA;OACG,CAAA,GACL,aACC,oBAAC,WAAD;QACE,mBAAkB;QAClB,oBAAoB;QACpB,SAAS;QACT,SAAS;QACT,WAAU;QAEV,UAAA,oBAAC,WAAD,EAAW,OAAO,UAAY,CAAA;OACrB,CAAA,CAET;;MACN,oBAAC,YAAD;OAAY,WAAU;OAAS,2BAA0B;OACtD,UAAA,OAAO,aAAa,WACnB,oBAAC,MAAD;QAAM,WAAU;QAAyC;OAAe,CAAA,IAExE;MAEQ,CAAA;MACX,UACC,oBAAC,MAAD;OACE,WAAW,GACT,qDACA,qBAAqB,gCACvB;OAEC,UAAA;MACG,CAAA;KAEJ;IACF,CAAA,CAAA;;EACe,CAAA;CAChB,CAAA,CACT,EAAA,CAAA;AAEN;;AAGA,SAAgB,WAAW,EAAE,YAAqC;CAChE,MAAM,QAAQ,WAAW,YAAY;CACrC,OAAO,oBAAA,YAAA,EAAA,UAAG,cAAc,UAAU,iBAAiB,QAAQ,CAAC,EAAI,CAAA;AAClE"}
@@ -0,0 +1,2 @@
1
+ import { Pagination, PaginationProps } from "./pagination.js";
2
+ export { Pagination, type PaginationProps };
@@ -0,0 +1,21 @@
1
+ import { ViewProps } from "react-native";
2
+ //#region src/components/pagination/pagination.d.ts
3
+ export type PaginationProps = ViewProps & {
4
+ className?: string;
5
+ /** Página atual, começando em 1. */
6
+ page: number;
7
+ /** Total de páginas. */
8
+ pageCount: number;
9
+ /** Chamado com o número da página escolhida. */
10
+ onPageChange: (page: number) => void;
11
+ /** Quantas páginas vizinhas da atual mostrar de cada lado. */
12
+ siblingCount?: number;
13
+ /** Nome acessível do botão de página anterior. */
14
+ previousLabel?: string;
15
+ /** Nome acessível do botão de próxima página. */
16
+ nextLabel?: string;
17
+ };
18
+ /** Navegação entre páginas de uma lista ou tabela. Controlado: você guarda o estado de `page`. */
19
+ export declare function Pagination({ className, page, pageCount, onPageChange, siblingCount, previousLabel, nextLabel, accessibilityLabel, ...props }: PaginationProps): import("react").JSX.Element;
20
+ //#endregion
21
+ //# sourceMappingURL=pagination.d.ts.map
@@ -0,0 +1,99 @@
1
+ import { cn } from "../../utils/cn.js";
2
+ import { ChevronRightIcon } from "../_shared/status.js";
3
+ import { useToken } from "../_shared/use-token.js";
4
+ import { Pressable, Text, View } from "react-native";
5
+ import { jsx, jsxs } from "react/jsx-runtime";
6
+ //#region src/components/pagination/pagination.tsx
7
+ /**
8
+ * Páginas a mostrar: a primeira, a última, a atual e `siblingCount` vizinhas de cada lado, com
9
+ * reticências no que sobra. Mantém sempre o mesmo número de itens, para a barra não pular.
10
+ */
11
+ function getPages(page, pageCount, siblingCount = 1) {
12
+ if (pageCount <= siblingCount * 2 + 5) return Array.from({ length: pageCount }, (_, i) => i + 1);
13
+ const range = (from, to) => Array.from({ length: to - from + 1 }, (_, i) => from + i);
14
+ const left = Math.max(page - siblingCount, 1);
15
+ const right = Math.min(page + siblingCount, pageCount);
16
+ const dotsLeft = left > 2;
17
+ const dotsRight = right < pageCount - 1;
18
+ const edge = 3 + siblingCount * 2;
19
+ if (!dotsLeft) return [
20
+ ...range(1, edge),
21
+ "ellipsis-right",
22
+ pageCount
23
+ ];
24
+ if (!dotsRight) return [
25
+ 1,
26
+ "ellipsis-left",
27
+ ...range(pageCount - edge + 1, pageCount)
28
+ ];
29
+ return [
30
+ 1,
31
+ "ellipsis-left",
32
+ ...range(left, right),
33
+ "ellipsis-right",
34
+ pageCount
35
+ ];
36
+ }
37
+ function ArrowButton({ label, disabled, reversed, onPress }) {
38
+ const color = useToken(disabled ? "--color-text-disabled" : "--color-text-primary");
39
+ return /* @__PURE__ */ jsx(Pressable, {
40
+ accessibilityRole: "button",
41
+ accessibilityLabel: label,
42
+ accessibilityState: { disabled },
43
+ disabled,
44
+ onPress,
45
+ className: "size-9 items-center justify-center rounded-full bg-transparent active:bg-button-secondary-bg-hover",
46
+ children: /* @__PURE__ */ jsx(View, {
47
+ style: reversed ? { transform: [{ rotate: "180deg" }] } : void 0,
48
+ children: /* @__PURE__ */ jsx(ChevronRightIcon, { color })
49
+ })
50
+ });
51
+ }
52
+ /** Navegação entre páginas de uma lista ou tabela. Controlado: você guarda o estado de `page`. */
53
+ function Pagination({ className, page, pageCount, onPageChange, siblingCount = 1, previousLabel = "Página anterior", nextLabel = "Próxima página", accessibilityLabel = "Paginação", ...props }) {
54
+ return /* @__PURE__ */ jsxs(View, {
55
+ accessibilityLabel,
56
+ className: cn("flex-row items-center gap-1", className),
57
+ ...props,
58
+ children: [
59
+ /* @__PURE__ */ jsx(ArrowButton, {
60
+ label: previousLabel,
61
+ reversed: true,
62
+ disabled: page <= 1,
63
+ onPress: () => onPageChange(page - 1)
64
+ }),
65
+ getPages(page, pageCount, siblingCount).map((item) => {
66
+ if (typeof item === "string") return /* @__PURE__ */ jsx(View, {
67
+ accessible: false,
68
+ importantForAccessibility: "no-hide-descendants",
69
+ className: "size-9 items-center justify-center",
70
+ children: /* @__PURE__ */ jsx(Text, {
71
+ className: "text-sm text-text-secondary",
72
+ children: "…"
73
+ })
74
+ }, item);
75
+ const current = item === page;
76
+ return /* @__PURE__ */ jsx(Pressable, {
77
+ accessibilityRole: "button",
78
+ accessibilityLabel: `Página ${item}`,
79
+ accessibilityState: { selected: current },
80
+ onPress: () => onPageChange(item),
81
+ className: cn("size-9 items-center justify-center rounded-full", current ? "bg-button-primary-bg active:bg-button-primary-bg-active" : "bg-transparent active:bg-button-secondary-bg-hover"),
82
+ children: /* @__PURE__ */ jsx(Text, {
83
+ className: cn("text-sm font-medium", current ? "text-button-primary-text" : "text-button-ghost-text"),
84
+ children: item
85
+ })
86
+ }, item);
87
+ }),
88
+ /* @__PURE__ */ jsx(ArrowButton, {
89
+ label: nextLabel,
90
+ disabled: page >= pageCount,
91
+ onPress: () => onPageChange(page + 1)
92
+ })
93
+ ]
94
+ });
95
+ }
96
+ //#endregion
97
+ export { Pagination, getPages };
98
+
99
+ //# sourceMappingURL=pagination.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pagination.js","names":[],"sources":["../../../src/components/pagination/pagination.tsx"],"sourcesContent":["/// <reference types=\"uniwind/types\" />\n\nimport { Pressable, Text, View, type ViewProps } from 'react-native'\nimport { cn } from '../../utils/cn'\nimport { ChevronRightIcon } from '../_shared/status'\nimport { useToken } from '../_shared/use-token'\n\n/**\n * Páginas a mostrar: a primeira, a última, a atual e `siblingCount` vizinhas de cada lado, com\n * reticências no que sobra. Mantém sempre o mesmo número de itens, para a barra não pular.\n */\nexport function getPages(page: number, pageCount: number, siblingCount = 1) {\n const slots = siblingCount * 2 + 5\n if (pageCount <= slots) return Array.from({ length: pageCount }, (_, i) => i + 1)\n\n const range = (from: number, to: number) =>\n Array.from({ length: to - from + 1 }, (_, i) => from + i)\n const left = Math.max(page - siblingCount, 1)\n const right = Math.min(page + siblingCount, pageCount)\n const dotsLeft = left > 2\n const dotsRight = right < pageCount - 1\n const edge = 3 + siblingCount * 2\n\n if (!dotsLeft) return [...range(1, edge), 'ellipsis-right' as const, pageCount]\n if (!dotsRight) return [1, 'ellipsis-left' as const, ...range(pageCount - edge + 1, pageCount)]\n return [1, 'ellipsis-left' as const, ...range(left, right), 'ellipsis-right' as const, pageCount]\n}\n\nexport type PaginationProps = ViewProps & {\n className?: string\n /** Página atual, começando em 1. */\n page: number\n /** Total de páginas. */\n pageCount: number\n /** Chamado com o número da página escolhida. */\n onPageChange: (page: number) => void\n /** Quantas páginas vizinhas da atual mostrar de cada lado. */\n siblingCount?: number\n /** Nome acessível do botão de página anterior. */\n previousLabel?: string\n /** Nome acessível do botão de próxima página. */\n nextLabel?: string\n}\n\nfunction ArrowButton({\n label,\n disabled,\n reversed,\n onPress,\n}: {\n label: string\n disabled: boolean\n reversed?: boolean\n onPress: () => void\n}) {\n const color = useToken(disabled ? '--color-text-disabled' : '--color-text-primary')\n\n return (\n <Pressable\n accessibilityRole=\"button\"\n accessibilityLabel={label}\n accessibilityState={{ disabled }}\n disabled={disabled}\n onPress={onPress}\n className=\"size-9 items-center justify-center rounded-full bg-transparent active:bg-button-secondary-bg-hover\"\n >\n {/* Valor dinâmico: a seta anterior é a próxima girada em 180°. */}\n <View style={reversed ? { transform: [{ rotate: '180deg' }] } : undefined}>\n <ChevronRightIcon color={color} />\n </View>\n </Pressable>\n )\n}\n\n/** Navegação entre páginas de uma lista ou tabela. Controlado: você guarda o estado de `page`. */\nexport function Pagination({\n className,\n page,\n pageCount,\n onPageChange,\n siblingCount = 1,\n previousLabel = 'Página anterior',\n nextLabel = 'Próxima página',\n accessibilityLabel = 'Paginação',\n ...props\n}: PaginationProps) {\n return (\n // RN não tem role \"navigation\": o nome acessível fica no accessibilityLabel.\n <View\n accessibilityLabel={accessibilityLabel}\n className={cn('flex-row items-center gap-1', className)}\n {...props}\n >\n <ArrowButton\n label={previousLabel}\n reversed\n disabled={page <= 1}\n onPress={() => onPageChange(page - 1)}\n />\n {getPages(page, pageCount, siblingCount).map((item) => {\n if (typeof item === 'string') {\n return (\n <View\n key={item}\n accessible={false}\n importantForAccessibility=\"no-hide-descendants\"\n className=\"size-9 items-center justify-center\"\n >\n <Text className=\"text-sm text-text-secondary\">…</Text>\n </View>\n )\n }\n const current = item === page\n return (\n <Pressable\n key={item}\n accessibilityRole=\"button\"\n accessibilityLabel={`Página ${item}`}\n // RN não tem aria-current: a página atual vai como `selected`.\n accessibilityState={{ selected: current }}\n onPress={() => onPageChange(item)}\n className={cn(\n 'size-9 items-center justify-center rounded-full',\n current\n ? 'bg-button-primary-bg active:bg-button-primary-bg-active'\n : 'bg-transparent active:bg-button-secondary-bg-hover',\n )}\n >\n <Text\n className={cn(\n 'text-sm font-medium',\n current ? 'text-button-primary-text' : 'text-button-ghost-text',\n )}\n >\n {item}\n </Text>\n </Pressable>\n )\n })}\n <ArrowButton\n label={nextLabel}\n disabled={page >= pageCount}\n onPress={() => onPageChange(page + 1)}\n />\n </View>\n )\n}\n"],"mappings":";;;;;;;;;;AAWA,SAAgB,SAAS,MAAc,WAAmB,eAAe,GAAG;CAE1E,IAAI,aADU,eAAe,IAAI,GACT,OAAO,MAAM,KAAK,EAAE,QAAQ,UAAU,IAAI,GAAG,MAAM,IAAI,CAAC;CAEhF,MAAM,SAAS,MAAc,OAC3B,MAAM,KAAK,EAAE,QAAQ,KAAK,OAAO,EAAE,IAAI,GAAG,MAAM,OAAO,CAAC;CAC1D,MAAM,OAAO,KAAK,IAAI,OAAO,cAAc,CAAC;CAC5C,MAAM,QAAQ,KAAK,IAAI,OAAO,cAAc,SAAS;CACrD,MAAM,WAAW,OAAO;CACxB,MAAM,YAAY,QAAQ,YAAY;CACtC,MAAM,OAAO,IAAI,eAAe;CAEhC,IAAI,CAAC,UAAU,OAAO;EAAC,GAAG,MAAM,GAAG,IAAI;EAAG;EAA2B;CAAS;CAC9E,IAAI,CAAC,WAAW,OAAO;EAAC;EAAG;EAA0B,GAAG,MAAM,YAAY,OAAO,GAAG,SAAS;CAAC;CAC9F,OAAO;EAAC;EAAG;EAA0B,GAAG,MAAM,MAAM,KAAK;EAAG;EAA2B;CAAS;AAClG;AAkBA,SAAS,YAAY,EACnB,OACA,UACA,UACA,WAMC;CACD,MAAM,QAAQ,SAAS,WAAW,0BAA0B,sBAAsB;CAElF,OACE,oBAAC,WAAD;EACE,mBAAkB;EAClB,oBAAoB;EACpB,oBAAoB,EAAE,SAAS;EACrB;EACD;EACT,WAAU;EAGV,UAAA,oBAAC,MAAD;GAAM,OAAO,WAAW,EAAE,WAAW,CAAC,EAAE,QAAQ,SAAS,CAAC,EAAE,IAAI,KAAA;GAC9D,UAAA,oBAAC,kBAAD,EAAyB,MAAQ,CAAA;EAC7B,CAAA;CACG,CAAA;AAEf;;AAGA,SAAgB,WAAW,EACzB,WACA,MACA,WACA,cACA,eAAe,GACf,gBAAgB,mBAChB,YAAY,kBACZ,qBAAqB,aACrB,GAAG,SACe;CAClB,OAEE,qBAAC,MAAD;EACsB;EACpB,WAAW,GAAG,+BAA+B,SAAS;EACtD,GAAI;EAHN,UAAA;GAKE,oBAAC,aAAD;IACE,OAAO;IACP,UAAA;IACA,UAAU,QAAQ;IAClB,eAAe,aAAa,OAAO,CAAC;GACrC,CAAA;GACA,SAAS,MAAM,WAAW,YAAY,CAAC,CAAC,KAAK,SAAS;IACrD,IAAI,OAAO,SAAS,UAClB,OACE,oBAAC,MAAD;KAEE,YAAY;KACZ,2BAA0B;KAC1B,WAAU;KAEV,UAAA,oBAAC,MAAD;MAAM,WAAU;MAA8B,UAAA;KAAO,CAAA;IACjD,GANC,IAMD;IAGV,MAAM,UAAU,SAAS;IACzB,OACE,oBAAC,WAAD;KAEE,mBAAkB;KAClB,oBAAoB,UAAU;KAE9B,oBAAoB,EAAE,UAAU,QAAQ;KACxC,eAAe,aAAa,IAAI;KAChC,WAAW,GACT,mDACA,UACI,4DACA,oDACN;KAEA,UAAA,oBAAC,MAAD;MACE,WAAW,GACT,uBACA,UAAU,6BAA6B,wBACzC;MAEC,UAAA;KACG,CAAA;IACG,GArBJ,IAqBI;GAEf,CAAC;GACD,oBAAC,aAAD;IACE,OAAO;IACP,UAAU,QAAQ;IAClB,eAAe,aAAa,OAAO,CAAC;GACrC,CAAA;EACG;;AAEV"}
@@ -0,0 +1,2 @@
1
+ import { Popover, PopoverClose, PopoverProps } from "./popover.js";
2
+ export { Popover, PopoverClose, type PopoverProps };
@@ -0,0 +1,29 @@
1
+ import { ReactNode } from "react";
2
+ //#region src/components/popover/popover.d.ts
3
+ export type PopoverProps = {
4
+ open?: boolean;
5
+ defaultOpen?: boolean;
6
+ onOpenChange?: (open: boolean) => void;
7
+ /** Lado do gatilho em que o painel abre; inverte se não couber. Padrão: `bottom`. */
8
+ side?: 'top' | 'bottom';
9
+ /** Elemento que abre o popover (ex.: um `<Button>`); recebe `onPress`. */
10
+ trigger: ReactNode;
11
+ /** Nome acessível do painel: descreve o que há nele, ex.: "Filtros". */
12
+ label: string;
13
+ /** O conteúdo do popover. Pode ter elementos interativos. */
14
+ children: ReactNode;
15
+ className?: string;
16
+ testID?: string;
17
+ };
18
+ /**
19
+ * Painel flutuante ancorado ao gatilho, aberto por toque. O conteúdo pode ser interativo; tocar
20
+ * fora ou o botão voltar do Android fecha. Diferenças do web: sem `align` (o painel alinha à
21
+ * esquerda do gatilho), sem lados `left`/`right` e sem devolução de foco.
22
+ */
23
+ export declare function Popover({ open, defaultOpen, onOpenChange, side, trigger, label, children, className, testID }: PopoverProps): import("react").JSX.Element;
24
+ /** Fecha o popover ao pressionar. Envolva um `<Button>` dentro do conteúdo (injeta `onPress`). */
25
+ export declare function PopoverClose({ children }: {
26
+ children: ReactNode;
27
+ }): import("react").JSX.Element;
28
+ //#endregion
29
+ //# sourceMappingURL=popover.d.ts.map
@@ -0,0 +1,54 @@
1
+ import { cn } from "../../utils/cn.js";
2
+ import { AnchoredOverlay } from "../_shared/anchored-overlay.js";
3
+ import { injectHandler, useOpenState } from "../_shared/overlay-state.js";
4
+ import { AnchorTrigger, useControlledAnchor } from "../_shared/overlay-anchor.js";
5
+ import { createContext, useContext } from "react";
6
+ import { View } from "react-native";
7
+ import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
8
+ //#region src/components/popover/popover.tsx
9
+ const CloseContext = createContext(null);
10
+ /**
11
+ * Painel flutuante ancorado ao gatilho, aberto por toque. O conteúdo pode ser interativo; tocar
12
+ * fora ou o botão voltar do Android fecha. Diferenças do web: sem `align` (o painel alinha à
13
+ * esquerda do gatilho), sem lados `left`/`right` e sem devolução de foco.
14
+ */
15
+ function Popover({ open, defaultOpen, onOpenChange, side = "bottom", trigger, label, children, className, testID }) {
16
+ const [isOpen, setOpen] = useOpenState({
17
+ open,
18
+ defaultOpen,
19
+ onOpenChange
20
+ });
21
+ const { ref, rect } = useControlledAnchor(isOpen);
22
+ const close = () => setOpen(false);
23
+ return /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx(AnchorTrigger, {
24
+ anchorRef: ref,
25
+ event: "onPress",
26
+ onTrigger: () => setOpen(!isOpen),
27
+ children: trigger
28
+ }), /* @__PURE__ */ jsx(AnchoredOverlay, {
29
+ visible: isOpen,
30
+ rect,
31
+ onRequestClose: close,
32
+ placement: side,
33
+ offset: 8,
34
+ testID,
35
+ className: cn("max-w-xs rounded-md border border-border-default bg-surface-elevated p-4 shadow-md", className),
36
+ children: /* @__PURE__ */ jsx(CloseContext.Provider, {
37
+ value: close,
38
+ children: /* @__PURE__ */ jsx(View, {
39
+ accessibilityLabel: label,
40
+ accessibilityViewIsModal: true,
41
+ children
42
+ })
43
+ })
44
+ })] });
45
+ }
46
+ /** Fecha o popover ao pressionar. Envolva um `<Button>` dentro do conteúdo (injeta `onPress`). */
47
+ function PopoverClose({ children }) {
48
+ const close = useContext(CloseContext);
49
+ return /* @__PURE__ */ jsx(Fragment$1, { children: injectHandler(children, "onPress", () => close?.()) });
50
+ }
51
+ //#endregion
52
+ export { Popover, PopoverClose };
53
+
54
+ //# sourceMappingURL=popover.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"popover.js","names":[],"sources":["../../../src/components/popover/popover.tsx"],"sourcesContent":["/// <reference types=\"uniwind/types\" />\n\nimport { createContext, type ReactNode, useContext } from 'react'\nimport { View } from 'react-native'\nimport { cn } from '../../utils/cn'\nimport { AnchoredOverlay } from '../_shared/anchored-overlay'\nimport { AnchorTrigger, useControlledAnchor } from '../_shared/overlay-anchor'\nimport { injectHandler, useOpenState } from '../_shared/overlay-state'\n\nconst CloseContext = createContext<(() => void) | null>(null)\n\nexport type PopoverProps = {\n open?: boolean\n defaultOpen?: boolean\n onOpenChange?: (open: boolean) => void\n /** Lado do gatilho em que o painel abre; inverte se não couber. Padrão: `bottom`. */\n side?: 'top' | 'bottom'\n /** Elemento que abre o popover (ex.: um `<Button>`); recebe `onPress`. */\n trigger: ReactNode\n /** Nome acessível do painel: descreve o que há nele, ex.: \"Filtros\". */\n label: string\n /** O conteúdo do popover. Pode ter elementos interativos. */\n children: ReactNode\n className?: string\n testID?: string\n}\n\n/**\n * Painel flutuante ancorado ao gatilho, aberto por toque. O conteúdo pode ser interativo; tocar\n * fora ou o botão voltar do Android fecha. Diferenças do web: sem `align` (o painel alinha à\n * esquerda do gatilho), sem lados `left`/`right` e sem devolução de foco.\n */\nexport function Popover({\n open,\n defaultOpen,\n onOpenChange,\n side = 'bottom',\n trigger,\n label,\n children,\n className,\n testID,\n}: PopoverProps) {\n const [isOpen, setOpen] = useOpenState({ open, defaultOpen, onOpenChange })\n const { ref, rect } = useControlledAnchor(isOpen)\n const close = () => setOpen(false)\n\n return (\n <>\n <AnchorTrigger anchorRef={ref} event=\"onPress\" onTrigger={() => setOpen(!isOpen)}>\n {trigger}\n </AnchorTrigger>\n <AnchoredOverlay\n visible={isOpen}\n rect={rect}\n onRequestClose={close}\n placement={side}\n offset={8}\n testID={testID}\n className={cn(\n 'max-w-xs rounded-md border border-border-default bg-surface-elevated p-4 shadow-md',\n className,\n )}\n >\n <CloseContext.Provider value={close}>\n <View accessibilityLabel={label} accessibilityViewIsModal>\n {children}\n </View>\n </CloseContext.Provider>\n </AnchoredOverlay>\n </>\n )\n}\n\n/** Fecha o popover ao pressionar. Envolva um `<Button>` dentro do conteúdo (injeta `onPress`). */\nexport function PopoverClose({ children }: { children: ReactNode }) {\n const close = useContext(CloseContext)\n return <>{injectHandler(children, 'onPress', () => close?.())}</>\n}\n"],"mappings":";;;;;;;;AASA,MAAM,eAAe,cAAmC,IAAI;;;;;;AAuB5D,SAAgB,QAAQ,EACtB,MACA,aACA,cACA,OAAO,UACP,SACA,OACA,UACA,WACA,UACe;CACf,MAAM,CAAC,QAAQ,WAAW,aAAa;EAAE;EAAM;EAAa;CAAa,CAAC;CAC1E,MAAM,EAAE,KAAK,SAAS,oBAAoB,MAAM;CAChD,MAAM,cAAc,QAAQ,KAAK;CAEjC,OACE,qBAAA,YAAA,EAAA,UAAA,CACE,oBAAC,eAAD;EAAe,WAAW;EAAK,OAAM;EAAU,iBAAiB,QAAQ,CAAC,MAAM;EAC5E,UAAA;CACY,CAAA,GACf,oBAAC,iBAAD;EACE,SAAS;EACH;EACN,gBAAgB;EAChB,WAAW;EACX,QAAQ;EACA;EACR,WAAW,GACT,sFACA,SACF;EAEA,UAAA,oBAAC,aAAa,UAAd;GAAuB,OAAO;GAC5B,UAAA,oBAAC,MAAD;IAAM,oBAAoB;IAAO,0BAAA;IAC9B;GACG,CAAA;EACe,CAAA;CACR,CAAA,CACjB,EAAA,CAAA;AAEN;;AAGA,SAAgB,aAAa,EAAE,YAAqC;CAClE,MAAM,QAAQ,WAAW,YAAY;CACrC,OAAO,oBAAA,YAAA,EAAA,UAAG,cAAc,UAAU,iBAAiB,QAAQ,CAAC,EAAI,CAAA;AAClE"}
@@ -0,0 +1,2 @@
1
+ import { Radio, RadioProps } from "./radio.js";
2
+ export { Radio, type RadioProps };
@@ -0,0 +1,16 @@
1
+ import { ReactNode } from "react";
2
+ import { PressableProps } from "react-native";
3
+ //#region src/components/radio/radio.d.ts
4
+ export type RadioProps = Omit<PressableProps, 'children' | 'style' | 'onPress'> & {
5
+ className?: string;
6
+ label?: ReactNode;
7
+ /** Controlado. No native não há `name`: para formar um grupo, controle `checked` de cada opção. */
8
+ checked?: boolean;
9
+ defaultChecked?: boolean;
10
+ /** Chamado com `true` ao selecionar; tocar numa opção já marcada não faz nada. */
11
+ onCheckedChange?: (checked: boolean) => void;
12
+ error?: boolean;
13
+ };
14
+ export declare function Radio({ label, checked, defaultChecked, onCheckedChange, error, disabled, className, accessibilityState, ...props }: RadioProps): import("react").JSX.Element;
15
+ //#endregion
16
+ //# sourceMappingURL=radio.d.ts.map
@@ -0,0 +1,37 @@
1
+ import { cn } from "../../utils/cn.js";
2
+ import { useState } from "react";
3
+ import { Pressable, Text, View } from "react-native";
4
+ import { jsx, jsxs } from "react/jsx-runtime";
5
+ //#region src/components/radio/radio.tsx
6
+ function Radio({ label, checked, defaultChecked = false, onCheckedChange, error, disabled, className, accessibilityState, ...props }) {
7
+ const [uncontrolled, setUncontrolled] = useState(defaultChecked);
8
+ const isChecked = checked ?? uncontrolled;
9
+ const isDisabled = Boolean(disabled);
10
+ return /* @__PURE__ */ jsxs(Pressable, {
11
+ accessibilityRole: "radio",
12
+ accessibilityState: {
13
+ ...accessibilityState,
14
+ checked: isChecked,
15
+ disabled: isDisabled
16
+ },
17
+ disabled: isDisabled,
18
+ onPress: () => {
19
+ if (isChecked) return;
20
+ setUncontrolled(true);
21
+ onCheckedChange?.(true);
22
+ },
23
+ className: cn("min-h-11 flex-row items-center gap-3 self-start", className),
24
+ ...props,
25
+ children: [/* @__PURE__ */ jsx(View, {
26
+ className: cn("size-5 items-center justify-center rounded-full border-2 border-selection-border-unselected bg-selection-bg-unselected", isChecked && "border-selection-border-selected bg-selection-bg-selected", isDisabled && "border-selection-border-disabled bg-selection-bg-disabled", error && "border-status-error-border"),
27
+ children: isChecked ? /* @__PURE__ */ jsx(View, { className: cn("size-2.5 rounded-full bg-selection-indicator-selected", isDisabled && "bg-selection-text-disabled") }) : null
28
+ }), typeof label === "string" ? /* @__PURE__ */ jsx(Text, {
29
+ className: cn("text-sm font-medium text-selection-text-label", isDisabled && "text-selection-text-disabled"),
30
+ children: label
31
+ }) : label]
32
+ });
33
+ }
34
+ //#endregion
35
+ export { Radio };
36
+
37
+ //# sourceMappingURL=radio.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"radio.js","names":[],"sources":["../../../src/components/radio/radio.tsx"],"sourcesContent":["/// <reference types=\"uniwind/types\" />\n\nimport { type ReactNode, useState } from 'react'\nimport { Pressable, type PressableProps, Text, View } from 'react-native'\nimport { cn } from '../../utils/cn'\n\nexport type RadioProps = Omit<PressableProps, 'children' | 'style' | 'onPress'> & {\n className?: string\n label?: ReactNode\n /** Controlado. No native não há `name`: para formar um grupo, controle `checked` de cada opção. */\n checked?: boolean\n defaultChecked?: boolean\n /** Chamado com `true` ao selecionar; tocar numa opção já marcada não faz nada. */\n onCheckedChange?: (checked: boolean) => void\n error?: boolean\n}\n\nexport function Radio({\n label,\n checked,\n defaultChecked = false,\n onCheckedChange,\n error,\n disabled,\n className,\n accessibilityState,\n ...props\n}: RadioProps) {\n const [uncontrolled, setUncontrolled] = useState(defaultChecked)\n const isChecked = checked ?? uncontrolled\n const isDisabled = Boolean(disabled)\n\n return (\n <Pressable\n accessibilityRole=\"radio\"\n accessibilityState={{ ...accessibilityState, checked: isChecked, disabled: isDisabled }}\n disabled={isDisabled}\n onPress={() => {\n if (isChecked) return\n setUncontrolled(true)\n onCheckedChange?.(true)\n }}\n className={cn('min-h-11 flex-row items-center gap-3 self-start', className)}\n {...props}\n >\n <View\n className={cn(\n 'size-5 items-center justify-center rounded-full border-2 border-selection-border-unselected bg-selection-bg-unselected',\n isChecked && 'border-selection-border-selected bg-selection-bg-selected',\n isDisabled && 'border-selection-border-disabled bg-selection-bg-disabled',\n error && 'border-status-error-border',\n )}\n >\n {isChecked ? (\n <View\n className={cn(\n 'size-2.5 rounded-full bg-selection-indicator-selected',\n isDisabled && 'bg-selection-text-disabled',\n )}\n />\n ) : null}\n </View>\n {typeof label === 'string' ? (\n <Text\n className={cn(\n 'text-sm font-medium text-selection-text-label',\n isDisabled && 'text-selection-text-disabled',\n )}\n >\n {label}\n </Text>\n ) : (\n label\n )}\n </Pressable>\n )\n}\n"],"mappings":";;;;;AAiBA,SAAgB,MAAM,EACpB,OACA,SACA,iBAAiB,OACjB,iBACA,OACA,UACA,WACA,oBACA,GAAG,SACU;CACb,MAAM,CAAC,cAAc,mBAAmB,SAAS,cAAc;CAC/D,MAAM,YAAY,WAAW;CAC7B,MAAM,aAAa,QAAQ,QAAQ;CAEnC,OACE,qBAAC,WAAD;EACE,mBAAkB;EAClB,oBAAoB;GAAE,GAAG;GAAoB,SAAS;GAAW,UAAU;EAAW;EACtF,UAAU;EACV,eAAe;GACb,IAAI,WAAW;GACf,gBAAgB,IAAI;GACpB,kBAAkB,IAAI;EACxB;EACA,WAAW,GAAG,mDAAmD,SAAS;EAC1E,GAAI;EAVN,UAAA,CAYE,oBAAC,MAAD;GACE,WAAW,GACT,0HACA,aAAa,6DACb,cAAc,6DACd,SAAS,4BACX;GAEC,UAAA,YACC,oBAAC,MAAD,EACE,WAAW,GACT,yDACA,cAAc,4BAChB,EACD,CAAA,IACC;EACA,CAAA,GACL,OAAO,UAAU,WAChB,oBAAC,MAAD;GACE,WAAW,GACT,iDACA,cAAc,8BAChB;GAEC,UAAA;EACG,CAAA,IAEN,KAEO;;AAEf"}
@@ -0,0 +1,2 @@
1
+ import { SegmentedControl, SegmentedControlProps, SegmentedItem, SegmentedItemProps } from "./segmented-control.js";
2
+ export { SegmentedControl, type SegmentedControlProps, SegmentedItem, type SegmentedItemProps };
@@ -0,0 +1,23 @@
1
+ import { ReactNode } from "react";
2
+ import { PressableProps, ViewProps } from "react-native";
3
+ //#region src/components/segmented-control/segmented-control.d.ts
4
+ export type SegmentedControlProps = Omit<ViewProps, 'style'> & {
5
+ className?: string;
6
+ /** Obrigatório: descreve o que o controle escolhe (ex.: "Modo de exibição"). */
7
+ accessibilityLabel: string;
8
+ /** Controlado. Sem ele, o componente guarda o próprio estado (começa em `defaultValue`). */
9
+ value?: string;
10
+ defaultValue?: string;
11
+ onValueChange?: (value: string) => void;
12
+ children: ReactNode;
13
+ };
14
+ /** Escolha única entre 2–5 views do mesmo conteúdo. Para navegar entre seções, use Tabs. */
15
+ export declare function SegmentedControl({ className, value, defaultValue, onValueChange, ...props }: SegmentedControlProps): import("react").JSX.Element;
16
+ export type SegmentedItemProps = Omit<PressableProps, 'style' | 'children' | 'onPress'> & {
17
+ className?: string;
18
+ value: string;
19
+ children: ReactNode;
20
+ };
21
+ export declare function SegmentedItem({ className, value, children, disabled, accessibilityState, ...props }: SegmentedItemProps): import("react").JSX.Element;
22
+ //#endregion
23
+ //# sourceMappingURL=segmented-control.d.ts.map
@@ -0,0 +1,53 @@
1
+ import { cn } from "../../utils/cn.js";
2
+ import { createContext, useContext, useState } from "react";
3
+ import { Pressable, Text, View } from "react-native";
4
+ import { jsx } from "react/jsx-runtime";
5
+ //#region src/components/segmented-control/segmented-control.tsx
6
+ const Context = createContext(null);
7
+ /** Escolha única entre 2–5 views do mesmo conteúdo. Para navegar entre seções, use Tabs. */
8
+ function SegmentedControl({ className, value, defaultValue, onValueChange, ...props }) {
9
+ const [uncontrolled, setUncontrolled] = useState(defaultValue);
10
+ const current = value ?? uncontrolled;
11
+ const select = (next) => {
12
+ if (next === current) return;
13
+ setUncontrolled(next);
14
+ onValueChange?.(next);
15
+ };
16
+ return /* @__PURE__ */ jsx(Context.Provider, {
17
+ value: {
18
+ value: current,
19
+ select
20
+ },
21
+ children: /* @__PURE__ */ jsx(View, {
22
+ accessibilityRole: "radiogroup",
23
+ className: cn("flex-row items-start gap-1 self-start rounded-md bg-bg-tertiary p-1", className),
24
+ ...props
25
+ })
26
+ });
27
+ }
28
+ function SegmentedItem({ className, value, children, disabled, accessibilityState, ...props }) {
29
+ const group = useContext(Context);
30
+ if (!group) throw new Error("SegmentedItem deve ser usado dentro de SegmentedControl");
31
+ const checked = group.value === value;
32
+ const isDisabled = Boolean(disabled);
33
+ return /* @__PURE__ */ jsx(Pressable, {
34
+ accessibilityRole: "radio",
35
+ accessibilityState: {
36
+ ...accessibilityState,
37
+ checked,
38
+ disabled: isDisabled
39
+ },
40
+ disabled: isDisabled,
41
+ onPress: () => group.select(value),
42
+ className: cn("items-center justify-center rounded-md px-4 py-2", checked ? "bg-surface-primary shadow-sm" : "active:bg-bg-secondary", className),
43
+ ...props,
44
+ children: typeof children === "string" ? /* @__PURE__ */ jsx(Text, {
45
+ className: cn("text-sm leading-5 text-text-secondary", checked && "font-medium text-text-primary", isDisabled && "text-text-disabled"),
46
+ children
47
+ }) : children
48
+ });
49
+ }
50
+ //#endregion
51
+ export { SegmentedControl, SegmentedItem };
52
+
53
+ //# sourceMappingURL=segmented-control.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"segmented-control.js","names":[],"sources":["../../../src/components/segmented-control/segmented-control.tsx"],"sourcesContent":["/// <reference types=\"uniwind/types\" />\n\nimport { createContext, type ReactNode, useContext, useState } from 'react'\nimport { Pressable, type PressableProps, Text, View, type ViewProps } from 'react-native'\nimport { cn } from '../../utils/cn'\n\ntype SegmentedContext = { value: string | undefined; select: (value: string) => void }\n\nconst Context = createContext<SegmentedContext | null>(null)\n\nexport type SegmentedControlProps = Omit<ViewProps, 'style'> & {\n className?: string\n /** Obrigatório: descreve o que o controle escolhe (ex.: \"Modo de exibição\"). */\n accessibilityLabel: string\n /** Controlado. Sem ele, o componente guarda o próprio estado (começa em `defaultValue`). */\n value?: string\n defaultValue?: string\n onValueChange?: (value: string) => void\n children: ReactNode\n}\n\n/** Escolha única entre 2–5 views do mesmo conteúdo. Para navegar entre seções, use Tabs. */\nexport function SegmentedControl({\n className,\n value,\n defaultValue,\n onValueChange,\n ...props\n}: SegmentedControlProps) {\n const [uncontrolled, setUncontrolled] = useState(defaultValue)\n const current = value ?? uncontrolled\n\n const select = (next: string) => {\n if (next === current) return\n setUncontrolled(next)\n onValueChange?.(next)\n }\n\n return (\n <Context.Provider value={{ value: current, select }}>\n <View\n accessibilityRole=\"radiogroup\"\n className={cn(\n 'flex-row items-start gap-1 self-start rounded-md bg-bg-tertiary p-1',\n className,\n )}\n {...props}\n />\n </Context.Provider>\n )\n}\n\nexport type SegmentedItemProps = Omit<PressableProps, 'style' | 'children' | 'onPress'> & {\n className?: string\n value: string\n children: ReactNode\n}\n\nexport function SegmentedItem({\n className,\n value,\n children,\n disabled,\n accessibilityState,\n ...props\n}: SegmentedItemProps) {\n const group = useContext(Context)\n if (!group) throw new Error('SegmentedItem deve ser usado dentro de SegmentedControl')\n\n const checked = group.value === value\n const isDisabled = Boolean(disabled)\n\n return (\n <Pressable\n accessibilityRole=\"radio\"\n accessibilityState={{ ...accessibilityState, checked, disabled: isDisabled }}\n disabled={isDisabled}\n onPress={() => group.select(value)}\n className={cn(\n 'items-center justify-center rounded-md px-4 py-2',\n checked ? 'bg-surface-primary shadow-sm' : 'active:bg-bg-secondary',\n className,\n )}\n {...props}\n >\n {typeof children === 'string' ? (\n <Text\n className={cn(\n 'text-sm leading-5 text-text-secondary',\n checked && 'font-medium text-text-primary',\n isDisabled && 'text-text-disabled',\n )}\n >\n {children}\n </Text>\n ) : (\n children\n )}\n </Pressable>\n )\n}\n"],"mappings":";;;;;AAQA,MAAM,UAAU,cAAuC,IAAI;;AAc3D,SAAgB,iBAAiB,EAC/B,WACA,OACA,cACA,eACA,GAAG,SACqB;CACxB,MAAM,CAAC,cAAc,mBAAmB,SAAS,YAAY;CAC7D,MAAM,UAAU,SAAS;CAEzB,MAAM,UAAU,SAAiB;EAC/B,IAAI,SAAS,SAAS;EACtB,gBAAgB,IAAI;EACpB,gBAAgB,IAAI;CACtB;CAEA,OACE,oBAAC,QAAQ,UAAT;EAAkB,OAAO;GAAE,OAAO;GAAS;EAAO;EAChD,UAAA,oBAAC,MAAD;GACE,mBAAkB;GAClB,WAAW,GACT,uEACA,SACF;GACA,GAAI;EACL,CAAA;CACe,CAAA;AAEtB;AAQA,SAAgB,cAAc,EAC5B,WACA,OACA,UACA,UACA,oBACA,GAAG,SACkB;CACrB,MAAM,QAAQ,WAAW,OAAO;CAChC,IAAI,CAAC,OAAO,MAAM,IAAI,MAAM,yDAAyD;CAErF,MAAM,UAAU,MAAM,UAAU;CAChC,MAAM,aAAa,QAAQ,QAAQ;CAEnC,OACE,oBAAC,WAAD;EACE,mBAAkB;EAClB,oBAAoB;GAAE,GAAG;GAAoB;GAAS,UAAU;EAAW;EAC3E,UAAU;EACV,eAAe,MAAM,OAAO,KAAK;EACjC,WAAW,GACT,oDACA,UAAU,iCAAiC,0BAC3C,SACF;EACA,GAAI;EAEH,UAAA,OAAO,aAAa,WACnB,oBAAC,MAAD;GACE,WAAW,GACT,yCACA,WAAW,iCACX,cAAc,oBAChB;GAEC;EACG,CAAA,IAEN;CAEO,CAAA;AAEf"}
@@ -0,0 +1,6 @@
1
+ import { ReactNode } from "react";
2
+ import { View } from "react-native";
3
+ //#region src/components/select/field-trigger.d.ts
4
+ export type FieldSize = 'sm' | 'md' | 'lg';
5
+ //#endregion
6
+ //# sourceMappingURL=field-trigger.d.ts.map