@codeleap/mobile 2.3.8 → 2.3.12
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/package.json +2 -3
- package/src/components/ActionIcon/index.tsx +32 -0
- package/src/components/ActionIcon/styles.ts +97 -0
- package/src/components/ActivityIndicator/index.tsx +50 -0
- package/src/components/ActivityIndicator/styles.ts +68 -0
- package/src/components/Animated.tsx +34 -0
- package/src/components/AutoComplete/index.tsx +163 -0
- package/src/components/AutoComplete/styles.ts +44 -0
- package/src/components/Backdrop/index.tsx +48 -0
- package/src/components/Backdrop/styles.ts +33 -0
- package/src/components/Button/index.tsx +155 -0
- package/src/components/Button/styles.ts +129 -0
- package/src/components/Calendar/index.tsx +65 -0
- package/src/components/Calendar/style.ts +35 -0
- package/src/components/Calendar/types.ts +102 -0
- package/src/components/Checkbox/index.tsx +91 -0
- package/src/components/Checkbox/styles.ts +81 -0
- package/src/components/ContentView/index.tsx +63 -0
- package/src/components/ContentView/styles.ts +24 -0
- package/src/components/Drawer/index.tsx +33 -0
- package/src/components/Drawer/styles.ts +43 -0
- package/src/components/EmptyPlaceholder/index.tsx +88 -0
- package/src/components/EmptyPlaceholder/styles.ts +58 -0
- package/src/components/FileInput/index.tsx +181 -0
- package/src/components/FileInput/styles.ts +15 -0
- package/src/components/Grid/index.tsx +117 -0
- package/src/components/Grid/styles.ts +11 -0
- package/src/components/Icon/index.tsx +69 -0
- package/src/components/Icon/styles.ts +57 -0
- package/src/components/Image/index.tsx +91 -0
- package/src/components/Image/styles.ts +20 -0
- package/src/components/ImageView/Spotlight.tsx +157 -0
- package/src/components/ImageView/component.tsx +38 -0
- package/src/components/ImageView/index.ts +2 -0
- package/src/components/InputLabel/index.tsx +38 -0
- package/src/components/InputLabel/styles.ts +19 -0
- package/src/components/List/PaginationIndicator.tsx +71 -0
- package/src/components/List/index.tsx +114 -0
- package/src/components/List/styles.ts +19 -0
- package/src/components/Modal/index.tsx +218 -0
- package/src/components/Modal/styles.ts +153 -0
- package/src/components/MultiSelect/index.tsx +138 -0
- package/src/components/MultiSelect/styles.ts +18 -0
- package/src/components/MultiSelect/types.ts +42 -0
- package/src/components/Navigation/Navigation.tsx +54 -0
- package/src/components/Navigation/constants.ts +8 -0
- package/src/components/Navigation/index.tsx +3 -0
- package/src/components/Navigation/types.ts +35 -0
- package/src/components/Navigation/utils.tsx +57 -0
- package/src/components/Pager/index.tsx +121 -0
- package/src/components/Pager/styles.ts +81 -0
- package/src/components/RadioInput/index.tsx +106 -0
- package/src/components/RadioInput/styles.ts +67 -0
- package/src/components/Scroll/index.tsx +124 -0
- package/src/components/Scroll/styles.ts +18 -0
- package/src/components/Sections/index.tsx +91 -0
- package/src/components/SegmentedControl/index.tsx +204 -0
- package/src/components/SegmentedControl/styles.ts +89 -0
- package/src/components/Select/index.tsx +167 -0
- package/src/components/Select/styles.ts +62 -0
- package/src/components/Select/types.ts +43 -0
- package/src/components/Slider/Mark.tsx +46 -0
- package/src/components/Slider/Thumb.tsx +29 -0
- package/src/components/Slider/index.tsx +130 -0
- package/src/components/Slider/styles.ts +76 -0
- package/src/components/Slider/types.ts +30 -0
- package/src/components/Switch/index.tsx +91 -0
- package/src/components/Switch/styles.ts +38 -0
- package/src/components/Text/index.tsx +124 -0
- package/src/components/Text/styles.ts +50 -0
- package/src/components/TextInput/index.tsx +319 -0
- package/src/components/TextInput/styles.ts +127 -0
- package/src/components/Touchable/index.tsx +174 -0
- package/src/components/Touchable/styles.ts +28 -0
- package/src/components/View/index.tsx +103 -0
- package/src/components/View/styles.ts +24 -0
- package/src/components/components.ts +42 -0
- package/src/components/defaultStyles.ts +62 -0
- package/src/components/legacy/Modal/index.tsx +163 -0
- package/src/components/legacy/Modal/styles.ts +125 -0
- package/src/components/legacy/Pager/index.tsx +242 -0
- package/src/components/legacy/Pager/styles.ts +51 -0
- package/src/components/legacy/index.ts +2 -0
- package/src/modules/documentPicker.ts +7 -0
- package/src/modules/fastImage.ts +2 -0
- package/src/modules/imageCropPicker.d.ts +497 -0
- package/src/modules/index.d.ts +682 -0
- package/src/modules/reactNavigation.ts +15 -0
- package/src/modules/textInputMask.ts +11 -0
- package/src/modules/types/documentPicker.d.ts +215 -0
- package/src/modules/types/fileTypes.ts +138 -0
- package/src/modules/types/textInputMask.ts +9 -0
- package/src/types/index.ts +1 -0
- package/src/types/utility.ts +9 -0
- package/src/utils/KeyboardAware/context.tsx +75 -0
- package/src/utils/KeyboardAware/index.ts +17 -0
- package/src/utils/KeyboardAware/keyboardHooks.ts +124 -0
- package/src/utils/KeyboardAware/lib/KeyboardAwareFlatList.ts +4 -0
- package/src/utils/KeyboardAware/lib/KeyboardAwareHOC.tsx +618 -0
- package/src/utils/KeyboardAware/lib/KeyboardAwareInterface.ts +13 -0
- package/src/utils/KeyboardAware/lib/KeyboardAwareScrollView.ts +6 -0
- package/src/utils/KeyboardAware/lib/KeyboardAwareSectionList.ts +6 -0
- package/src/utils/KeyboardAware/types.ts +159 -0
- package/src/utils/ModalManager/components.tsx +112 -0
- package/src/utils/ModalManager/context.tsx +260 -0
- package/src/utils/ModalManager/index.ts +16 -0
- package/src/utils/OSAlert.ts +180 -0
- package/src/utils/PermissionManager/context.tsx +302 -0
- package/src/utils/PermissionManager/index.ts +20 -0
- package/src/utils/PermissionManager/types.ts +24 -0
- package/src/utils/hooks.ts +163 -0
- package/src/utils/index.ts +11 -0
- package/src/utils/input.ts +51 -0
- package/src/utils/misc.ts +83 -0
- package/src/utils/notifications.ts +206 -0
- package/src/utils/theme.ts +58 -0
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
// Type definitions for react-native-keyboard-aware-scroll-view
|
|
2
|
+
// Project: https://github.com/APSL/react-native-keyboard-aware-scroll-view
|
|
3
|
+
// Definitions by: Kyle Roach <https://github.com/iRoachie>
|
|
4
|
+
// TypeScript Version: 2.3.2
|
|
5
|
+
|
|
6
|
+
import * as React from 'react'
|
|
7
|
+
import {
|
|
8
|
+
ScrollViewProps,
|
|
9
|
+
FlatListProps,
|
|
10
|
+
SectionListProps,
|
|
11
|
+
} from 'react-native'
|
|
12
|
+
|
|
13
|
+
interface KeyboardAwareProps {
|
|
14
|
+
/**
|
|
15
|
+
* Catches the reference of the component.
|
|
16
|
+
*
|
|
17
|
+
*
|
|
18
|
+
* @type {function}
|
|
19
|
+
* @memberof KeyboardAwareProps
|
|
20
|
+
*/
|
|
21
|
+
innerRef?: (ref: JSX.Element) => void
|
|
22
|
+
/**
|
|
23
|
+
* Adds an extra offset that represents the TabBarIOS height.
|
|
24
|
+
*
|
|
25
|
+
* Default is false
|
|
26
|
+
* @type {boolean}
|
|
27
|
+
* @memberof KeyboardAwareProps
|
|
28
|
+
*/
|
|
29
|
+
viewIsInsideTabBar?: boolean
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Coordinates that will be used to reset the scroll when the keyboard hides.
|
|
33
|
+
*
|
|
34
|
+
* @type {{
|
|
35
|
+
* x: number,
|
|
36
|
+
* y: number
|
|
37
|
+
* }}
|
|
38
|
+
* @memberof KeyboardAwareProps
|
|
39
|
+
*/
|
|
40
|
+
resetScrollToCoords?: {
|
|
41
|
+
x: number
|
|
42
|
+
y: number
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Lets the user enable or disable automatic resetScrollToCoords
|
|
47
|
+
*
|
|
48
|
+
* @type {boolean}
|
|
49
|
+
* @memberof KeyboardAwareProps
|
|
50
|
+
*/
|
|
51
|
+
enableResetScrollToCoords?: boolean
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* When focus in TextInput will scroll the position
|
|
55
|
+
*
|
|
56
|
+
* Default is true
|
|
57
|
+
*
|
|
58
|
+
* @type {boolean}
|
|
59
|
+
* @memberof KeyboardAwareProps
|
|
60
|
+
*/
|
|
61
|
+
|
|
62
|
+
enableAutomaticScroll?: boolean
|
|
63
|
+
/**
|
|
64
|
+
* Enables keyboard aware settings for Android
|
|
65
|
+
*
|
|
66
|
+
* Default is false
|
|
67
|
+
*
|
|
68
|
+
* @type {boolean}
|
|
69
|
+
* @memberof KeyboardAwareProps
|
|
70
|
+
*/
|
|
71
|
+
enableOnAndroid?: boolean
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Adds an extra offset when focusing the TextInputs.
|
|
75
|
+
*
|
|
76
|
+
* Default is 75
|
|
77
|
+
* @type {number}
|
|
78
|
+
* @memberof KeyboardAwareProps
|
|
79
|
+
*/
|
|
80
|
+
extraHeight?: number
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Adds an extra offset to the keyboard.
|
|
84
|
+
* Useful if you want to stick elements above the keyboard.
|
|
85
|
+
*
|
|
86
|
+
* Default is 0
|
|
87
|
+
*
|
|
88
|
+
* @type {number}
|
|
89
|
+
* @memberof KeyboardAwareProps
|
|
90
|
+
*/
|
|
91
|
+
extraScrollHeight?: number
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Sets the delay time before scrolling to new position
|
|
95
|
+
*
|
|
96
|
+
* Default is 250
|
|
97
|
+
*
|
|
98
|
+
* @type {number}
|
|
99
|
+
* @memberof KeyboardAwareProps
|
|
100
|
+
*/
|
|
101
|
+
keyboardOpeningTime?: number
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Callback when the keyboard will show.
|
|
105
|
+
*
|
|
106
|
+
* @param frames Information about the keyboard frame and animation.
|
|
107
|
+
*/
|
|
108
|
+
onKeyboardWillShow?: (frames: Object) => void
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Callback when the keyboard did show.
|
|
112
|
+
*
|
|
113
|
+
* @param frames Information about the keyboard frame and animation.
|
|
114
|
+
*/
|
|
115
|
+
onKeyboardDidShow?: (frames: Object) => void
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Callback when the keyboard will hide.
|
|
119
|
+
*
|
|
120
|
+
* @param frames Information about the keyboard frame and animation.
|
|
121
|
+
*/
|
|
122
|
+
onKeyboardWillHide?: (frames: Object) => void
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Callback when the keyboard did hide.
|
|
126
|
+
*
|
|
127
|
+
* @param frames Information about the keyboard frame and animation.
|
|
128
|
+
*/
|
|
129
|
+
onKeyboardDidHide?: (frames: Object) => void
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Callback when the keyboard frame will change.
|
|
133
|
+
*
|
|
134
|
+
* @param frames Information about the keyboard frame and animation.
|
|
135
|
+
*/
|
|
136
|
+
onKeyboardWillChangeFrame?: (frames: Object) => void
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Callback when the keyboard frame did change.
|
|
140
|
+
*
|
|
141
|
+
* @param frames Information about the keyboard frame and animation.
|
|
142
|
+
*/
|
|
143
|
+
onKeyboardDidChangeFrame?: (frames: Object) => void
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
interface KeyboardAwareScrollViewProps
|
|
147
|
+
extends KeyboardAwareProps,
|
|
148
|
+
ScrollViewProps {}
|
|
149
|
+
interface KeyboardAwareFlatListProps<ItemT>
|
|
150
|
+
extends KeyboardAwareProps,
|
|
151
|
+
FlatListProps<ItemT> {}
|
|
152
|
+
interface KeyboardAwareSectionListProps<ItemT>
|
|
153
|
+
extends KeyboardAwareProps,
|
|
154
|
+
SectionListProps<ItemT> {}
|
|
155
|
+
|
|
156
|
+
interface KeyboardAwareState {
|
|
157
|
+
keyboardSpace: number
|
|
158
|
+
}
|
|
159
|
+
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import * as React from 'react'
|
|
2
|
+
import { usePrevious, onMount, onUpdate, PropsOf } from '@codeleap/common'
|
|
3
|
+
import { useModalContext } from './context'
|
|
4
|
+
import { Portal } from '@gorhom/portal'
|
|
5
|
+
import { Modal as _Modal } from '../../components/Modal'
|
|
6
|
+
import { Drawer as _Drawer } from '../../components/Drawer'
|
|
7
|
+
|
|
8
|
+
type UseManagedModalArgs = {
|
|
9
|
+
id?: string
|
|
10
|
+
initialVisible ?: boolean
|
|
11
|
+
parent?: string
|
|
12
|
+
visible?: boolean
|
|
13
|
+
toggle?: PropsOf<typeof _Modal>['toggle']
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function useManagedModal(props:UseManagedModalArgs) {
|
|
17
|
+
const {
|
|
18
|
+
initialVisible = false,
|
|
19
|
+
parent,
|
|
20
|
+
id,
|
|
21
|
+
} = props
|
|
22
|
+
const modalId = id
|
|
23
|
+
const modals = useModalContext()
|
|
24
|
+
|
|
25
|
+
onMount(() => {
|
|
26
|
+
if (!modalId) return
|
|
27
|
+
modals.setModal(modalId, {
|
|
28
|
+
attachedTo: [],
|
|
29
|
+
attachments: [],
|
|
30
|
+
visible: initialVisible,
|
|
31
|
+
})
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
const prevParent = usePrevious(parent)
|
|
35
|
+
|
|
36
|
+
onUpdate(() => {
|
|
37
|
+
if (!modalId) return
|
|
38
|
+
if (!!parent || !!prevParent) {
|
|
39
|
+
modals.attach(modalId, parent)
|
|
40
|
+
}
|
|
41
|
+
}, [parent])
|
|
42
|
+
|
|
43
|
+
const visible = modalId ? modals.isVisible(modalId) : props.visible
|
|
44
|
+
|
|
45
|
+
const ctxProps = modals?.state?.[modalId]?.props
|
|
46
|
+
|
|
47
|
+
const componentProps = {
|
|
48
|
+
...props, ...ctxProps, visible: visible, toggle: () => {
|
|
49
|
+
if (modalId) {
|
|
50
|
+
modals.toggleModal(modalId)
|
|
51
|
+
} else {
|
|
52
|
+
props?.toggle?.()
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return componentProps
|
|
58
|
+
}
|
|
59
|
+
export type ManagedModalProps<T = PropsOf<typeof _Modal>> = Omit<T, 'visible' | 'toggle'> & UseManagedModalArgs & {
|
|
60
|
+
|
|
61
|
+
absolute?: boolean
|
|
62
|
+
|
|
63
|
+
}
|
|
64
|
+
export const Modal:React.FC<ManagedModalProps> = ({
|
|
65
|
+
children,
|
|
66
|
+
absolute = true,
|
|
67
|
+
...props
|
|
68
|
+
}) => {
|
|
69
|
+
const componentProps = useManagedModal(props)
|
|
70
|
+
|
|
71
|
+
const content = (
|
|
72
|
+
<_Modal {...componentProps}>
|
|
73
|
+
{children}
|
|
74
|
+
</_Modal>
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
if (absolute) {
|
|
78
|
+
return <Portal>
|
|
79
|
+
{content}
|
|
80
|
+
</Portal>
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return content
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export type ManagedDrawerProps<T = PropsOf<typeof _Drawer>> = Omit<T, 'visible' | 'toggle'> & UseManagedModalArgs & {
|
|
87
|
+
|
|
88
|
+
absolute?: boolean
|
|
89
|
+
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export const Drawer:React.FC<ManagedDrawerProps> = ({
|
|
93
|
+
children,
|
|
94
|
+
absolute = true,
|
|
95
|
+
...props
|
|
96
|
+
}) => {
|
|
97
|
+
const componentProps = useManagedModal(props)
|
|
98
|
+
|
|
99
|
+
const content = (
|
|
100
|
+
<_Drawer {...componentProps}>
|
|
101
|
+
{children}
|
|
102
|
+
</_Drawer>
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
if (absolute) {
|
|
106
|
+
return <Portal>
|
|
107
|
+
{content}
|
|
108
|
+
</Portal>
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
return content
|
|
112
|
+
}
|
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
import * as React from 'react'
|
|
2
|
+
import { AnyFunction, onUpdate, TypeGuards, useCodeleapContext, useState } from '@codeleap/common'
|
|
3
|
+
import { PortalProvider } from '@gorhom/portal'
|
|
4
|
+
import { KeyboardProvider } from '../KeyboardAware'
|
|
5
|
+
|
|
6
|
+
export type AppModalProps = {
|
|
7
|
+
visible: boolean
|
|
8
|
+
attachments: string[]
|
|
9
|
+
attachedTo: string[]
|
|
10
|
+
props?: any
|
|
11
|
+
}
|
|
12
|
+
type TModalState = AppModalProps
|
|
13
|
+
type ModalTransitionOptions = {
|
|
14
|
+
duration?: number
|
|
15
|
+
props?: any
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
type TModalContext = {
|
|
19
|
+
state: Record<string, TModalState>
|
|
20
|
+
toggleModal: (name: string, setTo?: boolean, props?: any) => void
|
|
21
|
+
setModal: (name: string, to: Partial<TModalState>) => void
|
|
22
|
+
currentModal: string
|
|
23
|
+
isVisible: (name: string) => boolean
|
|
24
|
+
transition: (from: string, to: string, options?: ModalTransitionOptions) => Promise<void>
|
|
25
|
+
attach: (modal: string, to: string) => void
|
|
26
|
+
remove(name: string): void
|
|
27
|
+
transitionDuration: number
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const ModalContext = React.createContext({} as TModalContext)
|
|
31
|
+
|
|
32
|
+
export function Provider({ children }) {
|
|
33
|
+
const [modals, setModals] = useState<TModalContext['state']>({})
|
|
34
|
+
const currentModal = Object.keys(modals).find(name => modals[name].visible)
|
|
35
|
+
|
|
36
|
+
function isVisible(name: string) {
|
|
37
|
+
return !!modals[name]?.visible
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const toggleModal:TModalContext['toggleModal'] = (name, set?: boolean, props?: any) => {
|
|
41
|
+
const visible = isVisible(name)
|
|
42
|
+
|
|
43
|
+
const newVisible = typeof set === 'boolean' ? set : !visible
|
|
44
|
+
|
|
45
|
+
setModals((current) => {
|
|
46
|
+
const attached = newVisible ? [] : current[name].attachments.map(m => [m, { ...current[m], visible: false }])
|
|
47
|
+
return {
|
|
48
|
+
...current,
|
|
49
|
+
[name]: {
|
|
50
|
+
...current[name],
|
|
51
|
+
visible: newVisible,
|
|
52
|
+
props,
|
|
53
|
+
},
|
|
54
|
+
...Object.fromEntries(attached),
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
})
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const setModal:TModalContext['setModal'] = (name, to) => {
|
|
61
|
+
|
|
62
|
+
setModals((current) => ({
|
|
63
|
+
...current,
|
|
64
|
+
[name]: {
|
|
65
|
+
...current[name],
|
|
66
|
+
...to,
|
|
67
|
+
},
|
|
68
|
+
}))
|
|
69
|
+
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const codeleapCtx = useCodeleapContext()
|
|
73
|
+
const defaultDuration = codeleapCtx?.Theme?.values?.transitions?.modal?.duration || 300
|
|
74
|
+
const transition:TModalContext['transition'] = (from, to, options) => {
|
|
75
|
+
return new Promise((resolve) => {
|
|
76
|
+
setTimeout(() => {
|
|
77
|
+
|
|
78
|
+
if (!from) {
|
|
79
|
+
toggleModal(to, true, options?.props)
|
|
80
|
+
return
|
|
81
|
+
}
|
|
82
|
+
const _options:ModalTransitionOptions = {
|
|
83
|
+
duration: defaultDuration,
|
|
84
|
+
...options,
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const toVisible = isVisible(to)
|
|
88
|
+
const fromVisible = isVisible(from)
|
|
89
|
+
|
|
90
|
+
// if (!fromVisible && !toVisible) {
|
|
91
|
+
// toggleModal(to, true, options?.props)
|
|
92
|
+
// return
|
|
93
|
+
// }
|
|
94
|
+
|
|
95
|
+
toggleModal(from, false)
|
|
96
|
+
setTimeout(() => {
|
|
97
|
+
|
|
98
|
+
toggleModal(to, true, options?.props)
|
|
99
|
+
|
|
100
|
+
resolve()
|
|
101
|
+
}, _options.duration)
|
|
102
|
+
|
|
103
|
+
})
|
|
104
|
+
})
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function attach(modal: string, to: string) {
|
|
108
|
+
setModals((modals) => {
|
|
109
|
+
const toModal = modals[to]
|
|
110
|
+
const _modal = modals[modal]
|
|
111
|
+
if (!toModal || !_modal) return modals
|
|
112
|
+
|
|
113
|
+
const isAttached = toModal.attachments.includes(modal) || _modal.attachedTo.includes(to)
|
|
114
|
+
|
|
115
|
+
const newVal = { ...modals }
|
|
116
|
+
if (isAttached) {
|
|
117
|
+
|
|
118
|
+
newVal[to].attachments = newVal[to].attachments.filter(x => x !== modal)
|
|
119
|
+
newVal[modal].attachedTo = newVal[modal].attachedTo.filter(x => x !== to)
|
|
120
|
+
|
|
121
|
+
} else {
|
|
122
|
+
|
|
123
|
+
newVal[to].attachments.push(modal)
|
|
124
|
+
newVal[modal].attachedTo.push(to)
|
|
125
|
+
|
|
126
|
+
}
|
|
127
|
+
return newVal
|
|
128
|
+
})
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function remove(id: string) {
|
|
132
|
+
const newModals = { ...modals }
|
|
133
|
+
|
|
134
|
+
delete newModals[id]
|
|
135
|
+
|
|
136
|
+
setModals(newModals)
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
return <KeyboardProvider>
|
|
140
|
+
<ModalContext.Provider value={{
|
|
141
|
+
state: modals,
|
|
142
|
+
toggleModal,
|
|
143
|
+
setModal,
|
|
144
|
+
currentModal,
|
|
145
|
+
attach,
|
|
146
|
+
isVisible,
|
|
147
|
+
remove,
|
|
148
|
+
transition,
|
|
149
|
+
transitionDuration: defaultDuration,
|
|
150
|
+
|
|
151
|
+
}}>
|
|
152
|
+
<PortalProvider>
|
|
153
|
+
|
|
154
|
+
{children}
|
|
155
|
+
</PortalProvider>
|
|
156
|
+
</ModalContext.Provider>
|
|
157
|
+
</KeyboardProvider>
|
|
158
|
+
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
export function useModalContext() {
|
|
162
|
+
const context = React.useContext(ModalContext)
|
|
163
|
+
return context
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
export type UseModalSequenceOptions = {
|
|
167
|
+
onFinish?: AnyFunction
|
|
168
|
+
resetOnFinish?: boolean
|
|
169
|
+
closeLastOnFinish?: boolean
|
|
170
|
+
waitForLastToCloseBeforeCallingFinish?: boolean
|
|
171
|
+
transitionOpts?: Partial<Parameters<TModalContext['transition']>[2]>
|
|
172
|
+
autoOpen?: boolean
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
export function useModalSequence(ids: string[], options?: UseModalSequenceOptions) {
|
|
176
|
+
|
|
177
|
+
const _options:UseModalSequenceOptions = {
|
|
178
|
+
closeLastOnFinish: true,
|
|
179
|
+
onFinish: () => {},
|
|
180
|
+
resetOnFinish: false,
|
|
181
|
+
waitForLastToCloseBeforeCallingFinish: true,
|
|
182
|
+
...options,
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
const modals = useModalContext()
|
|
186
|
+
const [idx, setIdx] = useState(0)
|
|
187
|
+
|
|
188
|
+
const state = {
|
|
189
|
+
currentId: ids[idx],
|
|
190
|
+
nextId: ids[idx + 1],
|
|
191
|
+
previousId: ids[idx - 1],
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
onUpdate(() => {
|
|
195
|
+
if (_options.autoOpen && typeof ids?.[0] === 'number') {
|
|
196
|
+
if (!modals.isVisible(ids[0])) {
|
|
197
|
+
modals.toggleModal(ids[0])
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}, [_options.autoOpen, ids?.[0]])
|
|
201
|
+
|
|
202
|
+
function next(props?: any) {
|
|
203
|
+
if (idx === ids.length - 1) {
|
|
204
|
+
if (_options.closeLastOnFinish) {
|
|
205
|
+
modals.transition(ids[idx], null).then(() => {
|
|
206
|
+
if (_options.waitForLastToCloseBeforeCallingFinish) {
|
|
207
|
+
_options.onFinish()
|
|
208
|
+
}
|
|
209
|
+
})
|
|
210
|
+
}
|
|
211
|
+
if (_options.resetOnFinish) {
|
|
212
|
+
reset()
|
|
213
|
+
}
|
|
214
|
+
if (!(_options.waitForLastToCloseBeforeCallingFinish && _options.closeLastOnFinish)) {
|
|
215
|
+
_options.onFinish()
|
|
216
|
+
}
|
|
217
|
+
return
|
|
218
|
+
} else {
|
|
219
|
+
if (!state.nextId) return
|
|
220
|
+
modals.transition(ids[idx], state.nextId, props)
|
|
221
|
+
setIdx(i => i + 1)
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
function previous(props?: any) {
|
|
225
|
+
if (!state.previousId) return
|
|
226
|
+
|
|
227
|
+
modals.transition(ids[idx], state.previousId, props)
|
|
228
|
+
setIdx(i => i - 1)
|
|
229
|
+
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
function goto(idxOrId: string | number, props?: any) {
|
|
233
|
+
let newId:string = null
|
|
234
|
+
if (TypeGuards.isString(idxOrId)) {
|
|
235
|
+
newId = idxOrId
|
|
236
|
+
|
|
237
|
+
} else {
|
|
238
|
+
newId = ids[idxOrId]
|
|
239
|
+
}
|
|
240
|
+
modals.transition(ids[idx], newId, {
|
|
241
|
+
props,
|
|
242
|
+
})
|
|
243
|
+
setIdx(ids.indexOf(newId))
|
|
244
|
+
}
|
|
245
|
+
function reset() {
|
|
246
|
+
setIdx(0)
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
return {
|
|
250
|
+
reset,
|
|
251
|
+
next,
|
|
252
|
+
previous,
|
|
253
|
+
setModal: setIdx,
|
|
254
|
+
goto,
|
|
255
|
+
currentIdx: idx,
|
|
256
|
+
...state,
|
|
257
|
+
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { useModalContext, useModalSequence, Provider } from './context'
|
|
2
|
+
import { Modal, ManagedModalProps, Drawer, ManagedDrawerProps } from './components'
|
|
3
|
+
|
|
4
|
+
export const ModalManager = {
|
|
5
|
+
useModalContext,
|
|
6
|
+
Modal,
|
|
7
|
+
Drawer,
|
|
8
|
+
Provider,
|
|
9
|
+
useModalSequence,
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export type {
|
|
13
|
+
ManagedModalProps,
|
|
14
|
+
ManagedDrawerProps,
|
|
15
|
+
|
|
16
|
+
}
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
/* eslint no-restricted-imports: 'off' */
|
|
2
|
+
/* eslint prefer-const: 'off' */
|
|
3
|
+
import { Alert, AlertButton } from 'react-native'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Alert.{function} receives three parameters, the last one being an object:
|
|
7
|
+
*
|
|
8
|
+
* @param {string} title text to appear in title
|
|
9
|
+
* @param {string} body text to appear in body
|
|
10
|
+
* @param {object} options array of buttons and callbacks
|
|
11
|
+
*
|
|
12
|
+
* options properties
|
|
13
|
+
* @property {string} name object description
|
|
14
|
+
* @property {function} run callback function to run on press
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
type NativeAlertArgs = Parameters<typeof Alert.alert>
|
|
18
|
+
|
|
19
|
+
export type OSAlertArgs = {
|
|
20
|
+
title: NativeAlertArgs['0']
|
|
21
|
+
body?: NativeAlertArgs['1']
|
|
22
|
+
options?: NativeAlertArgs['2']
|
|
23
|
+
}
|
|
24
|
+
export type AlertEvent = AlertButton['onPress']
|
|
25
|
+
export type OSAlertType = 'info' | 'error' | 'warn' | 'ask'
|
|
26
|
+
export type NamedEvents<E extends string> = Partial<Record<E, AlertEvent>>
|
|
27
|
+
|
|
28
|
+
const currentAlerts = {
|
|
29
|
+
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function generateAlertID(from:OSAlertArgs & {type: OSAlertType}) {
|
|
33
|
+
return [
|
|
34
|
+
from.type,
|
|
35
|
+
from.title,
|
|
36
|
+
from.body,
|
|
37
|
+
].join('-')
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function shouldShowAlert(args:OSAlertArgs, type: OSAlertType) {
|
|
41
|
+
const alertId = generateAlertID({ ...args, type })
|
|
42
|
+
|
|
43
|
+
if (!Object.keys(currentAlerts).includes(alertId)) {
|
|
44
|
+
currentAlerts[alertId] = true
|
|
45
|
+
|
|
46
|
+
return alertId
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return ''
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function popAlert(id:string) {
|
|
53
|
+
delete currentAlerts[id]
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function ask({ title, body, options = null }: OSAlertArgs) {
|
|
57
|
+
if (!title) {
|
|
58
|
+
title = 'Quick quetion'
|
|
59
|
+
}
|
|
60
|
+
_OSAlert({
|
|
61
|
+
title,
|
|
62
|
+
body,
|
|
63
|
+
options,
|
|
64
|
+
})
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function warn(args: OSAlertArgs & NamedEvents<'onReject' | 'onAccept'>) {
|
|
68
|
+
const id = shouldShowAlert(args, 'warn')
|
|
69
|
+
|
|
70
|
+
if (!id) return
|
|
71
|
+
let {
|
|
72
|
+
title,
|
|
73
|
+
body,
|
|
74
|
+
onAccept,
|
|
75
|
+
onReject,
|
|
76
|
+
} = args
|
|
77
|
+
|
|
78
|
+
if (!title) {
|
|
79
|
+
title = 'Hang on'
|
|
80
|
+
}
|
|
81
|
+
if (!body) {
|
|
82
|
+
body = 'Are you sure?'
|
|
83
|
+
}
|
|
84
|
+
if (!onReject) {
|
|
85
|
+
onReject = () => null
|
|
86
|
+
}
|
|
87
|
+
_OSAlert({
|
|
88
|
+
title,
|
|
89
|
+
body,
|
|
90
|
+
options: [
|
|
91
|
+
{
|
|
92
|
+
text: 'Cancel',
|
|
93
|
+
onPress: () => {
|
|
94
|
+
popAlert(id)
|
|
95
|
+
onReject()
|
|
96
|
+
},
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
text: 'OK',
|
|
100
|
+
style: 'destructive',
|
|
101
|
+
onPress: () => {
|
|
102
|
+
popAlert(id)
|
|
103
|
+
onAccept?.()
|
|
104
|
+
},
|
|
105
|
+
},
|
|
106
|
+
],
|
|
107
|
+
})
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function info(args: OSAlertArgs & NamedEvents<'onDismiss'>) {
|
|
111
|
+
const id = shouldShowAlert(args, 'info')
|
|
112
|
+
|
|
113
|
+
if (!id) return
|
|
114
|
+
let {
|
|
115
|
+
title,
|
|
116
|
+
body,
|
|
117
|
+
onDismiss = () => null,
|
|
118
|
+
} = args
|
|
119
|
+
|
|
120
|
+
if (!title) {
|
|
121
|
+
title = 'FYI'
|
|
122
|
+
}
|
|
123
|
+
_OSAlert({
|
|
124
|
+
title,
|
|
125
|
+
body,
|
|
126
|
+
options: [
|
|
127
|
+
{
|
|
128
|
+
text: 'OK',
|
|
129
|
+
onPress: () => {
|
|
130
|
+
popAlert(id)
|
|
131
|
+
onDismiss?.()
|
|
132
|
+
},
|
|
133
|
+
},
|
|
134
|
+
],
|
|
135
|
+
})
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function OSError(args: OSAlertArgs & NamedEvents<'onDismiss'>) {
|
|
139
|
+
const id = shouldShowAlert(args, 'error')
|
|
140
|
+
|
|
141
|
+
if (!id) return
|
|
142
|
+
|
|
143
|
+
let {
|
|
144
|
+
title,
|
|
145
|
+
body,
|
|
146
|
+
} = args
|
|
147
|
+
|
|
148
|
+
if (!title) {
|
|
149
|
+
title = 'Whoops!'
|
|
150
|
+
}
|
|
151
|
+
if (!body) {
|
|
152
|
+
body = 'Something went wrong'
|
|
153
|
+
}
|
|
154
|
+
_OSAlert({
|
|
155
|
+
title,
|
|
156
|
+
body,
|
|
157
|
+
options: [
|
|
158
|
+
{
|
|
159
|
+
text: 'OK',
|
|
160
|
+
onPress: () => {
|
|
161
|
+
popAlert(id)
|
|
162
|
+
args?.onDismiss?.()
|
|
163
|
+
},
|
|
164
|
+
},
|
|
165
|
+
],
|
|
166
|
+
})
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function _OSAlert(params: OSAlertArgs) {
|
|
170
|
+
Alert.alert(params.title, params.body, params.options, {
|
|
171
|
+
cancelable: false,
|
|
172
|
+
})
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
export const OSAlert = {
|
|
176
|
+
ask,
|
|
177
|
+
warn,
|
|
178
|
+
info,
|
|
179
|
+
error: OSError,
|
|
180
|
+
}
|