57 行
1.5 KiB
TypeScript
57 行
1.5 KiB
TypeScript
import { useEffect } from 'react'
|
|
import { useTranslation } from 'react-i18next'
|
|
import { create } from 'zustand'
|
|
|
|
import LocalisedText from '@/components/LocalisedText'
|
|
import { translateUIMessage } from '@/lib/i18n'
|
|
|
|
import type { FC } from 'react'
|
|
|
|
type OverlayStore = {
|
|
active: boolean
|
|
setActive: (v: boolean) => void }
|
|
|
|
|
|
export const useOverlayStore = create<OverlayStore> (set => ({
|
|
active: false,
|
|
setActive: v => set ({ active: v }) }))
|
|
|
|
|
|
const RouteBlockerOverlay: FC = () => {
|
|
const { i18n } = useTranslation ('common')
|
|
const loading = translateUIMessage ('loading', 'common', i18n.language)
|
|
const loadingLabel = translateUIMessage ('loadingLabel', 'common', i18n.language)
|
|
const active = useOverlayStore (s => s.active)
|
|
|
|
useEffect (() => {
|
|
if (active)
|
|
{
|
|
document.body.style.overflow = 'hidden'
|
|
document.body.setAttribute ('aria-busy', 'true')
|
|
}
|
|
else
|
|
{
|
|
document.body.style.overflow = ''
|
|
document.body.removeAttribute ('aria-busy')
|
|
}
|
|
}, [active])
|
|
|
|
if (!(active))
|
|
return null
|
|
|
|
return (
|
|
<div
|
|
role="progressbar"
|
|
aria-label={loadingLabel.text}
|
|
lang={loadingLabel.language}
|
|
className="fixed inset-0 z-[9999] bg-black/50 backdrop-blur-sm pointer-events-auto">
|
|
<div className="absolute inset-0 flex items-center justify-center">
|
|
<div className="rounded-2xl bg-black/60 text-white px-6 py-3 text-sm">
|
|
<LocalisedText language={loading.language}>{loading.text}</LocalisedText>
|
|
</div>
|
|
</div>
|
|
</div>)
|
|
}
|
|
|
|
export default RouteBlockerOverlay
|