@medway-ui/native 1.9.1 → 1.10.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 (298) 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/metro.js +26 -73
  255. package/setup/tailwind.js +16 -8
  256. package/setup/tokens.native.css +51 -20
  257. package/src/components/Alert.tsx +37 -20
  258. package/src/components/AlertDialog.tsx +18 -19
  259. package/src/components/Badge.tsx +16 -15
  260. package/src/components/Button.tsx +64 -31
  261. package/src/components/CardNews.tsx +17 -6
  262. package/src/components/Carousel.tsx +5 -5
  263. package/src/components/Checkbox.tsx +17 -7
  264. package/src/components/Chip.tsx +49 -21
  265. package/src/components/Choice.tsx +34 -14
  266. package/src/components/Dialog.tsx +15 -5
  267. package/src/components/DropdownMenu.tsx +62 -14
  268. package/src/components/EmptyState.tsx +3 -3
  269. package/src/components/FallbackImage.tsx +32 -23
  270. package/src/components/Feed.tsx +2 -1
  271. package/src/components/Feedback.tsx +0 -1
  272. package/src/components/Filter.tsx +2 -3
  273. package/src/components/Input.tsx +83 -64
  274. package/src/components/InvitePopover.tsx +11 -3
  275. package/src/components/LeaderboardCard.tsx +4 -1
  276. package/src/components/Loading.tsx +17 -14
  277. package/src/components/LoadingOverlay.tsx +33 -50
  278. package/src/components/MemberRow.tsx +3 -3
  279. package/src/components/PageHeader.tsx +4 -1
  280. package/src/components/PostForm.tsx +28 -16
  281. package/src/components/PrizeCollection.tsx +3 -3
  282. package/src/components/RadioGroup.tsx +69 -12
  283. package/src/components/SectionHeader.tsx +4 -1
  284. package/src/components/Select.tsx +4 -3
  285. package/src/components/Separator.tsx +2 -2
  286. package/src/components/Sheet.tsx +15 -8
  287. package/src/components/Skeleton.tsx +1 -1
  288. package/src/components/Tabs.tsx +5 -4
  289. package/src/components/Tag.tsx +56 -35
  290. package/src/components/Toast.tsx +10 -6
  291. package/src/editor/NoteEditor.tsx +246 -72
  292. package/src/editor/editorHtml.generated.ts +1 -1
  293. package/src/editor/index.ts +8 -4
  294. package/src/editor/protocol.ts +4 -12
  295. package/src/hooks/useThemeColor.ts +41 -19
  296. package/src/index.ts +1 -1
  297. package/src/lib/backdrop.ts +16 -0
  298. package/src/lib/overlay-host.tsx +5 -5
@@ -54,6 +54,11 @@ interface CardNewsAuthorProps extends Omit<ViewProps, "role"> {
54
54
  src?: string;
55
55
  alt?: string;
56
56
  name?: string;
57
+ jobTitle?: string;
58
+ /**
59
+ * @deprecated Use `jobTitle` para o cargo. Sem `jobTitle`, `role` ainda é
60
+ * exibido como cargo; com `jobTitle`, vira o papel de acessibilidade da View.
61
+ */
57
62
  role?: string;
58
63
  children?: ReactNode;
59
64
  }
@@ -177,8 +182,8 @@ function CardNewsImage({
177
182
  }: CardNewsImageProps) {
178
183
  const { orientation } = useContext(CardNewsContext);
179
184
  const { width } = useWindowDimensions();
180
- const [hasError, setHasError] = useState(false);
181
- const showImage = !!src && !hasError;
185
+ const [failedSrc, setFailedSrc] = useState<string>();
186
+ const showImage = !!src && failedSrc !== src;
182
187
  const showHorizontalImage =
183
188
  orientation !== "horizontal" || width >= HORIZONTAL_IMAGE_MIN_WIDTH;
184
189
 
@@ -206,11 +211,11 @@ function CardNewsImage({
206
211
  {...imageProps}
207
212
  onError={(event) => {
208
213
  imageProps?.onError?.(event);
209
- setHasError(true);
214
+ setFailedSrc(src);
210
215
  }}
211
216
  />
212
217
  ) : (
213
- children ?? <FallbackImage className="absolute inset-0" />
218
+ children ?? <FallbackImage className="absolute inset-0" label={alt} />
214
219
  )}
215
220
  </View>
216
221
  );
