@codeleap/mobile 2.1.8 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. package/dist/components/AutoComplete/styles.js +1 -2
  2. package/dist/components/AutoComplete/styles.js.map +1 -1
  3. package/dist/components/Drawer/index.js +13 -2
  4. package/dist/components/Drawer/index.js.map +1 -1
  5. package/dist/components/Drawer/styles.js +3 -5
  6. package/dist/components/Drawer/styles.js.map +1 -1
  7. package/dist/components/EmptyPlaceholder/styles.js +1 -1
  8. package/dist/components/EmptyPlaceholder/styles.js.map +1 -1
  9. package/dist/components/Grid/index.d.ts +3 -1
  10. package/dist/components/Grid/index.js +19 -4
  11. package/dist/components/Grid/index.js.map +1 -1
  12. package/dist/components/ImageView/Spotlight.js +1 -2
  13. package/dist/components/ImageView/Spotlight.js.map +1 -1
  14. package/dist/components/List/index.d.ts +2 -1
  15. package/dist/components/List/index.js +19 -5
  16. package/dist/components/List/index.js.map +1 -1
  17. package/dist/components/Modal/index.d.ts +4 -2
  18. package/dist/components/Modal/index.js +11 -3
  19. package/dist/components/Modal/index.js.map +1 -1
  20. package/dist/components/Modal/styles.d.ts +1 -1
  21. package/dist/components/Modal/styles.js +15 -15
  22. package/dist/components/Modal/styles.js.map +1 -1
  23. package/dist/components/MultiSelect/index.js +8 -7
  24. package/dist/components/MultiSelect/index.js.map +1 -1
  25. package/dist/components/MultiSelect/types.d.ts +1 -0
  26. package/dist/components/Pager/styles.js +7 -7
  27. package/dist/components/Pager/styles.js.map +1 -1
  28. package/dist/components/Scroll/index.d.ts +4 -2
  29. package/dist/components/Scroll/index.js +20 -6
  30. package/dist/components/Scroll/index.js.map +1 -1
  31. package/dist/components/SegmentedControl/index.js +1 -1
  32. package/dist/components/SegmentedControl/index.js.map +1 -1
  33. package/dist/components/Select/index.js +20 -6
  34. package/dist/components/Select/index.js.map +1 -1
  35. package/dist/components/Select/styles.js +2 -0
  36. package/dist/components/Select/styles.js.map +1 -1
  37. package/dist/components/Select/types.d.ts +3 -1
  38. package/dist/components/legacy/Modal/index.d.ts +2 -1
  39. package/dist/components/legacy/Modal/index.js +1 -1
  40. package/dist/components/legacy/Modal/index.js.map +1 -1
  41. package/dist/components/legacy/Modal/styles.js +9 -9
  42. package/dist/components/legacy/Modal/styles.js.map +1 -1
  43. package/dist/utils/KeyboardAware/index.d.ts +1 -0
  44. package/dist/utils/KeyboardAware/index.js +1 -0
  45. package/dist/utils/KeyboardAware/index.js.map +1 -1
  46. package/dist/utils/KeyboardAware/keyboardHooks.d.ts +30 -0
  47. package/dist/utils/KeyboardAware/keyboardHooks.js +113 -0
  48. package/dist/utils/KeyboardAware/keyboardHooks.js.map +1 -0
  49. package/dist/utils/KeyboardAware/lib/KeyboardAwareHOC.d.ts +1 -1
  50. package/dist/utils/PermissionManager/context.d.ts +2 -2
  51. package/dist/utils/PermissionManager/context.js +25 -29
  52. package/dist/utils/PermissionManager/context.js.map +1 -1
  53. package/dist/utils/hooks.d.ts +1 -0
  54. package/dist/utils/hooks.js +17 -1
  55. package/dist/utils/hooks.js.map +1 -1
  56. package/dist/utils/index.d.ts +1 -0
  57. package/dist/utils/index.js +1 -0
  58. package/dist/utils/index.js.map +1 -1
  59. package/dist/utils/input.d.ts +35 -0
  60. package/dist/utils/input.js +49 -0
  61. package/dist/utils/input.js.map +1 -0
  62. package/package.json +1 -1
  63. package/src/components/AutoComplete/styles.ts +1 -2
  64. package/src/components/Drawer/index.tsx +11 -1
  65. package/src/components/Drawer/styles.ts +4 -4
  66. package/src/components/EmptyPlaceholder/styles.ts +1 -1
  67. package/src/components/Grid/index.tsx +28 -18
  68. package/src/components/ImageView/Spotlight.tsx +0 -1
  69. package/src/components/List/index.tsx +31 -24
  70. package/src/components/Modal/index.tsx +18 -7
  71. package/src/components/Modal/styles.ts +38 -27
  72. package/src/components/MultiSelect/index.tsx +8 -8
  73. package/src/components/MultiSelect/types.ts +1 -0
  74. package/src/components/Pager/styles.ts +7 -7
  75. package/src/components/Scroll/index.tsx +37 -24
  76. package/src/components/SegmentedControl/index.tsx +1 -0
  77. package/src/components/Select/index.tsx +20 -14
  78. package/src/components/Select/styles.ts +2 -0
  79. package/src/components/Select/types.ts +3 -1
  80. package/src/components/legacy/Modal/index.tsx +3 -2
  81. package/src/components/legacy/Modal/styles.ts +17 -17
  82. package/src/utils/KeyboardAware/index.ts +1 -0
  83. package/src/utils/KeyboardAware/keyboardHooks.ts +155 -0
  84. package/src/utils/PermissionManager/context.tsx +31 -22
  85. package/src/utils/hooks.ts +19 -1
  86. package/src/utils/index.ts +1 -0
  87. package/src/utils/input.ts +51 -0
@@ -0,0 +1,155 @@
1
+ import { PropsOf, TypeGuards, useCodeleapContext } from '@codeleap/common'
2
+ import { useEffect, useState } from 'react'
3
+ import {
4
+ Keyboard,
5
+ Platform,
6
+ KeyboardEvent,
7
+ KeyboardEventName,
8
+ Dimensions,
9
+ ScrollView,
10
+ StyleSheet,
11
+ } from 'react-native'
12
+
13
+ type KeyboardVisibilityEvents = {
14
+ show: KeyboardEventName
15
+ hide: KeyboardEventName
16
+ }
17
+
18
+ export const useKeyboard = () => {
19
+ const [keyboardEvent, setKeyboardEvent] = useState<KeyboardEvent>(null)
20
+ const [keyboardVisible, setKeyboardVisible] = useState(false)
21
+
22
+ useEffect(() => {
23
+ const eventNames = Platform.select<KeyboardVisibilityEvents>({
24
+ ios: {
25
+ show: 'keyboardWillShow',
26
+ hide: 'keyboardWillHide',
27
+ },
28
+ android: {
29
+ show: 'keyboardDidShow',
30
+ hide: 'keyboardDidHide',
31
+ },
32
+ })
33
+ const events = [
34
+ Keyboard.addListener(eventNames.show, (e) => {
35
+ setKeyboardVisible(true)
36
+ setKeyboardEvent(e)
37
+ }),
38
+ Keyboard.addListener(eventNames.hide, (e) => {
39
+ setKeyboardVisible(false)
40
+ setKeyboardEvent(e)
41
+ }),
42
+ ]
43
+ return () => {
44
+ events.forEach(e => e.remove())
45
+ }
46
+ }, [])
47
+
48
+ const _return = {
49
+ event: keyboardEvent,
50
+ isVisible: keyboardVisible,
51
+ height: keyboardEvent?.endCoordinates?.height || 0,
52
+ ...Keyboard,
53
+ }
54
+
55
+ return _return
56
+ }
57
+ type ScrollViewProps = Partial<
58
+ Pick<
59
+ PropsOf<typeof ScrollView>,
60
+ 'horizontal' | 'contentContainerStyle' | 'style'
61
+ >
62
+ >
63
+
64
+ export type GetKeyboardAwarePropsOptions = {
65
+ baseStyleProp?: 'style' | 'contentContainerStyle'
66
+ adapt?: 'height' | 'maxHeight' | 'paddingBottom' | 'marginBottom' | 'bottom'
67
+ enabled?: boolean
68
+ enableOnAndroid?: boolean
69
+ }
70
+ type UseKeyboardAwareViewParams = {
71
+ debugName?: string
72
+ }
73
+ export const useKeyboardAwareView = (params?: UseKeyboardAwareViewParams) => {
74
+ const keyboard = useKeyboard()
75
+ const { logger } = useCodeleapContext()
76
+ function getKeyboardAwareProps<T extends ScrollViewProps>(
77
+ props?:T,
78
+ options?: GetKeyboardAwarePropsOptions,
79
+ ): T {
80
+ const _options:GetKeyboardAwarePropsOptions = {
81
+ adapt: 'maxHeight',
82
+ baseStyleProp: 'style',
83
+ enabled: true,
84
+ enableOnAndroid: true,
85
+ ...options,
86
+ }
87
+
88
+ if (!_options.enabled || props?.horizontal || (Platform.OS === 'android' && !_options.enableOnAndroid)) return props
89
+
90
+ if (!props) {
91
+ props = {} as T
92
+ }
93
+ const baseStyle = StyleSheet.flatten(props[_options.baseStyleProp] || {})
94
+
95
+ const baseValue = baseStyle[_options.adapt] as number
96
+ let valid = true
97
+ const warnOnNotNumber = () => {
98
+ if (!TypeGuards.isNil(baseValue) && !TypeGuards.isNumber(baseValue)) {
99
+ valid = false
100
+ const debugStr = params?.debugName ? 'at ' + params?.debugName + ' ' : ''
101
+ logger.warn(
102
+ `${_options.baseStyleProp}.${_options.adapt} must be a number or not be set at all`,
103
+ { props, options: _options },
104
+ 'useKeyboardAwareView' + debugStr,
105
+ )
106
+ }
107
+ }
108
+
109
+ const newStyleProp = {
110
+ ...baseStyle,
111
+ }
112
+
113
+ switch (_options.adapt) {
114
+ case 'height':
115
+ warnOnNotNumber()
116
+ const baseHeight = baseValue || 0
117
+ newStyleProp.height = baseHeight - keyboard.height
118
+ break
119
+ case 'maxHeight':
120
+ if (keyboard.isVisible) {
121
+ warnOnNotNumber()
122
+ const baseMaxHeight = baseValue || Dimensions.get('window').height
123
+ newStyleProp.maxHeight = baseMaxHeight - keyboard.height
124
+ // break
125
+ }
126
+ break
127
+ case 'paddingBottom':
128
+ warnOnNotNumber()
129
+ const basePaddingBottom = baseValue || 0
130
+ newStyleProp.paddingBottom = basePaddingBottom + keyboard.height
131
+ break
132
+ case 'marginBottom':
133
+ warnOnNotNumber()
134
+ const baseMarginBottom = baseValue || 0
135
+ newStyleProp.marginBottom = baseMarginBottom + keyboard.height
136
+ break
137
+ case 'bottom':
138
+ warnOnNotNumber()
139
+ const baseBottom = baseValue || 0
140
+ newStyleProp.bottom = baseBottom + keyboard.height
141
+ break
142
+ }
143
+
144
+ if (!valid) return props
145
+
146
+ return {
147
+ ...props,
148
+ [_options.baseStyleProp]: newStyleProp,
149
+ }
150
+ }
151
+ return {
152
+ keyboard,
153
+ getKeyboardAwareProps,
154
+ }
155
+ }
@@ -63,42 +63,49 @@ type AskManyOpts<T extends string|number|symbol > = {
63
63
  export type UsePermissions<
64
64
  _PermissionNames extends string,
65
65
  PermissionNames extends string = `${_PermissionNames}?` | _PermissionNames> = () => TPermissionContext & {
66
- askPermission: (name: PermissionNames, onResolve?: (status: PermissionTypes.PermissionStatus) => any) => any
66
+ askPermission: (name: PermissionNames, onResolve?: (status: PermissionTypes.PermissionStatus) => any) => Promise<PermissionTypes.PermissionStatus>
67
67
  askMany<T extends PermissionNames, R = TAskManyResults<T>>(
68
68
  perms: T[],
69
69
  onResolve?: (res:R) => any,
70
70
  options?: AskManyOpts<T>
71
- ):Promise<void>
71
+ ):Promise<
72
+ R
73
+ >
72
74
  }
