Vlad Narizhnyi
1 year ago
109 changed files with 1457 additions and 421 deletions
@ -1,7 +1,10 @@
@@ -1,7 +1,10 @@
|
||||
module.exports = { |
||||
bracketSpacing: false, |
||||
jsxBracketSameLine: true, |
||||
bracketSpacing: true, |
||||
bracketSameLine: true, |
||||
singleQuote: true, |
||||
trailingComma: 'all', |
||||
arrowParens: 'avoid', |
||||
tabWidth: 4, |
||||
useTabs: true, |
||||
semi: false, |
||||
}; |
||||
|
@ -1,26 +1,22 @@
@@ -1,26 +1,22 @@
|
||||
import React, {useEffect} from 'react'; |
||||
import SplashScreen from 'react-native-splash-screen'; |
||||
import {Navigation} from './src/module/root'; |
||||
import './src/i18n/index'; |
||||
import {Provider} from 'react-redux'; |
||||
import {store} from './src/store/store'; |
||||
import {GestureHandlerRootView} from 'react-native-gesture-handler'; |
||||
|
||||
if (__DEV__) { |
||||
import('./ReactotronConfig').then(() => console.log('Reactotron Configured')); |
||||
} |
||||
import React, { useEffect } from 'react' |
||||
import SplashScreen from 'react-native-splash-screen' |
||||
import { Navigation } from './src/module/root' |
||||
import './src/i18n/index' |
||||
import { Provider } from 'react-redux' |
||||
import { store } from './src/store/store' |
||||
import { GestureHandlerRootView } from 'react-native-gesture-handler' |
||||
|
||||
const App = () => { |
||||
useEffect(() => { |
||||
SplashScreen.hide(); |
||||
}, []); |
||||
return ( |
||||
<GestureHandlerRootView style={{flex: 1}}> |
||||
<Provider store={store}> |
||||
<Navigation /> |
||||
</Provider> |
||||
</GestureHandlerRootView> |
||||
); |
||||
}; |
||||
useEffect(() => { |
||||
SplashScreen.hide() |
||||
}, []) |
||||
return ( |
||||
<GestureHandlerRootView style={{ flex: 1 }}> |
||||
<Provider store={store}> |
||||
<Navigation /> |
||||
</Provider> |
||||
</GestureHandlerRootView> |
||||
) |
||||
} |
||||
|
||||
export default App; |
||||
export default App |
||||
|
Binary file not shown.
Binary file not shown.
@ -0,0 +1,17 @@
@@ -0,0 +1,17 @@
|
||||
{ |
||||
"migIndex": 1, |
||||
"data": [ |
||||
{ |
||||
"path": "src/assets/resources/fonts/Roboto-Bold.ttf", |
||||
"sha1": "62442a18a9fe9457c1afeabf683d263a691b7798" |
||||
}, |
||||
{ |
||||
"path": "src/assets/resources/fonts/Roboto-Regular.ttf", |
||||
"sha1": "56c5c0d38bde4c1f1549dda43db37b09c608aad3" |
||||
}, |
||||
{ |
||||
"path": "src/assets/resources/fonts/fontello.ttf", |
||||
"sha1": "73fc32c22bdcda8f8bea6b28b74a9a45ba721f7d" |
||||
} |
||||
] |
||||
} |
Binary file not shown.
Binary file not shown.
@ -0,0 +1,17 @@
@@ -0,0 +1,17 @@
|
||||
{ |
||||
"migIndex": 1, |
||||
"data": [ |
||||
{ |
||||
"path": "src/assets/resources/fonts/Roboto-Bold.ttf", |
||||
"sha1": "62442a18a9fe9457c1afeabf683d263a691b7798" |
||||
}, |
||||
{ |
||||
"path": "src/assets/resources/fonts/Roboto-Regular.ttf", |
||||
"sha1": "56c5c0d38bde4c1f1549dda43db37b09c608aad3" |
||||
}, |
||||
{ |
||||
"path": "src/assets/resources/fonts/fontello.ttf", |
||||
"sha1": "73fc32c22bdcda8f8bea6b28b74a9a45ba721f7d" |
||||
} |
||||
] |
||||
} |
@ -1,10 +1,3 @@
@@ -1,10 +1,3 @@
|
||||
module.exports = { |
||||
assets: ['./src/assets/resources/fonts'], |
||||
dependencies: { |
||||
'react-native-vector-icons': { |
||||
platforms: { |
||||
ios: null, |
||||
}, |
||||
}, |
||||
}, |
||||
}; |
||||
|
Binary file not shown.
Binary file not shown.
@ -0,0 +1,10 @@
@@ -0,0 +1,10 @@
|
||||
export const colors = { |
||||
primaryColor: '#37296B', |
||||
secondaryColor: '#99EDCC', |
||||
textPrimary: '#FFFF', |
||||
secondaryText: '#7669C2', |
||||
red: '#E36588', |
||||
darkPurple: '#2C205C', |
||||
purple: '#A798FF', |
||||
lightPurple2: '#51418D', |
||||
} |
@ -0,0 +1,86 @@
@@ -0,0 +1,86 @@
|
||||
import React, { FC, PropsWithChildren } from 'react' |
||||
import { |
||||
ActivityIndicator, |
||||
DimensionValue, |
||||
StyleSheet, |
||||
TouchableOpacity, |
||||
View, |
||||
ViewStyle, |
||||
} from 'react-native' |
||||
import { Txt, TxtModType } from '../txt' |
||||
import { Font } from '../../typing' |
||||
import { $size } from '../../helpers' |
||||
import { colors } from '../../colors' |
||||
|
||||
type ButtonStyleMod = 'filled' | 'outline' | 'danger' |
||||
|
||||
interface IButtonPrimaryProps { |
||||
mod?: ButtonStyleMod |
||||
style?: ViewStyle |
||||
onPress: () => void |
||||
isLoading?: boolean |
||||
|
||||
mb?: number |
||||
width?: DimensionValue | undefined |
||||
txtMod?: TxtModType |
||||
txtFont?: Font |
||||
txtColor?: string |
||||
disabled?: boolean |
||||
} |
||||
|
||||
export const ButtonPrimary: FC<PropsWithChildren<IButtonPrimaryProps>> = ({ |
||||
children, |
||||
style, |
||||
onPress, |
||||
isLoading, |
||||
|
||||
mb = $size(20), |
||||
width = '100%', |
||||
txtFont = Font.Roboto700, |
||||
txtMod = 'md', |
||||
txtColor = colors.textPrimary, |
||||
disabled = false, |
||||
}) => { |
||||
if (isLoading) { |
||||
return ( |
||||
<View style={style}> |
||||
<ActivityIndicator |
||||
style={[styles.loader, { marginBottom: mb, width }]} |
||||
/> |
||||
</View> |
||||
) |
||||
} |
||||
|
||||
return ( |
||||
<TouchableOpacity |
||||
activeOpacity={0.6} |
||||
disabled={disabled} |
||||
onPress={onPress} |
||||
style={[styles.container, style, { marginBottom: mb, width }]}> |
||||
<Txt |
||||
font={txtFont} |
||||
mod={txtMod} |
||||
style={[styles.txtBtn, { color: txtColor }]}> |
||||
{children} |
||||
</Txt> |
||||
</TouchableOpacity> |
||||
) |
||||
} |
||||
|
||||
const styles = StyleSheet.create({ |
||||
container: { |
||||
backgroundColor: colors.red, |
||||
alignItems: 'center', |
||||
justifyContent: 'center', |
||||
borderRadius: 60, |
||||
height: $size(50), |
||||
}, |
||||
|
||||
txtBtn: { |
||||
color: colors.textPrimary, |
||||
}, |
||||
|
||||
loader: { |
||||
height: $size(56, 54), |
||||
}, |
||||
}) |
@ -0,0 +1,29 @@
@@ -0,0 +1,29 @@
|
||||
import React from 'react' |
||||
import { StyleSheet, TouchableOpacity } from 'react-native' |
||||
import { colors } from '../../colors' |
||||
import { Icon } from '../icon' |
||||
import { $size } from '../../helpers' |
||||
|
||||
interface IProps { |
||||
iconName: string |
||||
onPress: () => void |
||||
} |
||||
|
||||
export const RedButton: React.FC<IProps> = ({ iconName, onPress }) => { |
||||
return ( |
||||
<TouchableOpacity style={styles.container} onPress={onPress}> |
||||
<Icon name={iconName} size={$size(24)} /> |
||||
</TouchableOpacity> |
||||
) |
||||
} |
||||
|
||||
const styles = StyleSheet.create({ |
||||
container: { |
||||
borderRadius: 60, |
||||
backgroundColor: colors.red, |
||||
height: $size(50), |
||||
width: $size(101), |
||||
justifyContent: 'center', |
||||
alignItems: 'center', |
||||
}, |
||||
}) |
@ -0,0 +1,3 @@
@@ -0,0 +1,3 @@
|
||||
export * from './go-back.component' |
||||
export * from './button-primary.component' |
||||
export * from './button-with-icon' |
@ -0,0 +1,32 @@
@@ -0,0 +1,32 @@
|
||||
import React, { FC } from 'react' |
||||
import { createIconSetFromFontello } from 'react-native-vector-icons' |
||||
import { ColorValue, TouchableOpacity, ViewStyle } from 'react-native' |
||||
import { fontelloConfig } from '../../config' |
||||
|
||||
const FontelloIcon = createIconSetFromFontello(fontelloConfig) |
||||
|
||||
interface IProps { |
||||
name: string |
||||
size: number |
||||
color?: ColorValue |
||||
style?: any |
||||
onPress?: () => void |
||||
buttonStyle?: ViewStyle | ViewStyle[] |
||||
disabled?: boolean |
||||
} |
||||
|
||||
export const Icon: FC<IProps> = props => { |
||||
if (props.onPress) { |
||||
return ( |
||||
<TouchableOpacity |
||||
disabled={props.disabled} |
||||
onPress={props.onPress} |
||||
style={props.buttonStyle} |
||||
activeOpacity={0.3}> |
||||
<FontelloIcon {...props} onPress={() => null} /> |
||||
</TouchableOpacity> |
||||
) |
||||
} |
||||
|
||||
return <FontelloIcon {...props} /> |
||||
} |
@ -0,0 +1 @@
@@ -0,0 +1 @@
|
||||
export * from './icon.component' |
@ -1,4 +1,7 @@
@@ -1,4 +1,7 @@
|
||||
export * from './Icon'; |
||||
export * from './icon'; |
||||
export * from './buttons'; |
||||
export * from './header'; |
||||
export * from './layout'; |
||||
export * from './modal'; |
||||
export * from './txt'; |
||||
|
@ -0,0 +1,2 @@
@@ -0,0 +1,2 @@
|
||||
export * from './screen-layout-content.component' |
||||
export * from './screen-layout.component' |
@ -0,0 +1,93 @@
@@ -0,0 +1,93 @@
|
||||
import React, { FC } from 'react' |
||||
import { |
||||
KeyboardAvoidingView, |
||||
Platform, |
||||
StyleSheet, |
||||
View, |
||||
ViewStyle, |
||||
} from 'react-native' |
||||
import { KeyboardAwareScrollView } from 'react-native-keyboard-aware-scroll-view' |
||||
import { $size } from '~modules/common/helpers' |
||||
|
||||
interface ScreenLayoutContentProps { |
||||
children: JSX.Element | JSX.Element[] |
||||
header?: () => React.ReactNode |
||||
horizontalPadding?: number |
||||
needScroll?: Boolean |
||||
scrollStyle?: ViewStyle |
||||
viewStyle?: ViewStyle |
||||
leftBottomRound?: boolean |
||||
background?: string |
||||
keyboardSpacerOn?: boolean |
||||
scrollRef?: React.MutableRefObject<KeyboardAwareScrollView> |
||||
extraHeight?: number |
||||
pointerEvents?: 'box-none' | 'none' | 'box-only' | 'auto' | undefined |
||||
topPadding?: number |
||||
} |
||||
|
||||
export const ScreenLayoutContent: FC<ScreenLayoutContentProps> = ({ |
||||
children, |
||||
header, |
||||
topPadding = 16, |
||||
horizontalPadding = 16, |
||||
needScroll, |
||||
scrollStyle, |
||||
viewStyle, |
||||
scrollRef, |
||||
extraHeight = 160, |
||||
pointerEvents = undefined, |
||||
}) => { |
||||
if (needScroll) { |
||||
return ( |
||||
<View style={styles.view}> |
||||
{header && header()} |
||||
<KeyboardAwareScrollView |
||||
ref={scrollRef} |
||||
enableAutomaticScroll={true} |
||||
keyboardShouldPersistTaps="handled" |
||||
extraHeight={extraHeight} |
||||
scrollEventThrottle={20} |
||||
pointerEvents={pointerEvents} |
||||
contentContainerStyle={[ |
||||
{ |
||||
paddingTop: 16, |
||||
paddingHorizontal: $size(horizontalPadding), |
||||
}, |
||||
scrollStyle, |
||||
]}> |
||||
{children} |
||||
</KeyboardAwareScrollView> |
||||
</View> |
||||
) |
||||
} else { |
||||
return ( |
||||
<KeyboardAvoidingView |
||||
style={{ flex: 1 }} |
||||
behavior="height" |
||||
keyboardVerticalOffset={Platform.OS === 'android' ? 50 : 70}> |
||||
<View style={styles.view}> |
||||
{header && header()} |
||||
<View |
||||
style={[ |
||||
viewStyle, |
||||
{ |
||||
paddingTop: topPadding, |
||||
paddingHorizontal: $size(horizontalPadding), |
||||
flex: 1, |
||||
}, |
||||
]} |
||||
pointerEvents={pointerEvents}> |
||||
{children} |
||||
</View> |
||||
</View> |
||||
</KeyboardAvoidingView> |
||||
) |
||||
} |
||||
} |
||||
|
||||
const styles = StyleSheet.create({ |
||||
view: { |
||||
flex: 1, |
||||
zIndex: 2, |
||||
}, |
||||
}) |
@ -0,0 +1,98 @@
@@ -0,0 +1,98 @@
|
||||
import React, { ReactElement } from 'react' |
||||
import { |
||||
ColorValue, |
||||
StatusBar, |
||||
StyleSheet, |
||||
View, |
||||
ViewStyle, |
||||
} from 'react-native' |
||||
import { KeyboardAwareScrollView } from 'react-native-keyboard-aware-scroll-view' |
||||
import { useSafeAreaInsets } from 'react-native-safe-area-context' |
||||
import { ScreenLayoutContent } from './screen-layout-content.component' |
||||
import _ from 'lodash' |
||||
import { gcService } from '../../tools' |
||||
import { colors } from '../../colors' |
||||
|
||||
export interface ScreenLayoutProps { |
||||
children: JSX.Element | JSX.Element[] |
||||
needScroll?: Boolean |
||||
scrollStyle?: ViewStyle |
||||
viewStyle?: ViewStyle |
||||
leftBottomRound?: boolean |
||||
statusBarBg?: ColorValue |
||||
background?: string |
||||
horizontalPadding?: number |
||||
keyboardSpacerOn?: boolean |
||||
headerComponent?: ReactElement |
||||
scrollRef?: React.MutableRefObject<KeyboardAwareScrollView> |
||||
extraHeight?: number |
||||
footer?: () => ReactElement |
||||
pointerEvents?: 'box-none' | 'none' | 'box-only' | 'auto' | undefined |
||||
bottomSafeArea?: boolean |
||||
bottomBarSafeArea?: boolean |
||||
topPadding?: number |
||||
} |
||||
type StatusBarStyleConfig = { |
||||
light: 'dark-content' |
||||
dark: 'light-content' |
||||
} |
||||
const statusBarStyleConfig: StatusBarStyleConfig = { |
||||
light: 'dark-content', |
||||
dark: 'light-content', |
||||
} |
||||
|
||||
export const ScreenLayout = ({ |
||||
statusBarBg, |
||||
background, |
||||
bottomSafeArea = false, |
||||
topPadding, |
||||
bottomBarSafeArea = false, |
||||
...props |
||||
}: ScreenLayoutProps) => { |
||||
const insets = useSafeAreaInsets() |
||||
|
||||
const heightTopDefault = _.defaultTo( |
||||
Math.max(insets.top, gcService.get('insetsTop')), |
||||
50, |
||||
) |
||||
|
||||
return ( |
||||
<> |
||||
<View |
||||
style={{ |
||||
backgroundColor: colors.primaryColor, |
||||
height: heightTopDefault, |
||||
width: '100%', |
||||
}}> |
||||
<StatusBar |
||||
barStyle="light-content" |
||||
backgroundColor={colors.primaryColor} |
||||
/>{' '} |
||||
</View> |
||||
<View |
||||
style={[ |
||||
styles.container, |
||||
{ |
||||
backgroundColor: _.defaultTo( |
||||
background, |
||||
colors.primaryColor, |
||||
), |
||||
}, |
||||
]}> |
||||
<ScreenLayoutContent |
||||
{...props} |
||||
header={() => props.headerComponent} |
||||
topPadding={topPadding} |
||||
/> |
||||
|
||||
{props.footer ? props.footer() : null} |
||||
</View> |
||||
</> |
||||
) |
||||
} |
||||
|
||||
const styles = StyleSheet.create({ |
||||
container: { |
||||
flex: 1, |
||||
}, |
||||
}) |
@ -0,0 +1,13 @@
@@ -0,0 +1,13 @@
|
||||
import React from 'react' |
||||
import { StyleSheet, View } from 'react-native' |
||||
|
||||
export const ModalBodyAtom = ({ children }: { children?: React.ReactNode }) => ( |
||||
<View style={styles.body}>{children}</View> |
||||
) |
||||
|
||||
const styles = StyleSheet.create({ |
||||
body: { |
||||
justifyContent: 'center', |
||||
minHeight: 100, |
||||
}, |
||||
}) |
@ -0,0 +1,20 @@
@@ -0,0 +1,20 @@
|
||||
import React from 'react' |
||||
import { StyleSheet, View, ViewStyle } from 'react-native' |
||||
import { colors } from '../../../colors' |
||||
|
||||
export const ModalContainerAtom = ({ |
||||
children, |
||||
style, |
||||
}: { |
||||
children: React.ReactNode |
||||
style?: ViewStyle |
||||
}) => { |
||||
return <View style={[styles.container, style]}>{children}</View> |
||||
} |
||||
|
||||
const styles = StyleSheet.create({ |
||||
container: { |
||||
backgroundColor: colors.darkPurple, |
||||
borderRadius: 4, |
||||
}, |
||||
}) |
@ -0,0 +1,16 @@
@@ -0,0 +1,16 @@
|
||||
import React from 'react' |
||||
import { StyleSheet, View } from 'react-native' |
||||
|
||||
export const ModalFooterAtom = ({ |
||||
children, |
||||
}: { |
||||
children?: React.ReactNode |
||||
}) => <View style={styles.footer}>{children}</View> |
||||
|
||||
const styles = StyleSheet.create({ |
||||
footer: { |
||||
justifyContent: 'center', |
||||
alignItems: 'center', |
||||
flexDirection: 'row', |
||||
}, |
||||
}) |
@ -0,0 +1,14 @@
@@ -0,0 +1,14 @@
|
||||
import React from 'react' |
||||
import { StyleSheet, View } from 'react-native' |
||||
|
||||
export const ModalHeaderAtom = ({ |
||||
children, |
||||
}: { |
||||
children?: React.ReactNode |
||||
}) => <View style={styles.header}>{children}</View> |
||||
|
||||
const styles = StyleSheet.create({ |
||||
header: { |
||||
justifyContent: 'center', |
||||
}, |
||||
}) |
@ -0,0 +1,4 @@
@@ -0,0 +1,4 @@
|
||||
export * from './header-modal.atom' |
||||
export * from './body-modal.atom' |
||||
export * from './footer-modal.atom' |
||||
export * from './container-modal.atom' |
@ -0,0 +1 @@
@@ -0,0 +1 @@
|
||||
export * from './modal' |
@ -0,0 +1,61 @@
@@ -0,0 +1,61 @@
|
||||
import React from 'react' |
||||
import RNModal, { ModalProps } from 'react-native-modal' |
||||
import { getDimensions } from '../../helpers' |
||||
import { |
||||
ModalBodyAtom, |
||||
ModalContainerAtom, |
||||
ModalFooterAtom, |
||||
ModalHeaderAtom, |
||||
} from './atoms' |
||||
|
||||
interface Props extends Partial<ModalProps> { |
||||
isVisible: boolean |
||||
children: React.ReactNode |
||||
onClose: () => void |
||||
positionModal?: |
||||
| 'center' |
||||
| 'flex-start' |
||||
| 'flex-end' |
||||
| 'space-between' |
||||
| 'space-around' |
||||
| 'space-evenly' |
||||
} |
||||
|
||||
const dimensions = getDimensions() |
||||
|
||||
export const ModalComponent = ({ |
||||
isVisible = false, |
||||
children, |
||||
positionModal = 'center', |
||||
onClose, |
||||
...props |
||||
}: Props) => { |
||||
return ( |
||||
<RNModal |
||||
deviceHeight={dimensions.height} |
||||
deviceWidth={dimensions.width} |
||||
statusBarTranslucent |
||||
isVisible={isVisible} |
||||
animationInTiming={600} |
||||
animationOutTiming={300} |
||||
backdropTransitionInTiming={300} |
||||
backdropTransitionOutTiming={300} |
||||
backdropOpacity={0.7} |
||||
hasBackdrop={true} |
||||
onBackdropPress={onClose} |
||||
style={{ |
||||
marginHorizontal: 0, |
||||
marginVertical: 0, |
||||
justifyContent: positionModal, |
||||
paddingHorizontal: 16, |
||||
}} |
||||
{...props}> |
||||
{children} |
||||
</RNModal> |
||||
) |
||||
} |
||||
|
||||
ModalComponent.Header = ModalHeaderAtom |
||||
ModalComponent.Container = ModalContainerAtom |
||||
ModalComponent.Body = ModalBodyAtom |
||||
ModalComponent.Footer = ModalFooterAtom |
@ -0,0 +1 @@
@@ -0,0 +1 @@
|
||||
export * from './txt.component' |
@ -0,0 +1,67 @@
@@ -0,0 +1,67 @@
|
||||
import React, { FC } from 'react' |
||||
import { Text, TextStyle, TextProps } from 'react-native' |
||||
import { $size } from '../../helpers' |
||||
import { Font } from '../../typing' |
||||
import { colors } from '../../colors' |
||||
|
||||
export type TxtModType = 's' | 'es' | 'sm' | 'md' | 'lg' | 'xl' | 'xxl' |
||||
|
||||
const sizes = { |
||||
s: $size(10, 8), |
||||
es: $size(12, 10), |
||||
sm: $size(14, 12), |
||||
md: $size(16, 14), |
||||
lg: $size(18, 16), |
||||
xl: $size(20, 18), |
||||
xxl: $size(24, 22), |
||||
} |
||||
|
||||
const lineHeights = { |
||||
s: $size(12, 10), |
||||
es: $size(14, 12), |
||||
sm: $size(16, 14), |
||||
md: $size(18, 16), |
||||
lg: $size(20, 18), |
||||
xl: $size(22, 20), |
||||
xxl: $size(26, 24), |
||||
} |
||||
|
||||
export interface TxtProps extends TextProps { |
||||
children: any |
||||
style?: TextStyle | TextStyle[] |
||||
color?: string |
||||
mod?: keyof typeof sizes |
||||
hide?: Boolean |
||||
numberOfLines?: number |
||||
font?: Font |
||||
} |
||||
|
||||
export const Txt: FC<TxtProps> = ({ |
||||
children, |
||||
style = {}, |
||||
mod = 'es', |
||||
hide, |
||||
color, |
||||
font = Font.Roboto400, |
||||
...props |
||||
}) => { |
||||
if (hide === true) { |
||||
return null |
||||
} |
||||
|
||||
return ( |
||||
<Text |
||||
{...props} |
||||
style={[ |
||||
{ |
||||
color: color || colors.textPrimary, |
||||
fontFamily: font, |
||||
fontSize: sizes[mod], |
||||
lineHeight: lineHeights[mod], |
||||
}, |
||||
style, |
||||
]}> |
||||
{children} |
||||
</Text> |
||||
) |
||||
} |
@ -0,0 +1,26 @@
@@ -0,0 +1,26 @@
|
||||
import { Events } from 'jet-tools' |
||||
|
||||
export type AppEvents = { |
||||
event: { |
||||
data: string |
||||
} |
||||
|
||||
alert: { |
||||
title: string |
||||
subtitle: string |
||||
btnText?: string |
||||
onClose?: () => void |
||||
} |
||||
|
||||
confirm: { |
||||
cancelBtnText?: string |
||||
confirmBtnText?: string |
||||
title: string |
||||
subtitle: string |
||||
buttons: { |
||||
onPress: () => void |
||||
}[] |
||||
} |
||||
} |
||||
|
||||
export const appEvents = new Events<AppEvents>() |
@ -0,0 +1 @@
@@ -0,0 +1 @@
|
||||
export * from './app-events'; |
@ -0,0 +1,6 @@
@@ -0,0 +1,6 @@
|
||||
import { Dimensions } from 'react-native' |
||||
|
||||
export const getDimensions = () => { |
||||
const { height, width } = Dimensions.get('screen') |
||||
return { height, width } |
||||
} |
@ -0,0 +1,4 @@
@@ -0,0 +1,4 @@
|
||||
export * from './helper-height-view'; |
||||
export * from './dimensions.helper'; |
||||
export * from './style.helper'; |
||||
|
@ -0,0 +1,18 @@
@@ -0,0 +1,18 @@
|
||||
import { Dimensions } from 'react-native' |
||||
|
||||
const screenHeight = Dimensions.get('screen').height |
||||
const BASIC_HEIGHT = 812 |
||||
const coff = (screenHeight / BASIC_HEIGHT) * 100 |
||||
|
||||
const maxSize = (size: number) => size + (size / 100) * 20 |
||||
const prepareRes = (res: number, size: number) => Math.min(res, maxSize(size)) |
||||
|
||||
export const $size = (size: number, min?: number) => { |
||||
const res = size * (coff / 100) |
||||
|
||||
if (min) { |
||||
return Math.max(prepareRes(res, size), min) |
||||
} else { |
||||
return prepareRes(res, size) |
||||
} |
||||
} |
@ -0,0 +1,21 @@
@@ -0,0 +1,21 @@
|
||||
import { useEffect } from 'react' |
||||
import { AppEvents, appEvents } from '../events' |
||||
|
||||
export const useEventsListener = <T extends keyof AppEvents>( |
||||
name: T, |
||||
action: (data: AppEvents[T]) => void, |
||||
dependencies: any[] = [], |
||||
) => { |
||||
useEffect(() => { |
||||
const fn = (data: AppEvents[T]) => { |
||||
try { |
||||
action(data) |
||||
} catch (e) { |
||||
console.log('error event', e) |
||||
} |
||||
} |
||||
appEvents.on(name, fn) |
||||
|
||||
return () => appEvents.off(name, fn) |
||||
}, dependencies) |
||||
} |
@ -0,0 +1,9 @@
@@ -0,0 +1,9 @@
|
||||
export * from './colors' |
||||
export * from './components' |
||||
export * from './config' |
||||
export * from './typing' |
||||
export * from './helpers' |
||||
export * from './hooks' |
||||
export * from './events' |
||||
export * from './tools' |
||||
export * from './widgets' |
@ -0,0 +1,17 @@
@@ -0,0 +1,17 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */ |
||||
const globalData: any = { |
||||
store: null, |
||||
internetConnect: true, |
||||
scrollY: 0, |
||||
} |
||||
|
||||
const set = (key: any, value: any) => { |
||||
globalData[key] = value |
||||
} |
||||
|
||||
const get = (key: string) => globalData[key] |
||||
|
||||
export const gcService = { |
||||
set, |
||||
get, |
||||
} |
@ -0,0 +1 @@
@@ -0,0 +1 @@
|
||||
export * from './global-container.tool' |
@ -0,0 +1,4 @@
@@ -0,0 +1,4 @@
|
||||
export enum Font { |
||||
Roboto400 = 'Roboto-Regular', |
||||
Roboto700 = 'Roboto-Bold', |
||||
} |
@ -0,0 +1,2 @@
@@ -0,0 +1,2 @@
|
||||
export * from './route-keys' |
||||
export * from './fonts.enum' |
@ -0,0 +1,2 @@
@@ -0,0 +1,2 @@
|
||||
export * from './interfaces'; |
||||
export * from './enums'; |
@ -0,0 +1,203 @@
@@ -0,0 +1,203 @@
|
||||
import React, { FC, useEffect, useRef, useState } from 'react' |
||||
import { StyleSheet, TouchableOpacity, View, ViewStyle } from 'react-native' |
||||
import _ from 'lodash' |
||||
import { useEventsListener, useTheme } from '~modules/common/hooks' |
||||
import { Theme } from '~modules/common/themes/interfaces' |
||||
import { ModalComponent, Txt, ButtonPrimary } from '~modules/common/components' |
||||
import { AppEvents, appEvents } from '~modules/common/events' |
||||
import { $size } from '~modules/common/helpers' |
||||
import { Font } from '~modules/common/typing' |
||||
|
||||
interface IContent { |
||||
title: string |
||||
subtitle: string |
||||
confirmBtnText?: string |
||||
cancelBtnText?: string |
||||
buttons: IContentButton[] |
||||
} |
||||
|
||||
interface IContentButton { |
||||
btnText?: string |
||||
btnStyle?: ViewStyle |
||||
txtColor?: string |
||||
onPress: () => void |
||||
} |
||||
|
||||
interface Params { |
||||
buttons: (() => void)[] |
||||
} |
||||
|
||||
const contentInitial: IContent = { |
||||
title: '', |
||||
subtitle: '', |
||||
cancelBtnText: 'Yes', |
||||
confirmBtnText: 'No', |
||||
buttons: [ |
||||
{ |
||||
onPress: _.noop, |
||||
}, |
||||
], |
||||
} |
||||
|
||||
export const AlertConfirmWidget: FC = () => { |
||||
const { styles, theme } = useTheme(createStyles) |
||||
const settingsConfirmRef = useRef<Params>() |
||||
const [content, setContent] = useState<IContent>(contentInitial) |
||||
const [isVisible, setVisible] = useState(false) |
||||
|
||||
useEventsListener( |
||||
'confirm', |
||||
data => { |
||||
settingsConfirmRef.current = { |
||||
buttons: data?.buttons.map(el => el.onPress), |
||||
} |
||||
setContent({ |
||||
title: data?.title, |
||||
subtitle: data?.subtitle, |
||||
buttons: data?.buttons, |
||||
confirmBtnText: _.defaultTo(data?.confirmBtnText, 'Yes'), |
||||
cancelBtnText: _.defaultTo(data?.cancelBtnText, 'No'), |
||||
}) |
||||
}, |
||||
[setContent, settingsConfirmRef.current, content], |
||||
) |
||||
|
||||
useEffect(() => { |
||||
if (content?.title) { |
||||
setVisible(true) |
||||
} |
||||
}, [content, settingsConfirmRef.current]) |
||||
|
||||
const close = () => { |
||||
setVisible(false) |
||||
} |
||||
const onConfirm = (index: number) => { |
||||
settingsConfirmRef.current.buttons[index]() |
||||
close() |
||||
} |
||||
|
||||
const buttonsConfig = (buttonsOnPress: { onPress: () => void }[]) => { |
||||
return [ |
||||
{ |
||||
btnText: content.cancelBtnText, |
||||
btnStyle: { ...styles.button, ...styles.borderRight }, |
||||
txtColor: theme.$primary, |
||||
onPress: buttonsOnPress[0].onPress, |
||||
}, |
||||
{ |
||||
btnText: content.confirmBtnText, |
||||
onPress: buttonsOnPress[1]?.onPress, |
||||
txtColor: theme.$primary, |
||||
btnStyle: styles.button, |
||||
}, |
||||
] |
||||
} |
||||
|
||||
const buttons = buttonsConfig(content.buttons) |
||||
|
||||
return ( |
||||
<ModalComponent |
||||
coverScreen={false} |
||||
useNativeDriverForBackdrop={true} |
||||
backdropTransitionOutTiming={400} |
||||
hideModalContentWhileAnimating={true} |
||||
backdropColor={'rgba(0,0,0,0.8)'} |
||||
animationIn="pulse" |
||||
isVisible={Boolean(isVisible)} |
||||
onBackdropPress={close} |
||||
onClose={close} |
||||
style={{ alignItems: 'center', justifyContent: 'center' }}> |
||||
<ModalComponent.Container style={styles.container}> |
||||
<View style={styles.content}> |
||||
<Txt |
||||
mod="xl" |
||||
font={Font.SFProDisplay500} |
||||
style={styles.title}> |
||||
{content?.title} |
||||
</Txt> |
||||
|
||||
<Txt |
||||
mod="md" |
||||
style={styles.subtitle} |
||||
font={Font.SFProDisplay500}> |
||||
{content?.subtitle} |
||||
</Txt> |
||||
</View> |
||||
<View style={styles.buttonsContainer}> |
||||
{buttons.map((el: IContentButton, index: number) => ( |
||||
<TouchableOpacity |
||||
key={index} |
||||
style={el.btnStyle} |
||||
onPress={() => onConfirm(index)}> |
||||
<Txt |
||||
mod="md" |
||||
font={Font.SFProDisplay500} |
||||
color={el.txtColor}> |
||||
{el.btnText} |
||||
</Txt> |
||||
</TouchableOpacity> |
||||
))} |
||||
</View> |
||||
</ModalComponent.Container> |
||||
</ModalComponent> |
||||
) |
||||
} |
||||
|
||||
const createStyles = (theme: Theme) => |
||||
StyleSheet.create({ |
||||
container: { |
||||
borderRadius: 15, |
||||
overflow: 'hidden', |
||||
backgroundColor: '#262626', |
||||
justifyContent: 'space-between', |
||||
width: $size(270, 260), |
||||
}, |
||||
|
||||
content: { |
||||
flexDirection: 'column', |
||||
justifyContent: 'center', |
||||
alignItems: 'center', |
||||
gap: 8, |
||||
paddingHorizontal: $size(16), |
||||
paddingVertical: $size(20), |
||||
borderBottomWidth: 1, |
||||
borderBottomColor: 'rgba(255, 255, 255, 0.30)', |
||||
}, |
||||
|
||||
title: { |
||||
textAlign: 'center', |
||||
width: '80%', |
||||
color: theme.$textPrimary, |
||||
}, |
||||
|
||||
subtitle: { |
||||
textAlign: 'center', |
||||
width: '80%', |
||||
color: 'rgba(255, 255, 255, 0.50)', |
||||
}, |
||||
|
||||
buttonsContainer: { |
||||
flexDirection: 'row', |
||||
alignItems: 'center', |
||||
justifyContent: 'space-between', |
||||
}, |
||||
|
||||
button: { |
||||
width: '49.9%', |
||||
paddingHorizontal: $size(16), |
||||
paddingVertical: $size(12), |
||||
alignItems: 'center', |
||||
justifyContent: 'center', |
||||
}, |
||||
borderRight: { |
||||
borderRightColor: 'rgba(255, 255, 255, 0.70)', |
||||
borderRightWidth: 1, |
||||
}, |
||||
}) |
||||
|
||||
export const Confirm = { |
||||
Component: AlertConfirmWidget, |
||||
Show: (params: AppEvents['confirm']) => { |
||||
appEvents.emit('confirm', params) |
||||
}, |
||||
} |
@ -0,0 +1 @@
@@ -0,0 +1 @@
|
||||
export * from './alert-confirm-widget.component' |
@ -0,0 +1,139 @@
@@ -0,0 +1,139 @@
|
||||
import React, { useEffect, useRef, useState } from 'react' |
||||
import { StyleSheet, View } from 'react-native' |
||||
import _ from 'lodash' |
||||
import { useEventsListener } from '../../hooks/use-events-listener.hook' |
||||
import { ButtonPrimary, ModalComponent, Txt } from '../../components' |
||||
import { $size } from '../../helpers' |
||||
import { colors } from '../../colors' |
||||
import { AppEvents, appEvents } from '../../events' |
||||
|
||||
interface IContent { |
||||
title: string |
||||
subtitle: string |
||||
btnText: string |
||||
} |
||||
|
||||
const contentInitial: IContent = { |
||||
title: '', |
||||
subtitle: '', |
||||
btnText: 'OK', |
||||
} |
||||
|
||||
export const AlertWidget = () => { |
||||
const settingsRef = useRef({ |
||||
onClose: _.noop, |
||||
}) |
||||
const [content, setContent] = useState<IContent>(contentInitial) |
||||
const [isVisible, setVisible] = useState(false) |
||||
|
||||
useEventsListener( |
||||
'alert', |
||||
data => { |
||||
settingsRef.current = { |
||||
onClose: () => data?.onClose, |
||||
} |
||||
setContent({ |
||||
title: data?.title, |
||||
subtitle: data?.subtitle, |
||||
btnText: _.defaultTo(data.btnText, contentInitial.btnText), |
||||
}) |
||||
}, |
||||
[setContent, settingsRef.current, content], |
||||
) |
||||
|
||||
useEffect(() => { |
||||
if (content?.title) { |
||||
setVisible(true) |
||||
} |
||||
}, [content, settingsRef.current]) |
||||
|
||||
const close = () => { |
||||
setVisible(false) |
||||
} |
||||
|
||||
const onConfirm = () => { |
||||
if (typeof settingsRef.current.onClose === 'function') { |
||||
settingsRef.current.onClose() |
||||
} |
||||
|
||||
close() |
||||
} |
||||
|
||||
return ( |
||||
<ModalComponent |
||||
coverScreen={false} |
||||
useNativeDriverForBackdrop={true} |
||||
backdropTransitionOutTiming={400} |
||||
hideModalContentWhileAnimating={true} |
||||
backdropColor={'#787878'} |
||||
animationIn="pulse" |
||||
isVisible={Boolean(isVisible)} |
||||
onBackdropPress={close} |
||||
onClose={close}> |
||||
<ModalComponent.Container style={styles.container}> |
||||
<View style={styles.content}> |
||||
<Txt style={styles.title}>{content?.title}</Txt> |
||||
|
||||
<Txt mod="md" style={styles.subtitle}> |
||||
{content?.subtitle} |
||||
</Txt> |
||||
</View> |
||||
|
||||
<View style={styles.button}> |
||||
<ButtonPrimary onPress={onConfirm} mb={0}> |
||||
{content.btnText} |
||||
</ButtonPrimary> |
||||
</View> |
||||
</ModalComponent.Container> |
||||
</ModalComponent> |
||||
) |
||||
} |
||||
|
||||
const styles = StyleSheet.create({ |
||||
container: { |
||||
borderRadius: 16, |
||||
paddingHorizontal: 16, |
||||
paddingVertical: $size(80), |
||||
overflow: 'hidden', |
||||
backgroundColor: colors.darkPurple, |
||||
justifyContent: 'space-between', |
||||
height: $size(338), |
||||
}, |
||||
|
||||
content: { |
||||
justifyContent: 'center', |
||||
alignItems: 'center', |
||||
gap: 8, |
||||
}, |
||||
|
||||
title: { |
||||
textAlign: 'center', |
||||
width: '80%', |
||||
color: colors.textPrimary, |
||||
fontWeight: '600', |
||||
fontSize: 22, |
||||
lineHeight: 24, |
||||
}, |
||||
|
||||
subtitle: { |
||||
textAlign: 'center', |
||||
width: '80%', |
||||
color: colors.textPrimary, |
||||
fontWeight: '600', |
||||
}, |
||||
|
||||
button: { |
||||
alignSelf: 'center', |
||||
width: '100%', |
||||
}, |
||||
btnTxt: {}, |
||||
close: {}, |
||||
bottomBtn: {}, |
||||
}) |
||||
|
||||
export const Alert = { |
||||
Component: AlertWidget, |
||||
Show: (params: AppEvents['alert']) => { |
||||
appEvents.emit('alert', params) |
||||
}, |
||||
} |
@ -0,0 +1 @@
@@ -0,0 +1 @@
|
||||
export * from './alert-widget.component' |
@ -0,0 +1,54 @@
@@ -0,0 +1,54 @@
|
||||
import _ from 'lodash' |
||||
import React, { FC } from 'react' |
||||
import { StyleSheet } from 'react-native' |
||||
import { TouchableOpacity } from 'react-native' |
||||
import { Icon } from '~modules/common' |
||||
import { Txt } from '~modules/common/components/typography' |
||||
import { $size } from '~modules/common/helpers' |
||||
|
||||
interface IProps { |
||||
iconName: string |
||||
onPress: () => void |
||||
label: string |
||||
showBottomBorder: boolean |
||||
} |
||||
export const ActionSheetButtonAtom: FC<IProps> = ({ |
||||
iconName, |
||||
onPress, |
||||
showBottomBorder, |
||||
label, |
||||
}) => { |
||||
const renderIcon = () => { |
||||
if (!iconName) { |
||||
return null |
||||
} |
||||
return <Icon name={iconName} size={$size(28)} color={'#007AFF'} /> |
||||
} |
||||
|
||||
return ( |
||||
<TouchableOpacity |
||||
onPress={onPress} |
||||
style={[ |
||||
styles.container, |
||||
{ borderBottomWidth: showBottomBorder ? 1 : 0 }, |
||||
]}> |
||||
{renderIcon()} |
||||
<Txt style={styles.label} color="#FFFFFF" mod="md"> |
||||
{label} |
||||
</Txt> |
||||
</TouchableOpacity> |
||||
) |
||||
} |
||||
|
||||
const styles = StyleSheet.create({ |
||||
container: { |
||||
flexDirection: 'row', |
||||
alignItems: 'center', |
||||
backgroundColor: '#161616', |
||||
padding: $size(10), |
||||
borderBottomColor: 'rgba(212, 212, 212, 0.1)', |
||||
}, |
||||
label: { |
||||
marginLeft: $size(10), |
||||
}, |
||||
}) |
@ -0,0 +1,77 @@
@@ -0,0 +1,77 @@
|
||||
import React, { FC } from 'react' |
||||
import { StyleSheet, Text, TouchableOpacity, View } from 'react-native' |
||||
|
||||
import ActionSheet, { SheetProps } from 'react-native-actions-sheet' |
||||
import { Txt } from '~modules/common/components/typography' |
||||
import { SheetManager } from 'react-native-actions-sheet' |
||||
import { $size } from '~modules/common/helpers' |
||||
import { ActionSheetButtonAtom } from './action-sheet-button.atom' |
||||
import _ from 'lodash' |
||||
|
||||
interface IPayloadSheet { |
||||
iconName?: string |
||||
onPress: () => void |
||||
label: string |
||||
} |
||||
|
||||
export const BottomSheetView: FC<SheetProps<IPayloadSheet[]>> = ({ |
||||
sheetId, |
||||
payload, |
||||
}) => { |
||||
const close = () => SheetManager.hide('bottom-sheet') |
||||
|
||||
return ( |
||||
<ActionSheet |
||||
id={sheetId} |
||||
containerStyle={styles.container} |
||||
defaultOverlayOpacity={0.8} |
||||
statusBarTranslucent={true} |
||||
animated> |
||||
<View style={styles.actionContainer}> |
||||
{payload.map((it, index) => { |
||||
const showBottomBorder = payload.length - 1 !== index |
||||
return ( |
||||
<ActionSheetButtonAtom |
||||
key={it.label} |
||||
onPress={it.onPress} |
||||
iconName={it.iconName} |
||||
label={it.label} |
||||
showBottomBorder={showBottomBorder} |
||||
/> |
||||
) |
||||
})} |
||||
</View> |
||||
<TouchableOpacity style={styles.button} onPress={close}> |
||||
<Txt mod="md" style={styles.txtContent}> |
||||
Cancel |
||||
</Txt> |
||||
</TouchableOpacity> |
||||
</ActionSheet> |
||||
) |
||||
} |
||||
|
||||
const styles = StyleSheet.create({ |
||||
button: { |
||||
paddingVertical: $size(18), |
||||
alignItems: 'center', |
||||
justifyContent: 'center', |
||||
backgroundColor: '#161616', |
||||
borderRadius: 16, |
||||
}, |
||||
container: { |
||||
backgroundColor: 'rgba(0,0,0,0.1)', |
||||
paddingHorizontal: $size(12), |
||||
paddingBottom: $size(35), |
||||
}, |
||||
txtContent: { |
||||
color: '#FFFFFF', |
||||
fontWeight: '500', |
||||
}, |
||||
actionContainer: { |
||||
borderRadius: 16, |
||||
padding: $size(10), |
||||
backgroundColor: '#161616', |
||||
flexDirection: 'column', |
||||
marginBottom: $size(16), |
||||
}, |
||||
}) |
@ -0,0 +1 @@
@@ -0,0 +1 @@
|
||||
export * from './bottom-sheet-view.atom' |
@ -0,0 +1,6 @@
@@ -0,0 +1,6 @@
|
||||
import { registerSheet } from 'react-native-actions-sheet' |
||||
import { BottomSheetView } from './atoms' |
||||
|
||||
registerSheet('bottom-sheet', BottomSheetView) |
||||
|
||||
export {} |
@ -0,0 +1,2 @@
@@ -0,0 +1,2 @@
|
||||
export * from './alert' |
||||
export * from './alert-confirm' |
@ -1,11 +0,0 @@
@@ -1,11 +0,0 @@
|
||||
export const colors = { |
||||
primaryColor: '#37296B', |
||||
secondaryColor: '#99EDCC', |
||||
prymaryText: '#FFFF', |
||||
secondaryText: '#7669C2', |
||||
prymaryBtn: '#E36588', |
||||
darkPurple: '#2C205C', |
||||
purple: '#A798FF', |
||||
darkPurple2: '#51418D', |
||||
white: '#FFFFFF', |
||||
}; |
@ -1,14 +0,0 @@
@@ -1,14 +0,0 @@
|
||||
import React, {FC} from 'react'; |
||||
import {ColorValue, Text, TouchableOpacity, ViewStyle} from 'react-native'; |
||||
import {createIconSetFromFontello} from 'react-native-vector-icons'; |
||||
import {fontelloConfig} from '../../config/'; |
||||
const Svg = createIconSetFromFontello(fontelloConfig); |
||||
interface IProps { |
||||
name: string; |
||||
size: number; |
||||
color?: ColorValue; |
||||
} |
||||
|
||||
export const Icon: FC<IProps> = props => { |
||||
return <Svg name={props.name} size={props.size} color={props.color} />; |
||||
}; |
@ -1 +0,0 @@
@@ -1 +0,0 @@
|
||||
export * from './Icon.component'; |
@ -1,2 +0,0 @@
@@ -1,2 +0,0 @@
|
||||
export * from './go-back.component'; |
||||
export * from './primary-btn.component'; |
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue