Merge branch 'dark-mode' into 'main'

Dark mode

See merge request sc07/canvas!16
This commit is contained in:
Grant 2024-07-15 15:54:59 +00:00
commit eb7b275ba1
17 changed files with 202 additions and 70 deletions

24
package-lock.json generated
View File

@ -6278,6 +6278,19 @@
"tslib": "^2.4.0" "tslib": "^2.4.0"
} }
}, },
"node_modules/@theme-toggles/react": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/@theme-toggles/react/-/react-4.1.0.tgz",
"integrity": "sha512-h3SuJMsej8DfelHt5fjNIlaMfJOK52Vku4pPDVoHaTwjAcoTr4fn8hzeur2oiqWBYFYfKugvv1RdQaBFXaiPKg==",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/alfiejones"
},
"peerDependencies": {
"react": "^16 || ^17 || ^18",
"react-dom": "^16 || ^17 || ^18"
}
},
"node_modules/@tsconfig/node10": { "node_modules/@tsconfig/node10": {
"version": "1.0.9", "version": "1.0.9",
"resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz",
@ -16261,10 +16274,12 @@
"@icons-pack/react-simple-icons": "^9.6.0", "@icons-pack/react-simple-icons": "^9.6.0",
"@nextui-org/react": "^2.2.9", "@nextui-org/react": "^2.2.9",
"@sc07-canvas/lib": "^1.0.0", "@sc07-canvas/lib": "^1.0.0",
"@theme-toggles/react": "^4.1.0",
"@typescript-eslint/parser": "^7.1.0", "@typescript-eslint/parser": "^7.1.0",
"eventemitter3": "^5.0.1", "eventemitter3": "^5.0.1",
"framer-motion": "^11.3.2", "framer-motion": "^11.3.2",
"lodash.throttle": "^4.1.1", "lodash.throttle": "^4.1.1",
"next-themes": "^0.3.0",
"prop-types": "^15.8.1", "prop-types": "^15.8.1",
"react": "^18.2.0", "react": "^18.2.0",
"react-dom": "^18.2.0", "react-dom": "^18.2.0",
@ -16472,6 +16487,15 @@
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
}, },
"packages/client/node_modules/next-themes": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/next-themes/-/next-themes-0.3.0.tgz",
"integrity": "sha512-/QHIrsYpd6Kfk7xakK4svpDI5mmXP0gfvCoJdGpZQ2TOrQZmsW0QxjaiLn8wbIKjtm4BTSqLoix4lxYYOnLJ/w==",
"peerDependencies": {
"react": "^16.8 || ^17 || ^18",
"react-dom": "^16.8 || ^17 || ^18"
}
},
"packages/lib": { "packages/lib": {
"name": "@sc07-canvas/lib", "name": "@sc07-canvas/lib",
"version": "1.0.0", "version": "1.0.0",

View File

@ -0,0 +1,13 @@
import { useTheme } from "next-themes";
import { ToastContainer } from "react-toastify";
export const ToastWrapper = () => {
const { theme } = useTheme()
return (
<ToastContainer
position="bottom-right"
theme={theme}
/>
);
};

View File

@ -10,6 +10,7 @@ import { AccountsPage } from "./pages/Accounts/Accounts/page.tsx";
import { ServiceSettingsPage } from "./pages/Service/settings.tsx"; import { ServiceSettingsPage } from "./pages/Service/settings.tsx";
import { ToastContainer } from "react-toastify"; import { ToastContainer } from "react-toastify";
import { AuditLog } from "./pages/AuditLog/auditlog.tsx"; import { AuditLog } from "./pages/AuditLog/auditlog.tsx";
import { ToastWrapper } from "./components/ToastWrapper.tsx";
const router = createBrowserRouter( const router = createBrowserRouter(
[ [
@ -47,7 +48,7 @@ ReactDOM.createRoot(document.getElementById("root")!).render(
<ThemeProvider defaultTheme="system"> <ThemeProvider defaultTheme="system">
<RouterProvider router={router} /> <RouterProvider router={router} />
<ToastContainer position="bottom-right" /> <ToastWrapper />
</ThemeProvider> </ThemeProvider>
</NextUIProvider> </NextUIProvider>
</React.StrictMode> </React.StrictMode>

View File

@ -27,10 +27,12 @@
"@icons-pack/react-simple-icons": "^9.6.0", "@icons-pack/react-simple-icons": "^9.6.0",
"@nextui-org/react": "^2.2.9", "@nextui-org/react": "^2.2.9",
"@sc07-canvas/lib": "^1.0.0", "@sc07-canvas/lib": "^1.0.0",
"@theme-toggles/react": "^4.1.0",
"@typescript-eslint/parser": "^7.1.0", "@typescript-eslint/parser": "^7.1.0",
"eventemitter3": "^5.0.1", "eventemitter3": "^5.0.1",
"framer-motion": "^11.3.2", "framer-motion": "^11.3.2",
"lodash.throttle": "^4.1.1", "lodash.throttle": "^4.1.1",
"next-themes": "^0.3.0",
"prop-types": "^15.8.1", "prop-types": "^15.8.1",
"react": "^18.2.0", "react": "^18.2.0",
"react-dom": "^18.2.0", "react-dom": "^18.2.0",

View File

@ -19,6 +19,7 @@ import { WelcomeModal } from "./Welcome/WelcomeModal";
import { InfoSidebar } from "./Info/InfoSidebar"; import { InfoSidebar } from "./Info/InfoSidebar";
import { ModModal } from "./Moderation/ModModal"; import { ModModal } from "./Moderation/ModModal";
import { DynamicModals } from "./DynamicModals"; import { DynamicModals } from "./DynamicModals";
import { ToastWrapper } from "./ToastWrapper";
const Chat = lazy(() => import("./Chat/Chat")); const Chat = lazy(() => import("./Chat/Chat"));
@ -152,7 +153,7 @@ const AppInner = () => {
<WelcomeModal /> <WelcomeModal />
<ModModal /> <ModModal />
<ToastContainer position="top-left" /> <ToastWrapper />
<DynamicModals /> <DynamicModals />
</> </>
); );

View File

@ -1,6 +1,8 @@
import { Badge, Button, Link } from "@nextui-org/react"; import { Badge, Button, Link } from "@nextui-org/react";
import { useChatContext } from "../../contexts/ChatContext"; import { useChatContext } from "../../contexts/ChatContext";
import { useAppContext } from "../../contexts/AppContext"; import { useAppContext } from "../../contexts/AppContext";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faComments } from "@fortawesome/free-solid-svg-icons";
const OpenChatButton = () => { const OpenChatButton = () => {
const { config } = useAppContext(); const { config } = useAppContext();
@ -13,7 +15,15 @@ const OpenChatButton = () => {
color="danger" color="danger"
size="sm" size="sm"
> >
{config?.chat?.element_host && <Button onPress={doLogin}>Chat</Button>} {
config?.chat?.element_host &&
<Button
onPress={doLogin}
variant="ghost"
>
<FontAwesomeIcon icon={faComments} />
<p>Chat</p>
</Button>}
</Badge> </Badge>
); );
}; };

View File

@ -1,18 +1,8 @@
import { Button, Card, CardBody, Link } from "@nextui-org/react"; import { Card, CardBody } from "@nextui-org/react";
import { useAppContext } from "../../contexts/AppContext"; import { useAppContext } from "../../contexts/AppContext";
import { User } from "./User";
import { Debug } from "@sc07-canvas/lib/src/debug";
import React, { lazy } from "react";
import { AccountStanding } from "./AccountStanding";
import { EventInfoOverlay } from "../EventInfoOverlay"; import { EventInfoOverlay } from "../EventInfoOverlay";
import { HeaderLeft } from "./HeaderLeft";
const OpenChatButton = lazy(() => import("../Chat/OpenChatButton")); import { HeaderRight } from "./HeaderRight";
const DynamicChat = () => {
const { loadChat } = useAppContext();
return <React.Suspense>{loadChat && <OpenChatButton />}</React.Suspense>;
};
export const Header = () => { export const Header = () => {
const { connected } = useAppContext(); const { connected } = useAppContext();
@ -20,8 +10,8 @@ export const Header = () => {
return ( return (
<header id="main-header"> <header id="main-header">
{import.meta.env.VITE_INCLUDE_EVENT_INFO && <EventInfoOverlay />} {import.meta.env.VITE_INCLUDE_EVENT_INFO && <EventInfoOverlay />}
<div className="flex justify-between w-full">
<HeaderLeft /> <HeaderLeft />
<div className="spacer"></div>
{!connected && ( {!connected && (
<div> <div>
<Card> <Card>
@ -29,39 +19,10 @@ export const Header = () => {
</Card> </Card>
</div> </div>
)} )}
<div className="spacer"></div>
<HeaderRight /> <HeaderRight />
</div>
</header> </header>
); );
}; };
const HeaderLeft = () => {
const { setInfoSidebar } = useAppContext();
return (
<div className="box">
<AccountStanding />
<Button onPress={() => setInfoSidebar(true)}>Info</Button>
{import.meta.env.DEV && (
<Button onPress={() => Debug.openDebugTools()}>Debug Tools</Button>
)}
</div>
);
};
const HeaderRight = () => {
const { setSettingsSidebar, hasAdmin } = useAppContext();
return (
<div className="box">
<User />
<Button onClick={() => setSettingsSidebar(true)}>Settings</Button>
{hasAdmin && (
<Button href="/admin" target="_blank" as={Link}>
Admin
</Button>
)}
<DynamicChat />
</div>
);
};

View File

@ -0,0 +1,32 @@
import { Button } from "@nextui-org/react";
import { useAppContext } from "../../contexts/AppContext";
import { AccountStanding } from "./AccountStanding";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { Debug } from "@sc07-canvas/lib/src/debug";
import { faInfoCircle, faTools } from "@fortawesome/free-solid-svg-icons";
export const HeaderLeft = () => {
const { setInfoSidebar } = useAppContext();
return (
<div className="box gap-2 flex">
<AccountStanding />
<Button
onPress={() => setInfoSidebar(true)}
variant="ghost"
>
<FontAwesomeIcon icon={faInfoCircle} />
<p>Info</p>
</Button>
{import.meta.env.DEV && (
<Button
onPress={() => Debug.openDebugTools()}
variant="ghost"
>
<FontAwesomeIcon icon={faTools} />
<p>Debug Tools</p>
</Button>
)}
</div>
);
};

View File

@ -0,0 +1,42 @@
import { Button, Link } from "@nextui-org/react";
import { useAppContext } from "../../contexts/AppContext";
import { User } from "./User";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { ThemeSwitcher } from "./ThemeSwitcher";
import { faGear, faHammer } from "@fortawesome/free-solid-svg-icons";
import React, { lazy } from "react";
const OpenChatButton = lazy(() => import("../Chat/OpenChatButton"));
const DynamicChat = () => {
const { loadChat } = useAppContext();
return <React.Suspense>{loadChat && <OpenChatButton />}</React.Suspense>;
};
export const HeaderRight = () => {
const { setSettingsSidebar, hasAdmin } = useAppContext();
return (
<div className="box flex flex-col gap-2">
<User />
<div className="flex gap-2">
<Button
onClick={() => setSettingsSidebar(true)}
variant="ghost"
>
<FontAwesomeIcon icon={faGear} />
<p>Settings</p>
</Button>
<ThemeSwitcher />
{hasAdmin && (
<Button href="/admin" target="_blank" as={Link} variant="ghost" >
<FontAwesomeIcon icon={faHammer} />
<p>Admin</p>
</Button>
)}
<DynamicChat />
</div>
</div>
);
};

View File

@ -0,0 +1,34 @@
import "@theme-toggles/react/css/Classic.css"
import { Classic } from "@theme-toggles/react"
import {useTheme} from "next-themes";
import { useEffect, useState } from "react";
import { Button } from "@nextui-org/react";
export function ThemeSwitcher() {
const [mounted, setMounted] = useState(false)
const { theme, setTheme } = useTheme()
const [isToggled, setToggle] = useState(false)
useEffect(() => {
setMounted(true)
setToggle(theme === 'dark')
}, [])
useEffect(() => {
if (isToggled) {
setTheme('dark')
} else {
setTheme('light')
}
}, [isToggled])
if(!mounted) return null
return (
<Button onClick={() => { setToggle(!isToggled) }} variant="ghost">
<Classic toggled={isToggled} placeholder={undefined} />
<p>{theme === 'dark' ? "Dark" : "Light"}</p>
</Button>
)
};

View File

@ -24,13 +24,13 @@ export const InfoSidebar = () => {
transition={{ type: 'spring', stiffness: 50 }} transition={{ type: 'spring', stiffness: 50 }}
/> />
<motion.div <motion.div
className="min-w-[20rem] max-w-[75vw] md:max-w-[30vw] bg-white text-black flex flex-col justify-between fixed left-0 h-full shadow-xl overflow-y-auto z-50 top-0" className="min-w-[20rem] max-w-[75vw] md:max-w-[30vw] bg-white dark:bg-black flex flex-col justify-between fixed left-0 h-full shadow-xl overflow-y-auto z-50 top-0"
initial={{ x: '-150%' }} initial={{ x: '-150%' }}
animate={{ x: infoSidebar ? '-50%' : '-150%' }} animate={{ x: infoSidebar ? '-50%' : '-150%' }}
transition={{ type: 'spring', stiffness: 50 }} transition={{ type: 'spring', stiffness: 50 }}
/> />
<motion.div <motion.div
className="min-w-[20rem] max-w-[75vw] md:max-w-[30vw] bg-white text-black flex flex-col justify-between fixed left-0 h-full shadow-xl overflow-y-auto z-50 top-0" className="min-w-[20rem] max-w-[75vw] md:max-w-[30vw] bg-white dark:bg-black text-black dark:text-white flex flex-col justify-between fixed left-0 h-full shadow-xl overflow-y-auto z-50 top-0"
initial={{ x: '-100%' }} initial={{ x: '-100%' }}
animate={{ x: infoSidebar ? 0 : '-100%' }} animate={{ x: infoSidebar ? 0 : '-100%' }}
transition={{ type: 'spring', stiffness: 50 }} transition={{ type: 'spring', stiffness: 50 }}

View File

@ -12,7 +12,7 @@ export const SettingsSidebar = () => {
return ( return (
<div <div
className="sidebar sidebar-right" className="sidebar sidebar-right bg-white text-black dark:bg-black dark:text-white"
style={{ ...(settingsSidebar ? {} : { display: "none" }) }} style={{ ...(settingsSidebar ? {} : { display: "none" }) }}
> >
<header> <header>

View File

@ -0,0 +1,13 @@
import { useTheme } from "next-themes";
import { ToastContainer } from "react-toastify";
export const ToastWrapper = () => {
const { theme } = useTheme()
return (
<ToastContainer
position="top-left"
theme={theme}
/>
);
};

View File

@ -12,8 +12,6 @@
z-index: 10; z-index: 10;
position: relative; position: relative;
background-color: #fff;
.pallete-colors { .pallete-colors {
// display: flex; // display: flex;
// width: 100%; // width: 100%;

View File

@ -29,7 +29,7 @@ export const Palette = () => {
}, []); }, []);
return ( return (
<div id="pallete"> <div id="pallete" className="bg-[#fff] dark:bg-[#000]">
<div className="pallete-colors"> <div className="pallete-colors">
<button <button
aria-label="Deselect Color" aria-label="Deselect Color"
@ -71,12 +71,12 @@ export const Palette = () => {
{import.meta.env.VITE_INCLUDE_EVENT_INFO ? ( {import.meta.env.VITE_INCLUDE_EVENT_INFO ? (
<>The event hasn't started yet</> <>The event hasn't started yet</>
) : ( ) : (
<> <div className="flex gap-3 items-center">
You are not logged in You are not logged in
<Button as={Link} href="/api/login" className="user-login"> <Button as={Link} href="/api/login" className="user-login" variant="faded">
Login Login
</Button> </Button>
</> </div>
)} )}
</div> </div>
)} )}

View File

@ -1,6 +1,7 @@
import React from "react"; import React from "react";
import { createRoot } from "react-dom/client"; import { createRoot } from "react-dom/client";
import { NextUIProvider } from "@nextui-org/react"; import { NextUIProvider } from "@nextui-org/react";
import { ThemeProvider } from 'next-themes'
import App from "./components/App"; import App from "./components/App";
import Bugsnag from "@bugsnag/js"; import Bugsnag from "@bugsnag/js";
@ -26,7 +27,11 @@ root.render(
<React.StrictMode> <React.StrictMode>
<ErrorBoundary> <ErrorBoundary>
<NextUIProvider> <NextUIProvider>
<ThemeProvider attribute="class" defaultTheme="system">
<div className="w-screen h-screen bg-[#ddd] dark:bg-[#060606]">
<App /> <App />
</div>
</ThemeProvider>
</NextUIProvider> </NextUIProvider>
</ErrorBoundary> </ErrorBoundary>
</React.StrictMode> </React.StrictMode>

View File

@ -10,8 +10,6 @@ html,
body { body {
overscroll-behavior: contain; overscroll-behavior: contain;
touch-action: none; touch-action: none;
background-color: #ddd !important;
} }
header#main-header { header#main-header {
@ -150,8 +148,6 @@ main {
position: fixed; position: fixed;
top: 0; top: 0;
z-index: 9998; z-index: 9998;
background-color: #fff;
color: #000;
height: 100%; height: 100%;
min-width: 20rem; min-width: 20rem;