@bigbinary/neetoui-rn 2.3.4 → 3.0.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 (280) hide show
  1. package/README.md +118 -0
  2. package/lib/typescript/src/components/Alert/Alert.d.ts +32 -0
  3. package/lib/typescript/src/components/Alert/Alert.d.ts.map +1 -0
  4. package/lib/typescript/src/components/Alert/index.d.ts +2 -0
  5. package/lib/typescript/src/components/Alert/index.d.ts.map +1 -0
  6. package/lib/typescript/src/components/Avatar/Avatar.d.ts +21 -0
  7. package/lib/typescript/src/components/Avatar/Avatar.d.ts.map +1 -0
  8. package/lib/typescript/src/components/Avatar/index.d.ts +2 -0
  9. package/lib/typescript/src/components/Avatar/index.d.ts.map +1 -0
  10. package/lib/typescript/src/components/BottomSheet/BottomSheet.d.ts +61 -0
  11. package/lib/typescript/src/components/BottomSheet/BottomSheet.d.ts.map +1 -0
  12. package/lib/typescript/src/components/BottomSheet/index.d.ts +2 -0
  13. package/lib/typescript/src/components/BottomSheet/index.d.ts.map +1 -0
  14. package/lib/typescript/src/components/BottomSheetMenu/BottomSheetMenu.d.ts +28 -0
  15. package/lib/typescript/src/components/BottomSheetMenu/BottomSheetMenu.d.ts.map +1 -0
  16. package/lib/typescript/src/components/BottomSheetMenu/index.d.ts +2 -0
  17. package/lib/typescript/src/components/BottomSheetMenu/index.d.ts.map +1 -0
  18. package/lib/typescript/src/components/Button/Button.d.ts +79 -0
  19. package/lib/typescript/src/components/Button/Button.d.ts.map +1 -0
  20. package/lib/typescript/src/components/Button/index.d.ts +2 -0
  21. package/lib/typescript/src/components/Button/index.d.ts.map +1 -0
  22. package/lib/typescript/src/components/ChatInput/AttachmentsView.d.ts +11 -0
  23. package/lib/typescript/src/components/ChatInput/AttachmentsView.d.ts.map +1 -0
  24. package/lib/typescript/src/components/ChatInput/ChatInput.d.ts +78 -0
  25. package/lib/typescript/src/components/ChatInput/ChatInput.d.ts.map +1 -0
  26. package/lib/typescript/src/components/ChatInput/IconButton.d.ts +16 -0
  27. package/lib/typescript/src/components/ChatInput/IconButton.d.ts.map +1 -0
  28. package/lib/typescript/src/components/ChatInput/MentionsInput.d.ts +13 -0
  29. package/lib/typescript/src/components/ChatInput/MentionsInput.d.ts.map +1 -0
  30. package/lib/typescript/src/components/ChatInput/constants.d.ts +9 -0
  31. package/lib/typescript/src/components/ChatInput/constants.d.ts.map +1 -0
  32. package/lib/typescript/src/components/ChatInput/index.d.ts +4 -0
  33. package/lib/typescript/src/components/ChatInput/index.d.ts.map +1 -0
  34. package/lib/typescript/src/components/ChatInput/mentions.d.ts +26 -0
  35. package/lib/typescript/src/components/ChatInput/mentions.d.ts.map +1 -0
  36. package/lib/typescript/src/components/CheckBox/CheckBox.d.ts +13 -0
  37. package/lib/typescript/src/components/CheckBox/CheckBox.d.ts.map +1 -0
  38. package/lib/typescript/src/components/CheckBox/index.d.ts +2 -0
  39. package/lib/typescript/src/components/CheckBox/index.d.ts.map +1 -0
  40. package/lib/typescript/src/components/Container/Container.d.ts +9 -0
  41. package/lib/typescript/src/components/Container/Container.d.ts.map +1 -0
  42. package/lib/typescript/src/components/Container/index.d.ts +2 -0
  43. package/lib/typescript/src/components/Container/index.d.ts.map +1 -0
  44. package/lib/typescript/src/components/Divider/Divider.d.ts +7 -0
  45. package/lib/typescript/src/components/Divider/Divider.d.ts.map +1 -0
  46. package/lib/typescript/src/components/Divider/index.d.ts +2 -0
  47. package/lib/typescript/src/components/Divider/index.d.ts.map +1 -0
  48. package/lib/typescript/src/components/EmptyView/EmptyView.d.ts +26 -0
  49. package/lib/typescript/src/components/EmptyView/EmptyView.d.ts.map +1 -0
  50. package/lib/typescript/src/components/EmptyView/index.d.ts +2 -0
  51. package/lib/typescript/src/components/EmptyView/index.d.ts.map +1 -0
  52. package/lib/typescript/src/components/FlashList/FlashList.d.ts +14 -0
  53. package/lib/typescript/src/components/FlashList/FlashList.d.ts.map +1 -0
  54. package/lib/typescript/src/components/FlashList/index.d.ts +2 -0
  55. package/lib/typescript/src/components/FlashList/index.d.ts.map +1 -0
  56. package/lib/typescript/src/components/Header/BaseHeader.d.ts +5 -0
  57. package/lib/typescript/src/components/Header/BaseHeader.d.ts.map +1 -0
  58. package/lib/typescript/src/components/Header/Header.d.ts +21 -0
  59. package/lib/typescript/src/components/Header/Header.d.ts.map +1 -0
  60. package/lib/typescript/src/components/Header/ScreenHeader.d.ts +11 -0
  61. package/lib/typescript/src/components/Header/ScreenHeader.d.ts.map +1 -0
  62. package/lib/typescript/src/components/Header/index.d.ts +4 -0
  63. package/lib/typescript/src/components/Header/index.d.ts.map +1 -0
  64. package/lib/typescript/src/components/Input/Input.d.ts +15 -0
  65. package/lib/typescript/src/components/Input/Input.d.ts.map +1 -0
  66. package/lib/typescript/src/components/Input/index.d.ts +2 -0
  67. package/lib/typescript/src/components/Input/index.d.ts.map +1 -0
  68. package/lib/typescript/src/components/Loader/Loader.d.ts +12 -0
  69. package/lib/typescript/src/components/Loader/Loader.d.ts.map +1 -0
  70. package/lib/typescript/src/components/Loader/index.d.ts +2 -0
  71. package/lib/typescript/src/components/Loader/index.d.ts.map +1 -0
  72. package/lib/typescript/src/components/NeetoUIProvider/NeetoUIProvider.d.ts +16 -0
  73. package/lib/typescript/src/components/NeetoUIProvider/NeetoUIProvider.d.ts.map +1 -0
  74. package/lib/typescript/src/components/NeetoUIProvider/index.d.ts +2 -0
  75. package/lib/typescript/src/components/NeetoUIProvider/index.d.ts.map +1 -0
  76. package/lib/typescript/src/components/OnBoarding/OnBoarding.d.ts +22 -0
  77. package/lib/typescript/src/components/OnBoarding/OnBoarding.d.ts.map +1 -0
  78. package/lib/typescript/src/components/OnBoarding/index.d.ts +2 -0
  79. package/lib/typescript/src/components/OnBoarding/index.d.ts.map +1 -0
  80. package/lib/typescript/src/components/ParentView/ParentView.d.ts +13 -0
  81. package/lib/typescript/src/components/ParentView/ParentView.d.ts.map +1 -0
  82. package/lib/typescript/src/components/ParentView/index.d.ts +2 -0
  83. package/lib/typescript/src/components/ParentView/index.d.ts.map +1 -0
  84. package/lib/typescript/src/components/RadioButton/RadioButton.d.ts +12 -0
  85. package/lib/typescript/src/components/RadioButton/RadioButton.d.ts.map +1 -0
  86. package/lib/typescript/src/components/RadioButton/index.d.ts +2 -0
  87. package/lib/typescript/src/components/RadioButton/index.d.ts.map +1 -0
  88. package/lib/typescript/src/components/ScrollView/ScrollView.d.ts +8 -0
  89. package/lib/typescript/src/components/ScrollView/ScrollView.d.ts.map +1 -0
  90. package/lib/typescript/src/components/ScrollView/index.d.ts +2 -0
  91. package/lib/typescript/src/components/ScrollView/index.d.ts.map +1 -0
  92. package/lib/typescript/src/components/SearchBar/SearchBar.d.ts +12 -0
  93. package/lib/typescript/src/components/SearchBar/SearchBar.d.ts.map +1 -0
  94. package/lib/typescript/src/components/SearchBar/index.d.ts +2 -0
  95. package/lib/typescript/src/components/SearchBar/index.d.ts.map +1 -0
  96. package/lib/typescript/src/components/Skeleton/Skeleton.d.ts +41 -0
  97. package/lib/typescript/src/components/Skeleton/Skeleton.d.ts.map +1 -0
  98. package/lib/typescript/src/components/Skeleton/index.d.ts +2 -0
  99. package/lib/typescript/src/components/Skeleton/index.d.ts.map +1 -0
  100. package/lib/typescript/src/components/TextWithUrl/TextWithUrl.d.ts +11 -0
  101. package/lib/typescript/src/components/TextWithUrl/TextWithUrl.d.ts.map +1 -0
  102. package/lib/typescript/src/components/TextWithUrl/index.d.ts +2 -0
  103. package/lib/typescript/src/components/TextWithUrl/index.d.ts.map +1 -0
  104. package/lib/typescript/src/components/Toast/Toast.d.ts +13 -0
  105. package/lib/typescript/src/components/Toast/Toast.d.ts.map +1 -0
  106. package/lib/typescript/src/components/Toast/ToastGlyph.d.ts +4 -0
  107. package/lib/typescript/src/components/Toast/ToastGlyph.d.ts.map +1 -0
  108. package/lib/typescript/src/components/Toast/index.d.ts +2 -0
  109. package/lib/typescript/src/components/Toast/index.d.ts.map +1 -0
  110. package/lib/typescript/src/components/Touchable/Touchable.d.ts +23 -0
  111. package/lib/typescript/src/components/Touchable/Touchable.d.ts.map +1 -0
  112. package/lib/typescript/src/components/Touchable/index.d.ts +2 -0
  113. package/lib/typescript/src/components/Touchable/index.d.ts.map +1 -0
  114. package/lib/typescript/src/components/Typography/Typography.d.ts +64 -0
  115. package/lib/typescript/src/components/Typography/Typography.d.ts.map +1 -0
  116. package/lib/typescript/src/components/Typography/index.d.ts +2 -0
  117. package/lib/typescript/src/components/Typography/index.d.ts.map +1 -0
  118. package/lib/typescript/src/hooks/index.d.ts +4 -0
  119. package/lib/typescript/src/hooks/index.d.ts.map +1 -0
  120. package/lib/typescript/src/hooks/useDebounce.d.ts +2 -0
  121. package/lib/typescript/src/hooks/useDebounce.d.ts.map +1 -0
  122. package/lib/typescript/src/hooks/useNeetoTheme.d.ts +8 -0
  123. package/lib/typescript/src/hooks/useNeetoTheme.d.ts.map +1 -0
  124. package/lib/typescript/src/hooks/useRefreshByUser.d.ts +5 -0
  125. package/lib/typescript/src/hooks/useRefreshByUser.d.ts.map +1 -0
  126. package/lib/typescript/src/index.d.ts +31 -0
  127. package/lib/typescript/src/index.d.ts.map +1 -0
  128. package/lib/typescript/src/theme/palette.d.ts +28 -0
  129. package/lib/typescript/src/theme/palette.d.ts.map +1 -0
  130. package/lib/typescript/src/theme/palette.generated.d.ts +134 -0
  131. package/lib/typescript/src/theme/palette.generated.d.ts.map +1 -0
  132. package/lib/typescript/src/theme/tokens.d.ts +184 -0
  133. package/lib/typescript/src/theme/tokens.d.ts.map +1 -0
  134. package/lib/typescript/src/unistyles-init.d.ts +2 -0
  135. package/lib/typescript/src/unistyles-init.d.ts.map +1 -0
  136. package/lib/typescript/src/unistyles.d.ts +21 -0
  137. package/lib/typescript/src/unistyles.d.ts.map +1 -0
  138. package/lib/typescript/src/utils/index.d.ts +18 -0
  139. package/lib/typescript/src/utils/index.d.ts.map +1 -0
  140. package/lib/typescript/src/utils/scale.d.ts +2 -0
  141. package/lib/typescript/src/utils/scale.d.ts.map +1 -0
  142. package/package.json +125 -51
  143. package/src/components/Alert/Alert.tsx +222 -0
  144. package/src/components/Alert/index.ts +1 -0
  145. package/src/components/Avatar/Avatar.tsx +123 -0
  146. package/src/components/Avatar/index.ts +1 -0
  147. package/src/components/BottomSheet/BottomSheet.tsx +468 -0
  148. package/src/components/BottomSheet/index.ts +5 -0
  149. package/src/components/BottomSheetMenu/BottomSheetMenu.tsx +150 -0
  150. package/src/components/BottomSheetMenu/index.ts +5 -0
  151. package/src/components/Button/Button.tsx +134 -0
  152. package/src/components/Button/index.ts +1 -0
  153. package/src/components/ChatInput/AttachmentsView.tsx +68 -0
  154. package/src/components/ChatInput/ChatInput.tsx +393 -0
  155. package/src/components/ChatInput/IconButton.tsx +52 -0
  156. package/src/components/ChatInput/MentionsInput.tsx +126 -0
  157. package/src/components/ChatInput/constants.ts +18 -0
  158. package/src/components/ChatInput/index.ts +11 -0
  159. package/src/components/ChatInput/mentions.ts +91 -0
  160. package/src/components/CheckBox/CheckBox.tsx +86 -0
  161. package/src/components/CheckBox/index.ts +1 -0
  162. package/src/components/Container/Container.tsx +14 -0
  163. package/src/components/Container/index.ts +1 -0
  164. package/src/components/Divider/Divider.tsx +32 -0
  165. package/src/components/Divider/index.ts +1 -0
  166. package/src/components/EmptyView/EmptyView.tsx +80 -0
  167. package/src/components/EmptyView/index.ts +1 -0
  168. package/src/components/FlashList/FlashList.tsx +162 -0
  169. package/src/components/FlashList/index.ts +1 -0
  170. package/src/components/Header/BaseHeader.tsx +19 -0
  171. package/src/components/Header/Header.tsx +114 -0
  172. package/src/components/Header/ScreenHeader.tsx +34 -0
  173. package/src/components/Header/index.ts +3 -0
  174. package/src/components/Input/Input.tsx +122 -0
  175. package/src/components/Input/index.ts +1 -0
  176. package/src/components/Loader/Loader.tsx +93 -0
  177. package/src/components/Loader/index.ts +1 -0
  178. package/src/components/NeetoUIProvider/NeetoUIProvider.tsx +43 -0
  179. package/src/components/NeetoUIProvider/index.ts +1 -0
  180. package/src/components/OnBoarding/OnBoarding.tsx +177 -0
  181. package/src/components/OnBoarding/index.ts +5 -0
  182. package/src/components/ParentView/ParentView.tsx +98 -0
  183. package/src/components/ParentView/index.ts +1 -0
  184. package/src/components/RadioButton/RadioButton.tsx +80 -0
  185. package/src/components/RadioButton/index.ts +1 -0
  186. package/src/components/ScrollView/ScrollView.tsx +30 -0
  187. package/src/components/ScrollView/index.ts +1 -0
  188. package/src/components/SearchBar/SearchBar.tsx +122 -0
  189. package/src/components/SearchBar/index.ts +1 -0
  190. package/src/components/Skeleton/Skeleton.tsx +121 -0
  191. package/src/components/Skeleton/index.ts +1 -0
  192. package/src/components/TextWithUrl/TextWithUrl.tsx +54 -0
  193. package/src/components/TextWithUrl/index.ts +1 -0
  194. package/src/components/Toast/Toast.tsx +157 -0
  195. package/src/components/Toast/ToastGlyph.tsx +53 -0
  196. package/src/components/Toast/index.ts +1 -0
  197. package/src/components/Touchable/Touchable.tsx +54 -0
  198. package/src/components/Touchable/index.ts +1 -0
  199. package/src/components/Typography/Typography.tsx +76 -0
  200. package/src/components/Typography/index.ts +1 -0
  201. package/src/hooks/index.ts +3 -0
  202. package/src/hooks/useDebounce.ts +17 -0
  203. package/src/hooks/useNeetoTheme.ts +10 -0
  204. package/src/hooks/useRefreshByUser.ts +17 -0
  205. package/src/index.ts +68 -0
  206. package/src/theme/palette.generated.ts +141 -0
  207. package/src/theme/palette.ts +34 -0
  208. package/src/theme/tokens.ts +205 -0
  209. package/src/types/neeto-icons-rn.d.ts +33 -0
  210. package/src/unistyles-init.ts +16 -0
  211. package/src/unistyles.ts +34 -0
  212. package/src/utils/index.ts +41 -0
  213. package/src/utils/scale.ts +16 -0
  214. package/components/Accordion.js +0 -1
  215. package/components/ActionIcon.js +0 -1
  216. package/components/Alert.js +0 -1
  217. package/components/Avatar.js +0 -1
  218. package/components/Badge.js +0 -1
  219. package/components/BottomSheet.js +0 -1
  220. package/components/BottomTabBar.js +0 -1
  221. package/components/Button.js +0 -1
  222. package/components/ButtonGroup.js +0 -1
  223. package/components/Calendar.js +0 -1
  224. package/components/Card.js +0 -1
  225. package/components/Carousel.js +0 -1
  226. package/components/ChatInput/AttachmentsView.js +0 -1
  227. package/components/ChatInput/Badge.js +0 -1
  228. package/components/ChatInput/ChatInput.js +0 -1
  229. package/components/ChatInput/EmailFields.js +0 -1
  230. package/components/ChatInput/IconButton.js +0 -1
  231. package/components/ChatInput/MentionsInputWrapper.js +0 -1
  232. package/components/CheckBox.js +0 -1
  233. package/components/Chip.js +0 -1
  234. package/components/Container.js +0 -1
  235. package/components/Divider.js +0 -1
  236. package/components/FAB.js +0 -1
  237. package/components/FlashList.js +0 -1
  238. package/components/FlatList.js +0 -1
  239. package/components/Input.js +0 -1
  240. package/components/InputEmailChip.js +0 -1
  241. package/components/LineLoader.js +0 -1
  242. package/components/ListItem.js +0 -1
  243. package/components/Loader.js +0 -1
  244. package/components/MultiSelect.js +0 -1
  245. package/components/NotificationIcon.js +0 -1
  246. package/components/NotificationPreferenceList.js +0 -1
  247. package/components/OnBoarding.js +0 -1
  248. package/components/OrganizationItem.js +0 -1
  249. package/components/OtpInputs.js +0 -1
  250. package/components/ParentView.js +0 -1
  251. package/components/Popover.js +0 -1
  252. package/components/RadioButton.js +0 -1
  253. package/components/RichTextEditor.js +0 -1
  254. package/components/Ripple.js +0 -1
  255. package/components/ScrollView.js +0 -1
  256. package/components/SearchBar.js +0 -1
  257. package/components/SegmentedTopBar/Indicator.js +0 -1
  258. package/components/SegmentedTopBar/Tab.js +0 -1
  259. package/components/SegmentedTopBar/index.js +0 -1
  260. package/components/SocialButton.js +0 -1
  261. package/components/Toast.js +0 -1
  262. package/components/ToggleSwitch.js +0 -1
  263. package/components/TopBar.js +0 -1
  264. package/components/Touchable.js +0 -1
  265. package/components/TouchableWithoutFeedback.js +0 -1
  266. package/components/Typography.js +0 -1
  267. package/components/index.js +0 -1
  268. package/config/index.js +0 -1
  269. package/config/toasterConfig.js +0 -1
  270. package/contexts/index.js +0 -1
  271. package/hooks/index.js +0 -1
  272. package/hooks/useDebounce.js +0 -1
  273. package/hooks/useKeyboard.js +0 -1
  274. package/hooks/useRefreshByUser.js +0 -1
  275. package/index.d.ts +0 -630
  276. package/index.js +0 -1
  277. package/react-native.config.js +0 -10
  278. package/theme/index.js +0 -1
  279. package/utils/index.js +0 -1
  280. package/utils/scale.js +0 -1
