22// Licensed under the MIT License.
33
44import { Box , Typography , Button , useTheme , alpha , IconButton , Divider } from "@mui/material" ;
5- import React , { FC , useState , useEffect } from "react" ;
5+ import React , { FC , useState , useEffect , useRef } from "react" ;
66import ArrowBackIosNewIcon from '@mui/icons-material/ArrowBackIosNew' ;
77import ArrowForwardIosIcon from '@mui/icons-material/ArrowForwardIos' ;
88import GridViewIcon from '@mui/icons-material/GridView' ;
@@ -62,6 +62,8 @@ export const About: FC<{}> = function About({ }) {
6262 const theme = useTheme ( ) ;
6363 const [ currentFeature , setCurrentFeature ] = useState ( 0 ) ;
6464 const [ currentScreenshot , setCurrentScreenshot ] = useState ( 0 ) ;
65+ const videoRef = useRef < HTMLVideoElement | null > ( null ) ;
66+ const videoDurationsRef = useRef < Map < string , number > > ( new Map ( ) ) ;
6567
6668 const handlePrevious = ( ) => {
6769 setCurrentFeature ( ( prev ) => ( prev === 0 ? features . length - 1 : prev - 1 ) ) ;
@@ -71,6 +73,27 @@ export const About: FC<{}> = function About({ }) {
7173 setCurrentFeature ( ( prev ) => ( prev === features . length - 1 ? 0 : prev + 1 ) ) ;
7274 } ;
7375
76+ // Auto-advance features based on video duration
77+ useEffect ( ( ) => {
78+ const currentMedia = features [ currentFeature ] . media ;
79+ const isVideo = features [ currentFeature ] . mediaType === 'video' ;
80+
81+ // Default duration for images or if video duration is not yet loaded
82+ let duration = 10000 ; // 10 seconds for images
83+
84+ if ( isVideo && videoDurationsRef . current . has ( currentMedia ) ) {
85+ // Use the stored video duration (in milliseconds)
86+ duration = videoDurationsRef . current . get ( currentMedia ) ! * 1000 ;
87+ duration = duration + 3000 ; // add 3 seconds to the video duration
88+ }
89+
90+ const timeoutId = setTimeout ( ( ) => {
91+ setCurrentFeature ( ( prev ) => ( prev + 1 ) % features . length ) ;
92+ } , duration ) ;
93+
94+ return ( ) => clearTimeout ( timeoutId ) ;
95+ } , [ currentFeature ] ) ;
96+
7497 // Preload adjacent carousel items for smoother transitions
7598 useEffect ( ( ) => {
7699 const preloadMedia = ( index : number ) => {
@@ -133,11 +156,26 @@ export const About: FC<{}> = function About({ }) {
133156 href = "https://pypi.org/project/data-formulator/"
134157 > Install Locally</ Button >
135158 < Button size = "large" variant = "outlined" color = "primary"
159+ sx = { {
160+ animation : 'subtleGlow 2s ease-in-out infinite' ,
161+ '@keyframes subtleGlow' : {
162+ '0%, 100%' : {
163+ boxShadow : `0 0 8px ${ alpha ( theme . palette . primary . main , 0.4 ) } , 0 0 16px ${ alpha ( theme . palette . primary . main , 0.2 ) } ` ,
164+ } ,
165+ '50%' : {
166+ boxShadow : `0 0 12px ${ alpha ( theme . palette . primary . main , 0.6 ) } , 0 0 24px ${ alpha ( theme . palette . primary . main , 0.3 ) } , 0 0 32px ${ alpha ( theme . palette . primary . main , 0.1 ) } ` ,
167+ }
168+ } ,
169+ '&:hover' : {
170+ animation : 'subtleGlow 1.5s ease-in-out infinite' ,
171+ boxShadow : `0 0 16px ${ alpha ( theme . palette . primary . main , 0.7 ) } , 0 0 32px ${ alpha ( theme . palette . primary . main , 0.4 ) } !important` ,
172+ }
173+ } }
136174 startIcon = { < GridViewIcon sx = { { fontSize : '1rem' } } /> }
137175 href = "/app"
138- > Online Demo</ Button >
176+ > Try Online Demo</ Button >
139177 < Typography variant = "caption" sx = { { mt : 1.5 , color : 'text.secondary' , fontStyle : 'italic' } } >
140- Psst — install locally for the full experience ✨. The online demo is a bit slow & has limited features ( at the moment ) .
178+ Psst — install locally for the full experience ✨. The online demo has limited features (at the moment).
141179 </ Typography >
142180 </ Box >
143181 ) ;
@@ -290,11 +328,21 @@ export const About: FC<{}> = function About({ }) {
290328 component = "video"
291329 key = { features [ currentFeature ] . media }
292330 src = { features [ currentFeature ] . media }
331+ ref = { videoRef }
293332 autoPlay
294333 loop
295334 muted
296335 playsInline
297336 preload = "metadata"
337+ onLoadedMetadata = { ( e ) => {
338+ const video = e . currentTarget as HTMLVideoElement ;
339+ if ( video . duration && ! isNaN ( video . duration ) ) {
340+ videoDurationsRef . current . set (
341+ features [ currentFeature ] . media ,
342+ video . duration
343+ ) ;
344+ }
345+ } }
298346 sx = { {
299347 width : '100%' ,
300348 height : 'auto' ,
0 commit comments