@medway-ui/native 1.1.2 → 1.3.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 (244) hide show
  1. package/lib/commonjs/components/Alert.js +5 -4
  2. package/lib/commonjs/components/Alert.js.map +1 -1
  3. package/lib/commonjs/components/Avatar.js +2 -1
  4. package/lib/commonjs/components/Avatar.js.map +1 -1
  5. package/lib/commonjs/components/Badge.js +2 -1
  6. package/lib/commonjs/components/Badge.js.map +1 -1
  7. package/lib/commonjs/components/Button.js +3 -2
  8. package/lib/commonjs/components/Button.js.map +1 -1
  9. package/lib/commonjs/components/CardNews.js +7 -6
  10. package/lib/commonjs/components/CardNews.js.map +1 -1
  11. package/lib/commonjs/components/Carousel.js +122 -0
  12. package/lib/commonjs/components/Carousel.js.map +1 -0
  13. package/lib/commonjs/components/Checkbox.js +11 -2
  14. package/lib/commonjs/components/Checkbox.js.map +1 -1
  15. package/lib/commonjs/components/DropdownMenu.js +225 -0
  16. package/lib/commonjs/components/DropdownMenu.js.map +1 -0
  17. package/lib/commonjs/components/EmptyState.js +19 -20
  18. package/lib/commonjs/components/EmptyState.js.map +1 -1
  19. package/lib/commonjs/components/Feed.js +82 -0
  20. package/lib/commonjs/components/Feed.js.map +1 -0
  21. package/lib/commonjs/components/FeedNotice.js +76 -0
  22. package/lib/commonjs/components/FeedNotice.js.map +1 -0
  23. package/lib/commonjs/components/Input.js +21 -9
  24. package/lib/commonjs/components/Input.js.map +1 -1
  25. package/lib/commonjs/components/InvitePopover.js +196 -0
  26. package/lib/commonjs/components/InvitePopover.js.map +1 -0
  27. package/lib/commonjs/components/Label.js +2 -1
  28. package/lib/commonjs/components/Label.js.map +1 -1
  29. package/lib/commonjs/components/LeaderboardCard.js +58 -0
  30. package/lib/commonjs/components/LeaderboardCard.js.map +1 -0
  31. package/lib/commonjs/components/MemberRow.js +131 -0
  32. package/lib/commonjs/components/MemberRow.js.map +1 -0
  33. package/lib/commonjs/components/PageHeader.js +3 -2
  34. package/lib/commonjs/components/PageHeader.js.map +1 -1
  35. package/lib/commonjs/components/PostForm.js +182 -0
  36. package/lib/commonjs/components/PostForm.js.map +1 -0
  37. package/lib/commonjs/components/PrizeCollection.js +135 -0
  38. package/lib/commonjs/components/PrizeCollection.js.map +1 -0
  39. package/lib/commonjs/components/RadioGroup.js +22 -0
  40. package/lib/commonjs/components/RadioGroup.js.map +1 -1
  41. package/lib/commonjs/components/SectionHeader.js +36 -0
  42. package/lib/commonjs/components/SectionHeader.js.map +1 -0
  43. package/lib/commonjs/components/Switch.js +33 -19
  44. package/lib/commonjs/components/Switch.js.map +1 -1
  45. package/lib/commonjs/components/Tabs.js +156 -0
  46. package/lib/commonjs/components/Tabs.js.map +1 -0
  47. package/lib/commonjs/components/Tag.js +2 -1
  48. package/lib/commonjs/components/Tag.js.map +1 -1
  49. package/lib/commonjs/components/Text.js +60 -0
  50. package/lib/commonjs/components/Text.js.map +1 -0
  51. package/lib/commonjs/components/Toast.js +3 -2
  52. package/lib/commonjs/components/Toast.js.map +1 -1
  53. package/lib/commonjs/components/Tooltip.js +2 -2
  54. package/lib/commonjs/components/Tooltip.js.map +1 -1
  55. package/lib/commonjs/editor/NoteEditor.js +710 -0
  56. package/lib/commonjs/editor/NoteEditor.js.map +1 -0
  57. package/lib/commonjs/editor/editorHtml.generated.js +21 -0
  58. package/lib/commonjs/editor/editorHtml.generated.js.map +1 -0
  59. package/lib/commonjs/editor/index.js +33 -0
  60. package/lib/commonjs/editor/index.js.map +1 -0
  61. package/lib/commonjs/editor/protocol.js +88 -0
  62. package/lib/commonjs/editor/protocol.js.map +1 -0
  63. package/lib/commonjs/hooks/useThemeColor.js +66 -29
  64. package/lib/commonjs/hooks/useThemeColor.js.map +1 -1
  65. package/lib/commonjs/index.js +319 -1
  66. package/lib/commonjs/index.js.map +1 -1
  67. package/lib/commonjs/lib/overlay-host.js +101 -0
  68. package/lib/commonjs/lib/overlay-host.js.map +1 -0
  69. package/lib/commonjs/lib/typography.js +26 -0
  70. package/lib/commonjs/lib/typography.js.map +1 -0
  71. package/lib/commonjs/nativewind-env.d.js +0 -1
  72. package/lib/commonjs/nativewind-env.d.js.map +1 -1
  73. package/lib/module/components/Alert.js +3 -2
  74. package/lib/module/components/Alert.js.map +1 -1
  75. package/lib/module/components/Avatar.js +2 -1
  76. package/lib/module/components/Avatar.js.map +1 -1
  77. package/lib/module/components/Badge.js +2 -1
  78. package/lib/module/components/Badge.js.map +1 -1
  79. package/lib/module/components/Button.js +2 -1
  80. package/lib/module/components/Button.js.map +1 -1
  81. package/lib/module/components/CardNews.js +2 -1
  82. package/lib/module/components/CardNews.js.map +1 -1
  83. package/lib/module/components/Carousel.js +117 -0
  84. package/lib/module/components/Carousel.js.map +1 -0
  85. package/lib/module/components/Checkbox.js +12 -2
  86. package/lib/module/components/Checkbox.js.map +1 -1
  87. package/lib/module/components/DropdownMenu.js +216 -0
  88. package/lib/module/components/DropdownMenu.js.map +1 -0
  89. package/lib/module/components/EmptyState.js +19 -20
  90. package/lib/module/components/EmptyState.js.map +1 -1
  91. package/lib/module/components/Feed.js +77 -0
  92. package/lib/module/components/Feed.js.map +1 -0
  93. package/lib/module/components/FeedNotice.js +73 -0
  94. package/lib/module/components/FeedNotice.js.map +1 -0
  95. package/lib/module/components/Input.js +21 -9
  96. package/lib/module/components/Input.js.map +1 -1
  97. package/lib/module/components/InvitePopover.js +183 -0
  98. package/lib/module/components/InvitePopover.js.map +1 -0
  99. package/lib/module/components/Label.js +2 -1
  100. package/lib/module/components/Label.js.map +1 -1
  101. package/lib/module/components/LeaderboardCard.js +53 -0
  102. package/lib/module/components/LeaderboardCard.js.map +1 -0
  103. package/lib/module/components/MemberRow.js +127 -0
  104. package/lib/module/components/MemberRow.js.map +1 -0
  105. package/lib/module/components/PageHeader.js +2 -1
  106. package/lib/module/components/PageHeader.js.map +1 -1
  107. package/lib/module/components/PostForm.js +174 -0
  108. package/lib/module/components/PostForm.js.map +1 -0
  109. package/lib/module/components/PrizeCollection.js +127 -0
  110. package/lib/module/components/PrizeCollection.js.map +1 -0
  111. package/lib/module/components/RadioGroup.js +21 -1
  112. package/lib/module/components/RadioGroup.js.map +1 -1
  113. package/lib/module/components/SectionHeader.js +33 -0
  114. package/lib/module/components/SectionHeader.js.map +1 -0
  115. package/lib/module/components/Switch.js +34 -20
  116. package/lib/module/components/Switch.js.map +1 -1
  117. package/lib/module/components/Tabs.js +148 -0
  118. package/lib/module/components/Tabs.js.map +1 -0
  119. package/lib/module/components/Tag.js +2 -1
  120. package/lib/module/components/Tag.js.map +1 -1
  121. package/lib/module/components/Text.js +58 -0
  122. package/lib/module/components/Text.js.map +1 -0
  123. package/lib/module/components/Toast.js +2 -1
  124. package/lib/module/components/Toast.js.map +1 -1
  125. package/lib/module/components/Tooltip.js +1 -1
  126. package/lib/module/components/Tooltip.js.map +1 -1
  127. package/lib/module/editor/NoteEditor.js +705 -0
  128. package/lib/module/editor/NoteEditor.js.map +1 -0
  129. package/lib/module/editor/editorHtml.generated.js +17 -0
  130. package/lib/module/editor/editorHtml.generated.js.map +1 -0
  131. package/lib/module/editor/index.js +5 -0
  132. package/lib/module/editor/index.js.map +1 -0
  133. package/lib/module/editor/protocol.js +83 -0
  134. package/lib/module/editor/protocol.js.map +1 -0
  135. package/lib/module/hooks/useThemeColor.js +66 -31
  136. package/lib/module/hooks/useThemeColor.js.map +1 -1
  137. package/lib/module/index.js +33 -3
  138. package/lib/module/index.js.map +1 -1
  139. package/lib/module/lib/overlay-host.js +96 -0
  140. package/lib/module/lib/overlay-host.js.map +1 -0
  141. package/lib/module/lib/typography.js +22 -0
  142. package/lib/module/lib/typography.js.map +1 -0
  143. package/lib/module/nativewind-env.d.js +0 -1
  144. package/lib/module/nativewind-env.d.js.map +1 -1
  145. package/lib/typescript/components/Alert.d.ts.map +1 -1
  146. package/lib/typescript/components/Avatar.d.ts.map +1 -1
  147. package/lib/typescript/components/Badge.d.ts.map +1 -1
  148. package/lib/typescript/components/Button.d.ts.map +1 -1
  149. package/lib/typescript/components/CardNews.d.ts.map +1 -1
  150. package/lib/typescript/components/Carousel.d.ts +29 -0
  151. package/lib/typescript/components/Carousel.d.ts.map +1 -0
  152. package/lib/typescript/components/Checkbox.d.ts.map +1 -1
  153. package/lib/typescript/components/DropdownMenu.d.ts +39 -0
  154. package/lib/typescript/components/DropdownMenu.d.ts.map +1 -0
  155. package/lib/typescript/components/EmptyState.d.ts +1 -1
  156. package/lib/typescript/components/EmptyState.d.ts.map +1 -1
  157. package/lib/typescript/components/Feed.d.ts +22 -0
  158. package/lib/typescript/components/Feed.d.ts.map +1 -0
  159. package/lib/typescript/components/FeedNotice.d.ts +17 -0
  160. package/lib/typescript/components/FeedNotice.d.ts.map +1 -0
  161. package/lib/typescript/components/Input.d.ts +3 -2
  162. package/lib/typescript/components/Input.d.ts.map +1 -1
  163. package/lib/typescript/components/InvitePopover.d.ts +46 -0
  164. package/lib/typescript/components/InvitePopover.d.ts.map +1 -0
  165. package/lib/typescript/components/Label.d.ts.map +1 -1
  166. package/lib/typescript/components/LeaderboardCard.d.ts +16 -0
  167. package/lib/typescript/components/LeaderboardCard.d.ts.map +1 -0
  168. package/lib/typescript/components/MemberRow.d.ts +40 -0
  169. package/lib/typescript/components/MemberRow.d.ts.map +1 -0
  170. package/lib/typescript/components/PageHeader.d.ts.map +1 -1
  171. package/lib/typescript/components/PostForm.d.ts +50 -0
  172. package/lib/typescript/components/PostForm.d.ts.map +1 -0
  173. package/lib/typescript/components/PrizeCollection.d.ts +35 -0
  174. package/lib/typescript/components/PrizeCollection.d.ts.map +1 -0
  175. package/lib/typescript/components/RadioGroup.d.ts +10 -2
  176. package/lib/typescript/components/RadioGroup.d.ts.map +1 -1
  177. package/lib/typescript/components/SectionHeader.d.ts +12 -0
  178. package/lib/typescript/components/SectionHeader.d.ts.map +1 -0
  179. package/lib/typescript/components/Switch.d.ts.map +1 -1
  180. package/lib/typescript/components/Tabs.d.ts +32 -0
  181. package/lib/typescript/components/Tabs.d.ts.map +1 -0
  182. package/lib/typescript/components/Tag.d.ts.map +1 -1
  183. package/lib/typescript/components/Text.d.ts +23 -0
  184. package/lib/typescript/components/Text.d.ts.map +1 -0
  185. package/lib/typescript/components/Toast.d.ts +1 -1
  186. package/lib/typescript/components/Toast.d.ts.map +1 -1
  187. package/lib/typescript/components/Tooltip.d.ts.map +1 -1
  188. package/lib/typescript/editor/NoteEditor.d.ts +82 -0
  189. package/lib/typescript/editor/NoteEditor.d.ts.map +1 -0
  190. package/lib/typescript/editor/editorHtml.generated.d.ts +13 -0
  191. package/lib/typescript/editor/editorHtml.generated.d.ts.map +1 -0
  192. package/lib/typescript/editor/index.d.ts +6 -0
  193. package/lib/typescript/editor/index.d.ts.map +1 -0
  194. package/lib/typescript/editor/protocol.d.ts +153 -0
  195. package/lib/typescript/editor/protocol.d.ts.map +1 -0
  196. package/lib/typescript/hooks/useThemeColor.d.ts +4 -2
  197. package/lib/typescript/hooks/useThemeColor.d.ts.map +1 -1
  198. package/lib/typescript/index.d.ts +28 -4
  199. package/lib/typescript/index.d.ts.map +1 -1
  200. package/lib/typescript/lib/overlay-host.d.ts +23 -0
  201. package/lib/typescript/lib/overlay-host.d.ts.map +1 -0
  202. package/lib/typescript/lib/typography.d.ts +12 -0
  203. package/lib/typescript/lib/typography.d.ts.map +1 -0
  204. package/package.json +36 -3
  205. package/setup/MedwayUiProvider.tsx +15 -3
  206. package/setup/metro.js +27 -5
  207. package/setup/tailwind.js +52 -4
  208. package/setup/tokens.native.css +8 -3
  209. package/src/components/Alert.tsx +4 -2
  210. package/src/components/Avatar.tsx +3 -1
  211. package/src/components/Badge.tsx +3 -1
  212. package/src/components/Button.tsx +3 -7
  213. package/src/components/CardNews.tsx +2 -1
  214. package/src/components/Carousel.tsx +152 -0
  215. package/src/components/Checkbox.tsx +14 -4
  216. package/src/components/DropdownMenu.tsx +312 -0
  217. package/src/components/EmptyState.tsx +21 -23
  218. package/src/components/Feed.tsx +96 -0
  219. package/src/components/FeedNotice.tsx +112 -0
  220. package/src/components/Input.tsx +11 -6
  221. package/src/components/InvitePopover.tsx +240 -0
  222. package/src/components/Label.tsx +3 -1
  223. package/src/components/LeaderboardCard.tsx +70 -0
  224. package/src/components/MemberRow.tsx +157 -0
  225. package/src/components/PageHeader.tsx +3 -1
  226. package/src/components/PostForm.tsx +250 -0
  227. package/src/components/PrizeCollection.tsx +190 -0
  228. package/src/components/RadioGroup.tsx +32 -3
  229. package/src/components/SectionHeader.tsx +45 -0
  230. package/src/components/Switch.tsx +65 -59
  231. package/src/components/Tabs.tsx +182 -0
  232. package/src/components/Tag.tsx +3 -1
  233. package/src/components/Text.tsx +66 -0
  234. package/src/components/Toast.tsx +3 -1
  235. package/src/components/Tooltip.tsx +2 -1
  236. package/src/editor/NoteEditor.tsx +802 -0
  237. package/src/editor/editorHtml.generated.ts +14 -0
  238. package/src/editor/index.ts +13 -0
  239. package/src/editor/protocol.ts +184 -0
  240. package/src/hooks/useThemeColor.ts +77 -32
  241. package/src/index.ts +153 -6
  242. package/src/lib/overlay-host.tsx +102 -0
  243. package/src/lib/typography.ts +21 -0
  244. package/src/nativewind-env.d.ts +2 -0
