@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 @@
1
+ {"version":3,"file":"field.js","names":[],"sources":["../../../src/components/field/field.tsx"],"sourcesContent":["/// <reference types=\"uniwind/types\" />\n\nimport { cloneElement, type ReactElement, type ReactNode } from 'react'\nimport { Text, View } from 'react-native'\nimport { cn } from '../../utils/cn'\nimport { StatusIcon } from '../_shared/status'\nimport { useToken } from '../_shared/use-token'\n\nexport type FieldProps = {\n /** Texto do rótulo. */\n label: string\n /** Exibe um asterisco vermelho após o rótulo. */\n required?: boolean\n /** Propaga vermelho para rótulo, borda do controle e texto de apoio. */\n error?: boolean\n disabled?: boolean\n /** Propaga somente leitura ao controle. */\n readOnly?: boolean\n /** Tamanho do rótulo e do controle (o `sm` usa rótulo de 12px). */\n size?: 'sm' | 'md' | 'lg'\n /** Instrução (estado normal) ou mensagem de erro (estado error). */\n supportingText?: ReactNode\n /** O controle: Input, Textarea ou Select. Recebe disabled/error/size e o nome acessível. */\n children: ReactElement<{\n disabled?: boolean\n readOnly?: boolean\n size?: 'sm' | 'md' | 'lg'\n error?: boolean\n accessibilityLabel?: string\n accessibilityHint?: string\n }>\n className?: string\n}\n\n/** Combina rótulo + controle + texto de apoio — o jeito certo de montar um campo de formulário. */\nexport function Field({\n label,\n required,\n error,\n disabled,\n readOnly,\n size,\n supportingText,\n children,\n className,\n}: FieldProps) {\n const errorColor = useToken('--color-status-error-text')\n\n return (\n <View className={cn('gap-1', className)}>\n <Text\n className={cn(\n 'font-medium text-field-text-label',\n size === 'sm' ? 'text-xs leading-4' : 'text-sm',\n error && 'text-status-error-text',\n )}\n >\n {label}\n {required ? (\n // Sem `htmlFor` no native: o nome acessível vai por accessibilityLabel no controle,\n // então o asterisco fica escondido do leitor de tela.\n <Text\n className=\"text-status-error-text\"\n accessibilityElementsHidden\n importantForAccessibility=\"no\"\n >\n {' *'}\n </Text>\n ) : null}\n </Text>\n {cloneElement(children, {\n disabled: disabled ?? children.props.disabled,\n readOnly: readOnly ?? children.props.readOnly,\n size: size ?? children.props.size,\n error: error ?? children.props.error,\n accessibilityLabel: children.props.accessibilityLabel ?? label,\n accessibilityHint:\n children.props.accessibilityHint ??\n (typeof supportingText === 'string' ? supportingText : undefined),\n })}\n {supportingText ? (\n <View\n className=\"flex-row items-center gap-1\"\n // Mensagem de erro é anunciada assim que aparece.\n accessibilityLiveRegion={error ? 'polite' : undefined}\n >\n {error ? (\n <StatusIcon status=\"error\" color={errorColor} style={{ width: 16, height: 16 }} />\n ) : null}\n {typeof supportingText === 'string' ? (\n <Text\n className={cn(\n 'shrink text-xs leading-4 text-field-text-label',\n error && 'text-status-error-text',\n )}\n >\n {supportingText}\n </Text>\n ) : (\n supportingText\n )}\n </View>\n ) : null}\n </View>\n )\n}\n"],"mappings":";;;;;;;;AAmCA,SAAgB,MAAM,EACpB,OACA,UACA,OACA,UACA,UACA,MACA,gBACA,UACA,aACa;CACb,MAAM,aAAa,SAAS,2BAA2B;CAEvD,OACE,qBAAC,MAAD;EAAM,WAAW,GAAG,SAAS,SAAS;EAAtC,UAAA;GACE,qBAAC,MAAD;IACE,WAAW,GACT,qCACA,SAAS,OAAO,sBAAsB,WACtC,SAAS,wBACX;IALF,UAAA,CAOG,OACA,WAGC,oBAAC,MAAD;KACE,WAAU;KACV,6BAAA;KACA,2BAA0B;KAEzB,UAAA;IACG,CAAA,IACJ,IACA;;GACL,aAAa,UAAU;IACtB,UAAU,YAAY,SAAS,MAAM;IACrC,UAAU,YAAY,SAAS,MAAM;IACrC,MAAM,QAAQ,SAAS,MAAM;IAC7B,OAAO,SAAS,SAAS,MAAM;IAC/B,oBAAoB,SAAS,MAAM,sBAAsB;IACzD,mBACE,SAAS,MAAM,sBACd,OAAO,mBAAmB,WAAW,iBAAiB,KAAA;GAC3D,CAAC;GACA,iBACC,qBAAC,MAAD;IACE,WAAU;IAEV,yBAAyB,QAAQ,WAAW,KAAA;IAH9C,UAAA,CAKG,QACC,oBAAC,YAAD;KAAY,QAAO;KAAQ,OAAO;KAAY,OAAO;MAAE,OAAO;MAAI,QAAQ;KAAG;IAAI,CAAA,IAC/E,MACH,OAAO,mBAAmB,WACzB,oBAAC,MAAD;KACE,WAAW,GACT,kDACA,SAAS,wBACX;KAEC,UAAA;IACG,CAAA,IAEN,cAEE;GACJ,CAAA,IAAA;EACA;;AAEV"}
@@ -0,0 +1,2 @@
1
+ import { Field, FieldProps } from "./field.js";
2
+ export { Field, type FieldProps };
@@ -0,0 +1,35 @@
1
+ import { ViewProps } from "react-native";
2
+ //#region src/components/grid/grid.d.ts
3
+ declare const gapPx: {
4
+ readonly 0: 0;
5
+ readonly 1: 4;
6
+ readonly 2: 8;
7
+ readonly 3: 12;
8
+ readonly 4: 16;
9
+ readonly 6: 24;
10
+ readonly 8: 32;
11
+ };
12
+ type Columns = 'responsive' | 1 | 2 | 3 | 4 | 6 | 12;
13
+ type Gap = keyof typeof gapPx;
14
+ export type GridProps = ViewProps & {
15
+ className?: string;
16
+ /** `responsive` segue o grid do DS: 4 colunas, 8 a partir de 768px e 12 a partir de 1440px. */
17
+ columns?: Columns;
18
+ /** Espaçamento entre células, na escala do DS (px = valor x 4). */
19
+ gap?: Gap;
20
+ };
21
+ /**
22
+ * Grid de colunas iguais. Não há CSS grid no RN: a largura de cada `GridItem` é calculada a
23
+ * partir da largura medida do Grid (`onLayout`) e as células quebram linha com `flex-wrap`.
24
+ * Antes da primeira medição, usa porcentagem (sem descontar o gap).
25
+ */
26
+ export declare function Grid({ className, columns, gap, onLayout, children, ...props }: GridProps): import("react").JSX.Element;
27
+ export type GridItemProps = ViewProps & {
28
+ className?: string;
29
+ /** Quantas colunas a célula ocupa (`full` ocupa a linha toda). Padrão: 1. */
30
+ span?: 1 | 2 | 3 | 4 | 6 | 8 | 12 | 'full';
31
+ };
32
+ /** Célula do Grid que ocupa `span` colunas (limitado ao total de colunas atual). */
33
+ export declare function GridItem({ className, span, style, ...props }: GridItemProps): import("react").JSX.Element;
34
+ //#endregion
35
+ //# sourceMappingURL=grid.d.ts.map
@@ -0,0 +1,64 @@
1
+ import { cn } from "../../utils/cn.js";
2
+ import { createContext, useContext, useState } from "react";
3
+ import { View, useWindowDimensions } from "react-native";
4
+ import { jsx } from "react/jsx-runtime";
5
+ //#region src/components/grid/grid.tsx
6
+ const TABLET_MIN_WIDTH = 768;
7
+ const DESKTOP_MIN_WIDTH = 1440;
8
+ const gapPx = {
9
+ 0: 0,
10
+ 1: 4,
11
+ 2: 8,
12
+ 3: 12,
13
+ 4: 16,
14
+ 6: 24,
15
+ 8: 32
16
+ };
17
+ const GridContext = createContext({
18
+ columns: 1,
19
+ gap: 0,
20
+ width: 0
21
+ });
22
+ /**
23
+ * Grid de colunas iguais. Não há CSS grid no RN: a largura de cada `GridItem` é calculada a
24
+ * partir da largura medida do Grid (`onLayout`) e as células quebram linha com `flex-wrap`.
25
+ * Antes da primeira medição, usa porcentagem (sem descontar o gap).
26
+ */
27
+ function Grid({ className, columns = "responsive", gap = 4, onLayout, children, ...props }) {
28
+ const { width: windowWidth } = useWindowDimensions();
29
+ const [width, setWidth] = useState(0);
30
+ const count = columns === "responsive" ? windowWidth >= DESKTOP_MIN_WIDTH ? 12 : windowWidth >= TABLET_MIN_WIDTH ? 8 : 4 : columns;
31
+ const handleLayout = (event) => {
32
+ setWidth(event.nativeEvent.layout.width);
33
+ onLayout?.(event);
34
+ };
35
+ return /* @__PURE__ */ jsx(GridContext.Provider, {
36
+ value: {
37
+ columns: count,
38
+ gap: gapPx[gap],
39
+ width
40
+ },
41
+ children: /* @__PURE__ */ jsx(View, {
42
+ className: cn("flex-row flex-wrap", className),
43
+ style: { gap: gapPx[gap] },
44
+ onLayout: handleLayout,
45
+ ...props,
46
+ children
47
+ })
48
+ });
49
+ }
50
+ /** Célula do Grid que ocupa `span` colunas (limitado ao total de colunas atual). */
51
+ function GridItem({ className, span = 1, style, ...props }) {
52
+ const { columns, gap, width } = useContext(GridContext);
53
+ const n = span === "full" ? columns : Math.min(span, columns);
54
+ const itemWidth = width > 0 ? (width - gap * (columns - 1)) * (n / columns) + gap * (n - 1) : `${n / columns * 100}%`;
55
+ return /* @__PURE__ */ jsx(View, {
56
+ className: cn(className),
57
+ style: [{ width: itemWidth }, style],
58
+ ...props
59
+ });
60
+ }
61
+ //#endregion
62
+ export { Grid, GridItem };
63
+
64
+ //# sourceMappingURL=grid.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"grid.js","names":[],"sources":["../../../src/components/grid/grid.tsx"],"sourcesContent":["/// <reference types=\"uniwind/types\" />\n\nimport { createContext, useContext, useState } from 'react'\nimport { type LayoutChangeEvent, useWindowDimensions, View, type ViewProps } from 'react-native'\nimport { cn } from '../../utils/cn'\n\nconst TABLET_MIN_WIDTH = 768\nconst DESKTOP_MIN_WIDTH = 1440\n\n// escala de gap do DS (base 4): chave -> px\nconst gapPx = { 0: 0, 1: 4, 2: 8, 3: 12, 4: 16, 6: 24, 8: 32 } as const\n\ntype Columns = 'responsive' | 1 | 2 | 3 | 4 | 6 | 12\ntype Gap = keyof typeof gapPx\n\ntype GridContextValue = { columns: number; gap: number; width: number }\nconst GridContext = createContext<GridContextValue>({ columns: 1, gap: 0, width: 0 })\n\nexport type GridProps = ViewProps & {\n className?: string\n /** `responsive` segue o grid do DS: 4 colunas, 8 a partir de 768px e 12 a partir de 1440px. */\n columns?: Columns\n /** Espaçamento entre células, na escala do DS (px = valor x 4). */\n gap?: Gap\n}\n\n/**\n * Grid de colunas iguais. Não há CSS grid no RN: a largura de cada `GridItem` é calculada a\n * partir da largura medida do Grid (`onLayout`) e as células quebram linha com `flex-wrap`.\n * Antes da primeira medição, usa porcentagem (sem descontar o gap).\n */\nexport function Grid({\n className,\n columns = 'responsive',\n gap = 4,\n onLayout,\n children,\n ...props\n}: GridProps) {\n const { width: windowWidth } = useWindowDimensions()\n const [width, setWidth] = useState(0)\n\n const count =\n columns === 'responsive'\n ? windowWidth >= DESKTOP_MIN_WIDTH\n ? 12\n : windowWidth >= TABLET_MIN_WIDTH\n ? 8\n : 4\n : columns\n\n const handleLayout = (event: LayoutChangeEvent) => {\n setWidth(event.nativeEvent.layout.width)\n onLayout?.(event)\n }\n\n return (\n <GridContext.Provider value={{ columns: count, gap: gapPx[gap], width }}>\n <View\n className={cn('flex-row flex-wrap', className)}\n style={{ gap: gapPx[gap] }}\n onLayout={handleLayout}\n {...props}\n >\n {children}\n </View>\n </GridContext.Provider>\n )\n}\n\nexport type GridItemProps = ViewProps & {\n className?: string\n /** Quantas colunas a célula ocupa (`full` ocupa a linha toda). Padrão: 1. */\n span?: 1 | 2 | 3 | 4 | 6 | 8 | 12 | 'full'\n}\n\n/** Célula do Grid que ocupa `span` colunas (limitado ao total de colunas atual). */\nexport function GridItem({ className, span = 1, style, ...props }: GridItemProps) {\n const { columns, gap, width } = useContext(GridContext)\n const n = span === 'full' ? columns : Math.min(span, columns)\n\n const itemWidth =\n width > 0\n ? (width - gap * (columns - 1)) * (n / columns) + gap * (n - 1)\n : `${(n / columns) * 100}%`\n\n return (\n <View className={cn(className)} style={[{ width: itemWidth as number }, style]} {...props} />\n )\n}\n"],"mappings":";;;;;AAMA,MAAM,mBAAmB;AACzB,MAAM,oBAAoB;AAG1B,MAAM,QAAQ;CAAE,GAAG;CAAG,GAAG;CAAG,GAAG;CAAG,GAAG;CAAI,GAAG;CAAI,GAAG;CAAI,GAAG;AAAG;AAM7D,MAAM,cAAc,cAAgC;CAAE,SAAS;CAAG,KAAK;CAAG,OAAO;AAAE,CAAC;;;;;;AAepF,SAAgB,KAAK,EACnB,WACA,UAAU,cACV,MAAM,GACN,UACA,UACA,GAAG,SACS;CACZ,MAAM,EAAE,OAAO,gBAAgB,oBAAoB;CACnD,MAAM,CAAC,OAAO,YAAY,SAAS,CAAC;CAEpC,MAAM,QACJ,YAAY,eACR,eAAe,oBACb,KACA,eAAe,mBACb,IACA,IACJ;CAEN,MAAM,gBAAgB,UAA6B;EACjD,SAAS,MAAM,YAAY,OAAO,KAAK;EACvC,WAAW,KAAK;CAClB;CAEA,OACE,oBAAC,YAAY,UAAb;EAAsB,OAAO;GAAE,SAAS;GAAO,KAAK,MAAM;GAAM;EAAM;EACpE,UAAA,oBAAC,MAAD;GACE,WAAW,GAAG,sBAAsB,SAAS;GAC7C,OAAO,EAAE,KAAK,MAAM,KAAK;GACzB,UAAU;GACV,GAAI;GAEH;EACG,CAAA;CACc,CAAA;AAE1B;;AASA,SAAgB,SAAS,EAAE,WAAW,OAAO,GAAG,OAAO,GAAG,SAAwB;CAChF,MAAM,EAAE,SAAS,KAAK,UAAU,WAAW,WAAW;CACtD,MAAM,IAAI,SAAS,SAAS,UAAU,KAAK,IAAI,MAAM,OAAO;CAE5D,MAAM,YACJ,QAAQ,KACH,QAAQ,OAAO,UAAU,OAAO,IAAI,WAAW,OAAO,IAAI,KAC3D,GAAI,IAAI,UAAW,IAAI;CAE7B,OACE,oBAAC,MAAD;EAAM,WAAW,GAAG,SAAS;EAAG,OAAO,CAAC,EAAE,OAAO,UAAoB,GAAG,KAAK;EAAG,GAAI;CAAQ,CAAA;AAEhG"}
@@ -0,0 +1,2 @@
1
+ import { Grid, GridItem, GridItemProps, GridProps } from "./grid.js";
2
+ export { Grid, GridItem, type GridItemProps, type GridProps };
@@ -0,0 +1,23 @@
1
+ import { ReactNode } from "react";
2
+ import { PressableProps } from "react-native";
3
+ import { VariantProps } from "class-variance-authority";
4
+ //#region src/components/icon-button/icon-button.d.ts
5
+ declare const iconButtonVariants: (props?: ({
6
+ variant?: "destructive" | "ghost" | "primary" | "secondary" | "tertiary" | null | undefined;
7
+ size?: "lg" | "md" | "sm" | null | undefined;
8
+ } & import("class-variance-authority/types").ClassProp) | undefined) => string;
9
+ export type IconButtonProps = Omit<PressableProps, 'children' | 'style' | 'accessibilityLabel'> & VariantProps<typeof iconButtonVariants> & {
10
+ className?: string;
11
+ /** Obrigatório: sem texto visível, é o nome acessível do botão. */
12
+ accessibilityLabel: string;
13
+ /**
14
+ * O ícone. No native a cor não é herdada: passe `color` ao ícone (ex.: lida com `useCSSVariable`, do uniwind).
15
+ * O tamanho do ícone também fica por conta de quem passa (24/20/16 por `size`).
16
+ */
17
+ children: ReactNode;
18
+ /** Estado de carregamento: troca o ícone por um spinner e desabilita o botão. */
19
+ loading?: boolean;
20
+ };
21
+ export declare function IconButton({ className, variant, size, loading, disabled, children, accessibilityState, ...props }: IconButtonProps): import("react").JSX.Element;
22
+ //#endregion
23
+ //# sourceMappingURL=icon-button.d.ts.map
@@ -0,0 +1,48 @@
1
+ import { cn } from "../../utils/cn.js";
2
+ import { Spinner } from "../spinner/spinner.js";
3
+ import { Pressable } from "react-native";
4
+ import { jsx } from "react/jsx-runtime";
5
+ import { cva } from "class-variance-authority";
6
+ //#region src/components/icon-button/icon-button.tsx
7
+ const iconButtonVariants = cva("shrink-0 items-center justify-center rounded-full", {
8
+ variants: {
9
+ variant: {
10
+ primary: "bg-button-primary-bg active:bg-button-primary-bg-active disabled:bg-button-disabled-bg",
11
+ secondary: "bg-button-secondary-bg active:bg-button-secondary-bg-hover disabled:bg-button-disabled-bg",
12
+ tertiary: "border border-button-tertiary-border bg-transparent active:bg-bg-secondary",
13
+ ghost: "bg-transparent active:bg-button-secondary-bg-hover",
14
+ destructive: "bg-button-destructive-bg active:bg-button-destructive-bg-active disabled:bg-button-disabled-bg"
15
+ },
16
+ size: {
17
+ lg: "size-14",
18
+ md: "size-11",
19
+ sm: "size-9"
20
+ }
21
+ },
22
+ defaultVariants: {
23
+ variant: "primary",
24
+ size: "md"
25
+ }
26
+ });
27
+ function IconButton({ className, variant, size, loading = false, disabled, children, accessibilityState, ...props }) {
28
+ const isDisabled = Boolean(disabled || loading);
29
+ return /* @__PURE__ */ jsx(Pressable, {
30
+ accessibilityRole: "button",
31
+ accessibilityState: {
32
+ ...accessibilityState,
33
+ disabled: isDisabled,
34
+ busy: loading
35
+ },
36
+ disabled: isDisabled,
37
+ className: cn(iconButtonVariants({
38
+ variant,
39
+ size
40
+ }), className),
41
+ ...props,
42
+ children: loading ? /* @__PURE__ */ jsx(Spinner, { size: size ?? "md" }) : children
43
+ });
44
+ }
45
+ //#endregion
46
+ export { IconButton };
47
+
48
+ //# sourceMappingURL=icon-button.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"icon-button.js","names":[],"sources":["../../../src/components/icon-button/icon-button.tsx"],"sourcesContent":["/// <reference types=\"uniwind/types\" />\n\nimport { cva, type VariantProps } from 'class-variance-authority'\nimport type { ReactNode } from 'react'\nimport { Pressable, type PressableProps } from 'react-native'\nimport { cn } from '../../utils/cn'\nimport { Spinner } from '../spinner/spinner'\n\n// Mesmos estilos do Button (não exportados de lá). Quadrado 1:1 nas alturas 56/44/36.\nconst iconButtonVariants = cva('shrink-0 items-center justify-center rounded-full', {\n variants: {\n variant: {\n primary:\n 'bg-button-primary-bg active:bg-button-primary-bg-active disabled:bg-button-disabled-bg',\n secondary:\n 'bg-button-secondary-bg active:bg-button-secondary-bg-hover disabled:bg-button-disabled-bg',\n tertiary: 'border border-button-tertiary-border bg-transparent active:bg-bg-secondary',\n ghost: 'bg-transparent active:bg-button-secondary-bg-hover',\n destructive:\n 'bg-button-destructive-bg active:bg-button-destructive-bg-active disabled:bg-button-disabled-bg',\n },\n size: {\n lg: 'size-14',\n md: 'size-11',\n sm: 'size-9',\n },\n },\n defaultVariants: { variant: 'primary', size: 'md' },\n})\n\nexport type IconButtonProps = Omit<PressableProps, 'children' | 'style' | 'accessibilityLabel'> &\n VariantProps<typeof iconButtonVariants> & {\n className?: string\n /** Obrigatório: sem texto visível, é o nome acessível do botão. */\n accessibilityLabel: string\n /**\n * O ícone. No native a cor não é herdada: passe `color` ao ícone (ex.: lida com `useCSSVariable`, do uniwind).\n * O tamanho do ícone também fica por conta de quem passa (24/20/16 por `size`).\n */\n children: ReactNode\n /** Estado de carregamento: troca o ícone por um spinner e desabilita o botão. */\n loading?: boolean\n }\n\nexport function IconButton({\n className,\n variant,\n size,\n loading = false,\n disabled,\n children,\n accessibilityState,\n ...props\n}: IconButtonProps) {\n const isDisabled = Boolean(disabled || loading)\n\n return (\n <Pressable\n accessibilityRole=\"button\"\n accessibilityState={{ ...accessibilityState, disabled: isDisabled, busy: loading }}\n disabled={isDisabled}\n className={cn(iconButtonVariants({ variant, size }), className)}\n {...props}\n >\n {loading ? <Spinner size={size ?? 'md'} /> : children}\n </Pressable>\n )\n}\n"],"mappings":";;;;;;AASA,MAAM,qBAAqB,IAAI,qDAAqD;CAClF,UAAU;EACR,SAAS;GACP,SACE;GACF,WACE;GACF,UAAU;GACV,OAAO;GACP,aACE;EACJ;EACA,MAAM;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;EACN;CACF;CACA,iBAAiB;EAAE,SAAS;EAAW,MAAM;CAAK;AACpD,CAAC;AAgBD,SAAgB,WAAW,EACzB,WACA,SACA,MACA,UAAU,OACV,UACA,UACA,oBACA,GAAG,SACe;CAClB,MAAM,aAAa,QAAQ,YAAY,OAAO;CAE9C,OACE,oBAAC,WAAD;EACE,mBAAkB;EAClB,oBAAoB;GAAE,GAAG;GAAoB,UAAU;GAAY,MAAM;EAAQ;EACjF,UAAU;EACV,WAAW,GAAG,mBAAmB;GAAE;GAAS;EAAK,CAAC,GAAG,SAAS;EAC9D,GAAI;EAEH,UAAA,UAAU,oBAAC,SAAD,EAAS,MAAM,QAAQ,KAAO,CAAA,IAAI;CACpC,CAAA;AAEf"}
@@ -0,0 +1,2 @@
1
+ import { IconButton, IconButtonProps } from "./icon-button.js";
2
+ export { IconButton, type IconButtonProps };
@@ -0,0 +1,2 @@
1
+ import { Input, InputProps } from "./input.js";
2
+ export { Input, type InputProps };
@@ -0,0 +1,22 @@
1
+ import { ReactNode } from "react";
2
+ import { TextInputProps } from "react-native";
3
+ import { VariantProps } from "class-variance-authority";
4
+ //#region src/components/input/input.d.ts
5
+ /** Estilo do campo, compartilhado com o Textarea (não faz parte da API pública). */
6
+ export declare const inputVariants: (props?: ({
7
+ size?: "lg" | "md" | "sm" | null | undefined;
8
+ error?: boolean | null | undefined;
9
+ } & import("class-variance-authority/types").ClassProp) | undefined) => string;
10
+ export type InputProps = Omit<TextInputProps, 'style' | 'editable'> & Omit<VariantProps<typeof inputVariants>, 'error'> & {
11
+ className?: string;
12
+ /** Borda e foco em vermelho. */
13
+ error?: boolean;
14
+ disabled?: boolean;
15
+ /** Ícone antes do texto (o consumidor define a cor; o slot tem 24px). */
16
+ leadingIcon?: ReactNode;
17
+ /** Ícone depois do texto (o consumidor define a cor; o slot tem 24px). */
18
+ trailingIcon?: ReactNode;
19
+ };
20
+ export declare function Input({ className, size, error, disabled, readOnly, leadingIcon, trailingIcon, accessibilityState, ...props }: InputProps): import("react").JSX.Element;
21
+ //#endregion
22
+ //# sourceMappingURL=input.d.ts.map
@@ -0,0 +1,86 @@
1
+ import { cn } from "../../utils/cn.js";
2
+ import { useToken } from "../_shared/use-token.js";
3
+ import { TextInput, View } from "react-native";
4
+ import { jsx, jsxs } from "react/jsx-runtime";
5
+ import { cva } from "class-variance-authority";
6
+ //#region src/components/input/input.tsx
7
+ /** Estilo do campo, compartilhado com o Textarea (não faz parte da API pública). */
8
+ const inputVariants = cva("w-full rounded-md border border-field-border bg-field-bg py-0 text-field-text focus:border-field-focus-ring", {
9
+ variants: {
10
+ size: {
11
+ lg: "h-14 px-4 text-base",
12
+ md: "h-12 px-3 text-base",
13
+ sm: "h-10 px-2 text-sm"
14
+ },
15
+ error: {
16
+ true: "border-field-border-error focus:border-field-border-error",
17
+ false: ""
18
+ }
19
+ },
20
+ defaultVariants: {
21
+ size: "md",
22
+ error: false
23
+ }
24
+ });
25
+ const iconSlot = {
26
+ lg: {
27
+ icon: "w-5",
28
+ left: "left-4",
29
+ right: "right-4",
30
+ padL: "pl-11",
31
+ padR: "pr-11"
32
+ },
33
+ md: {
34
+ icon: "w-6",
35
+ left: "left-3",
36
+ right: "right-3",
37
+ padL: "pl-11",
38
+ padR: "pr-11"
39
+ },
40
+ sm: {
41
+ icon: "w-6",
42
+ left: "left-2",
43
+ right: "right-2",
44
+ padL: "pl-9",
45
+ padR: "pr-9"
46
+ }
47
+ };
48
+ function Input({ className, size, error = false, disabled = false, readOnly = false, leadingIcon, trailingIcon, accessibilityState, ...props }) {
49
+ const placeholderColor = useToken("--color-field-text-placeholder");
50
+ const slot = iconSlot[size ?? "md"];
51
+ const input = /* @__PURE__ */ jsx(TextInput, {
52
+ editable: !disabled && !readOnly,
53
+ readOnly,
54
+ placeholderTextColor: placeholderColor,
55
+ accessibilityState: {
56
+ ...accessibilityState,
57
+ disabled
58
+ },
59
+ className: cn(inputVariants({
60
+ size,
61
+ error
62
+ }), disabled && "border-field-border-disabled bg-field-bg-disabled text-field-text-disabled focus:border-field-border-disabled", readOnly && !disabled && "border-transparent bg-field-bg-disabled focus:border-transparent", leadingIcon && slot.padL, trailingIcon && slot.padR, className),
63
+ ...props
64
+ });
65
+ if (!leadingIcon && !trailingIcon) return input;
66
+ return /* @__PURE__ */ jsxs(View, {
67
+ className: "justify-center",
68
+ children: [
69
+ leadingIcon ? /* @__PURE__ */ jsx(View, {
70
+ pointerEvents: "none",
71
+ className: cn("absolute inset-y-0 z-10 items-center justify-center", slot.icon, slot.left),
72
+ children: leadingIcon
73
+ }) : null,
74
+ input,
75
+ trailingIcon ? /* @__PURE__ */ jsx(View, {
76
+ pointerEvents: "none",
77
+ className: cn("absolute inset-y-0 z-10 items-center justify-center", slot.icon, slot.right),
78
+ children: trailingIcon
79
+ }) : null
80
+ ]
81
+ });
82
+ }
83
+ //#endregion
84
+ export { Input, inputVariants };
85
+
86
+ //# sourceMappingURL=input.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"input.js","names":[],"sources":["../../../src/components/input/input.tsx"],"sourcesContent":["/// <reference types=\"uniwind/types\" />\n\nimport { cva, type VariantProps } from 'class-variance-authority'\nimport type { ReactNode } from 'react'\nimport { TextInput, type TextInputProps, View } from 'react-native'\nimport { cn } from '../../utils/cn'\nimport { useToken } from '../_shared/use-token'\n\n/** Estilo do campo, compartilhado com o Textarea (não faz parte da API pública). */\nexport const inputVariants = cva(\n 'w-full rounded-md border border-field-border bg-field-bg py-0 text-field-text focus:border-field-focus-ring',\n {\n variants: {\n size: {\n lg: 'h-14 px-4 text-base',\n md: 'h-12 px-3 text-base',\n sm: 'h-10 px-2 text-sm',\n },\n error: {\n true: 'border-field-border-error focus:border-field-border-error',\n false: '',\n },\n },\n defaultVariants: { size: 'md', error: false },\n },\n)\n\n// Posição do ícone e recuo do texto por tamanho (Figma: padding + ícone 24/20/24 + gap 8/8/4).\nconst iconSlot = {\n lg: { icon: 'w-5', left: 'left-4', right: 'right-4', padL: 'pl-11', padR: 'pr-11' },\n md: { icon: 'w-6', left: 'left-3', right: 'right-3', padL: 'pl-11', padR: 'pr-11' },\n sm: { icon: 'w-6', left: 'left-2', right: 'right-2', padL: 'pl-9', padR: 'pr-9' },\n}\n\nexport type InputProps = Omit<TextInputProps, 'style' | 'editable'> &\n Omit<VariantProps<typeof inputVariants>, 'error'> & {\n className?: string\n /** Borda e foco em vermelho. */\n error?: boolean\n disabled?: boolean\n /** Ícone antes do texto (o consumidor define a cor; o slot tem 24px). */\n leadingIcon?: ReactNode\n /** Ícone depois do texto (o consumidor define a cor; o slot tem 24px). */\n trailingIcon?: ReactNode\n }\n\nexport function Input({\n className,\n size,\n error = false,\n disabled = false,\n readOnly = false,\n leadingIcon,\n trailingIcon,\n accessibilityState,\n ...props\n}: InputProps) {\n const placeholderColor = useToken('--color-field-text-placeholder')\n const slot = iconSlot[size ?? 'md']\n\n const input = (\n <TextInput\n editable={!disabled && !readOnly}\n readOnly={readOnly}\n placeholderTextColor={placeholderColor}\n accessibilityState={{ ...accessibilityState, disabled }}\n className={cn(\n inputVariants({ size, error }),\n disabled &&\n 'border-field-border-disabled bg-field-bg-disabled text-field-text-disabled focus:border-field-border-disabled',\n readOnly && !disabled && 'border-transparent bg-field-bg-disabled focus:border-transparent',\n leadingIcon && slot.padL,\n trailingIcon && slot.padR,\n className,\n )}\n {...props}\n />\n )\n\n if (!leadingIcon && !trailingIcon) return input\n\n return (\n <View className=\"justify-center\">\n {leadingIcon ? (\n <View\n pointerEvents=\"none\"\n className={cn(\n 'absolute inset-y-0 z-10 items-center justify-center',\n slot.icon,\n slot.left,\n )}\n >\n {leadingIcon}\n </View>\n ) : null}\n {input}\n {trailingIcon ? (\n <View\n pointerEvents=\"none\"\n className={cn(\n 'absolute inset-y-0 z-10 items-center justify-center',\n slot.icon,\n slot.right,\n )}\n >\n {trailingIcon}\n </View>\n ) : null}\n </View>\n )\n}\n"],"mappings":";;;;;;;AASA,MAAa,gBAAgB,IAC3B,+GACA;CACE,UAAU;EACR,MAAM;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;EACN;EACA,OAAO;GACL,MAAM;GACN,OAAO;EACT;CACF;CACA,iBAAiB;EAAE,MAAM;EAAM,OAAO;CAAM;AAC9C,CACF;AAGA,MAAM,WAAW;CACf,IAAI;EAAE,MAAM;EAAO,MAAM;EAAU,OAAO;EAAW,MAAM;EAAS,MAAM;CAAQ;CAClF,IAAI;EAAE,MAAM;EAAO,MAAM;EAAU,OAAO;EAAW,MAAM;EAAS,MAAM;CAAQ;CAClF,IAAI;EAAE,MAAM;EAAO,MAAM;EAAU,OAAO;EAAW,MAAM;EAAQ,MAAM;CAAO;AAClF;AAcA,SAAgB,MAAM,EACpB,WACA,MACA,QAAQ,OACR,WAAW,OACX,WAAW,OACX,aACA,cACA,oBACA,GAAG,SACU;CACb,MAAM,mBAAmB,SAAS,gCAAgC;CAClE,MAAM,OAAO,SAAS,QAAQ;CAE9B,MAAM,QACJ,oBAAC,WAAD;EACE,UAAU,CAAC,YAAY,CAAC;EACd;EACV,sBAAsB;EACtB,oBAAoB;GAAE,GAAG;GAAoB;EAAS;EACtD,WAAW,GACT,cAAc;GAAE;GAAM;EAAM,CAAC,GAC7B,YACE,iHACF,YAAY,CAAC,YAAY,oEACzB,eAAe,KAAK,MACpB,gBAAgB,KAAK,MACrB,SACF;EACA,GAAI;CACL,CAAA;CAGH,IAAI,CAAC,eAAe,CAAC,cAAc,OAAO;CAE1C,OACE,qBAAC,MAAD;EAAM,WAAU;EAAhB,UAAA;GACG,cACC,oBAAC,MAAD;IACE,eAAc;IACd,WAAW,GACT,uDACA,KAAK,MACL,KAAK,IACP;IAEC,UAAA;GACG,CAAA,IACJ;GACH;GACA,eACC,oBAAC,MAAD;IACE,eAAc;IACd,WAAW,GACT,uDACA,KAAK,MACL,KAAK,KACP;IAEC,UAAA;GACG,CAAA,IACJ;EACA;;AAEV"}
@@ -0,0 +1,2 @@
1
+ import { Link, LinkProps } from "./link.js";
2
+ export { Link, type LinkProps };
@@ -0,0 +1,13 @@
1
+ import { TextProps } from "react-native";
2
+ //#region src/components/link/link.d.ts
3
+ export type LinkProps = Omit<TextProps, 'style' | 'onPress'> & {
4
+ className?: string;
5
+ /** URL aberta com `Linking.openURL` ao tocar (depois de `onPress`). */
6
+ href?: string;
7
+ /** Para navegar com o seu roteador, use só `onPress` (sem `href`). */
8
+ onPress?: TextProps['onPress'];
9
+ };
10
+ /** Link de texto na cor da marca, sempre sublinhado (a cor sozinha não distingue o link do texto). */
11
+ export declare function Link({ className, href, onPress, ...props }: LinkProps): import("react").JSX.Element;
12
+ //#endregion
13
+ //# sourceMappingURL=link.d.ts.map
@@ -0,0 +1,20 @@
1
+ import { cn } from "../../utils/cn.js";
2
+ import { Linking, Text } from "react-native";
3
+ import { jsx } from "react/jsx-runtime";
4
+ //#region src/components/link/link.tsx
5
+ /** Link de texto na cor da marca, sempre sublinhado (a cor sozinha não distingue o link do texto). */
6
+ function Link({ className, href, onPress, ...props }) {
7
+ return /* @__PURE__ */ jsx(Text, {
8
+ accessibilityRole: "link",
9
+ onPress: (event) => {
10
+ onPress?.(event);
11
+ if (href) Linking.openURL(href).catch(() => {});
12
+ },
13
+ className: cn("text-text-brand underline active:text-interactive-primary-hover", className),
14
+ ...props
15
+ });
16
+ }
17
+ //#endregion
18
+ export { Link };
19
+
20
+ //# sourceMappingURL=link.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"link.js","names":[],"sources":["../../../src/components/link/link.tsx"],"sourcesContent":["/// <reference types=\"uniwind/types\" />\n\nimport { Linking, Text, type TextProps } from 'react-native'\nimport { cn } from '../../utils/cn'\n\nexport type LinkProps = Omit<TextProps, 'style' | 'onPress'> & {\n className?: string\n /** URL aberta com `Linking.openURL` ao tocar (depois de `onPress`). */\n href?: string\n /** Para navegar com o seu roteador, use só `onPress` (sem `href`). */\n onPress?: TextProps['onPress']\n}\n\n/** Link de texto na cor da marca, sempre sublinhado (a cor sozinha não distingue o link do texto). */\nexport function Link({ className, href, onPress, ...props }: LinkProps) {\n return (\n <Text\n accessibilityRole=\"link\"\n onPress={(event) => {\n onPress?.(event)\n // URL inválida/sem app para abrir não deve virar rejeição não tratada\n if (href) Linking.openURL(href).catch(() => {})\n }}\n className={cn('text-text-brand underline active:text-interactive-primary-hover', className)}\n {...props}\n />\n )\n}\n"],"mappings":";;;;;AAcA,SAAgB,KAAK,EAAE,WAAW,MAAM,SAAS,GAAG,SAAoB;CACtE,OACE,oBAAC,MAAD;EACE,mBAAkB;EAClB,UAAU,UAAU;GAClB,UAAU,KAAK;GAEf,IAAI,MAAM,QAAQ,QAAQ,IAAI,CAAC,CAAC,YAAY,CAAC,CAAC;EAChD;EACA,WAAW,GAAG,mEAAmE,SAAS;EAC1F,GAAI;CACL,CAAA;AAEL"}
@@ -0,0 +1,2 @@
1
+ import { List, ListItem, ListItemProps, ListProps } from "./list.js";
2
+ export { List, ListItem, type ListItemProps, type ListProps };
@@ -0,0 +1,26 @@
1
+ import { ReactNode } from "react";
2
+ import { ViewProps } from "react-native";
3
+ //#region src/components/list/list.d.ts
4
+ export type ListProps = ViewProps & {
5
+ className?: string;
6
+ /** Linha entre os itens. */
7
+ divided?: boolean;
8
+ };
9
+ /** Lista vertical de itens com o mesmo formato (contatos, arquivos, configurações). */
10
+ export declare function List({ className, divided, children, ...props }: ListProps): import("react").JSX.Element;
11
+ export type ListItemProps = Omit<ViewProps, 'children'> & {
12
+ className?: string;
13
+ /** Título do item. */
14
+ title: string;
15
+ /** Texto de apoio abaixo do título. */
16
+ description?: string;
17
+ /** Elemento antes do texto (Avatar, ícone). */
18
+ leading?: ReactNode;
19
+ /** Elemento no fim da linha (Badge, Menu, botão). */
20
+ trailing?: ReactNode;
21
+ /** Conteúdo extra abaixo da descrição. */
22
+ children?: ReactNode;
23
+ };
24
+ export declare function ListItem({ className, title, description, leading, trailing, children, ...props }: ListItemProps): import("react").JSX.Element;
25
+ //#endregion
26
+ //# sourceMappingURL=list.d.ts.map
@@ -0,0 +1,45 @@
1
+ import { cn } from "../../utils/cn.js";
2
+ import { Divider } from "../divider/divider.js";
3
+ import { Children, Fragment } from "react";
4
+ import { Text, View } from "react-native";
5
+ import { jsx, jsxs } from "react/jsx-runtime";
6
+ //#region src/components/list/list.tsx
7
+ /** Lista vertical de itens com o mesmo formato (contatos, arquivos, configurações). */
8
+ function List({ className, divided = false, children, ...props }) {
9
+ const items = Children.toArray(children);
10
+ return /* @__PURE__ */ jsx(View, {
11
+ accessibilityRole: "list",
12
+ className: cn(className),
13
+ ...props,
14
+ children: divided ? items.map((child, i) => /* @__PURE__ */ jsxs(Fragment, { children: [i > 0 && /* @__PURE__ */ jsx(Divider, {}), child] }, i)) : children
15
+ });
16
+ }
17
+ function ListItem({ className, title, description, leading, trailing, children, ...props }) {
18
+ return /* @__PURE__ */ jsxs(View, {
19
+ role: "listitem",
20
+ className: cn("flex-row items-center gap-3 py-3", className),
21
+ ...props,
22
+ children: [
23
+ leading,
24
+ /* @__PURE__ */ jsxs(View, {
25
+ className: "min-w-0 flex-1",
26
+ children: [
27
+ /* @__PURE__ */ jsx(Text, {
28
+ className: "text-sm font-medium leading-5 text-text-primary",
29
+ children: title
30
+ }),
31
+ description ? /* @__PURE__ */ jsx(Text, {
32
+ className: "text-sm leading-5 text-text-secondary",
33
+ children: description
34
+ }) : null,
35
+ children
36
+ ]
37
+ }),
38
+ trailing
39
+ ]
40
+ });
41
+ }
42
+ //#endregion
43
+ export { List, ListItem };
44
+
45
+ //# sourceMappingURL=list.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"list.js","names":[],"sources":["../../../src/components/list/list.tsx"],"sourcesContent":["/// <reference types=\"uniwind/types\" />\n\nimport { Children, Fragment, type ReactNode } from 'react'\nimport { Text, View, type ViewProps } from 'react-native'\nimport { cn } from '../../utils/cn'\nimport { Divider } from '../divider/divider'\n\nexport type ListProps = ViewProps & {\n className?: string\n /** Linha entre os itens. */\n divided?: boolean\n}\n\n/** Lista vertical de itens com o mesmo formato (contatos, arquivos, configurações). */\nexport function List({ className, divided = false, children, ...props }: ListProps) {\n // sem `divide-y` no native: intercala um Divider entre os filhos\n const items = Children.toArray(children)\n\n return (\n <View accessibilityRole=\"list\" className={cn(className)} {...props}>\n {divided\n ? items.map((child, i) => (\n // biome-ignore lint/suspicious/noArrayIndexKey: lista estática de filhos já com key própria\n <Fragment key={i}>\n {i > 0 && <Divider />}\n {child}\n </Fragment>\n ))\n : children}\n </View>\n )\n}\n\nexport type ListItemProps = Omit<ViewProps, 'children'> & {\n className?: string\n /** Título do item. */\n title: string\n /** Texto de apoio abaixo do título. */\n description?: string\n /** Elemento antes do texto (Avatar, ícone). */\n leading?: ReactNode\n /** Elemento no fim da linha (Badge, Menu, botão). */\n trailing?: ReactNode\n /** Conteúdo extra abaixo da descrição. */\n children?: ReactNode\n}\n\nexport function ListItem({\n className,\n title,\n description,\n leading,\n trailing,\n children,\n ...props\n}: ListItemProps) {\n return (\n <View role=\"listitem\" className={cn('flex-row items-center gap-3 py-3', className)} {...props}>\n {leading}\n <View className=\"min-w-0 flex-1\">\n <Text className=\"text-sm font-medium leading-5 text-text-primary\">{title}</Text>\n {description ? (\n <Text className=\"text-sm leading-5 text-text-secondary\">{description}</Text>\n ) : null}\n {children}\n </View>\n {trailing}\n </View>\n )\n}\n"],"mappings":";;;;;;;AAcA,SAAgB,KAAK,EAAE,WAAW,UAAU,OAAO,UAAU,GAAG,SAAoB;CAElF,MAAM,QAAQ,SAAS,QAAQ,QAAQ;CAEvC,OACE,oBAAC,MAAD;EAAM,mBAAkB;EAAO,WAAW,GAAG,SAAS;EAAG,GAAI;EAC1D,UAAA,UACG,MAAM,KAAK,OAAO,MAEhB,qBAAC,UAAD,EAAA,UAAA,CACG,IAAI,KAAK,oBAAC,SAAD,CAAU,CAAA,GACnB,KACO,EAAA,GAHK,CAGL,CACX,IACD;CACA,CAAA;AAEV;AAgBA,SAAgB,SAAS,EACvB,WACA,OACA,aACA,SACA,UACA,UACA,GAAG,SACa;CAChB,OACE,qBAAC,MAAD;EAAM,MAAK;EAAW,WAAW,GAAG,oCAAoC,SAAS;EAAG,GAAI;EAAxF,UAAA;GACG;GACD,qBAAC,MAAD;IAAM,WAAU;IAAhB,UAAA;KACE,oBAAC,MAAD;MAAM,WAAU;MAAmD,UAAA;KAAY,CAAA;KAC9E,cACC,oBAAC,MAAD;MAAM,WAAU;MAAyC,UAAA;KAAkB,CAAA,IACzE;KACH;IACG;;GACL;EACG;;AAEV"}
@@ -0,0 +1,2 @@
1
+ import { Menu, MenuItem, MenuItemProps, MenuLabel, MenuLabelProps, MenuProps, MenuSeparator, MenuSeparatorProps } from "./menu.js";
2
+ export { Menu, MenuItem, type MenuItemProps, MenuLabel, type MenuLabelProps, type MenuProps, MenuSeparator, type MenuSeparatorProps };
@@ -0,0 +1,47 @@
1
+ import { ReactNode } from "react";
2
+ import { PressableProps } from "react-native";
3
+ //#region src/components/menu/menu.d.ts
4
+ export type MenuProps = {
5
+ open?: boolean;
6
+ defaultOpen?: boolean;
7
+ onOpenChange?: (open: boolean) => void;
8
+ /** Lado do gatilho em que o menu abre; inverte se não couber. Padrão: `bottom`. */
9
+ side?: 'top' | 'bottom';
10
+ /** Elemento que abre o menu (ex.: um `<Button>`); recebe `onPress`. */
11
+ trigger: ReactNode;
12
+ /** Os `MenuItem`, `MenuLabel` e `MenuSeparator` do menu. */
13
+ children: ReactNode;
14
+ className?: string;
15
+ testID?: string;
16
+ };
17
+ /**
18
+ * Menu de ações aberto por um botão, ancorado nele. Tocar num item o executa e fecha o menu;
19
+ * tocar fora ou o botão voltar do Android também fecha. Diferenças do web: sem navegação por
20
+ * teclado, sem `align` (alinha à esquerda do gatilho) e sem lados `left`/`right`.
21
+ */
22
+ export declare function Menu({ open, defaultOpen, onOpenChange, side, trigger, children, className, testID }: MenuProps): import("react").JSX.Element;
23
+ export type MenuItemProps = Omit<PressableProps, 'children' | 'style'> & {
24
+ /** Chamado ao escolher o item (antes de o menu fechar). Equivale ao `onSelect` do web. */
25
+ onSelect?: () => void;
26
+ /** Ícone à esquerda do rótulo (16px). */
27
+ icon?: ReactNode;
28
+ /** Ação que desfaz dados (excluir, remover): texto em vermelho. */
29
+ destructive?: boolean;
30
+ children: string;
31
+ className?: string;
32
+ };
33
+ /** Uma ação do menu. Usa `onSelect` (como o web); `onPress` também é chamado. */
34
+ export declare function MenuItem({ className, icon, destructive, disabled, onSelect, onPress, children, accessibilityState, ...props }: MenuItemProps): import("react").JSX.Element;
35
+ export type MenuLabelProps = {
36
+ children: string;
37
+ className?: string;
38
+ };
39
+ /** Título de um grupo de itens. */
40
+ export declare function MenuLabel({ className, children }: MenuLabelProps): import("react").JSX.Element;
41
+ export type MenuSeparatorProps = {
42
+ className?: string;
43
+ };
44
+ /** Linha entre grupos de itens. */
45
+ export declare function MenuSeparator({ className }: MenuSeparatorProps): import("react").JSX.Element;
46
+ //#endregion
47
+ //# sourceMappingURL=menu.d.ts.map
@@ -0,0 +1,81 @@
1
+ import { cn } from "../../utils/cn.js";
2
+ import { AnchoredOverlay } from "../_shared/anchored-overlay.js";
3
+ import { useOpenState } from "../_shared/overlay-state.js";
4
+ import { AnchorTrigger, useControlledAnchor } from "../_shared/overlay-anchor.js";
5
+ import { createContext, useContext } from "react";
6
+ import { Pressable, Text, View } from "react-native";
7
+ import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
8
+ //#region src/components/menu/menu.tsx
9
+ const CloseContext = createContext(null);
10
+ /**
11
+ * Menu de ações aberto por um botão, ancorado nele. Tocar num item o executa e fecha o menu;
12
+ * tocar fora ou o botão voltar do Android também fecha. Diferenças do web: sem navegação por
13
+ * teclado, sem `align` (alinha à esquerda do gatilho) e sem lados `left`/`right`.
14
+ */
15
+ function Menu({ open, defaultOpen, onOpenChange, side = "bottom", trigger, 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
+ testID,
34
+ className: cn("min-w-[180px] rounded-md border border-border-default bg-surface-elevated p-1 shadow-md", className),
35
+ children: /* @__PURE__ */ jsx(CloseContext.Provider, {
36
+ value: close,
37
+ children: /* @__PURE__ */ jsx(View, {
38
+ accessibilityRole: "menu",
39
+ children
40
+ })
41
+ })
42
+ })] });
43
+ }
44
+ /** Uma ação do menu. Usa `onSelect` (como o web); `onPress` também é chamado. */
45
+ function MenuItem({ className, icon, destructive = false, disabled, onSelect, onPress, children, accessibilityState, ...props }) {
46
+ const close = useContext(CloseContext);
47
+ return /* @__PURE__ */ jsxs(Pressable, {
48
+ accessibilityRole: "menuitem",
49
+ accessibilityState: {
50
+ ...accessibilityState,
51
+ disabled: Boolean(disabled)
52
+ },
53
+ disabled,
54
+ onPress: (event) => {
55
+ onPress?.(event);
56
+ onSelect?.();
57
+ close?.();
58
+ },
59
+ className: cn("flex-row items-center gap-2 rounded-sm px-3 py-2 active:bg-bg-tertiary", className),
60
+ ...props,
61
+ children: [icon, /* @__PURE__ */ jsx(Text, {
62
+ className: cn("text-sm leading-5", disabled ? "text-text-disabled" : destructive ? "text-status-error-text" : "text-text-primary"),
63
+ children
64
+ })]
65
+ });
66
+ }
67
+ /** Título de um grupo de itens. */
68
+ function MenuLabel({ className, children }) {
69
+ return /* @__PURE__ */ jsx(Text, {
70
+ className: cn("px-3 py-1.5 text-xs font-medium leading-4 text-text-secondary", className),
71
+ children
72
+ });
73
+ }
74
+ /** Linha entre grupos de itens. */
75
+ function MenuSeparator({ className }) {
76
+ return /* @__PURE__ */ jsx(View, { className: cn("-mx-1 my-1 h-px bg-border-default", className) });
77
+ }
78
+ //#endregion
79
+ export { Menu, MenuItem, MenuLabel, MenuSeparator };
80
+
81
+ //# sourceMappingURL=menu.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"menu.js","names":[],"sources":["../../../src/components/menu/menu.tsx"],"sourcesContent":["/// <reference types=\"uniwind/types\" />\n\nimport { createContext, type ReactNode, useContext } from 'react'\nimport { Pressable, type PressableProps, Text, View } from 'react-native'\nimport { cn } from '../../utils/cn'\nimport { AnchoredOverlay } from '../_shared/anchored-overlay'\nimport { AnchorTrigger, useControlledAnchor } from '../_shared/overlay-anchor'\nimport { useOpenState } from '../_shared/overlay-state'\n\nconst CloseContext = createContext<(() => void) | null>(null)\n\nexport type MenuProps = {\n open?: boolean\n defaultOpen?: boolean\n onOpenChange?: (open: boolean) => void\n /** Lado do gatilho em que o menu abre; inverte se não couber. Padrão: `bottom`. */\n side?: 'top' | 'bottom'\n /** Elemento que abre o menu (ex.: um `<Button>`); recebe `onPress`. */\n trigger: ReactNode\n /** Os `MenuItem`, `MenuLabel` e `MenuSeparator` do menu. */\n children: ReactNode\n className?: string\n testID?: string\n}\n\n/**\n * Menu de ações aberto por um botão, ancorado nele. Tocar num item o executa e fecha o menu;\n * tocar fora ou o botão voltar do Android também fecha. Diferenças do web: sem navegação por\n * teclado, sem `align` (alinha à esquerda do gatilho) e sem lados `left`/`right`.\n */\nexport function Menu({\n open,\n defaultOpen,\n onOpenChange,\n side = 'bottom',\n trigger,\n children,\n className,\n testID,\n}: MenuProps) {\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 testID={testID}\n className={cn(\n 'min-w-[180px] rounded-md border border-border-default bg-surface-elevated p-1 shadow-md',\n className,\n )}\n >\n <CloseContext.Provider value={close}>\n <View accessibilityRole=\"menu\">{children}</View>\n </CloseContext.Provider>\n </AnchoredOverlay>\n </>\n )\n}\n\nexport type MenuItemProps = Omit<PressableProps, 'children' | 'style'> & {\n /** Chamado ao escolher o item (antes de o menu fechar). Equivale ao `onSelect` do web. */\n onSelect?: () => void\n /** Ícone à esquerda do rótulo (16px). */\n icon?: ReactNode\n /** Ação que desfaz dados (excluir, remover): texto em vermelho. */\n destructive?: boolean\n children: string\n className?: string\n}\n\n/** Uma ação do menu. Usa `onSelect` (como o web); `onPress` também é chamado. */\nexport function MenuItem({\n className,\n icon,\n destructive = false,\n disabled,\n onSelect,\n onPress,\n children,\n accessibilityState,\n ...props\n}: MenuItemProps) {\n const close = useContext(CloseContext)\n\n return (\n <Pressable\n accessibilityRole=\"menuitem\"\n accessibilityState={{ ...accessibilityState, disabled: Boolean(disabled) }}\n disabled={disabled}\n onPress={(event) => {\n onPress?.(event)\n onSelect?.()\n close?.()\n }}\n className={cn(\n 'flex-row items-center gap-2 rounded-sm px-3 py-2 active:bg-bg-tertiary',\n className,\n )}\n {...props}\n >\n {icon}\n <Text\n className={cn(\n 'text-sm leading-5',\n disabled\n ? 'text-text-disabled'\n : destructive\n ? 'text-status-error-text'\n : 'text-text-primary',\n )}\n >\n {children}\n </Text>\n </Pressable>\n )\n}\n\nexport type MenuLabelProps = { children: string; className?: string }\n\n/** Título de um grupo de itens. */\nexport function MenuLabel({ className, children }: MenuLabelProps) {\n return (\n <Text\n className={cn('px-3 py-1.5 text-xs font-medium leading-4 text-text-secondary', className)}\n >\n {children}\n </Text>\n )\n}\n\nexport type MenuSeparatorProps = { className?: string }\n\n/** Linha entre grupos de itens. */\nexport function MenuSeparator({ className }: MenuSeparatorProps) {\n return <View className={cn('-mx-1 my-1 h-px bg-border-default', className)} />\n}\n"],"mappings":";;;;;;;;AASA,MAAM,eAAe,cAAmC,IAAI;;;;;;AAqB5D,SAAgB,KAAK,EACnB,MACA,aACA,cACA,OAAO,UACP,SACA,UACA,WACA,UACY;CACZ,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;EACH;EACR,WAAW,GACT,2FACA,SACF;EAEA,UAAA,oBAAC,aAAa,UAAd;GAAuB,OAAO;GAC5B,UAAA,oBAAC,MAAD;IAAM,mBAAkB;IAAQ;GAAe,CAAA;EAC1B,CAAA;CACR,CAAA,CACjB,EAAA,CAAA;AAEN;;AAcA,SAAgB,SAAS,EACvB,WACA,MACA,cAAc,OACd,UACA,UACA,SACA,UACA,oBACA,GAAG,SACa;CAChB,MAAM,QAAQ,WAAW,YAAY;CAErC,OACE,qBAAC,WAAD;EACE,mBAAkB;EAClB,oBAAoB;GAAE,GAAG;GAAoB,UAAU,QAAQ,QAAQ;EAAE;EAC/D;EACV,UAAU,UAAU;GAClB,UAAU,KAAK;GACf,WAAW;GACX,QAAQ;EACV;EACA,WAAW,GACT,0EACA,SACF;EACA,GAAI;EAbN,UAAA,CAeG,MACD,oBAAC,MAAD;GACE,WAAW,GACT,qBACA,WACI,uBACA,cACE,2BACA,mBACR;GAEC;EACG,CAAA,CACG;;AAEf;;AAKA,SAAgB,UAAU,EAAE,WAAW,YAA4B;CACjE,OACE,oBAAC,MAAD;EACE,WAAW,GAAG,iEAAiE,SAAS;EAEvF;CACG,CAAA;AAEV;;AAKA,SAAgB,cAAc,EAAE,aAAiC;CAC/D,OAAO,oBAAC,MAAD,EAAM,WAAW,GAAG,qCAAqC,SAAS,EAAI,CAAA;AAC/E"}
@@ -0,0 +1,2 @@
1
+ import { Modal, ModalClose, ModalProps } from "./modal.js";
2
+ export { Modal, ModalClose, type ModalProps };