mirror of
https://github.com/stashapp/stash.git
synced 2025-12-07 08:54:10 +01:00
Add responsive menu (#257)
This commit is contained in:
parent
bb164f1895
commit
50930f6ca7
4 changed files with 185 additions and 86 deletions
|
|
@ -1,4 +1,4 @@
|
|||
import React, { FunctionComponent, useEffect } from "react";
|
||||
import React, { FunctionComponent, useState } from "react";
|
||||
import { Route, Switch } from "react-router-dom";
|
||||
import { ErrorBoundary } from "./components/ErrorBoundary";
|
||||
import Galleries from "./components/Galleries/Galleries";
|
||||
|
|
@ -11,26 +11,80 @@ import { Stats } from "./components/Stats";
|
|||
import Studios from "./components/Studios/Studios";
|
||||
import Tags from "./components/Tags/Tags";
|
||||
import { SceneFilenameParser } from "./components/scenes/SceneFilenameParser";
|
||||
import { Sidebar } from "./components/Sidebar";
|
||||
import { IconName } from "@blueprintjs/core";
|
||||
|
||||
export interface IMenuItem {
|
||||
icon: IconName
|
||||
text: string
|
||||
href: string
|
||||
}
|
||||
|
||||
interface IProps {}
|
||||
|
||||
export const App: FunctionComponent<IProps> = (props: IProps) => {
|
||||
const [menuOpen, setMenuOpen] = useState<boolean>(false);
|
||||
|
||||
function getSidebarClosedClass() {
|
||||
if (!menuOpen) {
|
||||
return " sidebar-closed";
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
const menuItems: IMenuItem[] = [
|
||||
{
|
||||
icon: "video",
|
||||
text: "Scenes",
|
||||
href: "/scenes"
|
||||
},
|
||||
{
|
||||
href: "/scenes/markers",
|
||||
icon: "map-marker",
|
||||
text: "Markers"
|
||||
},
|
||||
{
|
||||
href: "/galleries",
|
||||
icon: "media",
|
||||
text: "Galleries"
|
||||
},
|
||||
{
|
||||
href: "/performers",
|
||||
icon: "person",
|
||||
text: "Performers"
|
||||
},
|
||||
{
|
||||
href: "/studios",
|
||||
icon: "mobile-video",
|
||||
text: "Studios"
|
||||
},
|
||||
{
|
||||
href: "/tags",
|
||||
icon: "tag",
|
||||
text: "Tags"
|
||||
}
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="bp3-dark">
|
||||
<ErrorBoundary>
|
||||
<MainNavbar />
|
||||
<Switch>
|
||||
<Route exact={true} path="/" component={Stats} />
|
||||
<Route path="/scenes" component={Scenes} />
|
||||
{/* <Route path="/scenes/:id" component={Scene} /> */}
|
||||
<Route path="/galleries" component={Galleries} />
|
||||
<Route path="/performers" component={Performers} />
|
||||
<Route path="/tags" component={Tags} />
|
||||
<Route path="/studios" component={Studios} />
|
||||
<Route path="/settings" component={Settings} />
|
||||
<Route path="/sceneFilenameParser" component={SceneFilenameParser} />
|
||||
<Route component={PageNotFound} />
|
||||
</Switch>
|
||||
<MainNavbar onMenuToggle={() => setMenuOpen(!menuOpen)} menuItems={menuItems}/>
|
||||
<Sidebar className={getSidebarClosedClass()} menuItems={menuItems}/>
|
||||
<div className={"main" + getSidebarClosedClass()}>
|
||||
<Switch>
|
||||
<Route exact={true} path="/" component={Stats} />
|
||||
<Route path="/scenes" component={Scenes} />
|
||||
{/* <Route path="/scenes/:id" component={Scene} /> */}
|
||||
<Route path="/galleries" component={Galleries} />
|
||||
<Route path="/performers" component={Performers} />
|
||||
<Route path="/tags" component={Tags} />
|
||||
<Route path="/studios" component={Studios} />
|
||||
<Route path="/settings" component={Settings} />
|
||||
<Route path="/sceneFilenameParser" component={SceneFilenameParser} />
|
||||
<Route component={PageNotFound} />
|
||||
</Switch>
|
||||
</div>
|
||||
</ErrorBoundary>
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -3,12 +3,17 @@ import {
|
|||
NavbarDivider,
|
||||
NavbarGroup,
|
||||
NavbarHeading,
|
||||
Button,
|
||||
} from "@blueprintjs/core";
|
||||
import React, { FunctionComponent, useEffect, useState } from "react";
|
||||
import { Link, NavLink } from "react-router-dom";
|
||||
import useLocation from "react-use/lib/useLocation";
|
||||
import { IMenuItem } from "../App";
|
||||
|
||||
interface IProps {}
|
||||
interface IProps {
|
||||
onMenuToggle() : void
|
||||
menuItems: IMenuItem[]
|
||||
}
|
||||
|
||||
export const MainNavbar: FunctionComponent<IProps> = (props) => {
|
||||
const [newButtonPath, setNewButtonPath] = useState<string | undefined>(undefined);
|
||||
|
|
@ -46,76 +51,38 @@ export const MainNavbar: FunctionComponent<IProps> = (props) => {
|
|||
}
|
||||
|
||||
return (
|
||||
<Navbar fixedToTop={true}>
|
||||
<div>
|
||||
<NavbarGroup align="left">
|
||||
<NavbarHeading><Link to="/" className="bp3-button bp3-minimal">Stash</Link></NavbarHeading>
|
||||
<NavbarDivider />
|
||||
<>
|
||||
<Navbar fixedToTop={true}>
|
||||
<div>
|
||||
<NavbarGroup align="left">
|
||||
<Button className="menu-button" icon="menu" onClick={() => props.onMenuToggle()}/>
|
||||
<NavbarHeading><Link to="/" className="bp3-button bp3-minimal">Stash</Link></NavbarHeading>
|
||||
<NavbarDivider />
|
||||
|
||||
<NavLink
|
||||
exact={true}
|
||||
to="/scenes"
|
||||
className="bp3-button bp3-minimal bp3-icon-video"
|
||||
activeClassName="bp3-active"
|
||||
>
|
||||
Scenes
|
||||
</NavLink>
|
||||
|
||||
<NavLink
|
||||
exact={true}
|
||||
to="/scenes/markers"
|
||||
className="bp3-button bp3-minimal bp3-icon-map-marker"
|
||||
activeClassName="bp3-active"
|
||||
>
|
||||
Markers
|
||||
</NavLink>
|
||||
|
||||
<NavLink
|
||||
exact={true}
|
||||
to="/galleries"
|
||||
className="bp3-button bp3-minimal bp3-icon-media"
|
||||
activeClassName="bp3-active"
|
||||
>
|
||||
Galleries
|
||||
</NavLink>
|
||||
|
||||
<NavLink
|
||||
exact={true}
|
||||
to="/performers"
|
||||
className="bp3-button bp3-minimal bp3-icon-person"
|
||||
activeClassName="bp3-active"
|
||||
>
|
||||
Performers
|
||||
</NavLink>
|
||||
|
||||
<NavLink
|
||||
exact={true}
|
||||
to="/studios"
|
||||
className="bp3-button bp3-minimal bp3-icon-mobile-video"
|
||||
activeClassName="bp3-active"
|
||||
>
|
||||
Studios
|
||||
</NavLink>
|
||||
|
||||
<NavLink
|
||||
exact={true}
|
||||
to="/tags"
|
||||
className="bp3-button bp3-minimal bp3-icon-tag"
|
||||
activeClassName="bp3-active"
|
||||
>
|
||||
Tags
|
||||
</NavLink>
|
||||
</NavbarGroup>
|
||||
<NavbarGroup align="right">
|
||||
{renderNewButton()}
|
||||
<NavLink
|
||||
exact={true}
|
||||
to="/settings"
|
||||
className="bp3-button bp3-minimal bp3-icon-cog"
|
||||
activeClassName="bp3-active"
|
||||
/>
|
||||
</NavbarGroup>
|
||||
</div>
|
||||
</Navbar>
|
||||
{props.menuItems.map((i) => {
|
||||
return (
|
||||
<NavLink
|
||||
exact={true}
|
||||
to={i.href}
|
||||
className={"bp3-button bp3-minimal collapsible-navlink bp3-icon-" + i.icon}
|
||||
activeClassName="bp3-active"
|
||||
>
|
||||
{i.text}
|
||||
</NavLink>
|
||||
);
|
||||
})}
|
||||
</NavbarGroup>
|
||||
<NavbarGroup align="right">
|
||||
{renderNewButton()}
|
||||
<NavLink
|
||||
exact={true}
|
||||
to="/settings"
|
||||
className="bp3-button bp3-minimal bp3-icon-cog"
|
||||
activeClassName="bp3-active"
|
||||
/>
|
||||
</NavbarGroup>
|
||||
</div>
|
||||
</Navbar>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
32
ui/v2/src/components/Sidebar.tsx
Normal file
32
ui/v2/src/components/Sidebar.tsx
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
import {
|
||||
MenuItem,
|
||||
Menu,
|
||||
IconName,
|
||||
} from "@blueprintjs/core";
|
||||
import React, { FunctionComponent } from "react";
|
||||
import { IMenuItem } from "../App";
|
||||
|
||||
interface IProps {
|
||||
className: string
|
||||
menuItems: IMenuItem[]
|
||||
}
|
||||
|
||||
export const Sidebar: FunctionComponent<IProps> = (props) => {
|
||||
return (
|
||||
<>
|
||||
<div className={"sidebar" + props.className}>
|
||||
<Menu large={true}>
|
||||
{props.menuItems.map((i) => {
|
||||
return (
|
||||
<MenuItem
|
||||
icon={i.icon}
|
||||
text={i.text}
|
||||
href={i.href}
|
||||
/>
|
||||
)
|
||||
})}
|
||||
</Menu>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
|
@ -476,4 +476,50 @@ span.block {
|
|||
|
||||
.aliases-field > label{
|
||||
font-weight: 300;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 768px) {
|
||||
// collapse menu items into sidemenu
|
||||
.collapsible-navlink {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.sidebar {
|
||||
position: fixed;
|
||||
top: 50px;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 200px;
|
||||
background-color: #394b59;
|
||||
transition: left 0.5s;
|
||||
z-index: 11;
|
||||
}
|
||||
|
||||
.sidebar.sidebar-closed {
|
||||
left: -200px;
|
||||
transition: left 0.5s;
|
||||
}
|
||||
|
||||
// overlay menu on smaller devices
|
||||
@media only screen and (min-width: 768px) {
|
||||
// hide menu button on larger devices
|
||||
.menu-button {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.main {
|
||||
margin-left: 200px;
|
||||
transition: margin-left 0.5s;
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
left: -200px;
|
||||
}
|
||||
|
||||
.main.sidebar-closed {
|
||||
margin-left: 0px;
|
||||
transition: margin-left 0.5s;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue