Created
August 25, 2019 22:44
-
-
Save timc1/cbb9a817d29a1a94bd23c42bb1099533 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import React from 'react' | |
import styled from '@emotion/styled' | |
type StatusType = 'error' | 'neutral' | 'success' | |
type State = { | |
isStatusShowing: boolean | |
status: string | |
type: StatusType | |
} | |
type Action = { | |
type: string | |
payload: { | |
[k: string]: any | |
} | |
} | |
const reducer = (state: State, action: Action) => { | |
switch (action.type) { | |
case 'SET_STATUS_MESSAGE': | |
return { | |
isStatusShowing: true, | |
status: action.payload.value, | |
type: action.payload.type, | |
} | |
case 'HIDE_STATUS_MESSAGE': | |
return { | |
...state, | |
isStatusShowing: false, | |
} | |
default: | |
throw new Error(`No action type of ${action.type} found.`) | |
} | |
} | |
const StatusNotificationContext = React.createContext<any>(undefined) | |
export function StatusNotificationProvider({ | |
children, | |
}: { | |
children: React.ReactNode | |
}) { | |
const [state, dispatch] = React.useReducer(reducer, { | |
isStatusShowing: false, | |
status: '', | |
type: 'neutral', | |
}) | |
const currentTimeoutId = React.useRef<any>(null) | |
React.useEffect(() => { | |
if (state.isStatusShowing) { | |
clearTimeout(currentTimeoutId.current) | |
currentTimeoutId.current = setTimeout(() => { | |
dispatch({ | |
type: 'HIDE_STATUS_MESSAGE', | |
payload: {}, | |
}) | |
}, 2500) | |
} | |
}, [state.isStatusShowing]) | |
// Expose separate methods for toggling various | |
// statuses so users don't have to remember the type. | |
const setSuccessStatus = (value: string) => { | |
dispatch({ | |
type: 'SET_STATUS_MESSAGE', | |
payload: { value, type: 'success' }, | |
}) | |
} | |
const setErrorStatus = (value: string) => { | |
dispatch({ | |
type: 'SET_STATUS_MESSAGE', | |
payload: { value, type: 'error' }, | |
}) | |
} | |
const setNeutralStatus = (value: string) => { | |
dispatch({ | |
type: 'SET_STATUS_MESSAGE', | |
payload: { value, type: 'neutral' }, | |
}) | |
} | |
const value = { | |
setSuccessStatus, | |
setNeutralStatus, | |
setErrorStatus, | |
} | |
return ( | |
<StatusNotificationContext.Provider value={value}> | |
<StatusNotification | |
isShowing={state.isStatusShowing} | |
status={state.status} | |
type={state.type} | |
/> | |
{children} | |
</StatusNotificationContext.Provider> | |
) | |
} | |
export default function useStatusNotification() { | |
const context = React.useContext(StatusNotificationContext) | |
if (!context) { | |
throw new Error( | |
`useStatusNotification must be used within an StatusNotificationProvider.` | |
) | |
} | |
return context | |
} | |
const StatusNotification = ({ | |
isShowing, | |
status, | |
type, | |
}: { | |
isShowing: boolean | |
status: string | |
type: StatusType | |
}) => { | |
return ( | |
<NotificationContainer | |
isShowing={isShowing} | |
type={type} | |
aria-hidden={isShowing ? 'false' : 'true'} | |
> | |
<span aria-label={status}>{status}</span> | |
</NotificationContainer> | |
) | |
} | |
// eslint-disable-next-line | |
const NotificationContainer = styled.div<{ | |
isShowing: boolean | |
type: StatusType | |
}>` | |
border: 1px solid var(--color-dark-3); | |
border-radius: var(--border-radius-1); | |
padding: var(--padding); | |
background: var(--color-light-1); | |
position: fixed; | |
bottom: calc(var(--padding) * 2); | |
left: calc(var(--padding) * 2); | |
max-width: 400px; | |
opacity: ${props => (props.isShowing ? '1' : '0')}; | |
transform: translateY(${props => (props.isShowing ? '0px' : '40px')}); | |
transition: transform 250ms var(--ease), opacity 200ms var(--ease) 50ms; | |
pointer-events: ${props => (props.isShowing ? 'initial' : 'none')}; | |
touch-action: ${props => (props.isShowing ? 'initial' : 'none')}; | |
z-index: 9; | |
> span { | |
font-weight: var(--font-weight-md); | |
color: ${props => | |
props.type === 'error' | |
? 'var(--color-error-1)' | |
: props.type === 'success' | |
? 'var(--color-success-1)' | |
: 'var(--color-dark-1)'}; | |
} | |
` |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment