Improved navigation significantly on mobile

Affects issues:
- Close #2228
This commit is contained in:
Aurora Lahtela 2023-02-03 20:29:03 +02:00
parent 12d22f945f
commit 413e087c4d
7 changed files with 112 additions and 43 deletions

View File

@ -1,6 +1,7 @@
import './style/main.sass'; import './style/main.sass';
import './style/sb-admin-2.css' import './style/sb-admin-2.css'
import './style/style.css'; import './style/style.css';
import './style/mobile.css';
import {BrowserRouter, Navigate, Route, Routes} from "react-router-dom"; import {BrowserRouter, Navigate, Route, Routes} from "react-router-dom";
import React from "react"; import React from "react";

View File

@ -30,7 +30,7 @@ const Visualizer = ({option, groups, colors, name}) => {
const VisualizerSelector = ({onClick, icon}) => { const VisualizerSelector = ({onClick, icon}) => {
return ( return (
<button className="btn float-end" onClick={onClick}> <button className="btn float-end visualizer-button" onClick={onClick}>
<FontAwesomeIcon icon={icon} className="col-gray"/> <FontAwesomeIcon icon={icon} className="col-gray"/>
</button> </button>
) )

View File

@ -60,7 +60,7 @@ const Contributor = ({contributor}) => {
const icons = contributor.contributed.map( const icons = contributor.contributed.map(
(type, i) => <Fa key={i} icon={["fa", getContributionIcon(type)]}/>); (type, i) => <Fa key={i} icon={["fa", getContributionIcon(type)]}/>);
return ( return (
<li className="col-4">{contributor.name} {icons} </li> <li className="contributor">{contributor.name} {icons} </li>
) )
} }

View File

@ -42,7 +42,7 @@ const Header = ({page, tab, hideUpdater}) => {
const headImageUrl = user ? getPlayerHeadImageUrl(user.playerName, user.linkedToUuid) : undefined const headImageUrl = user ? getPlayerHeadImageUrl(user.playerName, user.linkedToUuid) : undefined
// TODO Remove .replace('<', '') after locale replacement // TODO Remove .replace('<', '') after locale replacement
return ( return (
<nav className="nav mt-3 align-items-center justify-content-between container-fluid"> <nav className="nav-header nav mt-3 align-items-center justify-content-between container-fluid">
<div className="d-sm-flex"> <div className="d-sm-flex">
<h1 className="h3 mb-0 text-gray-800"> <h1 className="h3 mb-0 text-gray-800">
<button onClick={toggleSidebar}> <button onClick={toggleSidebar}>

View File

@ -24,7 +24,7 @@ const Divider = ({showMargin}) => (
<hr className={"sidebar-divider" + (showMargin ? '' : " my-0")}/> <hr className={"sidebar-divider" + (showMargin ? '' : " my-0")}/>
) )
const InnerItem = ({href, icon, name, nameShort, color, external}) => { const InnerItem = ({href, icon, name, nameShort, color, external, collapseSidebar}) => {
if (!href) { if (!href) {
return (<hr className={"nav-servers dropdown-divider mx-3 my-2"}/>) return (<hr className={"nav-servers dropdown-divider mx-3 my-2"}/>)
} }
@ -38,14 +38,14 @@ const InnerItem = ({href, icon, name, nameShort, color, external}) => {
) )
} }
return <NavLink to={href} className={({isActive}) => { return <NavLink to={href} onClick={collapseSidebar} className={({isActive}) => {
return isActive ? "collapse-item nav-button active" : "collapse-item nav-button" return isActive ? "collapse-item nav-button active" : "collapse-item nav-button"
}}> }}>
<Fa icon={icon} className={color ? "col-" + color : undefined}/> <span>{nameShort ? nameShort : name}</span> <Fa icon={icon} className={color ? "col-" + color : undefined}/> <span>{nameShort ? nameShort : name}</span>
</NavLink> </NavLink>
} }
export const Item = ({item, inner}) => { export const Item = ({item, inner, collapseSidebar}) => {
const {setCurrentTab} = useNavigation(); const {setCurrentTab} = useNavigation();
const {pathname} = useLocation(); const {pathname} = useLocation();
const {t} = useTranslation(); const {t} = useTranslation();
@ -58,7 +58,7 @@ export const Item = ({item, inner}) => {
if (inner) { if (inner) {
return (<InnerItem href={href} icon={icon} name={t(name)} nameShort={t(nameShort)} color={color} return (<InnerItem href={href} icon={icon} name={t(name)} nameShort={t(nameShort)} color={color}
external={external}/>) external={external} collapseSidebar={collapseSidebar}/>)
} }
if (external) { if (external) {
@ -74,7 +74,7 @@ export const Item = ({item, inner}) => {
return ( return (
<li className={"nav-item nav-button"}> <li className={"nav-item nav-button"}>
<NavLink to={href} className={({isActive}) => { <NavLink to={href} onClick={collapseSidebar} className={({isActive}) => {
return isActive ? "nav-link active" : "nav-link" return isActive ? "nav-link active" : "nav-link"
}}> }}>
<Fa icon={icon} className={color ? "col-" + color : undefined}/> <span>{t(name)}</span> <Fa icon={icon} className={color ? "col-" + color : undefined}/> <span>{t(name)}</span>
@ -95,36 +95,19 @@ const VersionButton = ({toggleVersionModal, versionInfo}) => {
</button>; </button>;
} }
const FooterButtons = () => { const FooterButtons = ({collapseSidebar, toggleInfoModal, toggleVersionModal, versionInfo}) => {
const {t} = useTranslation(); const {t} = useTranslation();
const {toggleColorChooser} = useTheme(); const {toggleColorChooser} = useTheme();
const openColorChooser = useCallback(() => {
toggleColorChooser();
collapseSidebar();
}, [toggleColorChooser, collapseSidebar]);
const {authRequired} = useAuth(); const {authRequired} = useAuth();
const [infoModalOpen, setInfoModalOpen] = useState(false);
const toggleInfoModal = () => setInfoModalOpen(!infoModalOpen);
const [versionModalOpen, setVersionModalOpen] = useState(false);
const toggleVersionModal = () => setVersionModalOpen(!versionModalOpen);
const [versionInfo, setVersionInfo] = useState({currentVersion: 'Loading..', updateAvailable: false});
const loadVersion = async () => {
const {data, error} = await fetchPlanVersion();
if (data) {
setVersionInfo(data);
} else if (error) {
setVersionInfo({currentVersion: "Error getting version", updateAvailable: false})
}
}
useEffect(() => {
loadVersion();
}, [])
return ( return (
<> <div className={"footer-buttons"}>
<div className="mt-2 ms-md-3 text-center text-md-start"> <div className="mt-2 ms-md-3 text-center text-md-start">
<button className="btn bg-transparent-light" onClick={toggleColorChooser} <button className="btn bg-transparent-light" onClick={openColorChooser}
title={t('html.label.themeSelect')}> title={t('html.label.themeSelect')}>
<Fa icon={faPalette}/> <Fa icon={faPalette}/>
</button> </button>
@ -140,13 +123,11 @@ const FooterButtons = () => {
<div className="ms-md-3 text-center text-md-start"> <div className="ms-md-3 text-center text-md-start">
<VersionButton toggleVersionModal={toggleVersionModal} versionInfo={versionInfo}/> <VersionButton toggleVersionModal={toggleVersionModal} versionInfo={versionInfo}/>
</div> </div>
<PluginInformationModal open={infoModalOpen} toggle={toggleInfoModal}/> </div>
<VersionInformationModal open={versionModalOpen} toggle={toggleVersionModal} versionInfo={versionInfo}/>
</>
) )
} }
const SidebarCollapse = ({item, open, setOpen}) => { const SidebarCollapse = ({item, open, setOpen, collapseSidebar}) => {
const {t} = useTranslation(); const {t} = useTranslation();
const toggle = event => { const toggle = event => {
event.preventDefault(); event.preventDefault();
@ -174,6 +155,7 @@ const SidebarCollapse = ({item, open, setOpen}) => {
inner inner
active={false} active={false}
item={content} item={content}
collapseSidebar={collapseSidebar}
/>)} />)}
</div> </div>
</div> </div>
@ -182,18 +164,20 @@ const SidebarCollapse = ({item, open, setOpen}) => {
) )
} }
const renderItem = (item, i, openCollapse, setOpenCollapse, t) => { const renderItem = (item, i, openCollapse, setOpenCollapse, t, windowWidth, collapseSidebar) => {
if (item.contents) { if (item.contents) {
return <SidebarCollapse key={i} return <SidebarCollapse key={i}
item={item} item={item}
open={openCollapse && openCollapse === i} open={windowWidth < 660 || (openCollapse && openCollapse === i)}
setOpen={() => setOpenCollapse(i)}/> setOpen={() => setOpenCollapse(i)}
collapseSidebar={collapseSidebar}/>
} }
if (item.href !== undefined) { if (item.href !== undefined) {
return <Item key={i} return <Item key={i}
active={false} active={false}
item={item} item={item}
collapseSidebar={collapseSidebar}
/> />
} }
@ -220,8 +204,41 @@ const Sidebar = ({page, items}) => {
return () => window.removeEventListener('resize', updateWidth); return () => window.removeEventListener('resize', updateWidth);
}, [updateWidth]); }, [updateWidth]);
const collapseSidebar = () => setSidebarExpanded(windowWidth > 1350); const collapseSidebar = useCallback(() => {
useEffect(collapseSidebar, [windowWidth, currentTab, setSidebarExpanded]); setSidebarExpanded(windowWidth > 1350);
if (windowWidth < 660) {
window.scrollTo({top: 0});
}
}, [setSidebarExpanded, windowWidth]);
const collapseConditionallyOnItemClick = useCallback(() => {
if (windowWidth < 660) {
setTimeout(collapseSidebar, 10);
}
}, [collapseSidebar, windowWidth]);
useEffect(collapseSidebar, [windowWidth, currentTab, setSidebarExpanded, collapseSidebar]);
const [infoModalOpen, setInfoModalOpen] = useState(false);
const toggleInfoModal = useCallback(() => {
setInfoModalOpen(!infoModalOpen);
collapseConditionallyOnItemClick();
}, [setInfoModalOpen, infoModalOpen, collapseConditionallyOnItemClick]);
const [versionModalOpen, setVersionModalOpen] = useState(false);
const toggleVersionModal = useCallback(() => {
setVersionModalOpen(!versionModalOpen);
collapseConditionallyOnItemClick();
}, [setVersionModalOpen, versionModalOpen, collapseConditionallyOnItemClick]);
const [versionInfo, setVersionInfo] = useState({currentVersion: 'Loading..', updateAvailable: false});
const loadVersion = async () => {
const {data, error} = await fetchPlanVersion();
if (data) {
setVersionInfo(data);
} else if (error) {
setVersionInfo({currentVersion: "Error getting version", updateAvailable: false})
}
}
useEffect(() => loadVersion(), []);
return ( return (
<> <>
@ -230,10 +247,17 @@ const Sidebar = ({page, items}) => {
<Logo/> <Logo/>
<PageNavigationItem page={page}/> <PageNavigationItem page={page}/>
<Divider showMargin={items.length && !items[0].contents && items[0].href === undefined}/> <Divider showMargin={items.length && !items[0].contents && items[0].href === undefined}/>
{items.length ? items.filter(item => item !== undefined).map((item, i) => renderItem(item, i, openCollapse, toggleCollapse, t)) : ''} {items.length ? items.filter(item => item !== undefined).map((item, i) => renderItem(item, i, openCollapse, toggleCollapse, t, windowWidth, collapseConditionallyOnItemClick)) : ''}
<Divider/> <Divider/>
<FooterButtons/> <FooterButtons
collapseSidebar={collapseConditionallyOnItemClick}
toggleInfoModal={toggleInfoModal}
toggleVersionModal={toggleVersionModal}
versionInfo={versionInfo}
/>
</ul>} </ul>}
<PluginInformationModal open={infoModalOpen} toggle={toggleInfoModal}/>
<VersionInformationModal open={versionModalOpen} toggle={toggleVersionModal} versionInfo={versionInfo}/>
</> </>
) )
} }

View File

@ -0,0 +1,40 @@
@media (max-width: 660px) {
.w-22 {
width: unset !important;
height: 4rem;
}
#wrapper {
font-size: small;
background-image: var(--color-theme);
}
.sidebar {
position: absolute;
padding-top: 1rem;
width: calc(100vw - 1rem) !important;
padding-right: 0.15rem;
z-index: 9999;
height: 100%;
}
.sidebar .nav-item .nav-link {
width: 100%;
}
.nav-header {
margin-top: 0.75rem !important;
}
.nav-header > * {
margin-top: 0.5rem;
}
.visualizer-button {
padding: 0.25rem;
}
.contributor {
width: 50%;
}
}

View File

@ -1424,4 +1424,8 @@ ul.filters {
display: flex; display: flex;
min-height: 100vh; min-height: 100vh;
background-image: linear-gradient(to right, var(--color-theme) 0%, var(--color-theme) 14rem, #f8f9fc 14.01rem, #f8f9fc 100%); background-image: linear-gradient(to right, var(--color-theme) 0%, var(--color-theme) 14rem, #f8f9fc 14.01rem, #f8f9fc 100%);
}
.contributor {
width: 33%;
} }