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 md:tw-top-28 tw-left-0 tw-w-full tw-z-50 md:tw-px-4 md:tw-mx-auto" style={{ zIndex: 500 }} > <div className={`tw-w-full md:tw-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 md:tw-rounded-lg tw-shadow tw-text-secondary-content tw-text-lg lg:tw-text-xl tw-font-medium md:tw-bg-opacity-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> ) }