import React, { useState, useEffect, createContext } from 'react' import { Spinner } from '@freesewing/react/components/Spinner' import { OkIcon, WarningIcon } from '@freesewing/react/components/Icon' /* * The actual context */ export const LoadingStatusContext = createContext([false]) /* * Timeout in seconds before the loading status dissapears */ const timeout = 2 /* * The React component displaying the loading status */ const LoadingStatus = ({ loadingStatus }) => { const [fade, setFade] = useState('tw:opacity-100') const [timer, setTimer] = useState(false) useEffect(() => { if (loadingStatus[2]) { if (timer) clearTimeout(timer) setTimer( window.setTimeout( () => { setFade('opacity-0') }, timeout * 1000 - 350 ) ) } }, [loadingStatus[2]]) if (!loadingStatus[0]) return null let color = 'secondary' let icon = <Spinner /> if (loadingStatus[2]) { color = loadingStatus[3] ? 'success' : 'error' icon = loadingStatus[3] ? ( <OkIcon stroke={4} className="tw:w-8 tw:h-8" /> ) : ( <WarningIcon className="tw:w-8 tw:h-8" stroke={2} /> ) } return ( <div className="tw:fixed tw:bottom-14 tw:md:top-28 tw:left-0 tw:w-full tw:z-50 tw:md:px-4 tw:md:mx-auto" style={{ zIndex: 500 }} > <div className={`tw:w-full tw:md:max-w-2xl tw:m-auto tw:bg-${color} tw:flex tw:flex-row tw:items-center tw:gap-4 tw:p-4 tw:px-4 ${fade} tw:transition-opacity tw:delay-[${timeout * 1000 - 400}ms] tw:duration-300 tw:md:rounded-lg tw:shadow tw:text-secondary-content tw:text-lg tw:lg:text-xl tw:font-medium tw:md:bg-${color}/90`} > <span className="tw:shrink-0">{icon}</span> {loadingStatus[1]} </div> </div> ) } /* * An animated loading state */ const LoadingProgress = ({ val = 0, max = 1, msg }) => ( <div className="tw:flex tw:flex-col tw:gap-2 tw:w-full tw:grow-0"> {msg} <progress className="tw:progress tw:progress-white" value={val} max={max}></progress> </div> ) /* * The Context provider */ export const LoadingStatusContextProvider = ({ children }) => { /* * LoadingStatus should hold an array with 1 to 4 elements: * 0 => Show loading status or not (true or false) * 1 => Message to show * 2 => Set this to true to make the loadingStatus dissapear after 2 seconds * 3 => Set this to true to show success, false to show error (only when 2 is true) */ const [timer, setTimer] = useState(false) const [__loadingStatus, __setLoadingStatus] = useState({ status: [false], setLoadingStatus, loading: false, LoadingStatus: () => <LoadingStatus loadingStatus={[false]} />, LoadingProgress, }) useEffect(() => { if (__loadingStatus.status[2]) { if (timer) clearTimeout(timer) setTimer( window.setTimeout(() => { setLoadingStatus([false]) }, timeout * 1000) ) } }, [__loadingStatus.status[2]]) function setLoadingStatus(newStatus) { __setLoadingStatus({ ...__loadingStatus, status: newStatus, loading: newStatus[0] || false, LoadingStatus: () => <LoadingStatus loadingStatus={newStatus} />, }) } return ( <LoadingStatusContext.Provider value={__loadingStatus}> {children} </LoadingStatusContext.Provider> ) }