@medway-ui/native 1.9.2 → 1.10.1

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 (297) hide show
  1. package/README.md +2 -2
  2. package/lib/commonjs/components/Alert.js +36 -25
  3. package/lib/commonjs/components/Alert.js.map +1 -1
  4. package/lib/commonjs/components/AlertDialog.js +22 -23
  5. package/lib/commonjs/components/AlertDialog.js.map +1 -1
  6. package/lib/commonjs/components/Avatar.js +4 -4
  7. package/lib/commonjs/components/Badge.js +22 -15
  8. package/lib/commonjs/components/Badge.js.map +1 -1
  9. package/lib/commonjs/components/Button.js +56 -42
  10. package/lib/commonjs/components/Button.js.map +1 -1
  11. package/lib/commonjs/components/CardNews.js +18 -13
  12. package/lib/commonjs/components/CardNews.js.map +1 -1
  13. package/lib/commonjs/components/Carousel.js +12 -8
  14. package/lib/commonjs/components/Carousel.js.map +1 -1
  15. package/lib/commonjs/components/Checkbox.js +5 -5
  16. package/lib/commonjs/components/Checkbox.js.map +1 -1
  17. package/lib/commonjs/components/Chip.js +52 -23
  18. package/lib/commonjs/components/Chip.js.map +1 -1
  19. package/lib/commonjs/components/Choice.js +26 -12
  20. package/lib/commonjs/components/Choice.js.map +1 -1
  21. package/lib/commonjs/components/Dialog.js +16 -8
  22. package/lib/commonjs/components/Dialog.js.map +1 -1
  23. package/lib/commonjs/components/DropdownMenu.js +35 -14
  24. package/lib/commonjs/components/DropdownMenu.js.map +1 -1
  25. package/lib/commonjs/components/EmptyState.js +5 -5
  26. package/lib/commonjs/components/EmptyState.js.map +1 -1
  27. package/lib/commonjs/components/FallbackImage.js +42 -35
  28. package/lib/commonjs/components/FallbackImage.js.map +1 -1
  29. package/lib/commonjs/components/Feed.js +6 -5
  30. package/lib/commonjs/components/Feed.js.map +1 -1
  31. package/lib/commonjs/components/FeedNotice.js +3 -3
  32. package/lib/commonjs/components/Feedback.js +2 -4
  33. package/lib/commonjs/components/Feedback.js.map +1 -1
  34. package/lib/commonjs/components/Filter.js +6 -9
  35. package/lib/commonjs/components/Filter.js.map +1 -1
  36. package/lib/commonjs/components/IndexLevel.js +4 -4
  37. package/lib/commonjs/components/Input.js +25 -20
  38. package/lib/commonjs/components/Input.js.map +1 -1
  39. package/lib/commonjs/components/InvitePopover.js +19 -8
  40. package/lib/commonjs/components/InvitePopover.js.map +1 -1
  41. package/lib/commonjs/components/Label.js +2 -2
  42. package/lib/commonjs/components/LeaderboardCard.js +3 -2
  43. package/lib/commonjs/components/LeaderboardCard.js.map +1 -1
  44. package/lib/commonjs/components/Loading.js +18 -15
  45. package/lib/commonjs/components/Loading.js.map +1 -1
  46. package/lib/commonjs/components/LoadingOverlay.js +36 -43
  47. package/lib/commonjs/components/LoadingOverlay.js.map +1 -1
  48. package/lib/commonjs/components/MemberRow.js +7 -6
  49. package/lib/commonjs/components/MemberRow.js.map +1 -1
  50. package/lib/commonjs/components/PageHeader.js +3 -2
  51. package/lib/commonjs/components/PageHeader.js.map +1 -1
  52. package/lib/commonjs/components/Popover.js +1 -1
  53. package/lib/commonjs/components/PostForm.js +38 -24
  54. package/lib/commonjs/components/PostForm.js.map +1 -1
  55. package/lib/commonjs/components/PrizeCollection.js +5 -5
  56. package/lib/commonjs/components/PrizeCollection.js.map +1 -1
  57. package/lib/commonjs/components/RadioGroup.js +65 -15
  58. package/lib/commonjs/components/RadioGroup.js.map +1 -1
  59. package/lib/commonjs/components/SectionHeader.js +3 -2
  60. package/lib/commonjs/components/SectionHeader.js.map +1 -1
  61. package/lib/commonjs/components/SegmentControl.js +4 -4
  62. package/lib/commonjs/components/Select.js +7 -6
  63. package/lib/commonjs/components/Select.js.map +1 -1
  64. package/lib/commonjs/components/Separator.js +2 -2
  65. package/lib/commonjs/components/Separator.js.map +1 -1
  66. package/lib/commonjs/components/Sheet.js +17 -11
  67. package/lib/commonjs/components/Sheet.js.map +1 -1
  68. package/lib/commonjs/components/Skeleton.js +2 -2
  69. package/lib/commonjs/components/Skeleton.js.map +1 -1
  70. package/lib/commonjs/components/Switch.js +3 -3
  71. package/lib/commonjs/components/Tabs.js +3 -2
  72. package/lib/commonjs/components/Tabs.js.map +1 -1
  73. package/lib/commonjs/components/Tag.js +52 -32
  74. package/lib/commonjs/components/Tag.js.map +1 -1
  75. package/lib/commonjs/components/Text.js +1 -1
  76. package/lib/commonjs/components/ThemedIcon.js +1 -1
  77. package/lib/commonjs/components/Toast.js +12 -12
  78. package/lib/commonjs/components/Toast.js.map +1 -1
  79. package/lib/commonjs/components/Tooltip.js +2 -2
  80. package/lib/commonjs/editor/NoteEditor.js +190 -67
  81. package/lib/commonjs/editor/NoteEditor.js.map +1 -1
  82. package/lib/commonjs/editor/editorHtml.generated.js +1 -1
  83. package/lib/commonjs/editor/editorHtml.generated.js.map +1 -1
  84. package/lib/commonjs/editor/index.js.map +1 -1
  85. package/lib/commonjs/editor/protocol.js +1 -2
  86. package/lib/commonjs/editor/protocol.js.map +1 -1
  87. package/lib/commonjs/hooks/useThemeColor.js +41 -19
  88. package/lib/commonjs/hooks/useThemeColor.js.map +1 -1
  89. package/lib/commonjs/index.js +46 -46
  90. package/lib/commonjs/lib/backdrop.js +18 -0
  91. package/lib/commonjs/lib/backdrop.js.map +1 -0
  92. package/lib/commonjs/lib/overlay-host.js +5 -3
  93. package/lib/commonjs/lib/overlay-host.js.map +1 -1
  94. package/lib/commonjs/lib/raised-surface.js +1 -1
  95. package/lib/module/components/Alert.js +36 -25
  96. package/lib/module/components/Alert.js.map +1 -1
  97. package/lib/module/components/AlertDialog.js +21 -22
  98. package/lib/module/components/AlertDialog.js.map +1 -1
  99. package/lib/module/components/Avatar.js +4 -4
  100. package/lib/module/components/Badge.js +22 -15
  101. package/lib/module/components/Badge.js.map +1 -1
  102. package/lib/module/components/Button.js +56 -42
  103. package/lib/module/components/Button.js.map +1 -1
  104. package/lib/module/components/CardNews.js +18 -13
  105. package/lib/module/components/CardNews.js.map +1 -1
  106. package/lib/module/components/Carousel.js +12 -8
  107. package/lib/module/components/Carousel.js.map +1 -1
  108. package/lib/module/components/Checkbox.js +5 -5
  109. package/lib/module/components/Checkbox.js.map +1 -1
  110. package/lib/module/components/Chip.js +52 -23
  111. package/lib/module/components/Chip.js.map +1 -1
  112. package/lib/module/components/Choice.js +26 -12
  113. package/lib/module/components/Choice.js.map +1 -1
  114. package/lib/module/components/Dialog.js +16 -8
  115. package/lib/module/components/Dialog.js.map +1 -1
  116. package/lib/module/components/DropdownMenu.js +36 -15
  117. package/lib/module/components/DropdownMenu.js.map +1 -1
  118. package/lib/module/components/EmptyState.js +5 -5
  119. package/lib/module/components/EmptyState.js.map +1 -1
  120. package/lib/module/components/FallbackImage.js +42 -35
  121. package/lib/module/components/FallbackImage.js.map +1 -1
  122. package/lib/module/components/Feed.js +6 -5
  123. package/lib/module/components/Feed.js.map +1 -1
  124. package/lib/module/components/FeedNotice.js +3 -3
  125. package/lib/module/components/Feedback.js +2 -4
  126. package/lib/module/components/Feedback.js.map +1 -1
  127. package/lib/module/components/Filter.js +6 -9
  128. package/lib/module/components/Filter.js.map +1 -1
  129. package/lib/module/components/IndexLevel.js +4 -4
  130. package/lib/module/components/Input.js +24 -18
  131. package/lib/module/components/Input.js.map +1 -1
  132. package/lib/module/components/InvitePopover.js +20 -9
  133. package/lib/module/components/InvitePopover.js.map +1 -1
  134. package/lib/module/components/Label.js +2 -2
  135. package/lib/module/components/LeaderboardCard.js +3 -2
  136. package/lib/module/components/LeaderboardCard.js.map +1 -1
  137. package/lib/module/components/Loading.js +18 -15
  138. package/lib/module/components/Loading.js.map +1 -1
  139. package/lib/module/components/LoadingOverlay.js +37 -44
  140. package/lib/module/components/LoadingOverlay.js.map +1 -1
  141. package/lib/module/components/MemberRow.js +7 -6
  142. package/lib/module/components/MemberRow.js.map +1 -1
  143. package/lib/module/components/PageHeader.js +3 -2
  144. package/lib/module/components/PageHeader.js.map +1 -1
  145. package/lib/module/components/Popover.js +1 -1
  146. package/lib/module/components/PostForm.js +38 -24
  147. package/lib/module/components/PostForm.js.map +1 -1
  148. package/lib/module/components/PrizeCollection.js +5 -5
  149. package/lib/module/components/PrizeCollection.js.map +1 -1
  150. package/lib/module/components/RadioGroup.js +65 -15
  151. package/lib/module/components/RadioGroup.js.map +1 -1
  152. package/lib/module/components/SectionHeader.js +3 -2
  153. package/lib/module/components/SectionHeader.js.map +1 -1
  154. package/lib/module/components/SegmentControl.js +4 -4
  155. package/lib/module/components/Select.js +6 -5
  156. package/lib/module/components/Select.js.map +1 -1
  157. package/lib/module/components/Separator.js +2 -2
  158. package/lib/module/components/Separator.js.map +1 -1
  159. package/lib/module/components/Sheet.js +17 -11
  160. package/lib/module/components/Sheet.js.map +1 -1
  161. package/lib/module/components/Skeleton.js +2 -2
  162. package/lib/module/components/Skeleton.js.map +1 -1
  163. package/lib/module/components/Switch.js +3 -3
  164. package/lib/module/components/Tabs.js +3 -2
  165. package/lib/module/components/Tabs.js.map +1 -1
  166. package/lib/module/components/Tag.js +52 -32
  167. package/lib/module/components/Tag.js.map +1 -1
  168. package/lib/module/components/Text.js +1 -1
  169. package/lib/module/components/ThemedIcon.js +1 -1
  170. package/lib/module/components/Toast.js +12 -12
  171. package/lib/module/components/Toast.js.map +1 -1
  172. package/lib/module/components/Tooltip.js +2 -2
  173. package/lib/module/editor/NoteEditor.js +193 -69
  174. package/lib/module/editor/NoteEditor.js.map +1 -1
  175. package/lib/module/editor/editorHtml.generated.js +1 -1
  176. package/lib/module/editor/editorHtml.generated.js.map +1 -1
  177. package/lib/module/editor/index.js.map +1 -1
  178. package/lib/module/editor/protocol.js +1 -2
  179. package/lib/module/editor/protocol.js.map +1 -1
  180. package/lib/module/hooks/useThemeColor.js +41 -19
  181. package/lib/module/hooks/useThemeColor.js.map +1 -1
  182. package/lib/module/index.js +46 -46
  183. package/lib/module/lib/backdrop.js +15 -0
  184. package/lib/module/lib/backdrop.js.map +1 -0
  185. package/lib/module/lib/overlay-host.js +6 -4
  186. package/lib/module/lib/overlay-host.js.map +1 -1
  187. package/lib/module/lib/raised-surface.js +1 -1
  188. package/lib/typescript/components/Alert.d.ts +1 -1
  189. package/lib/typescript/components/Alert.d.ts.map +1 -1
  190. package/lib/typescript/components/AlertDialog.d.ts +1 -1
  191. package/lib/typescript/components/AlertDialog.d.ts.map +1 -1
  192. package/lib/typescript/components/Badge.d.ts.map +1 -1
  193. package/lib/typescript/components/Button.d.ts +1 -0
  194. package/lib/typescript/components/Button.d.ts.map +1 -1
  195. package/lib/typescript/components/CardNews.d.ts +7 -2
  196. package/lib/typescript/components/CardNews.d.ts.map +1 -1
  197. package/lib/typescript/components/Checkbox.d.ts +3 -1
  198. package/lib/typescript/components/Checkbox.d.ts.map +1 -1
  199. package/lib/typescript/components/Chip.d.ts.map +1 -1
  200. package/lib/typescript/components/Choice.d.ts +4 -1
  201. package/lib/typescript/components/Choice.d.ts.map +1 -1
  202. package/lib/typescript/components/Dialog.d.ts +2 -1
  203. package/lib/typescript/components/Dialog.d.ts.map +1 -1
  204. package/lib/typescript/components/DropdownMenu.d.ts +3 -2
  205. package/lib/typescript/components/DropdownMenu.d.ts.map +1 -1
  206. package/lib/typescript/components/FallbackImage.d.ts.map +1 -1
  207. package/lib/typescript/components/Feed.d.ts.map +1 -1
  208. package/lib/typescript/components/Feedback.d.ts.map +1 -1
  209. package/lib/typescript/components/Filter.d.ts.map +1 -1
  210. package/lib/typescript/components/IndexLevel.d.ts +1 -1
  211. package/lib/typescript/components/Input.d.ts +5 -5
  212. package/lib/typescript/components/Input.d.ts.map +1 -1
  213. package/lib/typescript/components/InvitePopover.d.ts +1 -1
  214. package/lib/typescript/components/InvitePopover.d.ts.map +1 -1
  215. package/lib/typescript/components/LeaderboardCard.d.ts.map +1 -1
  216. package/lib/typescript/components/Loading.d.ts +1 -1
  217. package/lib/typescript/components/Loading.d.ts.map +1 -1
  218. package/lib/typescript/components/LoadingOverlay.d.ts +9 -9
  219. package/lib/typescript/components/LoadingOverlay.d.ts.map +1 -1
  220. package/lib/typescript/components/PageHeader.d.ts.map +1 -1
  221. package/lib/typescript/components/PostForm.d.ts.map +1 -1
  222. package/lib/typescript/components/RadioGroup.d.ts +4 -2
  223. package/lib/typescript/components/RadioGroup.d.ts.map +1 -1
  224. package/lib/typescript/components/SectionHeader.d.ts.map +1 -1
  225. package/lib/typescript/components/SegmentControl.d.ts +1 -1
  226. package/lib/typescript/components/Select.d.ts +2 -1
  227. package/lib/typescript/components/Select.d.ts.map +1 -1
  228. package/lib/typescript/components/Separator.d.ts.map +1 -1
  229. package/lib/typescript/components/Sheet.d.ts +2 -1
  230. package/lib/typescript/components/Sheet.d.ts.map +1 -1
  231. package/lib/typescript/components/Tabs.d.ts +4 -3
  232. package/lib/typescript/components/Tabs.d.ts.map +1 -1
  233. package/lib/typescript/components/Tag.d.ts +3 -1
  234. package/lib/typescript/components/Tag.d.ts.map +1 -1
  235. package/lib/typescript/components/ThemedIcon.d.ts +1 -1
  236. package/lib/typescript/components/Toast.d.ts +1 -1
  237. package/lib/typescript/components/Toast.d.ts.map +1 -1
  238. package/lib/typescript/editor/NoteEditor.d.ts +28 -8
  239. package/lib/typescript/editor/NoteEditor.d.ts.map +1 -1
  240. package/lib/typescript/editor/editorHtml.generated.d.ts.map +1 -1
  241. package/lib/typescript/editor/index.d.ts +1 -1
  242. package/lib/typescript/editor/index.d.ts.map +1 -1
  243. package/lib/typescript/editor/protocol.d.ts +4 -14
  244. package/lib/typescript/editor/protocol.d.ts.map +1 -1
  245. package/lib/typescript/hooks/use-toast.d.ts +1 -1
  246. package/lib/typescript/hooks/useThemeColor.d.ts.map +1 -1
  247. package/lib/typescript/index.d.ts +91 -91
  248. package/lib/typescript/index.d.ts.map +1 -1
  249. package/lib/typescript/lib/backdrop.d.ts +4 -0
  250. package/lib/typescript/lib/backdrop.d.ts.map +1 -0
  251. package/lib/typescript/lib/overlay-host.d.ts +1 -0
  252. package/lib/typescript/lib/overlay-host.d.ts.map +1 -1
  253. package/package.json +9 -14
  254. package/setup/tailwind.js +16 -8
  255. package/setup/tokens.native.css +181 -80
  256. package/src/components/Alert.tsx +37 -20
  257. package/src/components/AlertDialog.tsx +18 -19
  258. package/src/components/Badge.tsx +16 -15
  259. package/src/components/Button.tsx +64 -31
  260. package/src/components/CardNews.tsx +17 -6
  261. package/src/components/Carousel.tsx +5 -5
  262. package/src/components/Checkbox.tsx +17 -7
  263. package/src/components/Chip.tsx +49 -21
  264. package/src/components/Choice.tsx +34 -14
  265. package/src/components/Dialog.tsx +15 -5
  266. package/src/components/DropdownMenu.tsx +62 -14
  267. package/src/components/EmptyState.tsx +3 -3
  268. package/src/components/FallbackImage.tsx +32 -23
  269. package/src/components/Feed.tsx +2 -1
  270. package/src/components/Feedback.tsx +0 -1
  271. package/src/components/Filter.tsx +2 -3
  272. package/src/components/Input.tsx +83 -64
  273. package/src/components/InvitePopover.tsx +11 -3
  274. package/src/components/LeaderboardCard.tsx +4 -1
  275. package/src/components/Loading.tsx +17 -14
  276. package/src/components/LoadingOverlay.tsx +33 -50
  277. package/src/components/MemberRow.tsx +3 -3
  278. package/src/components/PageHeader.tsx +4 -1
  279. package/src/components/PostForm.tsx +28 -16
  280. package/src/components/PrizeCollection.tsx +3 -3
  281. package/src/components/RadioGroup.tsx +69 -12
  282. package/src/components/SectionHeader.tsx +4 -1
  283. package/src/components/Select.tsx +4 -3
  284. package/src/components/Separator.tsx +2 -2
  285. package/src/components/Sheet.tsx +15 -8
  286. package/src/components/Skeleton.tsx +1 -1
  287. package/src/components/Tabs.tsx +5 -4
  288. package/src/components/Tag.tsx +56 -35
  289. package/src/components/Toast.tsx +10 -6
  290. package/src/editor/NoteEditor.tsx +246 -72
  291. package/src/editor/editorHtml.generated.ts +1 -1
  292. package/src/editor/index.ts +8 -4
  293. package/src/editor/protocol.ts +4 -12
  294. package/src/hooks/useThemeColor.ts +41 -19
  295. package/src/index.ts +1 -1
  296. package/src/lib/backdrop.ts +16 -0
  297. package/src/lib/overlay-host.tsx +5 -5
