Improved navigation significantly on mobile
Affects issues: - Close #2228
This commit is contained in:
parent
12d22f945f
commit
413e087c4d
|
@ -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";
|
||||||
|
|
|
@ -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>
|
||||||
)
|
)
|
||||||
|
|
|
@ -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>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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}>
|
||||||
|
|
|
@ -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}/>
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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%;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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%;
|
||||||
}
|
}
|
Loading…
Reference in New Issue