@codeleap/mobile 1.9.27 → 1.9.30

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 (119) 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 +4 -15
  17. package/dist/components/Modal/styles.js +27 -23
  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/NewPager/index.js +3 -7
  26. package/dist/components/NewPager/index.js.map +1 -1
  27. package/dist/components/Overlay.js +13 -9
  28. package/dist/components/Overlay.js.map +1 -1
  29. package/dist/components/Scroll.d.ts +4 -2
  30. package/dist/components/Scroll.js.map +1 -1
  31. package/dist/components/SegmentedControl/index.d.ts +42 -0
  32. package/dist/components/SegmentedControl/index.js +137 -0
  33. package/dist/components/SegmentedControl/index.js.map +1 -0
  34. package/dist/components/SegmentedControl/styles.d.ts +54 -0
  35. package/dist/components/SegmentedControl/styles.js +36 -0
  36. package/dist/components/SegmentedControl/styles.js.map +1 -0
  37. package/dist/components/SegmentedControl.d.ts +5 -0
  38. package/dist/components/SegmentedControl.js +32 -0
  39. package/dist/components/SegmentedControl.js.map +1 -0
  40. package/dist/components/Select/index.js +1 -1
  41. package/dist/components/Select/index.js.map +1 -1
  42. package/dist/components/Text.d.ts +8 -3
  43. package/dist/components/Text.js +12 -5
  44. package/dist/components/Text.js.map +1 -1
  45. package/dist/components/TextInput.d.ts +4 -2
  46. package/dist/components/TextInput.js +2 -2
  47. package/dist/components/TextInput.js.map +1 -1
  48. package/dist/components/Touchable.d.ts +5 -3
  49. package/dist/components/Touchable.js +26 -19
  50. package/dist/components/Touchable.js.map +1 -1
  51. package/dist/components/View.js +1 -1
  52. package/dist/components/View.js.map +1 -1
  53. package/dist/components/_Modal/index.d.ts +27 -0
  54. package/dist/components/_Modal/index.js +114 -0
  55. package/dist/components/_Modal/index.js.map +1 -0
  56. package/dist/components/_Modal/styles.d.ts +64 -0
  57. package/dist/components/_Modal/styles.js +60 -0
  58. package/dist/components/_Modal/styles.js.map +1 -0
  59. package/dist/components/components.d.ts +3 -0
  60. package/dist/components/components.js +3 -0
  61. package/dist/components/components.js.map +1 -1
  62. package/dist/index.d.ts +4 -0
  63. package/dist/index.js +18 -1
  64. package/dist/index.js.map +1 -1
  65. package/dist/utils/ModalManager/components.d.ts +12 -0
  66. package/dist/utils/ModalManager/components.js +86 -0
  67. package/dist/utils/ModalManager/components.js.map +1 -0
  68. package/dist/utils/ModalManager/context.d.ts +47 -0
  69. package/dist/utils/ModalManager/context.js +196 -0
  70. package/dist/utils/ModalManager/context.js.map +1 -0
  71. package/dist/utils/ModalManager/index.d.ts +10 -0
  72. package/dist/utils/ModalManager/index.js +12 -0
  73. package/dist/utils/ModalManager/index.js.map +1 -0
  74. package/dist/utils/PermissionManager/components.d.ts +18 -0
  75. package/dist/utils/PermissionManager/components.js +52 -0
  76. package/dist/utils/PermissionManager/components.js.map +1 -0
  77. package/dist/utils/PermissionManager/context.d.ts +52 -0
  78. package/dist/utils/PermissionManager/context.js +325 -0
  79. package/dist/utils/PermissionManager/context.js.map +1 -0
  80. package/dist/utils/PermissionManager/index.d.ts +4 -0
  81. package/dist/utils/PermissionManager/index.js +9 -0
  82. package/dist/utils/PermissionManager/index.js.map +1 -0
  83. package/dist/utils/PermissionManager/types.d.ts +13 -0
  84. package/dist/utils/PermissionManager/types.js +3 -0
  85. package/dist/utils/PermissionManager/types.js.map +1 -0
  86. package/dist/utils/hooks.d.ts +6 -0
  87. package/dist/utils/hooks.js +62 -0
  88. package/dist/utils/hooks.js.map +1 -0
  89. package/package.json +2 -1
  90. package/src/components/Backdrop.tsx +77 -0
  91. package/src/components/Button.tsx +3 -2
  92. package/src/components/FileInput.tsx +2 -2
  93. package/src/components/Image.tsx +3 -2
  94. package/src/components/List.tsx +44 -5
  95. package/src/components/Modal/index.tsx +37 -50
  96. package/src/components/Modal/styles.ts +36 -39
  97. package/src/components/NewPager/index.tsx +5 -7
  98. package/src/components/Overlay.tsx +22 -13
  99. package/src/components/Scroll.tsx +3 -1
  100. package/src/components/SegmentedControl/index.tsx +182 -0
  101. package/src/components/SegmentedControl/styles.ts +65 -0
  102. package/src/components/Select/index.tsx +1 -2
  103. package/src/components/Text.tsx +23 -10
  104. package/src/components/TextInput.tsx +4 -2
  105. package/src/components/Touchable.tsx +31 -20
  106. package/src/components/View.tsx +1 -1
  107. package/src/components/_Modal/index.tsx +162 -0
  108. package/src/components/_Modal/styles.ts +125 -0
  109. package/src/components/components.ts +3 -0
  110. package/src/index.ts +6 -0
  111. package/src/modules/imageCropPicker.d.ts +497 -0
  112. package/src/modules/index.d.ts +186 -0
  113. package/src/utils/ModalManager/components.tsx +69 -0
  114. package/src/utils/ModalManager/context.tsx +247 -0
  115. package/src/utils/ModalManager/index.ts +13 -0
  116. package/src/utils/PermissionManager/context.tsx +299 -0
  117. package/src/utils/PermissionManager/index.ts +20 -0
  118. package/src/utils/PermissionManager/types.ts +24 -0
  119. package/src/utils/hooks.ts +65 -0
