Skip to content

Commit

Permalink
feat: add ability to toggle position
Browse files Browse the repository at this point in the history
  • Loading branch information
Jean Verster committed Oct 29, 2019
1 parent 92bc1f4 commit 2804761
Show file tree
Hide file tree
Showing 4 changed files with 103 additions and 26 deletions.
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
{
"name": "react-native-styled-toast",
"version": "1.0.4",
"version": "1.0.6",
"description": "A themeable toast component for React Native.",
"main": "./src/index.tsx",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"scripts": {
"build": "yarn format && rm -rf dist && tsc",
Expand Down
27 changes: 21 additions & 6 deletions src/Context/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,31 @@ import * as React from 'react'
import { LayoutAnimation } from 'react-native'
import Box from '../Box'
import Toast, { ToastConfig } from '../Toast'
import { ToastInternalConfig } from '../Toast/index'
import { uuid } from '../Utils'

type ToastContextType = {
toast?: (options: ToastConfig) => void
position?: 'TOP' | 'BOTTOM'
}

export const ToastContext = React.createContext<Partial<ToastContextType>>({})

export const useToast = () => React.useContext(ToastContext)

const offset = Constants.statusBarHeight + 16

const ToastProvider: React.FC<ToastContextType> = ({ children }) => {
const [toasts, setToasts] = React.useState<ToastConfig[]>([])
export type FullToastConfig = ToastConfig & ToastInternalConfig

const ToastProvider: React.FC<ToastContextType> = ({ children, position }) => {
const [toasts, setToasts] = React.useState<FullToastConfig[]>([])

const toast = (newToast: ToastConfig) => {
LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut)
setToasts((prevToasts) => [{ index: prevToasts.length, id: uuid(), ...newToast }, ...prevToasts])
setToasts((prevToasts) =>
position === 'BOTTOM'
? [...prevToasts, { index: prevToasts.length, id: uuid(), ...newToast }]
: [{ index: prevToasts.length, id: uuid(), ...newToast }, ...prevToasts]
)
}

const hideToast = (id: string) => {
Expand All @@ -31,8 +38,16 @@ const ToastProvider: React.FC<ToastContextType> = ({ children }) => {
return (
<ToastContext.Provider value={{ toast }}>
{children}
<Box px={4} pt={offset} position="absolute" top={0} left={0} right={0}>
{toasts.map((config) => {
<Box
px={4}
left={0}
right={0}
position="absolute"
pt={position === 'BOTTOM' ? 0 : offset}
pb={position === 'BOTTOM' ? offset : 0}
style={position === 'BOTTOM' ? { bottom: 0 } : { top: 0 }}
>
{toasts.map((config: ToastConfig & ToastInternalConfig) => {
return <Toast key={config.id} onClose={(id) => hideToast(id)} {...config} />
})}
</Box>
Expand Down
68 changes: 58 additions & 10 deletions src/Toast/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,30 +3,59 @@ import * as React from 'react'
import { Animated, Easing, StyleSheet, TouchableOpacity, Vibration } from 'react-native'
import Box from '../Box'
import Icon from '../Icon'
import { Accent, IconCont, StyledToast, SubText } from './styles'
import { Accent, CloseButtonCont, Heading, IconCont, StyledToast, SubText } from './styles'

export type ToastConfig = {
id?: string
index?: number
bg?: string
color?: string
message: string
subMessage?: string
duration?: number
onPress?: () => void
borderColor?: string
closeIconColor?: string
shouldVibrate?: boolean
closeButtonBgColor?: string
position?: 'TOP' | 'BOTTOM'
intent?: 'SUCCESS' | 'ERROR'
closeIconBorderRadius?: number
}

export type ToastInternalConfig = {
id?: string
index?: number
onClose?: (id: string) => void
}

const offset = Constants.statusBarHeight + 16

export const Toast: React.FC<ToastConfig> = ({
const shadow = {
shadowColor: '#000',
shadowOffset: {
width: 0,
height: 0
},
shadowOpacity: 0.1,
shadowRadius: 1,
elevation: 1
}

export const Toast: React.FC<ToastConfig & ToastInternalConfig> = ({
intent,
message,
duration,
onClose,
id,
index,
shouldVibrate,
onPress
onPress,
borderColor,
closeButtonBgColor,
closeIconColor,
closeIconBorderRadius,
bg,
color,
subMessage
}) => {
const isSuccess = intent === 'SUCCESS'
const topOffset = offset + 60 * (index || 0)
Expand Down Expand Up @@ -83,12 +112,17 @@ export const Toast: React.FC<ToastConfig> = ({
})

return (
<StyledToast mb={4} style={{ transform: [{ translateY }, { scale }], opacity }} py={2}>
<StyledToast
mb={4}
py={2}
bg={bg}
borderColor={borderColor}
style={{ transform: [{ translateY }, { scale }], opacity, ...shadow }}
>
<TouchableOpacity
activeOpacity={1}
testID="toast-touchable"
onPress={onPress}
// eslint-disable-next-line
testID="toast-touchable"
style={{ ...StyleSheet.absoluteFillObject, flexDirection: 'row' }}
>
<Accent testID="toast-accent" bg={isSuccess ? 'success' : 'error'} />
Expand All @@ -101,9 +135,19 @@ export const Toast: React.FC<ToastConfig> = ({
/>
</IconCont>
<Box flex={1} alignItems="flex-start">
<SubText>{message}</SubText>
<Heading color={color}>{message}</Heading>
{!!subMessage && (
<SubText color={color} mt={2}>
{subMessage}
</SubText>
)}
</Box>
</TouchableOpacity>
<CloseButtonCont onPress={() => onClose && id && onClose(id)}>
<Box pl={1} p={2} borderRadius={closeIconBorderRadius} mx={2} bg={closeButtonBgColor} alignItems="center">
<Icon size={20} family="Feather" name="x" color={closeIconColor} />
</Box>
</CloseButtonCont>
</StyledToast>
)
}
Expand All @@ -115,5 +159,9 @@ Toast.defaultProps = {
intent: 'SUCCESS',
onPress: () => false,
shouldVibrate: false,
message: 'Toast message!'
closeIconColor: 'text',
message: 'Toast message!',
closeButtonBgColor: 'muted',
closeIconBorderRadius: 4,
borderColor: 'border'
}
30 changes: 22 additions & 8 deletions src/Toast/styles.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Animated } from 'react-native'
import styled from 'styled-components/native'
import {
BorderColorProps,
color,
ColorProps,
fontSize,
Expand All @@ -13,11 +14,14 @@ import {
TopProps
} from 'styled-system'

type StyledToastProps = {
accentColor?: string
}
type StyledToastProps = SpaceProps &
ColorProps &
TopProps &
BorderColorProps & {
accentColor?: string
}

export const StyledToast = styled(Animated.View)<SpaceProps & ColorProps & TopProps & StyledToastProps>`
export const StyledToast = styled(Animated.View)<StyledToastProps>`
${top};
${color};
${space};
Expand All @@ -28,11 +32,10 @@ export const StyledToast = styled(Animated.View)<SpaceProps & ColorProps & TopPr
align-items: center;
flex-direction: row;
justify-content: center;
box-shadow: 0 0px 2px rgba(30, 30, 30, 0.2);
`

StyledToast.defaultProps = {
bg: 'muted',
bg: 'background',
accentColor: 'success'
}

Expand All @@ -59,7 +62,7 @@ export const Heading = styled.Text<TextProps>`
`

Heading.defaultProps = {
fontSize: 4,
fontSize: 2,
color: 'text'
}

Expand All @@ -69,7 +72,7 @@ export const SubText = styled.Text<TextProps>`
${fontSize};
${textAlign};
flex-wrap: wrap;
font-weight: bold;
font-weight: lighter;
`

SubText.defaultProps = {
Expand All @@ -80,4 +83,15 @@ SubText.defaultProps = {
export const IconCont = styled.View<SpaceProps>`
${space};
align-items: center;
justify-content: center;
`

export const CloseButtonCont = styled.TouchableOpacity<SpaceProps>`
${space};
top: 0;
right: 0;
bottom: 0;
position: absolute;
align-items: center;
justify-content: center;
`

0 comments on commit 2804761

Please # to comment.