@ccrf01/react-native-template 0.0.16

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 (233) hide show
  1. package/.github/workflows/npm-publish.yml +28 -0
  2. package/.vscode/settings.json +15 -0
  3. package/README.md +58 -0
  4. package/package.json +16 -0
  5. package/template/.bundle/config +2 -0
  6. package/template/.env.development +1 -0
  7. package/template/.env.production +1 -0
  8. package/template/.eslintrc.js +392 -0
  9. package/template/.java-version +1 -0
  10. package/template/.prettierrc.js +5 -0
  11. package/template/.ruby-version +1 -0
  12. package/template/.watchmanconfig +1 -0
  13. package/template/Gemfile +23 -0
  14. package/template/Gemfile.lock +330 -0
  15. package/template/README.md +97 -0
  16. package/template/ReactotronConfig.js +17 -0
  17. package/template/__tests__/App.test.tsx +13 -0
  18. package/template/_gitignore +75 -0
  19. package/template/_node-version +1 -0
  20. package/template/android/app/build.gradle +162 -0
  21. package/template/android/app/debug.keystore +0 -0
  22. package/template/android/app/proguard-rules.pro +10 -0
  23. package/template/android/app/src/debug/AndroidManifest.xml +9 -0
  24. package/template/android/app/src/main/AndroidManifest.xml +26 -0
  25. package/template/android/app/src/main/java/com/projectname/MainActivity.kt +30 -0
  26. package/template/android/app/src/main/java/com/projectname/MainApplication.kt +27 -0
  27. package/template/android/app/src/main/res/drawable/rn_edit_text_material.xml +37 -0
  28. package/template/android/app/src/main/res/mipmap-hdpi/ic_launcher.png +0 -0
  29. package/template/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png +0 -0
  30. package/template/android/app/src/main/res/mipmap-mdpi/ic_launcher.png +0 -0
  31. package/template/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png +0 -0
  32. package/template/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png +0 -0
  33. package/template/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png +0 -0
  34. package/template/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png +0 -0
  35. package/template/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png +0 -0
  36. package/template/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png +0 -0
  37. package/template/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png +0 -0
  38. package/template/android/app/src/main/res/values/strings.xml +3 -0
  39. package/template/android/app/src/main/res/values/styles.xml +9 -0
  40. package/template/android/build.gradle +37 -0
  41. package/template/android/gradle/wrapper/gradle-wrapper.jar +0 -0
  42. package/template/android/gradle/wrapper/gradle-wrapper.properties +7 -0
  43. package/template/android/gradle.properties +48 -0
  44. package/template/android/gradlew +251 -0
  45. package/template/android/gradlew.bat +99 -0
  46. package/template/android/keystore/keystore.properties +4 -0
  47. package/template/android/settings.gradle +6 -0
  48. package/template/android/version.properties +2 -0
  49. package/template/app.json +4 -0
  50. package/template/babel.config.js +38 -0
  51. package/template/fastlane/Fastfile +136 -0
  52. package/template/fastlane/Pluginfile +9 -0
  53. package/template/fastlane/README.md +46 -0
  54. package/template/index.js +9 -0
  55. package/template/ios/.xcode.env +11 -0
  56. package/template/ios/Podfile +35 -0
  57. package/template/ios/Podfile.lock +3669 -0
  58. package/template/ios/ProjectName/AppDelegate.swift +48 -0
  59. package/template/ios/ProjectName/Images.xcassets/AppIcon.appiconset/Contents.json +53 -0
  60. package/template/ios/ProjectName/Images.xcassets/Contents.json +6 -0
  61. package/template/ios/ProjectName/Info.plist +66 -0
  62. package/template/ios/ProjectName/LaunchScreen.storyboard +47 -0
  63. package/template/ios/ProjectName/PrivacyInfo.xcprivacy +47 -0
  64. package/template/ios/ProjectName.xcodeproj/project.pbxproj +482 -0
  65. package/template/ios/ProjectName.xcodeproj/xcshareddata/xcschemes/ProjectName.xcscheme +88 -0
  66. package/template/ios/ProjectName.xcworkspace/contents.xcworkspacedata +10 -0
  67. package/template/jest.config.js +3 -0
  68. package/template/metro.config.js +20 -0
  69. package/template/package-lock.json +17073 -0
  70. package/template/package.json +156 -0
  71. package/template/src/@types/emotion.d.ts +12 -0
  72. package/template/src/@types/env.d.ts +3 -0
  73. package/template/src/@types/redux-persist-transform-immutable.d.ts +1 -0
  74. package/template/src/@types/typing.d.ts +17 -0
  75. package/template/src/AppContainer.tsx +109 -0
  76. package/template/src/assets/images/143.png +0 -0
  77. package/template/src/assets/index.ts +13 -0
  78. package/template/src/components/basic/BackButton/index.tsx +34 -0
  79. package/template/src/components/basic/Body/index.tsx +68 -0
  80. package/template/src/components/basic/Button/index.tsx +181 -0
  81. package/template/src/components/basic/Button/utils.ts +78 -0
  82. package/template/src/components/basic/ButtonGroup/index.tsx +182 -0
  83. package/template/src/components/basic/Card/index.tsx +3 -0
  84. package/template/src/components/basic/Container/index.tsx +38 -0
  85. package/template/src/components/basic/Content/index.tsx +87 -0
  86. package/template/src/components/basic/DropDown/index.tsx +354 -0
  87. package/template/src/components/basic/ExpandableOverlay/index.tsx +113 -0
  88. package/template/src/components/basic/Header/index.tsx +216 -0
  89. package/template/src/components/basic/Header/styles.ts +0 -0
  90. package/template/src/components/basic/Icons/index.tsx +131 -0
  91. package/template/src/components/basic/InputLabel/index.tsx +19 -0
  92. package/template/src/components/basic/LoadingOverlay/index.tsx +68 -0
  93. package/template/src/components/basic/MaterialTextInput/index.tsx +153 -0
  94. package/template/src/components/basic/NumberInput/index.tsx +53 -0
  95. package/template/src/components/basic/Picker/PickerContext.ts +7 -0
  96. package/template/src/components/basic/Picker/PickerHeader.tsx +130 -0
  97. package/template/src/components/basic/Picker/PickerItem.tsx +105 -0
  98. package/template/src/components/basic/Picker/PickerItemsList.tsx +135 -0
  99. package/template/src/components/basic/Picker/PickerPresenter.ts +54 -0
  100. package/template/src/components/basic/Picker/hooks/useImperativePickerHandle.ts +27 -0
  101. package/template/src/components/basic/Picker/hooks/usePickerLabel.ts +74 -0
  102. package/template/src/components/basic/Picker/hooks/usePickerSearch.ts +37 -0
  103. package/template/src/components/basic/Picker/hooks/usePickerSelection.ts +57 -0
  104. package/template/src/components/basic/Picker/index.tsx +284 -0
  105. package/template/src/components/basic/Picker/types.tsx +229 -0
  106. package/template/src/components/basic/PressableOpacity/index.tsx +20 -0
  107. package/template/src/components/basic/RootDialog/Dialog.tsx +246 -0
  108. package/template/src/components/basic/RootDialog/Manager.tsx +110 -0
  109. package/template/src/components/basic/RootDialog/animations/Animation.ts +29 -0
  110. package/template/src/components/basic/RootDialog/animations/FadeAnimation.ts +40 -0
  111. package/template/src/components/basic/RootDialog/animations/ScaleAnimation.ts +37 -0
  112. package/template/src/components/basic/RootDialog/animations/SlideAnimation.ts +89 -0
  113. package/template/src/components/basic/RootDialog/components/Backdrop.tsx +60 -0
  114. package/template/src/components/basic/RootDialog/components/BaseDialog.tsx +564 -0
  115. package/template/src/components/basic/RootDialog/components/BottomDialog.tsx +32 -0
  116. package/template/src/components/basic/RootDialog/components/DialogButton.tsx +87 -0
  117. package/template/src/components/basic/RootDialog/components/DialogContent.tsx +26 -0
  118. package/template/src/components/basic/RootDialog/components/DialogContext.tsx +8 -0
  119. package/template/src/components/basic/RootDialog/components/DialogFooter.tsx +42 -0
  120. package/template/src/components/basic/RootDialog/components/DialogTitle.tsx +53 -0
  121. package/template/src/components/basic/RootDialog/components/DraggableView.tsx +271 -0
  122. package/template/src/components/basic/RootDialog/index.ts +21 -0
  123. package/template/src/components/basic/RootDialog/type.ts +102 -0
  124. package/template/src/components/basic/Text/index.tsx +8 -0
  125. package/template/src/components/basic/index.ts +35 -0
  126. package/template/src/configs/constants/type/APIStatus.type.ts +8 -0
  127. package/template/src/configs/constants/type/Locale.type.ts +7 -0
  128. package/template/src/configs/constants/type/StorageKey.type.ts +7 -0
  129. package/template/src/configs/constants/type/ThemeType.type.ts +6 -0
  130. package/template/src/configs/constants/type/index.ts +11 -0
  131. package/template/src/configs/index.ts +22 -0
  132. package/template/src/contexts/ThemeContext.ts +6 -0
  133. package/template/src/hooks/useAppLoading.ts +26 -0
  134. package/template/src/hooks/useAppState.ts +36 -0
  135. package/template/src/hooks/useArray.ts +47 -0
  136. package/template/src/hooks/useAsync.ts +42 -0
  137. package/template/src/hooks/useAsyncStorage.ts +41 -0
  138. package/template/src/hooks/useBoolean.ts +21 -0
  139. package/template/src/hooks/useBuildTheme.ts +249 -0
  140. package/template/src/hooks/useCountDown.ts +111 -0
  141. package/template/src/hooks/useCounter.ts +27 -0
  142. package/template/src/hooks/useDebounce.ts +25 -0
  143. package/template/src/hooks/useDebouncedValidate.ts +32 -0
  144. package/template/src/hooks/useDebugInformation.ts +38 -0
  145. package/template/src/hooks/useDeviceToken.ts +20 -0
  146. package/template/src/hooks/useEncryptedStorage.ts +41 -0
  147. package/template/src/hooks/useFontFamily.ts +13 -0
  148. package/template/src/hooks/useHttp.ts +18 -0
  149. package/template/src/hooks/useInterval.ts +24 -0
  150. package/template/src/hooks/useIsForeground.ts +17 -0
  151. package/template/src/hooks/useMMKVStorage.ts +0 -0
  152. package/template/src/hooks/usePrevious.ts +12 -0
  153. package/template/src/hooks/useRenderCount.ts +10 -0
  154. package/template/src/hooks/useTheme.ts +17 -0
  155. package/template/src/hooks/useTimeCountDown.ts +91 -0
  156. package/template/src/index.tsx +65 -0
  157. package/template/src/infrastructures/NetClient/AbstractClient.ts +66 -0
  158. package/template/src/infrastructures/NetClient/ApiResponse.ts +16 -0
  159. package/template/src/infrastructures/NetClient/ApisauceClient.ts +76 -0
  160. package/template/src/infrastructures/NetClient/AxiosClient.ts +80 -0
  161. package/template/src/infrastructures/NetClient/FetchNetClient.ts +120 -0
  162. package/template/src/infrastructures/NetClient/config.ts +3 -0
  163. package/template/src/infrastructures/NetClient/interfaces/INetClient.ts +6 -0
  164. package/template/src/infrastructures/Storage/IStorage.ts +7 -0
  165. package/template/src/infrastructures/Storage/MMKVStorage.ts +41 -0
  166. package/template/src/infrastructures/common/Timeout.ts +27 -0
  167. package/template/src/infrastructures/common/colorUtils.ts +82 -0
  168. package/template/src/infrastructures/common/dateUtils.ts +39 -0
  169. package/template/src/infrastructures/common/logger.ts +115 -0
  170. package/template/src/locales/en-US/general.json +26 -0
  171. package/template/src/locales/en-US/index.ts +9 -0
  172. package/template/src/locales/en-US/screens.json +4 -0
  173. package/template/src/locales/en-US/setting.json +3 -0
  174. package/template/src/locales/i18n.ts +109 -0
  175. package/template/src/locales/zh-TW/general.json +26 -0
  176. package/template/src/locales/zh-TW/index.ts +9 -0
  177. package/template/src/locales/zh-TW/screens.json +4 -0
  178. package/template/src/locales/zh-TW/setting.json +3 -0
  179. package/template/src/models/index.ts +5 -0
  180. package/template/src/models/request.model.ts +50 -0
  181. package/template/src/navigators/DrawerNav/DrawerContent.tsx +66 -0
  182. package/template/src/navigators/DrawerNav/DrawerItem.tsx +261 -0
  183. package/template/src/navigators/DrawerNav/index.tsx +39 -0
  184. package/template/src/navigators/DrawerNav/props.ts +12 -0
  185. package/template/src/navigators/DrawerNav/types.ts +8 -0
  186. package/template/src/navigators/MainBottomTabNav/index.tsx +83 -0
  187. package/template/src/navigators/MainBottomTabNav/props.ts +16 -0
  188. package/template/src/navigators/MainBottomTabNav/types.ts +6 -0
  189. package/template/src/navigators/RootStack.tsx +43 -0
  190. package/template/src/navigators/index.tsx +40 -0
  191. package/template/src/navigators/props.ts +14 -0
  192. package/template/src/navigators/types.ts +18 -0
  193. package/template/src/navigators/utils.ts +68 -0
  194. package/template/src/redux/api/api.ts +41 -0
  195. package/template/src/redux/reducers/appSlice.ts +26 -0
  196. package/template/src/redux/reducers/index.ts +21 -0
  197. package/template/src/redux/reducers/nonPersistSlice.ts +51 -0
  198. package/template/src/redux/reducers/settingSlice.ts +48 -0
  199. package/template/src/redux/reducers/themeSlice.ts +55 -0
  200. package/template/src/redux/saga/index.ts +5 -0
  201. package/template/src/redux/saga/settingSaga.ts +21 -0
  202. package/template/src/redux/selectors/app.ts +9 -0
  203. package/template/src/redux/selectors/nonPersist.ts +23 -0
  204. package/template/src/redux/selectors/setting.ts +29 -0
  205. package/template/src/redux/selectors/theme.ts +13 -0
  206. package/template/src/redux/store/index.ts +79 -0
  207. package/template/src/redux/store/types.d.ts +5 -0
  208. package/template/src/screens/Home/index.tsx +146 -0
  209. package/template/src/screens/Settings/components/Item.tsx +45 -0
  210. package/template/src/screens/Settings/index.tsx +97 -0
  211. package/template/src/screens/Splash/index.tsx +53 -0
  212. package/template/src/services/Dialogs.tsx +226 -0
  213. package/template/src/services/PermissionCheck.ts +257 -0
  214. package/template/src/theme/Common.ts +48 -0
  215. package/template/src/theme/Fonts.ts +196 -0
  216. package/template/src/theme/Gutters.ts +63 -0
  217. package/template/src/theme/Icons.ts +28 -0
  218. package/template/src/theme/Images.ts +13 -0
  219. package/template/src/theme/Layout.ts +106 -0
  220. package/template/src/theme/Variables.ts +167 -0
  221. package/template/src/theme/components/Buttons.ts +37 -0
  222. package/template/src/theme/index.ts +8 -0
  223. package/template/src/theme/metrics.ts +57 -0
  224. package/template/src/theme/themes/default_dark/Images.ts +7 -0
  225. package/template/src/theme/themes/default_dark/Variables.ts +84 -0
  226. package/template/src/theme/themes/default_dark/index.ts +2 -0
  227. package/template/src/theme/themes/index.ts +8 -0
  228. package/template/src/theme/types.ts +152 -0
  229. package/template/src/utils/encrypt-helper.ts +118 -0
  230. package/template/src/utils/index.ts +76 -0
  231. package/template/src/utils/sys-info-data.ts +17 -0
  232. package/template/tsconfig.json +44 -0
  233. package/template.config.js +8 -0