@@ -310,12 +315,18 @@ function CardNewsAuthor({
310
315
  src,
311
316
  alt,
312
317
  name,
318
+ jobTitle,
313
319
  role,
314
320
  children,
315
321
  ...props
316
322
  }: CardNewsAuthorProps) {
323
+ const title = jobTitle ?? role;
324
+ const accessibilityRole =
325
+ jobTitle === undefined ? undefined : (role as ViewProps["role"]);
326
+
317
327
  return (
318
328
  <View
329
+ role={accessibilityRole}
319
330
  className={cn("flex-row items-center gap-2", className)}
320
331
  {...props}
321
332
  >
@@ -328,9 +339,9 @@ function CardNewsAuthor({
328
339
  </AvatarFallback>
329
340
  </Avatar>
330
341
  <View className="flex flex-col gap-0.5">
331
- {role ? (
342
+ {title ? (
332
343
  <Text className="text-xs font-medium leading-xs text-secondary-foreground">
333
- {role}
344
+ {title}
334
345
  </Text>
335
346
  ) : null}
336
347
  {name ? (
@@ -48,6 +48,7 @@ function Carousel({
48
48
  }: CarouselProps) {
49
49
  const chevronColor = useThemeColor("primary-foreground");
50
50
  const slidePositionLabel = getSlidePositionLabel(slideIndex, slideCount);
51
+ const slideValue = slidePositionLabel ? { text: slidePositionLabel } : undefined;
51
52
 
52
53
  return (
53
54
  <View
@@ -64,11 +65,9 @@ function Carousel({
64
65
  iconLeft={<ChevronLeftMd width={20} height={20} color={chevronColor} />}
65
66
  onPress={onPrevious}
66
67
  accessibilityLabel="Slide anterior"
68
+ accessibilityValue={slideValue}
67
69
  />
68
- <View
69
- className="h-full min-w-0 flex-1 items-center justify-center px-4"
70
- accessibilityLabel={slidePositionLabel}
71
- >
70
+ <View className="h-full min-w-0 flex-1 items-center justify-center px-4">
72
71
  {children}
73
72
  </View>
74
73
  <Button
@@ -78,6 +77,7 @@ function Carousel({
78
77
  iconLeft={<ChevronRightMd width={20} height={20} color={chevronColor} />}
79
78
  onPress={onNext}
80
79
  accessibilityLabel="Próximo slide"
80
+ accessibilityValue={slideValue}
81
81
  />
82
82
  </View>
83
83
  );
@@ -147,7 +147,7 @@ function CarouselMetricSlide({
147
147
  <View className="items-center justify-center">{icon}</View>
148
148
  ) : null}
149
149
  <View className="w-full items-center gap-3">
150
- <Text className="text-center text-[32px] font-bold leading-10 text-primary-foreground">
150
+ <Text className="text-center text-title-lg font-bold leading-title-lg text-primary-foreground">
151
151
  {value}
152
152
  </Text>
153
153
  <Text className="text-center text-sm font-semibold leading-sm text-primary-foreground">
@@ -1,5 +1,9 @@
1
1
  import { useState, type ReactNode } from "react";
2
- import { type ViewStyle } from "react-native";
2
+ import {
3
+ type PressableStateCallbackType,
4
+ type StyleProp,
5
+ type ViewStyle,
6
+ } from "react-native";
3
7
  import { cssInterop } from "nativewind";
4
8
  import * as CheckboxPrimitive from "@rn-primitives/checkbox";
5
9
  import { cva, type VariantProps } from "class-variance-authority";
@@ -20,10 +24,13 @@ const StyledCheckboxIndicator = cssInterop(CheckboxPrimitive.Indicator, {
20
24
  interface CheckboxProps
21
25
  extends Omit<
22
26
  CheckboxPrimitive.RootProps,
23
- "checked" | "onCheckedChange" | "children"
27
+ "checked" | "onCheckedChange" | "children" | "style"
24
28
  >,
25
29
  Omit<VariantProps<typeof checkboxVariants>, "checked" | "disabled"> {
26
30
  children?: ReactNode;
31
+ style?:
32
+ | StyleProp<ViewStyle>
33
+ | ((state: PressableStateCallbackType) => StyleProp<ViewStyle>);
27
34
  checked?: boolean;
28
35
  defaultChecked?: boolean;
29
36
  onCheckedChange?: (checked: boolean) => void;
@@ -91,7 +98,9 @@ function Checkbox({
91
98
  const [internalChecked, setInternalChecked] = useState(defaultChecked);
92
99
  const isControlled = checked !== undefined;
93
100
  const isChecked = isControlled ? checked : internalChecked;
94
- const iconColor = useThemeColor("primary-foreground-onfill");
101
+ const iconColor = useThemeColor(
102
+ disabled ? "gray-background-fill-muted" : "primary-foreground-onfill"
103
+ );
95
104
 
96
105
  const handleCheckedChange = (nextChecked: boolean) => {
97
106
  if (!isControlled) setInternalChecked(nextChecked);
@@ -115,10 +124,11 @@ function Checkbox({
115
124
  onCheckedChange={handleCheckedChange}
116
125
  disabled={disabled}
117
126
  hitSlop={getMinTouchHitSlop(dimension)}
118
- style={(state) => [
119
- rootStyle,
120
- typeof style === "function" ? style(state) : style,
121
- ]}
127
+ style={
128
+ typeof style === "function"
129
+ ? (state: PressableStateCallbackType) => [rootStyle, style(state)]
130
+ : [rootStyle, style]
131
+ }
122
132
  className={cn(
123
133
  checkboxVariants({
124
134
  size,
@@ -1,10 +1,11 @@
1
1
  import { type ReactNode } from "react";
2
- import { Pressable, View } from "react-native";
2
+ import { Pressable, View, type AccessibilityActionEvent } from "react-native";
3
3
  import { cva, type VariantProps } from "class-variance-authority";
4
4
  import { CloseSm } from "@medway-ui/icons/native";
5
5
 
6
6
  import { Text } from "@/components/Text";
7
7
  import { ThemedIcon } from "@/components/ThemedIcon";
8
+ import { getMinTouchHitSlop } from "@/lib/touch-target";
8
9
  import { cn } from "@/lib/utils";
9
10
 
10
11
  const chipVariants = cva(
@@ -31,12 +32,12 @@ const chipBackgroundVariants = cva("", {
31
32
  "emphasis-soft": "bg-secondary-background-soft",
32
33
  gray: "bg-gray-background-fill",
33
34
  "gray-soft": "bg-gray-background-soft",
34
- success: "bg-green-background-fill",
35
- "success-soft": "bg-green-background-soft",
36
- error: "bg-red-background-fill",
37
- "error-soft": "bg-red-background-soft",
38
- warning: "bg-yellow-background-fill",
39
- "warning-soft": "bg-yellow-background-soft",
35
+ success: "bg-feedback-success-background-fill",
36
+ "success-soft": "bg-feedback-success-background-soft",
37
+ error: "bg-feedback-error-background-fill",
38
+ "error-soft": "bg-feedback-error-background-soft",
39
+ warning: "bg-feedback-warning-background-fill",
40
+ "warning-soft": "bg-feedback-warning-background-soft",
40
41
  go: "bg-go-background-fill",
41
42
  "go-soft": "bg-go-background-soft",
42
43
  cm: "bg-cm-background-fill",
@@ -79,12 +80,12 @@ const chipTextVariants = cva("text-center font-semibold", {
79
80
  "emphasis-soft": "text-secondary-foreground",
80
81
  gray: "text-gray-foreground-onfill",
81
82
  "gray-soft": "text-gray-foreground",
82
- success: "text-green-foreground-onfill",
83
- "success-soft": "text-green-foreground",
84
- error: "text-red-foreground-onfill",
85
- "error-soft": "text-red-foreground",
86
- warning: "text-yellow-foreground-onfill",
87
- "warning-soft": "text-yellow-foreground",
83
+ success: "text-feedback-success-foreground-onfill",
84
+ "success-soft": "text-feedback-success-foreground",
85
+ error: "text-feedback-error-foreground-onfill",
86
+ "error-soft": "text-feedback-error-foreground",
87
+ warning: "text-feedback-warning-foreground-onfill",
88
+ "warning-soft": "text-feedback-warning-foreground",
88
89
  go: "text-go-foreground-onfill",
89
90
  "go-soft": "text-go-foreground",
90
91
  cm: "text-cm-foreground-onfill",
@@ -131,12 +132,12 @@ const CHIP_ICON_COLOR_TOKEN: Record<ChipVariant, string> = {
131
132
  "emphasis-soft": "secondary-foreground",
132
133
  gray: "gray-foreground-onfill",
133
134
  "gray-soft": "gray-foreground",
134
- success: "green-foreground-onfill",
135
- "success-soft": "green-foreground",
136
- error: "red-foreground-onfill",
137
- "error-soft": "red-foreground",
138
- warning: "yellow-foreground-onfill",
139
- "warning-soft": "yellow-foreground",
135
+ success: "feedback-success-foreground-onfill",
136
+ "success-soft": "feedback-success-foreground",
137
+ error: "feedback-error-foreground-onfill",
138
+ "error-soft": "feedback-error-foreground",
139
+ warning: "feedback-warning-foreground-onfill",
140
+ "warning-soft": "feedback-warning-foreground",
140
141
  go: "go-foreground-onfill",
141
142
  "go-soft": "go-foreground",
142
143
  cm: "cm-foreground-onfill",
@@ -151,6 +152,15 @@ const CHIP_ICON_COLOR_TOKEN: Record<ChipVariant, string> = {
151
152
  "ia-soft": "secondary-foreground",
152
153
  };
153
154
 
155
+ // gap-2/2.5/3 rounded down to px at NativeWind's default 14px rem, so the dismiss slop stops before the label.
156
+ const CHIP_LABEL_GAP: Record<ChipSize, number> = {
157
+ sm: 7,
158
+ md: 8,
159
+ lg: 10,
160
+ };
161
+
162
+ const DEFAULT_DISMISS_LABEL = "Remover";
163
+
154
164
  interface ChipProps
155
165
  extends Omit<VariantProps<typeof chipVariants>, "disabled"> {
156
166
  children: ReactNode;
@@ -177,6 +187,11 @@ function Chip({
177
187
  ? CHIP_ICON_COLOR_TOKEN[variant]
178
188
  : "primary-foreground";
179
189
  const iconColor = disabled ? "gray-foreground-muted" : variantIconColor;
190
+ const dismissLabel = dismissAccessibilityLabel ?? DEFAULT_DISMISS_LABEL;
191
+ const dismissHitSlop = {
192
+ ...getMinTouchHitSlop(iconSize),
193
+ left: CHIP_LABEL_GAP[size ?? "lg"],
194
+ };
180
195
 
181
196
  const containerClass = cn(
182
197
  chipVariants({ size, disabled }),
@@ -184,6 +199,12 @@ function Chip({
184
199
  className
185
200
  );
186
201
 
202
+ const handleAccessibilityAction = (event: AccessibilityActionEvent) => {
203
+ if (disabled) return;
204
+ if (event.nativeEvent.actionName === "activate") onPress?.();
205
+ if (event.nativeEvent.actionName === "dismiss") onDismiss?.();
206
+ };
207
+
187
208
  const content = (
188
209
  <>
189
210
  <Text className={cn(chipTextVariants({ size, variant, disabled }))}>
@@ -194,8 +215,8 @@ function Chip({
194
215
  onPress={onDismiss}
195
216
  disabled={disabled}
196
217
  accessibilityRole="button"
197
- accessibilityLabel={dismissAccessibilityLabel ?? "Remover"}
198
- hitSlop={6}
218
+ accessibilityLabel={dismissLabel}
219
+ hitSlop={dismissHitSlop}
199
220
  >
200
221
  <ThemedIcon
201
222
  icon={CloseSm as never}
@@ -213,6 +234,13 @@ function Chip({
213
234
  onPress={onPress}
214
235
  disabled={disabled}
215
236
  accessibilityRole="button"
237
+ accessibilityState={{ disabled }}
238
+ accessibilityActions={
239
+ onDismiss
240
+ ? [{ name: "activate" }, { name: "dismiss", label: dismissLabel }]
241
+ : undefined
242
+ }
243
+ onAccessibilityAction={onDismiss ? handleAccessibilityAction : undefined}
216
244
  className={containerClass}
217
245
  >
218
246
  {content}
@@ -9,19 +9,24 @@ import { cn } from "@/lib/utils";
9
9
 
10
10
  type ChoiceSize = "sm" | "md" | "lg";
11
11
 
12
- type ChoiceTheme =
12
+ type ChoiceResolvedTheme =
13
13
  | "gray"
14
14
  | "primary"
15
15
  | "secondary"
16
- | "green"
17
- | "red"
18
- | "yellow"
16
+ | "success"
17
+ | "error"
18
+ | "warning"
19
19
  | "cm"
20
20
  | "go"
21
21
  | "cir"
22
22
  | "ped"
23
23
  | "prev";
24
24
 
25
+ /** @deprecated Use `success`, `error` and `warning`, the names used across the package. Removed in the next major. */
26
+ type ChoiceLegacyTheme = "green" | "red" | "yellow";
27
+
28
+ type ChoiceTheme = ChoiceResolvedTheme | ChoiceLegacyTheme;
29
+
25
30
  interface ChoiceProps {
26
31
  label: string;
27
32
  selected?: boolean;
@@ -58,13 +63,19 @@ const SIZE_MAP: Record<
58
63
  },
59
64
  };
60
65
 
61
- const THEME_SELECTED_OUTER_BG: Record<ChoiceTheme, string> = {
66
+ const LEGACY_THEME: Record<ChoiceLegacyTheme, ChoiceResolvedTheme> = {
67
+ green: "success",
68
+ red: "error",
69
+ yellow: "warning",
70
+ };
71
+
72
+ const THEME_SELECTED_OUTER_BG: Record<ChoiceResolvedTheme, string> = {
62
73
  gray: "bg-gray-background-soft",
63
74
  primary: "bg-primary-background-fill",
64
75
  secondary: "bg-secondary-background-fill",
65
- green: "bg-green-background-fill",
66
- red: "bg-red-background-fill",
67
- yellow: "bg-yellow-background-fill",
76
+ success: "bg-feedback-success-background-fill",
77
+ error: "bg-feedback-error-background-fill",
78
+ warning: "bg-feedback-warning-background-fill",
68
79
  cm: "bg-cm-background-fill",
69
80
  go: "bg-go-background-fill",
70
81
  cir: "bg-cir-background-fill-emphasis",
@@ -72,13 +83,13 @@ const THEME_SELECTED_OUTER_BG: Record<ChoiceTheme, string> = {
72
83
  prev: "bg-prev-background-fill",
73
84
  };
74
85
 
75
- const THEME_CHECK_COLOR_TOKEN: Record<ChoiceTheme, string> = {
86
+ const THEME_CHECK_COLOR_TOKEN: Record<ChoiceResolvedTheme, string> = {
76
87
  gray: "secondary-foreground",
77
88
  primary: "primary-foreground",
78
89
  secondary: "secondary-foreground",
79
- green: "green-foreground",
80
- red: "red-foreground",
81
- yellow: "yellow-foreground",
90
+ success: "feedback-success-foreground",
91
+ error: "feedback-error-foreground",
92
+ warning: "feedback-warning-foreground",
82
93
  cm: "cm-foreground",
83
94
  go: "go-foreground",
84
95
  cir: "cir-foreground",
@@ -86,6 +97,14 @@ const THEME_CHECK_COLOR_TOKEN: Record<ChoiceTheme, string> = {
86
97
  prev: "prev-foreground",
87
98
  };
88
99
 
100
+ function isLegacyTheme(theme: ChoiceTheme): theme is ChoiceLegacyTheme {
101
+ return theme in LEGACY_THEME;
102
+ }
103
+
104
+ function resolveTheme(theme: ChoiceTheme): ChoiceResolvedTheme {
105
+ return isLegacyTheme(theme) ? LEGACY_THEME[theme] : theme;
106
+ }
107
+
89
108
  function Choice({
90
109
  label,
91
110
  selected = false,
@@ -97,12 +116,13 @@ function Choice({
97
116
  className,
98
117
  }: ChoiceProps) {
99
118
  const sz = SIZE_MAP[size] ?? SIZE_MAP.md;
119
+ const resolvedTheme = resolveTheme(theme);
100
120
  const isRaised = selected && !disabled;
101
121
 
102
122
  const outerBg = disabled
103
123
  ? "bg-gray-background-soft-muted"
104
124
  : selected
105
- ? THEME_SELECTED_OUTER_BG[theme]
125
+ ? THEME_SELECTED_OUTER_BG[resolvedTheme]
106
126
  : "bg-gray-background-soft active:bg-gray-background-soft-emphasis";
107
127
 
108
128
  return (
@@ -145,7 +165,7 @@ function Choice({
145
165
  colorToken={
146
166
  disabled
147
167
  ? "gray-foreground-muted"
148
- : THEME_CHECK_COLOR_TOKEN[theme]
168
+ : THEME_CHECK_COLOR_TOKEN[resolvedTheme]
149
169
  }
150
170
  />
151
171
  </View>
@@ -3,12 +3,14 @@ import { View, ScrollView, type ViewProps } from "react-native";
3
3
  import * as DialogPrimitive from "@rn-primitives/dialog";
4
4
  import { CloseMd } from "@medway-ui/icons/native";
5
5
  import { useThemeColor } from "@/hooks/useThemeColor";
6
+ import { useBackdropStyle } from "@/lib/backdrop";
6
7
  import { cn } from "@/lib/utils";
7
8
  import { Separator } from "@/components/Separator";
8
9
 
9
10
  interface DialogContainerProps extends DialogPrimitive.ContentProps {
10
11
  showCloseButton?: boolean;
11
12
  overlayClassName?: string;
13
+ closeOnOverlayPress?: boolean;
12
14
  }
13
15
 
14
16
  interface DialogHeaderProps extends ViewProps {
@@ -41,9 +43,12 @@ function DialogClose({ ...props }: DialogPrimitive.CloseProps) {
41
43
  }
42
44
 
43
45
  function DialogOverlay({ className, ...props }: DialogPrimitive.OverlayProps) {
46
+ const backdropStyle = useBackdropStyle(className);
47
+
44
48
  return (
45
49
  <DialogPrimitive.Overlay
46
- className={cn("absolute inset-0 bg-black-48", className)}
50
+ className={cn("absolute inset-0", className)}
51
+ style={backdropStyle}
47
52
  {...props}
48
53
  />
49
54
  );
@@ -54,22 +59,27 @@ function DialogContainer({
54
59
  overlayClassName,
55
60
  children,
56
61
  showCloseButton = true,
62
+ closeOnOverlayPress = true,
57
63
  ...props
58
64
  }: DialogContainerProps) {
59
65
  const closeIconColor = useThemeColor("gray-foreground");
66
+ const backdropStyle = useBackdropStyle(overlayClassName);
60
67
 
61
68
  return (
62
69
  <DialogPrimitive.Portal>
63
70
  <DialogPrimitive.Overlay
64
- className={cn("absolute inset-0 bg-black-48", overlayClassName)}
71
+ className={cn("absolute inset-0", overlayClassName)}
72
+ style={backdropStyle}
73
+ closeOnPress={closeOnOverlayPress}
65
74
  />
66
75
  <DialogPrimitive.Content
67
76
  className="absolute inset-0 items-center justify-center px-4"
77
+ pointerEvents="box-none"
68
78
  {...props}
69
79
  >
70
80
  <View
71
81
  className={cn(
72
- "relative flex-col max-h-[90%] w-full rounded-xl bg-background shadow-lg overflow-hidden",
82
+ "relative flex-col max-h-[90%] w-full rounded-lg bg-surface-elevated shadow-lg overflow-hidden",
73
83
  className
74
84
  )}
75
85
  >
@@ -101,7 +111,7 @@ function DialogContent({
101
111
  }) {
102
112
  return (
103
113
  <ScrollView
104
- className={cn("flex-1", containerClassName)}
114
+ className={cn(containerClassName)}
105
115
  contentContainerStyle={{ flexGrow: 1 }}
106
116
  >
107
117
  <View className={cn("px-8 py-6", className)}>
@@ -175,7 +185,7 @@ function DialogTitle({ className, iconLeft, iconRight, children, ...props }: Dia
175
185
  function DialogDescription({ className, ...props }: DialogPrimitive.DescriptionProps) {
176
186
  return (
177
187
  <DialogPrimitive.Description
178
- className={cn("text-md leading-md text-content-paragraph", className)}
188
+ className={cn("text-md font-regular leading-md text-content-paragraph", className)}
179
189
  {...props}
180
190
  />
181
191
  );
@@ -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({