tooot/src/components/Button.tsx

189 lines
4.1 KiB
TypeScript
Raw Normal View History

import Icon from '@components/Icon'
import { StyleConstants } from '@utils/styles/constants'
import layoutAnimation from '@utils/styles/layoutAnimation'
import { useTheme } from '@utils/styles/ThemeManager'
import React, { useEffect, useMemo, useRef } from 'react'
2020-12-26 23:05:17 +01:00
import {
Pressable,
StyleProp,
StyleSheet,
Text,
View,
ViewStyle
} from 'react-native'
2021-02-08 23:47:20 +01:00
import { Flow } from 'react-native-animated-spinkit'
2020-12-03 01:28:56 +01:00
2020-12-26 23:05:17 +01:00
export interface Props {
style?: StyleProp<ViewStyle>
type: 'icon' | 'text'
content: string
loading?: boolean
destructive?: boolean
disabled?: boolean
2021-01-07 19:13:09 +01:00
active?: boolean
2020-12-26 23:05:17 +01:00
strokeWidth?: number
2020-12-26 23:05:17 +01:00
size?: 'S' | 'M' | 'L'
spacing?: 'XS' | 'S' | 'M' | 'L'
round?: boolean
overlay?: boolean
onPress: () => void
}
const Button: React.FC<Props> = ({
style: customStyle,
type,
content,
loading = false,
destructive = false,
disabled = false,
2021-01-07 19:13:09 +01:00
active = false,
strokeWidth,
2020-12-26 23:05:17 +01:00
size = 'M',
spacing = 'S',
round = false,
overlay = false,
onPress
}) => {
2021-02-05 01:13:57 +01:00
const { mode, theme } = useTheme()
2020-12-26 23:05:17 +01:00
const mounted = useRef(false)
useEffect(() => {
if (mounted.current) {
layoutAnimation()
} else {
mounted.current = true
}
2021-01-10 02:12:14 +01:00
}, [content, loading, disabled, active])
2020-12-26 23:05:17 +01:00
const loadingSpinkit = useMemo(
() => (
<View style={{ position: 'absolute' }}>
2021-02-08 23:47:20 +01:00
<Flow size={StyleConstants.Font.Size[size]} color={theme.secondary} />
2020-12-26 23:05:17 +01:00
</View>
),
2021-02-05 01:13:57 +01:00
[mode]
2020-12-26 23:05:17 +01:00
)
const colorContent = useMemo(() => {
2021-01-07 19:13:09 +01:00
if (active) {
return theme.blue
} else {
if (overlay) {
return theme.primaryOverlay
} else {
if (disabled) {
return theme.secondary
} else {
if (destructive) {
return theme.red
} else {
return theme.primaryDefault
2021-01-07 19:13:09 +01:00
}
}
}
}
2021-02-05 01:13:57 +01:00
}, [mode, disabled])
2021-01-07 19:13:09 +01:00
const colorBorder = useMemo(() => {
if (active) {
return theme.blue
2020-12-26 23:05:17 +01:00
} else {
2021-01-07 19:13:09 +01:00
if (disabled || loading) {
2020-12-26 23:05:17 +01:00
return theme.secondary
} else {
if (destructive) {
return theme.red
} else {
return theme.primaryDefault
2020-12-26 23:05:17 +01:00
}
}
}
2021-02-05 01:13:57 +01:00
}, [mode, loading, disabled])
2021-01-07 19:13:09 +01:00
const colorBackground = useMemo(() => {
if (overlay) {
return theme.backgroundOverlayInvert
2021-01-07 19:13:09 +01:00
} else {
return theme.backgroundDefault
2021-01-07 19:13:09 +01:00
}
2021-02-05 01:13:57 +01:00
}, [mode])
2020-12-26 23:05:17 +01:00
const children = useMemo(() => {
switch (type) {
case 'icon':
return (
<>
<Icon
name={content}
2020-12-26 23:05:17 +01:00
color={colorContent}
strokeWidth={strokeWidth}
2020-12-26 23:05:17 +01:00
style={{ opacity: loading ? 0 : 1 }}
size={StyleConstants.Font.Size[size] * (size === 'L' ? 1.25 : 1)}
2020-12-26 23:05:17 +01:00
/>
2021-01-18 00:23:40 +01:00
{loading ? loadingSpinkit : null}
2020-12-26 23:05:17 +01:00
</>
)
case 'text':
return (
<>
<Text
style={{
color: colorContent,
fontSize:
2020-12-28 17:30:20 +01:00
StyleConstants.Font.Size[size] * (size === 'L' ? 1.25 : 1),
2020-12-26 23:05:17 +01:00
fontWeight: destructive
? StyleConstants.Font.Weight.Bold
: undefined,
opacity: loading ? 0 : 1
}}
children={content}
2020-12-28 00:59:57 +01:00
testID='text'
2020-12-26 23:05:17 +01:00
/>
2021-01-18 00:23:40 +01:00
{loading ? loadingSpinkit : null}
2020-12-26 23:05:17 +01:00
</>
)
}
2021-02-05 01:13:57 +01:00
}, [mode, content, loading, disabled, active])
2020-12-26 23:05:17 +01:00
enum spacingMapping {
XS = 'S',
S = 'M',
M = 'L',
L = 'XL'
}
return (
2021-01-16 00:00:31 +01:00
<Pressable
style={[
styles.button,
{
borderWidth: overlay ? 0 : 1,
borderColor: colorBorder,
backgroundColor: colorBackground,
paddingVertical: StyleConstants.Spacing[spacing],
paddingHorizontal:
StyleConstants.Spacing[round ? spacing : spacingMapping[spacing]]
},
customStyle
]}
testID='base'
onPress={onPress}
children={children}
disabled={disabled || active || loading}
/>
2020-12-26 23:05:17 +01:00
)
}
const styles = StyleSheet.create({
button: {
borderRadius: 100,
flexDirection: 'row',
justifyContent: 'center',
alignItems: 'center'
}
})
export default Button