mirror of
https://github.com/cpvalente/ontime.git
synced 2026-08-12 02:43:50 +00:00
3603b836f6
* refactor: typescript migration * chore: remove prop-types package * refactor: file structure * refactor: migrate ontime table to tanstack table 8 * refactor: rundown controller uses service as data source * refactor: convert to typescript * feat: caching store * refactor: add delay values to rundown * feat: toggle past visibility * chore: update tests * refactor: add extra fields to CSV * style: show skipped events * chore: add route to navigation menu * style: allow jumping to bottom * chore: add tests
141 lines
5.0 KiB
TypeScript
141 lines
5.0 KiB
TypeScript
import { KeyboardEvent, memo, useEffect, useRef, useState } from 'react';
|
|
import { createPortal } from 'react-dom';
|
|
import { Link, useLocation, useSearchParams } from 'react-router-dom';
|
|
import { useDisclosure } from '@chakra-ui/react';
|
|
import { IoApps } from '@react-icons/all-files/io5/IoApps';
|
|
import { IoArrowUp } from '@react-icons/all-files/io5/IoArrowUp';
|
|
import { IoContract } from '@react-icons/all-files/io5/IoContract';
|
|
import { IoExpand } from '@react-icons/all-files/io5/IoExpand';
|
|
import { IoPencilSharp } from '@react-icons/all-files/io5/IoPencilSharp';
|
|
import { IoSwapVertical } from '@react-icons/all-files/io5/IoSwapVertical';
|
|
|
|
import { navigatorConstants } from '../../../viewerConfig';
|
|
import useClickOutside from '../../hooks/useClickOutside';
|
|
import useFullscreen from '../../hooks/useFullscreen';
|
|
import { useKeyDown } from '../../hooks/useKeyDown';
|
|
import { useViewOptionsStore } from '../../stores/viewOptions';
|
|
|
|
import RenameClientModal from './rename-client-modal/RenameClientModal';
|
|
|
|
import style from './NavigationMenu.module.scss';
|
|
|
|
function NavigationMenu() {
|
|
const location = useLocation();
|
|
|
|
const { isFullScreen, toggleFullScreen } = useFullscreen();
|
|
const { mirror, toggleMirror } = useViewOptionsStore();
|
|
const [showButton, setShowButton] = useState(false);
|
|
const [searchParams, setSearchParams] = useSearchParams();
|
|
const [showMenu, setShowMenu] = useState(false);
|
|
const menuRef = useRef<HTMLDivElement | null>(null);
|
|
useClickOutside(menuRef, () => setShowMenu(false));
|
|
|
|
const { isOpen, onOpen, onClose } = useDisclosure();
|
|
|
|
const toggleMenu = () => setShowMenu((prev) => !prev);
|
|
useKeyDown(toggleMenu, ' ', { isDisabled: searchParams.get('edit') === 'true' || isOpen });
|
|
|
|
useEffect(() => {
|
|
let fadeOut: NodeJS.Timeout | null = null;
|
|
const setShowMenuTrue = () => {
|
|
setShowButton(true);
|
|
if (fadeOut) {
|
|
clearTimeout(fadeOut);
|
|
}
|
|
fadeOut = setTimeout(() => setShowButton(false), 3000);
|
|
};
|
|
document.addEventListener('mousemove', setShowMenuTrue);
|
|
return () => {
|
|
document.removeEventListener('mousemove', setShowMenuTrue);
|
|
if (fadeOut) {
|
|
clearTimeout(fadeOut);
|
|
}
|
|
};
|
|
}, []);
|
|
|
|
const isKeyEnter = (event: KeyboardEvent<HTMLDivElement>) => event.key === 'Enter';
|
|
const handleFullscreen = () => toggleFullScreen();
|
|
const handleMirror = () => toggleMirror();
|
|
|
|
const showEditFormDrawer = () => {
|
|
searchParams.append('edit', 'true');
|
|
setSearchParams(searchParams);
|
|
};
|
|
|
|
return createPortal(
|
|
<div id='navigation-menu-portal' ref={menuRef} className={mirror ? style.mirror : ''}>
|
|
<RenameClientModal isOpen={isOpen} onClose={onClose} />
|
|
<div className={`${style.buttonContainer} ${!showButton && !showMenu ? style.hidden : ''}`}>
|
|
<button onClick={toggleMenu} aria-label='toggle menu' className={style.navButton}>
|
|
<IoApps />
|
|
</button>
|
|
<button className={style.button} onClick={showEditFormDrawer}>
|
|
<IoPencilSharp />
|
|
</button>
|
|
{showMenu && (
|
|
<div className={style.menuContainer} data-testid='navigation-menu'>
|
|
<div className={style.buttonsContainer}>
|
|
<div
|
|
className={style.link}
|
|
tabIndex={0}
|
|
role='button'
|
|
onClick={handleFullscreen}
|
|
onKeyDown={(event) => {
|
|
isKeyEnter(event) && handleFullscreen();
|
|
}}
|
|
>
|
|
Toggle Fullscreen
|
|
{isFullScreen ? <IoContract /> : <IoExpand />}
|
|
</div>
|
|
<div
|
|
className={style.link}
|
|
tabIndex={0}
|
|
role='button'
|
|
onClick={handleMirror}
|
|
onKeyDown={(event) => {
|
|
isKeyEnter(event) && handleMirror();
|
|
}}
|
|
>
|
|
Flip Screen
|
|
<IoSwapVertical />
|
|
</div>
|
|
<div
|
|
className={style.link}
|
|
tabIndex={0}
|
|
role='button'
|
|
onClick={onOpen}
|
|
onKeyDown={(event) => {
|
|
isKeyEnter(event) && onOpen();
|
|
}}
|
|
>
|
|
Rename Client
|
|
</div>
|
|
</div>
|
|
<hr className={style.separator} />
|
|
<Link to='/cuesheet' className={style.link} tabIndex={0}>
|
|
Cuesheet
|
|
<IoArrowUp className={style.linkIcon} />
|
|
</Link>
|
|
<hr className={style.separator} />
|
|
{navigatorConstants.map((route) => (
|
|
<Link
|
|
key={route.url}
|
|
to={route.url}
|
|
className={`${style.link} ${route.url === location.pathname ? style.current : undefined}`}
|
|
tabIndex={0}
|
|
>
|
|
{route.label}
|
|
<IoArrowUp className={style.linkIcon} />
|
|
</Link>
|
|
))}
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>,
|
|
|
|
document.body,
|
|
);
|
|
}
|
|
|
|
export default memo(NavigationMenu);
|