@@ -23,14 +23,14 @@ import {
2323 useTheme
2424} from '@mui/material' ;
2525
26- const currTheme = createTheme ( {
27- palette : {
28- primary : {
29- main : '#ffffff' ,
30- contrastText : '#000000'
31- } ,
32- } ,
33- } ) ;
26+ // const currTheme = createTheme({
27+ // palette: {
28+ // primary: {
29+ // main: '#ffffff',
30+ // contrastText: '#000000'
31+ // },
32+ // },
33+ // });
3434
3535export default function AppNavBar ( props ) {
3636 const [ isDrawerOpen , setDrawerOpen ] = React . useState ( false ) ;
@@ -41,6 +41,81 @@ export default function AppNavBar(props) {
4141 const apiDisplayName = API_Name ( apiStatus . currentApi ) ;
4242 const accentColor = displayStatus . secondaryColor ;
4343
44+ const TOP_TRIGGER_ZONE = 48 ; // px from top to reveal
45+ const HIDE_TIMEOUT_MS = 2500 ;
46+ const [ topbarLocked , setTopbarLocked ] = React . useState < boolean > ( false ) ;
47+ const [ topbarVisible , setTopbarVisible ] = React . useState < boolean > ( true ) ;
48+ const hideRef = React . useRef < number | null > ( null ) ;
49+ const pointerStartY = React . useRef < number | null > ( null ) ;
50+ const pointerId = React . useRef < number | null > ( null ) ;
51+
52+ const showTopbar = React . useCallback ( ( ) => {
53+ setTopbarVisible ( true ) ;
54+ if ( hideRef . current ) { window . clearTimeout ( hideRef . current ) ; hideRef . current = null ; }
55+ if ( ! topbarLocked ) {
56+ hideRef . current = window . setTimeout ( ( ) => setTopbarVisible ( false ) , HIDE_TIMEOUT_MS ) ;
57+ }
58+ } , [ topbarLocked ] ) ;
59+
60+ React . useEffect ( ( ) => { showTopbar ( ) ; } , [ showTopbar ] ) ; //to activate immediately
61+
62+ React . useEffect ( ( ) => {
63+ const onPointerDown = ( e : PointerEvent ) => {
64+ if ( pointerId . current !== null ) return ;
65+
66+ const y = e . clientY ;
67+ pointerId . current = e . pointerId ;
68+ pointerStartY . current = y ;
69+
70+ if ( y <= TOP_TRIGGER_ZONE ) {
71+ showTopbar ( ) ;
72+ }
73+ } ;
74+
75+ const onPointerMove = ( e : PointerEvent ) => {
76+ // proximity check
77+ if ( e . clientY <= TOP_TRIGGER_ZONE ) {
78+ showTopbar ( ) ;
79+ }
80+ // swipe down check
81+ if ( e . pointerId !== pointerId . current || pointerStartY . current === null ) {
82+ return ;
83+ }
84+ const y = e . clientY
85+ const delta = y - ( pointerStartY . current ?? 0 ) ;
86+ const SWIPE_THRESHOLD = 40 ; //how far to swipe
87+
88+ if ( pointerStartY . current <= TOP_TRIGGER_ZONE && delta >= SWIPE_THRESHOLD ) {
89+ showTopbar ( ) ;
90+
91+ // reset
92+ pointerStartY . current = null ;
93+ pointerId . current = null ;
94+ }
95+ } ;
96+
97+ const onPointerUp = ( e : PointerEvent ) => {
98+ if ( e . pointerId === pointerId . current ) {
99+ pointerId . current = null ;
100+ pointerStartY . current = null ;
101+ }
102+ } ;
103+
104+ window . addEventListener ( 'pointerdown' , onPointerDown , { passive : true } ) ;
105+ window . addEventListener ( 'pointermove' , onPointerMove , { passive : true } ) ;
106+ window . addEventListener ( 'pointerup' , onPointerUp ) ;
107+ window . addEventListener ( 'pointercancel' , onPointerUp ) ;
108+
109+ return ( ) => {
110+ window . removeEventListener ( 'pointerdown' , onPointerDown ) ;
111+ window . removeEventListener ( 'pointermove' , onPointerMove ) ;
112+ window . removeEventListener ( 'pointerup' , onPointerUp ) ;
113+ window . removeEventListener ( 'pointercancel' , onPointerUp ) ;
114+ } ;
115+ } , [ showTopbar ] ) ;
116+
117+ React . useEffect ( ( ) => ( ) => { if ( hideRef . current ) window . clearTimeout ( hideRef . current ) ; } , [ ] ) ;
118+
44119 const theme = useTheme ( ) ;
45120 // const isMobile = useMediaQuery(theme.breakpoints.down('sm'));
46121
@@ -54,7 +129,7 @@ export default function AppNavBar(props) {
54129
55130 return (
56131 < ThemeProvider theme = { theme } >
57- < AppBar position = "fixed" id = "topbar-wrapper" sx = { { transition : '0.6s ' , backgroundColor : accentColor } } >
132+ < AppBar position = "fixed" id = "topbar-wrapper" sx = { { transition : 'transform 240ms ease-in-out' , transform : topbarVisible ? 'translateY(0)' : 'translateY(-100%) ', backgroundColor : accentColor } } >
58133 < Toolbar sx = { { width : '100%' , minHeight : 56 , color : 'white' } } >
59134 < Grid container alignItems = "center" justifyContent = "space-between" >
60135 < Grid container alignItems = "center" justifyContent = "space-between" sx = { { width : '100%' } } >
@@ -89,6 +164,15 @@ export default function AppNavBar(props) {
89164 apiDisplayName = { apiDisplayName }
90165 listening = { controlStatus . listening }
91166 menuVisible = { displayStatus . menuVisible }
167+
168+ topbarLocked = { topbarLocked }
169+ onTopBarToggle = { ( v ) => {
170+ setTopbarLocked ( v ) ;
171+ if ( v ) {
172+ setTopbarVisible ( true ) ;
173+ if ( hideRef . current ) { window . clearTimeout ( hideRef . current ) ; hideRef . current = null ; }
174+ }
175+ } }
92176 />
93177 </ Grid >
94178 </ Grid >
0 commit comments