@codeleap/mobile 1.9.28 → 1.9.29

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 (116) hide show
  1. package/dist/components/Backdrop.d.ts +66 -0
  2. package/dist/components/Backdrop.js +70 -0
  3. package/dist/components/Backdrop.js.map +1 -0
  4. package/dist/components/Button.d.ts +3 -3
  5. package/dist/components/Button.js.map +1 -1
  6. package/dist/components/FileInput.js +0 -2
  7. package/dist/components/FileInput.js.map +1 -1
  8. package/dist/components/Image.js +2 -2
  9. package/dist/components/Image.js.map +1 -1
  10. package/dist/components/List.d.ts +23 -4
  11. package/dist/components/List.js +17 -1
  12. package/dist/components/List.js.map +1 -1
  13. package/dist/components/Modal/index.d.ts +1 -1
  14. package/dist/components/Modal/index.js +26 -27
  15. package/dist/components/Modal/index.js.map +1 -1
  16. package/dist/components/Modal/styles.d.ts +3 -9
  17. package/dist/components/Modal/styles.js +26 -17
  18. package/dist/components/Modal/styles.js.map +1 -1
  19. package/dist/components/NewModal/index.d.ts +27 -0
  20. package/dist/components/NewModal/index.js +99 -0
  21. package/dist/components/NewModal/index.js.map +1 -0
  22. package/dist/components/NewModal/styles.d.ts +57 -0
  23. package/dist/components/NewModal/styles.js +58 -0
  24. package/dist/components/NewModal/styles.js.map +1 -0
  25. package/dist/components/Overlay.js +13 -9
  26. package/dist/components/Overlay.js.map +1 -1
  27. package/dist/components/Scroll.d.ts +4 -2
  28. package/dist/components/Scroll.js.map +1 -1
  29. package/dist/components/SegmentedControl/index.d.ts +42 -0
  30. package/dist/components/SegmentedControl/index.js +137 -0
  31. package/dist/components/SegmentedControl/index.js.map +1 -0
  32. package/dist/components/SegmentedControl/styles.d.ts +54 -0
  33. package/dist/components/SegmentedControl/styles.js +36 -0
  34. package/dist/components/SegmentedControl/styles.js.map +1 -0
  35. package/dist/components/SegmentedControl.d.ts +5 -0
  36. package/dist/components/SegmentedControl.js +32 -0
  37. package/dist/components/SegmentedControl.js.map +1 -0
  38. package/dist/components/Select/index.js +1 -1
  39. package/dist/components/Select/index.js.map +1 -1
  40. package/dist/components/Text.d.ts +8 -3
  41. package/dist/components/Text.js +12 -5
  42. package/dist/components/Text.js.map +1 -1
  43. package/dist/components/TextInput.d.ts +4 -2
  44. package/dist/components/TextInput.js +2 -2
  45. package/dist/components/TextInput.js.map +1 -1
  46. package/dist/components/Touchable.d.ts +5 -3
  47. package/dist/components/Touchable.js +26 -19
  48. package/dist/components/Touchable.js.map +1 -1
  49. package/dist/components/View.js +1 -1
  50. package/dist/components/View.js.map +1 -1
  51. package/dist/components/_Modal/index.d.ts +27 -0
  52. package/dist/components/_Modal/index.js +114 -0
  53. package/dist/components/_Modal/index.js.map +1 -0
  54. package/dist/components/_Modal/styles.d.ts +64 -0
  55. package/dist/components/_Modal/styles.js +60 -0
  56. package/dist/components/_Modal/styles.js.map +1 -0
  57. package/dist/components/components.d.ts +2 -0
  58. package/dist/components/components.js +2 -0
  59. package/dist/components/components.js.map +1 -1
  60. package/dist/index.d.ts +3 -0
  61. package/dist/index.js +16 -1
  62. package/dist/index.js.map +1 -1
  63. package/dist/utils/ModalManager/components.d.ts +5 -5
  64. package/dist/utils/ModalManager/components.js +16 -9
  65. package/dist/utils/ModalManager/components.js.map +1 -1
  66. package/dist/utils/ModalManager/context.d.ts +8 -3
  67. package/dist/utils/ModalManager/context.js +47 -23
  68. package/dist/utils/ModalManager/context.js.map +1 -1
  69. package/dist/utils/ModalManager/index.d.ts +3 -7
  70. package/dist/utils/ModalManager/index.js +1 -1
  71. package/dist/utils/ModalManager/index.js.map +1 -1
  72. package/dist/utils/PermissionManager/components.d.ts +18 -0
  73. package/dist/utils/PermissionManager/components.js +52 -0
  74. package/dist/utils/PermissionManager/components.js.map +1 -0
  75. package/dist/utils/PermissionManager/context.d.ts +52 -0
  76. package/dist/utils/PermissionManager/context.js +325 -0
  77. package/dist/utils/PermissionManager/context.js.map +1 -0
  78. package/dist/utils/PermissionManager/index.d.ts +4 -0
  79. package/dist/utils/PermissionManager/index.js +9 -0
  80. package/dist/utils/PermissionManager/index.js.map +1 -0
  81. package/dist/utils/PermissionManager/types.d.ts +13 -0
  82. package/dist/utils/PermissionManager/types.js +3 -0
  83. package/dist/utils/PermissionManager/types.js.map +1 -0
  84. package/dist/utils/hooks.d.ts +6 -0
  85. package/dist/utils/hooks.js +62 -0
  86. package/dist/utils/hooks.js.map +1 -0
  87. package/package.json +1 -1
  88. package/src/components/Backdrop.tsx +77 -0
  89. package/src/components/Button.tsx +3 -2
  90. package/src/components/FileInput.tsx +2 -2
  91. package/src/components/Image.tsx +3 -2
  92. package/src/components/List.tsx +44 -5
  93. package/src/components/Modal/index.tsx +38 -49
  94. package/src/components/Modal/styles.ts +35 -31
  95. package/src/components/Overlay.tsx +22 -13
  96. package/src/components/Scroll.tsx +3 -1
  97. package/src/components/SegmentedControl/index.tsx +182 -0
  98. package/src/components/SegmentedControl/styles.ts +65 -0
  99. package/src/components/Select/index.tsx +1 -2
  100. package/src/components/Text.tsx +23 -10
  101. package/src/components/TextInput.tsx +4 -2
  102. package/src/components/Touchable.tsx +31 -20
  103. package/src/components/View.tsx +1 -1
  104. package/src/components/_Modal/index.tsx +162 -0
  105. package/src/components/_Modal/styles.ts +125 -0
  106. package/src/components/components.ts +3 -0
  107. package/src/index.ts +6 -0
  108. package/src/modules/imageCropPicker.d.ts +497 -0
  109. package/src/modules/index.d.ts +186 -0
  110. package/src/utils/ModalManager/components.tsx +20 -9
  111. package/src/utils/ModalManager/context.tsx +69 -30
  112. package/src/utils/ModalManager/index.ts +6 -2
  113. package/src/utils/PermissionManager/context.tsx +299 -0
  114. package/src/utils/PermissionManager/index.ts +20 -0
  115. package/src/utils/PermissionManager/types.ts +24 -0
  116. package/src/utils/hooks.ts +65 -0
@@ -0,0 +1,182 @@
1
+ import React, { ReactElement, useImperativeHandle, useMemo, useRef } from 'react'
2
+ import { Scroll, ScrollProps } from '../Scroll'
3
+
4
+ import { Easing, EasingFunction, StyleSheet } from 'react-native'
5
+ import { PropsOf, useCodeleapContext, useDefaultComponentStyle } from '@codeleap/common'
6
+ import { SegmentedControlComposition, SegmentedControlStyles } from './styles'
7
+ import { Touchable } from '../Touchable'
8
+ import { StylesOf } from '../../types/utility'
9
+ import { Text } from '../Text'
10
+ import { KeyboardAwareScrollViewTypes } from '../../modules'
11
+ import { View } from '../View'
12
+ export * from './styles'
13
+ export type SegmentedControlRef =KeyboardAwareScrollViewTypes.KeyboardAwareScrollView & {
14
+ scrollTo: (index: number) => void
15
+ scrollToCurrent: () => void
16
+ }
17
+
18
+ export type SegmentedControlProps<T = string> = ScrollProps & {
19
+ options : {label: string; value: T }[]
20
+ onValueChange: (value: T) => any
21
+ value: T
22
+ debugName: string
23
+ animation?: {
24
+ duration?: number
25
+ easing?: EasingFunction
26
+ }
27
+ styles?: StylesOf<SegmentedControlComposition>
28
+ scrollProps?: any
29
+ RenderButton?: (props: SegmentedControlProps & {
30
+ touchableProps: PropsOf<typeof Touchable>
31
+ textProps: PropsOf<typeof Text>
32
+ option: {label: string; value: any}
33
+ }) => ReactElement
34
+ RenderAnimatedView?: (props: SegmentedControlProps) => ReactElement
35
+ }
36
+
37
+ const defaultAnimation = {
38
+ duration: 200,
39
+ // easing: Easing.linear,
40
+ }
41
+
42
+ const _SegmentedControl = React.forwardRef<SegmentedControlRef, SegmentedControlProps>((props, ref) => {
43
+ const {
44
+ options = [],
45
+ onValueChange,
46
+ debugName,
47
+ value,
48
+ styles = {},
49
+ animation = {},
50
+ variants = [],
51
+ scrollProps = {},
52
+ RenderAnimatedView,
53
+ RenderButton,
54
+
55
+ } = props
56
+ const { Theme } = useCodeleapContext()
57
+
58
+ const _animation = {
59
+ ...defaultAnimation, ...animation,
60
+ }
61
+
62
+ let variantStyles = useDefaultComponentStyle<'u:SegmentedControl', typeof SegmentedControlStyles>('u:SegmentedControl', {
63
+ styles,
64
+ transform: StyleSheet.flatten,
65
+ variants,
66
+ })
67
+
68
+ const scrollRef = useRef<KeyboardAwareScrollViewTypes.KeyboardAwareScrollView>(null)
69
+
70
+ function scrollTo(idx:number) {
71
+ if (!scrollRef.current) return
72
+ setTimeout(() => {
73
+ scrollRef.current?.scrollToPosition?.(widthStyle.width * idx, 0, true)
74
+ })
75
+ }
76
+
77
+ const widthStyle = useMemo(() => {
78
+ const maxWordLength = Object.assign([], options)
79
+ .sort((a, b) => {
80
+ return a.label.length - b.label.length
81
+ })[0].label.length
82
+ const fitMaxWidth = Theme.values.width - Theme.spacing.value(4)
83
+ let fitItemWidth = maxWordLength * options.length * 6
84
+ if (fitItemWidth * options.length < fitMaxWidth) fitItemWidth = fitMaxWidth / options.length
85
+ return { width: fitItemWidth }
86
+ }, [options])
87
+
88
+ const currentOptionIdx = options.findIndex(o => o.value === value) || 0
89
+
90
+ const translateX = widthStyle.width * currentOptionIdx
91
+
92
+ const onPress = (txt:string, idx: number) => {
93
+ return () => {
94
+ onValueChange(txt)
95
+ scrollTo(idx)
96
+ }
97
+ }
98
+
99
+ const hasScrolledInitially = useRef(false)
100
+
101
+ useImperativeHandle(ref, () => {
102
+ if (!scrollRef.current) return null
103
+
104
+ return {
105
+ ...(scrollRef.current),
106
+ scrollTo,
107
+ scrollToCurrent() {
108
+ if (!scrollRef.current) return
109
+ scrollTo(currentOptionIdx)
110
+ },
111
+ } as SegmentedControlRef
112
+ })
113
+
114
+ if (!hasScrolledInitially.current && scrollRef.current) {
115
+ scrollTo(currentOptionIdx)
116
+ hasScrolledInitially.current = true
117
+ }
118
+
119
+ const AnimatedView = RenderAnimatedView || View
120
+ variantStyles = JSON.parse(JSON.stringify(variantStyles))
121
+ return (
122
+ <Scroll
123
+ horizontal
124
+ showsHorizontalScrollIndicator={false}
125
+ style={variantStyles.scroll}
126
+ contentContainerStyle={variantStyles.scrollContent}
127
+ {...scrollProps}
128
+ ref={scrollRef}
129
+ >
130
+ <View style={variantStyles.wrapper}>
131
+ <AnimatedView {...props}
132
+ animated
133
+ style={[variantStyles.selectedBubble, widthStyle]}
134
+ animate={{
135
+ translateX,
136
+ }}
137
+
138
+ transition={_animation}
139
+ />
140
+ {options.map((o, idx) => {
141
+ const selected = value === o.value
142
+
143
+ const touchableProps = {
144
+ key: idx,
145
+ debugName: `Segmented Control ${debugName}, option ${o.label}`,
146
+ onPress: onPress(o.value, idx),
147
+ style: [widthStyle, variantStyles.button],
148
+ }
149
+
150
+ const textProps = {
151
+ text: o.label as string,
152
+ colorChangeConfig: _animation,
153
+ style: StyleSheet.flatten([variantStyles.text, selected && variantStyles['text:selected']]),
154
+ animated: true,
155
+ }
156
+
157
+ if (RenderButton) {
158
+ return (
159
+ <RenderButton {...props} touchableProps={touchableProps} key={touchableProps.key} textProps={textProps} option={o}/>
160
+ )
161
+ }
162
+ return <Touchable
163
+ {...touchableProps}
164
+ key={touchableProps.key}
165
+ >
166
+ <Text
167
+
168
+ {...textProps}
169
+ />
170
+
171
+ </Touchable>
172
+
173
+ })}
174
+ </View>
175
+ </Scroll>
176
+ )
177
+
178
+ })
179
+
180
+ type SegControlComponent = <T = string>(props: SegmentedControlProps<T> & {ref?: React.Ref<SegmentedControlRef>}) => ReactElement
181
+
182
+ export const SegmentedControl = _SegmentedControl as SegControlComponent
@@ -0,0 +1,65 @@
1
+ import { createDefaultVariantFactory, includePresets } from '@codeleap/common'
2
+
3
+ export type SegmentedControlStates = 'selected'
4
+
5
+ export type SegmentedControlComposition =
6
+ 'selectedBubble' |
7
+ 'wrapper' |
8
+ 'scroll' |
9
+ 'scrollContent' |
10
+ 'text' |
11
+ `text:${SegmentedControlStates}` |
12
+ 'button' |
13
+ `button:${SegmentedControlStates}`
14
+
15
+ const createSegmentedControlStyle = createDefaultVariantFactory<SegmentedControlComposition>()
16
+
17
+ const presets = includePresets((style) => createSegmentedControlStyle(() => ({ scrollContent: style })))
18
+
19
+ export const SegmentedControlStyles = {
20
+ ...presets,
21
+ default: createSegmentedControlStyle((theme) => {
22
+
23
+ return {
24
+
25
+ text: {
26
+ color: theme.colors.text,
27
+ },
28
+ 'text:selected': {
29
+ color: theme.colors.white,
30
+ },
31
+ scroll: {
32
+ height: theme.values.buttons.default.height,
33
+ // borderRadius: Theme.borderRadius.large,
34
+ },
35
+ scrollContent: {
36
+ // borderRadius: Theme.borderRadius.large,
37
+ ...theme.spacing.paddingHorizontal(2),
38
+ },
39
+ button: {
40
+ backgroundColor: 'transparent',
41
+ ...theme.presets.alignCenter,
42
+ color: 'red',
43
+ ...theme.spacing.padding(2),
44
+ ...theme.presets.justifyCenter,
45
+ },
46
+ selectedBubble: {
47
+ position: 'absolute',
48
+ zIndex: -1,
49
+ ...theme.spacing.padding(2),
50
+ maxHeight: 50,
51
+ minHeight: 50,
52
+ borderRadius: theme.borderRadius.large,
53
+ backgroundColor: theme.colors.primary,
54
+ },
55
+ wrapper: {
56
+ borderRadius: theme.borderRadius.large,
57
+ backgroundColor: theme.colors.backgroundSecondary,
58
+ ...theme.presets.row,
59
+ position: 'relative',
60
+ },
61
+
62
+ }
63
+
64
+ }),
65
+ }
@@ -69,7 +69,6 @@ export const Select = <T extends string|number = string>(selectProps:CustomSelec
69
69
  close()
70
70
  }
