Implement a page navigation button

Improve page loading and theme system
- The sidebar is visible while loading with css
  There was an apparent problem when switching pages with the new switcher
- css variables are now used
- Theme color is applied to several buttons and texts
  where previously Plan color was used

Affects issues:
- Implements #2357
This commit is contained in:
Aurora Lahtela 2023-01-26 14:18:51 +02:00
parent b1a63d57b6
commit 362bb44aad
20 changed files with 522 additions and 397 deletions

View File

@ -4,7 +4,7 @@ import './style/style.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";
import {ThemeContextProvider} from "./hooks/themeHook"; import {NightModeCss, ThemeContextProvider, ThemeCss} from "./hooks/themeHook";
import axios from "axios"; import axios from "axios";
import ErrorView from "./views/ErrorView"; import ErrorView from "./views/ErrorView";
import {faMapSigns} from "@fortawesome/free-solid-svg-icons"; import {faMapSigns} from "@fortawesome/free-solid-svg-icons";
@ -99,8 +99,10 @@ function App() {
return ( return (
<div className="App"> <div className="App">
<ContextProviders> <ContextProviders>
<ThemeCss/>
<div id="wrapper"> <div id="wrapper">
<BrowserRouter basename={getBasename()}> <BrowserRouter basename={getBasename()}>
<NightModeCss/>
<Routes> <Routes>
<Route path="" element={<MainPageRedirect/>}/> <Route path="" element={<MainPageRedirect/>}/>
<Route path="/" element={<MainPageRedirect/>}/> <Route path="/" element={<MainPageRedirect/>}/>

View File

@ -227,7 +227,7 @@ const QueryOptionsCard = () => {
</Row> </Row>
</Card.Body> </Card.Body>
<button id={"query-button"} <button id={"query-button"}
className={"btn bg-plan m-2"} className={"btn bg-theme m-2"}
disabled={Boolean(invalidFields.length) || loadingResults} disabled={Boolean(invalidFields.length) || loadingResults}
onClick={performQuery}> onClick={performQuery}>
<FontAwesomeIcon icon={loadingResults ? faGear : faSearch} <FontAwesomeIcon icon={loadingResults ? faGear : faSearch}

View File

@ -31,9 +31,9 @@ const ColorSelectorModal = () => {
<button aria-label="Close" className="btn-close" onClick={theme.toggleColorChooser}/> <button aria-label="Close" className="btn-close" onClick={theme.toggleColorChooser}/>
</Modal.Header> </Modal.Header>
<Modal.Body style={{padding: "0.5rem 0 0.5rem 0.5rem"}}> <Modal.Body style={{padding: "0.5rem 0 0.5rem 0.5rem"}}>
{theme.themeColors.map((color, i) => {theme.themeColors.map(color =>
<ColorSelectorButton <ColorSelectorButton
key={i} key={color.name}
color={color.name} color={color.name}
setColor={theme.setColor} setColor={theme.setColor}
disabled={theme.nightModeEnabled} disabled={theme.nightModeEnabled}
@ -43,7 +43,7 @@ const ColorSelectorModal = () => {
<button className="btn" id="night-mode-toggle" type="button" onClick={theme.toggleNightMode}> <button className="btn" id="night-mode-toggle" type="button" onClick={theme.toggleNightMode}>
<Fa icon={faCloudMoon}/> {t('html.button.nightMode')} <Fa icon={faCloudMoon}/> {t('html.button.nightMode')}
</button> </button>
<button className="btn bg-plan" type="button" onClick={theme.toggleColorChooser}>OK</button> <button className="btn bg-theme" type="button" onClick={theme.toggleColorChooser}>OK</button>
</Modal.Footer> </Modal.Footer>
</Modal> </Modal>
) )

View File

@ -30,14 +30,14 @@ const LicenseSection = () => {
const Links = () => { const Links = () => {
const {t} = useTranslation(); const {t} = useTranslation();
return (<> return (<>
<a className="btn col-plan" href="https://github.com/plan-player-analytics/Plan/wiki" <a className="btn col-theme" href="https://github.com/plan-player-analytics/Plan/wiki"
rel="noopener noreferrer" target="_blank"> rel="noopener noreferrer" target="_blank">
<Fa icon={faGraduationCap}/> {t('html.modal.info.wiki')} <Fa icon={faGraduationCap}/> {t('html.modal.info.wiki')}
</a> </a>
<a className="btn col-plan" href="https://github.com/plan-player-analytics/Plan/issues" <a className="btn col-theme" href="https://github.com/plan-player-analytics/Plan/issues"
rel="noopener noreferrer" target="_blank"> rel="noopener noreferrer" target="_blank">
<Fa icon={faBug}/> {t('html.modal.info.bugs')}</a> <Fa icon={faBug}/> {t('html.modal.info.bugs')}</a>
<a className="btn col-plan" href="https://discord.gg/yXKmjzT" rel="noopener noreferrer" <a className="btn col-theme" href="https://discord.gg/yXKmjzT" rel="noopener noreferrer"
target="_blank"> target="_blank">
<Fa icon={faDiscord}/> {t('html.modal.info.discord')} <Fa icon={faDiscord}/> {t('html.modal.info.discord')}
</a> </a>
@ -75,7 +75,7 @@ const Contributions = () => {
// TODO Translate // TODO Translate
return (<> return (<>
<p>Player Analytics {t('html.modal.info.developer')} AuroraLS3.</p> <p>Player Analytics {t('html.modal.info.developer')} AuroraLS3.</p>
<p>In addition following <span className="col-plan">awesome people</span> have <p>In addition following <span className="col-theme">awesome people</span> have
contributed:</p> contributed:</p>
<ul className="row contributors"> <ul className="row contributors">
{contributors.map((contributor, i) => <Contributor key={i} contributor={contributor}/>)} {contributors.map((contributor, i) => <Contributor key={i} contributor={contributor}/>)}
@ -86,7 +86,7 @@ const Contributions = () => {
icon={faLanguage}/> {t('html.modal.info.contributors.translator')} icon={faLanguage}/> {t('html.modal.info.contributors.translator')}
</small> </small>
<hr/> <hr/>
<p className="col-plan"> <p className="col-theme">
{t('html.modal.info.contributors.donate')} {t('html.modal.info.contributors.donate')}
<Fa icon={faStar} className={"col-amber"}/> <Fa icon={faStar} className={"col-amber"}/>
</p> </p>
@ -98,19 +98,19 @@ const MetricsLinks = () => {
return ( return (
<> <>
<h6>{t('html.modal.info.metrics')}</h6> <h6>{t('html.modal.info.metrics')}</h6>
<a className="btn col-plan" href="https://bstats.org/plugin/bukkit/Plan" <a className="btn col-theme" href="https://bstats.org/plugin/bukkit/Plan"
rel="noopener noreferrer" target="_blank"> rel="noopener noreferrer" target="_blank">
<Fa icon={faChartArea}/> Bukkit <Fa icon={faChartArea}/> Bukkit
</a> </a>
<a className="btn col-plan" href="https://bstats.org/plugin/bungeecord/Plan" <a className="btn col-theme" href="https://bstats.org/plugin/bungeecord/Plan"
rel="noopener noreferrer" target="_blank"> rel="noopener noreferrer" target="_blank">
<Fa icon={faChartArea}/> BungeeCord <Fa icon={faChartArea}/> BungeeCord
</a> </a>
<a className="btn col-plan" href="https://bstats.org/plugin/sponge/plan" <a className="btn col-theme" href="https://bstats.org/plugin/sponge/plan"
rel="noopener noreferrer" target="_blank"> rel="noopener noreferrer" target="_blank">
<Fa icon={faChartArea}/> Sponge <Fa icon={faChartArea}/> Sponge
</a> </a>
<a className="btn col-plan" href="https://bstats.org/plugin/velocity/Plan/10326" <a className="btn col-theme" href="https://bstats.org/plugin/velocity/Plan/10326"
rel="noopener noreferrer" target="_blank"> rel="noopener noreferrer" target="_blank">
<Fa icon={faChartArea}/> Velocity <Fa icon={faChartArea}/> Velocity
</a> </a>
@ -138,7 +138,7 @@ const PluginInformationModal = ({open, toggle}) => {
<MetricsLinks/> <MetricsLinks/>
</Modal.Body> </Modal.Body>
<Modal.Footer> <Modal.Footer>
<button className="btn bg-plan" onClick={toggle}>OK</button> <button className="btn bg-theme" onClick={toggle}>OK</button>
</Modal.Footer> </Modal.Footer>
</Modal> </Modal>
) )

View File

@ -20,15 +20,15 @@ const UpdateAvailableModal = ({open, toggle, versionInfo}) => {
<p>You have version {versionInfo.currentVersion}.</p> <p>You have version {versionInfo.currentVersion}.</p>
<p>New <p>New
release: {versionInfo.newVersion}{versionInfo.isRelease ? '' : " (" + t('html.modal.version.dev') + ")"}</p> release: {versionInfo.newVersion}{versionInfo.isRelease ? '' : " (" + t('html.modal.version.dev') + ")"}</p>
<a className="btn col-plan" href={versionInfo.changelogUrl} rel="noopener noreferrer" target="_blank"> <a className="btn col-theme" href={versionInfo.changelogUrl} rel="noopener noreferrer" target="_blank">
{t('html.modal.version.changelog')} {t('html.modal.version.changelog')}
</a> </a>
<a className="btn col-plan" href={versionInfo.downloadUrl} rel="noopener noreferrer" target="_blank"> <a className="btn col-theme" href={versionInfo.downloadUrl} rel="noopener noreferrer" target="_blank">
{t('html.modal.version.download')} Plan-{versionInfo.newVersion}.jar {t('html.modal.version.download')} Plan-{versionInfo.newVersion}.jar
</a> </a>
</Modal.Body> </Modal.Body>
<Modal.Footer> <Modal.Footer>
<button className="btn bg-plan" onClick={toggle}>OK</button> <button className="btn bg-theme" onClick={toggle}>OK</button>
</Modal.Footer> </Modal.Footer>
</Modal> </Modal>
) )
@ -47,7 +47,7 @@ const NewestVersionModal = ({open, toggle, versionInfo}) => {
You're using the latest version {versionInfo.currentVersion}. (No updates available) You're using the latest version {versionInfo.currentVersion}. (No updates available)
</Modal.Body> </Modal.Body>
<Modal.Footer> <Modal.Footer>
<button className="btn bg-plan" onClick={toggle}>OK</button> <button className="btn bg-theme" onClick={toggle}>OK</button>
</Modal.Footer> </Modal.Footer>
</Modal> </Modal>
); );

View File

@ -0,0 +1,89 @@
import React, {useEffect, useState} from 'react';
import {faCompass} from "@fortawesome/free-solid-svg-icons";
import {useTranslation} from "react-i18next";
import {FontAwesomeIcon as Fa} from "@fortawesome/react-fontawesome";
import {InputGroup} from "react-bootstrap-v5";
import {useLocation, useNavigate} from "react-router-dom";
import {useMetadata} from "../../hooks/metadataHook";
import {useAuth} from "../../hooks/authenticationHook";
const PageNavigationItem = ({page}) => {
const {t} = useTranslation();
const navigate = useNavigate();
const location = useLocation();
const {authRequired, loggedIn, user} = useAuth();
const {networkMetadata} = useMetadata();
const [currentPage, setCurrentPage] = useState(undefined);
const [items, setItems] = useState([]);
useEffect(() => {
if (networkMetadata) {
let newItems = [
{id: 'network', displayName: t('html.label.network'), href: "/network", permission: 'page.network'},
{id: 'players', displayName: t('html.label.players'), href: "/players", permission: 'page.players'},
{
id: 'query',
displayName: t('html.query.title.text').replace('<', ''),
href: "/query",
permission: 'page.players'
},
...networkMetadata.servers
.filter(server => !server.proxy)
.map(server => {
return {
id: 'server-' + server.serverUUID,
displayName: t('html.label.server') + ', ' + server.serverName,
href: '/server/' + server.serverUUID,
permission: 'page.server'
}
})
];
if (page) {
newItems.unshift({id: 'page', displayName: page, href: location.pathname, permission: undefined})
}
if (authRequired && loggedIn) {
newItems = newItems.filter(item => item.permission && user.permissions.includes(item.permission))
}
setItems(newItems);
setCurrentPage(newItems.find(item => location.pathname.startsWith(item.href))?.id);
}
}, [t, networkMetadata, location, authRequired, loggedIn, user, page]);
const onSelect = ({target}) => {
const selected = target.value;
navigate(items.find(item => item.id === selected).href);
}
if (!currentPage || !items.length) {
return <li className={"nav-item nav-button nav-link"}
style={{
padding: "0.5rem",
paddingLeft: "1rem",
paddingRight: "1rem"
}}>
<p className={"p-0 m-0"}>&nbsp;</p>
</li>
}
return (
<li className={"nav-item nav-button nav-link"}
style={{padding: "1rem"}}>
<InputGroup>
<div className="input-group-text bg-theme col-white"
style={{paddingLeft: "0.5rem", paddingRight: "0.5rem"}}><Fa icon={faCompass}/></div>
<select onChange={onSelect}
aria-label="Page selector"
className="form-select form-select-sm scrollbar"
id="pageSelector"
defaultValue={currentPage}>
{items.map((item, i) =>
<option key={i} value={item.id}>{item.displayName}</option>)}
</select>
</InputGroup>
</li>
)
};
export default PageNavigationItem

View File

@ -1,7 +1,7 @@
import React, {useCallback, useEffect, useState} from "react"; import React, {useCallback, useEffect, useState} from "react";
import {FontAwesomeIcon as Fa} from "@fortawesome/react-fontawesome"; import {FontAwesomeIcon as Fa} from "@fortawesome/react-fontawesome";
import logo from '../../Flaticon_circle.png'; import logo from '../../Flaticon_circle.png';
import {faArrowLeft, faDoorOpen, faDownload, faPalette, faQuestionCircle} from "@fortawesome/free-solid-svg-icons"; import {faDoorOpen, faDownload, faPalette, faQuestionCircle} from "@fortawesome/free-solid-svg-icons";
import {NavLink, useLocation} from "react-router-dom"; import {NavLink, useLocation} from "react-router-dom";
import {useTheme} from "../../hooks/themeHook"; import {useTheme} from "../../hooks/themeHook";
import PluginInformationModal from "../modal/PluginInformationModal"; import PluginInformationModal from "../modal/PluginInformationModal";
@ -12,6 +12,7 @@ import {useNavigation} from "../../hooks/navigationHook";
import {useTranslation} from "react-i18next"; import {useTranslation} from "react-i18next";
import {Collapse} from "react-bootstrap-v5"; import {Collapse} from "react-bootstrap-v5";
import {baseAddress} from "../../service/backendConfiguration"; import {baseAddress} from "../../service/backendConfiguration";
import PageNavigationItem from "./PageNavigationItem";
const Logo = () => ( const Logo = () => (
<a className="sidebar-brand d-flex align-items-center justify-content-center" href="/"> <a className="sidebar-brand d-flex align-items-center justify-content-center" href="/">
@ -44,7 +45,7 @@ const InnerItem = ({href, icon, name, nameShort, color, external}) => {
</NavLink> </NavLink>
} }
const Item = ({item, inner}) => { export const Item = ({item, inner}) => {
const {setCurrentTab} = useNavigation(); const {setCurrentTab} = useNavigation();
const {pathname} = useLocation(); const {pathname} = useLocation();
const {t} = useTranslation(); const {t} = useTranslation();
@ -84,7 +85,7 @@ const Item = ({item, inner}) => {
const VersionButton = ({toggleVersionModal, versionInfo}) => { const VersionButton = ({toggleVersionModal, versionInfo}) => {
if (versionInfo.updateAvailable) { if (versionInfo.updateAvailable) {
return <button className="btn bg-white col-plan" onClick={toggleVersionModal}> return <button className="btn bg-white col-theme" onClick={toggleVersionModal}>
<Fa icon={faDownload}/> Update Available! <Fa icon={faDownload}/> Update Available!
</button>; </button>;
} }
@ -203,9 +204,8 @@ const renderItem = (item, i, openCollapse, setOpenCollapse, t) => {
return <hr key={i} className="sidebar-divider"/> return <hr key={i} className="sidebar-divider"/>
} }
const Sidebar = ({items, showBackButton}) => { const Sidebar = ({page, items}) => {
const {t} = useTranslation(); const {t} = useTranslation();
const {color} = useTheme();
const {currentTab, sidebarExpanded, setSidebarExpanded} = useNavigation(); const {currentTab, sidebarExpanded, setSidebarExpanded} = useNavigation();
const [openCollapse, setOpenCollapse] = useState(undefined); const [openCollapse, setOpenCollapse] = useState(undefined);
@ -226,13 +226,10 @@ const Sidebar = ({items, showBackButton}) => {
return ( return (
<> <>
{sidebarExpanded && {sidebarExpanded &&
<ul className={"navbar-nav sidebar sidebar-dark accordion bg-" + color} id="accordionSidebar"> <ul className={"navbar-nav sidebar sidebar-dark accordion bg-theme"} id="accordionSidebar">
<Logo/> <Logo/>
<Divider/> <PageNavigationItem page={page}/>
{showBackButton && <> <Divider showMargin={items.length && !items[0].contents && items[0].href === undefined}/>
<Item active={false} item={{href: "/", icon: faArrowLeft, name: t('html.label.toMainPage')}}/>
<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)) : ''}
<Divider/> <Divider/>
<FooterButtons/> <FooterButtons/>

View File

@ -1,7 +1,8 @@
import {createContext, useContext, useState} from "react"; import {createContext, useContext, useMemo, useState} from "react";
import {createNightModeCss, getColors} from "../util/colors"; import {createNightModeCss, getColors} from "../util/colors";
import {getLightModeChartTheming, getNightModeChartTheming} from "../util/graphColors"; import {getLightModeChartTheming, getNightModeChartTheming} from "../util/graphColors";
import {useMetadata} from "./metadataHook"; import {useMetadata} from "./metadataHook";
import {useLocation} from "react-router-dom";
const themeColors = getColors(); const themeColors = getColors();
themeColors.splice(themeColors.length - 4, 4); themeColors.splice(themeColors.length - 4, 4);
@ -40,14 +41,13 @@ export const ThemeContextProvider = ({children}) => {
const [selectedColor, setSelectedColor] = useState(getStoredTheme(getDefaultTheme(metadata))); const [selectedColor, setSelectedColor] = useState(getStoredTheme(getDefaultTheme(metadata)));
const [previousColor, setPreviousColor] = useState(undefined); const [previousColor, setPreviousColor] = useState(undefined);
const sharedState = { const sharedState = useMemo(() => {
selectedColor, return {
setSelectedColor, selectedColor, setSelectedColor,
previousColor, previousColor, setPreviousColor,
setPreviousColor, colorChooserOpen, setColorChooserOpen
colorChooserOpen, }
setColorChooserOpen }, [selectedColor, setSelectedColor, previousColor, setPreviousColor, colorChooserOpen, setColorChooserOpen]);
}
return (<ThemeContext.Provider value={sharedState}> return (<ThemeContext.Provider value={sharedState}>
{children} {children}
</ThemeContext.Provider> </ThemeContext.Provider>
@ -107,8 +107,29 @@ export const useTheme = () => {
}; };
} }
export const ThemeCss = () => {
const {color} = useTheme();
if (!color) return <></>;
return (
<style>
{':root {--color-theme: var(--color-' + color + ') !important;}'}
</style>
)
}
export const NightModeCss = () => { export const NightModeCss = () => {
const theme = useTheme(); const theme = useTheme();
const location = useLocation();
if (location.pathname.startsWith('/docs')) {
return <>
<style>
{'#wrapper {background-image: none;}'}
</style>
</>
}
return ( return (
<> <>

View File

@ -1670,22 +1670,10 @@ select.form-control:focus::-ms-value {
background-color: #858796 !important; background-color: #858796 !important;
} }
.bg-success {
background-color: #1cc88a !important;
}
.bg-info { .bg-info {
background-color: #36b9cc !important; background-color: #36b9cc !important;
} }
.bg-warning {
background-color: #f6c23e !important;
}
.bg-danger {
background-color: #e74a3b !important;
}
.bg-light { .bg-light {
background-color: #f8f9fc !important; background-color: #f8f9fc !important;
} }
@ -1866,11 +1854,6 @@ a.text-dark:hover, a.text-dark:focus {
} }
} }
#wrapper {
display: flex;
min-height: 100vh;
}
#wrapper #content-wrapper { #wrapper #content-wrapper {
background-color: #f8f9fc; background-color: #f8f9fc;
width: 100%; width: 100%;
@ -2575,145 +2558,145 @@ a.text-dark:hover, a.text-dark:focus {
} }
/*@media (min-width: 768px) { Commented because mobile should have full width sidebar when open. */ /*@media (min-width: 768px) { Commented because mobile should have full width sidebar when open. */
.sidebar { .sidebar {
width: 14rem !important; width: 14rem !important;
} }
.sidebar .nav-item .collapse { .sidebar .nav-item .collapse {
position: relative; position: relative;
left: 0; left: 0;
z-index: 1; z-index: 1;
top: 0; top: 0;
-webkit-animation: none; -webkit-animation: none;
animation: none; animation: none;
} }
.sidebar .nav-item .collapse .collapse-inner { .sidebar .nav-item .collapse .collapse-inner {
border-radius: 0; border-radius: 0;
box-shadow: none; box-shadow: none;
} }
.sidebar .nav-item .collapsing { .sidebar .nav-item .collapsing {
display: block; display: block;
transition: height 0.15s ease; transition: height 0.15s ease;
} }
.sidebar .nav-item .collapse, .sidebar .nav-item .collapse,
.sidebar .nav-item .collapsing { .sidebar .nav-item .collapsing {
margin: 0 1rem; margin: 0 1rem;
} }
.sidebar .nav-item .nav-link { .sidebar .nav-item .nav-link {
display: block; display: block;
width: 14rem; width: 14rem;
text-align: left; text-align: left;
padding: 1rem; padding: 1rem;
} }
.sidebar .nav-item .nav-link i, .sidebar .nav-item .nav-link svg { .sidebar .nav-item .nav-link i, .sidebar .nav-item .nav-link svg {
font-size: 0.85rem; font-size: 0.85rem;
margin-right: 0.25rem; margin-right: 0.25rem;
} }
.sidebar .nav-item .nav-link span { .sidebar .nav-item .nav-link span {
font-size: 0.85rem; font-size: 0.85rem;
display: inline; display: inline;
} }
.sidebar .nav-item .nav-link[data-bs-toggle="collapse"]::after { .sidebar .nav-item .nav-link[data-bs-toggle="collapse"]::after {
width: 1rem; width: 1rem;
text-align: center; text-align: center;
float: right; float: right;
vertical-align: 0; vertical-align: 0;
border: 0; border: 0;
font-weight: 900; font-weight: 900;
content: '\f107'; content: '\f107';
font-family: 'Font Awesome 5 Free'; font-family: 'Font Awesome 5 Free';
} }
.sidebar .nav-item .nav-link[data-bs-toggle="collapse"].collapsed::after { .sidebar .nav-item .nav-link[data-bs-toggle="collapse"].collapsed::after {
content: '\f105'; content: '\f105';
} }
.sidebar .sidebar-brand .sidebar-brand-icon i { .sidebar .sidebar-brand .sidebar-brand-icon i {
font-size: 2rem; font-size: 2rem;
} }
.sidebar .sidebar-brand .sidebar-brand-text { .sidebar .sidebar-brand .sidebar-brand-text {
display: inline; display: inline;
} }
.sidebar .sidebar-heading { .sidebar .sidebar-heading {
text-align: left; text-align: left;
} }
.sidebar.toggled { .sidebar.toggled {
overflow: visible; overflow: visible;
width: 6.5rem !important; width: 6.5rem !important;
} }
.sidebar.toggled .nav-item .collapse { .sidebar.toggled .nav-item .collapse {
position: absolute; position: absolute;
left: calc(6.5rem + 1.5rem / 2); left: calc(6.5rem + 1.5rem / 2);
z-index: 1; z-index: 1;
top: 2px; top: 2px;
-webkit-animation-name: growIn; -webkit-animation-name: growIn;
animation-name: growIn; animation-name: growIn;
-webkit-animation-duration: 200ms; -webkit-animation-duration: 200ms;
animation-duration: 200ms; animation-duration: 200ms;
-webkit-animation-timing-function: transform cubic-bezier(0.18, 1.25, 0.4, 1), opacity cubic-bezier(0, 1, 0.4, 1); -webkit-animation-timing-function: transform cubic-bezier(0.18, 1.25, 0.4, 1), opacity cubic-bezier(0, 1, 0.4, 1);
animation-timing-function: transform cubic-bezier(0.18, 1.25, 0.4, 1), opacity cubic-bezier(0, 1, 0.4, 1); animation-timing-function: transform cubic-bezier(0.18, 1.25, 0.4, 1), opacity cubic-bezier(0, 1, 0.4, 1);
} }
.sidebar.toggled .nav-item .collapse .collapse-inner { .sidebar.toggled .nav-item .collapse .collapse-inner {
box-shadow: 0 0.15rem 1.75rem 0 rgba(58, 59, 69, 0.15); box-shadow: 0 0.15rem 1.75rem 0 rgba(58, 59, 69, 0.15);
border-radius: 0.35rem; border-radius: 0.35rem;
} }
.sidebar.toggled .nav-item .collapsing { .sidebar.toggled .nav-item .collapsing {
display: none; display: none;
transition: none; transition: none;
} }
.sidebar.toggled .nav-item .collapse, .sidebar.toggled .nav-item .collapse,
.sidebar.toggled .nav-item .collapsing { .sidebar.toggled .nav-item .collapsing {
margin: 0; margin: 0;
} }
.sidebar.toggled .nav-item:last-child { .sidebar.toggled .nav-item:last-child {
margin-bottom: 1rem; margin-bottom: 1rem;
} }
.sidebar.toggled .nav-item .nav-link { .sidebar.toggled .nav-item .nav-link {
text-align: center; text-align: center;
padding: 0.75rem 1rem; padding: 0.75rem 1rem;
width: 6.5rem; width: 6.5rem;
} }
.sidebar.toggled .nav-item .nav-link span { .sidebar.toggled .nav-item .nav-link span {
font-size: 0.65rem; font-size: 0.65rem;
display: block; display: block;
} }
.sidebar.toggled .nav-item .nav-link i, .sidebar.toggled .nav-item .nav-link svg { .sidebar.toggled .nav-item .nav-link i, .sidebar.toggled .nav-item .nav-link svg {
margin-right: 0; margin-right: 0;
} }
.sidebar.toggled .nav-item .nav-link[data-bs-toggle="collapse"]::after { .sidebar.toggled .nav-item .nav-link[data-bs-toggle="collapse"]::after {
display: none; display: none;
} }
.sidebar.toggled .sidebar-brand .sidebar-brand-icon i { .sidebar.toggled .sidebar-brand .sidebar-brand-icon i {
font-size: 2rem; font-size: 2rem;
} }
.sidebar.toggled .sidebar-brand .sidebar-brand-text { .sidebar.toggled .sidebar-brand .sidebar-brand-text {
display: none; display: none;
} }
.sidebar.toggled .sidebar-heading { .sidebar.toggled .sidebar-heading {
text-align: center; text-align: center;
} }
/*}*/ /*}*/

View File

@ -1,3 +1,44 @@
:root {
--color-red: #F44336;
--color-pink: #E91E63;
--color-purple: #9C27B0;
--color-deep-purple: #673AB7;
--color-indigo: #3F51B5;
--color-blue: #2196F3;
--color-light-blue: #03A9F4;
--color-cyan: #00BCD4;
--color-teal: #009688;
--color-green: #4CAF50;
--color-light-green: #8BC34A;
--color-lime: #CDDC39;
--color-yellow: #ffe821;
--color-amber: #FFC107;
--color-orange: #FF9800;
--color-deep-orange: #FF5722;
--color-brown: #795548;
--color-grey: #9E9E9E;
--color-blue-grey: #607D8B;
--color-black: #555555;
--color-white: #ffffff;
--color-plan: #368F17;
--color-text-light-bg: #333;
--color-text-dark-bg: #fff;
--color-text-dark-bg-disabled: #ccc;
--color-success: #1CC88A;
--color-warning: #F6C23E;
--color-danger: #e74A3B;
--color-night-black: #282a36;
--color-night-dark-blue: #44475a;
--color-night-blue: #6272a4;
--color-night-grey-blue: #646e8c;
--color-night-dark-grey-blue: #606270;
--color-night-text-dark-bg: #eee8d5;
--color-theme: var(--color-plan);
}
a { a {
text-decoration: none; text-decoration: none;
} }
@ -132,9 +173,9 @@ p.collapsing {
display: inline-block; display: inline-block;
width: 2rem; width: 2rem;
height: 2rem; height: 2rem;
border: 4px solid #368F17; border: 4px solid var(--color-theme);
background-color: var(--color-theme);
border-radius: 5px; border-radius: 5px;
background-color: #368F17;
animation: loader 2s infinite ease; animation: loader 2s infinite ease;
} }
@ -767,135 +808,153 @@ div#navSrvContainer::-webkit-scrollbar-thumb {
} }
.bg-red, body.theme-red .fc-toolbar-chunk .btn.btn-primary { .bg-red, body.theme-red .fc-toolbar-chunk .btn.btn-primary {
background-color: #F44336; background-color: var(--color-red);
--bs-btn-disabled-bg: #F44336; --bs-btn-disabled-bg: var(--color-red);
color: #fff; color: var(--color-text-dark-bg);
} }
.bg-pink, body.theme-pink .fc-toolbar-chunk .btn.btn-primary { .bg-pink, body.theme-pink .fc-toolbar-chunk .btn.btn-primary {
background-color: #E91E63; background-color: var(--color-pink);
--bs-btn-disabled-bg: #E91E63; --bs-btn-disabled-bg: var(--color-pink);
color: #fff; color: var(--color-text-dark-bg);
} }
.bg-purple, body.theme-purple .fc-toolbar-chunk .btn.btn-primary { .bg-purple, body.theme-purple .fc-toolbar-chunk .btn.btn-primary {
background-color: #9C27B0; background-color: var(--color-purple);
--bs-btn-disabled-bg: #9C27B0; --bs-btn-disabled-bg: var(--color-purple);
color: #fff; color: var(--color-text-dark-bg);
} }
.bg-deep-purple, body.theme-deep-purple .fc-toolbar-chunk .btn.btn-primary { .bg-deep-purple, body.theme-deep-purple .fc-toolbar-chunk .btn.btn-primary {
background-color: #673AB7; background-color: var(--color-deep-purple);
--bs-btn-disabled-bg: #673AB7; --bs-btn-disabled-bg: var(--color-deep-purple);
color: #fff; color: var(--color-text-dark-bg);
} }
.bg-indigo, body.theme-indigo .fc-toolbar-chunk .btn.btn-primary { .bg-indigo, body.theme-indigo .fc-toolbar-chunk .btn.btn-primary {
background-color: #3F51B5; background-color: var(--color-indigo);
--bs-btn-disabled-bg: #3F51B5; --bs-btn-disabled-bg: var(--color-indigo);
color: #fff; color: var(--color-text-dark-bg);
} }
.bg-blue, body.theme-blue .fc-toolbar-chunk .btn.btn-primary { .bg-blue, body.theme-blue .fc-toolbar-chunk .btn.btn-primary {
background-color: #2196F3; background-color: var(--color-blue);
--bs-btn-disabled-bg: #2196F3; --bs-btn-disabled-bg: var(--color-blue);
color: #fff; color: var(--color-text-dark-bg);
} }
.bg-light-blue, body.theme-light-blue .fc-toolbar-chunk .btn.btn-primary { .bg-light-blue, body.theme-light-blue .fc-toolbar-chunk .btn.btn-primary {
background-color: #03A9F4; background-color: var(--color-light-blue);
--bs-btn-disabled-bg: #03A9F4; --bs-btn-disabled-bg: var(--color-light-blue);
color: #fff; color: var(--color-text-dark-bg);
} }
.bg-cyan, body.theme-cyan .fc-toolbar-chunk .btn.btn-primary { .bg-cyan, body.theme-cyan .fc-toolbar-chunk .btn.btn-primary {
background-color: #00BCD4; background-color: var(--color-cyan);
--bs-btn-disabled-bg: #00BCD4; --bs-btn-disabled-bg: var(--color-cyan);
color: #fff; color: var(--color-text-dark-bg);
} }
.bg-teal, body.theme-teal .fc-toolbar-chunk .btn.btn-primary { .bg-teal, body.theme-teal .fc-toolbar-chunk .btn.btn-primary {
background-color: #009688; background-color: var(--color-teal);
--bs-btn-disabled-bg: #009688; --bs-btn-disabled-bg: var(--color-teal);
color: #fff; color: var(--color-text-dark-bg);
} }
.bg-green, body.theme-green .fc-toolbar-chunk .btn.btn-primary { .bg-green, body.theme-green .fc-toolbar-chunk .btn.btn-primary {
background-color: #4CAF50; background-color: var(--color-green);
--bs-btn-disabled-bg: #4CAF50; --bs-btn-disabled-bg: var(--color-green);
color: #fff; color: var(--color-text-dark-bg);
} }
.bg-light-green, body.theme-light-green .fc-toolbar-chunk .btn.btn-primary { .bg-light-green, body.theme-light-green .fc-toolbar-chunk .btn.btn-primary {
background-color: #8BC34A; background-color: var(--color-light-green);
--bs-btn-disabled-bg: #8BC34A; --bs-btn-disabled-bg: var(--color-light-green);
color: #fff; color: var(--color-text-dark-bg);
} }
.bg-lime, body.theme-lime .fc-toolbar-chunk .btn.btn-primary { .bg-lime, body.theme-lime .fc-toolbar-chunk .btn.btn-primary {
background-color: #CDDC39; background-color: var(--color-lime);
--bs-btn-disabled-bg: #CDDC39; --bs-btn-disabled-bg: var(--color-lime);
color: #fff; color: var(--color-text-dark-bg);
} }
.bg-yellow, body.theme-yellow .fc-toolbar-chunk .btn.btn-primary { .bg-yellow, body.theme-yellow .fc-toolbar-chunk .btn.btn-primary {
background-color: #ffe821; background-color: var(--color-yellow);
--bs-btn-disabled-bg: #ffe821; --bs-btn-disabled-bg: var(--color-yellow);
color: #fff; color: var(--color-text-dark-bg);
} }
.bg-amber, body.theme-amber .fc-toolbar-chunk .btn.btn-primary { .bg-amber, body.theme-amber .fc-toolbar-chunk .btn.btn-primary {
background-color: #FFC107; background-color: var(--color-amber);
--bs-btn-disabled-bg: #FFC107; --bs-btn-disabled-bg: var(--color-amber);
color: #fff; color: var(--color-text-dark-bg);
} }
.bg-orange, body.theme-orange .fc-toolbar-chunk .btn.btn-primary { .bg-orange, body.theme-orange .fc-toolbar-chunk .btn.btn-primary {
background-color: #FF9800; background-color: var(--color-orange);
--bs-btn-disabled-bg: #FF9800; --bs-btn-disabled-bg: var(--color-orange);
color: #fff; color: var(--color-text-dark-bg);
} }
.bg-deep-orange, body.theme-deep-orange .fc-toolbar-chunk .btn.btn-primary { .bg-deep-orange, body.theme-deep-orange .fc-toolbar-chunk .btn.btn-primary {
background-color: #FF5722; background-color: var(--color-deep-orange);
--bs-btn-disabled-bg: #FF5722; --bs-btn-disabled-bg: var(--color-deep-orange);
color: #fff; color: var(--color-text-dark-bg);
} }
.bg-brown, body.theme-brown .fc-toolbar-chunk .btn.btn-primary { .bg-brown, body.theme-brown .fc-toolbar-chunk .btn.btn-primary {
background-color: #795548; background-color: var(--color-brown);
--bs-btn-disabled-bg: #795548; --bs-btn-disabled-bg: var(--color-brown);
color: #fff; color: var(--color-text-dark-bg);
} }
.bg-grey, body.theme-grey .fc-toolbar-chunk .btn.btn-primary { .bg-grey, body.theme-grey .fc-toolbar-chunk .btn.btn-primary {
background-color: #9E9E9E; background-color: var(--color-grey);
--bs-btn-disabled-bg: #9E9E9E; --bs-btn-disabled-bg: var(--color-grey);
color: #fff; color: var(--color-text-dark-bg);
} }
.bg-blue-grey, body.theme-blue-grey .fc-toolbar-chunk .btn.btn-primary { .bg-blue-grey, body.theme-blue-grey .fc-toolbar-chunk .btn.btn-primary {
background-color: #607D8B; background-color: var(--color-blue-grey);
--bs-btn-disabled-bg: #607D8B; --bs-btn-disabled-bg: var(--color-blue-grey);
color: #fff; color: var(--color-text-dark-bg);
} }
.bg-black, body.theme-black .fc-toolbar-chunk .btn.btn-primary { .bg-black, body.theme-black .fc-toolbar-chunk .btn.btn-primary {
background-color: #555555; background-color: var(--color-black);
--bs-btn-disabled-bg: #555555; --bs-btn-disabled-bg: var(--color-black);
color: #fff; color: var(--color-text-dark-bg);
} }
.bg-white, body.theme-white .fc-toolbar-chunk .btn.btn-primary { .bg-white, body.theme-white .fc-toolbar-chunk .btn.btn-primary {
background-color: #ffffff; background-color: var(--color-white);
--bs-btn-disabled-bg: #ffffff; --bs-btn-disabled-bg: var(--color-white);
color: #333; color: var(--color-text-light-bg);
} }
.bg-plan, body.theme-plan .fc-toolbar-chunk .btn.btn-primary { .bg-plan, body.theme-plan .fc-toolbar-chunk .btn.btn-primary {
background-color: #368F17; background-color: var(--color-plan);
--bs-btn-disabled-bg: #368F17; --bs-btn-disabled-bg: var(--color-plan);
color: #fff; color: var(--color-text-dark-bg);
}
.bg-theme, body.theme .fc-toolbar-chunk .btn.btn-primary {
background-color: var(--color-theme);
--bs-btn-disabled-bg: var(--color-theme);
color: var(--color-text-dark-bg);
}
.bg-success {
background-color: var(--color-success) !important;
}
.bg-warning {
background-color: var(--color-warning) !important;
}
.bg-danger {
background-color: var(--color-danger) !important;
} }
.btn.bg-plan:hover, .btn.bg-plan:hover,
@ -920,14 +979,14 @@ div#navSrvContainer::-webkit-scrollbar-thumb {
.btn.bg-grey:hover, .btn.bg-grey:hover,
.btn.bg-blue-grey:hover, .btn.bg-blue-grey:hover,
.btn.bg-transparent-light:hover { .btn.bg-transparent-light:hover {
color: #ccc !important; color: var(--color-text-dark-bg-disabled) !important;
background-color: var(--bs-btn-disabled-bg); background-color: var(--bs-btn-disabled-bg);
} }
.bg-night, body.theme-night .fc-toolbar-chunk .btn.btn-primary { .bg-night, body.theme-night .fc-toolbar-chunk .btn.btn-primary {
background-color: #44475a; background-color: var(--color-night-dark-blue);
--bs-btn-disabled-bg: #44475a; --bs-btn-disabled-bg: var(--color-night-dark-blue);
color: #eee8d5; color: var(--color-night-text-dark-bg);
} }
.fc-toolbar-chunk .btn.btn-primary { .fc-toolbar-chunk .btn.btn-primary {
@ -936,210 +995,210 @@ div#navSrvContainer::-webkit-scrollbar-thumb {
} }
.bg-red-outline { .bg-red-outline {
border-color: #F44336; border-color: var(--color-red);
border-style: solid; border-style: solid;
outline: #F44336 solid 1px; outline: var(--color-red) solid 1px;
} }
.bg-pink-outline { .bg-pink-outline {
border-color: #E91E63; border-color: var(--color-pink);
border-style: solid; border-style: solid;
outline: #E91E63 solid 1px; outline: var(--color-pink) solid 1px;
} }
.bg-purple-outline { .bg-purple-outline {
border-color: #9C27B0; border-color: var(--color-purple);
border-style: solid; border-style: solid;
outline: #9C27B0 solid 1px; outline: var(--color-purple) solid 1px;
} }
.bg-deep-purple-outline { .bg-deep-purple-outline {
border-color: #673AB7; border-color: var(--color-deep-purple);
border-style: solid; border-style: solid;
outline: #673AB7 solid 1px; outline: var(--color-deep-purple) solid 1px;
} }
.bg-indigo-outline { .bg-indigo-outline {
border-color: #3F51B5; border-color: var(--color-indigo);
border-style: solid; border-style: solid;
outline: #3F51B5 solid 1px; outline: var(--color-indigo) solid 1px;
} }
.bg-blue-outline { .bg-blue-outline {
border-color: #2196F3; border-color: var(--color-blue);
border-style: solid; border-style: solid;
outline: #2196F3 solid 1px; outline: var(--color-blue) solid 1px;
} }
.bg-light-blue-outline { .bg-light-blue-outline {
border-color: #03A9F4; border-color: var(--color-light-blue);
border-style: solid; border-style: solid;
outline: #03A9F4 solid 1px; outline: var(--color-light-blue) solid 1px;
} }
.bg-cyan-outline { .bg-cyan-outline {
border-color: #00BCD4; border-color: var(--color-cyan);
border-style: solid; border-style: solid;
outline: #00BCD4 solid 1px; outline: var(--color-cyan) solid 1px;
} }
.bg-teal-outline { .bg-teal-outline {
border-color: #009688; border-color: var(--color-teal);
border-style: solid; border-style: solid;
border-width: 3px; border-width: 3px;
outline: #009688 solid 1px; outline: var(--color-teal) solid 1px;
} }
.bg-green-outline { .bg-green-outline {
border-color: #4CAF50; border-color: var(--color-green);
border-style: solid; border-style: solid;
outline: #4CAF50 solid 1px; outline: var(--color-green) solid 1px;
} }
.bg-light-green-outline { .bg-light-green-outline {
border-color: #8BC34A; border-color: var(--color-light-green);
border-style: solid; border-style: solid;
outline: #8BC34A solid 1px; outline: var(--color-light-green) solid 1px;
} }
.bg-lime-outline { .bg-lime-outline {
border-color: #CDDC39; border-color: var(--color-lime);
border-style: solid; border-style: solid;
outline: #CDDC39 solid 1px; outline: var(--color-lime) solid 1px;
} }
.bg-yellow-outline { .bg-yellow-outline {
border-color: #ffe821; border-color: var(--color-yellow);
border-style: solid; border-style: solid;
outline: #ffe821 solid 1px; outline: var(--color-yellow) solid 1px;
} }
.bg-amber-outline { .bg-amber-outline {
border-color: #FFC107; border-color: var(--color-amber);
border-style: solid; border-style: solid;
outline: #FFC107 solid 1px; outline: var(--color-amber) solid 1px;
} }
.bg-orange-outline { .bg-orange-outline {
border-color: #FF9800; border-color: var(--color-orange);
border-style: solid; border-style: solid;
outline: #FF9800 solid 1px; outline: var(--color-orange) solid 1px;
} }
.bg-deep-orange-outline { .bg-deep-orange-outline {
border-color: #FF5722; border-color: var(--color-deep-orange);
border-style: solid; border-style: solid;
outline: #FF5722 solid 1px; outline: var(--color-deep-orange) solid 1px;
} }
.bg-brown-outline { .bg-brown-outline {
border-color: #795548; border-color: var(--color-brown);
border-style: solid; border-style: solid;
outline: #795548 solid 1px; outline: var(--color-brown) solid 1px;
} }
.bg-grey-outline { .bg-grey-outline {
border-color: #9E9E9E; border-color: var(--color-grey);
border-style: solid; border-style: solid;
outline: #9E9E9E solid 1px; outline: var(--color-grey) solid 1px;
} }
.bg-blue-grey-outline { .bg-blue-grey-outline {
border-color: #607D8B; border-color: var(--color-blue-grey);
border-style: solid; border-style: solid;
outline: #607D8B solid 1px; outline: var(--color-blue-grey) solid 1px;
} }
.bg-black-outline { .bg-black-outline {
border-color: #555555; border-color: var(--color-black);
border-style: solid; border-style: solid;
outline: #555555 solid 1px; outline: var(--color-black) solid 1px;
} }
.bg-plan-outline { .bg-plan-outline {
border-color: #368F17; border-color: var(--color-plan);
border-style: solid; border-style: solid;
outline: #368F17 solid 1px; outline: var(--color-plan) solid 1px;
} }
.col-red { .col-red {
color: #F44336; color: var(--color-red);
} }
.col-pink { .col-pink {
color: #E91E63; color: var(--color-pink);
} }
.col-purple { .col-purple {
color: #9C27B0; color: var(--color-purple);
} }
.col-deep-purple { .col-deep-purple {
color: #673AB7; color: var(--color-deep-purple);
} }
.col-indigo { .col-indigo {
color: #3F51B5; color: var(--color-indigo);
} }
.col-blue { .col-blue {
color: #2196F3; color: var(--color-blue);
} }
.col-light-blue { .col-light-blue {
color: #03A9F4; color: var(--color-light-blue);
} }
.col-cyan { .col-cyan {
color: #00BCD4; color: var(--color-cyan);
} }
.col-teal { .col-teal {
color: #009688; color: var(--color-teal);
} }
.col-green { .col-green {
color: #4CAF50; color: var(--color-green);
} }
.col-light-green { .col-light-green {
color: #8BC34A; color: var(--color-light-green);
} }
.col-lime { .col-lime {
color: #CDDC39; color: var(--color-lime);
} }
.col-yellow { .col-yellow {
color: #ffe821; color: var(--color-yellow);
} }
.col-amber { .col-amber {
color: #FFC107; color: var(--color-amber);
} }
.col-orange { .col-orange {
color: #FF9800; color: var(--color-orange);
} }
.col-deep-orange { .col-deep-orange {
color: #FF5722; color: var(--color-deep-orange);
} }
.col-brown { .col-brown {
color: #795548; color: var(--color-brown);
} }
.col-grey { .col-grey {
color: #9E9E9E; color: var(--color-grey);
} }
.col-blue-grey { .col-blue-grey {
color: #607D8B; color: var(--color-blue-grey);
} }
.col-black { .col-black {
color: #555555; color: var(--color-black);
} }
.col-white { .col-white {
@ -1147,7 +1206,11 @@ div#navSrvContainer::-webkit-scrollbar-thumb {
} }
.col-plan { .col-plan {
color: #368F17; color: var(--color-plan);
}
.col-theme {
color: var(--color-theme);
} }
/* Minecraft color codes */ /* Minecraft color codes */
@ -1184,7 +1247,7 @@ div#navSrvContainer::-webkit-scrollbar-thumb {
} }
.darkgray { .darkgray {
color: #555555; color: var(--color-black);
} }
.blue { .blue {
@ -1355,4 +1418,10 @@ button, input[type="submit"], input[type="reset"] {
ul.filters { ul.filters {
list-style: none; list-style: none;
padding: 0; padding: 0;
}
#wrapper {
display: flex;
min-height: 100vh;
background-image: linear-gradient(to right, var(--color-theme) 0%, var(--color-theme) 14rem, #f8f9fc 14.01rem, #f8f9fc 100%);
} }

View File

@ -164,40 +164,31 @@ export const withReducedSaturation = hex => {
return 'hsl(' + h * 360 + ',' + s * 100 * saturationReduction + '%,' + l * 95 + '%)'; return 'hsl(' + h * 360 + ',' + s * 100 * saturationReduction + '%,' + l * 95 + '%)';
} }
const nightColors = {
yellow: "#eee8d5",
black: "#282a36",
darkBlue: "#44475a",
blue: "#6272a4",
greyBlue: "#646e8c",
darkGreyBlue: "#606270"
}
const createNightModeColorCss = () => { const createNightModeColorCss = () => {
return getColors() return ':root {' + getColors()
.filter(color => color.name !== 'white' && color.name !== 'black' && color.name !== 'plan') .filter(color => color.name !== 'white' && color.name !== 'black' && color.name !== 'plan')
.map(color => { .map(color => {
const desaturatedColor = withReducedSaturation(color.hex); const desaturatedColor = withReducedSaturation(color.hex);
return `.bg-${color.name}{background-color: ${desaturatedColor} !important;color: ${nightColors.yellow};}` + return `--color-${color.name}: ${desaturatedColor} !important;`
`.bg-${color.name}-outline{outline-color: ${desaturatedColor};border-color: ${desaturatedColor};}` + }).join('') + '}';
`.col-${color.name}{color: ${desaturatedColor} !important;}`
}).join('');
} }
export const createNightModeCss = () => { export const createNightModeCss = () => {
return `#content-wrapper {background-color:${nightColors.black}!important;}` + return `#content-wrapper {background-color:var(--color-night-black)!important;}` +
`body,.btn,.bg-transparent-light {color: ${nightColors.yellow};}` + `#wrapper {background-image: linear-gradient(to right, var(--color-night-dark-blue) 0%, var(--color-night-dark-blue) 14rem, var(--color-night-black) 14.01rem, var(--color-night-black) 100%);}` +
`.card,.bg-white,.modal-content,.page-loader,.nav-tabs .nav-link:hover,.nav-tabs,hr,form .btn, .btn-outline-secondary{background-color:${nightColors.darkBlue}!important;border-color:${nightColors.blue}!important;}` + `body,.btn,.bg-transparent-light {color: var(--color-night-text-dark-bg);}` +
`.card,.bg-white,.modal-content,.page-loader,.nav-tabs .nav-link:hover,.nav-tabs,hr,form .btn, .btn-outline-secondary{background-color:var(--color-night-dark-blue)!important;border-color:var(--color-night-blue)!important;}` +
`.bg-white.collapse-inner {border:1px solid;}` + `.bg-white.collapse-inner {border:1px solid;}` +
`.card-header {background-color:${nightColors.darkBlue};border-color:${nightColors.blue};}` + `.card-header {background-color:var(--color-night-dark-blue);border-color:var(--color-night-blue);}` +
`#content,.col-black,.text-gray-900,.text-gray-800,.collapse-item,.modal-title,.modal-body,.page-loader,.fc-title,.fc-time,pre,.table-dark,input::placeholder{color:${nightColors.yellow} !important;}` + `#content,.col-black,.text-gray-900,.text-gray-800,.collapse-item,.modal-title,.modal-body,.page-loader,.fc-title,.fc-time,pre,.table-dark,input::placeholder{color:var(--color-night-text-dark-bg) !important;}` +
`.collapse-item:hover,.nav-link.active {background-color: ${nightColors.darkGreyBlue} !important;}` + `.collapse-item:hover,.nav-link.active {background-color: var(--color-night-dark-grey-blue) !important;}` +
`.nav-tabs .nav-link.active {background-color: ${nightColors.darkBlue} !important;border-color:${nightColors.blue} ${nightColors.blue} ${nightColors.darkBlue} !important;}` + `.nav-tabs .nav-link.active {background-color: var(--color-night-dark-blue) !important;border-color:var(--color-night-blue) var(--color-night-blue) var(--color-night-dark-blue) !important;}` +
`.fc-today {background:${nightColors.greyBlue} !important}` + `.fc-today {background:var(--color-night-grey-blue) !important}` +
`.fc-popover-body,.fc-popover-header{background-color: ${nightColors.darkBlue} !important;color: ${nightColors.yellow} !important;}` + `.fc-popover-body,.fc-popover-header{background-color: var(--color-night-dark-blue) !important;color: var(--color-night-text-dark-bg) !important;}` +
`select,input,.dataTables_paginate .page-item:not(.active) a,.input-group-text,.input-group-text > * {background-color:${nightColors.darkBlue} !important;border-color:${nightColors.blue} !important;color: ${nightColors.yellow} !important;}` + `select,input,.dataTables_paginate .page-item:not(.active) a,.input-group-text,.input-group-text > * {background-color:var(--color-night-dark-blue) !important;border-color:var(--color-night-blue) !important;color: var(--color-night-text-dark-bg) !important;}` +
`.topbar-divider,.fc td,.fc tr,.fc th, .fc table, .modal-header,.modal-body,.modal-footer{border-color:${nightColors.blue} !important;}` + `.topbar-divider,.fc td,.fc tr,.fc th, .fc table, .modal-header,.modal-body,.modal-footer{border-color:var(--color-night-blue) !important;}` +
`.fc a{color:${nightColors.yellow} !important;}` + `.fc a{color:var(--color-night-text-dark-bg) !important;}` +
`.fc-button{ background-color: ${withReducedSaturation(colorMap.PLAN.hex)} !important;}` + `.fc-button{ background-color: ${withReducedSaturation(colorMap.PLAN.hex)} !important;}` +
`.loader{border: 4px solid var(--color-plan); background-color: var(--color-plan);}` +
createNightModeColorCss() createNightModeColorCss()
} }

View File

@ -1,5 +1,4 @@
import React from 'react'; import React from 'react';
import {NightModeCss} from "../../hooks/themeHook";
import Sidebar from "../../components/navigation/Sidebar"; import Sidebar from "../../components/navigation/Sidebar";
import Header from "../../components/navigation/Header"; import Header from "../../components/navigation/Header";
import ErrorView from "../ErrorView"; import ErrorView from "../ErrorView";
@ -9,8 +8,7 @@ const ErrorPage = ({error}) => {
return ( return (
<> <>
<NightModeCss/> <Sidebar page={'Error occurred'} items={[]}/>
<Sidebar items={[]} showBackButton={true}/>
<div className="d-flex flex-column" id="content-wrapper"> <div className="d-flex flex-column" id="content-wrapper">
<Header page={error.title ? error.title : 'Unexpected error occurred'} hideUpdater/> <Header page={error.title ? error.title : 'Unexpected error occurred'} hideUpdater/>
<div id="content" style={{display: 'flex'}}> <div id="content" style={{display: 'flex'}}>

View File

@ -1,5 +1,4 @@
import React from 'react'; import React from 'react';
import {NightModeCss} from "../../hooks/themeHook";
import Sidebar from "../../components/navigation/Sidebar"; import Sidebar from "../../components/navigation/Sidebar";
import Header from "../../components/navigation/Header"; import Header from "../../components/navigation/Header";
import ColorSelectorModal from "../../components/modal/ColorSelectorModal"; import ColorSelectorModal from "../../components/modal/ColorSelectorModal";
@ -18,10 +17,9 @@ const ErrorsPage = () => {
return ( return (
<> <>
<NightModeCss/> <Sidebar page={'Errors'} items={[]}/>
<Sidebar items={[]} showBackButton={true}/>
<div className="d-flex flex-column" id="content-wrapper"> <div className="d-flex flex-column" id="content-wrapper">
<Header page={<><FontAwesomeIcon icon={faBug}/> Error Logs</>}/> <Header page={<><FontAwesomeIcon icon={faBug}/> Error Logs</>} hideUpdater/>
<div id="content" style={{display: 'flex'}}> <div id="content" style={{display: 'flex'}}>
<main className="container-fluid mt-4"> <main className="container-fluid mt-4">
<Card> <Card>

View File

@ -65,7 +65,7 @@ const LoginForm = ({login}) => {
id="inputPassword" placeholder={t('html.login.password')} type="password" id="inputPassword" placeholder={t('html.login.password')} type="password"
value={password} onChange={event => setPassword(event.target.value)}/> value={password} onChange={event => setPassword(event.target.value)}/>
</div> </div>
<button className="btn bg-plan btn-user w-100" id="login-button" onClick={onLogin}> <button className="btn bg-theme btn-user w-100" id="login-button" onClick={onLogin}>
{t('html.login.login')} {t('html.login.login')}
</button> </button>
</form> </form>
@ -78,7 +78,7 @@ const ColorChooserButton = () => {
return ( return (
<div className='text-center'> <div className='text-center'>
<button className="btn col-plan" onClick={toggleColorChooser} <button className="btn col-theme" onClick={toggleColorChooser}
title={t('html.label.themeSelect')}> title={t('html.label.themeSelect')}>
<Fa icon={faPalette}/> <Fa icon={faPalette}/>
</button> </button>
@ -90,7 +90,7 @@ const ForgotPasswordButton = ({onClick}) => {
return ( return (
<div className='text-center'> <div className='text-center'>
<button className='col-plan small' onClick={onClick}>{t('html.login.forgotPassword')}</button> <button className='col-theme small' onClick={onClick}>{t('html.login.forgotPassword')}</button>
</div> </div>
) )
} }
@ -100,7 +100,7 @@ const CreateAccountLink = () => {
return ( return (
<div className='text-center'> <div className='text-center'>
<Link to='/register' className='col-plan small'>{t('html.login.register')}</Link> <Link to='/register' className='col-theme small'>{t('html.login.register')}</Link>
</div> </div>
) )
} }
@ -132,7 +132,7 @@ const LoginPage = () => {
[setForgotPasswordModalOpen, forgotPasswordModalOpen]) [setForgotPasswordModalOpen, forgotPasswordModalOpen])
useEffect(() => { useEffect(() => {
document.body.classList.add("bg-plan", "plan-bg-gradient"); document.body.classList.add("bg-theme", "plan-bg-gradient");
const urlParams = new URLSearchParams(window.location.search); const urlParams = new URLSearchParams(window.location.search);
const cameFrom = urlParams.get('from'); const cameFrom = urlParams.get('from');
@ -142,7 +142,7 @@ const LoginPage = () => {
if (registerSuccess) setSuccessMessage(t('html.register.success')) if (registerSuccess) setSuccessMessage(t('html.register.success'))
return () => { return () => {
document.body.classList.remove("bg-plan", "plan-bg-gradient"); document.body.classList.remove("bg-theme", "plan-bg-gradient");
} }
}, [setRedirectTo, setSuccessMessage, t]) }, [setRedirectTo, setSuccessMessage, t])

View File

@ -16,7 +16,6 @@ import {
faUsers faUsers
} from "@fortawesome/free-solid-svg-icons"; } from "@fortawesome/free-solid-svg-icons";
import {useAuth} from "../../hooks/authenticationHook"; import {useAuth} from "../../hooks/authenticationHook";
import {NightModeCss} from "../../hooks/themeHook";
import Sidebar from "../../components/navigation/Sidebar"; import Sidebar from "../../components/navigation/Sidebar";
import Header from "../../components/navigation/Header"; import Header from "../../components/navigation/Header";
import ColorSelectorModal from "../../components/modal/ColorSelectorModal"; import ColorSelectorModal from "../../components/modal/ColorSelectorModal";
@ -39,6 +38,7 @@ const NetworkSidebar = () => {
const items = [ const items = [
{name: 'html.label.networkOverview', icon: faInfoCircle, href: "overview"}, {name: 'html.label.networkOverview', icon: faInfoCircle, href: "overview"},
{}, {},
{name: 'html.label.information'},
{ {
name: 'html.label.servers', name: 'html.label.servers',
icon: faServer, icon: faServer,
@ -52,14 +52,16 @@ const NetworkSidebar = () => {
{name: 'html.label.sessions', icon: faCalendarCheck, href: "sessions"}, {name: 'html.label.sessions', icon: faCalendarCheck, href: "sessions"},
staticSite ? undefined : {name: 'html.label.performance', icon: faCogs, href: "performance"}, staticSite ? undefined : {name: 'html.label.performance', icon: faCogs, href: "performance"},
{}, {},
...servers.map(server => { ...servers
return { .filter(server => !server.proxy)
name: server.serverName, .map(server => {
icon: faServer, return {
href: "/server/" + server.serverUUID, name: server.serverName,
color: 'light-green' icon: faServer,
} href: "/server/" + server.serverUUID,
}) color: 'light-green'
}
})
] ]
}, },
{ {
@ -108,7 +110,7 @@ const NetworkSidebar = () => {
}, [t, i18n, extensionData, setSidebarItems, networkMetadata]) }, [t, i18n, extensionData, setSidebarItems, networkMetadata])
return ( return (
<Sidebar items={sidebarItems} showBackButton={false}/> <Sidebar items={sidebarItems}/>
) )
} }
@ -124,7 +126,6 @@ const ServerPage = () => {
return ( return (
<> <>
<NightModeCss/>
<ServerExtensionContextProvider identifier={serverUUID}> <ServerExtensionContextProvider identifier={serverUUID}>
<NetworkSidebar/> <NetworkSidebar/>
<div className="d-flex flex-column" id="content-wrapper"> <div className="d-flex flex-column" id="content-wrapper">

View File

@ -2,10 +2,8 @@ import React, {useEffect} from "react";
import Sidebar from "../../components/navigation/Sidebar"; import Sidebar from "../../components/navigation/Sidebar";
import {Outlet, useOutletContext, useParams} from "react-router-dom"; import {Outlet, useOutletContext, useParams} from "react-router-dom";
import ColorSelectorModal from "../../components/modal/ColorSelectorModal"; import ColorSelectorModal from "../../components/modal/ColorSelectorModal";
import {NightModeCss} from "../../hooks/themeHook";
import {fetchPlayer} from "../../service/playerService"; import {fetchPlayer} from "../../service/playerService";
import {faCampground, faCubes, faInfoCircle, faNetworkWired} from "@fortawesome/free-solid-svg-icons"; import {faCampground, faCubes, faInfoCircle, faNetworkWired} from "@fortawesome/free-solid-svg-icons";
import {useAuth} from "../../hooks/authenticationHook";
import Header from "../../components/navigation/Header"; import Header from "../../components/navigation/Header";
import {useNavigation} from "../../hooks/navigationHook"; import {useNavigation} from "../../hooks/navigationHook";
import {useTranslation} from "react-i18next"; import {useTranslation} from "react-i18next";
@ -48,15 +46,11 @@ const PlayerPage = () => {
finishUpdate(player.timestamp, player.timestamp_f); finishUpdate(player.timestamp, player.timestamp_f);
}, [player, t, i18n, finishUpdate, setSidebarItems]) }, [player, t, i18n, finishUpdate, setSidebarItems])
const {hasPermissionOtherThan} = useAuth();
const showBackButton = hasPermissionOtherThan('page.player.self');
if (loadingError) return <ErrorPage error={loadingError}/>; if (loadingError) return <ErrorPage error={loadingError}/>;
return player ? ( return player ? (
<> <>
<NightModeCss/> <Sidebar page={player.info.name} items={sidebarItems}/>
<Sidebar items={sidebarItems} showBackButton={showBackButton}/>
<div className="d-flex flex-column" id="content-wrapper"> <div className="d-flex flex-column" id="content-wrapper">
<Header page={player.info.name} tab={currentTab}/> <Header page={player.info.name} tab={currentTab}/>
<div id="content" style={{display: 'flex'}}> <div id="content" style={{display: 'flex'}}>
@ -70,7 +64,6 @@ const PlayerPage = () => {
</div> </div>
</> </>
) : <> ) : <>
<NightModeCss/>
<div className="page-loader"> <div className="page-loader">
<div className="loader-container"> <div className="loader-container">
<span className="loader"/> <span className="loader"/>

View File

@ -3,7 +3,6 @@ import {useTranslation} from "react-i18next";
import {Outlet} from "react-router-dom"; import {Outlet} from "react-router-dom";
import {useNavigation} from "../../hooks/navigationHook"; import {useNavigation} from "../../hooks/navigationHook";
import {faSearch} from "@fortawesome/free-solid-svg-icons"; import {faSearch} from "@fortawesome/free-solid-svg-icons";
import {NightModeCss} from "../../hooks/themeHook";
import Sidebar from "../../components/navigation/Sidebar"; import Sidebar from "../../components/navigation/Sidebar";
import Header from "../../components/navigation/Header"; import Header from "../../components/navigation/Header";
import ColorSelectorModal from "../../components/modal/ColorSelectorModal"; import ColorSelectorModal from "../../components/modal/ColorSelectorModal";
@ -13,12 +12,10 @@ import {staticSite} from "../../service/backendConfiguration";
const PlayersPage = () => { const PlayersPage = () => {
const {t, i18n} = useTranslation(); const {t, i18n} = useTranslation();
const {isProxy, serverName} = useMetadata(); const {isProxy, networkName, serverName} = useMetadata();
const [error] = useState(undefined); const [error] = useState(undefined);
const {sidebarItems, setSidebarItems} = useNavigation(); const {sidebarItems, setSidebarItems, currentTab, setCurrentTab} = useNavigation();
const {currentTab, setCurrentTab} = useNavigation();
useEffect(() => { useEffect(() => {
const items = staticSite ? [] : [ const items = staticSite ? [] : [
@ -31,16 +28,12 @@ const PlayersPage = () => {
setCurrentTab('html.label.players') setCurrentTab('html.label.players')
}, [t, i18n, setCurrentTab, setSidebarItems]) }, [t, i18n, setCurrentTab, setSidebarItems])
// const {authRequired, user} = useAuth();
const showBackButton = true; // TODO
if (error) return <ErrorPage error={error}/>; if (error) return <ErrorPage error={error}/>;
const displayedServerName = !isProxy && serverName && serverName.startsWith('Server') ? "Plan" : serverName; const displayedServerName = isProxy ? networkName : (serverName && serverName.startsWith('Server') ? "Plan" : serverName);
return ( return (
<> <>
<NightModeCss/> <Sidebar items={sidebarItems}/>
<Sidebar items={sidebarItems} showBackButton={showBackButton}/>
<div className="d-flex flex-column" id="content-wrapper"> <div className="d-flex flex-column" id="content-wrapper">
<Header page={displayedServerName} tab={currentTab}/> <Header page={displayedServerName} tab={currentTab}/>
<div id="content" style={{display: 'flex'}}> <div id="content" style={{display: 'flex'}}>

View File

@ -3,7 +3,6 @@ import {useTranslation} from "react-i18next";
import {Outlet} from "react-router-dom"; import {Outlet} from "react-router-dom";
import {useNavigation} from "../../hooks/navigationHook"; import {useNavigation} from "../../hooks/navigationHook";
import {faUndo} from "@fortawesome/free-solid-svg-icons"; import {faUndo} from "@fortawesome/free-solid-svg-icons";
import {NightModeCss} from "../../hooks/themeHook";
import Sidebar from "../../components/navigation/Sidebar"; import Sidebar from "../../components/navigation/Sidebar";
import Header from "../../components/navigation/Header"; import Header from "../../components/navigation/Header";
import ColorSelectorModal from "../../components/modal/ColorSelectorModal"; import ColorSelectorModal from "../../components/modal/ColorSelectorModal";
@ -13,7 +12,7 @@ import {QueryResultContextProvider} from "../../hooks/queryResultContext";
const QueryPage = () => { const QueryPage = () => {
const {t, i18n} = useTranslation(); const {t, i18n} = useTranslation();
const {isProxy, serverName} = useMetadata(); const {isProxy, networkName, serverName} = useMetadata();
const [error] = useState(undefined); const [error] = useState(undefined);
const {sidebarItems, setSidebarItems} = useNavigation(); const {sidebarItems, setSidebarItems} = useNavigation();
@ -31,16 +30,13 @@ const QueryPage = () => {
setCurrentTab('html.query.title.text'); setCurrentTab('html.query.title.text');
}, [t, i18n, setCurrentTab, setSidebarItems]) }, [t, i18n, setCurrentTab, setSidebarItems])
const showBackButton = true;
if (error) return <ErrorPage error={error}/>; if (error) return <ErrorPage error={error}/>;
const displayedServerName = !isProxy && serverName && serverName.startsWith('Server') ? "Plan" : serverName; const displayedServerName = isProxy ? networkName : (serverName && serverName.startsWith('Server') ? "Plan" : serverName);
return ( return (
<> <>
<NightModeCss/>
<QueryResultContextProvider> <QueryResultContextProvider>
<Sidebar items={sidebarItems} showBackButton={showBackButton}/> <Sidebar items={sidebarItems}/>
<div className="d-flex flex-column" id="content-wrapper"> <div className="d-flex flex-column" id="content-wrapper">
<Header page={displayedServerName} tab={currentTab} hideUpdater/> <Header page={displayedServerName} tab={currentTab} hideUpdater/>
<div id="content" style={{display: 'flex'}}> <div id="content" style={{display: 'flex'}}>

View File

@ -66,7 +66,7 @@ const RegisterForm = ({register}) => {
value={password} onChange={event => setPassword(event.target.value)}/> value={password} onChange={event => setPassword(event.target.value)}/>
<div className={"form-text"}>{t('html.register.passwordTip')}</div> <div className={"form-text"}>{t('html.register.passwordTip')}</div>
</div> </div>
<button className="btn bg-plan btn-user w-100" id="register-button" onClick={onRegister}> <button className="btn bg-theme btn-user w-100" id="register-button" onClick={onRegister}>
{t('html.register.register')} {t('html.register.register')}
</button> </button>
</form> </form>
@ -79,7 +79,7 @@ const ColorChooserButton = () => {
return ( return (
<div className='text-center'> <div className='text-center'>
<button className="btn col-plan" onClick={toggleColorChooser} <button className="btn col-theme" onClick={toggleColorChooser}
title={t('html.label.themeSelect')}> title={t('html.label.themeSelect')}>
<Fa icon={faPalette}/> <Fa icon={faPalette}/>
</button> </button>
@ -92,7 +92,7 @@ const LoginLink = () => {
return ( return (
<div className='text-center'> <div className='text-center'>
<Link to='/login' className='col-plan small'>{t('html.register.login')}</Link> <Link to='/login' className='col-theme small'>{t('html.register.login')}</Link>
</div> </div>
) )
} }
@ -111,10 +111,10 @@ const RegisterPage = () => {
[setFinalizeRegistrationModalOpen, finalizeRegistrationModalOpen]) [setFinalizeRegistrationModalOpen, finalizeRegistrationModalOpen])
useEffect(() => { useEffect(() => {
document.body.classList.add("bg-plan", "plan-bg-gradient"); document.body.classList.add("bg-theme", "plan-bg-gradient");
return () => { return () => {
document.body.classList.remove("bg-plan", "plan-bg-gradient"); document.body.classList.remove("bg-theme", "plan-bg-gradient");
} }
}, []); }, []);

View File

@ -16,7 +16,6 @@ import {
faUsers faUsers
} from "@fortawesome/free-solid-svg-icons"; } from "@fortawesome/free-solid-svg-icons";
import {useAuth} from "../../hooks/authenticationHook"; import {useAuth} from "../../hooks/authenticationHook";
import {NightModeCss} from "../../hooks/themeHook";
import Sidebar from "../../components/navigation/Sidebar"; import Sidebar from "../../components/navigation/Sidebar";
import Header from "../../components/navigation/Header"; import Header from "../../components/navigation/Header";
import ColorSelectorModal from "../../components/modal/ColorSelectorModal"; import ColorSelectorModal from "../../components/modal/ColorSelectorModal";
@ -35,11 +34,6 @@ const ServerSidebar = () => {
const {t, i18n} = useTranslation(); const {t, i18n} = useTranslation();
const {sidebarItems, setSidebarItems} = useNavigation(); const {sidebarItems, setSidebarItems} = useNavigation();
const {extensionData} = useServerExtensionContext(); const {extensionData} = useServerExtensionContext();
const {authRequired, loggedIn, user} = useAuth();
const {isProxy} = useMetadata();
const showBackButton = isProxy
&& (!authRequired || (loggedIn && user.permissions.filter(perm => perm !== 'page.network').length));
useEffect(() => { useEffect(() => {
const items = [ const items = [
@ -107,14 +101,14 @@ const ServerSidebar = () => {
}, [t, i18n, extensionData, setSidebarItems]) }, [t, i18n, extensionData, setSidebarItems])
return ( return (
<Sidebar items={sidebarItems} showBackButton={showBackButton}/> <Sidebar items={sidebarItems}/>
) )
} }
const ServerPage = () => { const ServerPage = () => {
const {t} = useTranslation(); const {t} = useTranslation();
const {identifier} = useParams(); const {identifier} = useParams();
const {isProxy, serverName} = useMetadata(); const {isProxy, serverName, networkMetadata} = useMetadata();
const { const {
data: serverIdentity, data: serverIdentity,
@ -133,7 +127,8 @@ const ServerPage = () => {
} }
if (isProxy) { if (isProxy) {
return identifier; const fromMetadata = networkMetadata?.servers?.find(server => server.serverUUID === identifier);
return fromMetadata ? fromMetadata.serverName : identifier;
} else { } else {
return serverName && serverName.startsWith('Server') ? "Plan" : serverName return serverName && serverName.startsWith('Server') ? "Plan" : serverName
} }
@ -152,7 +147,6 @@ const ServerPage = () => {
return ( return (
<> <>
<NightModeCss/>
<ServerExtensionContextProvider identifier={identifier}> <ServerExtensionContextProvider identifier={identifier}>
<ServerSidebar/> <ServerSidebar/>
<div className="d-flex flex-column" id="content-wrapper"> <div className="d-flex flex-column" id="content-wrapper">