import styled from '@emotion/styled'; import React, { useEffect, useState } from 'react'; import { LoadingIcon } from './LoadingIcon'; /** * A component that displays a loading overlay when `loading` prop is true. * @param loading - A boolean indicating whether to show the loading overlay or not. Default is `true`. * @param extendTime - An optional number indicating the delay time in milliseconds before hiding the loading overlay. Default is `0`. * @returns A React functional component that displays a loading overlay. */ export const LoadingOverlay: React.FC<{ loading?: boolean; extendTime?: number; }> = React.memo(({ loading = true, extendTime = 0 }) => { const [delayLoading, setDelayLoading] = useState(loading); const [show, setShow] = useState(loading); const [prevBodyAttrs, setPrevBodyAttrs] = useState<{ overflow?: React.CSSProperties['overflow']; }>({}); const bodyScrollDisable = () => { setPrevBodyAttrs({ overflow: document.body.style.overflow, }); document.body.style.overflow = 'hidden'; }; const bodyScrollEnable = () => { document.body.style.overflow = prevBodyAttrs.overflow || ''; setPrevBodyAttrs({}); }; /** * if loading = true => delayLoading = true * if loading = false => delayLoading = false after extendTime * */ useEffect(() => { if (!loading) { setTimeout(() => { setDelayLoading(false); }, extendTime); } else { // prevent scroll when loading bodyScrollDisable(); } }, [extendTime, loading]); /** * if delayLoading = true => show = true * if delayLoading = false => show = false * */ useEffect(() => { if (delayLoading) { setShow(true); } else { setTimeout(() => { // enable scroll when loading is done bodyScrollEnable(); setShow(false); }, 200); } // eslint-disable-next-line }, [delayLoading]); if (!show) return null; return ( <> ); }); const Overlay = styled.div<{ $loading: boolean }>` position: fixed; z-index: 999999; top: 0; left: 0; right: 0; bottom: 0; color: #7367f0; background-color: #ffffff; opacity: ${({ $loading }) => ($loading ? 1 : 0)}; display: grid; place-items: center; transition: opacity 0.2s ease-in-out; `;