71
71
  }
72
-
73
72
  const selectedLabel:string = useMemo(() => {
74
73
  const current = options.find(o => o.value === value)
75
74
 
@@ -88,7 +87,7 @@ export const Select = <T extends string|number = string>(selectProps:CustomSelec
88
87
  }}
89
88
  editable={false}
90
89
  touchableWrapper
91
- innerWrapperProps={{
90
+ wrapperProps={{
92
91
  debugName: 'Select',
93
92
  onPress: close,
94
93
  }}
@@ -6,25 +6,38 @@ import {
6
6
  BaseViewProps,
7
7
  TextStyles,
8
8
  } from '@codeleap/common'
9
- import { Text as NativeText } from 'react-native'
10
-
9
+ import { Animated, StyleSheet, Text as NativeText } from 'react-native'
10
+ import { MotiText as _MotiText, MotiProps } from 'moti'
11
+ import { useAnimateColor } from '../utils/hooks'
11
12
  export type TextProps = ComponentPropsWithoutRef<typeof NativeText> & {
12
13
  text?: string
13
14
  variants?: ComponentVariants<typeof TextStyles>['variants']
14
- } & BaseViewProps
15
+ animated?: boolean
16
+ colorChangeConfig?: Partial<Animated.TimingAnimationConfig>
17
+ } & BaseViewProps & MotiProps
18
+
19
+ const MotiText = Animated.createAnimatedComponent(_MotiText)
15
20
 
16
21
  export const Text = forwardRef<NativeText, TextProps>((textProps, ref) => {
17
- const { variants = [], text, style, ...props } = textProps
22
+ const { variants = [], text, children, style, colorChangeConfig, ...props } = textProps
18
23
 
19
24
  const variantStyles = useDefaultComponentStyle('Text', {
20
25
  variants,
21
26
  rootElement: 'text',
22
27
  })
23
28
 
24
- const styles = [variantStyles.text, style]
25
- return (
26
- <NativeText {...props} style={styles} ref={ref}>
27
- {text}
28
- </NativeText>
29
- )
29
+ const styles = StyleSheet.flatten([variantStyles.text, style])
30
+
31
+ const animatedColor = useAnimateColor(styles.color, colorChangeConfig)
32
+
33
+ const Component = textProps.animated ? MotiText : NativeText
34
+
35
+ const colorStyle = { color: props.animated ? animatedColor : styles.color }
36
+
37
+ // @ts-ignore
38
+ return <Component {...props} style={[styles, colorStyle]} ref={ref}>
39
+ {text || children}
40
+ </Component>
41
+
30
42
  })
43
+
@@ -45,7 +45,8 @@ export type TextInputProps =
45
45
  touchableWrapper?: boolean
46
46
  onPress?: () => void
47
47
  masking?: FormTypes.TextField['masking']
48
- innerWrapperProps?: TouchableProps | ViewProps
48
+ innerWrapperProps?: ViewProps
49
+ wrapperProps?: TouchableProps | ViewProps
49
50
  onChangeMask?: TextInputMaskProps['onChangeText']
50
51
  required?:boolean
51
52
  }
@@ -62,6 +63,7 @@ export const TextInput = forwardRef<NativeTextInput, TextInputProps>((rawprops,
62
63
  onBlur,
63
64
  variants,
64
65
  label,
66
+ wrapperProps,
65
67
  leftIcon,
66
68
  rightIcon,
67
69
  styles,
@@ -165,7 +167,7 @@ export const TextInput = forwardRef<NativeTextInput, TextInputProps>((rawprops,
165
167
  style={getStyles('wrapper')}
166
168
  debugName={debugName}
167
169
  onPress={handlePress}
168
- {...innerWrapperProps}
170
+ {...wrapperProps}
169
171
  >
170
172
  <InputLabel
171
173
  label={label}
@@ -8,23 +8,25 @@ import {
8
8
  useCodeleapContext,
9
9
  AnyFunction,
10
10
  } from '@codeleap/common'
11
- import { TouchableOpacity as NativeTouchable } from 'react-native'
11
+ import { TouchableOpacity as NativeTouchable, Pressable, View } from 'react-native'
12
12
 
13
13
  import { createAnimatableComponent } from 'react-native-animatable'
14
14
  export type TouchableProps = Omit<
15
- ComponentPropsWithoutRef<typeof NativeTouchable>,
15
+ ComponentPropsWithoutRef<typeof Pressable>,
16
16
  'onPress'
17
17
  > & {
18
18
  variants?: ComponentVariants<typeof ViewStyles>['variants']
19
19
  component?: any
20
- ref?: React.Ref<NativeTouchable>
20
+ ref?: React.Ref<View>
21
21
  debugName: string
22
+ activeOpacity?: number
22
23
  debugComponent?: string
24
+ feedbackVariant?: 'opacity' | 'none' | 'highlight'
23
25
  onPress?: AnyFunction
24
26
  } & BaseViewProps
25
27
 
26
28
  export const Touchable: React.FC<TouchableProps> = forwardRef<
27
- NativeTouchable,
29
+ View,
28
30
  TouchableProps
29
31
  >((touchableProps, ref) => {
30
32
  const {
@@ -32,8 +34,10 @@ export const Touchable: React.FC<TouchableProps> = forwardRef<
32
34
  children,
33
35
  onPress,
34
36
  style,
37
+ activeOpacity = 0.5,
35
38
  debugName,
36
39
  debugComponent,
40
+ feedbackVariant = 'opacity',
37
41
  ...props
38
42
  } = touchableProps
39
43
 
@@ -41,24 +45,12 @@ export const Touchable: React.FC<TouchableProps> = forwardRef<
41
45
  variants,
42
46
  })
43
47
 
44
- const { logger: contextLogger } = useCodeleapContext() // NOTE for some reason this does not work in actual projects
45
- let activeLogger = null
46
- try {
47
- // @ts-ignore
48
- if (global?.logger) {
49
- // @ts-ignore
50
- activeLogger = global.logger
51
- } else {
52
- activeLogger = contextLogger
53
- }
54
- } catch (e) {
55
- console.log('Error getting Touchable logger')
56
- }
48
+ const { logger } = useCodeleapContext()
57
49
 
58
50
  const press = () => {
59
51
  if (!onPress) { throw { message: 'No onPress passed to touchable', touchableProps } }
60
52
 
61
- activeLogger && activeLogger.log(
53
+ logger.log(
62
54
  `<${debugComponent || 'Touchable'}/> pressed`,
63
55
  debugName || variants,
64
56
  'User interaction',
@@ -68,10 +60,29 @@ export const Touchable: React.FC<TouchableProps> = forwardRef<
68
60
 
69
61
  const styles = [variantStyles.wrapper, style]
70
62
 
63
+ function getFeedbackStyle(pressed:boolean, variant: TouchableProps['feedbackVariant']) {
64
+ switch (variant) {
65
+ case 'highlight':
66
+ return {
67
+ backgroundColor: pressed ? '#e0e0e0' : 'transparent',
68
+ }
69
+ break
70
+ case 'opacity':
71
+ return {
72
+ opacity: pressed ? activeOpacity : 1,
73
+ }
74
+ case 'none':
75
+ return {}
76
+ }
77
+ }
78
+
71
79
  return (
72
- <NativeTouchable onPress={press} style={styles} {...props} ref={ref}>
80
+ <Pressable onPress={press} style={({ pressed }) => ([
81
+ getFeedbackStyle(pressed, feedbackVariant),
82
+ styles,
83
+ ])} {...props} ref={ref}>
73
84
  {children}
74
- </NativeTouchable>
85
+ </Pressable>
75
86
  )
76
87
  })
77
88
 
@@ -32,7 +32,7 @@ export const View: React.FC<ViewProps & Partial<MotiProps>> = forwardRef<NativeV
32
32
  responsiveVariants,
33
33
  variants,
34
34
  })
35
- const Component = animated ? MotiView : component || NativeView
35
+ const Component = animated ? MotiView : (component || NativeView)
36
36
 
37
37
  return (
38
38
  <Component style={[variantStyles.wrapper, style]} ref={ref} {...props}>
@@ -0,0 +1,162 @@
1
+ import * as React from 'react'
2
+ import { View, ViewProps, AnimatedView } from '../View'
3
+ import { Button, ButtonProps } from '../Button'
4
+ import { Scroll } from '../Scroll'
5
+ import {
6
+ capitalize,
7
+ ComponentVariants,
8
+ IconPlaceholder,
9
+ useDefaultComponentStyle,
10
+ } from '@codeleap/common'
11
+ import {
12
+ MobileModalComposition,
13
+ MobileModalStyles,
14
+ MobileModalParts,
15
+ } from './styles'
16
+ import { StyleSheet } from 'react-native'
17
+ import { StylesOf } from '../../types/utility'
18
+
19
+ import { Touchable } from '../Touchable'
20
+ import { Text } from '../Text'
21
+ import { Animated } from '../Animated'
22
+
23
+ export * from './styles'
24
+
25
+ export type ModalProps = Omit<ViewProps, 'variants' | 'styles'> & {
26
+ variants?: ComponentVariants<typeof MobileModalStyles>['variants']
27
+ styles?: StylesOf<MobileModalComposition>
28
+ dismissOnBackdrop?: boolean
29
+ buttonProps?: ButtonProps
30
+ accessible?: boolean
31
+ showClose?: boolean
32
+ closable?: boolean
33
+ footer?: React.ReactNode
34
+ title?: React.ReactNode
35
+ debugName?: string
36
+ closeIconName?: IconPlaceholder
37
+ visible: boolean
38
+ toggle?: () => void
39
+ zIndex?: number
40
+ scroll?: boolean
41
+ keyboardAware?: boolean
42
+ }
43
+
44
+ export const Modal: React.FC<ModalProps> = (modalProps) => {
45
+ const {
46
+ variants = [],
47
+ styles = {},
48
+ visible,
49
+ showClose,
50
+ closable = true,
51
+ title,
52
+ footer,
53
+ children,
54
+ toggle = () => null,
55
+ dismissOnBackdrop = true,
56
+ closeIconName = 'close',
57
+ debugName,
58
+ scroll = true,
59
+ keyboardAware = true,
60
+ zIndex = null,
61
+ ...props
62
+ } = modalProps
63
+
64
+ const variantStyles = useDefaultComponentStyle('Modal', {
65
+ variants: variants as any,
66
+ transform: StyleSheet.flatten,
67
+ styles,
68
+ }) as ModalProps['styles']
69
+
70
+ function getStyles(key: MobileModalParts) {
71
+ const s = [
72
+ variantStyles[key],
73
+ styles[key],
74
+ visible ? variantStyles[key + ':visible'] : {},
75
+ visible ? styles[key + ':visible'] : {},
76
+ ]
77
+
78
+ return s
79
+ }
80
+
81
+ const buttonStyles = React.useMemo(() => {
82
+ const buttonEntries = {}
83
+
84
+ for (const [key, style] of Object.entries(variantStyles)) {
85
+ if (key.startsWith('closeButton')) {
86
+ buttonEntries[capitalize(key.replace('closeButton', ''), true)] = style
87
+ }
88
+ }
89
+ return buttonEntries
90
+ }, [variantStyles])
91
+
92
+ const boxAnimation = {
93
+ hidden: {
94
+ ...variantStyles['box:pose'],
95
+ ...styles['box:pose'],
96
+ },
97
+ visible: {
98
+ ...variantStyles['box:pose:visible'],
99
+ ...styles['box:pose:visible'],
100
+ },
101
+ }
102
+
103
+ const wrapperStyle = StyleSheet.flatten(getStyles('wrapper'))
104
+ return (
105
+ <View
106
+ style={[wrapperStyle, { zIndex: typeof zIndex === 'number' ? zIndex : wrapperStyle?.zIndex }]}
107
+ pointerEvents={visible ? 'auto' : 'none'}
108
+ >
109
+ <AnimatedView style={getStyles('overlay')} transition={'opacity'} />
110
+ <Scroll
111
+ style={getStyles('innerWrapper')}
112
+ contentContainerStyle={getStyles('innerWrapperScroll')}
113
+ scrollEnabled={scroll}
114
+ keyboardAware={keyboardAware}
115
+ >
116
+ {dismissOnBackdrop && (
117
+ <Touchable
118
+ debugName={`${debugName} modal backdrop`}
119
+ activeOpacity={1}
120
+ onPress={() => toggle()}
121
+ style={getStyles('touchableBackdrop')}
122
+ />
123
+ )}
124
+ <Animated
125
+ component='View'
126
+ config={boxAnimation}
127
+ pose={visible ? 'visible' : 'hidden'}
128
+ style={getStyles('box')}
129
+ >
130
+ {(title || showClose) && (
131
+ <View style={getStyles('header')}>
132
+ {typeof title === 'string' ? (
133
+ <Text text={title} style={getStyles('title')} />
134
+ ) : (
135
+ title
136
+ )}
137
+
138
+ {showClose && closable && (
139
+ <Button
140
+ debugName={`${debugName} modal close button`}
141
+ icon={closeIconName as IconPlaceholder}
142
+ variants={['icon']}
143
+ onPress={toggle}
144
+ styles={buttonStyles}
145
+ />
146
+ )}
147
+ </View>
148
+ )}
149
+
150
+ <View style={getStyles('body')}>{children}</View>
151
+ {footer && (
152
+ <View style={getStyles('footer')}>
153
+ {typeof footer === 'string' ? <Text text={footer} /> : footer}
154
+ </View>
155
+ )}
156
+ </Animated>
157
+ </Scroll>
158
+ </View>
159
+ )
160
+ }
161
+
162
+ export default Modal