mirror of
https://github.com/stashapp/stash.git
synced 2025-12-08 09:23:38 +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 { Route, Switch } from "react-router-dom";
|
||||||
import { ErrorBoundary } from "./components/ErrorBoundary";
|
import { ErrorBoundary } from "./components/ErrorBoundary";
|
||||||
import Galleries from "./components/Galleries/Galleries";
|
import Galleries from "./components/Galleries/Galleries";
|
||||||
|
|
@ -11,14 +11,67 @@ import { Stats } from "./components/Stats";
|
||||||
import Studios from "./components/Studios/Studios";
|
import Studios from "./components/Studios/Studios";
|
||||||
import Tags from "./components/Tags/Tags";
|
import Tags from "./components/Tags/Tags";
|
||||||
import { SceneFilenameParser } from "./components/scenes/SceneFilenameParser";
|
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 {}
|
interface IProps {}
|
||||||
|
|
||||||
export const App: FunctionComponent<IProps> = (props: 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 (
|
return (
|
||||||
<div className="bp3-dark">
|
<div className="bp3-dark">
|
||||||
<ErrorBoundary>
|
<ErrorBoundary>
|
||||||
<MainNavbar />
|
<MainNavbar onMenuToggle={() => setMenuOpen(!menuOpen)} menuItems={menuItems}/>
|
||||||
|
<Sidebar className={getSidebarClosedClass()} menuItems={menuItems}/>
|
||||||
|
<div className={"main" + getSidebarClosedClass()}>
|
||||||
<Switch>
|
<Switch>
|
||||||
<Route exact={true} path="/" component={Stats} />
|
<Route exact={true} path="/" component={Stats} />
|
||||||
<Route path="/scenes" component={Scenes} />
|
<Route path="/scenes" component={Scenes} />
|
||||||
|
|
@ -31,6 +84,7 @@ export const App: FunctionComponent<IProps> = (props: IProps) => {
|
||||||
<Route path="/sceneFilenameParser" component={SceneFilenameParser} />
|
<Route path="/sceneFilenameParser" component={SceneFilenameParser} />
|
||||||
<Route component={PageNotFound} />
|
<Route component={PageNotFound} />
|
||||||
</Switch>
|
</Switch>
|
||||||
|
</div>
|
||||||
</ErrorBoundary>
|
</ErrorBoundary>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -3,12 +3,17 @@ import {
|
||||||
NavbarDivider,
|
NavbarDivider,
|
||||||
NavbarGroup,
|
NavbarGroup,
|
||||||
NavbarHeading,
|
NavbarHeading,
|
||||||
|
Button,
|
||||||
} from "@blueprintjs/core";
|
} from "@blueprintjs/core";
|
||||||
import React, { FunctionComponent, useEffect, useState } from "react";
|
import React, { FunctionComponent, useEffect, useState } from "react";
|
||||||
import { Link, NavLink } from "react-router-dom";
|
import { Link, NavLink } from "react-router-dom";
|
||||||
import useLocation from "react-use/lib/useLocation";
|
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) => {
|
export const MainNavbar: FunctionComponent<IProps> = (props) => {
|
||||||
const [newButtonPath, setNewButtonPath] = useState<string | undefined>(undefined);
|
const [newButtonPath, setNewButtonPath] = useState<string | undefined>(undefined);
|
||||||
|
|
@ -46,65 +51,26 @@ export const MainNavbar: FunctionComponent<IProps> = (props) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<>
|
||||||
<Navbar fixedToTop={true}>
|
<Navbar fixedToTop={true}>
|
||||||
<div>
|
<div>
|
||||||
<NavbarGroup align="left">
|
<NavbarGroup align="left">
|
||||||
|
<Button className="menu-button" icon="menu" onClick={() => props.onMenuToggle()}/>
|
||||||
<NavbarHeading><Link to="/" className="bp3-button bp3-minimal">Stash</Link></NavbarHeading>
|
<NavbarHeading><Link to="/" className="bp3-button bp3-minimal">Stash</Link></NavbarHeading>
|
||||||
<NavbarDivider />
|
<NavbarDivider />
|
||||||
|
|
||||||
|
{props.menuItems.map((i) => {
|
||||||
|
return (
|
||||||
<NavLink
|
<NavLink
|
||||||
exact={true}
|
exact={true}
|
||||||
to="/scenes"
|
to={i.href}
|
||||||
className="bp3-button bp3-minimal bp3-icon-video"
|
className={"bp3-button bp3-minimal collapsible-navlink bp3-icon-" + i.icon}
|
||||||
activeClassName="bp3-active"
|
activeClassName="bp3-active"
|
||||||
>
|
>
|
||||||
Scenes
|
{i.text}
|
||||||
</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>
|
</NavLink>
|
||||||
|
);
|
||||||
|
})}
|
||||||
</NavbarGroup>
|
</NavbarGroup>
|
||||||
<NavbarGroup align="right">
|
<NavbarGroup align="right">
|
||||||
{renderNewButton()}
|
{renderNewButton()}
|
||||||
|
|
@ -117,5 +83,6 @@ export const MainNavbar: FunctionComponent<IProps> = (props) => {
|
||||||
</NavbarGroup>
|
</NavbarGroup>
|
||||||
</div>
|
</div>
|
||||||
</Navbar>
|
</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>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
@ -477,3 +477,49 @@ span.block {
|
||||||
.aliases-field > label{
|
.aliases-field > label{
|
||||||
font-weight: 300;
|
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