@codeleap/mobile 1.0.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/dist/components/ActivityIndicator.d.ts +13 -0
- package/dist/components/ActivityIndicator.js +49 -0
- package/dist/components/ActivityIndicator.js.map +1 -0
- package/dist/components/Button.d.ts +116 -0
- package/dist/components/Button.js +57 -0
- package/dist/components/Button.js.map +1 -0
- package/dist/components/Checkbox/index.d.ts +14 -0
- package/dist/components/Checkbox/index.js +75 -0
- package/dist/components/Checkbox/index.js.map +1 -0
- package/dist/components/Checkbox/styles.d.ts +48 -0
- package/dist/components/Checkbox/styles.js +49 -0
- package/dist/components/Checkbox/styles.js.map +1 -0
- package/dist/components/Checkbox.d.ts +12 -0
- package/dist/components/Checkbox.js +61 -0
- package/dist/components/Checkbox.js.map +1 -0
- package/dist/components/Drawer.d.ts +23 -0
- package/dist/components/Drawer.js +78 -0
- package/dist/components/Drawer.js.map +1 -0
- package/dist/components/FileInput.d.ts +2 -0
- package/dist/components/FileInput.js +28 -0
- package/dist/components/FileInput.js.map +1 -0
- package/dist/components/Icon.d.ts +13 -0
- package/dist/components/Icon.js +47 -0
- package/dist/components/Icon.js.map +1 -0
- package/dist/components/Image.d.ts +16 -0
- package/dist/components/Image.js +48 -0
- package/dist/components/Image.js.map +1 -0
- package/dist/components/Navigation.d.ts +30 -0
- package/dist/components/Navigation.js +126 -0
- package/dist/components/Navigation.js.map +1 -0
- package/dist/components/RadioInput/index.d.ts +25 -0
- package/dist/components/RadioInput/index.js +75 -0
- package/dist/components/RadioInput/index.js.map +1 -0
- package/dist/components/RadioInput/styles.d.ts +50 -0
- package/dist/components/RadioInput/styles.js +45 -0
- package/dist/components/RadioInput/styles.js.map +1 -0
- package/dist/components/Scroll.d.ts +104 -0
- package/dist/components/Scroll.js +73 -0
- package/dist/components/Scroll.js.map +1 -0
- package/dist/components/Select/index.d.ts +2 -0
- package/dist/components/Select/index.js +31 -0
- package/dist/components/Select/index.js.map +1 -0
- package/dist/components/Slider.d.ts +20 -0
- package/dist/components/Slider.js +130 -0
- package/dist/components/Slider.js.map +1 -0
- package/dist/components/Switch.d.ts +13 -0
- package/dist/components/Switch.js +73 -0
- package/dist/components/Switch.js.map +1 -0
- package/dist/components/Text.d.ts +12 -0
- package/dist/components/Text.js +49 -0
- package/dist/components/Text.js.map +1 -0
- package/dist/components/TextInput.d.ts +146 -0
- package/dist/components/TextInput.js +147 -0
- package/dist/components/TextInput.js.map +1 -0
- package/dist/components/Touchable.d.ts +19 -0
- package/dist/components/Touchable.js +62 -0
- package/dist/components/Touchable.js.map +1 -0
- package/dist/components/View.d.ts +190 -0
- package/dist/components/View.js +53 -0
- package/dist/components/View.js.map +1 -0
- package/dist/components/index.d.ts +16 -0
- package/dist/components/index.js +29 -0
- package/dist/components/index.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +20 -0
- package/dist/index.js.map +1 -0
- package/dist/modules/fastImage.d.ts +1 -0
- package/dist/modules/fastImage.js +10 -0
- package/dist/modules/fastImage.js.map +1 -0
- package/dist/types/utility.d.ts +2 -0
- package/dist/types/utility.js +3 -0
- package/dist/types/utility.js.map +1 -0
- package/dist/utils/OSAlert.d.ts +31 -0
- package/dist/utils/OSAlert.js +89 -0
- package/dist/utils/OSAlert.js.map +1 -0
- package/dist/utils/styles.d.ts +1 -0
- package/dist/utils/styles.js +13 -0
- package/dist/utils/styles.js.map +1 -0
- package/package.json +32 -0
- package/src/components/ActivityIndicator.tsx +37 -0
- package/src/components/Button.tsx +61 -0
- package/src/components/Checkbox/index.tsx +68 -0
- package/src/components/Checkbox/styles.ts +63 -0
- package/src/components/FileInput.tsx +6 -0
- package/src/components/Icon.tsx +35 -0
- package/src/components/Image.tsx +43 -0
- package/src/components/Navigation.tsx +125 -0
- package/src/components/RadioInput/index.tsx +76 -0
- package/src/components/RadioInput/styles.ts +62 -0
- package/src/components/Scroll.tsx +87 -0
- package/src/components/Select/index.tsx +8 -0
- package/src/components/Slider.tsx +198 -0
- package/src/components/Switch.tsx +73 -0
- package/src/components/Text.tsx +31 -0
- package/src/components/TextInput.tsx +206 -0
- package/src/components/Touchable.tsx +54 -0
- package/src/components/View.tsx +53 -0
- package/src/components/index.ts +16 -0
- package/src/index.ts +2 -0
- package/src/modules/fastImage.ts +2 -0
- package/src/types/utility.ts +3 -0
- package/src/utils/OSAlert.ts +122 -0
- package/src/utils/styles.ts +17 -0
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import * as React from 'react'
|
|
2
|
+
import { useComponentStyle, ButtonStyles, ComponentVariants, ButtonComposition } from '@codeleap/common';
|
|
3
|
+
import { forwardRef } from 'react'
|
|
4
|
+
import { StylesOf } from '../types/utility';
|
|
5
|
+
import { Text } from './Text';
|
|
6
|
+
import { Touchable, TouchableProps } from './Touchable';
|
|
7
|
+
import { Icon } from './Icon';
|
|
8
|
+
import { ActivityIndicator } from './ActivityIndicator';
|
|
9
|
+
import { IconPlaceholder } from '@codeleap/common';
|
|
10
|
+
import { StyleSheet, TouchableOpacity } from 'react-native';
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
export type ButtonProps = Omit<TouchableProps,'variants'> & ComponentVariants<typeof ButtonStyles> & {
|
|
16
|
+
text?:string
|
|
17
|
+
rightIcon?: IconPlaceholder
|
|
18
|
+
icon?: IconPlaceholder
|
|
19
|
+
styles?: StylesOf<ButtonComposition>
|
|
20
|
+
loading?: boolean
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export const Button = forwardRef<TouchableOpacity, ButtonProps>( (buttonProps,ref) => {
|
|
24
|
+
const {
|
|
25
|
+
variants = [],
|
|
26
|
+
responsiveVariants = {},
|
|
27
|
+
children,
|
|
28
|
+
icon,
|
|
29
|
+
text,
|
|
30
|
+
loading,
|
|
31
|
+
styles = {},
|
|
32
|
+
onPress,
|
|
33
|
+
rightIcon,
|
|
34
|
+
...props
|
|
35
|
+
} = buttonProps
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
const variantStyles = useComponentStyle('Button', {
|
|
39
|
+
variants,
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
function handlePress(e:Parameters<ButtonProps['onPress']>[0]){
|
|
44
|
+
onPress && onPress(e)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
return (
|
|
49
|
+
<Touchable
|
|
50
|
+
style={[variantStyles.wrapper, styles.wrapper]}
|
|
51
|
+
onPress={handlePress}
|
|
52
|
+
ref={ref}
|
|
53
|
+
{...props}
|
|
54
|
+
>
|
|
55
|
+
{loading && <ActivityIndicator style={[variantStyles.loader, styles.loader]} />}
|
|
56
|
+
{!loading && <Icon name={icon} style={StyleSheet.flatten([variantStyles.icon, styles.icon, variantStyles.leftIcon, styles.leftIcon])}/>}
|
|
57
|
+
{children || <Text text={text} style={[variantStyles.text, styles.text]}/>}
|
|
58
|
+
<Icon name={rightIcon} style={StyleSheet.flatten([variantStyles.icon, styles.icon, variantStyles.rightIcon, styles.rightIcon])}/>
|
|
59
|
+
</Touchable>
|
|
60
|
+
)
|
|
61
|
+
})
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import * as React from 'react'
|
|
2
|
+
import { ComponentVariants, useComponentStyle, StylesOf, Form, useValidate } from '@codeleap/common'
|
|
3
|
+
import { ComponentPropsWithRef, forwardRef, ReactNode } from 'react'
|
|
4
|
+
import { Switch as NativeCheckbox } from 'react-native'
|
|
5
|
+
import { InputLabel, FormError } from '../TextInput'
|
|
6
|
+
import { View } from '../View'
|
|
7
|
+
import { Touchable } from '../Touchable'
|
|
8
|
+
import {
|
|
9
|
+
MobileCheckboxStyles as CheckboxStyles, MobileCheckboxComposition as CheckboxComposition
|
|
10
|
+
} from './styles'
|
|
11
|
+
export * from './styles'
|
|
12
|
+
|
|
13
|
+
type NativeCheckboxProps = Omit<
|
|
14
|
+
ComponentPropsWithRef<typeof NativeCheckbox>,
|
|
15
|
+
'thumbColor'|'trackColor'
|
|
16
|
+
>
|
|
17
|
+
type CheckboxProps = NativeCheckboxProps & {
|
|
18
|
+
variants?: ComponentVariants<typeof CheckboxStyles>['variants']
|
|
19
|
+
label?: ReactNode
|
|
20
|
+
styles?: StylesOf<CheckboxComposition>
|
|
21
|
+
validate?: Form.ValidatorFunction | string
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export const Checkbox = forwardRef<NativeCheckbox,CheckboxProps>((checkboxProps,ref) => {
|
|
25
|
+
const {
|
|
26
|
+
variants = [],
|
|
27
|
+
style = {},
|
|
28
|
+
styles = {},
|
|
29
|
+
label,
|
|
30
|
+
value,
|
|
31
|
+
onValueChange,
|
|
32
|
+
validate,
|
|
33
|
+
...props
|
|
34
|
+
} = checkboxProps
|
|
35
|
+
|
|
36
|
+
const variantStyles = useComponentStyle('Checkbox', {
|
|
37
|
+
// @ts-ignore
|
|
38
|
+
variants,
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
const {error, showError} = useValidate(value,validate)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
function getStyles(key:CheckboxComposition){
|
|
45
|
+
return [
|
|
46
|
+
variantStyles[key],
|
|
47
|
+
variantStyles[key],
|
|
48
|
+
key === 'wrapper' ? style : {},
|
|
49
|
+
value ? variantStyles[key + ':checked'] : {},
|
|
50
|
+
value ? styles[key + ':checked'] : {},
|
|
51
|
+
showError ? variantStyles[key + ':error'] : {},
|
|
52
|
+
showError ? styles[key + ':error'] : {},
|
|
53
|
+
checkboxProps.disabled ? variantStyles[key + ':disabled'] : {},
|
|
54
|
+
checkboxProps.disabled ? styles[key + ':disabled'] : {},
|
|
55
|
+
]
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return <View style={getStyles('wrapper')} {...props}>
|
|
59
|
+
<Touchable style={getStyles('input')} onPress={() => onValueChange(!value)}>
|
|
60
|
+
<View style={getStyles('checkmarkWrapper')}>
|
|
61
|
+
<View style={getStyles('checkmark')}/>
|
|
62
|
+
</View>
|
|
63
|
+
<InputLabel label={label} style={getStyles('label')}/>
|
|
64
|
+
|
|
65
|
+
</Touchable>
|
|
66
|
+
<FormError message={error.message} style={getStyles('error')} />
|
|
67
|
+
</View>
|
|
68
|
+
})
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import {createDefaultVariantFactory, includePresets} from '@codeleap/common'
|
|
2
|
+
|
|
3
|
+
type CheckboxParts = 'wrapper' | 'label' | 'input' | 'checkmark' | 'checkmarkWrapper' | 'error'
|
|
4
|
+
|
|
5
|
+
export type MobileCheckboxComposition = CheckboxParts | `${CheckboxParts}:checked` | `${CheckboxParts}:disabled` | `${CheckboxParts}:error`;
|
|
6
|
+
const createCheckboxStyle = createDefaultVariantFactory<MobileCheckboxComposition>()
|
|
7
|
+
|
|
8
|
+
const presets = includePresets((styles) => createCheckboxStyle(() => ({ wrapper: styles })))
|
|
9
|
+
|
|
10
|
+
export const MobileCheckboxStyles = {
|
|
11
|
+
...presets,
|
|
12
|
+
default: createCheckboxStyle((theme) => {
|
|
13
|
+
const size = theme.typography.baseFontSize * 1.2
|
|
14
|
+
|
|
15
|
+
const markHeight = size * 0.5
|
|
16
|
+
const markWidth = size * 0.25
|
|
17
|
+
|
|
18
|
+
const translateX = -(markWidth/2)
|
|
19
|
+
const translateY = -(markHeight/2)
|
|
20
|
+
return {
|
|
21
|
+
wrapper: {
|
|
22
|
+
|
|
23
|
+
},
|
|
24
|
+
input: {
|
|
25
|
+
flexDirection: 'row',
|
|
26
|
+
...theme.presets.alignCenter,
|
|
27
|
+
borderRadius: theme.borderRadius.small
|
|
28
|
+
},
|
|
29
|
+
label: {
|
|
30
|
+
...theme.spacing.marginLeft(0.5),
|
|
31
|
+
},
|
|
32
|
+
|
|
33
|
+
checkmark: {
|
|
34
|
+
position: 'absolute',
|
|
35
|
+
top: '40%',
|
|
36
|
+
left: '50%',
|
|
37
|
+
|
|
38
|
+
height: markHeight,
|
|
39
|
+
width: markWidth,
|
|
40
|
+
transform: [{translateX}, {translateY}, {rotate: '45deg'},]
|
|
41
|
+
},
|
|
42
|
+
"checkmark:checked": {
|
|
43
|
+
...theme.border.white({
|
|
44
|
+
width: 2,
|
|
45
|
+
directions: ['right','bottom']
|
|
46
|
+
}),
|
|
47
|
+
},
|
|
48
|
+
checkmarkWrapper: {
|
|
49
|
+
position: 'relative',
|
|
50
|
+
width: size,
|
|
51
|
+
height: size,
|
|
52
|
+
...theme.border.gray(1)
|
|
53
|
+
},
|
|
54
|
+
"checkmarkWrapper:checked": {
|
|
55
|
+
backgroundColor: theme.colors.primary
|
|
56
|
+
},
|
|
57
|
+
error: {
|
|
58
|
+
color: theme.colors.negative
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}),
|
|
62
|
+
|
|
63
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import * as React from 'react'
|
|
2
|
+
import { ComponentVariants, IconPlaceholder, IconStyles, useComponentStyle, useStyle } from '@codeleap/common'
|
|
3
|
+
import { StyleSheet } from 'react-native'
|
|
4
|
+
|
|
5
|
+
export type IconProps = {
|
|
6
|
+
name:IconPlaceholder
|
|
7
|
+
style?: {
|
|
8
|
+
color: string
|
|
9
|
+
size?:string|number
|
|
10
|
+
width?:string|number
|
|
11
|
+
height?:string|number
|
|
12
|
+
}
|
|
13
|
+
variants?: ComponentVariants<typeof IconStyles>['variants']
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export const Icon:React.FC<IconProps> = ({name, style, variants}) => {
|
|
17
|
+
const {Theme} = useStyle()
|
|
18
|
+
|
|
19
|
+
if (!name) return null
|
|
20
|
+
|
|
21
|
+
const Component = Theme?.icons?.[name]
|
|
22
|
+
|
|
23
|
+
const {logger} = useStyle()
|
|
24
|
+
const variantStyles = useComponentStyle('Icon', {
|
|
25
|
+
variants,
|
|
26
|
+
styles: {
|
|
27
|
+
icon: StyleSheet.flatten([style]),
|
|
28
|
+
}
|
|
29
|
+
})
|
|
30
|
+
if (!Component) {
|
|
31
|
+
logger.warn('Icon', `No icon found in theme for name "${name}"`, 'Component')
|
|
32
|
+
return null
|
|
33
|
+
}
|
|
34
|
+
return <Component style={variantStyles.icon}/>
|
|
35
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import * as React from 'react'
|
|
2
|
+
import { ComponentVariants, ImageStyles, useComponentStyle } from "@codeleap/common"
|
|
3
|
+
import { ComponentPropsWithoutRef } from "react"
|
|
4
|
+
import { Image as NativeImage } from 'react-native'
|
|
5
|
+
import {FastImage} from '../modules/fastImage'
|
|
6
|
+
|
|
7
|
+
type NativeImageProps = ComponentPropsWithoutRef<typeof NativeImage>
|
|
8
|
+
export type ImageProps = Omit<NativeImageProps, 'source'> & {
|
|
9
|
+
variants?: ComponentVariants<typeof ImageStyles>['variants']
|
|
10
|
+
fast?: boolean
|
|
11
|
+
source: NativeImageProps['source'] & {
|
|
12
|
+
priority?: keyof typeof FastImage.priority
|
|
13
|
+
}
|
|
14
|
+
resizeMode?: keyof typeof FastImage.resizeMode
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export const Image:React.FC<ImageProps> = (props) => {
|
|
18
|
+
const {
|
|
19
|
+
variants,
|
|
20
|
+
style,
|
|
21
|
+
fast,
|
|
22
|
+
resizeMode,
|
|
23
|
+
resizeMethod,
|
|
24
|
+
...imageProps
|
|
25
|
+
} = props
|
|
26
|
+
|
|
27
|
+
const variantStyles = useComponentStyle('Image', {variants})
|
|
28
|
+
|
|
29
|
+
const styles = [variantStyles.wrapper, style]
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
if(fast){
|
|
33
|
+
return <FastImage
|
|
34
|
+
style={styles}
|
|
35
|
+
resizeMode={ FastImage.resizeMode[resizeMode || 'contain']}
|
|
36
|
+
{...imageProps}
|
|
37
|
+
/>
|
|
38
|
+
}
|
|
39
|
+
return <NativeImage
|
|
40
|
+
style={styles}
|
|
41
|
+
{...imageProps}
|
|
42
|
+
/>
|
|
43
|
+
}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
import * as React from 'react'
|
|
3
|
+
import { RouteConfig } from '@react-navigation/native'
|
|
4
|
+
import { createNativeStackNavigator, NativeStackScreenProps } from '@react-navigation/native-stack'
|
|
5
|
+
import { AnyFunction, EnhancedTheme, IconPlaceholder, useStyle } from '@codeleap/common'
|
|
6
|
+
import { createDrawerNavigator } from '@react-navigation/drawer'
|
|
7
|
+
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs'
|
|
8
|
+
import { Icon } from './Icon'
|
|
9
|
+
import { Paths } from '@codeleap/common/dist/types/pathMapping'
|
|
10
|
+
|
|
11
|
+
export type AppScenes = {
|
|
12
|
+
[x:string] : {
|
|
13
|
+
[y:string] : AnyFunction | ( Partial<Pick<RouteConfig<any, any, any, any, any>, 'options'>> & {
|
|
14
|
+
render: AnyFunction,
|
|
15
|
+
icon?: any
|
|
16
|
+
})
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const parseModulePages = (pageModule:string, [pageName, Component]) => {
|
|
21
|
+
const name = `${pageModule}.${pageName}`
|
|
22
|
+
|
|
23
|
+
let props = {
|
|
24
|
+
name,
|
|
25
|
+
key: name,
|
|
26
|
+
options: {
|
|
27
|
+
title: pageName,
|
|
28
|
+
},
|
|
29
|
+
component: null,
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
switch (typeof Component){
|
|
33
|
+
case 'function':
|
|
34
|
+
props.component = Component
|
|
35
|
+
case 'object':
|
|
36
|
+
props = {
|
|
37
|
+
...props,
|
|
38
|
+
...Component,
|
|
39
|
+
}
|
|
40
|
+
if (typeof Component.default === 'function'){
|
|
41
|
+
props.component = Component.default
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (Component.render){
|
|
45
|
+
props.component = typeof Component.render === 'function' ? Component.render : Component?.render?.default
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
default:
|
|
49
|
+
break
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return props
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
type FlattenScenesArgs = {
|
|
56
|
+
scenes:AppScenes,
|
|
57
|
+
Theme: EnhancedTheme<any>
|
|
58
|
+
}
|
|
59
|
+
const flattenScenes = ({
|
|
60
|
+
scenes,
|
|
61
|
+
Theme,
|
|
62
|
+
}:FlattenScenesArgs) => {
|
|
63
|
+
|
|
64
|
+
return Object.entries(scenes).reduce((acc, [pageModule, pages]) => {
|
|
65
|
+
const thisModulePages = {}
|
|
66
|
+
for (const pageData of Object.entries(pages)){
|
|
67
|
+
const parsedData = parseModulePages(pageModule, pageData)
|
|
68
|
+
|
|
69
|
+
thisModulePages[parsedData.name] = parsedData
|
|
70
|
+
}
|
|
71
|
+
return {...acc, ...thisModulePages}
|
|
72
|
+
}, {})
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const Navigators = {
|
|
76
|
+
drawer: createDrawerNavigator(),
|
|
77
|
+
stack: createNativeStackNavigator(),
|
|
78
|
+
tab: createBottomTabNavigator(),
|
|
79
|
+
}
|
|
80
|
+
type NavigationType = keyof typeof Navigators
|
|
81
|
+
type NavigationProps<T extends NavigationType> = {
|
|
82
|
+
scenes: AppScenes
|
|
83
|
+
type: T
|
|
84
|
+
} & React.ComponentPropsWithoutRef<(typeof Navigators)[T]['Navigator']>
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
export const Navigation = <T extends NavigationType, P extends NavigationProps<T> = NavigationProps<T>>({type, scenes, ...props}:P) => {
|
|
88
|
+
const Navigator:(typeof Navigators)[T] = Navigators[type]
|
|
89
|
+
const { Theme } = useStyle()
|
|
90
|
+
const flatScenes = flattenScenes({
|
|
91
|
+
Theme,
|
|
92
|
+
scenes,
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
const otherProps = props as any
|
|
96
|
+
// @ts-ignore
|
|
97
|
+
const screenOptions:P['screenOptions'] = ({route, navigation}) => {
|
|
98
|
+
const propOptions = typeof (otherProps?.screenOptions || {}) === 'function' ?
|
|
99
|
+
otherProps.screenOptions({route, navigation}) :
|
|
100
|
+
otherProps?.screenOptions
|
|
101
|
+
|
|
102
|
+
return {
|
|
103
|
+
...propOptions,
|
|
104
|
+
tabBarIcon: (style) => {
|
|
105
|
+
if (flatScenes?.[route.name]?.icon){
|
|
106
|
+
return <Icon name={flatScenes?.[route.name]?.icon} style={style}/>
|
|
107
|
+
}
|
|
108
|
+
return null
|
|
109
|
+
},
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
return <Navigator.Navigator {...otherProps} screenOptions={screenOptions} >
|
|
114
|
+
{
|
|
115
|
+
Object.values(flatScenes).map((props) => <Navigator.Screen {...(props as any)}/>)
|
|
116
|
+
}
|
|
117
|
+
</Navigator.Navigator>
|
|
118
|
+
|
|
119
|
+
}
|
|
120
|
+
export type NavigationScreenProps<S extends AppScenes, T extends NativeStackScreenProps<any, any> = NativeStackScreenProps<any, any>> = Omit<T, 'navigation'> & {
|
|
121
|
+
navigation: Omit<T['navigation'],'navigate'> & {
|
|
122
|
+
navigate: (to: Exclude<Paths<S, 1>, keyof S>) => void
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import * as React from 'react'
|
|
2
|
+
import { ReactNode, useRef, ComponentPropsWithoutRef } from 'react'
|
|
3
|
+
import { v4 } from 'uuid'
|
|
4
|
+
|
|
5
|
+
import {Text } from '../Text'
|
|
6
|
+
import { Touchable } from '../Touchable'
|
|
7
|
+
import { ComponentVariants, StylesOf, useComponentStyle } from '@codeleap/common'
|
|
8
|
+
import { View } from '../View'
|
|
9
|
+
import { MobileRadioInputStyles, MobileRadioInputComposition} from './styles'
|
|
10
|
+
|
|
11
|
+
export * from './styles'
|
|
12
|
+
|
|
13
|
+
type RadioItem<T extends unknown = any> = {
|
|
14
|
+
value: T,
|
|
15
|
+
label: ReactNode
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const getRadioStyle = (props) => useComponentStyle('RadioInput', props)
|
|
19
|
+
|
|
20
|
+
export type RadioButtonProps = Omit<ComponentPropsWithoutRef<typeof Touchable>, 'style'> & {
|
|
21
|
+
item: RadioItem
|
|
22
|
+
select: () => void
|
|
23
|
+
style: StylesOf<MobileRadioInputComposition>
|
|
24
|
+
checked: boolean
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export type RadioGroupProps<T> = {
|
|
28
|
+
options: RadioItem<T>[]
|
|
29
|
+
value: T
|
|
30
|
+
onValueChange(value:T):void
|
|
31
|
+
label:ReactNode
|
|
32
|
+
styles?: StylesOf<MobileRadioInputComposition>
|
|
33
|
+
} & ComponentVariants<typeof MobileRadioInputStyles>
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
export const RadioButton:React.FC<RadioButtonProps> = ({item, select, style, checked, ...props}) => {
|
|
37
|
+
|
|
38
|
+
return <Touchable onPress={select} style={style.itemWrapper} >
|
|
39
|
+
<View style={[
|
|
40
|
+
style.button,
|
|
41
|
+
checked && style['button:checked'],
|
|
42
|
+
]}>
|
|
43
|
+
<View style={[
|
|
44
|
+
style['buttonMark'],
|
|
45
|
+
checked && style['buttonMark:checked'],
|
|
46
|
+
]} />
|
|
47
|
+
</View>
|
|
48
|
+
{typeof item.label === 'string' ? <Text text={item.label} style={style.text}/> : item.label }
|
|
49
|
+
</Touchable>
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
export const RadioGroup = <T extends unknown>(radioGroupProps:RadioGroupProps<T>) => {
|
|
54
|
+
const {options, value, onValueChange, label, responsiveVariants, variants, styles} = radioGroupProps
|
|
55
|
+
|
|
56
|
+
const radioStyle = getRadioStyle({
|
|
57
|
+
responsiveVariants,
|
|
58
|
+
variants,
|
|
59
|
+
styles,
|
|
60
|
+
})
|
|
61
|
+
return <View style={radioStyle.wrapper}>
|
|
62
|
+
{typeof label === 'string' ? <Text text={label}/> : label }
|
|
63
|
+
<View style={radioStyle.listWrapper}>
|
|
64
|
+
{
|
|
65
|
+
options?.map((item, idx) => <RadioButton
|
|
66
|
+
item={item}
|
|
67
|
+
key={idx}
|
|
68
|
+
style={radioStyle}
|
|
69
|
+
|
|
70
|
+
checked={value === item.value}
|
|
71
|
+
select={() => onValueChange(item.value)}
|
|
72
|
+
/>)
|
|
73
|
+
}
|
|
74
|
+
</View>
|
|
75
|
+
</View>
|
|
76
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { createDefaultVariantFactory, RadioInputStyles } from '@codeleap/common';
|
|
2
|
+
type RadioParts = 'button' | 'itemWrapper' | 'text' | 'buttonMark'
|
|
3
|
+
|
|
4
|
+
type RadioGroupParts = 'label' |'wrapper' |'list'
|
|
5
|
+
|
|
6
|
+
export type MobileRadioInputComposition = `${RadioParts}:checked` | RadioParts | RadioGroupParts
|
|
7
|
+
|
|
8
|
+
const createRadioStyle = createDefaultVariantFactory<MobileRadioInputComposition>()
|
|
9
|
+
|
|
10
|
+
const defaultStyles = RadioInputStyles.default
|
|
11
|
+
|
|
12
|
+
export const MobileRadioInputStyles = {
|
|
13
|
+
...RadioInputStyles,
|
|
14
|
+
default: createRadioStyle((theme) => {
|
|
15
|
+
const style = defaultStyles(theme)
|
|
16
|
+
|
|
17
|
+
const itemHeight = theme.typography.baseFontSize * 1.2
|
|
18
|
+
const markHeight = itemHeight / 2
|
|
19
|
+
const translateX = -(markHeight/2)
|
|
20
|
+
const translateY = -(markHeight/2)
|
|
21
|
+
return {
|
|
22
|
+
...style,
|
|
23
|
+
itemWrapper: {
|
|
24
|
+
...style.itemWrapper,
|
|
25
|
+
},
|
|
26
|
+
button: {
|
|
27
|
+
height: itemHeight,
|
|
28
|
+
width: itemHeight,
|
|
29
|
+
...theme.border.primary(1),
|
|
30
|
+
borderRadius: theme.borderRadius.large,
|
|
31
|
+
|
|
32
|
+
position: 'relative',
|
|
33
|
+
...theme.spacing.marginRight(1),
|
|
34
|
+
|
|
35
|
+
},
|
|
36
|
+
'buttonMark': {
|
|
37
|
+
backgroundColor: theme.colors.primary,
|
|
38
|
+
position: 'absolute',
|
|
39
|
+
left: '50%',
|
|
40
|
+
top: '50%',
|
|
41
|
+
height: markHeight,
|
|
42
|
+
width: markHeight,
|
|
43
|
+
|
|
44
|
+
transform: [{translateX},{translateY}],
|
|
45
|
+
borderRadius: theme.borderRadius.large,
|
|
46
|
+
opacity: 0
|
|
47
|
+
},
|
|
48
|
+
'buttonMark:checked': {
|
|
49
|
+
opacity: 1
|
|
50
|
+
},
|
|
51
|
+
}
|
|
52
|
+
}),
|
|
53
|
+
square: createRadioStyle(() => ({
|
|
54
|
+
'buttonMark': {
|
|
55
|
+
borderRadius: 0,
|
|
56
|
+
},
|
|
57
|
+
button: {
|
|
58
|
+
borderRadius: 0,
|
|
59
|
+
|
|
60
|
+
},
|
|
61
|
+
})),
|
|
62
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import * as React from 'react'
|
|
2
|
+
import { forwardRef, useState } from 'react'
|
|
3
|
+
import { deepEqual, onUpdate, useComponentStyle, usePrevious, useStyle } from '@codeleap/common'
|
|
4
|
+
import { KeyboardAwareScrollViewProps, KeyboardAwareScrollView as KBDView} from 'react-native-keyboard-aware-scroll-view'
|
|
5
|
+
import { RefreshControl, ScrollView } from 'react-native'
|
|
6
|
+
import { ViewProps } from './View'
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
export type ScrollProps = KeyboardAwareScrollViewProps & ViewProps & {
|
|
10
|
+
onRefresh?:() => void
|
|
11
|
+
refreshTimeout?: number
|
|
12
|
+
changeData?:any
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const KeyboardAwareScrollView = KBDView as unknown as React.ForwardRefExoticComponent<
|
|
16
|
+
ViewProps &
|
|
17
|
+
{
|
|
18
|
+
refreshControl?: JSX.Element
|
|
19
|
+
ref?: ScrollView
|
|
20
|
+
|
|
21
|
+
}
|
|
22
|
+
>
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
export const Scroll = forwardRef<ScrollView, ScrollProps>((scrollProps, ref) => {
|
|
26
|
+
const {
|
|
27
|
+
variants = [],
|
|
28
|
+
style,
|
|
29
|
+
refreshTimeout = 3000,
|
|
30
|
+
children,
|
|
31
|
+
changeData,
|
|
32
|
+
...props
|
|
33
|
+
} = scrollProps
|
|
34
|
+
const hasRefresh = !!props.onRefresh
|
|
35
|
+
const [refreshing, setRefreshing] = useState(false)
|
|
36
|
+
|
|
37
|
+
const timer = React.useRef(null)
|
|
38
|
+
const previousData = usePrevious(changeData)
|
|
39
|
+
|
|
40
|
+
const onRefresh = () => {
|
|
41
|
+
if(timer.current){
|
|
42
|
+
clearTimeout(timer.current)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
setRefreshing(true)
|
|
46
|
+
|
|
47
|
+
props.onRefresh()
|
|
48
|
+
|
|
49
|
+
timer.current = setTimeout(() => {
|
|
50
|
+
setRefreshing(false)
|
|
51
|
+
}, refreshTimeout)
|
|
52
|
+
|
|
53
|
+
}
|
|
54
|
+
onUpdate(() => {
|
|
55
|
+
if(refreshing && !deepEqual(previousData, changeData)){
|
|
56
|
+
setRefreshing(false)
|
|
57
|
+
if(timer.current){
|
|
58
|
+
clearTimeout(timer.current)
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
},[refreshing, changeData])
|
|
62
|
+
const {
|
|
63
|
+
Theme
|
|
64
|
+
} = useStyle()
|
|
65
|
+
|
|
66
|
+
const variantStyles = useComponentStyle('View', {
|
|
67
|
+
variants
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
return (
|
|
71
|
+
|
|
72
|
+
<KeyboardAwareScrollView
|
|
73
|
+
style={[Theme.presets.full, variantStyles.wrapper, style]}
|
|
74
|
+
ref={ref as unknown as ScrollView}
|
|
75
|
+
{...props}
|
|
76
|
+
refreshControl={
|
|
77
|
+
hasRefresh &&
|
|
78
|
+
<RefreshControl
|
|
79
|
+
refreshing={refreshing}
|
|
80
|
+
onRefresh={onRefresh}
|
|
81
|
+
/>
|
|
82
|
+
}
|
|
83
|
+
>
|
|
84
|
+
{ children }
|
|
85
|
+
</KeyboardAwareScrollView>
|
|
86
|
+
)
|
|
87
|
+
})
|