package/setup/tailwind.js CHANGED
@@ -23,6 +23,47 @@ function resolveNativePackageRoot() {
23
23
  return path.resolve(__dirname, '..');
24
24
  }
25
25
 
26
+ /**
27
+ * React Native registers Montserrat as separate family names per weight
28
+ * (MontserratMedium, MontserratSemiBold, …). Numeric fontWeight + "Montserrat"
29
+ * does not resolve the file, so weight utilities must set fontFamily instead.
30
+ * Keys stay aligned with Medway UI tokens (`regular` | `medium` | `semibold` | …).
31
+ */
32
+ const NATIVE_MONTSERRAT_BY_WEIGHT = {
33
+ thin: 'MontserratLight',
34
+ extralight: 'MontserratLight',
35
+ light: 'MontserratLight',
36
+ normal: 'Montserrat',
37
+ regular: 'Montserrat',
38
+ medium: 'MontserratMedium',
39
+ semibold: 'MontserratSemiBold',
40
+ bold: 'MontserratBold',
41
+ extrabold: 'MontserratExtraBold',
42
+ black: 'MontserratBlack',
43
+ };
44
+
45
+ function createNativeMontserratFontPlugin() {
46
+ // Lazy-require so greenfield web tooling can still import this file if needed.
47
+ const plugin = require('tailwindcss/plugin');
48
+
49
+ return plugin(({ addBase, addUtilities }) => {
50
+ addBase({
51
+ '*': {
52
+ fontFamily: 'Montserrat',
53
+ },
54
+ });
55
+
56
+ addUtilities(
57
+ Object.fromEntries(
58
+ Object.entries(NATIVE_MONTSERRAT_BY_WEIGHT).map(([weight, fontFamily]) => [
59
+ `.font-${weight}`,
60
+ { fontFamily },
61
+ ]),
62
+ ),
63
+ );
64
+ });
65
+ }
66
+
26
67
  /**
27
68
  * Tailwind config para apps com @medway-ui/native.
28
69
  *
@@ -38,7 +79,6 @@ function createMedwayUiTailwindConfig(options = {}) {
38
79
  colors,
39
80
  fontFamily,
40
81
  fontSize,
41
- fontWeight,
42
82
  lineHeight,
43
83
  borderRadius,
44
84
  boxShadow,
@@ -68,17 +108,25 @@ function createMedwayUiTailwindConfig(options = {}) {
68
108
  darkMode: 'class',
69
109
  presets: [require('nativewind/preset')],
70
110
  theme: {
111
+ // Disable numeric font-weight utilities; RN needs fontFamily per weight.
112
+ fontWeight: {},
71
113
  extend: {
72
114
  colors,
73
- fontFamily,
115
+ fontFamily: {
116
+ ...fontFamily,
117
+ sans: ['Montserrat'],
118
+ },
74
119
  fontSize,
75
- fontWeight,
76
120
  lineHeight,
77
121
  borderRadius,
78
122
  boxShadow,
79
123
  },
80
124
  },
125
+ plugins: [createNativeMontserratFontPlugin()],
81
126
  };
82
127
  }
83
128
 
84
- module.exports = { createMedwayUiTailwindConfig };
129
+ module.exports = {
130
+ createMedwayUiTailwindConfig,
131
+ NATIVE_MONTSERRAT_BY_WEIGHT,
132
+ };
@@ -545,11 +545,15 @@
545
545
  --color-gradient-pt-bg-2: var(--color-gray-450);
546
546
 
547
547
  /* === Typography fontfamily === */
