Created
June 19, 2020 18:43
-
-
Save izakfilmalter/a99305f88797e06e631ad3a6d09a4e2f 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 css from '@emotion/css' | |
import { SerializedStyles } from '@emotion/serialize' | |
import styled from '@emotion/styled' | |
import { CircularProgress } from '@material-ui/core' | |
import React, { FC, ReactNode, useLayoutEffect, useState } from 'react' | |
import { ease, visible } from 'Helpers/animation' | |
import { BorderRadius, getBorderRadius } from 'Helpers/borderRadius' | |
import { Colors, getColor } from 'Helpers/colors' | |
import { flexFlow } from 'Helpers/flex' | |
import { isNotNil } from 'Helpers/typeGuards' | |
import { getZIndex, ZIndexNodeName } from 'Helpers/zIndex' | |
const ViewLoaderContainer = styled.div<{ | |
transparent: boolean | |
}>( | |
({ transparent }) => css` | |
${flexFlow('column')}; | |
${getBorderRadius(BorderRadius.Large)}; | |
background: ${transparent ? 'transparent' : getColor(Colors.Black)}; | |
position: relative; // pos:rel because the loader has to pos:abs | |
flex: 1; | |
overflow: hidden; | |
`, | |
) | |
const EmptyStateContainer = styled.div<{ showEmptyState: boolean }>( | |
({ showEmptyState }) => css` | |
${visible(showEmptyState)}; | |
transform: scale(${showEmptyState ? 1 : 0.97}) | |
translateY(${showEmptyState ? 0 : 16}px); | |
transition: all ${ease(showEmptyState)} 500ms; | |
`, | |
) | |
const ChildrenContainer = styled.div<{ showChildren: boolean }>( | |
({ showChildren }) => css` | |
${visible(showChildren)}; | |
${flexFlow('column')}; | |
flex: 1; | |
overflow: hidden; | |
transition: all ${ease(showChildren)} 32ms; | |
`, | |
) | |
export type ViewLoaderProps = Omit<LoadingListProps, 'transparent'> & { | |
EmptyState?: ReactNode | |
blackoutCss?: SerializedStyles | |
isEmpty: boolean | |
transparent?: boolean | |
} | |
export const ViewLoader: FC<ViewLoaderProps> = props => { | |
const { | |
children, | |
isLoaded, | |
isEmpty, | |
EmptyState, | |
blackoutCss, | |
transparent = false, | |
...domProps | |
} = props | |
const showEmptyState = isEmpty && isLoaded | |
const showChildren = isLoaded && !isEmpty | |
return ( | |
<ViewLoaderContainer transparent={transparent} {...domProps}> | |
<BlackOut | |
isLoaded={isLoaded} | |
css={blackoutCss} | |
transparent={transparent} | |
/> | |
<EmptyStateContainer showEmptyState={showEmptyState}> | |
{showEmptyState && isNotNil(EmptyState) ? EmptyState : <></>} | |
</EmptyStateContainer> | |
<ChildrenContainer showChildren={showChildren}> | |
{children} | |
</ChildrenContainer> | |
</ViewLoaderContainer> | |
) | |
} | |
type BlackOutContainerProps = { | |
isVisible: boolean | |
transparent: boolean | |
} | |
const BlackOutContainer = styled.div<BlackOutContainerProps>( | |
({ isVisible, transparent }) => css` | |
${flexFlow('column')}; | |
${getZIndex(ZIndexNodeName.BlackOut)}; | |
background: ${transparent | |
? 'transparent' | |
: getColor(Colors.Black, isVisible ? 0.9999 : 0)}; | |
border-radius: inherit; | |
bottom: 0; | |
left: 0; | |
position: absolute; | |
right: 0; | |
top: 0; | |
pointer-events: none; | |
transition: all ${ease(isVisible)} 32ms; | |
`, | |
) | |
export type LoadingListProps = { | |
isLoaded: boolean | |
transparent: boolean | |
} | |
export const BlackOut: FC<LoadingListProps> = props => { | |
const { isLoaded, transparent, ...domProps } = props | |
const [showLoader, setShowLoader] = useState(false) | |
useLayoutEffect(() => { | |
setTimeout(() => { | |
if (!isLoaded) { | |
setShowLoader(true) | |
} | |
// Using 32 to here because we are running at 60fps and wanna wait for two frames. | |
}, 32) | |
// eslint-disable-next-line react-hooks/exhaustive-deps | |
}, []) | |
useLayoutEffect(() => { | |
if (isLoaded && showLoader) { | |
setShowLoader(false) | |
} | |
}, [isLoaded, showLoader]) | |
return ( | |
<BlackOutContainer | |
isVisible={!isLoaded} | |
transparent={transparent} | |
{...domProps} | |
> | |
<CircularProgress | |
css={css` | |
${visible(showLoader)}; | |
margin: auto; | |
transition: all ${ease(showLoader)} 32ms; | |
`} | |
/> | |
</BlackOutContainer> | |
) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment