@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.
- package/dist/components/AutoComplete/styles.js +1 -2
- package/dist/components/AutoComplete/styles.js.map +1 -1
- package/dist/components/Drawer/index.js +13 -2
- package/dist/components/Drawer/index.js.map +1 -1
- package/dist/components/Drawer/styles.js +3 -5
- package/dist/components/Drawer/styles.js.map +1 -1
- package/dist/components/EmptyPlaceholder/styles.js +1 -1
- package/dist/components/EmptyPlaceholder/styles.js.map +1 -1
- package/dist/components/Grid/index.d.ts +3 -1
- package/dist/components/Grid/index.js +19 -4
- package/dist/components/Grid/index.js.map +1 -1
- package/dist/components/ImageView/Spotlight.js +1 -2
- package/dist/components/ImageView/Spotlight.js.map +1 -1
- package/dist/components/List/index.d.ts +2 -1
- package/dist/components/List/index.js +19 -5
- package/dist/components/List/index.js.map +1 -1
- package/dist/components/Modal/index.d.ts +4 -2
- package/dist/components/Modal/index.js +11 -3
- package/dist/components/Modal/index.js.map +1 -1
- package/dist/components/Modal/styles.d.ts +1 -1
- package/dist/components/Modal/styles.js +15 -15
- package/dist/components/Modal/styles.js.map +1 -1
- package/dist/components/MultiSelect/index.js +8 -7
- package/dist/components/MultiSelect/index.js.map +1 -1
- package/dist/components/MultiSelect/types.d.ts +1 -0
- package/dist/components/Pager/styles.js +7 -7
- package/dist/components/Pager/styles.js.map +1 -1
- package/dist/components/Scroll/index.d.ts +4 -2
- package/dist/components/Scroll/index.js +20 -6
- package/dist/components/Scroll/index.js.map +1 -1
- package/dist/components/SegmentedControl/index.js +1 -1
- package/dist/components/SegmentedControl/index.js.map +1 -1
- package/dist/components/Select/index.js +20 -6
- package/dist/components/Select/index.js.map +1 -1
- package/dist/components/Select/styles.js +2 -0
- package/dist/components/Select/styles.js.map +1 -1
- package/dist/components/Select/types.d.ts +3 -1
- package/dist/components/legacy/Modal/index.d.ts +2 -1
- package/dist/components/legacy/Modal/index.js +1 -1
- package/dist/components/legacy/Modal/index.js.map +1 -1
- package/dist/components/legacy/Modal/styles.js +9 -9
- package/dist/components/legacy/Modal/styles.js.map +1 -1
- package/dist/utils/KeyboardAware/index.d.ts +1 -0
- package/dist/utils/KeyboardAware/index.js +1 -0
- package/dist/utils/KeyboardAware/index.js.map +1 -1
- package/dist/utils/KeyboardAware/keyboardHooks.d.ts +30 -0
- package/dist/utils/KeyboardAware/keyboardHooks.js +113 -0
- package/dist/utils/KeyboardAware/keyboardHooks.js.map +1 -0
- package/dist/utils/KeyboardAware/lib/KeyboardAwareHOC.d.ts +1 -1
- package/dist/utils/PermissionManager/context.d.ts +2 -2
- package/dist/utils/PermissionManager/context.js +25 -29
- package/dist/utils/PermissionManager/context.js.map +1 -1
- package/dist/utils/hooks.d.ts +1 -0
- package/dist/utils/hooks.js +17 -1
- package/dist/utils/hooks.js.map +1 -1
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.js +1 -0
- package/dist/utils/index.js.map +1 -1
- package/dist/utils/input.d.ts +35 -0
- package/dist/utils/input.js +49 -0
- package/dist/utils/input.js.map +1 -0
- package/package.json +1 -1
- package/src/components/AutoComplete/styles.ts +1 -2
- package/src/components/Drawer/index.tsx +11 -1
- package/src/components/Drawer/styles.ts +4 -4
- package/src/components/EmptyPlaceholder/styles.ts +1 -1
- package/src/components/Grid/index.tsx +28 -18
- package/src/components/ImageView/Spotlight.tsx +0 -1
- package/src/components/List/index.tsx +31 -24
- package/src/components/Modal/index.tsx +18 -7
- package/src/components/Modal/styles.ts +38 -27
- package/src/components/MultiSelect/index.tsx +8 -8
- package/src/components/MultiSelect/types.ts +1 -0
- package/src/components/Pager/styles.ts +7 -7
- package/src/components/Scroll/index.tsx +37 -24
- package/src/components/SegmentedControl/index.tsx +1 -0
- package/src/components/Select/index.tsx +20 -14
- package/src/components/Select/styles.ts +2 -0
- package/src/components/Select/types.ts +3 -1
- package/src/components/legacy/Modal/index.tsx +3 -2
- package/src/components/legacy/Modal/styles.ts +17 -17
- package/src/utils/KeyboardAware/index.ts +1 -0
- package/src/utils/KeyboardAware/keyboardHooks.ts +155 -0
- package/src/utils/PermissionManager/context.tsx +31 -22
- package/src/utils/hooks.ts +19 -1
- package/src/utils/index.ts +1 -0
- 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) =>
|
|
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<
|
|
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
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
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
|
-
|
|
89
|
+
if (!status.isGranted) {
|
|
86
90
|
|
|
87
|
-
|
|
91
|
+
modalCtx.toggleModal(permissionModalName, true, {
|
|
92
|
+
onPermissionResolve: (status) => {
|
|
93
|
+
modalCtx.toggleModal(permissionModalName, false, {})
|
|
94
|
+
setTimeout(() => {
|
|
95
|
+
onResolve?.(status, permissionModalName)
|
|
88
96
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
onResolve?.(status, permissionModalName)
|
|
97
|
+
resolve(status)
|
|
98
|
+
}, modalCtx.transitionDuration)
|
|
99
|
+
},
|
|
100
|
+
})
|
|
94
101
|
|
|
95
|
-
|
|
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 {
|
package/src/utils/hooks.ts
CHANGED
|
@@ -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
|
+
}
|
package/src/utils/index.ts
CHANGED
|
@@ -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
|
+
})
|