@codeleap/mobile 2.3.0 → 2.3.2

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 (126) hide show
  1. package/dist/components/Button/index.d.ts +0 -1
  2. package/dist/components/Button/index.js +5 -12
  3. package/dist/components/Button/index.js.map +1 -1
  4. package/dist/components/List/styles.js +1 -1
  5. package/dist/components/List/styles.js.map +1 -1
  6. package/dist/components/Touchable/index.js +11 -9
  7. package/dist/components/Touchable/index.js.map +1 -1
  8. package/dist/utils/theme.d.ts +1 -0
  9. package/dist/utils/theme.js +7 -2
  10. package/dist/utils/theme.js.map +1 -1
  11. package/package.json +1 -1
  12. package/src/components/ActionIcon/index.tsx +32 -0
  13. package/src/components/ActionIcon/styles.ts +97 -0
  14. package/src/components/ActivityIndicator/index.tsx +50 -0
  15. package/src/components/ActivityIndicator/styles.ts +68 -0
  16. package/src/components/Animated.tsx +34 -0
  17. package/src/components/AutoComplete/index.tsx +163 -0
  18. package/src/components/AutoComplete/styles.ts +44 -0
  19. package/src/components/Backdrop/index.tsx +48 -0
  20. package/src/components/Backdrop/styles.ts +33 -0
  21. package/src/components/Button/index.tsx +154 -0
  22. package/src/components/Button/styles.ts +129 -0
  23. package/src/components/Calendar/index.tsx +65 -0
  24. package/src/components/Calendar/style.ts +35 -0
  25. package/src/components/Calendar/types.ts +102 -0
  26. package/src/components/Checkbox/index.tsx +91 -0
  27. package/src/components/Checkbox/styles.ts +81 -0
  28. package/src/components/ContentView/index.tsx +63 -0
  29. package/src/components/ContentView/styles.ts +24 -0
  30. package/src/components/Drawer/index.tsx +33 -0
  31. package/src/components/Drawer/styles.ts +43 -0
  32. package/src/components/EmptyPlaceholder/index.tsx +88 -0
  33. package/src/components/EmptyPlaceholder/styles.ts +58 -0
  34. package/src/components/FileInput/index.tsx +181 -0
  35. package/src/components/FileInput/styles.ts +15 -0
  36. package/src/components/Grid/index.tsx +117 -0
  37. package/src/components/Grid/styles.ts +11 -0
  38. package/src/components/Icon/index.tsx +69 -0
  39. package/src/components/Icon/styles.ts +57 -0
  40. package/src/components/Image/index.tsx +91 -0
  41. package/src/components/Image/styles.ts +20 -0
  42. package/src/components/ImageView/Spotlight.tsx +157 -0
  43. package/src/components/ImageView/component.tsx +38 -0
  44. package/src/components/ImageView/index.ts +2 -0
  45. package/src/components/InputLabel/index.tsx +38 -0
  46. package/src/components/InputLabel/styles.ts +19 -0
  47. package/src/components/List/PaginationIndicator.tsx +71 -0
  48. package/src/components/List/index.tsx +114 -0
  49. package/src/components/List/styles.ts +19 -0
  50. package/src/components/Modal/index.tsx +218 -0
  51. package/src/components/Modal/styles.ts +153 -0
  52. package/src/components/MultiSelect/index.tsx +138 -0
  53. package/src/components/MultiSelect/styles.ts +18 -0
  54. package/src/components/MultiSelect/types.ts +42 -0
  55. package/src/components/Navigation/Navigation.tsx +54 -0
  56. package/src/components/Navigation/constants.ts +8 -0
  57. package/src/components/Navigation/index.tsx +3 -0
  58. package/src/components/Navigation/types.ts +35 -0
  59. package/src/components/Navigation/utils.tsx +57 -0
  60. package/src/components/Pager/index.tsx +121 -0
  61. package/src/components/Pager/styles.ts +81 -0
  62. package/src/components/RadioInput/index.tsx +106 -0
  63. package/src/components/RadioInput/styles.ts +67 -0
  64. package/src/components/Scroll/index.tsx +124 -0
  65. package/src/components/Scroll/styles.ts +18 -0
  66. package/src/components/Sections/index.tsx +91 -0
  67. package/src/components/SegmentedControl/index.tsx +204 -0
  68. package/src/components/SegmentedControl/styles.ts +89 -0
  69. package/src/components/Select/index.tsx +167 -0
  70. package/src/components/Select/styles.ts +62 -0
  71. package/src/components/Select/types.ts +43 -0
  72. package/src/components/Slider/Mark.tsx +46 -0
  73. package/src/components/Slider/Thumb.tsx +29 -0
  74. package/src/components/Slider/index.tsx +130 -0
  75. package/src/components/Slider/styles.ts +76 -0
  76. package/src/components/Slider/types.ts +30 -0
  77. package/src/components/Switch/index.tsx +91 -0
  78. package/src/components/Switch/styles.ts +38 -0
  79. package/src/components/Text/index.tsx +97 -0
  80. package/src/components/Text/styles.ts +50 -0
  81. package/src/components/TextInput/index.tsx +319 -0
  82. package/src/components/TextInput/styles.ts +127 -0
  83. package/src/components/Touchable/index.tsx +174 -0
  84. package/src/components/Touchable/styles.ts +28 -0
  85. package/src/components/View/index.tsx +103 -0
  86. package/src/components/View/styles.ts +24 -0
  87. package/src/components/components.ts +42 -0
  88. package/src/components/defaultStyles.ts +62 -0
  89. package/src/components/legacy/Modal/index.tsx +163 -0
  90. package/src/components/legacy/Modal/styles.ts +125 -0
  91. package/src/components/legacy/Pager/index.tsx +242 -0
  92. package/src/components/legacy/Pager/styles.ts +51 -0
  93. package/src/components/legacy/index.ts +2 -0
  94. package/src/modules/documentPicker.ts +7 -0
  95. package/src/modules/fastImage.ts +2 -0
  96. package/src/modules/imageCropPicker.d.ts +497 -0
  97. package/src/modules/index.d.ts +682 -0
  98. package/src/modules/reactNavigation.ts +15 -0
  99. package/src/modules/textInputMask.ts +11 -0
  100. package/src/modules/types/documentPicker.d.ts +215 -0
  101. package/src/modules/types/fileTypes.ts +138 -0
  102. package/src/modules/types/textInputMask.ts +9 -0
  103. package/src/types/index.ts +1 -0
  104. package/src/types/utility.ts +9 -0
  105. package/src/utils/KeyboardAware/context.tsx +75 -0
  106. package/src/utils/KeyboardAware/index.ts +17 -0
  107. package/src/utils/KeyboardAware/keyboardHooks.ts +124 -0
  108. package/src/utils/KeyboardAware/lib/KeyboardAwareFlatList.ts +4 -0
  109. package/src/utils/KeyboardAware/lib/KeyboardAwareHOC.tsx +618 -0
  110. package/src/utils/KeyboardAware/lib/KeyboardAwareInterface.ts +13 -0
  111. package/src/utils/KeyboardAware/lib/KeyboardAwareScrollView.ts +6 -0
  112. package/src/utils/KeyboardAware/lib/KeyboardAwareSectionList.ts +6 -0
  113. package/src/utils/KeyboardAware/types.ts +159 -0
  114. package/src/utils/ModalManager/components.tsx +112 -0
  115. package/src/utils/ModalManager/context.tsx +260 -0
  116. package/src/utils/ModalManager/index.ts +16 -0
  117. package/src/utils/OSAlert.ts +180 -0
  118. package/src/utils/PermissionManager/context.tsx +302 -0
  119. package/src/utils/PermissionManager/index.ts +20 -0
  120. package/src/utils/PermissionManager/types.ts +24 -0
  121. package/src/utils/hooks.ts +163 -0
  122. package/src/utils/index.ts +11 -0
  123. package/src/utils/input.ts +51 -0
  124. package/src/utils/misc.ts +83 -0
  125. package/src/utils/notifications.ts +206 -0
  126. package/src/utils/theme.ts +58 -0