@@ -0,0 +1,53 @@
1
+ import React from 'react'
2
+ import { Text, View } from 'react-native'
3
+
4
+ import { Body, Button } from '@/components/basic'
5
+ import useTheme from '@/hooks/useTheme'
6
+
7
+ interface INumberInputProps {
8
+ value: number
9
+ canAdd?: boolean
10
+ canSub?: boolean
11
+ onValueChange?: (newValue: number) => void
12
+ }
13
+ const NumberInput = (
14
+ { onValueChange = (newValue) => {}, value, canSub = true, canAdd = true }: INumberInputProps
15
+ ) => {
16
+ const { Colors, Common, Fonts, Gutters, Layout } = useTheme()
17
+ // const [count, setCount] = React.useState(value)
18
+ const add = () => {
19
+ // setCount(count + 1)
20
+ onValueChange(value + 1)
21
+ }
22
+ const reduce = () => {
23
+ // setCount(count - 1 )
24
+ // if (count < 1) {
25
+ // setCount(0)
26
+ // } else {
27
+ // setCount(count - 1)
28
+ // }
29
+ onValueChange(Math.max(value-1, 0))
30
+ }
31
+
32
+ // React.useEffect(() => {
33
+ // setCount(value)
34
+ // }, [value])
35
+
36
+
37
+ return (
38
+ <View style={[Layout.row, Layout.alignItemsCenter]}>
39
+ <Button
40
+ text="-"
41
+ onPress={reduce}
42
+ />
43
+ <Text>{value}</Text>
44
+ <Button
45
+ text="+"
46
+ disabled={!canAdd}
47
+ onPress={add}
48
+ />
49
+ </View>
50
+ )
51
+ }
52
+
53
+ export default NumberInput
@@ -0,0 +1,7 @@
1
+ import React from 'react'
2
+
3
+ import { PickerContextProps } from './types'
4
+
5
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
6
+ // @ts-ignore
7
+ export default React.createContext<PickerContextProps>({})
@@ -0,0 +1,130 @@
1
+ import _ from 'lodash'
2
+ import React, { useCallback, useContext, useEffect, useMemo } from 'react'
3
+ import { StyleSheet, TouchableOpacity, View } from 'react-native'
4
+ import { Text } from 'react-native-paper'
5
+ import { EdgeInsets, useSafeAreaInsets } from 'react-native-safe-area-context'
6
+
7
+ import { Button } from '@/components/basic'
8
+ import { PickerHeaderProps, PickerLayouts } from '@/components/basic/Picker/types'
9
+ import useTheme from '@/hooks/useTheme'
10
+ import { Theme } from '@/theme/types'
11
+
12
+ const PickerHeader: React.FC<PickerHeaderProps> = (props: PickerHeaderProps) => {
13
+ const {
14
+ closeIcon,
15
+ closeLabel,
16
+ closeLabelStyle,
17
+ doneIcon,
18
+ doneLabel,
19
+ doneLabelStyle,
20
+ layout = PickerLayouts.full,
21
+ onClose,
22
+ onDone,
23
+ title,
24
+ titleStyle,
25
+ backgroundColor,
26
+ } = props
27
+ const { Colors, Gutters, Layout, Param } = useTheme()
28
+ const insets = useSafeAreaInsets()
29
+ const styles = makeStyles({ Colors, Gutters, Layout, Param }, layout, insets)
30
+
31
+ return (
32
+ <View
33
+ style={[styles.header, { backgroundColor: backgroundColor || Colors.primary }]}
34
+ >
35
+ <View style={styles.content}>
36
+ <View style={styles.titleContainer}>
37
+ <Text style={[styles.title, titleStyle]} variant={'titleLarge'}>{title || ''}</Text>
38
+ </View>
39
+ <View style={[styles.leftButton]}>
40
+ {closeLabel ? (
41
+ <Button
42
+ type={'text'}
43
+ onPress={onClose}
44
+ text={closeLabel}
45
+ textColor={Colors.onPrimary}
46
+ />
47
+ ) : (
48
+ <Button
49
+ type={'icon'}
50
+ onPress={onClose}
51
+ icon={closeIcon || 'close'}
52
+ textColor={Colors.onPrimary}
53
+ />
54
+ )}
55
+ </View>
56
+ <View style={[styles.rightButton]}>
57
+ {doneLabel && onDone ? (
58
+ <Button
59
+ type={'text'}
60
+ onPress={onDone}
61
+ text={doneLabel}
62
+ textColor={Colors.onPrimary}
63
+ />
64
+ ) : (<>
65
+ {
66
+ onDone && (
67
+ <Button
68
+ type={'icon'}
69
+ onPress={onDone}
70
+ icon={doneIcon || 'check'}
71
+ textColor={Colors.onPrimary}
72
+ />
73
+ )
74
+ }
75
+ </>)}
76
+ </View>
77
+ </View>
78
+ </View>
79
+ )
80
+ }
81
+ const makeStyles = ({ Colors, Gutters, Layout, Param }: Pick<Theme, 'Colors'|'Gutters'|'Layout'|'Param'>, layout: PickerLayouts, insets: EdgeInsets) => {
82
+ const headerHeight = layout === PickerLayouts.full ? (Param.headerHeight + insets.top) : Param.headerHeight
83
+ const shouldRoundTop = layout === PickerLayouts.bottomSheet
84
+
85
+ return StyleSheet.create({
86
+ header: {
87
+ flexDirection: 'row',
88
+ height: headerHeight,
89
+ width: '100%',
90
+ paddingTop: shouldRoundTop ? 0 : insets.top,
91
+ borderTopLeftRadius: shouldRoundTop ? Param.roundness * 3 : 0,
92
+ borderTopRightRadius: shouldRoundTop ? Param.roundness * 3 : 0,
93
+ },
94
+ content: {
95
+ flexDirection: 'row',
96
+ flex: 1,
97
+ height: Param.headerHeight,
98
+ alignItems: 'center',
99
+ },
100
+ titleContainer: {
101
+ flex: 1,
102
+ width: '100%',
103
+ flexDirection: 'row',
104
+ alignItems: 'center',
105
+ justifyContent: 'center',
106
+ position: 'absolute',
107
+ height: Param.headerHeight,
108
+ top: 0,
109
+ left: 0,
110
+ right: 0,
111
+ },
112
+ title: {
113
+ color: Colors.onPrimary,
114
+ },
115
+ leftButton: {
116
+ ...Gutters.smallLPadding,
117
+ flex: 1,
118
+ flexDirection: 'row',
119
+ alignItems: 'center',
120
+ },
121
+ rightButton: {
122
+ ...Gutters.smallRPadding,
123
+ flex: 1,
124
+ flexDirection: 'row',
125
+ alignItems: 'center',
126
+ justifyContent: 'flex-end',
127
+ },
128
+ })}
129
+
130
+ export default PickerHeader
@@ -0,0 +1,105 @@
1
+ import _ from 'lodash'
2
+ import React, { useCallback, useContext, useEffect, useMemo } from 'react'
3
+ import { StyleSheet, TouchableOpacity, View } from 'react-native'
4
+ import { Text } from 'react-native-paper'
5
+
6
+ import { IconX, PressableOpacity } from '@/components/basic'
7
+ import useTheme from '@/hooks/useTheme'
8
+ import { Theme } from '@/theme/types'
9
+
10
+ import PickerContext from './PickerContext'
11
+ import { getItemLabel, isItemSelected } from './PickerPresenter'
12
+ import { PickerItemProps } from './types'
13
+
14
+ const PickerItem = (props: PickerItemProps) => {
15
+ const {
16
+ disabled,
17
+ label,
18
+ labelStyle,
19
+ selectedIcon,
20
+ selectedIconColor,
21
+ testID,
22
+ value,
23
+ } = props
24
+ const { Colors } = useTheme()
25
+ const context = useContext(PickerContext)
26
+ const customRenderItem = context.renderItem || props.renderItem
27
+ const itemValue = value
28
+ const isSelected = isItemSelected(itemValue, context.value)
29
+ const itemLabel = getItemLabel(label, value, props.getItemLabel || context.getLabel)
30
+ const selectedCounter = context.selectionLimit && _.isArray(context.value) && context.value?.length
31
+ const styles = makeStyles({ Colors })
32
+
33
+ const isItemDisabled = useMemo(() => {
34
+ return !!(disabled || (!isSelected && context.selectionLimit && context.selectionLimit === selectedCounter))
35
+ }, [selectedCounter])
36
+
37
+
38
+ const selectedIndicator = useMemo(() => {
39
+ if (isSelected) {
40
+ return <IconX origin={IconX.MATERIAL_DESIGN_ICONS} name={'check'} size={20} color={Colors.primary} />
41
+ }
42
+ }, [isSelected, isItemDisabled, selectedIcon, selectedIconColor])
43
+
44
+ const itemLabelStyle = useMemo(() => {
45
+ return [styles.labelText, isItemDisabled ? styles.labelTextDisabled : undefined, labelStyle]
46
+ }, [isItemDisabled, labelStyle])
47
+
48
+ const _onPress = useCallback(() => {
49
+ context.onPress(value)
50
+ }, [value, context.onPress])
51
+
52
+ const onSelectedLayout = useCallback((...args: any[]) => {
53
+ _.invoke(context, 'onSelectedLayout', ...args)
54
+ }, [])
55
+
56
+ const _renderItem = () => {
57
+ return (
58
+ <View style={[styles.container]}>
59
+ <Text numberOfLines={1} style={itemLabelStyle}>
60
+ {itemLabel}
61
+ </Text>
62
+ {selectedIndicator}
63
+ </View>
64
+ )
65
+ }
66
+
67
+ return (
68
+ <PressableOpacity
69
+ activeOpacity={0.5}
70
+ onPress={_onPress}
71
+ onLayout={isSelected ? onSelectedLayout : undefined}
72
+ disabled={isItemDisabled}
73
+ testID={testID}
74
+ // throttleTime={0}
75
+ >
76
+ {customRenderItem ? customRenderItem(value, { ...props, isSelected, isItemDisabled }, itemLabel) : _renderItem()}
77
+ </PressableOpacity>
78
+ )
79
+ }
80
+
81
+ const makeStyles = ({ Colors }: Pick<Theme, 'Colors'>) => {
82
+ return StyleSheet.create({
83
+ container: {
84
+ height: 56.5,
85
+ paddingHorizontal: 23,
86
+ borderColor: Colors.border,
87
+ borderBottomWidth: 1,
88
+ display: 'flex',
89
+ flexDirection: 'row',
90
+ justifyContent: 'space-between',
91
+ alignItems: 'center',
92
+ },
93
+ labelText: {
94
+ // ...Typography.text70,
95
+ color: Colors.text,
96
+ flex: 1,
97
+ textAlign: 'left',
98
+ },
99
+ labelTextDisabled: {
100
+ color: Colors.onSurfaceDisabled,
101
+ },
102
+ })
103
+ }
104
+
105
+ export default PickerItem
@@ -0,0 +1,135 @@
1
+ import _ from 'lodash'
2
+ import React, { useCallback, useContext, useState } from 'react'
3
+ import { Animated, FlatList, ListRenderItemInfo, StyleSheet, View } from 'react-native'
4
+ import { Text, TextInput } from 'react-native-paper'
5
+ import { useSafeAreaInsets } from 'react-native-safe-area-context'
6
+
7
+ import { MaterialTextInput } from '@/components/basic'
8
+ import PickerHeader from '@/components/basic/Picker/PickerHeader'
9
+ import PickerItem from '@/components/basic/Picker/PickerItem'
10
+ import useTheme from '@/hooks/useTheme'
11
+ import logger from '@/infrastructures/common/logger'
12
+ import { Theme } from '@/theme/types'
13
+ import { screenHeight } from '@/utils'
14
+
15
+ import PickerContext from './PickerContext'
16
+ import { PickerItemProps, PickerItemsListProps, PickerLayouts, PickerOptionItem, PickerSingleValue } from './types'
17
+
18
+ const keyExtractor = (_item: string, index: number) => index.toString()
19
+
20
+ const PickerItemsList = (props: PickerItemsListProps) => {
21
+ const {
22
+ listProps,
23
+ children,
24
+ items,
25
+ showSearch,
26
+ searchStyle = {},
27
+ searchPlaceholder = 'Search...',
28
+ onSearchChange,
29
+ renderCustomSearch,
30
+ testID,
31
+ layout = PickerLayouts.full,
32
+ headerProps,
33
+ } = props
34
+
35
+ const context = useContext(PickerContext)
36
+ const insets = useSafeAreaInsets()
37
+ const { Colors, Gutters, Layout } = useTheme()
38
+ const styles = makeStyles({ Colors }, layout)
39
+ const { style: listStyle, ...restListProps } = listProps || {}
40
+
41
+ const renderHeader = () => {
42
+ return <PickerHeader {...headerProps} layout={layout} />
43
+ }
44
+ const renderSearchInput = () => {
45
+ if (showSearch) {
46
+ if (_.isFunction(renderCustomSearch)) {
47
+ return renderCustomSearch(props)
48
+ }
49
+
50
+ return (
51
+ <View style={styles.searchInputContainer}>
52
+
53
+ </View>
54
+ )
55
+ }
56
+ }
57
+
58
+ const renderItem = useCallback(({ index, item }: ListRenderItemInfo<PickerItemProps>) => {
59
+ const child = React.Children.toArray(children)[index]
60
+
61
+ return child
62
+ }, [children])
63
+
64
+ const renderList = () => {
65
+ return (
66
+ <FlatList
67
+ data={items}
68
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
69
+ // @ts-ignore
70
+ renderItem={renderItem}
71
+ keyExtractor={keyExtractor}
72
+ {...restListProps}
73
+ style={[
74
+ {
75
+ backgroundColor: Colors.background,
76
+ paddingBottom: insets.bottom,
77
+ },
78
+ styles.listStyle,
79
+ listStyle ? listStyle : {},
80
+ ]}
81
+ />
82
+ )
83
+ }
84
+
85
+ return (
86
+ <View style={[styles.content]}>
87
+ <>
88
+ {renderHeader()}
89
+ {renderSearchInput()}
90
+ {renderList()}
91
+ </>
92
+ </View>
93
+ )
94
+ }
95
+ const makeStyles = ({ Colors }: Pick<Theme, 'Colors'>, layout: PickerLayouts) => {
96
+ let _temp = {}
97
+ const maxHeight = Math.max(screenHeight * 0.5, 420)
98
+ if (layout === PickerLayouts.full) {
99
+ _temp = {
100
+ flex: 1,
101
+ }
102
+ } else {
103
+ _temp = {
104
+ maxHeight: maxHeight,
105
+ height: maxHeight,
106
+ }
107
+ }
108
+
109
+ return StyleSheet.create({
110
+ content: {
111
+ ..._temp,
112
+ },
113
+ modalBody: {},
114
+ searchInputContainer: {
115
+ flexDirection: 'row',
116
+ alignItems: 'center',
117
+ paddingLeft: 16,
118
+ borderBottomWidth: StyleSheet.hairlineWidth,
119
+ borderBottomColor: Colors.border,
120
+ },
121
+ searchIcon: {
122
+ marginRight: 12,
123
+ },
124
+ searchInput: {
125
+ height: 60,
126
+ paddingRight: 16,
127
+ flex: 1,
128
+ },
129
+ listStyle: {
130
+ flex: 1,
131
+ },
132
+ })
133
+ }
134
+
135
+ export default PickerItemsList
@@ -0,0 +1,54 @@
1
+ import _ from 'lodash'
2
+ import React from 'react'
3
+
4
+ import { PickerProps, PickerSingleValue, PickerValue } from './types'
5
+
6
+ export const extractPickerItems = (props: PickerProps) => {
7
+ const { children } = props
8
+ const items = React.Children.map(children, child => ({
9
+ // @ts-expect-error handle use PickerItemProps once exist
10
+ value: child?.props.value,
11
+ // @ts-expect-error handle use PickerItemProps once exist
12
+ label: child?.props.label,
13
+ }))
14
+
15
+ return items ?? []
16
+ }
17
+
18
+ export const isItemSelected = (childValue: PickerSingleValue, selectedValue?: PickerValue) => {
19
+ let isSelected = false
20
+
21
+ if (Array.isArray(selectedValue)) {
22
+ isSelected = !!_.find(selectedValue, v => {
23
+ // @ts-expect-error TODO: fix after removing migrate prop completely
24
+ return v === childValue || (typeof v === 'object' && v?.value === childValue)
25
+ })
26
+ } else {
27
+ isSelected = childValue === selectedValue
28
+ }
29
+
30
+ return isSelected
31
+ }
32
+
33
+ // export function getItemValue(props) {
34
+ // if (_.isArray(props.value)) {
35
+ // return props.getItemValue ? _.map(props.value, item => props.getItemValue(item)) : _.map(props.value, 'value');
36
+ // } else if (!_.isObject(props.value)) {
37
+ // return props.value;
38
+ // }
39
+ // return _.invoke(props, 'getItemValue', props.value) || _.get(props.value, 'value');
40
+ // }
41
+
42
+ export const getItemLabel = (label: string, value: PickerValue, getLabel: PickerProps['getLabel']) => {
43
+ if (_.isObject(value)) {
44
+ if (getLabel) {
45
+ return getLabel(value)
46
+ }
47
+
48
+ return _.get(value, 'label')
49
+ }
50
+
51
+ return label
52
+ }
53
+
54
+ export const shouldFilterOut = (searchValue: string, itemLabel?: string) => !_.includes(_.lowerCase(itemLabel), _.lowerCase(searchValue))
@@ -0,0 +1,27 @@
1
+ import React, { useImperativeHandle, useRef } from 'react'
2
+
3
+ import { ExpandableOverlayMethods } from '@/components/basic/ExpandableOverlay'
4
+ const useImperativePickerHandle = (ref: React.Ref<any>,
5
+ expandableRef: React.MutableRefObject<ExpandableOverlayMethods | null>) => {
6
+ const pickerRef = useRef<ExpandableOverlayMethods>()
7
+ useImperativeHandle(ref, () => {
8
+ // const { blur, clear, focus, isFocused, validate } = pickerRef.current ?? {}
9
+ // @ts-expect-error useRef return type is possible null therefor it throw TS error
10
+ const { closeExpandable, openExpandable, toggleExpandable } = expandableRef.current
11
+
12
+ return {
13
+ // isFocused,
14
+ // focus,
15
+ // blur,
16
+ // clear,
17
+ // validate,
18
+ openExpandable,
19
+ closeExpandable,
20
+ toggleExpandable,
21
+ }
22
+ })
23
+
24
+ return pickerRef
25
+ }
26
+
27
+ export default useImperativePickerHandle
@@ -0,0 +1,74 @@
1
+ import _ from 'lodash'
2
+ import { useCallback, useMemo } from 'react'
3
+
4
+ import { PickerProps, PickerValue } from '../types'
5
+
6
+ type UsePickerLabelProps
7
+ = Pick<
8
+ PickerProps,
9
+ 'value' | 'getLabel' | 'placeholder' | 'accessibilityLabel' | 'accessibilityHint'
10
+ > & {
11
+ items: {value: string | number; label: string}[] | null | undefined
12
+ }
13
+
14
+ const usePickerLabel = (props: UsePickerLabelProps) => {
15
+ const {
16
+ accessibilityHint,
17
+ accessibilityLabel,
18
+ getLabel,
19
+ items,
20
+ placeholder,
21
+ value,
22
+ } = props
23
+
24
+ const getLabelsFromArray = useCallback((pickerValue: PickerValue) => {
25
+ const itemsByValue = _.keyBy(items, 'value')
26
+
27
+ return _.flow(arr =>
28
+ _.map(arr, item => (_.isPlainObject(item) ? getLabel?.(item) || item?.label : itemsByValue[item]?.label) as string),
29
+ arr => _.join(arr, ', '))(pickerValue)
30
+ },
31
+ [getLabel, items])
32
+
33
+ const _getLabel = useCallback((pickerValue: PickerValue) => {
34
+ if (_.isFunction(getLabel) && !_.isUndefined(getLabel(pickerValue))) {
35
+ return getLabel(pickerValue)
36
+ }
37
+
38
+ if (_.isArray(pickerValue)) {
39
+ return getLabelsFromArray(pickerValue)
40
+ }
41
+
42
+ // TODO: Remove
43
+ // if (typeof value === 'object') {
44
+ // return value?.label;
45
+ // }
46
+
47
+ // otherwise, extract from picker items
48
+ const selectedItem = _.find(items, { value: pickerValue })
49
+ console.log('[Picker] selectedItem', selectedItem)
50
+
51
+ return _.get(selectedItem, 'label')
52
+ },
53
+ [getLabelsFromArray])
54
+
55
+ const accessibilityInfo = useMemo(() => {
56
+ const label = _getLabel(value)
57
+
58
+ return {
59
+ accessibilityLabel:
60
+ accessibilityLabel ?? (label ? `${placeholder || ''}. selected. ${label}` : `Select ${placeholder || ''}`),
61
+ accessibilityHint:
62
+ accessibilityHint ?? (label ? 'Double tap to edit' : `Goes to ${placeholder || ''}. Suggestions will be provided`),
63
+ }
64
+ }, [value, accessibilityLabel, accessibilityHint])
65
+
66
+ return {
67
+ getLabelsFromArray,
68
+ getLabel: _getLabel,
69
+ accessibilityInfo,
70
+ label: _getLabel(value),
71
+ }
72
+ }
73
+
74
+ export default usePickerLabel
@@ -0,0 +1,37 @@
1
+ import _ from 'lodash'
2
+ import { useCallback, useMemo, useState } from 'react'
3
+
4
+ import { getItemLabel as getItemLabelPresenter, shouldFilterOut } from '../PickerPresenter'
5
+ import { PickerProps } from '../types'
6
+
7
+ type UsePickerSearchProps = Pick<PickerProps, 'showSearch' | 'onSearchChange' | 'children' | 'getLabel'>
8
+
9
+ const usePickerSearch = (props: UsePickerSearchProps) => {
10
+ const { children, getLabel, onSearchChange, showSearch } = props
11
+ const [searchValue, setSearchValue] = useState('')
12
+
13
+ const _onSearchChange = useCallback((newSearchValue: string) => {
14
+ setSearchValue(newSearchValue)
15
+ onSearchChange?.(newSearchValue)
16
+ },
17
+ [onSearchChange])
18
+
19
+ const filteredChildren = useMemo(() => {
20
+ if (showSearch && !_.isEmpty(searchValue)) {
21
+ // @ts-expect-error need to fix children type
22
+ return _.filter(children, child => {
23
+ // @ts-expect-error need to fix children type to be based on PickerItemProps
24
+ const { getItemLabel: childGetItemLabel, label, value } = child.props
25
+ const itemLabel = getItemLabelPresenter(label, value, childGetItemLabel || getLabel)
26
+
27
+ return !shouldFilterOut(searchValue, itemLabel)
28
+ })
29
+ }
30
+
31
+ return children
32
+ }, [showSearch, searchValue, children])
33
+
34
+ return { setSearchValue, onSearchChange: _onSearchChange, filteredChildren }
35
+ }
36
+
37
+ export default usePickerSearch
@@ -0,0 +1,57 @@
1
+ import _, { isArray } from 'lodash'
2
+ import { RefObject, useCallback, useEffect, useState } from 'react'
3
+
4
+ import { PickerModes, PickerMultiValue, PickerProps, PickerSingleValue, PickerValue } from '../types'
5
+
6
+ type UsePickerSelectionProps
7
+ = Pick<PickerProps, 'value' | 'onChange' | 'mode'> & {
8
+ pickerExpandableRef: RefObject<any>
9
+ setSearchValue: (searchValue: string) => void
10
+ }
11
+
12
+ const usePickerSelection = (props: UsePickerSelectionProps) => {
13
+ const { mode, onChange, pickerExpandableRef, setSearchValue, value } = props
14
+ const [multiDraftValue, setMultiDraftValue] = useState(value as PickerMultiValue)
15
+ const [multiFinalValue, setMultiFinalValue] = useState(value as PickerMultiValue)
16
+
17
+ useEffect(() => {
18
+ if (mode === PickerModes.MULTI && multiFinalValue !== value) {
19
+ setMultiDraftValue(value as PickerMultiValue)
20
+ setMultiFinalValue(value as PickerMultiValue)
21
+ }
22
+ }, [value])
23
+
24
+ const onDoneSelecting = useCallback((item: PickerValue) => {
25
+ setSearchValue('')
26
+ setMultiFinalValue(item as PickerMultiValue)
27
+ pickerExpandableRef.current?.closeExpandable?.()
28
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
29
+ // @ts-ignore
30
+ onChange?.(item)
31
+ },
32
+ [onChange])
33
+
34
+ const toggleItemSelection = useCallback((item: PickerSingleValue) => {
35
+ const itemAsArray = [item]
36
+ const newValue = _.xor(multiDraftValue, itemAsArray)
37
+
38
+ setMultiDraftValue(newValue)
39
+ },
40
+ [multiDraftValue])
41
+
42
+ const cancelSelect = useCallback(() => {
43
+ setSearchValue('')
44
+ setMultiDraftValue(multiFinalValue)
45
+ pickerExpandableRef.current?.closeExpandable?.()
46
+ // topBarProps?.onCancel?.()
47
+ }, [multiFinalValue])
48
+
49
+ return {
50
+ multiDraftValue,
51
+ onDoneSelecting,
52
+ toggleItemSelection,
53
+ cancelSelect,
54
+ }
55
+ }
56
+
57
+ export default usePickerSelection