@@ -0,0 +1,114 @@
1
+ import {
2
+ createElement,
3
+ isValidElement,
4
+ type ComponentType,
5
+ type ReactNode,
6
+ } from "react";
7
+ import { View } from "react-native";
8
+ import { LeftArrow } from "@bigbinary/neeto-icons-rn";
9
+ import { StyleSheet, useUnistyles } from "react-native-unistyles";
10
+
11
+ import { moderateScale } from "../../utils/scale";
12
+ import { Skeleton } from "../Skeleton";
13
+ import { Touchable } from "../Touchable";
14
+ import { Typography } from "../Typography";
15
+ import { BaseHeader } from "./BaseHeader";
16
+
17
+ export type HeaderProps = {
18
+ title: string;
19
+ subTitle?: string;
20
+ /** Back chevron renders only when this is passed. */
21
+ onBack?: () => void;
22
+ /** Leading slot after the back chevron (menu button, workspace logo…). */
23
+ LeftSlot?: ComponentType | ReactNode;
24
+ /** Trailing slot (actions, overflow menus…). */
25
+ RightSlot?: ComponentType | ReactNode;
26
+ /** Shows the progress line under the header (v2 LineLoader spot). */
27
+ isLoading?: boolean;
28
+ };
29
+
30
+ // memo() and forwardRef() components are objects, not functions — rendering
31
+ // one as a child throws "Objects are not valid as a React child", so the
32
+ // slot has to be instantiated whenever it is a component type of any kind.
33
+ const isComponentType = (slot: unknown): slot is ComponentType =>
34
+ typeof slot === "function" ||
35
+ (typeof slot === "object" &&
36
+ slot !== null &&
37
+ "$$typeof" in slot &&
38
+ !isValidElement(slot));
39
+
40
+ const renderSlot = (Slot: ComponentType | ReactNode): ReactNode => {
41
+ if (Slot === null || Slot === undefined) return null;
42
+
43
+ return isComponentType(Slot) ? createElement(Slot) : Slot;
44
+ };
45
+
46
+ /**
47
+ * Navigation header (promoted from neeto-commons-rn). The commons version
48
+ * read useNavigation() itself; v3 is navigation-agnostic — apps (or the
49
+ * commons compat wrapper) pass onBack and fill the slots. The mark-all
50
+ * menu, hamburger and workspace logo become slot content.
51
+ */
52
+ export const Header = ({
53
+ title,
54
+ subTitle,
55
+ onBack,
56
+ LeftSlot,
57
+ RightSlot,
58
+ isLoading = false,
59
+ }: HeaderProps) => {
60
+ const { theme } = useUnistyles();
61
+
62
+ return (
63
+ <View>
64
+ <BaseHeader style={styles.container}>
65
+ {onBack && (
66
+ <Touchable
67
+ accessibilityLabel="Go back"
68
+ accessibilityRole="button"
69
+ hitSlop={moderateScale(8)}
70
+ style={styles.backButton}
71
+ onPress={onBack}
72
+ >
73
+ <LeftArrow
74
+ color={theme.colors.text.primary}
75
+ size={moderateScale(28)}
76
+ />
77
+ </Touchable>
78
+ )}
79
+ {renderSlot(LeftSlot)}
80
+ <View style={styles.titleBlock}>
81
+ <Typography numberOfLines={1} size="xl" weight="semibold">
82
+ {title}
83
+ </Typography>
84
+ {!!subTitle && (
85
+ <Typography numberOfLines={1} size="2xs" style={styles.subTitle}>
86
+ {subTitle}
87
+ </Typography>
88
+ )}
89
+ </View>
90
+ {renderSlot(RightSlot)}
91
+ </BaseHeader>
92
+ {isLoading && (
93
+ <Skeleton animated height={moderateScale(2)} variant="line" />
94
+ )}
95
+ </View>
96
+ );
97
+ };
98
+
99
+ const styles = StyleSheet.create(theme => ({
100
+ container: {
101
+ gap: moderateScale(8),
102
+ minHeight: moderateScale(48),
103
+ paddingHorizontal: moderateScale(16),
104
+ },
105
+ backButton: {
106
+ marginLeft: moderateScale(-8),
107
+ },
108
+ titleBlock: {
109
+ flex: 1,
110
+ },
111
+ subTitle: {
112
+ color: theme.colors.text.muted,
113
+ },
114
+ }));
@@ -0,0 +1,34 @@
1
+ import { Platform, type StyleProp, type ViewStyle } from "react-native";
2
+ import { StyleSheet } from "react-native-unistyles";
3
+
4
+ import { moderateScale } from "../../utils/scale";
5
+ import { Typography } from "../Typography";
6
+ import { BaseHeader } from "./BaseHeader";
7
+
8
+ export type ScreenHeaderProps = {
9
+ title: string;
10
+ style?: StyleProp<ViewStyle>;
11
+ };
12
+
13
+ /**
14
+ * Large screen title header (promoted from neeto-commons-rn). Platform
15
+ * heights kept: 48 Android / 42 iOS.
16
+ */
17
+ export const ScreenHeader = ({ title, style }: ScreenHeaderProps) => (
18
+ <BaseHeader style={[styles.container, style]}>
19
+ <Typography style={styles.title} variant="header">
20
+ {title}
21
+ </Typography>
22
+ </BaseHeader>
23
+ );
24
+
25
+ const styles = StyleSheet.create({
26
+ container: {
27
+ height: Platform.OS === "android" ? moderateScale(48) : moderateScale(42),
28
+ },
29
+ title: {
30
+ includeFontPadding: false,
31
+ textAlignVertical: "center",
32
+ ...(Platform.OS === "android" ? { lineHeight: moderateScale(28) } : {}),
33
+ },
34
+ });
@@ -0,0 +1,3 @@
1
+ export { BaseHeader, type BaseHeaderProps } from "./BaseHeader";
2
+ export { Header, type HeaderProps } from "./Header";
3
+ export { ScreenHeader, type ScreenHeaderProps } from "./ScreenHeader";
@@ -0,0 +1,122 @@
1
+ import { forwardRef, useState } from "react";
2
+ import {
3
+ TextInput,
4
+ View,
5
+ type StyleProp,
6
+ type TextInputProps,
7
+ type ViewStyle,
8
+ } from "react-native";
9
+ import { StyleSheet } from "react-native-unistyles";
10
+
11
+ import { Typography } from "../Typography";
12
+
13
+ export type InputProps = Omit<TextInputProps, "editable"> & {
14
+ label?: string;
15
+ error?: string;
16
+ disabled?: boolean;
17
+ containerStyle?: StyleProp<ViewStyle>;
18
+ };
19
+
20
+ /** Forwards its ref to the underlying TextInput, so forms can focus it. */
21
+ export const Input = forwardRef<TextInput, InputProps>(
22
+ (
23
+ {
24
+ label,
25
+ error,
26
+ disabled = false,
27
+ containerStyle,
28
+ style,
29
+ onFocus,
30
+ onBlur,
31
+ ...rest
32
+ },
33
+ ref
34
+ ) => {
35
+ const [isFocused, setIsFocused] = useState(false);
36
+
37
+ styles.useVariants({ focused: isFocused, error: !!error, disabled });
38
+
39
+ return (
40
+ <View style={[styles.container, containerStyle]}>
41
+ {!!label && (
42
+ <Typography size="xs" style={styles.label}>
43
+ {label}
44
+ </Typography>
45
+ )}
46
+ <TextInput
47
+ // Before the spread: the label is only a fallback, and setting it
48
+ // afterwards overwrote a caller's own accessibilityLabel with
49
+ // undefined whenever no label was passed.
50
+ accessibilityLabel={label}
51
+ {...rest}
52
+ editable={!disabled}
53
+ ref={ref}
54
+ style={[styles.input, style]}
55
+ onBlur={event => {
56
+ setIsFocused(false);
57
+ onBlur?.(event);
58
+ }}
59
+ onFocus={event => {
60
+ setIsFocused(true);
61
+ onFocus?.(event);
62
+ }}
63
+ />
64
+ {!!error && (
65
+ <Typography size="2xs" style={styles.error}>
66
+ {error}
67
+ </Typography>
68
+ )}
69
+ </View>
70
+ );
71
+ }
72
+ );
73
+
74
+ Input.displayName = "Input";
75
+
76
+ const styles = StyleSheet.create(theme => ({
77
+ container: {
78
+ gap: theme.spacing.xs,
79
+ },
80
+ label: {
81
+ color: theme.colors.text.secondary,
82
+ fontFamily: theme.fonts.medium,
83
+ },
84
+ input: {
85
+ borderColor: theme.colors.border.primary,
86
+ borderRadius: theme.radii.md,
87
+ borderWidth: 1,
88
+ color: theme.colors.text.primary,
89
+ fontFamily: theme.fonts.regular,
90
+ fontSize: theme.fontSizes.s,
91
+ paddingHorizontal: theme.spacing.md,
92
+ paddingVertical: theme.spacing.sm,
93
+ variants: {
94
+ focused: {
95
+ true: { borderColor: theme.colors.primary },
96
+ false: {},
97
+ },
98
+ error: {
99
+ true: { borderColor: theme.colors.danger },
100
+ false: {},
101
+ },
102
+ disabled: {
103
+ true: {
104
+ backgroundColor: theme.colors.background.secondary,
105
+ opacity: 0.5,
106
+ },
107
+ false: {},
108
+ },
109
+ },
110
+ // Error must win over focus when both are active.
111
+ compoundVariants: [
112
+ {
113
+ error: true,
114
+ focused: true,
115
+ styles: { borderColor: theme.colors.danger },
116
+ },
117
+ ],
118
+ },
119
+ error: {
120
+ color: theme.colors.danger,
121
+ },
122
+ }));
@@ -0,0 +1 @@
1
+ export { Input, type InputProps } from "./Input";
@@ -0,0 +1,93 @@
1
+ import { useEffect } from "react";
2
+ import { View } from "react-native";
3
+ import Animated, {
4
+ Easing,
5
+ interpolate,
6
+ useAnimatedProps,
7
+ useAnimatedStyle,
8
+ useSharedValue,
9
+ withRepeat,
10
+ withTiming,
11
+ } from "react-native-reanimated";
12
+ import Svg, { Circle } from "react-native-svg";
13
+ import { StyleSheet } from "react-native-unistyles";
14
+
15
+ import { moderateScale } from "../../utils/scale";
16
+ import { palette } from "../../theme/palette";
17
+
18
+ const LOADER_WIDTH = {
19
+ s: moderateScale(18),
20
+ m: moderateScale(24),
21
+ l: moderateScale(36),
22
+ };
23
+
24
+ const AnimatedCircle = Animated.createAnimatedComponent(Circle);
25
+
26
+ export type LoaderProps = {
27
+ // v2 default: the lightBlue400 accent, kept for parity.
28
+ color?: string;
29
+ size?: keyof typeof LOADER_WIDTH;
30
+ };
31
+
32
+ export const Loader = ({
33
+ color = palette.lightBlue400,
34
+ size = "s",
35
+ }: LoaderProps) => {
36
+ const progress = useSharedValue(0);
37
+ const width = LOADER_WIDTH[size];
38
+ const strokeWidth = width * 0.15;
39
+ const radius = (width - strokeWidth) / 2;
40
+ const circumference = 2 * Math.PI * radius;
41
+
42
+ useEffect(() => {
43
+ progress.value = withRepeat(
44
+ withTiming(2, { duration: 1500, easing: Easing.linear }),
45
+ -1,
46
+ false
47
+ );
48
+ }, [progress]);
49
+
50
+ const animatedProps = useAnimatedProps(() => ({
51
+ strokeDashoffset: -interpolate(
52
+ progress.value,
53
+ [0, 1, 2],
54
+ [0, circumference, circumference * 2]
55
+ ),
56
+ }));
57
+
58
+ const animatedStyles = useAnimatedStyle(() => ({
59
+ width,
60
+ height: width,
61
+ alignItems: "center",
62
+ justifyContent: "center",
63
+ transform: [
64
+ { rotate: `${interpolate(progress.value, [0, 2], [0, 420])}deg` },
65
+ ],
66
+ }));
67
+
68
+ return (
69
+ <View style={styles.container} testID="neeto-loader">
70
+ <Animated.View style={animatedStyles}>
71
+ <Svg fill="none" height={width} width={width}>
72
+ <AnimatedCircle
73
+ animatedProps={animatedProps}
74
+ cx={width / 2}
75
+ cy={width / 2}
76
+ r={radius}
77
+ stroke={color}
78
+ strokeDasharray={circumference}
79
+ strokeLinecap="round"
80
+ strokeWidth={strokeWidth}
81
+ />
82
+ </Svg>
83
+ </Animated.View>
84
+ </View>
85
+ );
86
+ };
87
+
88
+ const styles = StyleSheet.create({
89
+ container: {
90
+ alignItems: "center",
91
+ justifyContent: "center",
92
+ },
93
+ });
@@ -0,0 +1 @@
1
+ export { Loader, type LoaderProps } from "./Loader";
@@ -0,0 +1,43 @@
1
+ import { type ReactNode } from "react";
2
+ import { StyleSheet } from "react-native";
3
+ import { BottomSheetModalProvider } from "@gorhom/bottom-sheet";
4
+ import { GestureHandlerRootView } from "react-native-gesture-handler";
5
+ import { SafeAreaProvider } from "react-native-safe-area-context";
6
+
7
+ import { configureNeetoUI } from "../../unistyles";
8
+ import { Alert } from "../Alert";
9
+ import { Toast } from "../Toast";
10
+
11
+ export type NeetoUIProviderProps = {
12
+ children: ReactNode;
13
+ };
14
+
15
+ /**
16
+ * Single mount point for everything the library needs at app root.
17
+ *
18
+ * Unistyles registration runs before the first child render (idempotent) so
19
+ * lazily-resolved stylesheets see the themes. SafeAreaProvider feeds
20
+ * ParentView/BottomSheet insets, GestureHandlerRootView +
21
+ * BottomSheetModalProvider host BottomSheet portals, and the Toast host
22
+ * makes Toast.show work with no manual mounting — v2 required apps to
23
+ * render <Toast /> themselves; that footgun dies here.
24
+ */
25
+ export const NeetoUIProvider = ({ children }: NeetoUIProviderProps) => {
26
+ // Idempotent fallback — the root entry already configures on import
27
+ // (src/unistyles-init.ts); this covers deep-import edge cases.
28
+ configureNeetoUI();
29
+
30
+ return (
31
+ <GestureHandlerRootView style={styles.root}>
32
+ <SafeAreaProvider>
33
+ <BottomSheetModalProvider>
34
+ {children}
35
+ <Alert />
36
+ <Toast />
37
+ </BottomSheetModalProvider>
38
+ </SafeAreaProvider>
39
+ </GestureHandlerRootView>
40
+ );
41
+ };
42
+
43
+ const styles = StyleSheet.create({ root: { flex: 1 } });
@@ -0,0 +1 @@
1
+ export { NeetoUIProvider, type NeetoUIProviderProps } from "./NeetoUIProvider";
@@ -0,0 +1,177 @@
1
+ import { useRef, useState, type ComponentType, type ReactNode } from "react";
2
+ import {
3
+ FlatList,
4
+ View,
5
+ useWindowDimensions,
6
+ type NativeScrollEvent,
7
+ type NativeSyntheticEvent,
8
+ } from "react-native";
9
+ import { useSafeAreaInsets } from "react-native-safe-area-context";
10
+ import { StyleSheet } from "react-native-unistyles";
11
+
12
+ import { moderateScale } from "../../utils/scale";
13
+ import { Button } from "../Button";
14
+ import { Typography } from "../Typography";
15
+
16
+ export type OnBoardingSlide = {
17
+ title: string;
18
+ description: string;
19
+ illustration?: ReactNode;
20
+ };
21
+
22
+ export type OnBoardingProps = {
23
+ /** Rendered top-center; receives `width` (v2 contract). */
24
+ appLogo: ComponentType<{ width: number }>;
25
+ slides: OnBoardingSlide[];
26
+ onComplete: () => void;
27
+ logoWidth?: number;
28
+ };
29
+
30
+ /**
31
+ * v2's OnBoarding sat on react-native-snap-carousel, which is unmaintained
32
+ * and dies in v3 — the pager is now an internal paging FlatList with the
33
+ * same dots. Public API is unchanged.
34
+ */
35
+ export const OnBoarding = ({
36
+ appLogo: AppLogo,
37
+ slides,
38
+ onComplete,
39
+ logoWidth = moderateScale(150),
40
+ }: OnBoardingProps) => {
41
+ const [activeIndex, setActiveIndex] = useState(0);
42
+ const listRef = useRef<FlatList<OnBoardingSlide>>(null);
43
+ const { width } = useWindowDimensions();
44
+ const { bottom } = useSafeAreaInsets();
45
+
46
+ const isLastSlide = activeIndex === slides.length - 1;
47
+
48
+ const handleNext = () => {
49
+ if (isLastSlide) {
50
+ onComplete();
51
+
52
+ return;
53
+ }
54
+ const next = activeIndex + 1;
55
+ setActiveIndex(next);
56
+ listRef.current?.scrollToIndex({ index: next, animated: true });
57
+ };
58
+
59
+ const handleMomentumEnd = (
60
+ event: NativeSyntheticEvent<NativeScrollEvent>
61
+ ) => {
62
+ const index = Math.round(event.nativeEvent.contentOffset.x / width);
63
+ setActiveIndex(Math.max(0, Math.min(index, slides.length - 1)));
64
+ };
65
+
66
+ return (
67
+ <View style={styles.root}>
68
+ <View style={styles.logoContainer}>
69
+ <AppLogo width={logoWidth} />
70
+ </View>
71
+ <FlatList
72
+ horizontal
73
+ pagingEnabled
74
+ data={slides}
75
+ keyExtractor={(_item, index) => String(index)}
76
+ ref={listRef}
77
+ showsHorizontalScrollIndicator={false}
78
+ testID="onboarding-pager"
79
+ getItemLayout={(_data, index) => ({
80
+ length: width,
81
+ offset: width * index,
82
+ index,
83
+ })}
84
+ renderItem={({ item }) => (
85
+ <View style={[styles.slide, { width }]}>
86
+ <View style={styles.illustration}>{item.illustration}</View>
87
+ <View style={styles.slideText}>
88
+ <Typography size="4xl" style={styles.centered} weight="bold">
89
+ {item.title}
90
+ </Typography>
91
+ <Typography
92
+ size="l"
93
+ style={[styles.centered, styles.description]}
94
+ >
95
+ {item.description}
96
+ </Typography>
97
+ </View>
98
+ </View>
99
+ )}
100
+ onMomentumScrollEnd={handleMomentumEnd}
101
+ />
102
+ <View accessibilityLabel="Page indicator" style={styles.dots}>
103
+ {slides.map((_slide, index) => (
104
+ <View
105
+ key={index}
106
+ style={[styles.dot, index !== activeIndex && styles.inactiveDot]}
107
+ />
108
+ ))}
109
+ </View>
110
+ <View
111
+ style={[
112
+ styles.buttonContainer,
113
+ bottom === 0 && styles.buttonContainerInsetless,
114
+ ]}
115
+ >
116
+ <Button
117
+ label={isLastSlide ? "Get Started" : "Next"}
118
+ onPress={handleNext}
119
+ />
120
+ </View>
121
+ </View>
122
+ );
123
+ };
124
+
125
+ const styles = StyleSheet.create(theme => ({
126
+ root: {
127
+ backgroundColor: theme.colors.background.primary,
128
+ flex: 1,
129
+ },
130
+ logoContainer: {
131
+ alignItems: "center",
132
+ },
133
+ slide: {
134
+ flex: 1,
135
+ justifyContent: "space-between",
136
+ },
137
+ illustration: {
138
+ alignItems: "center",
139
+ flex: 1,
140
+ justifyContent: "center",
141
+ marginHorizontal: moderateScale(24),
142
+ marginVertical: moderateScale(12),
143
+ },
144
+ slideText: {
145
+ alignItems: "center",
146
+ marginHorizontal: moderateScale(24),
147
+ marginVertical: moderateScale(12),
148
+ },
149
+ centered: {
150
+ textAlign: "center",
151
+ },
152
+ description: {
153
+ color: theme.colors.text.muted,
154
+ },
155
+ dots: {
156
+ alignItems: "center",
157
+ flexDirection: "row",
158
+ justifyContent: "center",
159
+ marginVertical: moderateScale(20),
160
+ },
161
+ dot: {
162
+ backgroundColor: theme.colors.text.primary,
163
+ borderRadius: moderateScale(5),
164
+ height: moderateScale(10),
165
+ marginHorizontal: moderateScale(4),
166
+ width: moderateScale(10),
167
+ },
168
+ inactiveDot: {
169
+ backgroundColor: theme.colors.palette.grey400,
170
+ },
171
+ buttonContainer: {
172
+ marginHorizontal: moderateScale(24),
173
+ },
174
+ buttonContainerInsetless: {
175
+ marginBottom: moderateScale(16),
176
+ },
177
+ }));
@@ -0,0 +1,5 @@
1
+ export {
2
+ OnBoarding,
3
+ type OnBoardingProps,
4
+ type OnBoardingSlide,
5
+ } from "./OnBoarding";
@@ -0,0 +1,98 @@
1
+ import {
2
+ Keyboard,
3
+ StatusBar,
4
+ TextInput,
5
+ type StatusBarStyle,
6
+ type ViewProps,
7
+ } from "react-native";
8
+ import {
9
+ SafeAreaView,
10
+ useSafeAreaInsets,
11
+ } from "react-native-safe-area-context";
12
+ import { StyleSheet, useUnistyles } from "react-native-unistyles";
13
+
14
+ import { Container } from "../Container";
15
+
16
+ export type ParentViewProps = ViewProps & {
17
+ barStyle?: StatusBarStyle;
18
+ /** Screen background. Defaults to the active theme's primary background. */
19
+ backgroundColor?: string;
20
+ topInset?: boolean;
21
+ rightInset?: boolean;
22
+ leftInset?: boolean;
23
+ bottomInset?: boolean;
24
+ shouldDismissKeyboardOnTap?: boolean;
25
+ };
26
+
27
+ export const ParentView = ({
28
+ barStyle = "light-content",
29
+ children,
30
+ backgroundColor,
31
+ topInset = true,
32
+ rightInset = true,
33
+ leftInset = true,
34
+ bottomInset = true,
35
+ shouldDismissKeyboardOnTap = true,
36
+ style,
37
+ ...rest
38
+ }: ParentViewProps) => {
39
+ const { theme } = useUnistyles();
40
+ const insets = useSafeAreaInsets();
41
+
42
+ const edges = [
43
+ ...(topInset ? (["top"] as const) : []),
44
+ ...(rightInset ? (["right"] as const) : []),
45
+ ...(leftInset ? (["left"] as const) : []),
46
+ ];
47
+
48
+ // v2 mapping: light-content status bar sits on the brand color, everything
49
+ // else on the plain background. background.primary rather than the literal
50
+ // `white` token, which stays #ffffff in both themes and would paint a white
51
+ // strip above a dark screen.
52
+ const statusBarColor =
53
+ barStyle === "light-content"
54
+ ? theme.colors.primary
55
+ : theme.colors.background.primary;
56
+
57
+ const contentBackground = backgroundColor ?? theme.colors.background.primary;
58
+
59
+ return (
60
+ <SafeAreaView
61
+ edges={edges}
62
+ style={[styles.fill, { backgroundColor: statusBarColor }]}
63
+ >
64
+ <StatusBar backgroundColor={statusBarColor} barStyle={barStyle} />
65
+ <Container
66
+ {...(shouldDismissKeyboardOnTap
67
+ ? {
68
+ onTouchStart: event => {
69
+ // TextInput.State is the only architecture-agnostic way to
70
+ // know a tap landed outside the focused input — Fabric event
71
+ // targets don't carry the Paper fiber internals. (Carried
72
+ // over from v2's New Architecture fix.)
73
+ const focusedInput = TextInput.State.currentlyFocusedInput();
74
+ if (focusedInput && event.target !== focusedInput) {
75
+ Keyboard.dismiss();
76
+ }
77
+ },
78
+ }
79
+ : {})}
80
+ {...rest}
81
+ style={[
82
+ styles.fill,
83
+ { backgroundColor: contentBackground },
84
+ bottomInset && { paddingBottom: insets.bottom },
85
+ style,
86
+ ]}
87
+ >
88
+ {children}
89
+ </Container>
90
+ </SafeAreaView>
91
+ );
92
+ };
93
+
94
+ const styles = StyleSheet.create({
95
+ fill: {
96
+ flex: 1,
97
+ },
98
+ });