548
- --font-sans:
549
- "Montserrat", ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji",
550
- "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
548
+ /* RN: single family name (loaded via expo-font as "Montserrat"). */
549
+ --font-sans: Montserrat;
551
550
 
552
551
  /* === Typography weight === */
552
+ /*
553
+ * Token keys stay the same as web. On native, Tailwind maps these class
554
+ * names (font-medium, font-semibold, …) to Montserrat* fontFamily files
555
+ * in setup/tailwind.js — numeric fontWeight is not used for RN text.
556
+ */
553
557
  --font-weight-regular: 400;
554
558
  --font-weight-medium: 500;
555
559
  --font-weight-semibold: 600;
@@ -615,6 +619,7 @@
615
619
  --sidebar-width-mobile: 256px;
616
620
  --sidebar-width-icon: 72px;
617
621
  }
622
+
618
623
  .dark {
619
624
  /* === Dark base colors === */
620
625
  --color-background: var(--color-gray-850);
@@ -4,7 +4,9 @@ import {
4
4
  useMemo,
5
5
  type ReactNode,
6
6
  } from "react";
7
- import { View, Text, Pressable, type ViewProps, type PressableProps } from "react-native";
7
+ import { View, Pressable, type ViewProps, type PressableProps } from "react-native";
8
+
9
+ import { Text } from "@/components/Text";
8
10
  import { cva, type VariantProps } from "class-variance-authority";
9
11
  import { CloseMd } from "@medway-ui/icons/native";
10
12
  import { useThemeColor } from "@/hooks/useThemeColor";
@@ -141,7 +143,7 @@ function AlertDescription({ className, children, ...props }: ViewProps & { child
141
143
  <View className={cn("", className)} {...props}>
142
144
  <Text
143
145
  numberOfLines={3}
144
- className={cn("text-sm leading-5", alertTextColorVariants({ variant }))}
146
+ className={cn("text-sm font-medium leading-5", alertTextColorVariants({ variant }))}
145
147
  >
146
148
  {children}
147
149
  </Text>
@@ -1,5 +1,7 @@
1
1
  import { type ReactNode } from "react";
2
- import { View, Text, type ViewProps } from "react-native";
2
+ import { View, type ViewProps } from "react-native";
3
+
4
+ import { Text } from "@/components/Text";
3
5
  import * as AvatarPrimitive from "@rn-primitives/avatar";
4
6
  import { cva, type VariantProps } from "class-variance-authority";
5
7
  import { User } from "@medway-ui/icons/native";
@@ -1,5 +1,7 @@
1
1
  import { type ReactNode } from "react";
2
- import { View, Text, type ViewProps } from "react-native";
2
+ import { View, type ViewProps } from "react-native";
3
+
4
+ import { Text } from "@/components/Text";
3
5
  import { cva, type VariantProps } from "class-variance-authority";
4
6
  import { cn } from "@/lib/utils";
5
7
 
@@ -1,11 +1,7 @@
1
1
  import { type ReactNode } from "react";
2
- import {
3
- Pressable,
4
- View,
5
- Text,
6
- type PressableProps,
7
- type ViewStyle,
8
- } from "react-native";
2
+ import { Pressable, View, type PressableProps, type ViewStyle } from "react-native";
3
+
4
+ import { Text } from "@/components/Text";
9
5
  import { cva, type VariantProps } from "class-variance-authority";
10
6
  import { cn } from "@/lib/utils";
11
7
  import { Loading } from "@/components/Loading";
@@ -8,13 +8,14 @@ import {
8
8
  } from "react";
9
9
  import {
10
10
  Image,
11
- Text,
12
11
  View,
13
12
  useWindowDimensions,
14
13
  type ImageProps,
15
14
  type ViewProps,
16
15
  } from "react-native";
17
16
 
17
+ import { Text } from "@/components/Text";
18
+
18
19
  import { Avatar, AvatarFallback, AvatarImage } from "@/components/Avatar";
19
20
  import { Button, type ButtonProps } from "@/components/Button";
20
21
  import { FallbackImage } from "@/components/FallbackImage";
@@ -0,0 +1,152 @@
1
+ import { type ReactNode } from "react";
2
+ import { Image, Pressable, View, type ViewProps } from "react-native";
3
+
4
+ import { Text } from "@/components/Text";
5
+ import { ChevronLeftMd, ChevronRightMd } from "@medway-ui/icons/native";
6
+
7
+ import { Button } from "@/components/Button";
8
+ import { cn } from "@/lib/utils";
9
+
10
+ interface CarouselProps extends ViewProps {
11
+ slideIndex?: number;
12
+ slideCount?: number;
13
+ onPrevious?: () => void;
14
+ onNext?: () => void;
15
+ children?: ReactNode;
16
+ }
17
+
18
+ interface CarouselMediaSlideProps extends ViewProps {
19
+ imageSrc?: string;
20
+ image?: ReactNode;
21
+ label: string;
22
+ title: string;
23
+ linkLabel?: string;
24
+ onLinkPress?: () => void;
25
+ linkIcon?: ReactNode;
26
+ }
27
+
28
+ interface CarouselMetricSlideProps extends ViewProps {
29
+ icon?: ReactNode;
30
+ value: string;
31
+ description: string;
32
+ }
33
+
34
+ function Carousel({
35
+ className,
36
+ children,
37
+ onPrevious,
38
+ onNext,
39
+ ...props
40
+ }: CarouselProps) {
41
+ return (
42
+ <View
43
+ className={cn(
44
+ "h-[284px] flex-row items-center justify-between px-6",
45
+ className
46
+ )}
47
+ {...props}
48
+ >
49
+ <Button
50
+ variant="regular-soft"
51
+ size="md"
52
+ className="size-10 min-h-10 min-w-10 max-w-10 p-0"
53
+ iconLeft={<ChevronLeftMd width={20} height={20} color="#00205B" />}
54
+ onPress={onPrevious}
55
+ accessibilityLabel="Slide anterior"
56
+ />
57
+ <View className="h-full min-w-0 flex-1 items-center justify-center px-4">
58
+ {children}
59
+ </View>
60
+ <Button
61
+ variant="regular-soft"
62
+ size="md"
63
+ className="size-10 min-h-10 min-w-10 max-w-10 p-0"
64
+ iconLeft={<ChevronRightMd width={20} height={20} color="#00205B" />}
65
+ onPress={onNext}
66
+ accessibilityLabel="Próximo slide"
67
+ />
68
+ </View>
69
+ );
70
+ }
71
+
72
+ function CarouselMediaSlide({
73
+ className,
74
+ imageSrc,
75
+ image,
76
+ label,
77
+ title,
78
+ linkLabel = "Ver coleção",
79
+ onLinkPress,
80
+ linkIcon,
81
+ ...props
82
+ }: CarouselMediaSlideProps) {
83
+ return (
84
+ <View className={cn("items-center gap-2", className)} {...props}>
85
+ {image ??
86
+ (imageSrc ? (
87
+ <Image
88
+ source={{ uri: imageSrc }}
89
+ accessibilityLabel={title}
90
+ className="h-[188px] w-[161px]"
91
+ resizeMode="contain"
92
+ />
93
+ ) : null)}
94
+ <View className="items-center gap-2">
95
+ <View className="items-center">
96
+ <Text className="text-sm font-medium text-primary-foreground">
97
+ {label}
98
+ </Text>
99
+ <Text className="text-center text-title-xs font-semibold text-primary-foreground">
100
+ {title}
101
+ </Text>
102
+ </View>
103
+ {linkLabel ? (
104
+ <Pressable
105
+ accessibilityRole="link"
106
+ className="flex-row items-center gap-1"
107
+ onPress={onLinkPress}
108
+ >
109
+ <Text className="text-xs font-semibold text-secondary-stroke-emphasis">
110
+ {linkLabel}
111
+ </Text>
112
+ {linkIcon}
113
+ </Pressable>
114
+ ) : null}
115
+ </View>
116
+ </View>
117
+ );
118
+ }
119
+
120
+ function CarouselMetricSlide({
121
+ className,
122
+ icon,
123
+ value,
124
+ description,
125
+ ...props
126
+ }: CarouselMetricSlideProps) {
127
+ return (
128
+ <View
129
+ className={cn("max-w-[242px] items-center gap-3", className)}
130
+ {...props}
131
+ >
132
+ {icon ? (
133
+ <View className="items-center justify-center">{icon}</View>
134
+ ) : null}
135
+ <View className="w-full items-center gap-3">
136
+ <Text className="text-center text-[32px] font-bold leading-10 text-primary-foreground">
137
+ {value}
138
+ </Text>
139
+ <Text className="text-center text-sm font-semibold leading-sm text-primary-foreground">
140
+ {description}
141
+ </Text>
142
+ </View>
143
+ </View>
144
+ );
145
+ }
146
+
147
+ export { Carousel, CarouselMediaSlide, CarouselMetricSlide };
148
+ export type {
149
+ CarouselMediaSlideProps,
150
+ CarouselMetricSlideProps,
151
+ CarouselProps,
152
+ };
@@ -1,11 +1,21 @@
1
1
  import { useState, type ReactNode } from "react";
2
2
  import { type ViewStyle } from "react-native";
3
+ import { cssInterop } from "nativewind";
3
4
  import * as CheckboxPrimitive from "@rn-primitives/checkbox";
4
5
  import { cva, type VariantProps } from "class-variance-authority";
5
6
  import { cn } from "@/lib/utils";
6
7
  import { Check, RemoveMinus } from "@medway-ui/icons/native";
7
8
  import { useThemeColor } from "@/hooks/useThemeColor";
8
9
 
10
+ // `@rn-primitives/checkbox` renders plain React Native primitives with no
11
+ // NativeWind support, so `className` is otherwise silently ignored.
12
+ const StyledCheckboxRoot = cssInterop(CheckboxPrimitive.Root, {
13
+ className: "style",
14
+ });
15
+ const StyledCheckboxIndicator = cssInterop(CheckboxPrimitive.Indicator, {
16
+ className: "style",
17
+ });
18
+
9
19
  interface CheckboxProps
10
20
  extends Omit<
11
21
  CheckboxPrimitive.RootProps,
@@ -99,7 +109,7 @@ function Checkbox({
99
109
  };
100
110
 
101
111
  return (
102
- <CheckboxPrimitive.Root
112
+ <StyledCheckboxRoot
103
113
  checked={isChecked}
104
114
  onCheckedChange={handleCheckedChange}
105
115
  disabled={disabled}
@@ -118,15 +128,15 @@ function Checkbox({
118
128
  )}
119
129
  {...props}
120
130
  >
121
- <CheckboxPrimitive.Indicator className="items-center justify-center">
131
+ <StyledCheckboxIndicator className="items-center justify-center">
122
132
  {iconColor && (uncheckIcon ? (
123
133
  <RemoveMinus width={iconSize} height={iconSize} strokeWidth={2.6} color={iconColor} />
124
134
  ) : (
125
135
  <Check width={iconSize} height={iconSize} strokeWidth={2.6} color={iconColor} />
126
136
  ))}
127
- </CheckboxPrimitive.Indicator>
137
+ </StyledCheckboxIndicator>
128
138
  {children}
129
- </CheckboxPrimitive.Root>
139
+ </StyledCheckboxRoot>
130
140
  );
131
141
  }
132
142
 
@@ -0,0 +1,312 @@
1
+ import {
2
+ createContext,
3
+ forwardRef,
4
+ useContext,
5
+ useEffect,
6
+ useId,
7
+ useMemo,
8
+ type ReactNode,
9
+ } from "react";
10
+ import { Dimensions, Pressable, StyleSheet, View, type PressableProps, type ViewProps } from "react-native";
11
+
12
+ import { Text } from "@/components/Text";
13
+ import * as DropdownMenuPrimitive from "@rn-primitives/dropdown-menu";
14
+
15
+ import { useOverlayHost } from "@/lib/overlay-host";
16
+ import { cn } from "@/lib/utils";
17
+
18
+ interface DropdownMenuProps extends DropdownMenuPrimitive.RootProps {
19
+ children?: ReactNode;
20
+ }
21
+
22
+ interface DropdownMenuTriggerProps extends DropdownMenuPrimitive.TriggerProps {
23
+ children?: ReactNode;
24
+ className?: string;
25
+ }
26
+
27
+ interface DropdownMenuContentProps {
28
+ className?: string;
29
+ children?: ReactNode;
30
+ align?: "start" | "center" | "end";
31
+ sideOffset?: number;
32
+ /** Backdrop dim behind the menu. Defaults to true. */
33
+ overlay?: boolean;
34
+ }
35
+
36
+ interface DropdownMenuItemProps extends Omit<PressableProps, "children"> {
37
+ icon?: ReactNode;
38
+ children?: ReactNode;
39
+ className?: string;
40
+ }
41
+
42
+ type MenuActionsContextValue = {
43
+ close: () => void;
44
+ };
45
+
46
+ const MenuActionsContext = createContext<MenuActionsContextValue | null>(null);
47
+
48
+ function useMenuActions() {
49
+ const ctx = useContext(MenuActionsContext);
50
+ if (!ctx) {
51
+ throw new Error("DropdownMenuItem must be used within DropdownMenu");
52
+ }
53
+ return ctx;
54
+ }
55
+
56
+ function MenuActionsBridge({ children }: { children: ReactNode }) {
57
+ const { onOpenChange, setTriggerPosition, setContentLayout } =
58
+ DropdownMenuPrimitive.useRootContext();
59
+
60
+ const value = useMemo<MenuActionsContextValue>(
61
+ () => ({
62
+ close: () => {
63
+ setTriggerPosition(null);
64
+ setContentLayout(null);
65
+ onOpenChange(false);
66
+ },
67
+ }),
68
+ [onOpenChange, setContentLayout, setTriggerPosition]
69
+ );
70
+
71
+ return (
72
+ <MenuActionsContext.Provider value={value}>{children}</MenuActionsContext.Provider>
73
+ );
74
+ }
75
+
76
+ function DropdownMenu({ children, ...props }: DropdownMenuProps) {
77
+ return (
78
+ <DropdownMenuPrimitive.Root {...props}>
79
+ <MenuActionsBridge>{children}</MenuActionsBridge>
80
+ </DropdownMenuPrimitive.Root>
81
+ );
82
+ }
83
+
84
+ const DropdownMenuTrigger = forwardRef<
85
+ DropdownMenuPrimitive.TriggerRef,
86
+ DropdownMenuTriggerProps
87
+ >(function DropdownMenuTrigger({ className, children, style, ...props }, ref) {
88
+ return (
89
+ <DropdownMenuPrimitive.Trigger
90
+ ref={ref}
91
+ collapsable={false}
92
+ accessibilityRole="button"
93
+ className={cn("items-center justify-center", className)}
94
+ style={(state) => [
95
+ {
96
+ minWidth: 32,
97
+ minHeight: 32,
98
+ alignItems: "center",
99
+ justifyContent: "center",
100
+ },
101
+ typeof style === "function" ? style(state) : style,
102
+ ]}
103
+ {...props}
104
+ >
105
+ {children}
106
+ </DropdownMenuPrimitive.Trigger>
107
+ );
108
+ });
109
+
110
+ /**
111
+ * Content renderizado no OverlayHost do MedwayUiProvider (acima da stack).
112
+ * Mantém Root/Trigger do @rn-primitives/dropdown-menu para open + measure.
113
+ *
114
+ * `triggerPosition` vem em coordenadas de janela (pageX/pageY). O OverlayHost
115
+ * fica abaixo do header da navegação, então subtraímos a origem do host.
116
+ */
117
+ function DropdownMenuContent({
118
+ className,
119
+ align = "end",
120
+ sideOffset = 8,
121
+ overlay: showOverlay = true,
122
+ children,
123
+ }: DropdownMenuContentProps) {
124
+ const id = useId();
125
+ const overlay = useOverlayHost();
126
+ const { close } = useMenuActions();
127
+ const { open, triggerPosition } = DropdownMenuPrimitive.useRootContext();
128
+
129
+ useEffect(() => {
130
+ if (!open || !triggerPosition) {
131
+ overlay.unmount(id);
132
+ return;
133
+ }
134
+
135
+ let cancelled = false;
136
+ const menuWidth = 200;
137
+ const windowWidth = Dimensions.get("window").width;
138
+
139
+ overlay.measureHostOrigin((hostOrigin) => {
140
+ if (cancelled) {
141
+ return;
142
+ }
143
+
144
+ let left =
145
+ triggerPosition.pageX - hostOrigin.x;
146
+
147
+ if (align === "end") {
148
+ left =
149
+ triggerPosition.pageX +
150
+ triggerPosition.width -
151
+ menuWidth -
152
+ hostOrigin.x;
153
+ } else if (align === "center") {
154
+ left =
155
+ triggerPosition.pageX +
156
+ triggerPosition.width / 2 -
157
+ menuWidth / 2 -
158
+ hostOrigin.x;
159
+ }
160
+
161
+ left = Math.max(8, Math.min(left, windowWidth - hostOrigin.x - menuWidth - 8));
162
+
163
+ const top =
164
+ triggerPosition.pageY +
165
+ triggerPosition.height +
166
+ sideOffset -
167
+ hostOrigin.y;
168
+
169
+ overlay.mount(
170
+ id,
171
+ <MenuActionsContext.Provider value={{ close }}>
172
+ <View style={styles.layer} pointerEvents="box-none">
173
+ <Pressable
174
+ accessibilityLabel="Fechar menu"
175
+ style={[
176
+ StyleSheet.absoluteFill,
177
+ showOverlay ? styles.backdrop : null,
178
+ ]}
179
+ onPress={close}
180
+ />
181
+ <View
182
+ className={cn(
183
+ "overflow-hidden rounded-md border border-gray-stroke-muted bg-surface-elevated p-1 shadow-md",
184
+ className
185
+ )}
186
+ // Fallback visual caso NativeWind não aplique no host de overlay
187
+ style={[
188
+ styles.menu,
189
+ {
190
+ top,
191
+ left,
192
+ width: menuWidth,
193
+ backgroundColor: "white",
194
+ borderRadius: 8,
195
+ borderWidth: 1,
196
+ borderColor: "#E7E9EF",
197
+ padding: 4,
198
+ },
199
+ ]}
200
+ >
201
+ {children}
202
+ </View>
203
+ </View>
204
+ </MenuActionsContext.Provider>
205
+ );
206
+ });
207
+
208
+ return () => {
209
+ cancelled = true;
210
+ overlay.unmount(id);
211
+ };
212
+ }, [
213
+ align,
214
+ children,
215
+ className,
216
+ close,
217
+ id,
218
+ open,
219
+ overlay,
220
+ showOverlay,
221
+ sideOffset,
222
+ triggerPosition,
223
+ ]);
224
+
225
+ return null;
226
+ }
227
+
228
+ function DropdownMenuGroup({ className, children, ...props }: ViewProps) {
229
+ return (
230
+ <View className={cn("gap-0", className)} {...props}>
231
+ {children}
232
+ </View>
233
+ );
234
+ }
235
+
236
+ function DropdownMenuSeparator({ className, ...props }: ViewProps) {
237
+ return (
238
+ <View className={cn("my-1 h-px bg-gray-stroke-muted", className)} {...props} />
239
+ );
240
+ }
241
+
242
+ function DropdownMenuItem({
243
+ className,
244
+ icon,
245
+ children,
246
+ onPress,
247
+ ...props
248
+ }: DropdownMenuItemProps) {
249
+ const { close } = useMenuActions();
250
+
251
+ return (
252
+ <Pressable
253
+ accessibilityRole="button"
254
+ className={cn(
255
+ "flex-row items-center gap-2 rounded-md px-3 py-2 active:bg-gray-background-soft",
256
+ className
257
+ )}
258
+ onPress={(event) => {
259
+ onPress?.(event);
260
+ close();
261
+ }}
262
+ {...props}
263
+ >
264
+ {icon ? (
265
+ <View className="size-4 shrink-0" pointerEvents="none">
266
+ {icon}
267
+ </View>
268
+ ) : null}
269
+ {typeof children === "string" ? (
270
+ <Text
271
+ numberOfLines={1}
272
+ className="flex-1 text-xs font-medium text-gray-foreground"
273
+ >
274
+ {children}
275
+ </Text>
276
+ ) : (
277
+ children
278
+ )}
279
+ </Pressable>
280
+ );
281
+ }
282
+
283
+ const styles = StyleSheet.create({
284
+ layer: {
285
+ ...StyleSheet.absoluteFill,
286
+ },
287
+ backdrop: {
288
+ // Alinhado ao Dialog/Sheet (`bg-black/50`); style explícito porque
289
+ // NativeWind pode não aplicar classes montadas no OverlayHost.
290
+ backgroundColor: "rgba(0, 0, 0, 0.5)",
291
+ },
292
+ menu: {
293
+ position: "absolute",
294
+ zIndex: 2,
295
+ elevation: 8,
296
+ },
297
+ });
298
+
299
+ export {
300
+ DropdownMenu,
301
+ DropdownMenuContent,
302
+ DropdownMenuGroup,
303
+ DropdownMenuItem,
304
+ DropdownMenuSeparator,
305
+ DropdownMenuTrigger,
306
+ };
307
+ export type {
308
+ DropdownMenuContentProps,
309
+ DropdownMenuItemProps,
310
+ DropdownMenuProps,
311
+ DropdownMenuTriggerProps,
312
+ };