@@ -8,7 +8,18 @@ import {
8
8
  useState,
9
9
  type ReactNode,
10
10
  } from "react";
11
- import { Dimensions, Pressable, StyleSheet, View, type PressableProps, type ViewProps } from "react-native";
11
+ import {
12
+ BackHandler,
13
+ Dimensions,
14
+ Pressable,
15
+ StyleSheet,
16
+ View,
17
+ type PressableProps,
18
+ type PressableStateCallbackType,
19
+ type StyleProp,
20
+ type ViewProps,
21
+ type ViewStyle,
22
+ } from "react-native";
12
23
 
13
24
  import { Text } from "@/components/Text";
14
25
  import * as DropdownMenuPrimitive from "@rn-primitives/dropdown-menu";
@@ -21,8 +32,11 @@ interface DropdownMenuProps extends DropdownMenuPrimitive.RootProps {
21
32
  children?: ReactNode;
22
33
  }
23
34
 
24
- interface DropdownMenuTriggerProps extends DropdownMenuPrimitive.TriggerProps {
35
+ interface DropdownMenuTriggerProps extends Omit<DropdownMenuPrimitive.TriggerProps, "style"> {
25
36
  children?: ReactNode;
37
+ style?:
38
+ | StyleProp<ViewStyle>
39
+ | ((state: PressableStateCallbackType) => StyleProp<ViewStyle>);
26
40
  className?: string;
27
41
  }
28
42
 
@@ -47,7 +61,7 @@ type MenuActionsContextValue = {
47
61
 
48
62
  type MenuAlign = NonNullable<DropdownMenuContentProps["align"]>;
49
63
 
50
- type MenuPosition = { top: number; left: number };
64
+ type MenuPosition = { top: number; left: number; triggerTop: number; bottomLimit: number };
51
65
 
52
66
  type TriggerPosition = NonNullable<
53
67
  ReturnType<typeof DropdownMenuPrimitive.useRootContext>["triggerPosition"]
@@ -91,6 +105,13 @@ function DropdownMenu({ children, ...props }: DropdownMenuProps) {
91
105
  );
92
106
  }
93
107
 
108
+ const TRIGGER_MIN_SIZE: ViewStyle = {
109
+ minWidth: 32,
110
+ minHeight: 32,
111
+ alignItems: "center",
112
+ justifyContent: "center",
113
+ };
114
+
94
115
  const DropdownMenuTrigger = forwardRef<
95
116
  DropdownMenuPrimitive.TriggerRef,
96
117
  DropdownMenuTriggerProps
@@ -102,15 +123,11 @@ const DropdownMenuTrigger = forwardRef<
102
123
  accessibilityRole="button"
103
124
  hitSlop={6}
104
125
  className={cn("items-center justify-center", className)}
105
- style={(state) => [
106
- {
107
- minWidth: 32,
108
- minHeight: 32,
109
- alignItems: "center",
110
- justifyContent: "center",
111
- },
112
- typeof style === "function" ? style(state) : style,
113
- ]}
126
+ style={
127
+ typeof style === "function"
128
+ ? (state: PressableStateCallbackType) => [TRIGGER_MIN_SIZE, style(state)]
129
+ : [TRIGGER_MIN_SIZE, style]
130
+ }
114
131
  {...props}
115
132
  >
116
133
  {children}
@@ -142,12 +159,22 @@ function computeMenuPosition({
142
159
  const rawLeft = triggerPosition.pageX + alignOffset[align] - hostOrigin.x;
143
160
  const maxLeft = windowWidth - hostOrigin.x - MENU_WIDTH - MENU_EDGE_GAP;
144
161
 
162
+ const triggerTop = triggerPosition.pageY - hostOrigin.y;
163
+
145
164
  return {
146
165
  left: Math.max(MENU_EDGE_GAP, Math.min(rawLeft, maxLeft)),
147
- top: triggerPosition.pageY + triggerPosition.height + sideOffset - hostOrigin.y,
166
+ top: triggerTop + triggerPosition.height + sideOffset,
167
+ triggerTop,
168
+ bottomLimit: hostOrigin.height - MENU_EDGE_GAP,
148
169
  };
149
170
  }
150
171
 
172
+ function resolveMenuTop(position: MenuPosition, menuHeight: number, sideOffset: number) {
173
+ if (position.top + menuHeight <= position.bottomLimit) return position.top;
174
+
175
+ return Math.max(MENU_EDGE_GAP, position.triggerTop - sideOffset - menuHeight);
176
+ }
177
+
151
178
  function DropdownMenuContent({
152
179
  className,
153
180
  align = "end",
@@ -160,6 +187,8 @@ function DropdownMenuContent({
160
187
  const { close } = useMenuActions();
161
188
  const { open, triggerPosition } = DropdownMenuPrimitive.useRootContext();
162
189
  const [menuPosition, setMenuPosition] = useState<MenuPosition | null>(null);
190
+ const [menuHeight, setMenuHeight] = useState<number | null>(null);
191
+ const isMenuMounted = menuPosition !== null;
163
192
  const surfaceColor = useThemeColor("surface-elevated");
164
193
  const borderColor = useThemeColor("gray-stroke-muted");
165
194
  const backdropColor = useThemeColor("black-48");
@@ -167,6 +196,7 @@ function DropdownMenuContent({
167
196
  useEffect(() => {
168
197
  if (!open || !triggerPosition) {
169
198
  setMenuPosition(null);
199
+ setMenuHeight(null);
170
200
  return;
171
201
  }
172
202
 
@@ -208,13 +238,18 @@ function DropdownMenuContent({
208
238
  style={[
209
239
  styles.menu,
210
240
  {
211
- top: menuPosition.top,
241
+ top:
242
+ menuHeight === null
243
+ ? menuPosition.top
244
+ : resolveMenuTop(menuPosition, menuHeight, sideOffset),
212
245
  left: menuPosition.left,
213
246
  width: MENU_WIDTH,
214
247
  backgroundColor: surfaceColor,
215
248
  borderColor,
249
+ opacity: menuHeight === null ? 0 : 1,
216
250
  },
217
251
  ]}
252
+ onLayout={(event) => setMenuHeight(event.nativeEvent.layout.height)}
218
253
  >
219
254
  {children}
220
255
  </View>
@@ -228,12 +263,25 @@ function DropdownMenuContent({
228
263
  className,
229
264
  close,
230
265
  id,
266
+ menuHeight,
231
267
  menuPosition,
232
268
  overlay,
233
269
  showOverlay,
270
+ sideOffset,
234
271
  surfaceColor,
235
272
  ]);
236
273
 
274
+ useEffect(() => {
275
+ if (!isMenuMounted) return;
276
+
277
+ const subscription = BackHandler.addEventListener("hardwareBackPress", () => {
278
+ close();
279
+ return true;
280
+ });
281
+
282
+ return () => subscription.remove();
283
+ }, [close, isMenuMounted]);
284
+
237
285
  useEffect(() => () => overlay.unmount(id), [id, overlay]);
238
286
 
239
287
  return null;
@@ -17,9 +17,9 @@ type EmptyStateVariant = "regular" | "emphasis" | "success" | "warning" | "error
17
17
  const ICON_BACKGROUND_BY_VARIANT: Record<EmptyStateVariant, string> = {
18
18
  regular: "bg-gray-background-soft",
19
19
  emphasis: "bg-secondary-background-soft",
20
- success: "bg-green-background-soft",
21
- warning: "bg-yellow-background-soft",
22
- error: "bg-red-background-soft",
20
+ success: "bg-feedback-success-background-soft",
21
+ warning: "bg-feedback-warning-background-soft",
22
+ error: "bg-feedback-error-background-soft",
23
23
  };
24
24
 
25
25
  function EmptyState({
@@ -1,3 +1,4 @@
1
+ import { View } from "react-native";
1
2
  import Svg, {
2
3
  Defs,
3
4
  RadialGradient,
@@ -20,37 +21,45 @@ interface FallbackImageProps extends SvgProps {
20
21
  className?: string;
21
22
  }
22
23
 
24
+ // react-native-svg isn't registered with NativeWind, so the classes go on a View and the Svg fills it.
23
25
  function FallbackImage({
24
26
  className,
25
27
  label = "Medway",
26
28
  ...props
27
29
  }: FallbackImageProps) {
30
+ const isDecorative = label === "";
31
+
28
32
  return (
29
- <Svg
30
- accessibilityRole="image"
31
- accessibilityLabel={label}
32
- viewBox="0 0 320 167"
33
- preserveAspectRatio="xMidYMid slice"
33
+ <View
34
34
  className={cn("size-full", className)}
35
- {...props}
35
+ accessibilityElementsHidden={isDecorative}
36
+ importantForAccessibility={isDecorative ? "no-hide-descendants" : "auto"}
36
37
  >
37
- <Defs>
38
- <RadialGradient
39
- id={GRADIENT_ID}
40
- cx="50%"
41
- cy="42%"
42
- r="78%"
43
- >
44
- <Stop offset="0%" stopColor="#15397A" />
45
- <Stop offset="55%" stopColor="#06245E" />
46
- <Stop offset="100%" stopColor="#021536" />
47
- </RadialGradient>
48
- </Defs>
49
- <Rect width="320" height="167" fill={`url(#${GRADIENT_ID})`} />
50
- <G transform="translate(85.5 62.7) scale(1.38)" fill="#FFFFFF">
51
- <Path d={LOGO_PATH} />
52
- </G>
53
- </Svg>
38
+ <Svg
39
+ accessibilityRole="image"
40
+ accessibilityLabel={label}
41
+ viewBox="0 0 320 167"
42
+ preserveAspectRatio="xMidYMid slice"
43
+ {...props}
44
+ >
45
+ <Defs>
46
+ <RadialGradient
47
+ id={GRADIENT_ID}
48
+ cx="50%"
49
+ cy="42%"
50
+ r="78%"
51
+ >
52
+ <Stop offset="0%" stopColor="#15397A" />
53
+ <Stop offset="55%" stopColor="#06245E" />
54
+ <Stop offset="100%" stopColor="#021536" />
55
+ </RadialGradient>
56
+ </Defs>
57
+ <Rect width="320" height="167" fill={`url(#${GRADIENT_ID})`} />
58
+ <G transform="translate(85.5 62.7) scale(1.38)" fill="#FFFFFF">
59
+ <Path d={LOGO_PATH} />
60
+ </G>
61
+ </Svg>
62
+ </View>
54
63
  );
55
64
  }
56
65
 
@@ -5,6 +5,7 @@ import { Text } from "@/components/Text";
5
5
 
6
6
  import { InputText, inputVariants } from "@/components/Input";
7
7
  import { useTrackedText } from "@/hooks/useTrackedText";
8
+ import { fontFamilyMedium } from "@/lib/typography";
8
9
  import { cn } from "@/lib/utils";
9
10
 
10
11
  interface FeedProps extends ViewProps {
@@ -83,7 +84,7 @@ function FeedComposer({
83
84
  )}
84
85
  accessibilityLabel={placeholder}
85
86
  {...props}
86
- style={[{ fontFamily: "MontserratMedium" }, props.style]}
87
+ style={[{ fontFamily: fontFamilyMedium }, props.style]}
87
88
  />
88
89
  </View>
89
90
  <InputText className="text-right text-xs font-medium text-gray-foreground">
@@ -78,7 +78,6 @@ function Feedback({
78
78
 
79
79
  const shouldRenderIcon = showIcon && !!icon;
80
80
 
81
- // Aplica o tamanho do size ao ícone, sem sobrescrever width/height explícitos.
82
81
  const sizedIcon = isValidElement<{ width?: number; height?: number }>(icon)
83
82
  ? cloneElement(icon, {
84
83
  width: icon.props.width ?? sz.iconSize,
@@ -27,7 +27,7 @@ const SIZE_MAP: Record<
27
27
  > = {
28
28
  sm: { container: "gap-2 px-3 py-2", text: "text-xs", icon: 16 },
29
29
  md: { container: "gap-2.5 px-3.5 py-2.5", text: "text-sm", icon: 20 },
30
- lg: { container: "gap-3 px-4 py-3", text: "text-base", icon: 24 },
30
+ lg: { container: "gap-3 px-4 py-3", text: "text-md", icon: 24 },
31
31
  };
32
32
 
33
33
  const SHADOW_STYLE = {
@@ -75,8 +75,7 @@ function Filter({
75
75
  disabled={disabled}
76
76
  accessibilityRole="button"
77
77
  accessibilityState={{ selected, disabled }}
78
- className={cn("rounded-md", outerBg, className)}
79
- style={{ padding: 2 }}
78
+ className={cn("rounded-md p-0.5", outerBg, className)}
80
79
  >
81
80
  <View
82
81
  className={cn(
@@ -1,4 +1,5 @@
1
1
  import {
2
+ forwardRef,
2
3
  useState,
3
4
  useCallback,
4
5
  useEffect,
@@ -315,18 +316,21 @@ function useInputFocus(onFocus?: FocusHandler, onBlur?: BlurHandler) {
315
316
  return { focused, handleFocus, handleBlur };
316
317
  }
317
318
 
318
- function InputPassword({
319
- className,
320
- size = "lg",
321
- invalid = false,
322
- label,
323
- value,
324
- defaultValue,
325
- onFocus,
326
- onBlur,
327
- onChangeText,
328
- ...props
329
- }: InputPasswordProps) {
319
+ const InputPassword = forwardRef<TextInput, InputPasswordProps>(function InputPassword(
320
+ {
321
+ className,
322
+ size = "lg",
323
+ invalid = false,
324
+ label,
325
+ value,
326
+ defaultValue,
327
+ onFocus,
328
+ onBlur,
329
+ onChangeText,
330
+ ...props
331
+ }: InputPasswordProps,
332
+ ref
333
+ ) {
330
334
  const [visible, setVisible] = useState(false);
331
335
  const { focused, handleFocus, handleBlur } = useInputFocus(onFocus, onBlur);
332
336
  const { text, handleChangeText } = useTrackedText(value, defaultValue, onChangeText);
@@ -360,6 +364,7 @@ function InputPassword({
360
364
  </InputLabel>
361
365
  )}
362
366
  <TextInput
367
+ ref={ref}
363
368
  secureTextEntry={!visible}
364
369
  className={cn(
365
370
  inputVariants({ size, invalid, focused, disabled: props.editable === false }),
@@ -395,24 +400,27 @@ function InputPassword({
395
400
  </InputEndIcon>
396
401
  </InputAdornment>
397
402
  );
398
- }
403
+ });
399
404
 
400
- function InputNumeric({
401
- className,
402
- size = "lg",
403
- invalid = false,
404
- iconLeft,
405
- min,
406
- value,
407
- defaultValue,
408
- onChangeText,
409
- editable = true,
410
- allowNegative = false,
411
- label,
412
- onFocus,
413
- onBlur,
414
- ...props
415
- }: InputNumericProps) {
405
+ const InputNumeric = forwardRef<TextInput, InputNumericProps>(function InputNumeric(
406
+ {
407
+ className,
408
+ size = "lg",
409
+ invalid = false,
410
+ iconLeft,
411
+ min,
412
+ value,
413
+ defaultValue,
414
+ onChangeText,
415
+ editable = true,
416
+ allowNegative = false,
417
+ label,
418
+ onFocus,
419
+ onBlur,
420
+ ...props
421
+ }: InputNumericProps,
422
+ ref
423
+ ) {
416
424
  const [internalValue, setInternalValue] = useState(String(defaultValue ?? ""));
417
425
  const isControlled = value !== undefined;
418
426
  const displayValue = isControlled ? String(value ?? "") : internalValue;
@@ -456,6 +464,7 @@ function InputNumeric({
456
464
  </InputLabel>
457
465
  )}
458
466
  <TextInput
467
+ ref={ref}
459
468
  keyboardType={allowNegative ? "numbers-and-punctuation" : "numeric"}
460
469
  value={displayValue}
461
470
  onChangeText={(text) => {
@@ -517,24 +526,27 @@ function InputNumeric({
517
526
  </InputEndIcon>
518
527
  </InputAdornment>
519
528
  );
520
- }
529
+ });
521
530
 
522
- function InputTextual({
523
- className,
524
- type = "text",
525
- size = "lg",
526
- invalid = false,
527
- iconLeft,
528
- iconRight,
529
- label,
530
- value,
531
- defaultValue,
532
- editable = true,
533
- onFocus,
534
- onBlur,
535
- onChangeText,
536
- ...props
537
- }: InputProps) {
531
+ const InputTextual = forwardRef<TextInput, InputProps>(function InputTextual(
532
+ {
533
+ className,
534
+ type = "text",
535
+ size = "lg",
536
+ invalid = false,
537
+ iconLeft,
538
+ iconRight,
539
+ label,
540
+ value,
541
+ defaultValue,
542
+ editable = true,
543
+ onFocus,
544
+ onBlur,
545
+ onChangeText,
546
+ ...props
547
+ }: InputProps,
548
+ ref
549
+ ) {
538
550
  const { focused, handleFocus, handleBlur } = useInputFocus(onFocus, onBlur);
539
551
  const { text, handleChangeText } = useTrackedText(value, defaultValue, onChangeText);
540
552
  const isFloated = focused || text !== "";
@@ -580,6 +592,7 @@ function InputTextual({
580
592
  </InputLabel>
581
593
  )}
582
594
  <TextInput
595
+ ref={ref}
583
596
  keyboardType={type === "email" ? "email-address" : "default"}
584
597
  value={value}
585
598
  defaultValue={defaultValue}
@@ -608,27 +621,31 @@ function InputTextual({
608
621
  )}
609
622
  </InputAdornment>
610
623
  );
611
- }
624
+ });
612
625
 
613
- function Input({
614
- className,
615
- type = "text",
616
- size = "lg",
617
- invalid = false,
618
- iconLeft,
619
- iconRight,
620
- label,
621
- value,
622
- defaultValue,
623
- editable = true,
624
- onFocus,
625
- onBlur,
626
- onChangeText,
627
- ...props
628
- }: InputProps) {
626
+ const Input = forwardRef<TextInput, InputProps>(function Input(
627
+ {
628
+ className,
629
+ type = "text",
630
+ size = "lg",
631
+ invalid = false,
632
+ iconLeft,
633
+ iconRight,
634
+ label,
635
+ value,
636
+ defaultValue,
637
+ editable = true,
638
+ onFocus,
639
+ onBlur,
640
+ onChangeText,
641
+ ...props
642
+ }: InputProps,
643
+ ref
644
+ ) {
629
645
  if (type === "numeric") {
630
646
  return (
631
647
  <InputNumeric
648
+ ref={ref}
632
649
  className={className}
633
650
  size={size}
634
651
  invalid={invalid}
@@ -648,6 +665,7 @@ function Input({
648
665
  if (type === "password") {
649
666
  return (
650
667
  <InputPassword
668
+ ref={ref}
651
669
  className={className}
652
670
  size={size}
653
671
  invalid={invalid}
@@ -665,6 +683,7 @@ function Input({
665
683
 
666
684
  return (
667
685
  <InputTextual
686
+ ref={ref}
668
687
  className={className}
669
688
  type={type}
670
689
  size={size}
@@ -681,7 +700,7 @@ function Input({
681
700
  {...props}
682
701
  />
683
702
  );
684
- }
703
+ });
685
704
 
686
705
  export {
687
706
  Input,
@@ -1,5 +1,5 @@
1
1
  import { type ReactNode } from "react";
2
- import { View, type ViewProps } from "react-native";
2
+ import { StyleSheet, View, useWindowDimensions, type ViewProps } from "react-native";
3
3
 
4
4
  import { Text } from "@/components/Text";
5
5
  import * as PopoverPrimitive from "@rn-primitives/popover";
@@ -62,13 +62,20 @@ function InvitePopoverTrigger({
62
62
  );
63
63
  }
64
64
 
65
+ const CONTENT_MAX_WIDTH = 370;
66
+ const CONTENT_WINDOW_GUTTER = 32;
67
+
65
68
  function InvitePopoverContent({
66
69
  className,
67
70
  align = "end",
68
71
  sideOffset = 8,
72
+ style,
69
73
  children,
70
74
  ...props
71
75
  }: InvitePopoverContentProps) {
76
+ const { width: windowWidth } = useWindowDimensions();
77
+ const contentWidth = Math.min(CONTENT_MAX_WIDTH, windowWidth - CONTENT_WINDOW_GUTTER);
78
+
72
79
  return (
73
80
  <PopoverPrimitive.Portal>
74
81
  <PopoverPrimitive.Overlay className="absolute inset-0" />
@@ -76,9 +83,10 @@ function InvitePopoverContent({
76
83
  align={align}
77
84
  sideOffset={sideOffset}
78
85
  className={cn(
79
- "z-50 w-[min(370px,100vw-2rem)] border-0 bg-transparent p-0 shadow-none",
86
+ "z-50 border-0 bg-transparent p-0 shadow-none",
80
87
  className
81
88
  )}
89
+ style={{ ...StyleSheet.flatten(style), width: contentWidth }}
82
90
  {...props}
83
91
  >
84
92
  {children}
@@ -111,7 +119,7 @@ function InvitePopoverCard({ className, children, ...props }: ViewProps) {
111
119
  return (
112
120
  <View
113
121
  className={cn(
114
- "gap-6 rounded-lg border border-gray-stroke-muted bg-gray-foreground-onfill p-4",
122
+ "gap-6 rounded-lg border border-gray-stroke-muted bg-surface-elevated p-4",
115
123
  className
116
124
  )}
117
125
  {...props}
@@ -47,7 +47,10 @@ function LeaderboardCardHeader({
47
47
  </View>
48
48
  ) : null}
49
49
  <View className="min-w-0 flex-1 flex-row items-center justify-between gap-2">
50
- <Text className="text-lg font-semibold text-primary-foreground">
50
+ <Text
51
+ accessibilityRole="header"
52
+ className="text-lg font-semibold text-primary-foreground"
53
+ >
51
54
  {title}
52
55
  </Text>
53
56
  {typeof badge === "string" ? (
@@ -1,4 +1,5 @@
1
1
  import { useEffect } from "react";
2
+ import { View } from "react-native";
2
3
  import Svg, { Path } from "react-native-svg";
3
4
  import Animated, {
4
5
  useSharedValue,
@@ -44,20 +45,22 @@ function Loading({
44
45
  }));
45
46
 
46
47
  return (
47
- <Animated.View style={animatedStyle} className={cn(className)}>
48
- <Svg
49
- width={size}
50
- height={size}
51
- viewBox="0 0 24 24"
52
- fill="none"
53
- stroke={stroke}
54
- strokeWidth={2}
55
- strokeLinecap="round"
56
- strokeLinejoin="round"
57
- >
58
- <Path d="M21 12a9 9 0 1 1-6.219-8.56" />
59
- </Svg>
60
- </Animated.View>
48
+ <View className={cn(className)}>
49
+ <Animated.View style={animatedStyle}>
50
+ <Svg
51
+ width={size}
52
+ height={size}
53
+ viewBox="0 0 24 24"
54
+ fill="none"
55
+ stroke={stroke}
56
+ strokeWidth={2}
57
+ strokeLinecap="round"
58
+ strokeLinejoin="round"
59
+ >
60
+ <Path d="M21 12a9 9 0 1 1-6.219-8.56" />
61
+ </Svg>
62
+ </Animated.View>
63
+ </View>
61
64
  );
62
65
  }
63
66