@@ -6,6 +6,7 @@ import {
6
6
  useDefaultComponentStyle,
7
7
  usePrevious,
8
8
  useCodeleapContext,
9
+ TypeGuards,
9
10
  } from '@codeleap/common'
10
11
  import {
11
12
 
@@ -13,13 +14,29 @@ import {
13
14
  // @ts-ignore
14
15
  } from 'react-native-keyboard-aware-scroll-view'
15
16
 
16
- import { RefreshControl, FlatList, FlatListProps as RNFlatListProps } from 'react-native'
17
+ import { RefreshControl, FlatList, FlatListProps as RNFlatListProps, ListRenderItemInfo } from 'react-native'
17
18
  import { View, ViewProps } from './View'
18
19
  import { EmptyPlaceholder, EmptyPlaceholderProps } from './EmptyPlaceholder'
19
-
20
- export type FlatListProps<T = any> = RNFlatListProps<T> &
20
+ import { ActivityIndicator } from './ActivityIndicator'
21
+ import { Text } from './Text'
22
+ export type DataboundFlatListPropsTypes = 'data' | 'renderItem' | 'keyExtractor' | 'getItemLayout'
23
+
24
+ export type ReplaceFlatlistProps<P, T> = Omit<P, DataboundFlatListPropsTypes> & {
25
+ data: T[]
26
+ keyExtractor?: (item: T, index: number) => string
27
+ renderItem: (data: ListRenderItemInfo<T>) => React.ReactElement
28
+ onRefresh?: () => void
29
+ getItemLayout?: ((
30
+ data:T,
31
+ index: number,
32
+ ) => { length: number; offset: number; index: number })
33
+ }
34
+
35
+ export type FlatListProps<
36
+ T = any[],
37
+ Data = T extends Array<infer D> ? D : never
38
+ > =RNFlatListProps<Data> &
21
39
  ViewProps & {
22
- onRefresh?: () => void
23
40
  refreshTimeout?: number
24
41
  changeData?: any
25
42
  separators?: boolean
@@ -112,5 +129,27 @@ const ListCP = forwardRef<FlatList, FlatListProps>(
112
129
  },
113
130
  )
114
131
 
115
- export const List = ListCP as (<T = any>(props: FlatListProps<T>) => JSX.Element)
132
+ export type PaginationIndicatorProps = {
133
+ isFetching?: boolean
134
+ hasMore?: boolean
135
+ noMoreItemsText: React.ReactChild
136
+ activityIndicator?: JSX.Element
137
+ }
138
+
139
+ export const PaginationIndicator = ({ hasMore, isFetching, noMoreItemsText, activityIndicator }: PaginationIndicatorProps) => {
140
+ if (isFetching) {
141
+ return activityIndicator || <ActivityIndicator variants={['center', 'marginVertical:3']}/>
142
+ }
143
+ if (!hasMore) {
144
+ if (TypeGuards.isString(noMoreItemsText) || TypeGuards.isNumber(noMoreItemsText)) {
145
+ return <Text variants={['h4', 'marginVertical:3', 'textCenter', 'fullWidth']} text={noMoreItemsText.toString()}/>
146
+ }
147
+ return noMoreItemsText
148
+ }
149
+ return null
150
+ }
151
+
152
+ export type ListComponentType = <T extends any[] = any[]>(props: FlatListProps<T>) => React.ReactElement
153
+
154
+ export const List = ListCP as ListComponentType
116
155
 
@@ -1,14 +1,14 @@
1
1
  import * as React from 'react'
2
- import { View, ViewProps, AnimatedView } from '../View'
2
+ import { View, ViewProps } from '../View'
3
3
  import { Button, ButtonProps } from '../Button'
4
4
  import { Scroll } from '../Scroll'
5
5
  import {
6
- capitalize,
7
6
  ComponentVariants,
7
+ getNestedStylesByKey,
8
8
  IconPlaceholder,
9
9
  onUpdate,
10
+ TypeGuards,
10
11
  useDefaultComponentStyle,
11
- useCodeleapContext,
12
12
  } from '@codeleap/common'
13
13
  import {
14
14
  MobileModalComposition,
@@ -18,9 +18,10 @@ import {
18
18
  import { StyleSheet } from 'react-native'
19
19
  import { StylesOf } from '../../types/utility'
20
20
 
21
- import { Touchable } from '../Touchable'
21
+ import { useDynamicAnimation } from 'moti'
22
+ import { Backdrop } from '../Backdrop'
23
+ import { useStaticAnimationStyles } from '../../utils/hooks'
22
24
  import { Text } from '../Text'
23
- import { Animated } from '../Animated'
24
25
 
25
26
  export * from './styles'
26
27
 
@@ -34,7 +35,7 @@ export type ModalProps = Omit<ViewProps, 'variants' | 'styles'> & {
34
35
  closable?: boolean
35
36
  footer?: React.ReactNode
36
37
  title?: React.ReactNode
37
- debugName?: string
38
+ debugName: string
38
39
  closeIconName?: IconPlaceholder
39
40
  visible: boolean
40
41
  toggle?: () => void
@@ -63,7 +64,7 @@ export const Modal: React.FC<ModalProps> = (modalProps) => {
63
64
  ...props
64
65
  } = modalProps
65
66
 
66
- const variantStyles = useDefaultComponentStyle('Modal', {
67
+ const variantStyles = useDefaultComponentStyle('u:Modal', {
67
68
  variants: variants as any,
68
69
  transform: StyleSheet.flatten,
69
70
  styles,
@@ -73,61 +74,46 @@ export const Modal: React.FC<ModalProps> = (modalProps) => {
73
74
  const s = [
74
75
  variantStyles[key],
75
76
  styles[key],
76
- visible ? variantStyles[key + ':visible'] : {},
77
- visible ? styles[key + ':visible'] : {},
78
77
  ]
79
78
 
80
- return s
79
+ return StyleSheet.flatten(s)
81
80
  }
81
+ const buttonStyles = React.useMemo(() => getNestedStylesByKey('closeButton', variantStyles), [variantStyles])
82
82
 
83
- const buttonStyles = React.useMemo(() => {
84
- const buttonEntries = {}
83
+ const boxAnimationStates = useStaticAnimationStyles(variantStyles, ['box:hidden', 'box:visible'])
85
84
 
86
- for (const [key, style] of Object.entries(variantStyles)) {
87
- if (key.startsWith('closeButton')) {
88
- buttonEntries[capitalize(key.replace('closeButton', ''), true)] = style
89
- }
90
- }
91
- return buttonEntries
92
- }, [variantStyles])
85
+ const boxAnimation = useDynamicAnimation(() => {
86
+ return visible ? boxAnimationStates['box:visible'] : boxAnimationStates['box:hidden']
87
+ })
93
88
 
94
- const boxAnimation = {
95
- hidden: {
96
- ...variantStyles['box:pose'],
97
- ...styles['box:pose'],
98
- },
99
- visible: {
100
- ...variantStyles['box:pose:visible'],
101
- ...styles['box:pose:visible'],
102
- },
103
- }
104
-
105
- const wrapperStyle = StyleSheet.flatten(getStyles('wrapper'))
89
+ onUpdate(() => {
90
+ boxAnimation.animateTo(visible ? boxAnimationStates['box:visible'] : boxAnimationStates['box:hidden'])
91
+ }, [visible])
92
+ const wrapperStyle = getStyles('wrapper')
106
93
  return (
107
- <View
108
- style={[wrapperStyle, { zIndex: typeof zIndex === 'number' ? zIndex : wrapperStyle?.zIndex }]}
109
- pointerEvents={visible ? 'auto' : 'none'}
110
- >
111
- <AnimatedView style={getStyles('overlay')} transition={'opacity'} />
94
+ <View style={[wrapperStyle, { zIndex: TypeGuards.isNumber(zIndex) ? zIndex : wrapperStyle?.zIndex }]} pointerEvents={visible ? 'auto' : 'none'}>
95
+
112
96
  <Scroll
113
97
  style={getStyles('innerWrapper')}
114
98
  contentContainerStyle={getStyles('innerWrapperScroll')}
115
99
  scrollEnabled={scroll}
116
100
  keyboardAware={keyboardAware}
117
101
  >
118
- {dismissOnBackdrop && (
119
- <Touchable
120
- debugName={`${debugName} modal backdrop`}
121
- activeOpacity={1}
122
- onPress={() => toggle()}
123
- style={getStyles('touchableBackdrop')}
124
- />
125
- )}
126
- <Animated
127
- component='View'
128
- config={boxAnimation}
129
- pose={visible ? 'visible' : 'hidden'}
102
+ <Backdrop visible={visible} debugName={`Modal ${debugName} backdrop`} styles={{
103
+ 'wrapper:hidden': variantStyles['backdrop:hidden'],
104
+ 'wrapper:visible': variantStyles['backdrop:visible'],
105
+ wrapper: variantStyles.backdrop,
106
+ }} onPress={(dismissOnBackdrop && closable) ? toggle : (() => {})}
107
+ wrapperProps={{
108
+ transition: { ...variantStyles['backdrop:transition'] },
109
+ }}
110
+ />
111
+ <View
112
+ animated
113
+ state={boxAnimation}
130
114
  style={getStyles('box')}
115
+ transition={{ ...variantStyles['box:transition'] }}
116
+ {...props}
131
117
  >
132
118
  {(title || showClose) && (
133
119
  <View style={getStyles('header')}>
@@ -137,7 +123,7 @@ export const Modal: React.FC<ModalProps> = (modalProps) => {
137
123
  title
138
124
  )}
139
125
 
140
- {showClose && closable && (
126
+ {(showClose && closable) && (
141
127
  <Button
142
128
  debugName={`${debugName} modal close button`}
143
129
  icon={closeIconName as IconPlaceholder}
@@ -155,9 +141,10 @@ export const Modal: React.FC<ModalProps> = (modalProps) => {
155
141
  {typeof footer === 'string' ? <Text text={footer} /> : footer}
156
142
  </View>
157
143
  )}
158
- </Animated>
144
+ </View>
159
145
  </Scroll>
160
146
  </View>
147
+
161
148
  )
162
149
  }
163
150
 
@@ -1,58 +1,40 @@
1
1
  import {
2
+ assignTextStyle,
2
3
  ButtonComposition,
3
4
  createDefaultVariantFactory,
4
5
  includePresets,
5
- ModalComposition,
6
- ModalStyles,
7
- assignTextStyle,
8
6
  } from '@codeleap/common'
9
7
 
10
- export const backgroundTransition = {
11
- duration: 200,
12
- ease: 'easeOut',
13
- useNativeDriver: false,
14
- }
15
-
16
- export const modalTransition = {
17
- duration: 150,
18
- ease: 'easeOut',
19
- useNativeDriver: false,
20
- }
8
+ export type AnimatableParts = 'box' | 'backdrop'
21
9
 
22
10
  export type MobileModalParts =
11
+ | AnimatableParts
23
12
  | 'wrapper'
24
- | 'overlay'
25
13
  | 'innerWrapper'
26
14
  | 'innerWrapperScroll'
27
- | 'box'
28
- | 'footer'
29
15
  | 'body'
16
+ | 'footer'
30
17
  | 'header'
31
- | 'touchableBackdrop'
32
- | 'box:pose'
33
18
  | 'title'
34
19
  | `closeButton${Capitalize<ButtonComposition>}`
35
20
 
36
21
  export type MobileModalComposition =
37
22
  | MobileModalParts
38
- | `${MobileModalParts}:visible`
23
+ | `${AnimatableParts}:visible`
24
+ | `${AnimatableParts}:hidden`
25
+ | `${AnimatableParts}:transition`
39
26
 
40
27
  const createModalStyle = createDefaultVariantFactory<MobileModalComposition>()
41
28
 
42
- const presets = includePresets((style) => createModalStyle(() => ({ body: style })),
43
- )
44
-
45
- const defaultModalStyles = ModalStyles
29
+ const presets = includePresets((style) => createModalStyle(() => ({ body: style })))
46
30
 
47
31
  export const MobileModalStyles = {
48
- ...defaultModalStyles,
32
+
49
33
  ...presets,
50
34
  default: createModalStyle((Theme) => {
51
35
  const fullSize = {
52
36
  ...Theme.presets.whole,
53
37
  position: 'absolute',
54
- width: Theme?.values?.width,
55
- height: Theme?.values?.height,
56
38
  }
57
39
 
58
40
  return {
@@ -61,16 +43,34 @@ export const MobileModalStyles = {
61
43
 
62
44
  ...fullSize,
63
45
  },
64
-
65
- overlay: {
66
- opacity: 0,
46
+ 'box:transition': {
47
+ scale: {
48
+ duration: Theme.values.transitions.modal.duration,
49
+ type: 'timing',
50
+ },
51
+ opacity: {
52
+ duration: Theme.values.transitions.modal.duration,
53
+ type: 'timing',
54
+ },
55
+ },
56
+ 'backdrop:transition': {
57
+ opacity: {
58
+ duration: Theme.values.transitions.modal.duration,
59
+ type: 'timing',
60
+ },
61
+ },
62
+ backdrop: {
67
63
 
68
64
  backgroundColor: Theme.colors.black,
69
65
  ...fullSize,
70
66
  },
71
- 'overlay:visible': {
67
+
68
+ 'backdrop:visible': {
72
69
  opacity: 0.5,
73
70
  },
71
+ 'backdrop:hidden': {
72
+ opacity: 0,
73
+ },
74
74
  innerWrapper: {},
75
75
  innerWrapperScroll: {
76
76
  display: 'flex',
@@ -84,18 +84,15 @@ export const MobileModalStyles = {
84
84
  borderRadius: Theme.borderRadius.medium,
85
85
  ...Theme.spacing.padding(2),
86
86
  },
87
- touchableBackdrop: {
88
- ...fullSize,
89
- },
90
- 'box:pose': {
87
+
88
+ 'box:hidden': {
91
89
  opacity: 0,
92
90
  scale: 0.8,
93
- transition: modalTransition,
91
+
94
92
  },
95
- 'box:pose:visible': {
93
+ 'box:visible': {
96
94
  opacity: 1,
97
95
  scale: 1,
98
- transition: modalTransition,
99
96
  },
100
97
  header: {
101
98
  flexDirection: 'row',
@@ -114,7 +111,7 @@ export const MobileModalStyles = {
114
111
  },
115
112
  }
116
113
  }),
117
- popup: createModalStyle((theme) => ({})),
114
+ popup: createModalStyle(() => ({})),
118
115
  fullscreen: createModalStyle((theme) => ({
119
116
  overlay: {
120
117
  backgroundColor: theme.colors.background,
@@ -43,9 +43,9 @@ export const NewPager:React.FC<NewPagerProps> = (pagerProps) => {
43
43
  returnEarly = true,
44
44
  renderPageWrapper,
45
45
  pageWrapperProps = {},
46
-
46
+ children,
47
47
  } = pagerProps
48
- let { children } = pagerProps
48
+
49
49
  let variantStyles = useDefaultComponentStyle<'u:Pager', typeof NewMobilePagerStyles>(
50
50
  'u:Pager',
51
51
  {
@@ -58,7 +58,7 @@ export const NewPager:React.FC<NewPagerProps> = (pagerProps) => {
58
58
 
59
59
  const lastPage = nChildren - 1
60
60
 
61
- children = Array.isArray(children) ? children : [children]
61
+ const childArr = React.Children.toArray(children)
62
62
 
63
63
  const WrapperComponent = renderPageWrapper || View
64
64
 
@@ -68,7 +68,7 @@ export const NewPager:React.FC<NewPagerProps> = (pagerProps) => {
68
68
  return (
69
69
  <View style={[variantStyles.wrapper, style]} >
70
70
  {
71
- children.map((child:NewPagerProps['children'][number], index) => {
71
+ childArr.map((child:NewPagerProps['children'][number], index) => {
72
72
  const isActive = index === page
73
73
  const isLast = index === lastPage
74
74
  const isFirst = index === 0
@@ -96,9 +96,7 @@ export const NewPager:React.FC<NewPagerProps> = (pagerProps) => {
96
96
  index,
97
97
  page,
98
98
  }
99
- console.log({
100
- child,
101
- })
99
+
102
100
  const content = typeof child === 'function' ? child(pageProps) : child
103
101
 
104
102
  const wrapperProps = {
@@ -2,6 +2,7 @@ import * as React from 'react'
2
2
  import {
3
3
  ComponentVariants,
4
4
  IconPlaceholder,
5
+ onUpdate,
5
6
  OverlayComposition,
6
7
  OverlayStyles,
7
8
  useDefaultComponentStyle,
@@ -13,7 +14,8 @@ import { Button } from './Button'
13
14
  import { View } from './View'
14
15
  import { StylesOf } from '../types/utility'
15
16
  import { StyleSheet, ViewProps } from 'react-native'
16
- import { AnimatedTouchable } from './Touchable'
17
+ import { AnimatedTouchable, Touchable } from './Touchable'
18
+ import { useAnimationState } from 'moti'
17
19
 
18
20
  export type OverlayProps = ViewProps & {
19
21
  title?: ReactNode
@@ -43,21 +45,28 @@ export const Overlay: React.FC<OverlayProps> = (overlayProps) => {
43
45
  variants: variants as any,
44
46
  }) as StylesOf<OverlayComposition>
45
47
 
46
- const touchableStyle = [
48
+ const animation = useAnimationState({
49
+ visible: variantStyles['wrapper:visible'],
50
+ hidden: styles['wrapper:visible'],
51
+ })
52
+
53
+ onUpdate(() => {
54
+ animation.transitionTo(visible ? 'visible' : 'hidden')
55
+ }, [visible])
56
+
57
+ const viewStyles = [
47
58
  variantStyles.wrapper,
48
59
  styles.wrapper,
49
- visible && variantStyles['wrapper:visible'],
50
- visible && styles['wrapper:visible'],
51
60
  ]
52
61
 
53
62
  return (
54
- <AnimatedTouchable
55
- // @ts-ignore
56
- transition={'opacity'}
57
- style={touchableStyle}
58
- {...props}
59
- >
60
- <View>
63
+ <View animated state={animation} style={viewStyles}>
64
+ <Touchable
65
+
66
+ feedbackVariant='none'
67
+ variants={['whole', 'full', 'absolute']}
68
+ {...props}
69
+ >
61
70
  {(title || showClose) && (
62
71
  <View style={variantStyles.header}>
63
72
  <InputLabel style={variantStyles.title} label={title} />
@@ -71,7 +80,7 @@ export const Overlay: React.FC<OverlayProps> = (overlayProps) => {
71
80
  )}
72
81
  </View>
73
82
  )}
74
- </View>
75
- </AnimatedTouchable>
83
+ </Touchable>
84
+ </View>
76
85
  )
77
86
  }
@@ -9,12 +9,14 @@ import {
9
9
  } from '@codeleap/common'
10
10
 
11
11
  import {
12
- KeyboardAwareScrollViewProps,
13
12
  KeyboardAwareScrollView as KBDView,
14
13
  // @ts-ignore
15
14
  } from 'react-native-keyboard-aware-scroll-view'
16
15
  import { RefreshControl, ScrollView, ScrollViewProps, ViewStyle } from 'react-native'
17
16
  import { ViewProps } from './View'
17
+ import { KeyboardAwareScrollViewTypes } from '../modules'
18
+
19
+ type KeyboardAwareScrollViewProps = KeyboardAwareScrollViewTypes.KeyboardAwareScrollViewProps
18
20
 
19
21
  export type ScrollProps = KeyboardAwareScrollViewProps &
20
22
  ViewProps & {
@@ -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