react-native-molecules 0.5.0-beta.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 (355) hide show
  1. package/components/Accordion/Accordion.tsx +89 -0
  2. package/components/Accordion/AccordionItem.tsx +94 -0
  3. package/components/Accordion/AccordionItemContent.tsx +17 -0
  4. package/components/Accordion/AccordionItemHeader.tsx +122 -0
  5. package/components/Accordion/index.tsx +36 -0
  6. package/components/Accordion/utils.ts +62 -0
  7. package/components/ActivityIndicator/ActivityIndicator.ios.tsx +1 -0
  8. package/components/ActivityIndicator/ActivityIndicator.tsx +200 -0
  9. package/components/ActivityIndicator/AnimatedSpinner.tsx +121 -0
  10. package/components/ActivityIndicator/index.tsx +17 -0
  11. package/components/Appbar/AppbarActions.tsx +13 -0
  12. package/components/Appbar/AppbarBase.tsx +60 -0
  13. package/components/Appbar/AppbarCenterAligned.tsx +13 -0
  14. package/components/Appbar/AppbarLarge.tsx +13 -0
  15. package/components/Appbar/AppbarLeft.tsx +26 -0
  16. package/components/Appbar/AppbarMedium.tsx +13 -0
  17. package/components/Appbar/AppbarRight.tsx +40 -0
  18. package/components/Appbar/AppbarSmall.tsx +13 -0
  19. package/components/Appbar/AppbarTitle.tsx +49 -0
  20. package/components/Appbar/index.tsx +46 -0
  21. package/components/Appbar/types.ts +19 -0
  22. package/components/Appbar/utils.ts +127 -0
  23. package/components/Avatar/Avatar.tsx +189 -0
  24. package/components/Avatar/index.tsx +11 -0
  25. package/components/Avatar/utils.ts +35 -0
  26. package/components/Backdrop/Backdrop.tsx +18 -0
  27. package/components/Backdrop/index.tsx +11 -0
  28. package/components/Backdrop/types.ts +3 -0
  29. package/components/Backdrop/utils.ts +21 -0
  30. package/components/Badge/Badge.tsx +29 -0
  31. package/components/Badge/index.tsx +11 -0
  32. package/components/Badge/utils.ts +38 -0
  33. package/components/Button/Button.tsx +349 -0
  34. package/components/Button/index.tsx +11 -0
  35. package/components/Button/types.ts +5 -0
  36. package/components/Button/utils.ts +416 -0
  37. package/components/Card/Card.tsx +160 -0
  38. package/components/Card/CardActions.tsx +18 -0
  39. package/components/Card/CardContent.tsx +29 -0
  40. package/components/Card/CardHeader.tsx +31 -0
  41. package/components/Card/CardHeadline.tsx +15 -0
  42. package/components/Card/CardMedia.tsx +36 -0
  43. package/components/Card/CardSubhead.tsx +15 -0
  44. package/components/Card/CardText.tsx +13 -0
  45. package/components/Card/CardTypography.tsx +113 -0
  46. package/components/Card/index.tsx +38 -0
  47. package/components/Card/types.ts +1 -0
  48. package/components/Card/utils.ts +23 -0
  49. package/components/Checkbox/Checkbox.tsx +123 -0
  50. package/components/Checkbox/CheckboxBase.ios.tsx +86 -0
  51. package/components/Checkbox/CheckboxBase.tsx +216 -0
  52. package/components/Checkbox/index.tsx +11 -0
  53. package/components/Checkbox/types.ts +72 -0
  54. package/components/Checkbox/utils.ts +210 -0
  55. package/components/Chip/Chip.tsx +416 -0
  56. package/components/Chip/index.tsx +51 -0
  57. package/components/Chip/utils.ts +100 -0
  58. package/components/DatePickerDocked/DatePickerDocked.tsx +30 -0
  59. package/components/DatePickerDocked/DatePickerDockedHeader.tsx +129 -0
  60. package/components/DatePickerDocked/index.tsx +21 -0
  61. package/components/DatePickerDocked/types.ts +11 -0
  62. package/components/DatePickerDocked/utils.ts +155 -0
  63. package/components/DatePickerInline/AutoSizer.tsx +46 -0
  64. package/components/DatePickerInline/DatePickerContext.tsx +21 -0
  65. package/components/DatePickerInline/DatePickerInline.tsx +82 -0
  66. package/components/DatePickerInline/DatePickerInlineBase.tsx +181 -0
  67. package/components/DatePickerInline/DatePickerInlineHeader.tsx +108 -0
  68. package/components/DatePickerInline/Day.tsx +88 -0
  69. package/components/DatePickerInline/DayName.tsx +17 -0
  70. package/components/DatePickerInline/DayNames.tsx +32 -0
  71. package/components/DatePickerInline/DayRange.tsx +48 -0
  72. package/components/DatePickerInline/HeaderItem.tsx +111 -0
  73. package/components/DatePickerInline/Month.tsx +233 -0
  74. package/components/DatePickerInline/MonthPicker.tsx +174 -0
  75. package/components/DatePickerInline/Swiper.native.tsx +172 -0
  76. package/components/DatePickerInline/Swiper.tsx +172 -0
  77. package/components/DatePickerInline/SwiperUtils.ts +40 -0
  78. package/components/DatePickerInline/Week.tsx +67 -0
  79. package/components/DatePickerInline/YearPicker.tsx +139 -0
  80. package/components/DatePickerInline/dateUtils.tsx +334 -0
  81. package/components/DatePickerInline/index.tsx +41 -0
  82. package/components/DatePickerInline/types.ts +104 -0
  83. package/components/DatePickerInline/utils.ts +367 -0
  84. package/components/DatePickerInput/DatePickerInput.tsx +139 -0
  85. package/components/DatePickerInput/DatePickerInputModal.tsx +48 -0
  86. package/components/DatePickerInput/DatePickerInputWithoutModal.tsx +77 -0
  87. package/components/DatePickerInput/DateRangeInput.tsx +88 -0
  88. package/components/DatePickerInput/index.tsx +14 -0
  89. package/components/DatePickerInput/inputUtils.ts +138 -0
  90. package/components/DatePickerInput/types.ts +28 -0
  91. package/components/DatePickerInput/utils.ts +16 -0
  92. package/components/DatePickerModal/AnimatedCrossView.tsx +94 -0
  93. package/components/DatePickerModal/CalendarEdit.tsx +139 -0
  94. package/components/DatePickerModal/DatePickerModal.tsx +85 -0
  95. package/components/DatePickerModal/DatePickerModalContent.tsx +155 -0
  96. package/components/DatePickerModal/DatePickerModalContentHeader.tsx +213 -0
  97. package/components/DatePickerModal/DatePickerModalHeader.tsx +74 -0
  98. package/components/DatePickerModal/DatePickerModalHeaderBackground.tsx +13 -0
  99. package/components/DatePickerModal/index.tsx +20 -0
  100. package/components/DatePickerModal/types.ts +92 -0
  101. package/components/DatePickerModal/utils.ts +121 -0
  102. package/components/DateTimePicker/DateTimePicker.tsx +172 -0
  103. package/components/DateTimePicker/index.tsx +14 -0
  104. package/components/DateTimePicker/utils.ts +13 -0
  105. package/components/Dialog/Dialog.tsx +66 -0
  106. package/components/Dialog/DialogActions.tsx +89 -0
  107. package/components/Dialog/DialogContent.tsx +37 -0
  108. package/components/Dialog/DialogIcon.tsx +69 -0
  109. package/components/Dialog/DialogTitle.tsx +68 -0
  110. package/components/Dialog/index.tsx +37 -0
  111. package/components/Dialog/utils.ts +80 -0
  112. package/components/Drawer/Collapsible/DrawerCollapsible.tsx +22 -0
  113. package/components/Drawer/Collapsible/DrawerCollapsibleItem.tsx +38 -0
  114. package/components/Drawer/Collapsible/DrawerCollapsibleItemContent.tsx +16 -0
  115. package/components/Drawer/Collapsible/DrawerCollapsibleItemHeader.tsx +48 -0
  116. package/components/Drawer/Collapsible/index.tsx +23 -0
  117. package/components/Drawer/Collapsible/utils.ts +49 -0
  118. package/components/Drawer/Drawer.tsx +43 -0
  119. package/components/Drawer/DrawerContent.tsx +35 -0
  120. package/components/Drawer/DrawerFooter.tsx +27 -0
  121. package/components/Drawer/DrawerHeader.tsx +27 -0
  122. package/components/Drawer/DrawerItem.tsx +206 -0
  123. package/components/Drawer/DrawerItemGroup.tsx +82 -0
  124. package/components/Drawer/index.tsx +47 -0
  125. package/components/Drawer/types.ts +3 -0
  126. package/components/Drawer/utils.ts +8 -0
  127. package/components/ElementGroup/ElementGroup.tsx +139 -0
  128. package/components/ElementGroup/index.tsx +11 -0
  129. package/components/ElementGroup/utils.ts +25 -0
  130. package/components/FAB/FAB.tsx +176 -0
  131. package/components/FAB/index.tsx +12 -0
  132. package/components/FAB/types.ts +1 -0
  133. package/components/FAB/utils.ts +221 -0
  134. package/components/FilePicker/FilePicker.tsx +133 -0
  135. package/components/FilePicker/index.tsx +11 -0
  136. package/components/FilePicker/utils.ts +13 -0
  137. package/components/HelperText/HelperText.tsx +139 -0
  138. package/components/HelperText/index.tsx +11 -0
  139. package/components/HelperText/utils.ts +29 -0
  140. package/components/HorizontalDivider/HorizontalDivider.tsx +101 -0
  141. package/components/HorizontalDivider/index.tsx +13 -0
  142. package/components/Icon/CrossFadeIcon.tsx +116 -0
  143. package/components/Icon/Icon.tsx +41 -0
  144. package/components/Icon/iconFactory.tsx +23 -0
  145. package/components/Icon/index.tsx +11 -0
  146. package/components/Icon/types.ts +35 -0
  147. package/components/IconButton/IconButton.tsx +218 -0
  148. package/components/IconButton/index.tsx +11 -0
  149. package/components/IconButton/types.ts +1 -0
  150. package/components/IconButton/utils.ts +325 -0
  151. package/components/If/index.tsx +13 -0
  152. package/components/InputAddon/InputAddon.tsx +27 -0
  153. package/components/InputAddon/index.tsx +11 -0
  154. package/components/InputAddon/utils.ts +33 -0
  155. package/components/Link/Link.tsx +48 -0
  156. package/components/Link/index.tsx +11 -0
  157. package/components/Link/utils.ts +37 -0
  158. package/components/ListItem/ListItem.tsx +136 -0
  159. package/components/ListItem/ListItemDescription.tsx +25 -0
  160. package/components/ListItem/ListItemTitle.tsx +25 -0
  161. package/components/ListItem/index.tsx +18 -0
  162. package/components/ListItem/utils.ts +113 -0
  163. package/components/Menu/Menu.tsx +69 -0
  164. package/components/Menu/MenuDivider.tsx +13 -0
  165. package/components/Menu/MenuItem.tsx +128 -0
  166. package/components/Menu/index.tsx +19 -0
  167. package/components/Menu/utils.ts +92 -0
  168. package/components/Modal/Modal.tsx +261 -0
  169. package/components/Modal/index.tsx +11 -0
  170. package/components/Modal/utils.ts +45 -0
  171. package/components/NavigationRail/NavigationRail.tsx +32 -0
  172. package/components/NavigationRail/NavigationRailContent.tsx +25 -0
  173. package/components/NavigationRail/NavigationRailFooter.tsx +18 -0
  174. package/components/NavigationRail/NavigationRailHeader.tsx +18 -0
  175. package/components/NavigationRail/NavigationRailItem.tsx +226 -0
  176. package/components/NavigationRail/index.tsx +35 -0
  177. package/components/NavigationRail/utils.ts +170 -0
  178. package/components/NavigationStack/NavigationStack.tsx +85 -0
  179. package/components/NavigationStack/NavigationStackItem.tsx +60 -0
  180. package/components/NavigationStack/index.tsx +20 -0
  181. package/components/NavigationStack/utils.tsx +16 -0
  182. package/components/Popover/Popover.native.tsx +185 -0
  183. package/components/Popover/Popover.tsx +198 -0
  184. package/components/Popover/common.ts +459 -0
  185. package/components/Popover/index.ts +2 -0
  186. package/components/Portal/Portal.tsx +13 -0
  187. package/components/Portal/index.tsx +12 -0
  188. package/components/RadioButton/RadioButton.tsx +138 -0
  189. package/components/RadioButton/RadioButtonAndroid.tsx +188 -0
  190. package/components/RadioButton/RadioButtonGroup.tsx +98 -0
  191. package/components/RadioButton/RadioButtonIOS.tsx +106 -0
  192. package/components/RadioButton/RadioButtonItem.tsx +232 -0
  193. package/components/RadioButton/index.ts +27 -0
  194. package/components/RadioButton/utils.ts +164 -0
  195. package/components/Rating/Rating.tsx +149 -0
  196. package/components/Rating/RatingItem.tsx +125 -0
  197. package/components/Rating/index.tsx +13 -0
  198. package/components/Rating/utils.ts +38 -0
  199. package/components/Select/Select.tsx +1038 -0
  200. package/components/Select/index.ts +14 -0
  201. package/components/Select/types.ts +115 -0
  202. package/components/StateLayer/StateLayer.tsx +12 -0
  203. package/components/StateLayer/index.tsx +11 -0
  204. package/components/StateLayer/utils.ts +17 -0
  205. package/components/Surface/BackgroundContextWrapper.tsx +27 -0
  206. package/components/Surface/Surface.android.tsx +62 -0
  207. package/components/Surface/Surface.ios.tsx +123 -0
  208. package/components/Surface/Surface.tsx +48 -0
  209. package/components/Surface/index.tsx +12 -0
  210. package/components/Surface/utils.ts +106 -0
  211. package/components/Switch/Switch.ios.tsx +67 -0
  212. package/components/Switch/Switch.tsx +278 -0
  213. package/components/Switch/index.tsx +11 -0
  214. package/components/Switch/utils.ts +283 -0
  215. package/components/Tabs/TabItem.tsx +150 -0
  216. package/components/Tabs/TabLabel.tsx +84 -0
  217. package/components/Tabs/Tabs.tsx +398 -0
  218. package/components/Tabs/index.tsx +21 -0
  219. package/components/Tabs/utils.ts +126 -0
  220. package/components/Text/Text.tsx +23 -0
  221. package/components/Text/index.tsx +2 -0
  222. package/components/Text/textFactory.tsx +33 -0
  223. package/components/TextInput/InputLabel.tsx +181 -0
  224. package/components/TextInput/TextInput.tsx +693 -0
  225. package/components/TextInput/index.tsx +16 -0
  226. package/components/TextInput/types.ts +96 -0
  227. package/components/TextInput/utils.ts +544 -0
  228. package/components/TextInputWithMask/TextInputMask.tsx +57 -0
  229. package/components/TextInputWithMask/index.tsx +11 -0
  230. package/components/TextInputWithMask/utils.ts +56 -0
  231. package/components/TimePicker/AmPmSwitcher.tsx +99 -0
  232. package/components/TimePicker/AnalogClock.tsx +165 -0
  233. package/components/TimePicker/AnalogClockHours.tsx +163 -0
  234. package/components/TimePicker/AnalogClockMinutes.tsx +68 -0
  235. package/components/TimePicker/AnimatedClockSwitcher.tsx +72 -0
  236. package/components/TimePicker/DisplayModeContext.tsx +6 -0
  237. package/components/TimePicker/TimeInput.tsx +112 -0
  238. package/components/TimePicker/TimeInputs.tsx +148 -0
  239. package/components/TimePicker/TimePicker.tsx +130 -0
  240. package/components/TimePicker/index.tsx +19 -0
  241. package/components/TimePicker/timeUtils.ts +159 -0
  242. package/components/TimePicker/utils.ts +285 -0
  243. package/components/TimePickerField/TimePickerField.tsx +152 -0
  244. package/components/TimePickerField/index.tsx +14 -0
  245. package/components/TimePickerField/sanitizeTime.ts +85 -0
  246. package/components/TimePickerField/utils.ts +95 -0
  247. package/components/TimePickerModal/TimePickerModal.tsx +115 -0
  248. package/components/TimePickerModal/index.tsx +14 -0
  249. package/components/TimePickerModal/utils.ts +48 -0
  250. package/components/Tooltip/Tooltip.tsx +137 -0
  251. package/components/Tooltip/TooltipContent.tsx +12 -0
  252. package/components/Tooltip/TooltipTrigger.tsx +94 -0
  253. package/components/Tooltip/index.tsx +20 -0
  254. package/components/Tooltip/utils.ts +21 -0
  255. package/components/TouchableRipple/TouchableRipple.native.tsx +105 -0
  256. package/components/TouchableRipple/TouchableRipple.tsx +286 -0
  257. package/components/TouchableRipple/index.tsx +14 -0
  258. package/components/TouchableRipple/utils.ts +15 -0
  259. package/components/VerticalDivider/VerticalDivider.tsx +99 -0
  260. package/components/VerticalDivider/index.tsx +13 -0
  261. package/context-bridge/index.tsx +87 -0
  262. package/core/componentsRegistry.ts +164 -0
  263. package/core/index.tsx +2 -0
  264. package/fast-context/index.tsx +190 -0
  265. package/hocs/index.tsx +5 -0
  266. package/hocs/typedMemo.tsx +5 -0
  267. package/hocs/withKeyboardAccessibility.tsx +231 -0
  268. package/hocs/withPortal.tsx +16 -0
  269. package/hooks/createPsuedoHook.tsx +50 -0
  270. package/hooks/index.tsx +29 -0
  271. package/hooks/useActionState.native.tsx +22 -0
  272. package/hooks/useActionState.tsx +34 -0
  273. package/hooks/useActive.tsx +5 -0
  274. package/hooks/useBreakpoints.tsx +7 -0
  275. package/hooks/useColorMode.tsx +17 -0
  276. package/hooks/useContrastColor.ts +15 -0
  277. package/hooks/useControlledValue.tsx +68 -0
  278. package/hooks/useFilePicker.tsx +48 -0
  279. package/hooks/useFocus.tsx +5 -0
  280. package/hooks/useHandleNumberFormat.tsx +106 -0
  281. package/hooks/useHover.tsx +5 -0
  282. package/hooks/useKeyboardDismissable.ts +66 -0
  283. package/hooks/useLatest.tsx +9 -0
  284. package/hooks/useMediaQuery.tsx +64 -0
  285. package/hooks/useMergedRefs.ts +14 -0
  286. package/hooks/usePrevious.ts +13 -0
  287. package/hooks/useQueryFilter.tsx +35 -0
  288. package/hooks/useSearchable.tsx +74 -0
  289. package/hooks/useSubcomponents.tsx +59 -0
  290. package/hooks/useTheme.ts +3 -0
  291. package/hooks/useToggle.tsx +24 -0
  292. package/package.json +114 -0
  293. package/shortcuts-manager/EventsManager.tsx +121 -0
  294. package/shortcuts-manager/ShortcutsManager/ShortcutsManager.native.tsx +9 -0
  295. package/shortcuts-manager/ShortcutsManager/ShortcutsManager.tsx +58 -0
  296. package/shortcuts-manager/ShortcutsManager/index.tsx +2 -0
  297. package/shortcuts-manager/ShortcutsManager/utils.tsx +30 -0
  298. package/shortcuts-manager/index.tsx +6 -0
  299. package/shortcuts-manager/types.ts +24 -0
  300. package/shortcuts-manager/useIsKeyPressed/index.tsx +1 -0
  301. package/shortcuts-manager/useIsKeyPressed/useIsKeyPress.tsx +9 -0
  302. package/shortcuts-manager/useSetScopes/index.tsx +1 -0
  303. package/shortcuts-manager/useSetScopes/useSetScopes.native.tsx +9 -0
  304. package/shortcuts-manager/useSetScopes/useSetScopes.tsx +25 -0
  305. package/shortcuts-manager/useShortcut/index.tsx +2 -0
  306. package/shortcuts-manager/useShortcut/types.ts +3 -0
  307. package/shortcuts-manager/useShortcut/useShortcut.native.tsx +9 -0
  308. package/shortcuts-manager/useShortcut/useShortcut.tsx +61 -0
  309. package/shortcuts-manager/utils.ts +105 -0
  310. package/styles/index.ts +4 -0
  311. package/styles/overlay.ts +69 -0
  312. package/styles/shadow.ts +21 -0
  313. package/styles/themes/DarkTheme.tsx +98 -0
  314. package/styles/themes/LightTheme.tsx +212 -0
  315. package/styles/themes/tokens.ts +248 -0
  316. package/styles/utils.ts +11 -0
  317. package/types/index.ts +129 -0
  318. package/types/theme.ts +159 -0
  319. package/utils/DocumentPicker/documentPicker.native.ts +10 -0
  320. package/utils/DocumentPicker/documentPicker.ts +76 -0
  321. package/utils/DocumentPicker/index.ts +2 -0
  322. package/utils/DocumentPicker/types.ts +28 -0
  323. package/utils/addEventListener.tsx +51 -0
  324. package/utils/backgroundContext.ts +9 -0
  325. package/utils/color.ts +22 -0
  326. package/utils/compare/index.ts +54 -0
  327. package/utils/composeEventHandlers.ts +9 -0
  328. package/utils/createNumberMask/createNumberMask.ts +98 -0
  329. package/utils/createNumberMask/index.ts +5 -0
  330. package/utils/createSyntheticEvent.ts +31 -0
  331. package/utils/date-fns.ts +7 -0
  332. package/utils/dateTimePicker.ts +5 -0
  333. package/utils/escapeRegex.ts +9 -0
  334. package/utils/extractTextStyles.ts +52 -0
  335. package/utils/formatNumberWithMask/formatNumberWithMask.ts +26 -0
  336. package/utils/formatNumberWithMask/formatWithMask.ts +119 -0
  337. package/utils/formatNumberWithMask/index.ts +6 -0
  338. package/utils/getCursorStyle/getCursorStyle.native.ts +1 -0
  339. package/utils/getCursorStyle/getCursorStyle.ts +1 -0
  340. package/utils/getCursorStyle/index.ts +1 -0
  341. package/utils/getOS/getOS.native.ts +7 -0
  342. package/utils/getOS/getOS.ts +24 -0
  343. package/utils/getOS/index.ts +1 -0
  344. package/utils/getyearRange.ts +5 -0
  345. package/utils/index.ts +20 -0
  346. package/utils/lodash.ts +50 -0
  347. package/utils/mergeRefs.ts +13 -0
  348. package/utils/normalizeBorderRadiuses.ts +24 -0
  349. package/utils/normalizeSpacings.ts +110 -0
  350. package/utils/normalizeToNumberString/index.ts +4 -0
  351. package/utils/normalizeToNumberString/normalizeToNumberString.ts +48 -0
  352. package/utils/repository.ts +103 -0
  353. package/utils/resolveColorMode.ts +9 -0
  354. package/utils/resolveStateVariant.ts +26 -0
  355. package/utils/tokenStylesParser.ts +7 -0
