11import Chessground from '@react-chess/chessground'
22import { useEffect , useMemo , useState } from 'react'
3+ import { motion , AnimatePresence } from 'framer-motion'
34
45const states = [
56 'rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1' ,
@@ -13,14 +14,38 @@ const states = [
1314]
1415
1516interface LoadingProps {
17+ // When true, shows loading UI (optionally after a delay); when false, shows children
18+ isLoading ?: boolean
19+ // Milliseconds to wait before showing the loading UI
20+ delay ?: number
21+ // Visual options for the loading UI
1622 transparent ?: boolean
1723 message ?: React . ReactNode
24+ // Optional content to render when not loading
25+ children ?: React . ReactNode
1826}
1927
2028export const Loading : React . FC < LoadingProps > = ( {
29+ isLoading = true ,
30+ delay = 1000 ,
2131 transparent = false ,
2232 message,
33+ children,
2334} ) => {
35+ // Delay handling for showing the loading UI
36+ const [ showLoading , setShowLoading ] = useState ( false )
37+ useEffect ( ( ) => {
38+ let timer : NodeJS . Timeout
39+ if ( isLoading ) {
40+ timer = setTimeout ( ( ) => setShowLoading ( true ) , delay )
41+ } else {
42+ setShowLoading ( false )
43+ }
44+ return ( ) => {
45+ if ( timer ) clearTimeout ( timer )
46+ }
47+ } , [ isLoading , delay ] )
48+
2449 const [ currentIndex , setCurrentIndex ] = useState ( 0 )
2550 const [ renderKey , setRenderKey ] = useState ( 0 )
2651
@@ -32,49 +57,71 @@ export const Loading: React.FC<LoadingProps> = ({
3257 useEffect ( ( ) => {
3358 const increment = async ( ) => {
3459 await new Promise ( ( resolve ) => setTimeout ( resolve , 500 ) )
35- setCurrentIndex ( currentIndex + 1 )
36-
60+ setCurrentIndex ( ( idx ) => idx + 1 )
3761 setRenderKey ( ( prev ) => prev + 1 )
3862 }
39-
40- increment ( )
41- } , [ currentIndex ] )
63+ if ( isLoading && showLoading ) {
64+ increment ( )
65+ }
66+ } , [ isLoading , showLoading , currentIndex ] )
4267
4368 return (
44- < div
45- className = { `my-40 flex w-screen items-center justify-center ${
46- transparent
47- ? 'absolute left-0 top-0 h-screen bg-backdrop/90'
48- : 'bg-backdrop'
49- } md:my-auto`}
50- >
51- < div className = "flex flex-col items-center gap-4" >
52- < div
53- className = { `h-[50vw] w-[50vw] md:h-[30vh] md:w-[30vh] ${
54- ! transparent ? 'opacity-50' : 'opacity-100'
55- } `}
69+ < AnimatePresence mode = "wait" >
70+ { isLoading && showLoading ? (
71+ < motion . div
72+ key = "loading"
73+ initial = { { opacity : 0 } }
74+ animate = { { opacity : 1 } }
75+ exit = { { opacity : 0 } }
76+ transition = { { duration : 0.3 } }
77+ className = "my-auto"
5678 >
57- < div className = "h-full w-full" >
58- < Chessground
59- key = { renderKey }
60- contained
61- config = { {
62- fen : currentState ,
63- animation : {
64- duration : 0 ,
65- } ,
66- viewOnly : true ,
67- } }
68- />
79+ < div
80+ className = { `my-40 flex w-screen items-center justify-center ${
81+ transparent
82+ ? 'absolute left-0 top-0 h-screen bg-backdrop/90'
83+ : 'bg-backdrop'
84+ } md:my-auto`}
85+ >
86+ < div className = "flex flex-col items-center gap-4" >
87+ < div
88+ className = { `h-[50vw] w-[50vw] md:h-[30vh] md:w-[30vh] ${
89+ ! transparent ? 'opacity-50' : 'opacity-100'
90+ } `}
91+ >
92+ < div className = "h-full w-full" >
93+ < Chessground
94+ key = { renderKey }
95+ contained
96+ config = { {
97+ fen : currentState ,
98+ animation : {
99+ duration : 0 ,
100+ } ,
101+ viewOnly : true ,
102+ } }
103+ />
104+ </ div >
105+ </ div >
106+ < h2 className = "text-2xl font-semibold" > Loading...</ h2 >
107+ { message ? (
108+ < p className = "max-w-prose px-4 text-center text-secondary" >
109+ { message }
110+ </ p >
111+ ) : null }
112+ </ div >
69113 </ div >
70- </ div >
71- < h2 className = "text-2xl font-semibold" > Loading...</ h2 >
72- { message ? (
73- < p className = "max-w-prose px-4 text-center text-secondary" >
74- { message }
75- </ p >
76- ) : null }
77- </ div >
78- </ div >
114+ </ motion . div >
115+ ) : ! isLoading ? (
116+ < motion . div
117+ key = "content"
118+ initial = { { opacity : 0 , y : 10 } }
119+ animate = { { opacity : 1 , y : 0 } }
120+ transition = { { duration : 0.4 , ease : 'easeOut' } }
121+ >
122+ { children }
123+ </ motion . div >
124+ ) : null }
125+ </ AnimatePresence >
79126 )
80127}
0 commit comments