@@ -4,10 +4,16 @@ import {
44 useColorModeValue ,
55 Heading ,
66 Skeleton ,
7+ IconButton ,
8+ Spacer ,
9+ Box ,
710} from "@chakra-ui/react" ;
811import { useDtt } from "../../../services/sbhsApi/useDtt" ;
912import { DateTime } from "luxon" ;
10- import { useEffect } from "react" ;
13+ import { useEffect , useRef , useState } from "react" ;
14+ import { PictureInPicture } from "phosphor-react" ;
15+
16+ declare const documentPictureInPicture : any ;
1117
1218export default function Countdown ( {
1319 countdown,
@@ -18,8 +24,10 @@ export default function Countdown({
1824} ) {
1925 const { data : dtt } = useDtt ( ) ;
2026 const isLoaded = ! ! dtt ;
27+ const pipColor = useColorModeValue ( "gray.100" , "gray.900" ) ;
2128 const bgColor =
2229 useToken ( "colors" , useColorModeValue ( "gray.300" , "gray.700" ) ) + "55" ;
30+ const textColor = useColorModeValue ( "gray.900" , "gray.100" ) ;
2331
2432 const activeIndex = dtt ?. periods . findIndex (
2533 ( { startTime, endTime } ) =>
@@ -44,28 +52,99 @@ export default function Countdown({
4452 return ( ) => clearInterval ( timer ) ;
4553 } ) ;
4654
55+ const countdownRef = useRef < HTMLDivElement > ( null ) ;
56+ const skeletonRef = useRef < HTMLDivElement > ( null ) ;
57+ const [ fullScreen , setFullScreen ] = useState ( false ) ;
58+
59+ const handlePictureInPicture = async ( ) => {
60+ if ( ! countdownRef . current ) return ;
61+
62+ setFullScreen ( true ) ;
63+
64+ const pipWindow = await documentPictureInPicture . requestWindow ( ) ;
65+
66+ [ ...document . styleSheets ] . forEach ( ( styleSheet ) => {
67+ try {
68+ const cssRules = [ ...styleSheet . cssRules ]
69+ . map ( ( rule ) => rule . cssText )
70+ . join ( "" ) ;
71+ const style = document . createElement ( "style" ) ;
72+
73+ style . textContent = cssRules . toString ( ) ;
74+ pipWindow . document . head . appendChild ( style ) ;
75+ } catch ( e ) {
76+ const link = document . createElement ( "link" ) ;
77+
78+ link . rel = "stylesheet" ;
79+ link . type = ( styleSheet . type as string ) ?? "" ;
80+ link . media = styleSheet . media ?. toString ( ) ?? "" ;
81+ link . href = styleSheet . href ?? "" ;
82+ pipWindow . document . head . appendChild ( link ) ;
83+ }
84+ } ) ;
85+
86+ pipWindow . document . body . append ( countdownRef . current ) ;
87+
88+ pipWindow . addEventListener ( "pagehide" , ( event : any ) => {
89+ const pipPlayer = event . target . body . firstChild ;
90+ skeletonRef . current ?. append ( pipPlayer ) ;
91+ setFullScreen ( false ) ;
92+ } ) ;
93+ } ;
94+
4795 if ( ! nextPeriod ) return null ;
4896
4997 return (
50- < Skeleton isLoaded = { isLoaded } >
51- < Flex
52- direction = { "column" }
53- p = { 3 }
54- bg = { bgColor }
55- rounded = { "lg" }
56- align = { "center" }
98+ < Skeleton ref = { skeletonRef } isLoaded = { isLoaded } >
99+ < Box
100+ ref = { countdownRef }
101+ h = { fullScreen ? "full" : "auto" }
102+ w = "full"
103+ bg = { fullScreen ? pipColor : "transparent" }
57104 >
58- < Heading size = { "xs" } fontFamily = { "Poppins, sans-serif" } >
59- { nextPeriod . name } in
60- </ Heading >
61- < Heading
62- size = { "lg" }
63- fontFamily = { "Poppins, sans-serif" }
64- fontWeight = { "normal" }
105+ < Flex
106+ direction = { "column" }
107+ p = { 3 }
108+ bg = { bgColor }
109+ rounded = { fullScreen ? "none" : "lg" }
110+ align = { "center" }
111+ h = "full"
112+ w = "full"
113+ justify = { "center" }
65114 >
66- { countdown }
67- </ Heading >
68- </ Flex >
115+ < Flex w = "full" >
116+ < Spacer />
117+ < Flex direction = { "column" } align = { "center" } >
118+ < Heading
119+ size = { fullScreen ? "xl" : "xs" }
120+ fontFamily = { "Poppins, sans-serif" }
121+ color = { textColor }
122+ >
123+ { nextPeriod ?. name ?? "Nothing" } in
124+ </ Heading >
125+ < Heading
126+ size = { fullScreen ? "2xl" : "lg" }
127+ fontFamily = { "Poppins, sans-serif" }
128+ fontWeight = { "normal" }
129+ color = { textColor }
130+ >
131+ { countdown }
132+ </ Heading >
133+ </ Flex >
134+ < Spacer />
135+ { "documentPictureInPicture" in window && ! fullScreen && (
136+ < IconButton
137+ icon = { < PictureInPicture weight = "fill" /> }
138+ aria-label = "Open Picture in Picture"
139+ alignSelf = { "start" }
140+ variant = { "ghost" }
141+ colorScheme = "gray"
142+ onClick = { handlePictureInPicture }
143+ />
144+ ) }
145+ </ Flex >
146+ </ Flex >
147+ </ Box >
69148 </ Skeleton >
70149 ) ;
71150}
0 commit comments