@@ -0,0 +1,693 @@
1
+ import React, {
2
+ forwardRef,
3
+ memo,
4
+ type PropsWithoutRef,
5
+ type ReactNode,
6
+ type RefObject,
7
+ useCallback,
8
+ useContext,
9
+ useEffect,
10
+ useImperativeHandle,
11
+ useMemo,
12
+ useRef,
13
+ useState,
14
+ } from 'react';
15
+ import type {
16
+ BlurEvent,
17
+ FocusEvent,
18
+ LayoutChangeEvent,
19
+ StyleProp,
20
+ TextInputProps,
21
+ TextStyle,
22
+ ViewProps,
23
+ ViewStyle,
24
+ } from 'react-native';
25
+ import { Animated, I18nManager, Platform, TextInput as NativeTextInput, View } from 'react-native';
26
+ import { StyleSheet } from 'react-native-unistyles';
27
+
28
+ import { useActionState } from '../../hooks/useActionState';
29
+ import useControlledValue from '../../hooks/useControlledValue';
30
+ import useLatest from '../../hooks/useLatest';
31
+ import type { WithElements } from '../../types';
32
+ import { BackgroundContext } from '../../utils';
33
+ import { createSyntheticEvent, resolveStateVariant } from '../../utils';
34
+ import { HelperText } from '../HelperText';
35
+ import { StateLayer } from '../StateLayer';
36
+ import InputLabel from './InputLabel';
37
+ import type { RenderProps, TextInputLabelProp, TextInputSize, TextInputVariant } from './types';
38
+ import { getInputMinHeight, styles } from './utils';
39
+
40
+ const BLUR_ANIMATION_DURATION = 180;
41
+ const FOCUS_ANIMATION_DURATION = 150;
42
+
43
+ export type ElementProps = {
44
+ color: string;
45
+ forceFocus: () => void;
46
+ focused: boolean;
47
+ };
48
+
49
+ type Element = ReactNode | ((props: ElementProps) => ReactNode);
50
+
51
+ export type Props = Omit<TextInputProps, 'ref'> &
52
+ WithElements<Element> & {
53
+ ref?: RefObject<TextInputHandles | null>;
54
+ /**
55
+ * Variant of the TextInput.
56
+ * - `flat` - flat input with an underline.
57
+ * - `outlined` - input with an outline.
58
+ *
59
+ * In `outlined` variant, the background color of the label is derived from `colors?.background` in theme or the `backgroundColor` style.
60
+ * This component render TextInputOutlined or TextInputFlat based on that props
61
+ */
62
+ variant?: TextInputVariant;
63
+ /**
64
+ * If true, user won't be able to interact with the component.
65
+ */
66
+ disabled?: boolean;
67
+ /**
68
+ * The text or component to use for the floating label.
69
+ */
70
+ label?: TextInputLabelProp;
71
+ /**
72
+ * Placeholder for the input.
73
+ */
74
+ placeholder?: string;
75
+ /**
76
+ * Whether to style the TextInput with error style.
77
+ */
78
+ error?: boolean;
79
+ /**
80
+ * Callback that is called when the text input's text changes. Changed text is passed as an argument to the callback handler.
81
+ */
82
+ onChangeText?: Function;
83
+ /**
84
+ * Selection color of the input
85
+ */
86
+ selectionColor?: string;
87
+ /**
88
+ * Inactive underline color of the input.
89
+ */
90
+ underlineColor?: string;
91
+ /**
92
+ * Active underline color of the input.
93
+ */
94
+ activeUnderlineColor?: string;
95
+ /**
96
+ * Inactive outline color of the input.
97
+ */
98
+ outlineColor?: string;
99
+ /**
100
+ * Active outline color of the input.
101
+ */
102
+ activeOutlineColor?: string;
103
+ /**
104
+ * Sets min height with densed layout. For `TextInput` in `flat` mode
105
+ * height is `64dp` or in dense layout - `52dp` with label or `40dp` without label.
106
+ * For `TextInput` in `outlined` mode
107
+ * height is `56dp` or in dense layout - `40dp` regardless of label.
108
+ * When you apply `height` prop in style the `dense` prop affects only `paddingVertical` inside `TextInput`
109
+ */
110
+ size?: TextInputSize;
111
+ /**
112
+ * Whether the input can have multiple lines.
113
+ */
114
+ multiline?: boolean;
115
+ /**
116
+ * The number of lines to show in the input (Android only).
117
+ */
118
+ numberOfLines?: number;
119
+ /**
120
+ * The Supporting Text below the TextInput
121
+ */
122
+ supportingText?: string;
123
+ /**
124
+ * To display the required indicator in Supporting Text and in the Label
125
+ */
126
+ required?: boolean;
127
+ /**
128
+ *
129
+ * Callback to render a custom input component such as `react-native-text-input-mask`
130
+ * instead of the default `TextInput` component from `react-native`.
131
+ *
132
+ * Example:
133
+ * ```js
134
+ * <TextInput
135
+ * label="Phone number"
136
+ * render={props =>
137
+ * <TextInputMask
138
+ * {...props}
139
+ * mask="+[00] [000] [000] [000]"
140
+ * />
141
+ * }
142
+ * />
143
+ * ```
144
+ */
145
+ render?: (props: RenderProps) => ReactNode;
146
+ /**
147
+ * Value of the text input.
148
+ */
149
+ value?: string;
150
+ /**
151
+ * Pass `fontSize` prop to modify the font size inside `TextInput`.
152
+ * Pass `height` prop to set `TextInput` height.
153
+ * Pass `backgroundColor` prop to set `TextInput` backgroundColor.
154
+ */
155
+ style?: StyleProp<TextStyle>;
156
+ /**
157
+ * Style of the Input Container
158
+ */
159
+ inputContainerStyle?: StyleProp<ViewStyle>;
160
+ /**
161
+ * Style of the Input
162
+ */
163
+ inputStyle?: StyleProp<TextStyle>;
164
+ /**
165
+ * Style of the rightElement
166
+ */
167
+ rightElementStyle?: StyleProp<TextStyle>;
168
+ /**
169
+ * Style of the leftElement
170
+ */
171
+ leftElementStyle?: StyleProp<TextStyle>;
172
+ /**
173
+ * props for the stateLayer
174
+ */
175
+ stateLayerProps?: PropsWithoutRef<ViewProps>;
176
+ /**
177
+ * testID to be used on tests.
178
+ */
179
+ testID?: string;
180
+ };
181
+
182
+ export type TextInputHandles = Pick<
183
+ NativeTextInput,
184
+ 'focus' | 'clear' | 'blur' | 'isFocused' | 'setNativeProps'
185
+ >;
186
+
187
+ const animationScale = 1;
188
+ const minimizedLabelFontSize = 12;
189
+ const maximizedLabelFontSize = 16;
190
+ const labelWiggleXOffset = 4;
191
+
192
+ const DefaultComponent = (props: RenderProps) => <NativeTextInput {...props} />;
193
+
194
+ const TextInput = forwardRef<TextInputHandles, Props>(
195
+ (
196
+ {
197
+ variant = 'flat',
198
+ size = 'md',
199
+ disabled = false,
200
+ error: errorProp = false,
201
+ multiline = false,
202
+ editable = true,
203
+ required = false,
204
+ maxFontSizeMultiplier = 15,
205
+ supportingText,
206
+ selectionColor: selectionColorProp,
207
+ underlineColor: underlineColorProp,
208
+ activeUnderlineColor: activeUnderlineColorProp,
209
+ outlineColor: outlineColorProp,
210
+ activeOutlineColor: activeOutlineColorProp,
211
+ placeholderTextColor: placeholderTextColorProp,
212
+ style,
213
+ inputContainerStyle,
214
+ inputStyle,
215
+ stateLayerProps = {},
216
+ left,
217
+ right,
218
+ render = DefaultComponent,
219
+ onBlur,
220
+ leftElementStyle,
221
+ rightElementStyle,
222
+ ...rest
223
+ }: Props,
224
+ ref,
225
+ ) => {
226
+ const { hovered, actionsRef } = useActionState({ actionsToListen: ['hover'] });
227
+ const isControlled = rest.value !== undefined;
228
+ const validInputValue = isControlled ? rest.value : rest.defaultValue;
229
+ const floatingLabelVerticalOffset = variant === 'flat' ? 16 : 0;
230
+
231
+ const { current: labelAnimation } = useRef<Animated.Value>(
232
+ new Animated.Value(validInputValue ? 0 : 1),
233
+ );
234
+ const { current: errorAnimation } = useRef<Animated.Value>(
235
+ new Animated.Value(errorProp ? 1 : 0),
236
+ );
237
+ const [focused, setFocused] = useState<boolean>(false);
238
+ const [placeholder, setPlaceholder] = useState<string | undefined>('');
239
+ // Use value from props instead of local state when input is controlled
240
+ const [value, onChangeValue] = useControlledValue({
241
+ value: rest.value,
242
+ defaultValue: rest.defaultValue,
243
+ onChange: rest.onChangeText,
244
+ disabled: !editable || disabled,
245
+ });
246
+
247
+ const onBlurRef = useLatest(onBlur);
248
+
249
+ const state = resolveStateVariant({
250
+ errorDisabled: errorProp && disabled,
251
+ disabled,
252
+ errorFocusedAndHovered: errorProp && hovered && focused,
253
+ errorFocused: errorProp && focused,
254
+ errorHovered: errorProp && hovered,
255
+ hoveredAndFocused: hovered && focused,
256
+ hovered,
257
+ focused: focused,
258
+ error: !!errorProp,
259
+ }) as any;
260
+
261
+ styles.useVariants({
262
+ variant: variant as any,
263
+ state,
264
+ size,
265
+ });
266
+
267
+ const [labelLayout, setLabelLayout] = useState<{
268
+ measured: boolean;
269
+ width: number;
270
+ height: number;
271
+ }>({
272
+ measured: false,
273
+ width: 0,
274
+ height: 0,
275
+ });
276
+
277
+ const [leftElementLayout, setElementLayout] = useState<{
278
+ measured: boolean;
279
+ width: number;
280
+ height: number;
281
+ }>({
282
+ measured: false,
283
+ width: 0,
284
+ height: 0,
285
+ });
286
+
287
+ const timer = useRef<NodeJS.Timeout | undefined>(undefined);
288
+ const inputRefLocal = useRef<NativeTextInput>(null);
289
+
290
+ useImperativeHandle(ref, () => inputRefLocal.current!);
291
+
292
+ const { backgroundColor: parentBackground } = useContext(BackgroundContext);
293
+ const hasActiveOutline = focused || errorProp;
294
+
295
+ useEffect(() => {
296
+ // When the input has an error, we wiggle the label and apply error styles
297
+ if (errorProp) {
298
+ // show error
299
+ Animated.timing(errorAnimation, {
300
+ toValue: 1,
301
+ duration: FOCUS_ANIMATION_DURATION * animationScale,
302
+ // To prevent this - https://github.com/callstack/react-native-paper/issues/941
303
+ useNativeDriver: true,
304
+ }).start();
305
+
306
+ return;
307
+ }
308
+
309
+ // hide error
310
+ Animated.timing(errorAnimation, {
311
+ toValue: 0,
312
+ duration: BLUR_ANIMATION_DURATION * animationScale,
313
+ // To prevent this - https://github.com/callstack/react-native-paper/issues/941
314
+ useNativeDriver: true,
315
+ }).start();
316
+ }, [errorProp, errorAnimation]);
317
+
318
+ useEffect(() => {
319
+ // Show placeholder text only if the input is focused, or there's no label
320
+ // We don't show placeholder if there's a label because the label acts as placeholder
321
+ // When focused, the label moves up, so we can show a placeholder
322
+ if (focused || !rest.label) {
323
+ // Set the placeholder in a delay to offset the label animation
324
+ // If we show it immediately, they'll overlap and look ugly
325
+ timer.current = setTimeout(
326
+ () => setPlaceholder(rest.placeholder),
327
+ 50,
328
+ ) as unknown as NodeJS.Timeout;
329
+ } else {
330
+ // hidePlaceholder
331
+ setPlaceholder('');
332
+ }
333
+
334
+ return () => {
335
+ if (timer.current) {
336
+ clearTimeout(timer.current);
337
+ }
338
+ };
339
+ }, [focused, rest.label, rest.placeholder]);
340
+
341
+ const hasValue = !!value || focused;
342
+
343
+ useEffect(() => {
344
+ // The label should be minimized if the text input is focused, or has text
345
+ // In minimized mode, the label moves up and becomes small
346
+ // workaround for animated regression for react native > 0.61
347
+ // https://github.com/callstack/react-native-paper/pull/1440
348
+ if (hasValue) {
349
+ // minimize label
350
+ Animated.timing(labelAnimation, {
351
+ toValue: 0,
352
+ duration: BLUR_ANIMATION_DURATION * animationScale,
353
+ // To prevent this - https://github.com/callstack/react-native-paper/issues/941
354
+ useNativeDriver: true,
355
+ }).start();
356
+ } else {
357
+ // restore label
358
+ Animated.timing(labelAnimation, {
359
+ toValue: 1,
360
+ duration: FOCUS_ANIMATION_DURATION * animationScale,
361
+ // To prevent this - https://github.com/callstack/react-native-paper/issues/941
362
+ useNativeDriver: true,
363
+ }).start();
364
+ }
365
+ }, [focused, hasValue, labelAnimation]);
366
+
367
+ const handleFocus = useCallback(
368
+ (args: FocusEvent) => {
369
+ if (disabled || !editable) {
370
+ return;
371
+ }
372
+
373
+ setFocused(true);
374
+
375
+ rest.onFocus?.(args);
376
+ },
377
+ [disabled, editable, rest],
378
+ );
379
+
380
+ const handleBlur = useCallback(
381
+ (args: BlurEvent) => {
382
+ if (!editable) {
383
+ return;
384
+ }
385
+
386
+ setFocused(false);
387
+ onBlur?.(args);
388
+ },
389
+ [editable, onBlur],
390
+ );
391
+
392
+ const handleLayoutAnimatedText = useCallback((e: LayoutChangeEvent) => {
393
+ setLabelLayout({
394
+ width: e.nativeEvent.layout.width,
395
+ height: e.nativeEvent.layout.height,
396
+ measured: true,
397
+ });
398
+ }, []);
399
+
400
+ const handleLayoutLeftElement = useCallback((e: LayoutChangeEvent) => {
401
+ setElementLayout({
402
+ width: e.nativeEvent.layout.width,
403
+ height: e.nativeEvent.layout.height,
404
+ measured: true,
405
+ });
406
+ }, []);
407
+
408
+ const forceFocus = useCallback(() => inputRefLocal.current?.focus(), []);
409
+
410
+ const inputMinHeight = getInputMinHeight(variant, size);
411
+
412
+ // This is because of a bug in react 18 doesn't trigger onBlur when the component is unmounted // we can remove it when it's fixed
413
+ useEffect(() => {
414
+ const isVersion18 =
415
+ typeof React.version === 'string' ? +React.version.split('.')[0] >= 18 : false;
416
+
417
+ const _onBlurRef = onBlurRef;
418
+ const input = inputRefLocal.current;
419
+
420
+ return () => {
421
+ if (!isVersion18 || !input?.isFocused() || Platform.OS !== 'web') return;
422
+
423
+ const event = new Event('blur', { bubbles: true });
424
+ Object.defineProperty(event, 'target', {
425
+ writable: false,
426
+ value: input,
427
+ });
428
+ const syntheticEvent = createSyntheticEvent(
429
+ event,
430
+ ) as React.ChangeEvent<HTMLInputElement>;
431
+ _onBlurRef.current?.(syntheticEvent as any);
432
+ };
433
+ }, [onBlurRef]);
434
+
435
+ const componentStyles = styles.root;
436
+
437
+ const labelWidth = labelLayout.width;
438
+ const labelHeight = labelLayout.height;
439
+ const labelHalfWidth = labelWidth / 2;
440
+ const labelScale =
441
+ minimizedLabelFontSize / (componentStyles.fontSize || maximizedLabelFontSize);
442
+ const baseLabelTranslateX =
443
+ (I18nManager.isRTL ? 1 : -1) *
444
+ (labelScale - 1 + labelHalfWidth - (labelScale * labelWidth) / 2);
445
+
446
+ // const normalizedLeftElementMarginRight = normalizeSpacings(
447
+ // styles.leftElement,
448
+ // 'marginRight',
449
+ // );
450
+
451
+ const baseLabelTranslateXOutline =
452
+ baseLabelTranslateX - leftElementLayout.width - (left ? 0 : 0);
453
+
454
+ const backgroundColor =
455
+ styles.container?.backgroundColor || componentStyles.backgroundColor;
456
+ // const viableRadiuses = normalizeBorderRadiuses(componentStyles);
457
+ const finalHeight =
458
+ (+(componentStyles.height ?? 0) > 0 ? componentStyles.height : +labelHeight) ?? 0;
459
+ const inputHeight = finalHeight < inputMinHeight ? inputMinHeight : finalHeight;
460
+ const hasLabel = !!rest.label;
461
+
462
+ const computedStyles = useMemo(
463
+ () => ({
464
+ activeIndicator: styles.activeIndicator,
465
+ fontSize: componentStyles.fontSize,
466
+ fontWeight: componentStyles.fontWeight,
467
+ height: componentStyles.height,
468
+ textAlign: componentStyles.textAlign,
469
+ backgroundColor,
470
+ activeColor: (styles as any).root.activeColor,
471
+ baseLabelTranslateX:
472
+ variant === 'outlined' ? baseLabelTranslateXOutline : baseLabelTranslateX,
473
+ labelScale,
474
+ selectionColor: selectionColorProp || (styles as any).root.activeColor,
475
+ underlineColor: underlineColorProp,
476
+ activeUnderlineColor: activeUnderlineColorProp,
477
+ outlineColor: outlineColorProp,
478
+ activeOutlineColor: activeOutlineColorProp,
479
+ placeholderTextColor: placeholderTextColorProp || styles.placeholder?.color,
480
+ floatingLabelVerticalOffset,
481
+ labelWiggleXOffset,
482
+ textInputStyle: [
483
+ styles.inputText,
484
+ variant === 'flat' && hasLabel && { paddingTop: 12 },
485
+ !multiline || (multiline && componentStyles.height)
486
+ ? { height: inputHeight || labelHeight }
487
+ : {},
488
+ multiline && variant === 'outlined' && { paddingTop: 12 },
489
+ {
490
+ textAlignVertical: multiline ? 'top' : 'center',
491
+ textAlign: componentStyles.textAlign
492
+ ? componentStyles.textAlign
493
+ : I18nManager.isRTL
494
+ ? 'right'
495
+ : 'left',
496
+ },
497
+ Platform.OS === 'ios' && !multiline
498
+ ? { lineHeight: undefined, textAlign: undefined }
499
+ : {},
500
+ Platform.OS === 'web' && { outline: 'none' },
501
+ inputStyle,
502
+ ],
503
+ inputContainerStyle: [
504
+ styles.labelContainer,
505
+ {
506
+ minHeight: componentStyles.height || labelHeight,
507
+ },
508
+ inputContainerStyle,
509
+ ],
510
+ underlineStyle: [
511
+ styles.underline,
512
+ styles.activeIndicator,
513
+ hasActiveOutline && activeOutlineColorProp
514
+ ? {
515
+ backgroundColor: hasActiveOutline
516
+ ? activeUnderlineColorProp
517
+ : underlineColorProp,
518
+ }
519
+ : {},
520
+ ],
521
+ outlineStyle: [
522
+ styles.outline,
523
+ hasActiveOutline && activeOutlineColorProp
524
+ ? {
525
+ borderColor: hasActiveOutline
526
+ ? activeOutlineColorProp
527
+ : outlineColorProp,
528
+ }
529
+ : {},
530
+ // viableRadiuses,
531
+ {},
532
+ ],
533
+ patchContainer: [
534
+ StyleSheet.absoluteFill,
535
+ {
536
+ backgroundColor,
537
+ },
538
+ styles.patchContainer,
539
+ ],
540
+ stateLayerStyle: [styles.stateLayer, stateLayerProps?.style],
541
+ }),
542
+ // forcing useMemo to recompute when state, size or variant change
543
+ // eslint-disable-next-line
544
+ [
545
+ hasLabel,
546
+ state,
547
+ size,
548
+ componentStyles,
549
+ backgroundColor,
550
+ variant,
551
+ parentBackground,
552
+ baseLabelTranslateXOutline,
553
+ baseLabelTranslateX,
554
+ labelScale,
555
+ selectionColorProp,
556
+ underlineColorProp,
557
+ activeUnderlineColorProp,
558
+ outlineColorProp,
559
+ activeOutlineColorProp,
560
+ placeholderTextColorProp,
561
+ floatingLabelVerticalOffset,
562
+ multiline,
563
+ inputHeight,
564
+ labelHeight,
565
+ inputStyle,
566
+ inputContainerStyle,
567
+ hasActiveOutline,
568
+ stateLayerProps?.style,
569
+ ],
570
+ );
571
+
572
+ return (
573
+ <>
574
+ <View ref={actionsRef} style={[styles.container, style]}>
575
+ {variant === 'flat' && (
576
+ <>
577
+ <Animated.View
578
+ testID={rest.testID && `${rest.testID}--text-input-underline`}
579
+ style={computedStyles.underlineStyle}
580
+ />
581
+
582
+ <StateLayer
583
+ testID={rest.testID && `${rest.testID}--stateLayer`}
584
+ {...stateLayerProps}
585
+ style={computedStyles.stateLayerStyle}
586
+ />
587
+ </>
588
+ )}
589
+ {variant === 'outlined' && (
590
+ <Animated.View
591
+ testID="text-input-outline"
592
+ pointerEvents="none"
593
+ style={computedStyles.outlineStyle}
594
+ />
595
+ )}
596
+
597
+ {left && (
598
+ <View
599
+ style={[styles.leftElement, leftElementStyle]}
600
+ onLayout={handleLayoutLeftElement}
601
+ testID={rest.testID && `${rest.testID}--text-input-left-element`}>
602
+ {typeof left === 'function'
603
+ ? left?.({ color: computedStyles.activeColor, forceFocus, focused })
604
+ : left}
605
+ </View>
606
+ )}
607
+
608
+ <View
609
+ style={computedStyles.inputContainerStyle}
610
+ testID={rest.testID && `${rest.testID}-${variant}`}>
611
+ {Platform.OS !== 'android' &&
612
+ multiline &&
613
+ !!rest.label &&
614
+ variant === 'flat' && (
615
+ // Workaround for: https://github.com/callstack/react-native-paper/issues/2799
616
+ // Patch for a multiline TextInput with fixed height, which allow to avoid covering input label with its value.
617
+ <View
618
+ testID={rest.testID && `${rest.testID}--patch-container`}
619
+ pointerEvents="none"
620
+ style={computedStyles.patchContainer}
621
+ />
622
+ )}
623
+
624
+ {variant !== 'plain' && (
625
+ <InputLabel
626
+ hasValue={!!value}
627
+ focused={focused}
628
+ labelAnimation={labelAnimation}
629
+ errorAnimation={errorAnimation}
630
+ labelLayout={labelLayout}
631
+ label={rest.label}
632
+ floatingStyle={styles.floatingLabel}
633
+ floatingLabelVerticalOffset={
634
+ computedStyles.floatingLabelVerticalOffset
635
+ }
636
+ required={required}
637
+ onLayoutAnimatedText={handleLayoutAnimatedText}
638
+ error={errorProp}
639
+ baseLabelTranslateX={computedStyles.baseLabelTranslateX}
640
+ labelScale={computedStyles.labelScale}
641
+ wiggleOffsetX={computedStyles.labelWiggleXOffset}
642
+ maxFontSizeMultiplier={maxFontSizeMultiplier}
643
+ testID={rest.testID}
644
+ style={styles.labelText}
645
+ />
646
+ )}
647
+
648
+ {render({
649
+ testID: rest.testID,
650
+ ...rest,
651
+ style: computedStyles.textInputStyle,
652
+ ref: inputRefLocal,
653
+ onChangeText: onChangeValue,
654
+ placeholder: rest.label ? placeholder : rest.placeholder,
655
+ placeholderTextColor: computedStyles.placeholderTextColor,
656
+ editable: !disabled && editable,
657
+ selectionColor: computedStyles.selectionColor,
658
+ onFocus: handleFocus,
659
+ onBlur: handleBlur,
660
+ underlineColorAndroid: 'transparent',
661
+ multiline,
662
+ size,
663
+ })}
664
+ </View>
665
+
666
+ {right && (
667
+ <View
668
+ style={[styles.rightElement, rightElementStyle]}
669
+ testID={rest.testID && `${rest.testID}--text-input-right-element`}>
670
+ {typeof right === 'function'
671
+ ? right?.({
672
+ color: computedStyles.activeColor,
673
+ forceFocus,
674
+ focused,
675
+ })
676
+ : right}
677
+ </View>
678
+ )}
679
+ </View>
680
+
681
+ {supportingText && (
682
+ <HelperText
683
+ variant={errorProp ? 'error' : 'info'}
684
+ style={styles.supportingText}>
685
+ {supportingText}
686
+ </HelperText>
687
+ )}
688
+ </>
689
+ );
690
+ },
691
+ );
692
+
693
+ export default memo(TextInput);