Using NavLinks on all pages.

This commit is contained in:
Roland 2022-01-29 22:25:35 -07:00
parent d17ad50d56
commit 8da7e984e2
No known key found for this signature in database
GPG key ID: 9D6DBD3ACF11AB93
7 changed files with 144 additions and 216 deletions

View file

@ -23,7 +23,8 @@ import {
startDownloads
} from "../api";
import TimezoneSelect from 'react-timezone-select';
import {secondsToDate, secondsToFrequency, textEllipsis, truncateLong, WROLModeMessage} from "./Common";
import {secondsToDate, secondsToFrequency, TabLinks, textEllipsis, WROLModeMessage} from "./Common";
import {Route} from "react-router-dom";
class Settings extends React.Component {
@ -440,23 +441,21 @@ class Downloads extends React.Component {
}
}
class Admin extends React.Component {
export default function Admin(props) {
render() {
const links = [
{text: 'Downloads', to: '/admin', exact: true},
{text: 'Settings', to: '/admin/settings'},
{text: 'WROL Mode', to: '/admin/wrol'},
];
const panes = [
{menuItem: 'Downloads', render: () => <Tab.Pane><Downloads/></Tab.Pane>},
{menuItem: 'Settings', render: () => <Tab.Pane><Settings/></Tab.Pane>},
{menuItem: 'WROL Mode', render: () => <Tab.Pane><WROLMode/></Tab.Pane>},
];
return (
<Container fluid>
<Tab panes={panes}/>
</Container>
)
}
return (
<>
<TabLinks links={links}/>
<Route path='/admin' exact component={Downloads}/>
<Route path='/admin/settings' exact component={Settings}/>
<Route path='/admin/wrol' exact component={WROLMode}/>
</>
)
}
export default Admin;

View file

@ -1,17 +1,17 @@
import React, {useState} from "react";
import {Card, Confirm, Container, Icon, Image, Placeholder, Tab} from "semantic-ui-react";
import {Card, Confirm, Container, Icon, Image, Placeholder} from "semantic-ui-react";
import Paginator, {
ClearButton,
ExternalLink,
SearchInput,
TabRouter,
TabLinks,
textEllipsis,
uploadDate,
WROLModeMessage
} from "./Common";
import {deleteArchive, postDownload, refreshArchives} from "../api";
import Button from "semantic-ui-react/dist/commonjs/elements/Button";
import {Link, NavLink} from "react-router-dom";
import {Link, Route} from "react-router-dom";
import {ArchivePlaceholder} from "./Placeholder";
import Table from "semantic-ui-react/dist/commonjs/collections/Table";
import {useArchives} from "../hooks/useArchives";
@ -298,40 +298,15 @@ function Domains() {
export function ArchiveRoute(props) {
const panes = [
{
menuItem: {
as: NavLink,
content: 'Archives',
id: 'archive',
to: '/archive',
exact: true,
key: 'home',
},
render: () => <Tab.Pane><Archives/></Tab.Pane>
},
{
menuItem: {
as: NavLink,
content: 'Domains',
id: 'domains',
to: '/archive/domains',
exact: true,
key: 'domains',
},
render: () => <Tab.Pane><Domains/></Tab.Pane>
},
{
menuItem: {
as: NavLink,
content: 'Manage',
id: 'manage',
to: '/archive/manage',
exact: true,
key: 'manage',
},
render: () => <Tab.Pane><ManageArchives/></Tab.Pane>
},
const links = [
{text: 'Archives', to: '/archive', exact: true},
{text: 'Domains', to: '/archive/domains'},
{text: 'Manage', to: '/archive/manage'},
];
return <TabRouter panes={panes} {...props} />
return <>
<TabLinks links={links}/>
<Route path='/archive' exact component={Archives}/>
<Route path='/archive/domains' component={Domains}/>
<Route path='/archive/manage' component={ManageArchives}/>
</>
}

View file

@ -1,6 +1,6 @@
import React, {useEffect, useState} from "react";
import {Card, Container, Form, Icon, Image, Input, Pagination, Progress, Tab} from 'semantic-ui-react';
import {Link} from "react-router-dom";
import {Card, Container, Form, Icon, Image, Input, Menu, Pagination, Progress} from 'semantic-ui-react';
import {Link, NavLink} from "react-router-dom";
import Button from "semantic-ui-react/dist/commonjs/elements/Button";
import Grid from "semantic-ui-react/dist/commonjs/collections/Grid";
import Message from "semantic-ui-react/dist/commonjs/collections/Message";
@ -636,61 +636,18 @@ export function textEllipsis(str, maxLength, {side = "end", ellipsis = "..."} =
return str;
}
export class TabRouter extends React.Component {
constructor(props) {
super(props);
const panes = props.panes;
let paneTos = [];
for (let i = 0; i < panes.length; i++) {
const pane = panes[i];
paneTos = paneTos.concat([pane['menuItem']['to']]);
}
this.state = {
activeIndex: this.matchPaneTo(paneTos),
paneTos: paneTos,
};
}
matchPaneTo = (paneTos) => {
const pathname = this.props.location.pathname;
// Attempt to find the exact match first.
let activeIndex = paneTos.indexOf(pathname);
if (activeIndex !== -1) {
return activeIndex;
}
// Use the first tab if we can't find one.
activeIndex = 0;
// Try to find the most-exact match.
for (let i = 0; i < paneTos.length; i++) {
let to = paneTos[i];
if (pathname.startsWith(to) && to.length > paneTos[activeIndex].length) {
// "to" is a better match because it is longer than the last one we found.
activeIndex = i;
}
}
return activeIndex;
}
componentDidUpdate(prevProps, prevState, snapshot) {
if (prevProps.location.pathname !== this.props.location.pathname) {
this.setState({activeIndex: this.matchPaneTo(this.state.paneTos)});
}
}
render() {
const {panes, renderActiveOnly} = this.props;
return (
<>
<Tab
panes={panes}
activeIndex={this.state.activeIndex}
renderActiveOnly={renderActiveOnly || true}
/>
</>
)
}
export function TabLinks({links}) {
return (
<Menu tabular>
{links.map((link) =>
<NavLink
to={link.to}
className={'item'}
exact={link.exact || false}
>
{link.text}
</NavLink>
)}
</Menu>
)
}

View file

@ -110,10 +110,8 @@ class Files extends React.Component {
}
}
export class FilesRoute extends React.Component {
render() {
return (
<Files/>
);
}
export function FilesRoute(props) {
return (
<Files/>
);
}

View file

@ -13,11 +13,10 @@ import {
updateInventory,
updateItem
} from "../api";
import {Button, Checkbox, Dropdown, Form, Grid, Header, Portal, Segment, Tab, Table} from "semantic-ui-react";
import {Button, Checkbox, Dropdown, Form, Grid, Header, Portal, Segment, Table} from "semantic-ui-react";
import {Route} from "react-router-dom";
import Container from "semantic-ui-react/dist/commonjs/elements/Container";
import {toast} from 'react-semantic-toasts';
import {arraysEqual, enumerate, replaceNullValues} from './Common';
import {arraysEqual, enumerate, replaceNullValues, TabLinks} from './Common';
import _ from 'lodash';
class InventorySummary extends React.Component {
@ -1089,27 +1088,16 @@ class InventoryAddList extends React.Component {
}
}
class InventoryTab extends React.Component {
render() {
let panes = [
{menuItem: 'List', render: () => <Tab.Pane><InventoryAddList/></Tab.Pane>},
{menuItem: 'Summary', render: () => <Tab.Pane><InventorySummary/></Tab.Pane>},
];
return (
<Tab panes={panes}/>
)
}
}
export class InventoryRoute extends React.Component {
render() {
return (
<>
<Container fluid>
<Route path='/inventory' exact component={InventoryTab}/>
</Container>
</>
)
}
export function InventoryRoute(props) {
const links = [
{text: 'List', to: '/inventory', exact: true},
{text: 'Summary', to: '/inventory/summary'},
];
return (
<>
<TabLinks links={links}/>
<Route path='/inventory' exact component={InventoryAddList}/>
<Route path='/inventory/summary' exact component={InventorySummary}/>
</>
)
}

View file

@ -5,14 +5,7 @@ import {Dropdown, Menu, Responsive} from "semantic-ui-react";
const responsiveWidth = 500;
const links = [
{
text: 'Videos', links: [
{to: '/videos', text: 'Newest Videos', exact: true},
{to: '/videos/favorites', text: 'Favorites', exact: true},
{to: '/videos/channel', text: 'Channels', exact: true},
{to: '/videos/manage', text: 'Manage', exact: true},
]
},
{text: 'Videos', to: '/videos'},
{text: 'Inventory', to: '/inventory', exact: true},
{text: 'Archive', to: '/archive'},
{text: 'Files', to: '/files'},
@ -33,7 +26,7 @@ function DropdownLinks(props) {
<Dropdown.Menu>
{props.link.links.map((l) => {
return (
<Link key={l.to} link={l}/>
<MenuLink key={l.to} link={l}/>
)
})}
</Dropdown.Menu>
@ -41,7 +34,8 @@ function DropdownLinks(props) {
)
}
function Link(props) {
function MenuLink(props) {
// Wrapper around NavLink to handle Navlink/Dropdown change.
let classes = 'item';
if (props.link.header) {
classes = `${classes} header`;
@ -68,17 +62,17 @@ export function NavBar() {
return (
<Menu>
{/*Always show WROLPi home button*/}
<Link link={{to: '/', text: 'WROLPi', exact: true}}/>
<MenuLink link={{to: '/', text: 'WROLPi', exact: true}}/>
{/*Show the links in a menu when on desktop*/}
{links.map((link) => {
return (
<Responsive minWidth={responsiveWidth} as={Link} link={link} key={link.to}/>
<Responsive minWidth={responsiveWidth} as={MenuLink} link={link} key={link.to}/>
)
})}
<Responsive minWidth={responsiveWidth} as={Menu.Menu} position="right">
{rightLinks.map((link) => {
return (<Link link={link} key={link.to}/>)
return (<MenuLink link={link} key={link.to}/>)
})}
</Responsive>
@ -87,7 +81,7 @@ export function NavBar() {
<Dropdown item icon="bars">
<Dropdown.Menu>
{collapsedLinks.map((link) => {
return (<Link link={link} key={link.to}/>)
return (<MenuLink link={link} key={link.to}/>)
})}
</Dropdown.Menu>
</Dropdown>

View file

@ -1,5 +1,5 @@
import React from 'react';
import {Link, Route} from "react-router-dom";
import {NavLink, Route} from "react-router-dom";
import Paginator, {
changePageHistory,
DEFAULT_LIMIT,
@ -10,8 +10,10 @@ import Paginator, {
scrollToTop,
searchOrders,
secondsToString,
TabLinks,
VideoCards,
videoOrders, WROLModeMessage
videoOrders,
WROLModeMessage
} from "./Common"
import VideoPage from "./VideoPlayer";
import {download, getChannel, getStatistics, getVideo, refresh, searchVideos} from "../api";
@ -27,8 +29,7 @@ import {
Modal,
Radio,
Segment,
Statistic,
Tab
Statistic
} from "semantic-ui-react";
import * as QueryString from 'query-string';
import Container from "semantic-ui-react/dist/commonjs/elements/Container";
@ -65,7 +66,6 @@ class ManageVideos extends React.Component {
return (
<Container fluid>
<WROLModeMessage content='Cannot modify Videos'/>
<Header as="h1">Manage Videos</Header>
<p>
<Button primary
@ -493,60 +493,77 @@ class Videos extends React.Component {
}
}
class ManageTabs extends React.Component {
render() {
const panes = [
{menuItem: 'Manage', render: () => <Tab.Pane><ManageVideos/></Tab.Pane>},
{menuItem: 'Statistics', render: () => <Tab.Pane><Statistics/></Tab.Pane>},
]
return (
<Container fluid>
<Tab panes={panes}/>
</Container>
)
}
function ManageTab(props) {
return (
<Container fluid>
<ManageVideos/>
<Statistics/>
</Container>
)
}
export class VideosRoute extends React.Component {
render() {
return (
<>
<Container fluid>
<Route path='/videos' exact
component={(i) =>
<Videos
match={i.match}
history={i.history}
location={i.location}
/>}
/>
<Route path='/videos/favorites' exact
component={(i) =>
<Videos
match={i.match}
history={i.history}
location={i.location}
filter='favorite'
header='Favorite Videos'
/>
}/>
<Route path='/videos/channel' exact component={Channels}/>
<Route path='/videos/manage' exact component={ManageTabs}/>
<Route path='/videos/channel/new' exact component={NewChannel}/>
<Route path='/videos/channel/:channel_link/edit' exact
component={(i) =>
<EditChannel
match={i.match}
history={i.history}
/>
}
/>
<Route path='/videos/channel/:channel_link/video' exact component={Videos}/>
</Container>
</>
)
function Link(props) {
let classes = 'item';
if (props.link.header) {
classes = `${classes} header`;
}
return (
<NavLink
className={classes}
exact={props.link.exact || false}
to={props.link.to}
>
{props.link.text}
</NavLink>
)
}
export function VideosRoute(props) {
const links = [
{text: 'Videos', to: '/videos', exact: true},
{text: 'Favorites', to: '/videos/favorites'},
{text: 'Channels', to: '/videos/channel',},
{text: 'Manage', to: '/videos/manage'},
];
return (
<>
<TabLinks links={links}/>
<Route path='/videos' exact
component={(i) =>
<Videos
match={i.match}
history={i.history}
location={i.location}
/>}
/>
<Route path='/videos/favorites' exact
component={(i) =>
<Videos
match={i.match}
history={i.history}
location={i.location}
filter='favorite'
header='Favorite Videos'
/>
}/>
<Route path='/videos/channel' exact component={Channels}/>
<Route path='/videos/manage' exact component={ManageTab}/>
<Route path='/videos/channel/new' exact component={NewChannel}/>
<Route path='/videos/channel/:channel_link/edit' exact
component={(i) =>
<EditChannel
match={i.match}
history={i.history}
/>
}
/>
<Route path='/videos/channel/:channel_link/video' exact component={Videos}/>
</>
)
}
class Statistics extends React.Component {