@medway-ui/native 1.1.2 → 1.2.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 (215) 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/hooks/useThemeColor.js +66 -29
  56. package/lib/commonjs/hooks/useThemeColor.js.map +1 -1
  57. package/lib/commonjs/index.js +319 -1
  58. package/lib/commonjs/index.js.map +1 -1
  59. package/lib/commonjs/lib/overlay-host.js +101 -0
  60. package/lib/commonjs/lib/overlay-host.js.map +1 -0
  61. package/lib/commonjs/lib/typography.js +26 -0
  62. package/lib/commonjs/lib/typography.js.map +1 -0
  63. package/lib/commonjs/nativewind-env.d.js +0 -1
  64. package/lib/commonjs/nativewind-env.d.js.map +1 -1
  65. package/lib/module/components/Alert.js +3 -2
  66. package/lib/module/components/Alert.js.map +1 -1
  67. package/lib/module/components/Avatar.js +2 -1
  68. package/lib/module/components/Avatar.js.map +1 -1
  69. package/lib/module/components/Badge.js +2 -1
  70. package/lib/module/components/Badge.js.map +1 -1
  71. package/lib/module/components/Button.js +2 -1
  72. package/lib/module/components/Button.js.map +1 -1
  73. package/lib/module/components/CardNews.js +2 -1
  74. package/lib/module/components/CardNews.js.map +1 -1
  75. package/lib/module/components/Carousel.js +117 -0
  76. package/lib/module/components/Carousel.js.map +1 -0
  77. package/lib/module/components/Checkbox.js +12 -2
  78. package/lib/module/components/Checkbox.js.map +1 -1
  79. package/lib/module/components/DropdownMenu.js +216 -0
  80. package/lib/module/components/DropdownMenu.js.map +1 -0
  81. package/lib/module/components/EmptyState.js +19 -20
  82. package/lib/module/components/EmptyState.js.map +1 -1
  83. package/lib/module/components/Feed.js +77 -0
  84. package/lib/module/components/Feed.js.map +1 -0
  85. package/lib/module/components/FeedNotice.js +73 -0
  86. package/lib/module/components/FeedNotice.js.map +1 -0
  87. package/lib/module/components/Input.js +21 -9
  88. package/lib/module/components/Input.js.map +1 -1
  89. package/lib/module/components/InvitePopover.js +183 -0
  90. package/lib/module/components/InvitePopover.js.map +1 -0
  91. package/lib/module/components/Label.js +2 -1
  92. package/lib/module/components/Label.js.map +1 -1
  93. package/lib/module/components/LeaderboardCard.js +53 -0
  94. package/lib/module/components/LeaderboardCard.js.map +1 -0
  95. package/lib/module/components/MemberRow.js +127 -0
  96. package/lib/module/components/MemberRow.js.map +1 -0
  97. package/lib/module/components/PageHeader.js +2 -1
  98. package/lib/module/components/PageHeader.js.map +1 -1
  99. package/lib/module/components/PostForm.js +174 -0
  100. package/lib/module/components/PostForm.js.map +1 -0
  101. package/lib/module/components/PrizeCollection.js +127 -0
  102. package/lib/module/components/PrizeCollection.js.map +1 -0
  103. package/lib/module/components/RadioGroup.js +21 -1
  104. package/lib/module/components/RadioGroup.js.map +1 -1
  105. package/lib/module/components/SectionHeader.js +33 -0
  106. package/lib/module/components/SectionHeader.js.map +1 -0
  107. package/lib/module/components/Switch.js +34 -20
  108. package/lib/module/components/Switch.js.map +1 -1
  109. package/lib/module/components/Tabs.js +148 -0
  110. package/lib/module/components/Tabs.js.map +1 -0
  111. package/lib/module/components/Tag.js +2 -1
  112. package/lib/module/components/Tag.js.map +1 -1
  113. package/lib/module/components/Text.js +58 -0
  114. package/lib/module/components/Text.js.map +1 -0
  115. package/lib/module/components/Toast.js +2 -1
  116. package/lib/module/components/Toast.js.map +1 -1
  117. package/lib/module/components/Tooltip.js +1 -1
  118. package/lib/module/components/Tooltip.js.map +1 -1
  119. package/lib/module/hooks/useThemeColor.js +66 -31
  120. package/lib/module/hooks/useThemeColor.js.map +1 -1
  121. package/lib/module/index.js +27 -3
  122. package/lib/module/index.js.map +1 -1
  123. package/lib/module/lib/overlay-host.js +96 -0
  124. package/lib/module/lib/overlay-host.js.map +1 -0
  125. package/lib/module/lib/typography.js +22 -0
  126. package/lib/module/lib/typography.js.map +1 -0
  127. package/lib/module/nativewind-env.d.js +0 -1
  128. package/lib/module/nativewind-env.d.js.map +1 -1
  129. package/lib/typescript/components/Alert.d.ts.map +1 -1
  130. package/lib/typescript/components/Avatar.d.ts.map +1 -1
  131. package/lib/typescript/components/Badge.d.ts.map +1 -1
  132. package/lib/typescript/components/Button.d.ts.map +1 -1
  133. package/lib/typescript/components/CardNews.d.ts.map +1 -1
  134. package/lib/typescript/components/Carousel.d.ts +29 -0
  135. package/lib/typescript/components/Carousel.d.ts.map +1 -0
  136. package/lib/typescript/components/Checkbox.d.ts.map +1 -1
  137. package/lib/typescript/components/DropdownMenu.d.ts +39 -0
  138. package/lib/typescript/components/DropdownMenu.d.ts.map +1 -0
  139. package/lib/typescript/components/EmptyState.d.ts +1 -1
  140. package/lib/typescript/components/EmptyState.d.ts.map +1 -1
  141. package/lib/typescript/components/Feed.d.ts +22 -0
  142. package/lib/typescript/components/Feed.d.ts.map +1 -0
  143. package/lib/typescript/components/FeedNotice.d.ts +17 -0
  144. package/lib/typescript/components/FeedNotice.d.ts.map +1 -0
  145. package/lib/typescript/components/Input.d.ts +3 -2
  146. package/lib/typescript/components/Input.d.ts.map +1 -1
  147. package/lib/typescript/components/InvitePopover.d.ts +46 -0
  148. package/lib/typescript/components/InvitePopover.d.ts.map +1 -0
  149. package/lib/typescript/components/Label.d.ts.map +1 -1
  150. package/lib/typescript/components/LeaderboardCard.d.ts +16 -0
  151. package/lib/typescript/components/LeaderboardCard.d.ts.map +1 -0
  152. package/lib/typescript/components/MemberRow.d.ts +40 -0
  153. package/lib/typescript/components/MemberRow.d.ts.map +1 -0
  154. package/lib/typescript/components/PageHeader.d.ts.map +1 -1
  155. package/lib/typescript/components/PostForm.d.ts +50 -0
  156. package/lib/typescript/components/PostForm.d.ts.map +1 -0
  157. package/lib/typescript/components/PrizeCollection.d.ts +35 -0
  158. package/lib/typescript/components/PrizeCollection.d.ts.map +1 -0
  159. package/lib/typescript/components/RadioGroup.d.ts +10 -2
  160. package/lib/typescript/components/RadioGroup.d.ts.map +1 -1
  161. package/lib/typescript/components/SectionHeader.d.ts +12 -0
  162. package/lib/typescript/components/SectionHeader.d.ts.map +1 -0
  163. package/lib/typescript/components/Switch.d.ts.map +1 -1
  164. package/lib/typescript/components/Tabs.d.ts +32 -0
  165. package/lib/typescript/components/Tabs.d.ts.map +1 -0
  166. package/lib/typescript/components/Tag.d.ts.map +1 -1
  167. package/lib/typescript/components/Text.d.ts +23 -0
  168. package/lib/typescript/components/Text.d.ts.map +1 -0
  169. package/lib/typescript/components/Toast.d.ts +1 -1
  170. package/lib/typescript/components/Toast.d.ts.map +1 -1
  171. package/lib/typescript/components/Tooltip.d.ts.map +1 -1
  172. package/lib/typescript/hooks/useThemeColor.d.ts +4 -2
  173. package/lib/typescript/hooks/useThemeColor.d.ts.map +1 -1
  174. package/lib/typescript/index.d.ts +28 -4
  175. package/lib/typescript/index.d.ts.map +1 -1
  176. package/lib/typescript/lib/overlay-host.d.ts +23 -0
  177. package/lib/typescript/lib/overlay-host.d.ts.map +1 -0
  178. package/lib/typescript/lib/typography.d.ts +12 -0
  179. package/lib/typescript/lib/typography.d.ts.map +1 -0
  180. package/package.json +13 -3
  181. package/setup/MedwayUiProvider.tsx +15 -3
  182. package/setup/tailwind.js +52 -4
  183. package/setup/tokens.native.css +8 -3
  184. package/src/components/Alert.tsx +4 -2
  185. package/src/components/Avatar.tsx +3 -1
  186. package/src/components/Badge.tsx +3 -1
  187. package/src/components/Button.tsx +3 -7
  188. package/src/components/CardNews.tsx +2 -1
  189. package/src/components/Carousel.tsx +152 -0
  190. package/src/components/Checkbox.tsx +14 -4
  191. package/src/components/DropdownMenu.tsx +312 -0
  192. package/src/components/EmptyState.tsx +21 -23
  193. package/src/components/Feed.tsx +96 -0
  194. package/src/components/FeedNotice.tsx +112 -0
  195. package/src/components/Input.tsx +11 -6
  196. package/src/components/InvitePopover.tsx +240 -0
  197. package/src/components/Label.tsx +3 -1
  198. package/src/components/LeaderboardCard.tsx +70 -0
  199. package/src/components/MemberRow.tsx +157 -0
  200. package/src/components/PageHeader.tsx +3 -1
  201. package/src/components/PostForm.tsx +250 -0
  202. package/src/components/PrizeCollection.tsx +190 -0
  203. package/src/components/RadioGroup.tsx +32 -3
  204. package/src/components/SectionHeader.tsx +45 -0
  205. package/src/components/Switch.tsx +65 -59
  206. package/src/components/Tabs.tsx +182 -0
  207. package/src/components/Tag.tsx +3 -1
  208. package/src/components/Text.tsx +66 -0
  209. package/src/components/Toast.tsx +3 -1
  210. package/src/components/Tooltip.tsx +2 -1
  211. package/src/hooks/useThemeColor.ts +77 -32
  212. package/src/index.ts +147 -6
  213. package/src/lib/overlay-host.tsx +102 -0
  214. package/src/lib/typography.ts +21 -0
  215. package/src/nativewind-env.d.ts +2 -0