@@ -0,0 +1,157 @@
1
+ import React, { useContext, useState } from 'react'
2
+ import { deepEqual, onUpdate, ReactState, TypeGuards, usePrevious, useUnmount } from '@codeleap/common'
3
+ // @ts-ignore
4
+ import { ImageSource } from 'react-native-image-viewing/dist/@types'
5
+ import uuid from 'react-native-uuid'
6
+ import { ImageView } from './component'
7
+ import { ImageProps } from '../Image'
8
+ type TImage = {
9
+ source: ImageSource
10
+ created: number
11
+ id: string
12
+
13
+ }
14
+ type ImageList = Record<string, TImage>
15
+
16
+ type SpotlightState = ReactState<Record<string, ImageList>>
17
+ type IndexState = ReactState<Record<string, number>>
18
+ type TSpotlightCtx = {
19
+ spotlights: SpotlightState[0]
20
+ setSpotlights: SpotlightState[1]
21
+ indexes: IndexState[0]
22
+ setIndexes: IndexState[1]
23
+
24
+ }
25
+
26
+ const SpotlightCtx = React.createContext({} as TSpotlightCtx)
27
+
28
+ export const SpotlightProvider:React.FC = ({ children }) => {
29
+ const [spotlights, setSpotlights] = useState<TSpotlightCtx['spotlights']>({})
30
+ const [indexes, setIndexes] = useState<TSpotlightCtx['indexes']>({})
31
+ const ctxValue:TSpotlightCtx = {
32
+ spotlights,
33
+ setSpotlights,
34
+ indexes,
35
+ setIndexes,
36
+ }
37
+
38
+ return <SpotlightCtx.Provider value={ctxValue}>
39
+ {children}
40
+ </SpotlightCtx.Provider>
41
+ }
42
+
43
+ export function useSpotlight(name: string) {
44
+ const ctx = useContext(SpotlightCtx)
45
+
46
+ const imList =
47
+ Object.values(ctx.spotlights[name] || {})
48
+
49
+ return {
50
+ images: imList,
51
+ currentIndex: ctx.indexes[name],
52
+ set(img: ImageSource, id?: string) {
53
+ const newId = id || uuid.v4() as string
54
+ ctx.setSpotlights((prev) => {
55
+ const images = { ...prev[name] }
56
+
57
+ if (id !== null) {
58
+ images[id] = {
59
+ ...images[id],
60
+ source: img,
61
+ }
62
+ } else {
63
+ const now = Date.now()
64
+
65
+ images[newId] = {
66
+ created: now,
67
+ id: newId,
68
+ source: img,
69
+ }
70
+ }
71
+
72
+ return {
73
+ ...prev,
74
+ [name]: images,
75
+ }
76
+ })
77
+
78
+ return newId
79
+ },
80
+ remove(id: string) {
81
+ ctx.setSpotlights((prev) => {
82
+ const images = { ...prev[name] }
83
+ delete images[id]
84
+ return {
85
+ ...prev,
86
+ [name]: images,
87
+ }
88
+ })
89
+ },
90
+ open(id: string) {
91
+ const newIdx = imList.findIndex(x => x.id === id)
92
+ ctx.setIndexes((prev) => ({
93
+ ...prev,
94
+ [name]: newIdx,
95
+ }))
96
+ },
97
+ close() {
98
+
99
+ ctx.setIndexes((prev) => ({
100
+ ...prev,
101
+ [name]: undefined,
102
+ }))
103
+ },
104
+ clear() {
105
+ ctx.setIndexes((prev) => ({
106
+ ...prev,
107
+ [name]: undefined,
108
+ }))
109
+ ctx.setSpotlights((prev) => {
110
+
111
+ return {
112
+ ...prev,
113
+ [name]: {},
114
+ }
115
+ })
116
+ },
117
+ }
118
+ }
119
+
120
+ export const useImageSpotlight = (name: string | null, src: ImageProps['source']) => {
121
+ const [id, setId] = useState(null)
122
+ const spotlight = useSpotlight(name)
123
+ const imSource = TypeGuards.isString(src) ? { uri: src } : src as TImage['source']
124
+ const prevSource = usePrevious(imSource)
125
+
126
+ onUpdate(() => {
127
+ if (!name) return
128
+ if (deepEqual(prevSource, imSource)) return
129
+ setId(spotlight.set(imSource, id))
130
+ }, [src, id])
131
+
132
+ useUnmount(() => {
133
+ if (!name) return
134
+ spotlight.remove(id)
135
+ })
136
+
137
+ return {
138
+ onImagePressed: () => {
139
+ spotlight.open(id)
140
+ },
141
+ }
142
+ }
143
+
144
+ export const Spotlight = ({ name }) => {
145
+ const spotlight = useSpotlight(name)
146
+ useUnmount(() => {
147
+ spotlight.clear()
148
+ })
149
+ return <ImageView
150
+ imageIndex={spotlight.currentIndex}
151
+ images={spotlight.images.map(x => x.source)}
152
+ keyExtractor={(_, index) => index.toString()}
153
+ onRequestClose={spotlight.close}
154
+ visible={typeof spotlight.currentIndex !== 'undefined'}
155
+
156
+ />
157
+ }
@@ -0,0 +1,38 @@
1
+ import * as React from 'react'
2
+
3
+ import { onUpdate, PropsOf } from '@codeleap/common'
4
+
5
+ // @ts-ignore
6
+ import _ImageView from 'react-native-image-viewing'
7
+ import { StatusBar } from 'react-native'
8
+ import { View } from '../View'
9
+ import { Text } from '../Text'
10
+
11
+ type FooterComponentType = React.ComponentType<{
12
+ imageIndex: number
13
+ imagesLength: number
14
+ }>
15
+
16
+ const FooterComponent: FooterComponentType = ({ imageIndex, imagesLength }) => (
17
+ <View variants={['marginBottom:5', 'alignCenter']}>
18
+ <Text text={imageIndex + 1 + '/' + imagesLength}/>
19
+ </View>
20
+ )
21
+
22
+ export type ImageViewProps = PropsOf<typeof _ImageView>
23
+
24
+ export const ImageView: React.FC<ImageViewProps> = (props) => {
25
+ onUpdate(() => {
26
+ StatusBar.setHidden(props.visible)
27
+ // StatusBar.setBarStyle(`${props.visible ? 'light' : 'dark'}-content`)
28
+ }, [props.visible])
29
+
30
+ return (
31
+ <_ImageView
32
+ doubleTapToZoomEnabled={false}
33
+ FooterComponent={({ imageIndex }) => <FooterComponent imageIndex={imageIndex} imagesLength={props.images.length}/>}
34
+ presentationStyle={'overFullScreen'}
35
+ {...props}
36
+ />
37
+ )
38
+ }
@@ -0,0 +1,2 @@
1
+ export * from './Spotlight'
2
+ export * from './component'
@@ -0,0 +1,38 @@
1
+ import React from 'react'
2
+ import { ComponentVariants, FormTypes, useDefaultComponentStyle } from '@codeleap/common'
3
+ import { StylesOf } from '../../types'
4
+ import { View, ViewProps } from '../View'
5
+ import { InputLabelComposition, InputLabelStyles } from './styles'
6
+ import { Text } from '../Text'
7
+ import { StyleSheet } from 'react-native'
8
+
9
+ export type InputLabelProps = ViewProps & {
10
+ styles?: StylesOf<InputLabelComposition>
11
+ label?: FormTypes.Label
12
+ required?: boolean
13
+ } & ComponentVariants<typeof InputLabelStyles>
14
+
15
+ export * from './styles'
16
+
17
+ export const InputLabel:React.FC<InputLabelProps> = (props) => {
18
+ const { label, required = false, variants = [], styles = {}, style, ...viewProps } = props
19
+ const variantStyles = useDefaultComponentStyle<'u:InputLabel', typeof InputLabelStyles>('u:InputLabel', {
20
+ variants, styles, transform: StyleSheet.flatten,
21
+ })
22
+
23
+ if (!label) return null
24
+
25
+ switch (typeof label) {
26
+ case 'string':
27
+ return <View {...viewProps} style={[variantStyles.wrapper, style]}>
28
+ <Text style={variantStyles.text} text={label} />
29
+ {required && <Text style={variantStyles.asterisk} text={' *'} />}
30
+ </View>
31
+ case 'object':
32
+ return <>
33
+ {label as React.ReactNode}
34
+ </>
35
+ default:
36
+ return null
37
+ }
38
+ }
@@ -0,0 +1,19 @@
1
+ import { assignTextStyle, createDefaultVariantFactory } from '@codeleap/common'
2
+
3
+ export type InputLabelComposition = 'text' | 'wrapper' | 'asterisk'
4
+
5
+ const createInputLabelStyle = createDefaultVariantFactory<InputLabelComposition>()
6
+
7
+ export const InputLabelStyles = {
8
+ default: createInputLabelStyle((theme) => ({
9
+ asterisk: {
10
+ color: theme.colors.negative,
11
+ },
12
+ wrapper: {
13
+ ...theme.presets.row,
14
+ },
15
+ text: {
16
+ ...assignTextStyle('h5')(theme).text,
17
+ },
18
+ })),
19
+ }
@@ -0,0 +1,71 @@
1
+ import React from 'react'
2
+ import {
3
+ assignTextStyle,
4
+ ComponentVariants,
5
+ createDefaultVariantFactory,
6
+ getNestedStylesByKey,
7
+ includePresets,
8
+ TypeGuards,
9
+ useDefaultComponentStyle,
10
+ } from '@codeleap/common'
11
+ import { StyleSheet, ViewStyle } from 'react-native'
12
+ import { StylesOf } from '../../types'
13
+ import { ActivityIndicator, ActivityIndicatorComposition } from '../ActivityIndicator'
14
+ import { Text } from '../Text'
15
+
16
+ export type PaginationIndicatorComposition = 'text' | `loader${Capitalize<ActivityIndicatorComposition>}`
17
+
18
+ const createPaginationIndicatorStyle = createDefaultVariantFactory<PaginationIndicatorComposition>()
19
+ const presets = includePresets((style) => createPaginationIndicatorStyle(() => ({ loaderWrapper: style, text: style })))
20
+ export const PaginationIndicatorStyles = {
21
+ ...presets,
22
+ default: createPaginationIndicatorStyle((theme) => {
23
+ return {
24
+ loaderWrapper: {
25
+ ...theme.presets.center,
26
+ ...theme.spacing.marginVertical(3),
27
+ },
28
+ text: {
29
+ ...assignTextStyle('h4')(theme).text,
30
+ ...theme.presets.textCenter,
31
+ ...theme.spacing.marginVertical(3),
32
+ ...theme.presets.fullWidth,
33
+ },
34
+ }
35
+ }),
36
+ }
37
+
38
+ export type PaginationIndicatorProps = {
39
+ isFetching?: boolean
40
+ noMoreItemsText: React.ReactChild
41
+ hasMore?: boolean
42
+ activityIndicator?: JSX.Element
43
+ styles?: StylesOf<PaginationIndicatorComposition>
44
+ style?: ViewStyle
45
+ } & ComponentVariants<typeof PaginationIndicatorStyles>
46
+
47
+ export const PaginationIndicator = (props: PaginationIndicatorProps) => {
48
+ const { hasMore, isFetching, noMoreItemsText, style, activityIndicator, styles = {}, variants = [] } = props
49
+
50
+ const variantStyles = useDefaultComponentStyle<
51
+ 'u:PaginationIndicator',
52
+ typeof PaginationIndicatorStyles
53
+ >('u:PaginationIndicator', {
54
+ variants,
55
+ styles,
56
+ transform: StyleSheet.flatten,
57
+ })
58
+
59
+ const loaderStyles = getNestedStylesByKey('loader', variantStyles)
60
+
61
+ if (isFetching) {
62
+ return activityIndicator || <ActivityIndicator style={style} styles={loaderStyles}/>
63
+ }
64
+ if (!hasMore) {
65
+ if (TypeGuards.isString(noMoreItemsText) || TypeGuards.isNumber(noMoreItemsText)) {
66
+ return <Text style={[variantStyles.text, style]} text={noMoreItemsText.toString()}/>
67
+ }
68
+ return noMoreItemsText
69
+ }
70
+ return null
71
+ }
@@ -0,0 +1,114 @@
1
+ import * as React from 'react'
2
+ import { forwardRef } from 'react'
3
+ import {
4
+
5
+ useDefaultComponentStyle,
6
+
7
+ ComponentVariants,
8
+ useCallback,
9
+ } from '@codeleap/common'
10
+
11
+ import { RefreshControl, FlatList, FlatListProps as RNFlatListProps, ListRenderItemInfo, StyleSheet, RefreshControlProps } from 'react-native'
12
+ import { View, ViewProps } from '../View'
13
+ import { EmptyPlaceholder, EmptyPlaceholderProps } from '../EmptyPlaceholder'
14
+ import { ListComposition, ListStyles } from './styles'
15
+ import { StylesOf } from '../../types'
16
+ import { GetKeyboardAwarePropsOptions, useKeyboardAwareView } from '../../utils'
17
+
18
+ export type DataboundFlatListPropsTypes = 'data' | 'renderItem' | 'keyExtractor' | 'getItemLayout'
19
+
20
+ export type ReplaceFlatlistProps<P, T> = Omit<P, DataboundFlatListPropsTypes> & {
21
+ data: T[]
22
+ keyExtractor?: (item: T, index: number) => string
23
+ renderItem: (data: ListRenderItemInfo<T>) => React.ReactElement
24
+ onRefresh?: () => void
25
+ getItemLayout?: ((
26
+ data:T,
27
+ index: number,
28
+ ) => { length: number; offset: number; index: number })
29
+ }
30
+
31
+ export * from './styles'
32
+ export * from './PaginationIndicator'
33
+
34
+ export type FlatListProps<
35
+ T = any[],
36
+ Data = T extends Array<infer D> ? D : never
37
+ > =RNFlatListProps<Data> &
38
+ Omit<ViewProps, 'variants'> & {
39
+ separators?: boolean
40
+ placeholder?: EmptyPlaceholderProps
41
+ keyboardAware?: GetKeyboardAwarePropsOptions
42
+ styles?: StylesOf<ListComposition>
43
+ refreshControlProps?: Partial<RefreshControlProps>
44
+ } & ComponentVariants<typeof ListStyles>
45
+
46
+ const ListCP = forwardRef<FlatList, FlatListProps>(
47
+ (flatListProps, ref) => {
48
+ const {
49
+ variants = [],
50
+ style,
51
+ styles = {},
52
+ onRefresh,
53
+ component,
54
+ refreshing,
55
+ placeholder,
56
+ keyboardAware,
57
+ refreshControlProps = {},
58
+ ...props
59
+ } = flatListProps
60
+
61
+ const variantStyles = useDefaultComponentStyle<'u:List', typeof ListStyles>('u:List', {
62
+ variants,
63
+ styles,
64
+ transform: StyleSheet.flatten,
65
+
66
+ })
67
+
68
+ const renderSeparator = useCallback(() => {
69
+ return (
70
+ <View style={variantStyles.separator}></View>
71
+ )
72
+ }, [])
73
+
74
+ const isEmpty = !props.data || !props.data.length
75
+ const separator = !isEmpty && props?.separators && renderSeparator
76
+
77
+ const Component:any = component || FlatList
78
+ const refreshStyles = StyleSheet.flatten([variantStyles.refreshControl, styles.refreshControl])
79
+
80
+ const _listProps = {
81
+ style: [variantStyles.wrapper, style],
82
+ contentContainerStyle: variantStyles.content,
83
+ ref: ref as unknown as FlatList,
84
+ ItemSeparatorComponent: separator,
85
+ refreshControl: !!onRefresh && (
86
+ <RefreshControl
87
+ refreshing={refreshing}
88
+ onRefresh={onRefresh}
89
+ tintColor={refreshStyles?.color}
90
+ colors={[refreshStyles?.color]}
91
+ {...refreshControlProps}
92
+ />
93
+ ),
94
+ ListEmptyComponent: <EmptyPlaceholder {...placeholder}/>,
95
+ ...props,
96
+ }
97
+ const keyboard = useKeyboardAwareView()
98
+ const listProps = keyboard.getKeyboardAwareProps(_listProps, {
99
+ adapt: 'paddingBottom',
100
+ baseStyleProp: 'contentContainerStyle',
101
+ ...keyboardAware,
102
+ })
103
+ return (
104
+ <Component
105
+ {...listProps}
106
+ />
107
+ )
108
+ },
109
+ )
110
+
111
+ export type ListComponentType = <T extends any[] = any[]>(props: FlatListProps<T>) => React.ReactElement
112
+
113
+ export const List = ListCP as ListComponentType
114
+
@@ -0,0 +1,19 @@
1
+ import { createDefaultVariantFactory } from '@codeleap/common'
2
+ import { ScrollComposition, ScrollStyles } from '../Scroll/styles'
3
+
4
+ export type ListComposition = ScrollComposition | 'separator'
5
+
6
+ const createListStyle = createDefaultVariantFactory<ListComposition>()
7
+
8
+ export const ListStyles = {
9
+ default: createListStyle((theme) => {
10
+ const defaultStyles = ScrollStyles.default(theme)
11
+ return {
12
+ ...defaultStyles,
13
+ separator: {
14
+ width: '100%',
15
+ height: theme.spacing.value(1),
16
+ },
17
+ }
18
+ }),
19
+ }
@@ -0,0 +1,218 @@
1
+ import * as React from 'react'
2
+ import { View, ViewProps } from '../View'
3
+ import { Button, ButtonProps } from '../Button'
4
+ import { Scroll } from '../Scroll'
5
+ import {
6
+ ComponentVariants,
7
+ getNestedStylesByKey,
8
+ IconPlaceholder,
9
+ onUpdate,
10
+ PropsOf,
11
+ TypeGuards,
12
+ useDefaultComponentStyle,
13
+ } from '@codeleap/common'
14
+ import {
15
+ ModalComposition,
16
+ ModalStyles,
17
+ ModalParts,
18
+ } from './styles'
19
+ import { StyleSheet } from 'react-native'
20
+ import { StylesOf } from '../../types/utility'
21
+
22
+ import { useDynamicAnimation } from 'moti'
23
+ import { Backdrop } from '../Backdrop'
24
+ import { useBackButton, useStaticAnimationStyles } from '../../utils/hooks'
25
+ import { Text, TextProps } from '../Text'
26
+ import { Touchable } from '../Touchable'
27
+ import { GetKeyboardAwarePropsOptions } from '../../utils'
28
+
29
+ export * from './styles'
30
+
31
+ export type ModalProps = Omit<ViewProps, 'variants' | 'styles'> & {
32
+ variants?: ComponentVariants<typeof ModalStyles>['variants']
33
+ styles?: StylesOf<ModalComposition>
34
+ dismissOnBackdrop?: boolean
35
+ buttonProps?: ButtonProps
36
+ accessible?: boolean
37
+ showClose?: boolean
38
+ closable?: boolean
39
+ footer?: React.ReactNode
40
+ title?: React.ReactNode
41
+ debugName: string
42
+ closeIconName?: IconPlaceholder
43
+ visible: boolean
44
+ toggle?: () => void
45
+ zIndex?: number
46
+ scroll?: boolean
47
+ header?: React.ReactElement
48
+ closeOnHardwareBackPress?: boolean
49
+ renderHeader?: (props: ModalHeaderProps) => React.ReactElement
50
+ keyboardAware?: GetKeyboardAwarePropsOptions
51
+ scrollProps?: PropsOf<typeof Scroll>
52
+ }
53
+
54
+ export type ModalHeaderProps = Omit<ModalProps, 'styles' | 'renderHeader'> & {
55
+ styles: {
56
+ wrapper: ViewProps['style']
57
+ title: TextProps['style']
58
+ closeButton: ButtonProps['styles']
59
+ }
60
+ }
61
+
62
+ const DefaultHeader:React.FC<ModalHeaderProps> = (props) => {
63
+ const { styles, title = null, showClose = false, closable, debugName, closeIconName = 'close', toggle } = props
64
+ return <>
65
+ {(title || showClose) && (
66
+ <View style={styles.wrapper}>
67
+ {typeof title === 'string' ? (
68
+ <Text text={title} style={styles.title} />
69
+ ) : (
70
+ title
71
+ )}
72
+
73
+ {(showClose && closable) && (
74
+ <Button
75
+ debugName={`${debugName} modal close button`}
76
+ icon={closeIconName as IconPlaceholder}
77
+ variants={['icon']}
78
+ onPress={toggle}
79
+ styles={styles.closeButton}
80
+ />
81
+ )}
82
+ </View>
83
+ )}</>
84
+ }
85
+
86
+ export const Modal: React.FC<ModalProps> = (modalProps) => {
87
+ const {
88
+ variants = [],
89
+ styles = {},
90
+ visible,
91
+
92
+ closable = true,
93
+
94
+ footer,
95
+ children,
96
+ toggle = () => null,
97
+ dismissOnBackdrop = true,
98
+ header = null,
99
+ debugName,
100
+ scroll = true,
101
+ renderHeader,
102
+ zIndex = null,
103
+ scrollProps = {},
104
+ closeOnHardwareBackPress = true,
105
+ ...props
106
+ } = modalProps
107
+
108
+ const variantStyles = useDefaultComponentStyle('u:Modal', {
109
+ variants: variants as any,
110
+ transform: StyleSheet.flatten,
111
+ styles,
112
+ }) as ModalProps['styles']
113
+
114
+ function getStyles(key: ModalParts) {
115
+ const s = [
116
+ variantStyles[key],
117
+ styles[key],
118
+ ]
119
+
120
+ return StyleSheet.flatten(s)
121
+ }
122
+ const buttonStyles = React.useMemo(() => getNestedStylesByKey('closeButton', variantStyles), [variantStyles])
123
+
124
+ const boxAnimationStates = useStaticAnimationStyles(variantStyles, ['box:hidden', 'box:visible'])
125
+
126
+ const boxAnimation = useDynamicAnimation(() => {
127
+ return visible ? boxAnimationStates['box:visible'] : boxAnimationStates['box:hidden']
128
+ })
129
+
130
+ onUpdate(() => {
131
+ boxAnimation.animateTo(visible ? boxAnimationStates['box:visible'] : boxAnimationStates['box:hidden'])
132
+ }, [visible])
133
+ const wrapperStyle = getStyles('wrapper')
134
+
135
+ const ScrollComponent = scroll ? Scroll : View
136
+ const scrollStyle = scroll ? getStyles('scroll') : getStyles('innerWrapper')
137
+
138
+ const headerProps:ModalHeaderProps = {
139
+ ...modalProps,
140
+ styles: {
141
+ wrapper: getStyles('header'),
142
+ title: getStyles('title'),
143
+ closeButton: buttonStyles,
144
+ },
145
+ }
146
+ const Header = renderHeader || DefaultHeader
147
+
148
+ useBackButton(() => {
149
+ if (visible && closeOnHardwareBackPress) {
150
+ toggle()
151
+ return true
152
+ }
153
+ }, [visible, toggle, closeOnHardwareBackPress])
154
+
155
+ return (
156
+ <View
157
+ style={[wrapperStyle, { zIndex: TypeGuards.isNumber(zIndex) ? zIndex : wrapperStyle?.zIndex }]}
158
+ pointerEvents={visible ? 'auto' : 'none'}
159
+ >
160
+
161
+ <Backdrop visible={visible} debugName={`Modal ${debugName} backdrop`} styles={{
162
+ 'wrapper:hidden': variantStyles['backdrop:hidden'],
163
+ 'wrapper:visible': variantStyles['backdrop:visible'],
164
+ wrapper: variantStyles.backdrop,
165
+ }}
166
+ wrapperProps={{
167
+ transition: { ...variantStyles['backdrop:transition'] },
168
+ }}
169
+ />
170
+ <ScrollComponent
171
+ style={scrollStyle}
172
+ contentContainerStyle={getStyles('scrollContent')}
173
+ keyboardAware= {{
174
+ adapt: 'marginBottom',
175
+ baseStyleProp: 'style',
176
+ animated: true,
177
+ enabled: visible,
178
+ enableOnAndroid: true,
179
+ }}
180
+ animated
181
+ { ...scrollProps}
182
+ >
183
+ {dismissOnBackdrop &&
184
+ <Touchable
185
+ onPress={ closable ? toggle : (() => {})}
186
+ debounce={400}
187
+ debugName={'Modal backdrop touchable'}
188
+ style={variantStyles.backdropTouchable}
189
+ android_ripple={null}
190
+ noFeedback
191
+ />}
192
+
193
+ <View
194
+ animated
195
+ state={boxAnimation}
196
+ style={getStyles('box')}
197
+ transition={{ ...variantStyles['box:transition'] }}
198
+
199
+ {...props}
200
+ >
201
+
202
+ {header ? header : <Header {...headerProps}/>}
203
+
204
+ <View style={getStyles('body')}>{children}</View>
205
+ {footer && (
206
+ <View style={getStyles('footer')}>
207
+ {typeof footer === 'string' ? <Text text={footer} /> : footer}
208
+ </View>
209
+ )}
210
+ </View>
211
+
212
+ </ScrollComponent>
213
+ </View>
214
+
215
+ )
216
+ }
217
+
218
+ export default Modal