73
75
 
74
76
  export const usePermissions:UsePermissions<any> = () => {
75
77
  const modalCtx = ModalManager.useModalContext()
76
78
  const permissionCtx = useContext(PermissionContext)
77
79
 
78
- async function askPermission(name: string, onResolve?: (status: PermissionTypes.PermissionStatus, modalName?: string) => any) {
79
- const status = await permissionCtx.manager.get(name, {
80
- ask: false,
81
- askOnDenied: false,
82
- askOnPending: false,
83
- })
80
+ function askPermission(name: string, onResolve?: (status: PermissionTypes.PermissionStatus, modalName?: string) => any) {
81
+ return new Promise<PermissionTypes.PermissionStatus>((resolve, reject) => {
82
+ permissionCtx.manager.get(name, {
83
+ ask: false,
84
+ askOnDenied: false,
85
+ askOnPending: false,
86
+ }).then(status => {
87
+ const permissionModalName = `permissions.${name}`
84
88
 
85
- const permissionModalName = `permissions.${name}`
89
+ if (!status.isGranted) {
86
90
 
87
- if (!status.isGranted) {
91
+ modalCtx.toggleModal(permissionModalName, true, {
92
+ onPermissionResolve: (status) => {
93
+ modalCtx.toggleModal(permissionModalName, false, {})
94
+ setTimeout(() => {
95
+ onResolve?.(status, permissionModalName)
88
96
 
89
- modalCtx.toggleModal(permissionModalName, true, {
90
- onPermissionResolve: (status) => {
91
- modalCtx.toggleModal(permissionModalName, false, {})
92
- setTimeout(() => {
93
- onResolve?.(status, permissionModalName)
97
+ resolve(status)
98
+ }, modalCtx.transitionDuration)
99
+ },
100
+ })
94
101
 
95
- }, modalCtx.transitionDuration)
96
- },
102
+ } else {
103
+ onResolve?.(status.status as unknown as PermissionTypes.PermissionStatus)
104
+ resolve(status)
105
+ }
97
106
  })
107
+ })
98
108
 
99
- } else {
100
- onResolve?.(status.status as unknown as PermissionTypes.PermissionStatus)
101
- }
102
109
  }
103
110
 
104
111
  const askMany = async (
@@ -164,10 +171,12 @@ export const usePermissions:UsePermissions<any> = () => {
164
171
  modalCtx.toggleModal(prevModal, false, {})
165
172
  })
166
173
  }
167
- onResolve({
174
+ const res:Parameters<typeof onResolve>[0] = {
168
175
  ...results,
169
176
  overall: Object.values(results).every(x => x === 'granted') ? 'granted' : 'denied',
170
- })
177
+ }
178
+ onResolve(res)
179
+ return res
171
180
  }
172
181
 
173
182
  return {
@@ -1,5 +1,5 @@
1
1
  import { onMount, onUpdate, shadeColor, TypeGuards, usePrevious, useRef, useState } from '@codeleap/common'
2
- import { Animated, AppState, AppStateStatus, Platform, PressableAndroidRippleConfig } from 'react-native'
2
+ import { Animated, AppState, AppStateStatus, Platform, PressableAndroidRippleConfig, BackHandler } from 'react-native'
3
3
 
4
4
  export function useAnimateColor(value: string, opts?: Partial<Animated.TimingAnimationConfig>) {
5
5
  const iters = useRef(0)
@@ -143,3 +143,21 @@ export function usePressableFeedback(styles: any, config:UsePressableFeedbackCon
143
143
  rippleConfig,
144
144
  }
145
145
  }
146
+
147
+ export function useBackButton(cb: () => boolean|void, deps = []) {
148
+ onUpdate(() => {
149
+ const subscription = BackHandler.addEventListener('hardwareBackPress', () => {
150
+ const stopBubbling = cb()
151
+
152
+ if (TypeGuards.isBoolean(stopBubbling)) {
153
+ return stopBubbling
154
+ }
155
+
156
+ return false
157
+
158
+ })
159
+ return () => {
160
+ subscription.remove()
161
+ }
162
+ }, deps)
163
+ }
@@ -7,3 +7,4 @@ import * as Permissions from './PermissionManager'
7
7
 
8
8
  export { Permissions }
9
9
  export * from './KeyboardAware'
10
+ export * from './input'
@@ -0,0 +1,51 @@
1
+ import { TextInputProps } from 'react-native'
2
+
3
+ const createInputPresets = <T extends Record<string, (...args:any[]) => Partial<TextInputProps>>>(p:T):T => {
4
+ return p
5
+ }
6
+
7
+ export const mobileInputPresets = createInputPresets({
8
+ email: () => ({
9
+ keyboardType: 'email-address',
10
+ autoCapitalize: 'none',
11
+ autoComplete: 'email',
12
+ textContentType: 'emailAddress',
13
+
14
+ }),
15
+ name: () => ({
16
+ autoCapitalize: 'words',
17
+ autoComplete: 'name',
18
+ textContentType: 'name',
19
+
20
+ }),
21
+ chat: () => ({
22
+ returnKeyType: 'send',
23
+
24
+ }),
25
+ password: (options?: {new?: boolean}) => {
26
+ return {
27
+ autoCapitalize: 'none',
28
+ autoComplete: options?.new ? 'password-new' : 'password',
29
+ textContentType: options?.new ? 'newPassword' : 'password',
30
+ }
31
+ },
32
+ search: () => ({
33
+ returnKeyType: 'search',
34
+ }),
35
+ redeemCode: (action:'join'|'obtain' = 'obtain') => {
36
+ const isJoin = action === 'join'
37
+ if (isJoin) {
38
+ return {
39
+ returnKeyType: 'join',
40
+ textContentType: 'oneTimeCode',
41
+ autoComplete: 'sms-otp',
42
+ }
43
+ }
44
+ return {
45
+ returnKeyType: 'done',
46
+ textContentType: 'oneTimeCode',
47
+ autoComplete: 'sms-otp',
48
+ }
49
+
50
+ },
51
+ })