1
0
Fork 0
freesewing/packages/react/context/LoadingStatus/index.mjs
Joost De Cock 51dc1d9732
[breaking]: FreeSewing v4 (#7297)
Refer to the CHANGELOG for all info.

---------

Co-authored-by: Wouter van Wageningen <wouter.vdub@yahoo.com>
Co-authored-by: Josh Munic <jpmunic@gmail.com>
Co-authored-by: Jonathan Haas <haasjona@gmail.com>
2025-04-01 16:15:20 +02:00

122 lines
3.3 KiB
JavaScript

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>
)
}