@codeleap/mobile 4.0.1 → 4.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +10 -7
- package/src/components/DatePickerModal/index.tsx +3 -2
- package/src/components/FileInput/index.tsx +7 -7
- package/src/components/FileInput/types.ts +1 -1
- package/src/components/NumberIncrement/index.tsx +21 -3
- package/src/components/NumberIncrement/types.ts +1 -0
- package/src/components/Pager/PagerDots.tsx +56 -0
- package/src/components/Pager/index.tsx +85 -173
- package/src/components/Pager/styles.ts +6 -1
- package/src/components/Pager/types.ts +16 -26
- package/src/components/PlacesAutocomplete/index.tsx +152 -0
- package/src/components/PlacesAutocomplete/styles.ts +12 -0
- package/src/components/PlacesAutocomplete/types.ts +42 -0
- package/src/components/Select/index.tsx +9 -13
- package/src/components/SortablePhotos/index.tsx +170 -0
- package/src/components/SortablePhotos/styles.ts +9 -0
- package/src/components/SortablePhotos/types.ts +58 -0
- package/src/components/SortablePhotos/useSortablePhotos.ts +174 -0
- package/src/components/Text/index.tsx +1 -1
- package/src/components/Text/types.ts +3 -2
- package/src/components/TextInput/index.tsx +8 -0
- package/src/components/TextInput/types.ts +2 -0
- package/src/components/components.ts +2 -0
- package/src/utils/misc.ts +41 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@codeleap/mobile",
|
|
3
|
-
"version": "4.0
|
|
3
|
+
"version": "4.1.0",
|
|
4
4
|
"main": "src/index.ts",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"repository": {
|
|
@@ -9,8 +9,8 @@
|
|
|
9
9
|
"directory": "packages/mobile"
|
|
10
10
|
},
|
|
11
11
|
"devDependencies": {
|
|
12
|
-
"@codeleap/
|
|
13
|
-
"@codeleap/
|
|
12
|
+
"@codeleap/common": "*",
|
|
13
|
+
"@codeleap/config": "*"
|
|
14
14
|
},
|
|
15
15
|
"scripts": {
|
|
16
16
|
"build": "tsc --build",
|
|
@@ -28,24 +28,27 @@
|
|
|
28
28
|
"@react-navigation/stack": "6.3.11",
|
|
29
29
|
"react": "18.1.0",
|
|
30
30
|
"react-native": "0.73.8",
|
|
31
|
+
"react-native-avoid-softinput": "3.1.5",
|
|
31
32
|
"react-native-calendars": "1.1293.0",
|
|
32
33
|
"react-native-date-picker": "4.2.13",
|
|
33
34
|
"react-native-device-info": "10.3.0",
|
|
34
35
|
"react-native-fast-image": "8.6.3",
|
|
35
36
|
"react-native-gesture-handler": "2.9.0",
|
|
36
37
|
"react-native-image-crop-picker": "0.37.2",
|
|
37
|
-
"react-native-super-grid": "4.6.1",
|
|
38
38
|
"react-native-image-viewing": "0.2.2",
|
|
39
39
|
"react-native-keyboard-aware-scroll-view": "0.9.5",
|
|
40
|
-
"
|
|
41
|
-
"react-native-
|
|
42
|
-
"
|
|
40
|
+
"react-native-mmkv": "2.12.2",
|
|
41
|
+
"react-native-super-grid": "4.6.1",
|
|
42
|
+
"typescript": "5.0.4"
|
|
43
43
|
},
|
|
44
44
|
"dependencies": {
|
|
45
45
|
"@gorhom/portal": "1.0.14",
|
|
46
46
|
"@miblanchard/react-native-slider": "2.3.1",
|
|
47
47
|
"date-fns": "2.29.3",
|
|
48
|
+
"react-native-currency-input": "^1.1.1",
|
|
49
|
+
"react-native-drag-sort": "^2.4.4",
|
|
48
50
|
"react-native-masked-text": "1.13.0",
|
|
51
|
+
"react-native-reanimated-carousel": "^3.5.1",
|
|
49
52
|
"react-native-uuid": "2.0.1"
|
|
50
53
|
}
|
|
51
54
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React, { useRef } from 'react'
|
|
2
|
-
import { TypeGuards,
|
|
2
|
+
import { TypeGuards, useConditionalState, useI18N } from '@codeleap/common'
|
|
3
3
|
import DatePicker from 'react-native-date-picker'
|
|
4
4
|
import Modal from '../Modal'
|
|
5
5
|
import { TextInput } from '../TextInput'
|
|
@@ -116,7 +116,8 @@ export const DatePickerModal = (props: DatePickerModalProps) => {
|
|
|
116
116
|
|
|
117
117
|
const styles = useStylesFor(DatePickerModal.styleRegistryName, style)
|
|
118
118
|
|
|
119
|
-
const [visible, toggle] =
|
|
119
|
+
const [visible, toggle] = useConditionalState(_visible, _toggle, { initialValue: false, isBooleanToggle: true })
|
|
120
|
+
|
|
120
121
|
const [value, setValue] = [_value, onValueChange]
|
|
121
122
|
|
|
122
123
|
const Wrapper = isCustomModal ? ModalManager.Modal : React.Fragment
|
|
@@ -95,12 +95,12 @@ const _FileInput = forwardRef<FileInputRef, FileInputProps>((fileInputProps, ref
|
|
|
95
95
|
}
|
|
96
96
|
}
|
|
97
97
|
|
|
98
|
-
const openFilePicker = async (imageSource = null) => {
|
|
98
|
+
const openFilePicker = async (imageSource = null, options = {}) => {
|
|
99
99
|
if (type === 'image') {
|
|
100
100
|
if (imageSource === 'camera') {
|
|
101
|
-
onPress('camera')
|
|
101
|
+
onPress('camera', options)
|
|
102
102
|
} else if (imageSource === 'library') {
|
|
103
|
-
onPress('library')
|
|
103
|
+
onPress('library', options)
|
|
104
104
|
} else {
|
|
105
105
|
OSAlert.ask({
|
|
106
106
|
title: 'Change Image',
|
|
@@ -150,8 +150,8 @@ const _FileInput = forwardRef<FileInputRef, FileInputProps>((fileInputProps, ref
|
|
|
150
150
|
}
|
|
151
151
|
|
|
152
152
|
useImperativeHandle(ref, () => ({
|
|
153
|
-
openFilePicker: (imageSource: FileInputImageSource = null) => {
|
|
154
|
-
openFilePicker(imageSource)
|
|
153
|
+
openFilePicker: (imageSource: FileInputImageSource = null, options: Partial<Options> = {}) => {
|
|
154
|
+
openFilePicker(imageSource, options)
|
|
155
155
|
return new Promise<FileResult[]>((resolve) => {
|
|
156
156
|
resolveWithFile.current = resolve
|
|
157
157
|
})
|
|
@@ -166,8 +166,8 @@ export const FileInput = _FileInput as unknown as ((props: FileInputProps) => JS
|
|
|
166
166
|
export const useFileInput = () => {
|
|
167
167
|
const inputRef = useRef<FileInputRef>(null)
|
|
168
168
|
|
|
169
|
-
const openFilePicker = (imageSource: FileInputImageSource = null): Promise<FileResult[]> => {
|
|
170
|
-
return inputRef.current?.openFilePicker(imageSource)
|
|
169
|
+
const openFilePicker = (imageSource: FileInputImageSource = null, options: Partial<Options> = {}): Promise<FileResult[]> => {
|
|
170
|
+
return inputRef.current?.openFilePicker(imageSource, options)
|
|
171
171
|
}
|
|
172
172
|
|
|
173
173
|
return {
|
|
@@ -8,7 +8,7 @@ export type FileInputImageSource = 'camera' | 'library' | 'fs'
|
|
|
8
8
|
export type FileResult = FormTypes.AnyFile
|
|
9
9
|
|
|
10
10
|
export type FileInputRef = {
|
|
11
|
-
openFilePicker: (
|
|
11
|
+
openFilePicker: (source?: FileInputImageSource, options?: Partial<Options>) => Promise<FileResult[]>
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
export type FileInputProps = {
|
|
@@ -11,6 +11,7 @@ import { NumberIncrementProps } from './types'
|
|
|
11
11
|
import { AnyRecord, AppIcon, IJSX, StyledComponentProps, StyledComponentWithProps } from '@codeleap/styles'
|
|
12
12
|
import { MobileStyleRegistry } from '../../Registry'
|
|
13
13
|
import { useStylesFor } from '../../hooks'
|
|
14
|
+
import CurrencyInput from 'react-native-currency-input'
|
|
14
15
|
|
|
15
16
|
export * from './styles'
|
|
16
17
|
export * from './types'
|
|
@@ -58,6 +59,7 @@ export const NumberIncrement = forwardRef<NativeTextInput, NumberIncrementProps>
|
|
|
58
59
|
timeoutActionFocus,
|
|
59
60
|
mask,
|
|
60
61
|
actionDebounce,
|
|
62
|
+
precision,
|
|
61
63
|
...textInputProps
|
|
62
64
|
} = others
|
|
63
65
|
|
|
@@ -73,11 +75,13 @@ export const NumberIncrement = forwardRef<NativeTextInput, NumberIncrementProps>
|
|
|
73
75
|
|
|
74
76
|
const isFormatted = TypeGuards.isFunction(formatter)
|
|
75
77
|
|
|
76
|
-
const hasMaskProps = [masking,
|
|
78
|
+
const hasMaskProps = [masking, mask].some(v => !!v)
|
|
79
|
+
const hasCurrencyProps = [prefix, suffix, delimiter, separator, precision].some(v => !!v)
|
|
77
80
|
|
|
78
|
-
const
|
|
81
|
+
const isCurrency = hasCurrencyProps
|
|
82
|
+
const isMasked = hasMaskProps && !isFormatted && !isCurrency
|
|
79
83
|
|
|
80
|
-
const InputElement = isMasked ? MaskedTextInput : NativeTextInput
|
|
84
|
+
const InputElement: any = isMasked ? MaskedTextInput : isCurrency ? CurrencyInput : NativeTextInput
|
|
81
85
|
|
|
82
86
|
// @ts-expect-error - React's ref type system is weird
|
|
83
87
|
useImperativeHandle(inputRef, () => {
|
|
@@ -219,6 +223,19 @@ export const NumberIncrement = forwardRef<NativeTextInput, NumberIncrementProps>
|
|
|
219
223
|
},
|
|
220
224
|
} : {}
|
|
221
225
|
|
|
226
|
+
const currencyExtraProps = isCurrency ? {
|
|
227
|
+
value,
|
|
228
|
+
onChangeText: null,
|
|
229
|
+
onChangeValue: onChange,
|
|
230
|
+
prefix: prefix,
|
|
231
|
+
separator: separator ?? '.',
|
|
232
|
+
suffix: suffix,
|
|
233
|
+
delimiter: delimiter ?? ',',
|
|
234
|
+
minValue: min,
|
|
235
|
+
maxValue: max,
|
|
236
|
+
precision,
|
|
237
|
+
} : {}
|
|
238
|
+
|
|
222
239
|
const onPressInnerWrapper = () => {
|
|
223
240
|
handleFocus()
|
|
224
241
|
if (editable) innerInputRef.current?.focus?.()
|
|
@@ -270,6 +287,7 @@ export const NumberIncrement = forwardRef<NativeTextInput, NumberIncrementProps>
|
|
|
270
287
|
style={inputTextStyle}
|
|
271
288
|
ref={innerInputRef}
|
|
272
289
|
{...maskingExtraProps}
|
|
290
|
+
{...currencyExtraProps}
|
|
273
291
|
/>
|
|
274
292
|
) : (
|
|
275
293
|
<Text
|
|
@@ -26,6 +26,7 @@ export type NumberIncrementProps =
|
|
|
26
26
|
suffix?: MaskOptions['suffixUnit']
|
|
27
27
|
separator?: MaskOptions['separator']
|
|
28
28
|
delimiter?: MaskOptions['delimiter']
|
|
29
|
+
precision?: number
|
|
29
30
|
mask?: MaskOptions['mask']
|
|
30
31
|
formatter?: (value: string | number) => string
|
|
31
32
|
parseValue?: (value: string) => number
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { AnyFunction, StylesOf } from '@codeleap/common'
|
|
2
|
+
import Animated, { useAnimatedStyle } from 'react-native-reanimated'
|
|
3
|
+
import { Touchable } from '../Touchable'
|
|
4
|
+
import { View } from '../View'
|
|
5
|
+
import { DotComposition } from './styles'
|
|
6
|
+
|
|
7
|
+
export type PagerDot = {
|
|
8
|
+
onPress: AnyFunction
|
|
9
|
+
isActive: boolean
|
|
10
|
+
index: number
|
|
11
|
+
styles: StylesOf<DotComposition>
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function Dot({ onPress, isActive, index, styles }: PagerDot) {
|
|
15
|
+
const animation = useAnimatedStyle(() => {
|
|
16
|
+
const scale = isActive ? 1 : 0.6
|
|
17
|
+
return {
|
|
18
|
+
transform: [{ scale }],
|
|
19
|
+
...(isActive ? styles['dot:active'] : styles.dot),
|
|
20
|
+
}
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
return (
|
|
24
|
+
<Touchable
|
|
25
|
+
debugName={`default-pager-dot-touchable-${index}`}
|
|
26
|
+
onPress={onPress}
|
|
27
|
+
noFeedback
|
|
28
|
+
style={[styles.touchable, isActive && styles['touchable:active']]}
|
|
29
|
+
>
|
|
30
|
+
<Animated.View style={[animation, styles.dot]} />
|
|
31
|
+
</Touchable>
|
|
32
|
+
)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export type PagerDots = {
|
|
36
|
+
styles: StylesOf<DotComposition>
|
|
37
|
+
currentPage: number
|
|
38
|
+
setCurrentPage: (page: number) => void
|
|
39
|
+
pages: Array<any>
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function PagerDots({ styles, currentPage, setCurrentPage, pages }: PagerDots) {
|
|
43
|
+
return (
|
|
44
|
+
<View style={styles.wrapper}>
|
|
45
|
+
{pages?.map((_, i) => (
|
|
46
|
+
<Dot
|
|
47
|
+
key={`default-pager-dots-index-${i}`}
|
|
48
|
+
index={i}
|
|
49
|
+
onPress={() => setCurrentPage(i)}
|
|
50
|
+
isActive={i === currentPage}
|
|
51
|
+
styles={styles}
|
|
52
|
+
/>
|
|
53
|
+
))}
|
|
54
|
+
</View>
|
|
55
|
+
)
|
|
56
|
+
}
|
|
@@ -1,207 +1,119 @@
|
|
|
1
|
-
import React, { useCallback, useRef } from 'react'
|
|
2
|
-
import {
|
|
3
|
-
import
|
|
4
|
-
import {
|
|
1
|
+
import React, { useCallback, useRef, useState } from 'react'
|
|
2
|
+
import { ReduceMotion } from 'react-native-reanimated'
|
|
3
|
+
import Carousel, { ICarouselInstance } from 'react-native-reanimated-carousel'
|
|
4
|
+
import { CarouselRenderItemInfo, TCarouselProps } from 'react-native-reanimated-carousel/lib/typescript/types'
|
|
5
|
+
import { Dimensions, LayoutChangeEvent } from 'react-native'
|
|
6
|
+
import { onUpdate, TypeGuards, useConditionalState } from '@codeleap/common'
|
|
7
|
+
import { AnyRecord, IJSX, StyledComponentProps, useNestedStylesByKey } from '@codeleap/styles'
|
|
8
|
+
import { useStylesFor } from '../../hooks'
|
|
5
9
|
import { MobileStyleRegistry } from '../../Registry'
|
|
6
10
|
import { View } from '../View'
|
|
7
|
-
import { PageProps, PagerProps
|
|
8
|
-
import {
|
|
11
|
+
import { PageProps, PagerProps } from './types'
|
|
12
|
+
import { PagerDots } from './PagerDots'
|
|
9
13
|
|
|
10
14
|
export * from './styles'
|
|
11
15
|
export * from './types'
|
|
16
|
+
export * from './PagerDots'
|
|
17
|
+
|
|
18
|
+
const window = Dimensions.get('window')
|
|
12
19
|
|
|
13
|
-
export const Pager = (
|
|
20
|
+
export const Pager = <T extends unknown>(props: PagerProps<T>) => {
|
|
14
21
|
const {
|
|
15
|
-
|
|
22
|
+
pages,
|
|
16
23
|
page,
|
|
24
|
+
onChangePage,
|
|
25
|
+
initialPage,
|
|
17
26
|
style,
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
scrollLeftEnabled,
|
|
26
|
-
scrollRightEnabled,
|
|
27
|
-
onScroll,
|
|
28
|
-
scrollDirectionThrottle,
|
|
29
|
-
onSwipeLastPage,
|
|
30
|
-
waitEventDispatchTimeout,
|
|
27
|
+
showDots,
|
|
28
|
+
renderItem: RenderItem,
|
|
29
|
+
footer,
|
|
30
|
+
width: carouselWidth,
|
|
31
|
+
height: carouselHeight,
|
|
32
|
+
autoCalculateFooterHeight,
|
|
33
|
+
...rest
|
|
31
34
|
} = {
|
|
32
35
|
...Pager.defaultProps,
|
|
33
|
-
...
|
|
36
|
+
...props,
|
|
34
37
|
}
|
|
35
38
|
|
|
36
|
-
const
|
|
37
|
-
const
|
|
38
|
-
const [
|
|
39
|
-
const [scrollPositionX, setScrollPositionX] = React.useState(0)
|
|
40
|
-
const [_scrollEnabled, setScrollEnabled] = React.useState(true)
|
|
41
|
-
const waitEventDispatch = useRef(false)
|
|
39
|
+
const [currentPage, setCurrentPage] = useConditionalState(page, onChangePage, { initialValue: initialPage })
|
|
40
|
+
const carouselRef = useRef<ICarouselInstance>(null)
|
|
41
|
+
const [footerHeight, setFooterHeight] = useState(0)
|
|
42
42
|
|
|
43
43
|
const styles = useStylesFor(Pager.styleRegistryName, style)
|
|
44
|
+
const dotStyles = useNestedStylesByKey('dot', styles)
|
|
44
45
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
const WrapperComponent = renderPageWrapper || View
|
|
61
|
-
|
|
62
|
-
const hasScrollDirectionDisabled = !scrollLeftEnabled || !scrollRightEnabled
|
|
63
|
-
|
|
64
|
-
const handleScrollEnd = useCallback((event: ScrollEvent) => {
|
|
65
|
-
if (!scrollEnabled) return null
|
|
66
|
-
|
|
67
|
-
if (waitEventDispatch.current === true) return null
|
|
68
|
-
|
|
69
|
-
waitEventDispatch.current = true
|
|
70
|
-
|
|
71
|
-
const x = event?.nativeEvent.contentOffset.x
|
|
72
|
-
const toPage = Math.floor(((Math.round(x)) / Math.round(width)))
|
|
73
|
-
|
|
74
|
-
const length = childArr.length - 1
|
|
75
|
-
|
|
76
|
-
if (toPage >= length && TypeGuards.isFunction(onSwipeLastPage) && page >= length) {
|
|
77
|
-
onSwipeLastPage?.(event)
|
|
78
|
-
} else if (toPage !== page && toPage <= length) {
|
|
79
|
-
setPage(toPage)
|
|
80
|
-
setPositionX(toPage * width)
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
setTimeout(() => {
|
|
84
|
-
waitEventDispatch.current = false
|
|
85
|
-
}, waitEventDispatchTimeout)
|
|
86
|
-
}, [childArr, page, setPage, waitEventDispatch.current])
|
|
87
|
-
|
|
88
|
-
const handleScroll = (event: ScrollEvent) => {
|
|
89
|
-
const scrollX = event?.nativeEvent?.contentOffset?.x
|
|
90
|
-
|
|
91
|
-
if (!scrollEnabled) {
|
|
92
|
-
if (TypeGuards.isFunction(onScroll)) onScroll?.(event, { x: scrollX })
|
|
93
|
-
return null
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
if (!_scrollEnabled) {
|
|
97
|
-
setScrollPositionX(scrollX)
|
|
98
|
-
return null
|
|
46
|
+
onUpdate(() => {
|
|
47
|
+
carouselRef.current?.scrollTo({ index: currentPage, animated: true })
|
|
48
|
+
}, [currentPage])
|
|
49
|
+
|
|
50
|
+
const renderItem = useCallback(({ item, index, animationValue }: CarouselRenderItemInfo<any>) => {
|
|
51
|
+
const itemProps: Omit<PageProps<T>, 'item'> = {
|
|
52
|
+
isFirst: index === 0,
|
|
53
|
+
isLast: index === pages?.length - 1,
|
|
54
|
+
isOnly: pages?.length === 1,
|
|
55
|
+
isActive: index === currentPage,
|
|
56
|
+
isNext: index === currentPage + 1,
|
|
57
|
+
isPrevious: index === currentPage - 1,
|
|
58
|
+
index,
|
|
59
|
+
animationValue,
|
|
99
60
|
}
|
|
100
61
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
if (TypeGuards.isFunction(onScroll)) onScroll?.(event, { isLeft, isRight, x: scrollX })
|
|
105
|
-
|
|
106
|
-
if (hasScrollDirectionDisabled) {
|
|
107
|
-
if (isRight && !scrollRightEnabled || isLeft && !scrollLeftEnabled) {
|
|
108
|
-
setScrollEnabled(false)
|
|
109
|
-
|
|
110
|
-
setTimeout(() => {
|
|
111
|
-
scrollRef.current.scrollTo({
|
|
112
|
-
x: positionX,
|
|
113
|
-
animated: true,
|
|
114
|
-
})
|
|
115
|
-
|
|
116
|
-
setTimeout(() => {
|
|
117
|
-
setScrollEnabled(true)
|
|
118
|
-
}, scrollDirectionThrottle)
|
|
119
|
-
})
|
|
120
|
-
}
|
|
62
|
+
if (TypeGuards.isFunction(item)) {
|
|
63
|
+
const ItemComponent = item
|
|
64
|
+
return <ItemComponent {...itemProps} />
|
|
121
65
|
}
|
|
122
66
|
|
|
123
|
-
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
onUpdate(() => {
|
|
127
|
-
const x = width * page
|
|
128
|
-
if (scrollRef.current && x !== positionX) {
|
|
129
|
-
scrollRef.current.scrollTo({
|
|
130
|
-
x,
|
|
131
|
-
animated: true,
|
|
132
|
-
})
|
|
133
|
-
setPositionX(x)
|
|
134
|
-
}
|
|
135
|
-
}, [page])
|
|
67
|
+
return <RenderItem {...itemProps} item={item} />
|
|
68
|
+
}, [RenderItem, pages?.length, currentPage])
|
|
136
69
|
|
|
137
70
|
return (
|
|
138
|
-
<
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
isNext,
|
|
168
|
-
isPrevious,
|
|
169
|
-
index,
|
|
170
|
-
page,
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
const content = typeof child === 'function' ? child(pageProps) : child
|
|
174
|
-
|
|
175
|
-
const wrapperProps = {
|
|
176
|
-
key: index,
|
|
177
|
-
...pageWrapperProps,
|
|
178
|
-
style: [{ height: '100%', width }, styles?.page],
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
return <WrapperComponent {...wrapperProps}>{content}</WrapperComponent>
|
|
182
|
-
})}
|
|
183
|
-
</ScrollView>
|
|
71
|
+
<View style={styles.wrapper}>
|
|
72
|
+
<Carousel
|
|
73
|
+
data={pages}
|
|
74
|
+
withAnimation={{
|
|
75
|
+
type: 'timing',
|
|
76
|
+
config: {
|
|
77
|
+
reduceMotion: ReduceMotion.Never
|
|
78
|
+
},
|
|
79
|
+
}}
|
|
80
|
+
autoPlay={false}
|
|
81
|
+
ref={carouselRef}
|
|
82
|
+
loop={false}
|
|
83
|
+
defaultIndex={initialPage}
|
|
84
|
+
onSnapToItem={setCurrentPage}
|
|
85
|
+
maxScrollDistancePerSwipe={carouselWidth}
|
|
86
|
+
width={carouselWidth}
|
|
87
|
+
height={carouselHeight - (autoCalculateFooterHeight ? footerHeight : 0)}
|
|
88
|
+
renderItem={renderItem}
|
|
89
|
+
{...rest as TCarouselProps<T>}
|
|
90
|
+
style={styles.carousel}
|
|
91
|
+
/>
|
|
92
|
+
|
|
93
|
+
<View onLayout={(event: LayoutChangeEvent) => setFooterHeight(event.nativeEvent.layout.height)} style={styles.footerWrapper}>
|
|
94
|
+
{footer}
|
|
95
|
+
{showDots ? (
|
|
96
|
+
<PagerDots currentPage={currentPage} pages={pages} setCurrentPage={setCurrentPage} styles={dotStyles} />
|
|
97
|
+
) : null}
|
|
98
|
+
</View>
|
|
99
|
+
</View>
|
|
184
100
|
)
|
|
185
101
|
}
|
|
186
102
|
|
|
187
103
|
Pager.styleRegistryName = 'Pager'
|
|
188
|
-
Pager.elements = ['
|
|
104
|
+
Pager.elements = ['carousel', 'wrapper', 'footerWrapper', 'dot']
|
|
189
105
|
Pager.rootElement = 'wrapper'
|
|
190
106
|
|
|
191
107
|
Pager.withVariantTypes = <S extends AnyRecord>(styles: S) => {
|
|
192
|
-
return Pager as (props: StyledComponentProps<PagerProps
|
|
108
|
+
return Pager as <T extends unknown>(props: StyledComponentProps<PagerProps<T>, typeof styles>) => IJSX
|
|
193
109
|
}
|
|
194
110
|
|
|
195
111
|
Pager.defaultProps = {
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
scrollLeftEnabled: true,
|
|
203
|
-
scrollDirectionThrottle: 650,
|
|
204
|
-
waitEventDispatchTimeout: 250,
|
|
205
|
-
} as Partial<PagerProps>
|
|
112
|
+
width: window.width,
|
|
113
|
+
height: window.height,
|
|
114
|
+
showDots: true,
|
|
115
|
+
autoCalculateFooterHeight: true,
|
|
116
|
+
initialPage: 0,
|
|
117
|
+
} as Partial<PagerProps<unknown>>
|
|
206
118
|
|
|
207
119
|
MobileStyleRegistry.registerComponent(Pager)
|
|
@@ -1,2 +1,7 @@
|
|
|
1
|
+
export type DotStates = '' | 'active'
|
|
1
2
|
|
|
2
|
-
export type
|
|
3
|
+
export type DotParts = 'wrapper' | `touchable` | `dot`
|
|
4
|
+
|
|
5
|
+
export type DotComposition = DotParts | `${DotParts}:${DotStates}`
|
|
6
|
+
|
|
7
|
+
export type PagerComposition = 'carousel' | 'wrapper' | 'footerWrapper' | `dot${Capitalize<DotParts>}`
|
|
@@ -1,37 +1,27 @@
|
|
|
1
|
+
import React, { ReactNode } from 'react'
|
|
1
2
|
import { StyledProp } from '@codeleap/styles'
|
|
2
|
-
import { ReactNode } from 'react'
|
|
3
|
-
import { NativeScrollEvent, NativeSyntheticEvent, ScrollViewProps } from 'react-native'
|
|
4
3
|
import { PagerComposition } from './styles'
|
|
4
|
+
import { TCarouselProps } from 'react-native-reanimated-carousel'
|
|
5
|
+
import { CarouselRenderItemInfo } from 'react-native-reanimated-carousel/lib/typescript/types'
|
|
5
6
|
|
|
6
|
-
export type PageProps = {
|
|
7
|
+
export type PageProps<T extends unknown> = CarouselRenderItemInfo<T> & {
|
|
7
8
|
isLast: boolean
|
|
8
9
|
isFirst: boolean
|
|
9
10
|
isActive: boolean
|
|
11
|
+
isOnly: boolean
|
|
10
12
|
isNext: boolean
|
|
11
|
-
page: number
|
|
12
13
|
index: number
|
|
13
14
|
isPrevious: boolean
|
|
14
15
|
}
|
|
15
16
|
|
|
16
|
-
export type
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
width?: number
|
|
28
|
-
onScroll?: (event: ScrollEvent, args: { isLeft?: boolean; isRight?: boolean; x?: number }) => void
|
|
29
|
-
/** If TRUE render page, nextPage and prevPage only */
|
|
30
|
-
windowing?: boolean
|
|
31
|
-
scrollRightEnabled?: boolean
|
|
32
|
-
scrollLeftEnabled?: boolean
|
|
33
|
-
scrollDirectionThrottle?: number
|
|
34
|
-
onSwipeLastPage?: (event: ScrollEvent) => void
|
|
35
|
-
waitEventDispatchTimeout?: number
|
|
36
|
-
style?: StyledProp<PagerComposition>
|
|
37
|
-
}
|
|
17
|
+
export type PagerProps<T extends unknown> = Partial<Omit<TCarouselProps<T>, 'data' | 'renderItem'>> & {
|
|
18
|
+
pages: TCarouselProps<T>['data']
|
|
19
|
+
renderItem?: (props: PageProps<T>) => JSX.Element
|
|
20
|
+
page?: number
|
|
21
|
+
onChangePage?: (page: number) => void
|
|
22
|
+
initialPage?: number
|
|
23
|
+
style?: StyledProp<PagerComposition>
|
|
24
|
+
showDots?: boolean
|
|
25
|
+
footer?: ReactNode
|
|
26
|
+
autoCalculateFooterHeight?: boolean
|
|
27
|
+
}
|