@@ -9,6 +9,7 @@ import Animated, {
9
9
  } from "react-native-reanimated";
10
10
  import { cva, type VariantProps } from "class-variance-authority";
11
11
  import { cn } from "@/lib/utils";
12
+ import { useThemeColor } from "@/hooks/useThemeColor";
12
13
 
13
14
  interface SwitchProps
14
15
  extends Omit<
@@ -35,31 +36,28 @@ const THUMB_TRANSLATE: Record<"sm" | "md" | "lg", number> = {
35
36
  lg: 19,
36
37
  };
37
38
 
38
- const SWITCH_TRACK: Record<"sm" | "md" | "lg", { width: number; height: number }> = {
39
+ const SWITCH_DIMENSIONS: Record<"sm" | "md" | "lg", { width: number; height: number }> = {
39
40
  sm: { width: 24, height: 16 },
40
41
  md: { width: 32, height: 20 },
41
42
  lg: { width: 40, height: 24 },
42
43
  };
43
44
 
44
- const switchVariants = cva(
45
- "rounded-full border-2 items-center shrink-0",
46
- {
47
- variants: {
48
- variant: {
49
- regular: "",
50
- },
51
- size: {
52
- sm: "h-4 w-6",
53
- md: "h-5 w-8",
54
- lg: "h-6 w-10",
55
- },
45
+ const switchVariants = cva("rounded-full border-2 items-center shrink-0", {
46
+ variants: {
47
+ variant: {
48
+ regular: "",
56
49
  },
57
- defaultVariants: {
58
- variant: "regular",
59
- size: "md",
50
+ size: {
51
+ sm: "h-4 w-6",
52
+ md: "h-5 w-8",
53
+ lg: "h-6 w-10",
60
54
  },
61
- }
62
- );
55
+ },
56
+ defaultVariants: {
57
+ variant: "regular",
58
+ size: "md",
59
+ },
60
+ });
63
61
 
64
62
  function Switch({
65
63
  className,
@@ -75,25 +73,33 @@ function Switch({
75
73
  ...props
76
74
  }: SwitchProps) {
77
75
  const resolvedSize = size ?? "md";
78
- const trackSize = SWITCH_TRACK[resolvedSize];
76
+ const dimensions = SWITCH_DIMENSIONS[resolvedSize];
79
77
  const [internalChecked, setInternalChecked] = useState(defaultChecked ?? false);
80
78
  const isControlled = checked !== undefined;
81
79
  const isChecked = isControlled ? (checked ?? false) : internalChecked;
82
80
 
81
+ const trackOn = useThemeColor("primary-background-fill");
82
+ const trackOff = useThemeColor("surface-card");
83
+ const trackDisabled = useThemeColor("gray-background-soft-muted");
84
+ const trackBorder = useThemeColor("gray-stroke-muted");
85
+ const thumbOn = useThemeColor("primary-foreground-onfill");
86
+ const thumbOff = useThemeColor("gray-foreground-muted");
87
+ const thumbDisabled = useThemeColor("gray-background-fill-muted");
88
+
83
89
  const translateX = useSharedValue(isChecked ? THUMB_TRANSLATE[resolvedSize] : 2);
84
90
 
85
91
  useEffect(() => {
86
- translateX.value = withTiming(
87
- isChecked ? THUMB_TRANSLATE[resolvedSize] : 2,
88
- { duration: 180, easing: Easing.out(Easing.ease) }
89
- );
92
+ translateX.value = withTiming(isChecked ? THUMB_TRANSLATE[resolvedSize] : 2, {
93
+ duration: 180,
94
+ easing: Easing.out(Easing.ease),
95
+ });
90
96
  }, [isChecked, resolvedSize, translateX]);
91
97
 
92
98
  const thumbStyle = useAnimatedStyle(() => ({
93
99
  transform: [{ translateX: translateX.value }],
94
100
  }));
95
101
 
96
- const hasIcons = checkedIcon || uncheckedIcon;
102
+ const hasIcons = Boolean(checkedIcon || uncheckedIcon);
97
103
  const thumbSize = THUMB_SIZES[resolvedSize];
98
104
 
99
105
  const handleCheckedChange = (nextChecked: boolean) => {
@@ -101,14 +107,33 @@ function Switch({
101
107
  onCheckedChange?.(nextChecked);
102
108
  };
103
109
 
104
- const trackStyle: ViewStyle = {
105
- width: trackSize.width,
106
- height: trackSize.height,
107
- minWidth: trackSize.width,
108
- minHeight: trackSize.height,
109
- borderRadius: trackSize.height / 2,
110
+ const trackBackgroundColor = disabled
111
+ ? trackDisabled
112
+ : isChecked
113
+ ? trackOn
114
+ : trackOff;
115
+
116
+ const trackBorderColor = disabled || !isChecked ? trackBorder : "transparent";
117
+
118
+ const thumbBackgroundColor = disabled
119
+ ? thumbDisabled
120
+ : hasIcons
121
+ ? "transparent"
122
+ : isChecked
123
+ ? thumbOn
124
+ : thumbOff;
125
+
126
+ const rootStyle: ViewStyle = {
127
+ width: dimensions.width,
128
+ height: dimensions.height,
129
+ minWidth: dimensions.width,
130
+ minHeight: dimensions.height,
131
+ borderRadius: dimensions.height / 2,
110
132
  borderWidth: 2,
133
+ alignItems: "flex-start",
111
134
  justifyContent: "center",
135
+ backgroundColor: trackBackgroundColor,
136
+ borderColor: trackBorderColor,
112
137
  };
113
138
 
114
139
  return (
@@ -117,18 +142,10 @@ function Switch({
117
142
  onCheckedChange={handleCheckedChange}
118
143
  disabled={disabled}
119
144
  style={(state) => [
120
- trackStyle,
145
+ rootStyle,
121
146
  typeof style === "function" ? style(state) : style,
122
147
  ]}
123
- className={cn(
124
- switchVariants({ size, variant }),
125
- disabled
126
- ? "bg-gray-background-soft-muted border-gray-stroke-muted"
127
- : isChecked
128
- ? "bg-primary-background-fill border-primary-background-fill"
129
- : "bg-surface-card border-gray-stroke-muted",
130
- className
131
- )}
148
+ className={cn(switchVariants({ size, variant }), className)}
132
149
  {...props}
133
150
  >
134
151
  <SwitchPrimitive.Thumb asChild>
@@ -138,28 +155,17 @@ function Switch({
138
155
  {
139
156
  width: thumbSize,
140
157
  height: thumbSize,
158
+ borderRadius: thumbSize / 2,
159
+ backgroundColor: thumbBackgroundColor,
141
160
  },
142
161
  ]}
143
162
  >
144
- <View
145
- className={cn(
146
- "flex-1 items-center justify-center rounded-full",
147
- disabled
148
- ? "bg-gray-background-fill-muted"
149
- : hasIcons
150
- ? "bg-transparent"
151
- : isChecked
152
- ? "bg-primary-foreground-onfill"
153
- : "bg-gray-foreground-muted"
154
- )}
155
- >
156
- {hasIcons ? (
157
- <>
158
- {isChecked && checkedIcon}
159
- {!isChecked && uncheckedIcon}
160
- </>
161
- ) : null}
162
- </View>
163
+ {hasIcons ? (
164
+ <View className="flex-1 items-center justify-center">
165
+ {isChecked && checkedIcon}
166
+ {!isChecked && uncheckedIcon}
167
+ </View>
168
+ ) : null}
163
169
  </Animated.View>
164
170
  </SwitchPrimitive.Thumb>
165
171
  </SwitchPrimitive.Root>
@@ -0,0 +1,182 @@
1
+ import { createContext, useContext, type ReactNode } from "react";
2
+ import { Pressable, View } from "react-native";
3
+
4
+ import { Text } from "@/components/Text";
5
+ import * as TabsPrimitive from "@rn-primitives/tabs";
6
+ import { cva, type VariantProps } from "class-variance-authority";
7
+
8
+ import { cn } from "@/lib/utils";
9
+
10
+ type TabsVariant = "pill" | "filter";
11
+
12
+ const TabsVariantContext = createContext<TabsVariant>("pill");
13
+
14
+ function useTabsVariant() {
15
+ return useContext(TabsVariantContext);
16
+ }
17
+
18
+ const tabsTriggerVariants = cva(
19
+ "min-h-12 flex-row items-center justify-center rounded-pill px-4",
20
+ {
21
+ variants: {
22
+ active: {
23
+ true: "bg-primary-background-fill",
24
+ false: "bg-transparent",
25
+ },
26
+ },
27
+ defaultVariants: {
28
+ active: false,
29
+ },
30
+ }
31
+ );
32
+
33
+ const tabsTriggerTextVariants = cva("text-sm font-semibold", {
34
+ variants: {
35
+ active: {
36
+ true: "text-primary-foreground-onfill",
37
+ false: "text-primary-foreground",
38
+ },
39
+ },
40
+ defaultVariants: {
41
+ active: false,
42
+ },
43
+ });
44
+
45
+ interface TabsProps extends TabsPrimitive.RootProps {
46
+ className?: string;
47
+ children?: ReactNode;
48
+ variant?: TabsVariant;
49
+ }
50
+
51
+ interface TabsListProps extends TabsPrimitive.ListProps {
52
+ className?: string;
53
+ children?: ReactNode;
54
+ }
55
+
56
+ interface TabsTriggerProps
57
+ extends TabsPrimitive.TriggerProps,
58
+ VariantProps<typeof tabsTriggerVariants> {
59
+ className?: string;
60
+ children?: ReactNode;
61
+ icon?: ReactNode;
62
+ }
63
+
64
+ interface TabsContentProps extends TabsPrimitive.ContentProps {
65
+ className?: string;
66
+ children?: ReactNode;
67
+ }
68
+
69
+ function Tabs({ className, children, variant = "pill", ...props }: TabsProps) {
70
+ return (
71
+ <TabsVariantContext.Provider value={variant}>
72
+ <TabsPrimitive.Root asChild {...props}>
73
+ <View
74
+ className={cn(variant === "filter" ? "gap-8" : "gap-2", className)}
75
+ >
76
+ {children}
77
+ </View>
78
+ </TabsPrimitive.Root>
79
+ </TabsVariantContext.Provider>
80
+ );
81
+ }
82
+
83
+ function TabsList({ className, children, ...props }: TabsListProps) {
84
+ const variant = useTabsVariant();
85
+
86
+ if (variant === "filter") {
87
+ return (
88
+ <View className="rounded-lg border border-gray-stroke-muted bg-surface-section px-6 py-1">
89
+ <TabsPrimitive.List asChild {...props}>
90
+ <View className={cn("w-full flex-row items-center", className)}>
91
+ {children}
92
+ </View>
93
+ </TabsPrimitive.List>
94
+ </View>
95
+ );
96
+ }
97
+
98
+ return (
99
+ <TabsPrimitive.List asChild {...props}>
100
+ <View className={cn("flex-row items-center gap-2", className)}>
101
+ {children}
102
+ </View>
103
+ </TabsPrimitive.List>
104
+ );
105
+ }
106
+
107
+ function TabsTrigger({
108
+ className,
109
+ children,
110
+ icon,
111
+ value,
112
+ ...props
113
+ }: TabsTriggerProps) {
114
+ const variant = useTabsVariant();
115
+ const { value: activeValue } = TabsPrimitive.useRootContext();
116
+ const isActive = activeValue === value;
117
+
118
+ if (variant === "filter") {
119
+ const hasLabel = typeof children === "string" && children.length > 0;
120
+ const showLabel = isActive && hasLabel;
121
+
122
+ return (
123
+ <TabsPrimitive.Trigger asChild value={value} {...props}>
124
+ <Pressable
125
+ className={cn(
126
+ "relative min-h-12 flex-1 flex-row items-center justify-center gap-3 py-3",
127
+ className
128
+ )}
129
+ >
130
+ {icon ? <View className="size-6 shrink-0">{icon}</View> : null}
131
+ {showLabel ? (
132
+ <Text className="text-md font-semibold text-primary-foreground">
133
+ {children}
134
+ </Text>
135
+ ) : !hasLabel ? (
136
+ children
137
+ ) : null}
138
+ {isActive ? (
139
+ <View className="absolute -bottom-1 left-0 right-0 h-0.5 rounded-t-pill bg-secondary-stroke-emphasis" />
140
+ ) : null}
141
+ </Pressable>
142
+ </TabsPrimitive.Trigger>
143
+ );
144
+ }
145
+
146
+ return (
147
+ <TabsPrimitive.Trigger asChild value={value} {...props}>
148
+ <Pressable
149
+ className={cn(tabsTriggerVariants({ active: isActive }), className)}
150
+ >
151
+ {typeof children === "string" ? (
152
+ <Text className={cn(tabsTriggerTextVariants({ active: isActive }))}>
153
+ {children}
154
+ </Text>
155
+ ) : (
156
+ children
157
+ )}
158
+ </Pressable>
159
+ </TabsPrimitive.Trigger>
160
+ );
161
+ }
162
+
163
+ function TabsContent({ className, children, ...props }: TabsContentProps) {
164
+ const variant = useTabsVariant();
165
+
166
+ return (
167
+ <TabsPrimitive.Content asChild {...props}>
168
+ <View className={cn(variant === "filter" ? "gap-8" : "pt-2", className)}>
169
+ {children}
170
+ </View>
171
+ </TabsPrimitive.Content>
172
+ );
173
+ }
174
+
175
+ export { Tabs, TabsContent, TabsList, TabsTrigger, tabsTriggerVariants };
176
+ export type {
177
+ TabsContentProps,
178
+ TabsListProps,
179
+ TabsProps,
180
+ TabsTriggerProps,
181
+ TabsVariant,
182
+ };
@@ -1,5 +1,7 @@
1
1
  import { type ReactNode, type ReactElement } 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
 
@@ -0,0 +1,66 @@
1
+ import { forwardRef, type ComponentProps } from 'react';
2
+ import { Text as RNText, type TextProps as RNTextProps } from 'react-native';
3
+
4
+ import { cn } from '@/lib/utils';
5
+
6
+ /**
7
+ * Maps Medway UI token weight class names → expo-font family keys.
8
+ * RN cannot pick a weight inside a single "Montserrat" family the way the web does.
9
+ */
10
+ const FONT_BY_WEIGHT_CLASS: Record<string, string> = {
11
+ 'font-thin': 'MontserratLight',
12
+ 'font-extralight': 'MontserratLight',
13
+ 'font-light': 'MontserratLight',
14
+ 'font-normal': 'Montserrat',
15
+ 'font-regular': 'Montserrat',
16
+ 'font-medium': 'MontserratMedium',
17
+ 'font-semibold': 'MontserratSemiBold',
18
+ 'font-bold': 'MontserratBold',
19
+ 'font-extrabold': 'MontserratExtraBold',
20
+ 'font-black': 'MontserratBlack',
21
+ };
22
+
23
+ function resolveMontserratFamily(className?: string): string {
24
+ if (!className) {
25
+ return 'Montserrat';
26
+ }
27
+
28
+ const tokens = className.split(/\s+/);
29
+ for (let i = tokens.length - 1; i >= 0; i -= 1) {
30
+ const family = FONT_BY_WEIGHT_CLASS[tokens[i]];
31
+ if (family) {
32
+ return family;
33
+ }
34
+ }
35
+
36
+ return 'Montserrat';
37
+ }
38
+
39
+ type TextProps = RNTextProps & {
40
+ className?: string;
41
+ };
42
+
43
+ /**
44
+ * NativeWind Text that always applies the Montserrat file for the token weight
45
+ * class (font-medium → MontserratMedium, etc.). Last style entry wins over
46
+ * className-derived styles so RN actually loads the correct TTF.
47
+ */
48
+ const Text = forwardRef<RNText, TextProps>(function Text(
49
+ { className, style, ...props },
50
+ ref,
51
+ ) {
52
+ const fontFamily = resolveMontserratFamily(className);
53
+
54
+ return (
55
+ <RNText
56
+ ref={ref}
57
+ className={cn(className)}
58
+ style={[style, { fontFamily }]}
59
+ {...props}
60
+ />
61
+ );
62
+ });
63
+
64
+ export { Text, resolveMontserratFamily, FONT_BY_WEIGHT_CLASS };
65
+ export type { TextProps };
66
+ export type TextComponentProps = ComponentProps<typeof Text>;
@@ -1,5 +1,7 @@
1
1
  import { useState, useEffect, type ReactElement, type ReactNode } from "react";
2
- import { View, Text, Pressable, type ViewProps } from "react-native";
2
+ import { View, Pressable, type ViewProps } from "react-native";
3
+
4
+ import { Text } from "@/components/Text";
3
5
  import { cva } from "class-variance-authority";
4
6
  import Animated, {
5
7
  useSharedValue,
@@ -1,5 +1,6 @@
1
1
  import { type ReactNode } from "react";
2
- import { Text } from "react-native";
2
+
3
+ import { Text } from "@/components/Text";
3
4
  import * as TooltipPrimitive from "@rn-primitives/tooltip";
4
5
  import { cn } from "@/lib/utils";
5
6
 
@@ -1,46 +1,77 @@
1
- import { useColorScheme, useUnstableNativeVariable } from "nativewind";
1
+ import { useColorScheme, useUnstableNativeVariable } from 'nativewind';
2
2
 
3
3
  /** Token sem prefixo `--color-` (ex.: `gray-foreground`, `primary-background-fill`). */
4
4
  type ThemeColorToken = string;
5
5
 
6
6
  /** Fallback quando NativeWind não resolve cadeias `var()` — light mode. */
7
7
  const LIGHT_COLOR_ALIAS_MAP: Record<string, string> = {
8
- "content-title": "primary",
9
- "content-paragraph": "gray-600",
10
- "gray-foreground": "gray-600",
11
- "primary-foreground": "primary",
12
- "secondary-foreground": "secondary-darken-3",
13
- "surface-card": "white",
14
- "gray-foreground-muted": "gray-450",
15
- "gray-stroke-muted": "gray-100",
16
- "gray-background-fill-muted": "gray-400",
17
- "gray-background-soft-muted": "gray-30",
18
- "primary-foreground-onfill": "white",
19
- "primary-background-fill": "primary",
8
+ 'content-title': 'primary',
9
+ 'content-paragraph': 'gray-600',
10
+ 'gray-foreground': 'gray-600',
11
+ 'primary-foreground': 'primary',
12
+ 'secondary-foreground': 'secondary-darken-3',
13
+ 'secondary-stroke-emphasis': 'secondary-darken-1',
14
+ 'surface-card': 'white',
15
+ 'gray-foreground-muted': 'gray-450',
16
+ 'gray-stroke-muted': 'gray-100',
17
+ 'gray-background-fill-muted': 'gray-400',
18
+ 'gray-background-soft-muted': 'gray-30',
19
+ 'primary-foreground-onfill': 'white',
20
+ 'primary-background-fill': 'primary',
21
+ 'red-foreground': 'red-darken-2',
22
+ 'go-foreground': 'go-darken-1',
20
23
  };
21
24
 
22
25
  /** Fallback — dark mode (espelha tokens.css `.dark`). */
23
26
  const DARK_COLOR_ALIAS_MAP: Record<string, string> = {
24
- "content-title": "white",
25
- "content-paragraph": "gray-150",
26
- "gray-foreground": "gray-100",
27
- "primary-foreground": "gray-100",
28
- "secondary-foreground": "secondary-lighten-1",
29
- "surface-card": "gray-850",
30
- "gray-foreground-muted": "gray-150",
31
- "gray-stroke-muted": "gray-700",
32
- "gray-background-fill-muted": "gray-300",
33
- "gray-background-soft-muted": "gray-700",
34
- "primary-foreground-onfill": "primary",
35
- "primary-background-fill": "gray-70",
27
+ 'content-title': 'white',
28
+ 'content-paragraph': 'gray-150',
29
+ 'gray-foreground': 'gray-100',
30
+ 'primary-foreground': 'gray-100',
31
+ 'secondary-foreground': 'secondary-lighten-1',
32
+ 'secondary-stroke-emphasis': 'secondary',
33
+ 'surface-card': 'gray-850',
34
+ 'gray-foreground-muted': 'gray-150',
35
+ 'gray-stroke-muted': 'gray-700',
36
+ 'gray-background-fill-muted': 'gray-300',
37
+ 'gray-background-soft-muted': 'gray-700',
38
+ 'primary-foreground-onfill': 'primary',
39
+ 'primary-background-fill': 'gray-70',
40
+ 'red-foreground': 'red-lighten-3',
41
+ 'go-foreground': 'go-lighten-5',
36
42
  };
37
43
 
44
+ /** Fallback estático quando NativeWind não resolve `var(--color-*)` encadeado. */
45
+ const THEME_COLOR_FALLBACKS: Record<string, string> = {
46
+ white: '#FFFFFF',
47
+ primary: 'rgba(0, 32, 91, 1)',
48
+ 'gray-600': 'rgba(49, 57, 73, 1)',
49
+ 'gray-450': 'rgba(90, 105, 135, 1)',
50
+ 'gray-400': 'rgba(122, 133, 151, 1)',
51
+ 'gray-100': 'rgba(231, 233, 239, 1)',
52
+ 'gray-30': 'rgba(249, 250, 251, 1)',
53
+ 'gray-150': 'rgba(206, 211, 222, 1)',
54
+ 'gray-300': 'rgba(133, 146, 173, 1)',
55
+ 'gray-700': 'rgba(33, 38, 49, 1)',
56
+ 'gray-850': 'rgba(16, 19, 24, 1)',
57
+ 'gray-70': 'rgba(240, 241, 245, 1)',
58
+ 'red-darken-2': 'rgba(182, 27, 45, 1)',
59
+ 'red-lighten-3': 'rgba(231, 131, 142, 1)',
60
+ 'go-darken-1': 'rgba(141, 22, 77, 1)',
61
+ 'go-lighten-5': 'rgba(237, 187, 192, 1)',
62
+ 'secondary-darken-1': 'rgba(7, 171, 149, 1)',
63
+ 'secondary-darken-3': 'rgba(13, 104, 92, 1)',
64
+ 'secondary-lighten-1': 'rgba(22, 208, 118, 1)',
65
+ };
66
+
67
+ const DEFAULT_COLOR = '#3B4958';
68
+
38
69
  function toCssVariable(token: ThemeColorToken): string {
39
- return token.startsWith("--") ? token : `--color-${token}`;
70
+ return token.startsWith('--') ? token : `--color-${token}`;
40
71
  }
41
72
 
42
73
  function toTokenName(token: ThemeColorToken): string {
43
- return token.startsWith("--color-") ? token.slice("--color-".length) : token;
74
+ return token.startsWith('--color-') ? token.slice('--color-'.length) : token;
44
75
  }
45
76
 
46
77
  function toResolvedCssVariable(
@@ -52,17 +83,31 @@ function toResolvedCssVariable(
52
83
  }
53
84
 
54
85
  function isResolvedColor(value: unknown): value is string {
55
- return typeof value === "string" && !value.startsWith("var(");
86
+ return typeof value === 'string' && !value.startsWith('var(');
87
+ }
88
+
89
+ function getFallbackColor(
90
+ token: ThemeColorToken,
91
+ aliasMap: Record<string, string>
92
+ ): string {
93
+ const tokenName = toTokenName(token);
94
+ const aliasToken = aliasMap[tokenName] ?? tokenName;
95
+
96
+ return (
97
+ THEME_COLOR_FALLBACKS[tokenName] ??
98
+ THEME_COLOR_FALLBACKS[aliasToken] ??
99
+ DEFAULT_COLOR
100
+ );
56
101
  }
57
102
 
58
103
  /**
59
104
  * Resolve uma cor semântica do design system (tokens.native.css) em runtime.
60
105
  * Use quando a prop `color` for obrigatória (SVG, stroke Reanimated, etc.).
61
106
  */
62
- function useThemeColor(token: ThemeColorToken): string | undefined {
107
+ function useThemeColor(token: ThemeColorToken): string {
63
108
  const { colorScheme } = useColorScheme();
64
109
  const aliasMap =
65
- colorScheme === "dark" ? DARK_COLOR_ALIAS_MAP : LIGHT_COLOR_ALIAS_MAP;
110
+ colorScheme === 'dark' ? DARK_COLOR_ALIAS_MAP : LIGHT_COLOR_ALIAS_MAP;
66
111
 
67
112
  const value = useUnstableNativeVariable(toCssVariable(token));
68
113
  const resolvedValue = useUnstableNativeVariable(
@@ -72,8 +117,8 @@ function useThemeColor(token: ThemeColorToken): string | undefined {
72
117
  if (isResolvedColor(value)) return value;
73
118
  if (isResolvedColor(resolvedValue)) return resolvedValue;
74
119
 
75
- return undefined;
120
+ return getFallbackColor(token, aliasMap);
76
121
  }
77
122
 
78
- export { useThemeColor, toCssVariable };
123
+ export { useThemeColor, toCssVariable, getFallbackColor, DEFAULT_COLOR };
79
124
  export type { ThemeColorToken };