mirror of
https://github.com/BrenBroZAYT/gameyfin.git
synced 2026-06-13 16:40:01 +00:00
WIP: Theme switcher
- Light/Dark Toggle works - Theme Preview works - TODO: Theme switching
This commit is contained in:
@@ -1,6 +1,16 @@
|
||||
import * as React from "react"
|
||||
import {Provider as JotaiProvider} from "jotai"
|
||||
import {ThemeProvider as NextThemesProvider} from "next-themes"
|
||||
import {type ThemeProviderProps} from "next-themes/dist/types"
|
||||
import {ThemeProviderProps} from "next-themes/dist/types"
|
||||
import {TooltipProvider} from "Frontend/@/components/ui/tooltip";
|
||||
|
||||
|
||||
export function ThemeProvider({children, ...props}: ThemeProviderProps) {
|
||||
return <NextThemesProvider {...props}>{children}</NextThemesProvider>
|
||||
}
|
||||
return (
|
||||
<JotaiProvider>
|
||||
<NextThemesProvider {...props}>
|
||||
<TooltipProvider delayDuration={0}>{children}</TooltipProvider>
|
||||
</NextThemesProvider>
|
||||
</JotaiProvider>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
import * as React from "react"
|
||||
import * as SwitchPrimitives from "@radix-ui/react-switch"
|
||||
|
||||
import {cn} from "Frontend/@/lib/utils"
|
||||
|
||||
const Switch = React.forwardRef<
|
||||
React.ElementRef<typeof SwitchPrimitives.Root>,
|
||||
React.ComponentPropsWithoutRef<typeof SwitchPrimitives.Root>
|
||||
>(({className, ...props}, ref) => (
|
||||
<SwitchPrimitives.Root
|
||||
className={cn(
|
||||
"peer inline-flex h-5 w-9 shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent shadow-sm transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=unchecked]:bg-input",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
ref={ref}
|
||||
>
|
||||
<SwitchPrimitives.Thumb
|
||||
className={cn(
|
||||
"pointer-events-none block h-4 w-4 rounded-full bg-background shadow-lg ring-0 transition-transform data-[state=checked]:translate-x-4 data-[state=unchecked]:translate-x-0"
|
||||
)}
|
||||
/>
|
||||
</SwitchPrimitives.Root>
|
||||
))
|
||||
Switch.displayName = SwitchPrimitives.Root.displayName
|
||||
|
||||
export {Switch}
|
||||
@@ -1,18 +1,20 @@
|
||||
import {useAtom} from "jotai"
|
||||
import {atomWithStorage} from "jotai/utils"
|
||||
|
||||
import {Theme} from "Frontend/@/registry/themes"
|
||||
import {Theme} from "@/registry/themes"
|
||||
|
||||
type Config = {
|
||||
theme: Theme["name"]
|
||||
mode: "light" | "dark",
|
||||
radius: number
|
||||
theme: {
|
||||
name: Theme["name"],
|
||||
mode: "light" | "dark" | "system"
|
||||
}
|
||||
}
|
||||
|
||||
const configAtom = atomWithStorage<Config>("config", {
|
||||
theme: "zinc",
|
||||
mode: "light",
|
||||
radius: 0.5,
|
||||
theme: {
|
||||
name: "zinc",
|
||||
mode: "system"
|
||||
}
|
||||
})
|
||||
|
||||
export function useConfig() {
|
||||
|
||||
+17
-3
@@ -1,6 +1,20 @@
|
||||
import { type ClassValue, clsx } from "clsx"
|
||||
import { twMerge } from "tailwind-merge"
|
||||
import {type ClassValue, clsx} from "clsx"
|
||||
import {twMerge} from "tailwind-merge"
|
||||
|
||||
export function cn(...inputs: ClassValue[]) {
|
||||
return twMerge(clsx(inputs))
|
||||
return twMerge(clsx(inputs))
|
||||
}
|
||||
|
||||
export function cssVar(variable: string) {
|
||||
return getComputedStyle(document.documentElement).getPropertyValue(`--${variable}`);
|
||||
}
|
||||
|
||||
export function hsl(hsl: string) {
|
||||
return `hsl(${hsl}`;
|
||||
}
|
||||
|
||||
export function rand(min: number, max: number) {
|
||||
const minCeiled = Math.ceil(min);
|
||||
const maxFloored = Math.floor(max);
|
||||
return Math.floor(Math.random() * (maxFloored - minCeiled) + minCeiled);
|
||||
}
|
||||
|
||||
+465
-623
File diff suppressed because it is too large
Load Diff
@@ -14,7 +14,6 @@ export default function App() {
|
||||
attribute="class"
|
||||
defaultTheme="system"
|
||||
enableSystem
|
||||
disableTransitionOnChange
|
||||
>
|
||||
<IconContext.Provider value={{size: 20}}>
|
||||
<RouterProvider router={router}/>
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
import {hsl} from "Frontend/@/lib/utils";
|
||||
|
||||
export default function GameyfinLogo({primary, secondary, className}: {
|
||||
primary: string,
|
||||
secondary: string,
|
||||
className?: string
|
||||
}) {
|
||||
const primaryColor = hsl(primary)
|
||||
const secondaryColor = (secondary === null || secondary === undefined) ? primaryColor : hsl(secondary);
|
||||
|
||||
return (
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 365.58 336.34" className={className}>
|
||||
<polygon points="190.1 49.13 190.1 69.24 207.98 44.13 190.1 49.13" fill={secondaryColor}/>
|
||||
<polygon points="365.58 0 263.22 28.66 205.64 95.97 365.58 51.18 365.58 0" fill={secondaryColor}/>
|
||||
<polygon
|
||||
points="190.1 283.11 248.6 266.73 248.6 149.74 365.58 116.99 365.58 73.12 190.1 122.25 190.1 283.11"
|
||||
fill={secondaryColor}/>
|
||||
<polygon
|
||||
points="58.49 144.48 155.98 117.18 175.48 89.79 175.48 53.23 0 102.36 0 336.34 58.49 254.15 58.49 144.48"
|
||||
fill={primaryColor}/>
|
||||
<polygon
|
||||
points="116.99 199.59 116.99 245.09 65.81 259.42 0 336.34 175.48 287.2 175.48 170.22 131.61 182.5 116.99 199.59"
|
||||
fill={primaryColor}/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
@@ -1,55 +1,53 @@
|
||||
import {Theme} from "Frontend/@/registry/themes";
|
||||
import {Card} from "Frontend/@/components/ui/card";
|
||||
import {Tooltip, TooltipContent, TooltipProvider, TooltipTrigger} from "Frontend/@/components/ui/tooltip";
|
||||
import {Tooltip, TooltipContent, TooltipTrigger} from "Frontend/@/components/ui/tooltip";
|
||||
import {useTheme} from "next-themes";
|
||||
import GameyfinLogo from "Frontend/components/theming/GameyfinLogo";
|
||||
import {hsl} from "Frontend/@/lib/utils";
|
||||
|
||||
export default function ThemePreview({theme}: { theme: Theme }) {
|
||||
const {resolvedTheme} = useTheme();
|
||||
//@ts-ignore
|
||||
let resolvedTheme: "light" | "dark" = useTheme().resolvedTheme ?? "light";
|
||||
const {setTheme} = useTheme();
|
||||
|
||||
function toggleMode() {
|
||||
resolvedTheme = resolvedTheme === "light" ? "dark" : "light";
|
||||
setTheme(resolvedTheme);
|
||||
}
|
||||
|
||||
return (
|
||||
<TooltipProvider>
|
||||
<Tooltip delayDuration={0}>
|
||||
<TooltipTrigger asChild>
|
||||
<Card className="overflow-hidden flex place-self-center">
|
||||
<svg width="228" height="120" viewBox="0 0 228 120" fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
{/*@ts-ignore*/}
|
||||
<path id="background" d="M0 0H228V120H0V0Z" fill={theme.cssVars[resolvedTheme].background}/>
|
||||
<rect id="background-secondary" x="29" y="54" width="144" height="53" rx="2"
|
||||
fill="#30363D"/>
|
||||
<rect x="184" y="54" width="32" height="36" rx="2" fill="#30363D"/>
|
||||
<rect opacity="0.3" x="29" y="59" width="144" height="12" fill="#2EA043"/>
|
||||
<path opacity="0.6" d="M0 0H228V23H0V0Z" fill="#484F58"/>
|
||||
<rect x="13" y="9" width="32" height="6" rx="3" fill="#8B949E"/>
|
||||
<rect x="29" y="36" width="48" height="6" rx="3" fill="#6E7681"/>
|
||||
<rect x="34" y="62" width="64" height="6" rx="3" fill="#3FB950"/>
|
||||
<rect x="210" y="36" width="6" height="6" rx="1" fill="#DA3633"/>
|
||||
<rect x="202" y="36" width="6" height="6" rx="1" fill="#3FB950"/>
|
||||
<rect x="53" y="9" width="32" height="6" rx="3" fill="#8B949E"/>
|
||||
<rect x="93" y="9" width="32" height="6" rx="3" fill="#8B949E"/>
|
||||
</svg>
|
||||
</Card>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent side="bottom">
|
||||
<p className="capitalize">{theme.name}</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Card
|
||||
className="overflow-hidden flex place-self-center justify-center p-6"
|
||||
style={{background: hsl(theme.cssVars[resolvedTheme].background)}}>
|
||||
<GameyfinLogo primary={theme.cssVars[resolvedTheme].primary}
|
||||
secondary={theme.cssVars[resolvedTheme].secondary}
|
||||
className="w-1/2"
|
||||
/>
|
||||
</Card>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent side="bottom">
|
||||
<p className="capitalize">{theme.name}</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
|
||||
/*
|
||||
<svg width="228" height="120" viewBox="0 0 228 120" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M0 0H228V120H0V0Z" fill="#161B22"/>
|
||||
<rect x="29" y="54" width="144" height="53" rx="2" fill="#30363D"/>
|
||||
<rect x="184" y="54" width="32" height="36" rx="2" fill="#30363D"/>
|
||||
<rect opacity="0.3" x="29" y="59" width="144" height="12" fill="#2EA043"/>
|
||||
<path opacity="0.6" d="M0 0H228V23H0V0Z" fill="#484F58"/>
|
||||
<rect x="13" y="9" width="32" height="6" rx="3" fill="#8B949E"/>
|
||||
<rect x="29" y="36" width="48" height="6" rx="3" fill="#6E7681"/>
|
||||
<rect x="34" y="62" width="64" height="6" rx="3" fill="#3FB950"/>
|
||||
<rect x="210" y="36" width="6" height="6" rx="1" fill="#DA3633"/>
|
||||
<rect x="202" y="36" width="6" height="6" rx="1" fill="#3FB950"/>
|
||||
<rect x="53" y="9" width="32" height="6" rx="3" fill="#8B949E"/>
|
||||
<rect x="93" y="9" width="32" height="6" rx="3" fill="#8B949E"/>
|
||||
</svg>
|
||||
*/
|
||||
|
||||
<svg width="228" height="120" viewBox="0 0 228 120" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path id="background" d="M0 0H228V120H0V0Z" fill={theme.cssVars[resolvedTheme].background}/>
|
||||
<rect id="background-secondary" x="29" y="54" width="144" height="53" rx="2" fill="#30363D"/>
|
||||
<rect x="184" y="54" width="32" height="36" rx="2" fill="#30363D"/>
|
||||
<rect opacity="0.3" x="29" y="59" width="144" height="12" fill="#2EA043"/>
|
||||
<path opacity="0.6" d="M0 0H228V23H0V0Z" fill="#484F58"/>
|
||||
<rect x="13" y="9" width="32" height="6" rx="3" fill="#8B949E"/>
|
||||
<rect x="29" y="36" width="48" height="6" rx="3" fill="#6E7681"/>
|
||||
<rect x="34" y="62" width="64" height="6" rx="3" fill="#3FB950"/>
|
||||
<rect x="210" y="36" width="6" height="6" rx="1" fill="#DA3633"/>
|
||||
<rect x="202" y="36" width="6" height="6" rx="1" fill="#3FB950"/>
|
||||
<rect x="53" y="9" width="32" height="6" rx="3" fill="#8B949E"/>
|
||||
<rect x="93" y="9" width="32" height="6" rx="3" fill="#8B949E"/>
|
||||
</svg>
|
||||
*/
|
||||
@@ -3,10 +3,12 @@ import * as Yup from 'yup';
|
||||
import Wizard from "Frontend/components/wizard/Wizard";
|
||||
import WizardStep from "Frontend/components/wizard/WizardStep";
|
||||
import Input from "Frontend/components/Input";
|
||||
import {GearFine, HandWaving, Palette, User} from "@phosphor-icons/react";
|
||||
import {GearFine, HandWaving, Moon, Palette, SunDim, User} from "@phosphor-icons/react";
|
||||
import ThemePreview from "Frontend/components/theming/ThemePreview";
|
||||
import {Theme, themes} from "Frontend/@/registry/themes";
|
||||
import {Card} from "Frontend/@/components/ui/card";
|
||||
import {Switch} from "Frontend/@/components/ui/switch";
|
||||
import {useTheme} from "next-themes";
|
||||
|
||||
const sleep = (ms: number) => new Promise(resolve => setTimeout(resolve, ms));
|
||||
|
||||
@@ -35,8 +37,19 @@ function WelcomeStep() {
|
||||
}
|
||||
|
||||
function ThemeStep() {
|
||||
const {setTheme, theme} = useTheme();
|
||||
|
||||
function toggleMode() {
|
||||
setTheme(theme === "light" ? "dark" : "light");
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex flex-col size-full items-center">
|
||||
<div className="w-full flex flex-row items-center justify-center gap-4 mb-16">
|
||||
<SunDim size={32}/>
|
||||
<Switch checked={theme === "dark"} onCheckedChange={() => toggleMode()}></Switch>
|
||||
<Moon size={32}/>
|
||||
</div>
|
||||
<div className="grid grid-cols-3 w-1/2 min-w-[468px] gap-12">
|
||||
{themes.map(((theme: Theme) => (
|
||||
<ThemePreview key={theme.name} theme={theme}/>
|
||||
|
||||
Generated
+47
@@ -30,6 +30,7 @@
|
||||
"@radix-ui/react-icons": "^1.3.0",
|
||||
"@radix-ui/react-label": "^2.0.2",
|
||||
"@radix-ui/react-slot": "^1.0.2",
|
||||
"@radix-ui/react-switch": "^1.0.3",
|
||||
"@radix-ui/react-tooltip": "^1.0.7",
|
||||
"@vaadin/bundles": "24.3.0",
|
||||
"@vaadin/common-frontend": "0.0.19",
|
||||
@@ -3328,6 +3329,35 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-switch": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-switch/-/react-switch-1.0.3.tgz",
|
||||
"integrity": "sha512-mxm87F88HyHztsI7N+ZUmEoARGkC22YVW5CaC+Byc+HRpuvCrOBPTAnXgf+tZ/7i0Sg/eOePGdMhUKhPaQEqow==",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.13.10",
|
||||
"@radix-ui/primitive": "1.0.1",
|
||||
"@radix-ui/react-compose-refs": "1.0.1",
|
||||
"@radix-ui/react-context": "1.0.1",
|
||||
"@radix-ui/react-primitive": "1.0.3",
|
||||
"@radix-ui/react-use-controllable-state": "1.0.1",
|
||||
"@radix-ui/react-use-previous": "1.0.1",
|
||||
"@radix-ui/react-use-size": "1.0.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"@types/react-dom": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0",
|
||||
"react-dom": "^16.8 || ^17.0 || ^18.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
},
|
||||
"@types/react-dom": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-tooltip": {
|
||||
"version": "1.0.7",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-tooltip/-/react-tooltip-1.0.7.tgz",
|
||||
@@ -3432,6 +3462,23 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-use-previous": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-previous/-/react-use-previous-1.0.1.tgz",
|
||||
"integrity": "sha512-cV5La9DPwiQ7S0gf/0qiD6YgNqM5Fk97Kdrlc5yBcrF3jyEZQwm7vYFqMo4IfeHgJXsRaMvLABFtd0OVEmZhDw==",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.13.10"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-use-rect": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-rect/-/react-use-rect-1.0.1.tgz",
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
"@radix-ui/react-icons": "^1.3.0",
|
||||
"@radix-ui/react-label": "^2.0.2",
|
||||
"@radix-ui/react-slot": "^1.0.2",
|
||||
"@radix-ui/react-switch": "^1.0.3",
|
||||
"@radix-ui/react-tooltip": "^1.0.7",
|
||||
"@vaadin/bundles": "24.3.0",
|
||||
"@vaadin/common-frontend": "0.0.19",
|
||||
|
||||
Reference in New Issue
Block a user