Finish theming implementation (finally...)

This commit is contained in:
grimsi
2024-05-14 10:22:17 +02:00
parent 47f8febbd2
commit f9d4e16604
45 changed files with 475 additions and 14951 deletions
+1 -1
View File
@@ -1,5 +1,5 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="UI debug" type="JavascriptDebugType" uri="http://localhost:8080" useFirstLineBreakpoints="true">
<configuration default="false" name="UI debug" type="JavascriptDebugType" engineId="37cae5b9-e8b2-4949-9172-aafa37fbc09c" uri="http://localhost:8080" useFirstLineBreakpoints="true">
<method v="2" />
</configuration>
</component>
-16
View File
@@ -1,16 +0,0 @@
import * as React from "react"
import {Provider as JotaiProvider} from "jotai"
import {ThemeProvider as NextThemesProvider} from "next-themes"
import {ThemeProviderProps} from "next-themes/dist/types"
import {TooltipProvider} from "Frontend/@/components/ui/tooltip";
export function ThemeProvider({children, ...props}: ThemeProviderProps) {
return (
<JotaiProvider>
<NextThemesProvider {...props}>
<TooltipProvider delayDuration={0}>{children}</TooltipProvider>
</NextThemesProvider>
</JotaiProvider>
)
}
+1 -2
View File
@@ -1,7 +1,6 @@
import * as React from "react"
import {cva, type VariantProps} from "class-variance-authority"
import {cn} from "Frontend/@/lib/utils"
import {cn} from "Frontend/util/utils";
const alertVariants = cva(
"relative w-full rounded-lg border px-4 py-3 text-sm [&>svg+div]:translate-y-[-3px] [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 [&>svg]:text-foreground [&>svg~*]:pl-7",
-48
View File
@@ -1,48 +0,0 @@
import * as React from "react"
import * as AvatarPrimitive from "@radix-ui/react-avatar"
import {cn} from "Frontend/@/lib/utils"
const Avatar = React.forwardRef<
React.ElementRef<typeof AvatarPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Root>
>(({className, ...props}, ref) => (
<AvatarPrimitive.Root
ref={ref}
className={cn(
"relative flex h-10 w-10 shrink-0 overflow-hidden rounded-full",
className
)}
{...props}
/>
))
Avatar.displayName = AvatarPrimitive.Root.displayName
const AvatarImage = React.forwardRef<
React.ElementRef<typeof AvatarPrimitive.Image>,
React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Image>
>(({className, ...props}, ref) => (
<AvatarPrimitive.Image
ref={ref}
className={cn("aspect-square h-full w-full", className)}
{...props}
/>
))
AvatarImage.displayName = AvatarPrimitive.Image.displayName
const AvatarFallback = React.forwardRef<
React.ElementRef<typeof AvatarPrimitive.Fallback>,
React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Fallback>
>(({className, ...props}, ref) => (
<AvatarPrimitive.Fallback
ref={ref}
className={cn(
"flex h-full w-full items-center justify-center rounded-full bg-muted",
className
)}
{...props}
/>
))
AvatarFallback.displayName = AvatarPrimitive.Fallback.displayName
export {Avatar, AvatarImage, AvatarFallback}
-58
View File
@@ -1,58 +0,0 @@
import * as React from "react"
import {Slot} from "@radix-ui/react-slot"
import {cva, type VariantProps} from "class-variance-authority"
import {cn} from "Frontend/@/lib/utils"
const buttonVariants = cva(
"inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50",
{
variants: {
variant: {
default:
"bg-primary text-primary-foreground shadow hover:bg-primary/90",
destructive:
"bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90",
outline:
"border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground",
secondary:
"bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80",
ghost: "hover:bg-accent hover:text-accent-foreground",
link: "text-primary underline-offset-4 hover:underline",
},
size: {
default: "h-9 px-4 py-2",
sm: "h-8 rounded-md px-3 text-xs",
lg: "h-10 rounded-md px-8",
icon: "h-9 w-9",
},
},
defaultVariants: {
variant: "default",
size: "default",
},
}
)
export interface ButtonProps
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
VariantProps<typeof buttonVariants> {
asChild?: boolean
}
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
({className, variant, size, asChild = false, ...props}, ref) => {
const Comp = asChild ? Slot : "button"
return (
<Comp
className={cn(buttonVariants({variant, size, className}))}
ref={ref}
type="button"
{...props}
/>
)
}
)
Button.displayName = "Button"
export {Button, buttonVariants}
-79
View File
@@ -1,79 +0,0 @@
import * as React from "react"
import {cn} from "Frontend/@/lib/utils"
const Card = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement>
>(({className, ...props}, ref) => (
<div
ref={ref}
className={cn(
"rounded-lg border bg-card text-card-foreground shadow-sm",
className
)}
{...props}
/>
))
Card.displayName = "Card"
const CardHeader = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement>
>(({className, ...props}, ref) => (
<div
ref={ref}
className={cn("flex flex-col space-y-1.5 p-6", className)}
{...props}
/>
))
CardHeader.displayName = "CardHeader"
const CardTitle = React.forwardRef<
HTMLParagraphElement,
React.HTMLAttributes<HTMLHeadingElement>
>(({className, ...props}, ref) => (
<h3
ref={ref}
className={cn(
"text-2xl font-semibold leading-none tracking-tight",
className
)}
{...props}
/>
))
CardTitle.displayName = "CardTitle"
const CardDescription = React.forwardRef<
HTMLParagraphElement,
React.HTMLAttributes<HTMLParagraphElement>
>(({className, ...props}, ref) => (
<p
ref={ref}
className={cn("text-sm text-muted-foreground", className)}
{...props}
/>
))
CardDescription.displayName = "CardDescription"
const CardContent = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement>
>(({className, ...props}, ref) => (
<div ref={ref} className={cn("p-6 pt-0", className)} {...props} />
))
CardContent.displayName = "CardContent"
const CardFooter = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement>
>(({className, ...props}, ref) => (
<div
ref={ref}
className={cn("flex items-center p-6 pt-0", className)}
{...props}
/>
))
CardFooter.displayName = "CardFooter"
export {Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent}
-199
View File
@@ -1,199 +0,0 @@
import * as React from "react"
import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu"
import {CheckIcon, ChevronRightIcon, DotFilledIcon,} from "@radix-ui/react-icons"
import {cn} from "Frontend/@/lib/utils"
const DropdownMenu = DropdownMenuPrimitive.Root
const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger
const DropdownMenuGroup = DropdownMenuPrimitive.Group
const DropdownMenuPortal = DropdownMenuPrimitive.Portal
const DropdownMenuSub = DropdownMenuPrimitive.Sub
const DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup
const DropdownMenuSubTrigger = React.forwardRef<
React.ElementRef<typeof DropdownMenuPrimitive.SubTrigger>,
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubTrigger> & {
inset?: boolean
}
>(({className, inset, children, ...props}, ref) => (
<DropdownMenuPrimitive.SubTrigger
ref={ref}
className={cn(
"flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent data-[state=open]:bg-accent",
inset && "pl-8",
className
)}
{...props}
>
{children}
<ChevronRightIcon className="ml-auto h-4 w-4"/>
</DropdownMenuPrimitive.SubTrigger>
))
DropdownMenuSubTrigger.displayName =
DropdownMenuPrimitive.SubTrigger.displayName
const DropdownMenuSubContent = React.forwardRef<
React.ElementRef<typeof DropdownMenuPrimitive.SubContent>,
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubContent>
>(({className, ...props}, ref) => (
<DropdownMenuPrimitive.SubContent
ref={ref}
className={cn(
"z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-lg data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
className
)}
{...props}
/>
))
DropdownMenuSubContent.displayName =
DropdownMenuPrimitive.SubContent.displayName
const DropdownMenuContent = React.forwardRef<
React.ElementRef<typeof DropdownMenuPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Content>
>(({className, sideOffset = 4, ...props}, ref) => (
<DropdownMenuPrimitive.Portal>
<DropdownMenuPrimitive.Content
ref={ref}
sideOffset={sideOffset}
className={cn(
"z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md",
"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
className
)}
{...props}
/>
</DropdownMenuPrimitive.Portal>
))
DropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName
const DropdownMenuItem = React.forwardRef<
React.ElementRef<typeof DropdownMenuPrimitive.Item>,
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Item> & {
inset?: boolean
}
>(({className, inset, ...props}, ref) => (
<DropdownMenuPrimitive.Item
ref={ref}
className={cn(
"relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
inset && "pl-8",
className
)}
{...props}
/>
))
DropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName
const DropdownMenuCheckboxItem = React.forwardRef<
React.ElementRef<typeof DropdownMenuPrimitive.CheckboxItem>,
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.CheckboxItem>
>(({className, children, checked, ...props}, ref) => (
<DropdownMenuPrimitive.CheckboxItem
ref={ref}
className={cn(
"relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
className
)}
checked={checked}
{...props}
>
<span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
<DropdownMenuPrimitive.ItemIndicator>
<CheckIcon className="h-4 w-4"/>
</DropdownMenuPrimitive.ItemIndicator>
</span>
{children}
</DropdownMenuPrimitive.CheckboxItem>
))
DropdownMenuCheckboxItem.displayName =
DropdownMenuPrimitive.CheckboxItem.displayName
const DropdownMenuRadioItem = React.forwardRef<
React.ElementRef<typeof DropdownMenuPrimitive.RadioItem>,
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.RadioItem>
>(({className, children, ...props}, ref) => (
<DropdownMenuPrimitive.RadioItem
ref={ref}
className={cn(
"relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
className
)}
{...props}
>
<span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
<DropdownMenuPrimitive.ItemIndicator>
<DotFilledIcon className="h-4 w-4 fill-current"/>
</DropdownMenuPrimitive.ItemIndicator>
</span>
{children}
</DropdownMenuPrimitive.RadioItem>
))
DropdownMenuRadioItem.displayName = DropdownMenuPrimitive.RadioItem.displayName
const DropdownMenuLabel = React.forwardRef<
React.ElementRef<typeof DropdownMenuPrimitive.Label>,
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Label> & {
inset?: boolean
}
>(({className, inset, ...props}, ref) => (
<DropdownMenuPrimitive.Label
ref={ref}
className={cn(
"px-2 py-1.5 text-sm font-semibold",
inset && "pl-8",
className
)}
{...props}
/>
))
DropdownMenuLabel.displayName = DropdownMenuPrimitive.Label.displayName
const DropdownMenuSeparator = React.forwardRef<
React.ElementRef<typeof DropdownMenuPrimitive.Separator>,
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Separator>
>(({className, ...props}, ref) => (
<DropdownMenuPrimitive.Separator
ref={ref}
className={cn("-mx-1 my-1 h-px bg-muted", className)}
{...props}
/>
))
DropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName
const DropdownMenuShortcut = ({
className,
...props
}: React.HTMLAttributes<HTMLSpanElement>) => {
return (
<span
className={cn("ml-auto text-xs tracking-widest opacity-60", className)}
{...props}
/>
)
}
DropdownMenuShortcut.displayName = "DropdownMenuShortcut"
export {
DropdownMenu,
DropdownMenuTrigger,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuCheckboxItem,
DropdownMenuRadioItem,
DropdownMenuLabel,
DropdownMenuSeparator,
DropdownMenuShortcut,
DropdownMenuGroup,
DropdownMenuPortal,
DropdownMenuSub,
DropdownMenuSubContent,
DropdownMenuSubTrigger,
DropdownMenuRadioGroup,
}
-26
View File
@@ -1,26 +0,0 @@
import * as React from "react"
import {cn} from "Frontend/@/lib/utils"
export interface InputProps
extends React.InputHTMLAttributes<HTMLInputElement> {
}
const Input = React.forwardRef<HTMLInputElement, InputProps>(
({className, type, ...props}, ref) => {
return (
<input
type={type}
className={cn(
"flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50",
className
)}
ref={ref}
{...props}
/>
)
}
)
Input.displayName = "Input"
export {Input}
-24
View File
@@ -1,24 +0,0 @@
import * as React from "react"
import * as LabelPrimitive from "@radix-ui/react-label"
import {cva, type VariantProps} from "class-variance-authority"
import {cn} from "Frontend/@/lib/utils"
const labelVariants = cva(
"text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
)
const Label = React.forwardRef<
React.ElementRef<typeof LabelPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root> &
VariantProps<typeof labelVariants>
>(({className, ...props}, ref) => (
<LabelPrimitive.Root
ref={ref}
className={cn(labelVariants(), className)}
{...props}
/>
))
Label.displayName = LabelPrimitive.Root.displayName
export {Label}
-27
View File
@@ -1,27 +0,0 @@
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}
-28
View File
@@ -1,28 +0,0 @@
import * as React from "react"
import * as TooltipPrimitive from "@radix-ui/react-tooltip"
import {cn} from "Frontend/@/lib/utils"
const TooltipProvider = TooltipPrimitive.Provider
const Tooltip = TooltipPrimitive.Root
const TooltipTrigger = TooltipPrimitive.Trigger
const TooltipContent = React.forwardRef<
React.ElementRef<typeof TooltipPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Content>
>(({className, sideOffset = 4, ...props}, ref) => (
<TooltipPrimitive.Content
ref={ref}
sideOffset={sideOffset}
className={cn(
"z-50 overflow-hidden rounded-md bg-primary px-3 py-1.5 text-xs text-primary-foreground animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
className
)}
{...props}
/>
))
TooltipContent.displayName = TooltipPrimitive.Content.displayName
export {Tooltip, TooltipTrigger, TooltipContent, TooltipProvider}
-22
View File
@@ -1,22 +0,0 @@
import {useAtom} from "jotai"
import {atomWithStorage} from "jotai/utils"
import {Theme} from "@/registry/themes"
type Config = {
theme: {
name: Theme["name"],
mode: "light" | "dark" | "system"
}
}
const configAtom = atomWithStorage<Config>("config", {
theme: {
name: "zinc",
mode: "system"
}
})
export function useConfig() {
return useAtom(configAtom)
}
File diff suppressed because it is too large Load Diff
-478
View File
@@ -1,478 +0,0 @@
export const themes = [
{
name: "zinc",
label: "Zinc",
activeColor: {
light: "240 5.9% 10%",
dark: "240 5.2% 33.9%",
},
cssVars: {
light: {
background: "0 0% 100%",
foreground: "240 10% 3.9%",
card: "0 0% 100%",
"card-foreground": "240 10% 3.9%",
popover: "0 0% 100%",
"popover-foreground": "240 10% 3.9%",
primary: "240 5.9% 10%",
"primary-foreground": "0 0% 98%",
secondary: "240 4.8% 95.9%",
"secondary-foreground": "240 5.9% 10%",
muted: "240 4.8% 95.9%",
"muted-foreground": "240 3.8% 46.1%",
accent: "240 4.8% 95.9%",
"accent-foreground": "240 5.9% 10%",
destructive: "0 84.2% 60.2%",
"destructive-foreground": "0 0% 98%",
border: "240 5.9% 90%",
input: "240 5.9% 90%",
ring: "240 5.9% 10%",
radius: "0.5rem",
},
dark: {
background: "240 10% 3.9%",
foreground: "0 0% 98%",
card: "240 10% 3.9%",
"card-foreground": "0 0% 98%",
popover: "240 10% 3.9%",
"popover-foreground": "0 0% 98%",
primary: "0 0% 98%",
"primary-foreground": "240 5.9% 10%",
secondary: "240 3.7% 15.9%",
"secondary-foreground": "0 0% 98%",
muted: "240 3.7% 15.9%",
"muted-foreground": "240 5% 64.9%",
accent: "240 3.7% 15.9%",
"accent-foreground": "0 0% 98%",
destructive: "0 62.8% 30.6%",
"destructive-foreground": "0 0% 98%",
border: "240 3.7% 15.9%",
input: "240 3.7% 15.9%",
ring: "240 4.9% 83.9%",
},
},
},
{
name: "slate",
label: "Slate",
activeColor: {
light: "215.4 16.3% 46.9%",
dark: "215.3 19.3% 34.5%",
},
cssVars: {
light: {
background: "0 0% 100%",
foreground: "222.2 84% 4.9%",
card: "0 0% 100%",
"card-foreground": "222.2 84% 4.9%",
popover: "0 0% 100%",
"popover-foreground": "222.2 84% 4.9%",
primary: "222.2 47.4% 11.2%",
"primary-foreground": "210 40% 98%",
secondary: "210 40% 96.1%",
"secondary-foreground": "222.2 47.4% 11.2%",
muted: "210 40% 96.1%",
"muted-foreground": "215.4 16.3% 46.9%",
accent: "210 40% 96.1%",
"accent-foreground": "222.2 47.4% 11.2%",
destructive: "0 84.2% 60.2%",
"destructive-foreground": "210 40% 98%",
border: "214.3 31.8% 91.4%",
input: "214.3 31.8% 91.4%",
ring: "222.2 84% 4.9%",
radius: "0.5rem",
},
dark: {
background: "222.2 84% 4.9%",
foreground: "210 40% 98%",
card: "222.2 84% 4.9%",
"card-foreground": "210 40% 98%",
popover: "222.2 84% 4.9%",
"popover-foreground": "210 40% 98%",
primary: "210 40% 98%",
"primary-foreground": "222.2 47.4% 11.2%",
secondary: "217.2 32.6% 17.5%",
"secondary-foreground": "210 40% 98%",
muted: "217.2 32.6% 17.5%",
"muted-foreground": "215 20.2% 65.1%",
accent: "217.2 32.6% 17.5%",
"accent-foreground": "210 40% 98%",
destructive: "0 62.8% 30.6%",
"destructive-foreground": "210 40% 98%",
border: "217.2 32.6% 17.5%",
input: "217.2 32.6% 17.5%",
ring: "212.7 26.8% 83.9",
},
},
},
{
name: "red",
label: "Red",
activeColor: {
light: "0 72.2% 50.6%",
dark: "0 72.2% 50.6%",
},
cssVars: {
light: {
background: "0 0% 100%",
foreground: "0 0% 3.9%",
card: "0 0% 100%",
"card-foreground": "0 0% 3.9%",
popover: "0 0% 100%",
"popover-foreground": "0 0% 3.9%",
primary: "0 72.2% 50.6%",
"primary-foreground": "0 85.7% 97.3%",
secondary: "0 0% 96.1%",
"secondary-foreground": "0 0% 9%",
muted: "0 0% 96.1%",
"muted-foreground": "0 0% 45.1%",
accent: "0 0% 96.1%",
"accent-foreground": "0 0% 9%",
destructive: "0 84.2% 60.2%",
"destructive-foreground": "0 0% 98%",
border: "0 0% 89.8%",
input: "0 0% 89.8%",
ring: "0 72.2% 50.6%",
radius: "0.4rem",
},
dark: {
background: "0 0% 3.9%",
foreground: "0 0% 98%",
card: "0 0% 3.9%",
"card-foreground": "0 0% 98%",
popover: "0 0% 3.9%",
"popover-foreground": "0 0% 98%",
primary: "0 72.2% 50.6%",
"primary-foreground": "0 85.7% 97.3%",
secondary: "0 0% 14.9%",
"secondary-foreground": "0 0% 98%",
muted: "0 0% 14.9%",
"muted-foreground": "0 0% 63.9%",
accent: "0 0% 14.9%",
"accent-foreground": "0 0% 98%",
destructive: "0 62.8% 30.6%",
"destructive-foreground": "0 0% 98%",
border: "0 0% 14.9%",
input: "0 0% 14.9%",
ring: "0 72.2% 50.6%",
},
},
},
{
name: "rose",
label: "Rose",
activeColor: {
light: "346.8 77.2% 49.8%",
dark: "346.8 77.2% 49.8%",
},
cssVars: {
light: {
background: "0 0% 100%",
foreground: "240 10% 3.9%",
card: "0 0% 100%",
"card-foreground": "240 10% 3.9%",
popover: "0 0% 100%",
"popover-foreground": "240 10% 3.9%",
primary: "346.8 77.2% 49.8%",
"primary-foreground": "355.7 100% 97.3%",
secondary: "240 4.8% 95.9%",
"secondary-foreground": "240 5.9% 10%",
muted: "240 4.8% 95.9%",
"muted-foreground": "240 3.8% 46.1%",
accent: "240 4.8% 95.9%",
"accent-foreground": "240 5.9% 10%",
destructive: "0 84.2% 60.2%",
"destructive-foreground": "0 0% 98%",
border: "240 5.9% 90%",
input: "240 5.9% 90%",
ring: "346.8 77.2% 49.8%",
radius: "0.5rem",
},
dark: {
background: "20 14.3% 4.1%",
foreground: "0 0% 95%",
popover: "0 0% 9%",
"popover-foreground": "0 0% 95%",
card: "24 9.8% 10%",
"card-foreground": "0 0% 95%",
primary: "346.8 77.2% 49.8%",
"primary-foreground": "355.7 100% 97.3%",
secondary: "240 3.7% 15.9%",
"secondary-foreground": "0 0% 98%",
muted: "0 0% 15%",
"muted-foreground": "240 5% 64.9%",
accent: "12 6.5% 15.1%",
"accent-foreground": "0 0% 98%",
destructive: "0 62.8% 30.6%",
"destructive-foreground": "0 85.7% 97.3%",
border: "240 3.7% 15.9%",
input: "240 3.7% 15.9%",
ring: "346.8 77.2% 49.8%",
},
},
},
{
name: "orange",
label: "Orange",
activeColor: {
light: "24.6 95% 53.1%",
dark: "20.5 90.2% 48.2%",
},
cssVars: {
light: {
background: "0 0% 100%",
foreground: "20 14.3% 4.1%",
card: "0 0% 100%",
"card-foreground": "20 14.3% 4.1%",
popover: "0 0% 100%",
"popover-foreground": "20 14.3% 4.1%",
primary: "24.6 95% 53.1%",
"primary-foreground": "60 9.1% 97.8%",
secondary: "60 4.8% 95.9%",
"secondary-foreground": "24 9.8% 10%",
muted: "60 4.8% 95.9%",
"muted-foreground": "25 5.3% 44.7%",
accent: "60 4.8% 95.9%",
"accent-foreground": "24 9.8% 10%",
destructive: "0 84.2% 60.2%",
"destructive-foreground": "60 9.1% 97.8%",
border: "20 5.9% 90%",
input: "20 5.9% 90%",
ring: "24.6 95% 53.1%",
radius: "0.95rem",
},
dark: {
background: "20 14.3% 4.1%",
foreground: "60 9.1% 97.8%",
card: "20 14.3% 4.1%",
"card-foreground": "60 9.1% 97.8%",
popover: "20 14.3% 4.1%",
"popover-foreground": "60 9.1% 97.8%",
primary: "20.5 90.2% 48.2%",
"primary-foreground": "60 9.1% 97.8%",
secondary: "12 6.5% 15.1%",
"secondary-foreground": "60 9.1% 97.8%",
muted: "12 6.5% 15.1%",
"muted-foreground": "24 5.4% 63.9%",
accent: "12 6.5% 15.1%",
"accent-foreground": "60 9.1% 97.8%",
destructive: "0 72.2% 50.6%",
"destructive-foreground": "60 9.1% 97.8%",
border: "12 6.5% 15.1%",
input: "12 6.5% 15.1%",
ring: "20.5 90.2% 48.2%",
},
},
},
{
name: "green",
label: "Green",
activeColor: {
light: "142.1 76.2% 36.3%",
dark: "142.1 70.6% 45.3%",
},
cssVars: {
light: {
background: "0 0% 100%",
foreground: "240 10% 3.9%",
card: "0 0% 100%",
"card-foreground": "240 10% 3.9%",
popover: "0 0% 100%",
"popover-foreground": "240 10% 3.9%",
primary: "142.1 76.2% 36.3%",
"primary-foreground": "355.7 100% 97.3%",
secondary: "240 4.8% 95.9%",
"secondary-foreground": "240 5.9% 10%",
muted: "240 4.8% 95.9%",
"muted-foreground": "240 3.8% 46.1%",
accent: "240 4.8% 95.9%",
"accent-foreground": "240 5.9% 10%",
destructive: "0 84.2% 60.2%",
"destructive-foreground": "0 0% 98%",
border: "240 5.9% 90%",
input: "240 5.9% 90%",
ring: "142.1 76.2% 36.3%",
},
dark: {
background: "20 14.3% 4.1%",
foreground: "0 0% 95%",
popover: "0 0% 9%",
"popover-foreground": "0 0% 95%",
card: "24 9.8% 10%",
"card-foreground": "0 0% 95%",
primary: "142.1 70.6% 45.3%",
"primary-foreground": "144.9 80.4% 10%",
secondary: "240 3.7% 15.9%",
"secondary-foreground": "0 0% 98%",
muted: "0 0% 15%",
"muted-foreground": "240 5% 64.9%",
accent: "12 6.5% 15.1%",
"accent-foreground": "0 0% 98%",
destructive: "0 62.8% 30.6%",
"destructive-foreground": "0 85.7% 97.3%",
border: "240 3.7% 15.9%",
input: "240 3.7% 15.9%",
ring: "142.4 71.8% 29.2%",
},
},
},
{
name: "blue",
label: "Blue",
activeColor: {
light: "221.2 83.2% 53.3%",
dark: "217.2 91.2% 59.8%",
},
cssVars: {
light: {
background: "0 0% 100%",
foreground: "222.2 84% 4.9%",
card: "0 0% 100%",
"card-foreground": "222.2 84% 4.9%",
popover: "0 0% 100%",
"popover-foreground": "222.2 84% 4.9%",
primary: "221.2 83.2% 53.3%",
"primary-foreground": "210 40% 98%",
secondary: "210 40% 96.1%",
"secondary-foreground": "222.2 47.4% 11.2%",
muted: "210 40% 96.1%",
"muted-foreground": "215.4 16.3% 46.9%",
accent: "210 40% 96.1%",
"accent-foreground": "222.2 47.4% 11.2%",
destructive: "0 84.2% 60.2%",
"destructive-foreground": "210 40% 98%",
border: "214.3 31.8% 91.4%",
input: "214.3 31.8% 91.4%",
ring: "221.2 83.2% 53.3%",
},
dark: {
background: "222.2 84% 4.9%",
foreground: "210 40% 98%",
card: "222.2 84% 4.9%",
"card-foreground": "210 40% 98%",
popover: "222.2 84% 4.9%",
"popover-foreground": "210 40% 98%",
primary: "217.2 91.2% 59.8%",
"primary-foreground": "222.2 47.4% 11.2%",
secondary: "217.2 32.6% 17.5%",
"secondary-foreground": "210 40% 98%",
muted: "217.2 32.6% 17.5%",
"muted-foreground": "215 20.2% 65.1%",
accent: "217.2 32.6% 17.5%",
"accent-foreground": "210 40% 98%",
destructive: "0 62.8% 30.6%",
"destructive-foreground": "210 40% 98%",
border: "217.2 32.6% 17.5%",
input: "217.2 32.6% 17.5%",
ring: "224.3 76.3% 48%",
},
},
},
{
name: "yellow",
label: "Yellow",
activeColor: {
light: "47.9 95.8% 53.1%",
dark: "47.9 95.8% 53.1%",
},
cssVars: {
light: {
background: "0 0% 100%",
foreground: "20 14.3% 4.1%",
card: "0 0% 100%",
"card-foreground": "20 14.3% 4.1%",
popover: "0 0% 100%",
"popover-foreground": "20 14.3% 4.1%",
primary: "47.9 95.8% 53.1%",
"primary-foreground": "26 83.3% 14.1%",
secondary: "60 4.8% 95.9%",
"secondary-foreground": "24 9.8% 10%",
muted: "60 4.8% 95.9%",
"muted-foreground": "25 5.3% 44.7%",
accent: "60 4.8% 95.9%",
"accent-foreground": "24 9.8% 10%",
destructive: "0 84.2% 60.2%",
"destructive-foreground": "60 9.1% 97.8%",
border: "20 5.9% 90%",
input: "20 5.9% 90%",
ring: "20 14.3% 4.1%",
radius: "0.95rem",
},
dark: {
background: "20 14.3% 4.1%",
foreground: "60 9.1% 97.8%",
card: "20 14.3% 4.1%",
"card-foreground": "60 9.1% 97.8%",
popover: "20 14.3% 4.1%",
"popover-foreground": "60 9.1% 97.8%",
primary: "47.9 95.8% 53.1%",
"primary-foreground": "26 83.3% 14.1%",
secondary: "12 6.5% 15.1%",
"secondary-foreground": "60 9.1% 97.8%",
muted: "12 6.5% 15.1%",
"muted-foreground": "24 5.4% 63.9%",
accent: "12 6.5% 15.1%",
"accent-foreground": "60 9.1% 97.8%",
destructive: "0 62.8% 30.6%",
"destructive-foreground": "60 9.1% 97.8%",
border: "12 6.5% 15.1%",
input: "12 6.5% 15.1%",
ring: "35.5 91.7% 32.9%",
},
},
},
{
name: "violet",
label: "Violet",
activeColor: {
light: "262.1 83.3% 57.8%",
dark: "263.4 70% 50.4%",
},
cssVars: {
light: {
background: "0 0% 100%",
foreground: "224 71.4% 4.1%",
card: "0 0% 100%",
"card-foreground": "224 71.4% 4.1%",
popover: "0 0% 100%",
"popover-foreground": "224 71.4% 4.1%",
primary: "262.1 83.3% 57.8%",
"primary-foreground": "210 20% 98%",
secondary: "220 14.3% 95.9%",
"secondary-foreground": "220.9 39.3% 11%",
muted: "220 14.3% 95.9%",
"muted-foreground": "220 8.9% 46.1%",
accent: "220 14.3% 95.9%",
"accent-foreground": "220.9 39.3% 11%",
destructive: "0 84.2% 60.2%",
"destructive-foreground": "210 20% 98%",
border: "220 13% 91%",
input: "220 13% 91%",
ring: "262.1 83.3% 57.8%",
},
dark: {
background: "224 71.4% 4.1%",
foreground: "210 20% 98%",
card: "224 71.4% 4.1%",
"card-foreground": "210 20% 98%",
popover: "224 71.4% 4.1%",
"popover-foreground": "210 20% 98%",
primary: "263.4 70% 50.4%",
"primary-foreground": "210 20% 98%",
secondary: "215 27.9% 16.9%",
"secondary-foreground": "210 20% 98%",
muted: "215 27.9% 16.9%",
"muted-foreground": "217.9 10.6% 64.9%",
accent: "215 27.9% 16.9%",
"accent-foreground": "210 20% 98%",
destructive: "0 62.8% 30.6%",
"destructive-foreground": "210 20% 98%",
border: "215 27.9% 16.9%",
input: "215 27.9% 16.9%",
ring: "263.4 70% 50.4%",
},
},
},
] as const
export type Theme = (typeof themes)[number]
+12 -12
View File
@@ -4,22 +4,22 @@ import {RouterProvider} from 'react-router-dom';
import "./main.css";
import {IconContext} from "@phosphor-icons/react";
import {StrictMode} from "react";
import {ThemeProvider} from "Frontend/@/components/theme-provider";
import {NextUIProvider} from "@nextui-org/react";
import {ThemeProvider as NextThemesProvider} from "next-themes";
import {themeNames} from "Frontend/theming/themes";
export default function App() {
return (
<StrictMode>
<AuthProvider>
<ThemeProvider
attribute="class"
defaultTheme="system"
enableSystem
>
<IconContext.Provider value={{size: 20}}>
<RouterProvider router={router}/>
</IconContext.Provider>
</ThemeProvider>
</AuthProvider>
<NextUIProvider className="size-full">
<NextThemesProvider attribute="class" defaultTheme="green-light" themes={themeNames()}>
<AuthProvider>
<IconContext.Provider value={{size: 20}}>
<RouterProvider router={router}/>
</IconContext.Provider>
</AuthProvider>
</NextThemesProvider>
</NextUIProvider>
</StrictMode>
);
}
+8 -11
View File
@@ -1,7 +1,6 @@
import {useField} from "formik";
import {XCircle} from "@phosphor-icons/react";
import {Input as ShadcnInput} from "Frontend/@/components/ui/input";
import {Label} from "Frontend/@/components/ui/label";
import {Input as NextUiInput} from "@nextui-org/react";
// @ts-ignore
const Input = ({label, ...props}) => {
@@ -10,19 +9,17 @@ const Input = ({label, ...props}) => {
return (
<div className="grid w-full max-w-sm items-center gap-1.5">
<Label htmlFor={label}>{label}</Label>
<ShadcnInput
<NextUiInput
{...props}
{...field}
id={label}
placeholder={label}
isInvalid={meta.touched && !!meta.error}
errorMessage={
<small className="flex flex-row items-center gap-1 text-danger">
<XCircle weight="fill" size={14}/> {meta.error}
</small>}
/>
{(meta.touched && !!meta.error) ?
<small
className="flex flex-row items-center gap-1 text-red-500"
>
<XCircle weight="fill" size={14}/> {meta.error}
</small> : <></>
}
</div>
);
}
+20 -23
View File
@@ -1,17 +1,9 @@
import {useState} from "react";
import {useAuth} from "Frontend/util/auth";
import {useNavigate} from "react-router-dom";
import {GearFine, Question, SignOut, User} from "@phosphor-icons/react";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger
} from "Frontend/@/components/ui/dropdown-menu";
import {Avatar, AvatarFallback} from "Frontend/@/components/ui/avatar";
import {Avatar, Dropdown, DropdownItem, DropdownMenu, DropdownTrigger} from "@nextui-org/react";
export default function ProfileMenu() {
const [isMenuOpen, setIsMenuOpen] = useState(false);
const {state, logout} = useAuth();
const navigate = useNavigate();
@@ -36,28 +28,33 @@ export default function ProfileMenu() {
label: "Sign Out",
icon: <SignOut/>,
onClick: () => logout(),
color: "red-500"
color: "danger"
},
];
return (
<DropdownMenu open={isMenuOpen}>
<DropdownMenuTrigger>
<Avatar>
<AvatarFallback>{state.user?.name?.substring(0, 2).toUpperCase()}</AvatarFallback>
</Avatar>
</DropdownMenuTrigger>
<DropdownMenuContent>
<Dropdown placement="bottom-end">
<DropdownTrigger>
<Avatar showFallback radius="full" as="button" className="transition-transform"/>
</DropdownTrigger>
<DropdownMenu>
{/* @ts-ignore */}
{profileMenuItems.map(({label, icon, onClick, showIf, color}) => {
return (
(showIf === undefined || showIf === true) ?
<DropdownMenuItem key={label} onClick={onClick}>
{icon}
<p color={color ? color : ""}>{label}</p>
</DropdownMenuItem> : null
<DropdownItem
key={label}
onClick={onClick}
startContent={<div color={color}>{icon}</div>}
/* @ts-ignore */
color={color ? color : ""}
className={`text-${color}`}
>
{label}
</DropdownItem> : null
);
})}
</DropdownMenuContent>
</DropdownMenu>
</DropdownMenu>
</Dropdown>
);
}
+6 -16
View File
@@ -1,26 +1,16 @@
import {hsl} from "Frontend/@/lib/utils";
export default function GameyfinLogo({primary, secondary, className}: {
primary: string,
secondary: string,
export default function GameyfinLogo({className}: {
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 49.13 190.1 69.24 207.98 44.13 190.1 49.13"/>
<polygon points="365.58 0 263.22 28.66 205.64 95.97 365.58 51.18 365.58 0"/>
<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}/>
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"/>
<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}/>
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"/>
<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}/>
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"/>
</svg>
);
}
+14 -30
View File
@@ -1,41 +1,25 @@
import {Theme} from "Frontend/@/registry/themes";
import {Card} from "Frontend/@/components/ui/card";
import {Tooltip, TooltipContent, TooltipTrigger} from "Frontend/@/components/ui/tooltip";
import {useTheme} from "next-themes";
import {Theme} from "Frontend/theming/theme";
import {Card, Tooltip} from "@nextui-org/react";
import GameyfinLogo from "Frontend/components/theming/GameyfinLogo";
import {hsl} from "Frontend/@/lib/utils";
export default function ThemePreview({theme}: { theme: Theme }) {
//@ts-ignore
let resolvedTheme: "light" | "dark" = useTheme().resolvedTheme ?? "light";
const {setTheme} = useTheme();
function toggleMode() {
resolvedTheme = resolvedTheme === "light" ? "dark" : "light";
setTheme(resolvedTheme);
}
export default function ThemePreview({theme, mode, isSelected}: {
theme: Theme,
mode: "light" | "dark",
isSelected?: boolean
}) {
return (
<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 content={<p className="capitalize">{theme.name?.replace("-", " ")}</p>} placement="bottom">
<Card
shadow="none"
className={`${theme.name}-${mode} flex-row justify-center p-6 border-2 ${isSelected ? "border-focus" : "border-foreground-200 hover:border-focus"}`}
>
<GameyfinLogo className="w-1/2 fill-primary"/>
</Card>
</Tooltip>
);
}
/*
<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"/>
+18 -14
View File
@@ -1,7 +1,8 @@
import React, {ReactNode, useState} from "react";
import {Form, Formik, FormikBag, FormikHelpers} from "formik";
import {ArrowLeft, ArrowRight, Check, SpinnerGap} from "@phosphor-icons/react";
import {Button} from "Frontend/@/components/ui/button";
import {ArrowLeft, ArrowRight, Check} from "@phosphor-icons/react";
import {Button} from "@nextui-org/react";
import {Step, Stepper} from "@material-tailwind/react";
const Wizard = ({children, initialValues, onSubmit}: {
children: ReactNode,
@@ -51,31 +52,34 @@ const Wizard = ({children, initialValues, onSubmit}: {
{formik => (
<Form className="flex flex-col h-full">
<div className="w-full mb-8">
<p>Step {stepNumber + 1} of {steps.length}</p>
{/*<Stepper activeStep={stepNumber}>
{/*<p>Step {stepNumber + 1} of {steps.length}</p>*/}
<Stepper activeStep={stepNumber} activeLineClassName="bg-primary"
lineClassName="bg-foreground">
{steps.map((child, index) => (
<Step key={index}>
<Step key={index}
className="bg-foreground text-background"
activeClassName="bg-primary"
completedClassName="bg-primary">
{/*@ts-ignore*/}
{child.props.icon}
</Step>
))}
</Stepper>*/}
</Stepper>
</div>
<div className="flex grow">
{step}
</div>
<div className="bottom-0 w-full">
<div className="flex justify-between">
<Button onClick={() => previous(formik.values)} disabled={isFirstStep}
className="rounded-full">
<Button color="primary" onClick={() => previous(formik.values)} disabled={isFirstStep}>
<ArrowLeft/>
</Button>
<Button disabled={formik.isSubmitting}
className="rounded-full"
type="submit"
<Button
color="primary"
isLoading={formik.isSubmitting}
type="submit"
>
{formik.isSubmitting ?
<SpinnerGap className="animate-spin"/> : isLastStep ? <Check/> : <ArrowRight/>
}
{formik.isSubmitting ? "" : isLastStep ? <Check/> : <ArrowRight/>}
</Button>
</div>
</div>
+1 -1
View File
@@ -19,7 +19,7 @@
<!-- index.ts is included here automatically (either by the dev server or during the build) -->
</head>
<body>
<body class="text-foreground bg-background">
<div id="outlet"></div>
</body>
</html>
+1 -81
View File
@@ -1,83 +1,3 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
@layer base {
:root {
--background: 0 0% 100%;
--foreground: 222.2 84% 4.9%;
--card: 0 0% 100%;
--card-foreground: 222.2 84% 4.9%;
--popover: 0 0% 100%;
--popover-foreground: 222.2 84% 4.9%;
--primary: 222.2 47.4% 11.2%;
--primary-foreground: 210 40% 98%;
--secondary: 210 40% 96.1%;
--secondary-foreground: 222.2 47.4% 11.2%;
--muted: 210 40% 96.1%;
--muted-foreground: 215.4 16.3% 46.9%;
--accent: 210 40% 96.1%;
--accent-foreground: 222.2 47.4% 11.2%;
--destructive: 0 84.2% 60.2%;
--destructive-foreground: 210 40% 98%;
--border: 214.3 31.8% 91.4%;
--input: 214.3 31.8% 91.4%;
--ring: 222.2 84% 4.9%;
--radius: 0.5rem;
--warning: 38 92% 50%;
--warning-foreground: 48 96% 89%;
}
.dark {
--background: 222.2 84% 4.9%;
--foreground: 210 40% 98%;
--card: 222.2 84% 4.9%;
--card-foreground: 210 40% 98%;
--popover: 222.2 84% 4.9%;
--popover-foreground: 210 40% 98%;
--primary: 210 40% 98%;
--primary-foreground: 222.2 47.4% 11.2%;
--secondary: 217.2 32.6% 17.5%;
--secondary-foreground: 210 40% 98%;
--muted: 217.2 32.6% 17.5%;
--muted-foreground: 215 20.2% 65.1%;
--accent: 217.2 32.6% 17.5%;
--accent-foreground: 210 40% 98%;
--destructive: 0 62.8% 30.6%;
--destructive-foreground: 210 40% 98%;
--border: 217.2 32.6% 17.5%;
--input: 217.2 32.6% 17.5%;
--ring: 212.7 26.8% 83.9%;
--warning: 48 96% 89%;
--warning-foreground: 38 92% 50%;
}
}
@layer base {
* {
@apply border-border;
}
body {
@apply bg-background text-foreground;
}
}
@tailwind utilities;
+21
View File
@@ -0,0 +1,21 @@
export type Theme = {
name?: string,
colors: {
background?: string,
foreground?: string,
primary: {
50: string,
100: string,
200: string,
300: string,
400: string,
500: string,
600: string,
700: string,
800: string,
900: string,
DEFAULT: string
},
focus?: string,
}
}
+46
View File
@@ -0,0 +1,46 @@
import {GameyfinClassic} from "./themes/gameyfin-classic";
import {GameyfinBlue} from "./themes/gameyfin-blue";
import {GameyfinViolet} from "./themes/gameyfin-violet";
import {Purple} from "./themes/purple";
import {Neutral} from "./themes/neutral";
import {Slate} from "./themes/slate";
import {Red} from "./themes/red";
import {Rose} from "./themes/rose";
import {Blue} from "./themes/blue";
import {Yellow} from "./themes/yellow";
import {Violet} from "./themes/violet";
import {Orange} from "./themes/orange";
import {Theme} from "./theme";
import {ConfigTheme, ConfigThemes} from "@nextui-org/react";
function light(c: Theme): ConfigTheme {
let t: Theme = structuredClone(c);
delete t.name;
(t as ConfigTheme).extend = "light";
return t;
}
function dark(c: Theme): ConfigTheme {
let t: Theme = structuredClone(c);
delete t.name;
(t as ConfigTheme).extend = "dark";
return t;
}
export function compileThemes(themes: Theme[]): ConfigThemes {
let compiledThemes: any = {};
themes.forEach((c: Theme) => {
compiledThemes[`${c.name}-light`] = light(c);
compiledThemes[`${c.name}-dark`] = dark(c);
})
return compiledThemes;
}
export function themeNames(): string[] {
return Object.keys(compileThemes(themes));
}
export const themes: Theme[] = [GameyfinBlue, GameyfinViolet, GameyfinClassic, Neutral, Slate, Red, Rose, Orange, Purple, Blue, Yellow, Violet];
+20
View File
@@ -0,0 +1,20 @@
import {Theme} from '../theme';
export const Blue: Theme = {
name: 'blue',
colors: {
primary: {
50: '#e3eeff',
100: '#b6cdfe',
200: '#88abf7',
300: '#5b8af1',
400: '#2d69ec',
500: '#134fd2',
600: '#0b3da4',
700: '#052c77',
800: '#001a4a',
900: '#00091e',
DEFAULT: '#2563EB'
}
}
};
+20
View File
@@ -0,0 +1,20 @@
import {Theme} from '../theme';
export const GameyfinBlue: Theme = {
name: 'gameyfin-blue',
colors: {
primary: {
50: '#e7eaff',
100: '#bdc3f9',
200: '#919bee',
300: '#6672e5',
400: '#3c4add',
500: '#2231c3',
600: '#1a2699',
700: '#101b6f',
800: '#070f45',
900: '#02041d',
DEFAULT: '#2332c8'
}
}
};
@@ -0,0 +1,20 @@
import {Theme} from '../theme';
export const GameyfinClassic: Theme = {
name: 'gameyfin-classic',
colors: {
primary: {
50: '#e1ffec',
100: '#b8f7cf',
200: '#8ef0b2',
300: '#62ea94',
400: '#38e476',
500: '#20ca5d',
600: '#159d47',
700: '#0b7032',
800: '#02431d',
900: '#001804',
DEFAULT: '#16A34A'
}
}
};
@@ -0,0 +1,20 @@
import {Theme} from '../theme';
export const GameyfinViolet: Theme = {
name: 'gameyfin-violet',
colors: {
primary: {
50: '#f3ebff',
100: '#d5c7ed',
200: '#b7a4dd',
300: '#9a7fce',
400: '#7d5abe',
500: '#6441a5',
600: '#4e3281',
700: '#37235d',
800: '#21153a',
900: '#0d0519',
DEFAULT: '#6441a5'
}
}
};
+20
View File
@@ -0,0 +1,20 @@
import {Theme} from '../theme';
export const Neutral: Theme = {
name: 'neutral',
colors: {
primary: {
50: '#fceff2',
100: '#ddd7d9',
200: '#c1bfbf',
300: '#a6a6a6',
400: '#8c8c8c',
500: '#737373',
600: '#595959',
700: '#413f40',
800: '#292526',
900: '#16090d',
DEFAULT: '#525252'
}
}
};
+20
View File
@@ -0,0 +1,20 @@
import {Theme} from '../theme';
export const Orange: Theme = {
name: 'orange',
colors: {
primary: {
50: '#ffedde',
100: '#ffcdb2',
200: '#fbac84',
300: '#f78c54',
400: '#f46c25',
500: '#da520b',
600: '#ab3f07',
700: '#7a2d03',
800: '#4b1900',
900: '#1f0600',
DEFAULT: '#EA580C'
}
}
};
+20
View File
@@ -0,0 +1,20 @@
import {Theme} from "../theme";
export const Purple: Theme = {
name: 'purple',
colors: {
primary: {
50: '#ffe6ff',
100: '#f2b9f9',
200: '#e78df3',
300: '#dc5fed',
400: '#d132e6',
500: '#b91acd',
600: '#9012a0',
700: '#670b73',
800: '#3f0547',
900: '#19001b',
DEFAULT: '#DD62ED'
}
}
};
+20
View File
@@ -0,0 +1,20 @@
import {Theme} from '../theme';
export const Red: Theme = {
name: 'red',
colors: {
primary: {
50: '#ffe5e5',
100: '#f9bbbb',
200: '#ef9090',
300: '#e76464',
400: '#df3939',
500: '#c62020',
600: '#9b1718',
700: '#6f0f11',
800: '#450708',
900: '#1e0000',
DEFAULT: '#DC2626'
}
}
};
+20
View File
@@ -0,0 +1,20 @@
import {Theme} from "../theme";
export const Rose: Theme = {
name: 'rose',
colors: {
primary: {
50: '#ffe4ed',
100: '#fbb9c8',
200: '#f28da4',
300: '#ec607f',
400: '#e5345b',
500: '#cb1a41',
600: '#9f1233',
700: '#730b23',
800: '#470415',
900: '#1e0006',
DEFAULT: '#E11D48'
}
}
};
+20
View File
@@ -0,0 +1,20 @@
import {Theme} from "../theme";
export const Slate: Theme = {
name: 'slate',
colors: {
primary: {
50: '#eaf3ff',
100: '#cfd7e4',
200: '#b2bdcd',
300: '#95a3b7',
400: '#7788a1',
500: '#5e6f88',
600: '#48566a',
700: '#323e4d',
800: '#1d2531',
900: '#040d17',
DEFAULT: '#475569'
}
}
};
+20
View File
@@ -0,0 +1,20 @@
import {Theme} from '../theme';
export const Violet: Theme = {
name: 'violet',
colors: {
primary: {
50: '#f2e7ff',
100: '#d4bdf8',
200: '#b592ee',
300: '#9867e5',
400: '#7b3cdd',
500: '#6122c3',
600: '#4b1a99',
700: '#36126e',
800: '#200944',
900: '#0d021c',
DEFAULT: '#6D28D9'
}
}
};
+20
View File
@@ -0,0 +1,20 @@
import {Theme} from '../theme';
export const Yellow: Theme = {
name: 'yellow',
colors: {
primary: {
50: '#fffadb',
100: '#feefae',
200: '#fce47f',
300: '#fbd94e',
400: '#face1e',
500: '#e1b505',
600: '#af8c00',
700: '#7d6400',
800: '#4c3c00',
900: '#1c1400',
DEFAULT: '#FACC15'
}
}
};
@@ -17,4 +17,4 @@ 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);
}
}
+13 -17
View File
@@ -1,11 +1,9 @@
import {useAuth} from "Frontend/util/auth";
import {useState} from "react";
import {Link, useNavigate} from "react-router-dom";
import {SpinnerGap, XCircle} from "@phosphor-icons/react";
import {Card} from "Frontend/@/components/ui/card";
import {XCircle} from "@phosphor-icons/react";
import {Button, Card, CardBody, CardHeader, Input} from "@nextui-org/react";
import {Alert, AlertDescription, AlertTitle} from "Frontend/@/components/ui/alert";
import {Button} from "Frontend/@/components/ui/button";
import {Input} from "Frontend/@/components/ui/input";
export default function LoginView() {
const {state, login} = useAuth();
@@ -24,11 +22,14 @@ export default function LoginView() {
return (
<div className="flex size-full bg-gradient-to-br from-gf-primary to-gf-secondary">
<Card className="m-auto p-12">
<img
className="h-28 w-full content-center"
src="/images/Logo.svg"
/>
<div className="mt-8 mb-2 w-80 max-w-screen-lg sm:w-96">
<CardHeader>
<img
className="h-28 w-full content-center"
src="/images/Logo.svg"
alt="Gameyfin Logo"
/>
</CardHeader>
<CardBody className="mt-8 mb-2 w-80 max-w-screen-lg sm:w-96">
{hasError &&
<Alert className="mb-4" variant="destructive">
<XCircle weight="fill" className="size-4"/>
@@ -82,17 +83,12 @@ export default function LoginView() {
/>
<div className="flex justify-between items-center">
<Link to="#">Forgot password?</Link>
<Button
type="submit"
size="lg"
className="w-28 h-12 flex justify-center"
disabled={loading}
>
{loading ? <SpinnerGap className="size-6 animate-spin"/> : "Log in"}
<Button color="primary" type="submit" isLoading={loading}>
Log in
</Button>
</div>
</form>
</div>
</CardBody>
</Card>
</div>
);
+1 -1
View File
@@ -2,7 +2,7 @@ import {useRouteMetadata} from 'Frontend/util/routing.js';
import {useEffect} from 'react';
import ProfileMenu from "Frontend/components/ProfileMenu";
import {Outlet} from "react-router-dom";
import {Card} from "Frontend/@/components/ui/card";
import {Card} from "@nextui-org/react";
export default function MainLayout() {
const currentTitle = `Gameyfin - ${useRouteMetadata()?.title}` ?? 'Gameyfin';
+37 -14
View File
@@ -1,14 +1,14 @@
import React from 'react';
import React, {useLayoutEffect, useState} from 'react';
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, 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 {themes} from "Frontend/theming/themes";
import {Card, Switch} from "@nextui-org/react";
import {useTheme} from "next-themes";
import {Theme} from "Frontend/theming/theme";
const sleep = (ms: number) => new Promise(resolve => setTimeout(resolve, ms));
@@ -37,23 +37,46 @@ function WelcomeStep() {
}
function ThemeStep() {
const {setTheme, theme} = useTheme();
const {theme, setTheme} = useTheme();
const [isSelected, setIsSelected] = useState(theme ? theme.includes("light") : false);
const [currentTheme, setCurrentTheme] = useState(theme?.split('-')[0]);
function toggleMode() {
setTheme(theme === "light" ? "dark" : "light");
useLayoutEffect(() => setTheme(`${currentTheme}-${mode()}`), [isSelected]);
function mode(): "light" | "dark" {
return !isSelected ? "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}/>
<Switch
size="lg"
startContent={<SunDim size={32}/>}
endContent={<Moon size={32}/>}
isSelected={isSelected}
onValueChange={() => {
setIsSelected(!isSelected);
}}
/>
</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}/>
)))}
<div className="grid grid-cols-4 w-3/4 min-w-[468px] gap-12">
{
themes.map(((t: Theme) => (
<div
key={t.name}
onClick={() => {
setCurrentTheme(t.name);
setTheme(`${t.name}-${mode()}`);
}}>
<ThemePreview
theme={t}
mode={mode()}
isSelected={currentTheme === t.name}/>
</div>
)))
}
</div>
</div>
)
+5 -1
View File
@@ -1,5 +1,9 @@
import {Link} from "react-router-dom";
export default function TestView() {
return (
<h1>Hello Gameyfin!</h1>
<div className="size-full flex justify-center">
<Link to="/setup">Setup</Link>
</div>
);
}
+7
View File
@@ -0,0 +1,7 @@
import {NextUIPluginConfig} from "@nextui-org/react";
import {compileThemes, themes} from "./frontend/theming/themes"
export const NextUIConfig: NextUIPluginConfig = {
prefix: "gf",
themes: compileThemes(themes)
};
-12071
View File
File diff suppressed because it is too large Load Diff
+10 -22
View File
@@ -1,5 +1,5 @@
{
"name": "Gameyfin",
"name": "gameyfin",
"version": "2.0.0-ALPHA",
"type": "module",
"dependencies": {
@@ -18,15 +18,10 @@
"@hilla/react-components": "2.3.0",
"@hilla/react-crud": "2.5.6",
"@hilla/react-form": "2.5.6",
"@material-tailwind/react": "^2.1.9",
"@nextui-org/react": "^2.3.6",
"@phosphor-icons/react": "^2.0.15",
"@polymer/polymer": "3.5.1",
"@radix-ui/react-avatar": "^1.0.4",
"@radix-ui/react-dropdown-menu": "^2.0.6",
"@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",
"@vaadin/router": "1.7.5",
@@ -35,15 +30,13 @@
"clsx": "^2.1.0",
"construct-style-sheets-polyfill": "3.1.0",
"formik": "^2.4.5",
"jotai": "^2.7.1",
"framer-motion": "^11.1.7",
"lit": "3.1.0",
"lucide-react": "^0.360.0",
"next-themes": "^0.3.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-router-dom": "^6.4.2",
"tailwind-merge": "^2.2.2",
"tailwindcss-animate": "^1.0.7",
"tailwind-merge": "^2.3.0",
"yup": "^1.4.0"
},
"devDependencies": {
@@ -59,6 +52,7 @@
"autoprefixer": "^10.4.18",
"glob": "10.3.3",
"postcss": "^8.4.35",
"postcss-import": "^16.1.0",
"rollup-plugin-brotli": "3.1.0",
"rollup-plugin-visualizer": "5.9.2",
"strip-css-comments": "5.0.0",
@@ -102,17 +96,11 @@
"yup": "$yup",
"class-variance-authority": "$class-variance-authority",
"clsx": "$clsx",
"jotai": "$jotai",
"lucide-react": "$lucide-react",
"next-themes": "$next-themes",
"tailwind-merge": "$tailwind-merge",
"tailwindcss-animate": "$tailwindcss-animate",
"@radix-ui/react-label": "$@radix-ui/react-label",
"@radix-ui/react-slot": "$@radix-ui/react-slot",
"@radix-ui/react-avatar": "$@radix-ui/react-avatar",
"@radix-ui/react-dropdown-menu": "$@radix-ui/react-dropdown-menu",
"@radix-ui/react-tooltip": "$@radix-ui/react-tooltip",
"@radix-ui/react-icons": "$@radix-ui/react-icons"
"@nextui-org/react": "$@nextui-org/react",
"framer-motion": "$framer-motion",
"@material-tailwind/react": "$@material-tailwind/react"
},
"vaadin": {
"dependencies": {
@@ -156,6 +144,6 @@
"workbox-core": "7.0.0",
"workbox-precaching": "7.0.0"
},
"hash": "5375ee653b483f5c0b565f19670e73ec7e02e2abb121e622411201c6c6cb2431"
"hash": "a2234ddab9db9a07cd47eded5c9642efac70874179c7dadb6c6887c4fe6519a7"
}
}
+12 -72
View File
@@ -1,84 +1,24 @@
import {Config} from "tailwindcss/types/config";
import {nextui} from "@nextui-org/react";
import {NextUIConfig} from "./nextui";
import withMT from "@material-tailwind/react/utils/withMT";
export default {
export default withMT({
darkMode: "class",
content: [
'./frontend/index.html',
'./frontend/**/*.{js,ts,jsx,tsx}',
'./pages/**/*.{ts,tsx}',
'./components/**/*.{ts,tsx}',
'./app/**/*.{ts,tsx}',
'./src/**/*.{ts,tsx}'
'./node_modules/@nextui-org/theme/dist/**/*.{js,ts,jsx,tsx}'
],
prefix: "",
theme: {
container: {
center: true,
padding: "2rem",
screens: {
"2xl": "1400px",
},
},
extend: {
colors: {
'gf-primary': '#2332c8',
'gf-secondary': '#6441a5',
warning: "hsl(var(--warning))",
"warning-foreground": "hsl(var(--warning-foreground))",
border: "hsl(var(--border))",
input: "hsl(var(--input))",
ring: "hsl(var(--ring))",
background: "hsl(var(--background))",
foreground: "hsl(var(--foreground))",
primary: {
DEFAULT: "hsl(var(--primary))",
foreground: "hsl(var(--primary-foreground))",
},
secondary: {
DEFAULT: "hsl(var(--secondary))",
foreground: "hsl(var(--secondary-foreground))",
},
destructive: {
DEFAULT: "hsl(var(--destructive))",
foreground: "hsl(var(--destructive-foreground))",
},
muted: {
DEFAULT: "hsl(var(--muted))",
foreground: "hsl(var(--muted-foreground))",
},
accent: {
DEFAULT: "hsl(var(--accent))",
foreground: "hsl(var(--accent-foreground))",
},
popover: {
DEFAULT: "hsl(var(--popover))",
foreground: "hsl(var(--popover-foreground))",
},
card: {
DEFAULT: "hsl(var(--card))",
foreground: "hsl(var(--card-foreground))",
},
},
borderRadius: {
lg: "var(--radius)",
md: "calc(var(--radius) - 2px)",
sm: "calc(var(--radius) - 4px)",
},
keyframes: {
"accordion-down": {
from: {height: "0"},
to: {height: "var(--radix-accordion-content-height)"},
},
"accordion-up": {
from: {height: "var(--radix-accordion-content-height)"},
to: {height: "0"},
},
},
animation: {
"accordion-down": "accordion-down 0.2s ease-out",
"accordion-up": "accordion-up 0.2s ease-out",
},
},
'gf-secondary': '#6441a5'
}
}
},
plugins: [require("tailwindcss-animate")],
} satisfies Config;
plugins: [
nextui(NextUIConfig)
],
} satisfies Config);