From d30cd9706db3479fb45706d488ca6a41ddb00387 Mon Sep 17 00:00:00 2001 From: grimsi <9295182+grimsi@users.noreply.github.com> Date: Wed, 27 Mar 2024 18:26:47 +0100 Subject: [PATCH] Added dark/light mode (next: custom themes) --- frontend/@/components/theme-provider.tsx | 6 + frontend/@/components/ui/alert.tsx | 59 ++ frontend/@/components/ui/avatar.tsx | 48 + frontend/@/components/ui/button.tsx | 56 ++ frontend/@/components/ui/card.tsx | 79 ++ frontend/@/components/ui/dropdown-menu.tsx | 198 +++++ frontend/@/components/ui/input.tsx | 26 + frontend/@/components/ui/label.tsx | 24 + frontend/@/components/ui/tooltip.tsx | 28 + frontend/@/hooks/useConfig.ts | 2 +- frontend/App.tsx | 9 +- frontend/components/Input.tsx | 23 +- frontend/components/ProfileMenu.tsx | 48 +- frontend/components/theming/Theme.tsx | 10 - frontend/components/theming/ThemePreview.tsx | 7 +- frontend/components/theming/ThemeSwitcher.tsx | 24 - frontend/components/theming/Themes.tsx | 20 - frontend/components/wizard/Wizard.tsx | 14 +- frontend/components/wizard/WizardStep.tsx | 5 +- frontend/views/LoginView.tsx | 50 +- frontend/views/MainLayout.tsx | 6 +- frontend/views/SetupView.tsx | 25 +- package-lock.json | 832 +++++++++++++++++- package.json | 22 +- tailwind.config.js => tailwind.config.ts | 9 +- tsconfig.json | 12 +- vite.config.ts | 24 +- 27 files changed, 1479 insertions(+), 187 deletions(-) create mode 100644 frontend/@/components/theme-provider.tsx create mode 100644 frontend/@/components/ui/alert.tsx create mode 100644 frontend/@/components/ui/avatar.tsx create mode 100644 frontend/@/components/ui/button.tsx create mode 100644 frontend/@/components/ui/card.tsx create mode 100644 frontend/@/components/ui/dropdown-menu.tsx create mode 100644 frontend/@/components/ui/input.tsx create mode 100644 frontend/@/components/ui/label.tsx create mode 100644 frontend/@/components/ui/tooltip.tsx delete mode 100644 frontend/components/theming/Theme.tsx delete mode 100644 frontend/components/theming/ThemeSwitcher.tsx delete mode 100644 frontend/components/theming/Themes.tsx rename tailwind.config.js => tailwind.config.ts (91%) diff --git a/frontend/@/components/theme-provider.tsx b/frontend/@/components/theme-provider.tsx new file mode 100644 index 0000000..8ff6135 --- /dev/null +++ b/frontend/@/components/theme-provider.tsx @@ -0,0 +1,6 @@ +import {ThemeProvider as NextThemesProvider} from "next-themes" +import {type ThemeProviderProps} from "next-themes/dist/types" + +export function ThemeProvider({children, ...props}: ThemeProviderProps) { + return {children} +} \ No newline at end of file diff --git a/frontend/@/components/ui/alert.tsx b/frontend/@/components/ui/alert.tsx new file mode 100644 index 0000000..f3d3252 --- /dev/null +++ b/frontend/@/components/ui/alert.tsx @@ -0,0 +1,59 @@ +import * as React from "react" +import {cva, type VariantProps} from "class-variance-authority" + +import {cn} from "Frontend/@/lib/utils" + +const alertVariants = cva( + "relative w-full rounded-lg border border-slate-200 p-4 [&>svg~*]:pl-7 [&>svg+div]:translate-y-[-3px] [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 [&>svg]:text-slate-950 dark:border-slate-800 dark:[&>svg]:text-slate-50", + { + variants: { + variant: { + default: "bg-white text-slate-950 dark:bg-slate-950 dark:text-slate-50", + destructive: + "border-red-500/50 text-red-500 dark:border-red-500 [&>svg]:text-red-500 dark:border-red-900/50 dark:text-red-900 dark:dark:border-red-900 dark:[&>svg]:text-red-900", + }, + }, + defaultVariants: { + variant: "default", + }, + } +) + +const Alert = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes & VariantProps +>(({className, variant, ...props}, ref) => ( +
+)) +Alert.displayName = "Alert" + +const AlertTitle = React.forwardRef< + HTMLParagraphElement, + React.HTMLAttributes +>(({className, ...props}, ref) => ( +
+)) +AlertTitle.displayName = "AlertTitle" + +const AlertDescription = React.forwardRef< + HTMLParagraphElement, + React.HTMLAttributes +>(({className, ...props}, ref) => ( +
+)) +AlertDescription.displayName = "AlertDescription" + +export {Alert, AlertTitle, AlertDescription} diff --git a/frontend/@/components/ui/avatar.tsx b/frontend/@/components/ui/avatar.tsx new file mode 100644 index 0000000..e923abd --- /dev/null +++ b/frontend/@/components/ui/avatar.tsx @@ -0,0 +1,48 @@ +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, + React.ComponentPropsWithoutRef +>(({className, ...props}, ref) => ( + +)) +Avatar.displayName = AvatarPrimitive.Root.displayName + +const AvatarImage = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({className, ...props}, ref) => ( + +)) +AvatarImage.displayName = AvatarPrimitive.Image.displayName + +const AvatarFallback = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({className, ...props}, ref) => ( + +)) +AvatarFallback.displayName = AvatarPrimitive.Fallback.displayName + +export {Avatar, AvatarImage, AvatarFallback} diff --git a/frontend/@/components/ui/button.tsx b/frontend/@/components/ui/button.tsx new file mode 100644 index 0000000..a5cc5af --- /dev/null +++ b/frontend/@/components/ui/button.tsx @@ -0,0 +1,56 @@ +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 ring-offset-white transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-slate-950 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 dark:ring-offset-slate-950 dark:focus-visible:ring-slate-300", + { + variants: { + variant: { + default: "bg-slate-900 text-slate-50 hover:bg-slate-900/90 dark:bg-slate-50 dark:text-slate-900 dark:hover:bg-slate-50/90", + destructive: + "bg-red-500 text-slate-50 hover:bg-red-500/90 dark:bg-red-900 dark:text-slate-50 dark:hover:bg-red-900/90", + outline: + "border border-slate-200 bg-white hover:bg-slate-100 hover:text-slate-900 dark:border-slate-800 dark:bg-slate-950 dark:hover:bg-slate-800 dark:hover:text-slate-50", + secondary: + "bg-slate-100 text-slate-900 hover:bg-slate-100/80 dark:bg-slate-800 dark:text-slate-50 dark:hover:bg-slate-800/80", + ghost: "hover:bg-slate-100 hover:text-slate-900 dark:hover:bg-slate-800 dark:hover:text-slate-50", + link: "text-slate-900 underline-offset-4 hover:underline dark:text-slate-50", + }, + size: { + default: "h-10 px-4 py-2", + sm: "h-9 rounded-md px-3", + lg: "h-11 rounded-md px-8", + icon: "h-10 w-10", + }, + }, + defaultVariants: { + variant: "default", + size: "default", + }, + } +) + +export interface ButtonProps + extends React.ButtonHTMLAttributes, + VariantProps { + asChild?: boolean +} + +const Button = React.forwardRef( + ({className, variant, size, asChild = false, ...props}, ref) => { + const Comp = asChild ? Slot : "button" + return ( + + ) + } +) +Button.displayName = "Button" + +export {Button, buttonVariants} diff --git a/frontend/@/components/ui/card.tsx b/frontend/@/components/ui/card.tsx new file mode 100644 index 0000000..05500c5 --- /dev/null +++ b/frontend/@/components/ui/card.tsx @@ -0,0 +1,79 @@ +import * as React from "react" + +import {cn} from "Frontend/@/lib/utils" + +const Card = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({className, ...props}, ref) => ( +
+)) +Card.displayName = "Card" + +const CardHeader = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({className, ...props}, ref) => ( +
+)) +CardHeader.displayName = "CardHeader" + +const CardTitle = React.forwardRef< + HTMLParagraphElement, + React.HTMLAttributes +>(({className, ...props}, ref) => ( +

+)) +CardTitle.displayName = "CardTitle" + +const CardDescription = React.forwardRef< + HTMLParagraphElement, + React.HTMLAttributes +>(({className, ...props}, ref) => ( +

+)) +CardDescription.displayName = "CardDescription" + +const CardContent = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({className, ...props}, ref) => ( +

+)) +CardContent.displayName = "CardContent" + +const CardFooter = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({className, ...props}, ref) => ( +
+)) +CardFooter.displayName = "CardFooter" + +export {Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent} diff --git a/frontend/@/components/ui/dropdown-menu.tsx b/frontend/@/components/ui/dropdown-menu.tsx new file mode 100644 index 0000000..c331a09 --- /dev/null +++ b/frontend/@/components/ui/dropdown-menu.tsx @@ -0,0 +1,198 @@ +import * as React from "react" +import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu" +import {Check, ChevronRight, Circle} from "lucide-react" + +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, + React.ComponentPropsWithoutRef & { + inset?: boolean +} +>(({className, inset, children, ...props}, ref) => ( + + {children} + + +)) +DropdownMenuSubTrigger.displayName = + DropdownMenuPrimitive.SubTrigger.displayName + +const DropdownMenuSubContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({className, ...props}, ref) => ( + +)) +DropdownMenuSubContent.displayName = + DropdownMenuPrimitive.SubContent.displayName + +const DropdownMenuContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({className, sideOffset = 4, ...props}, ref) => ( + + + +)) +DropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName + +const DropdownMenuItem = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef & { + inset?: boolean +} +>(({className, inset, ...props}, ref) => ( + +)) +DropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName + +const DropdownMenuCheckboxItem = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({className, children, checked, ...props}, ref) => ( + + + + + + + {children} + +)) +DropdownMenuCheckboxItem.displayName = + DropdownMenuPrimitive.CheckboxItem.displayName + +const DropdownMenuRadioItem = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({className, children, ...props}, ref) => ( + + + + + + + {children} + +)) +DropdownMenuRadioItem.displayName = DropdownMenuPrimitive.RadioItem.displayName + +const DropdownMenuLabel = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef & { + inset?: boolean +} +>(({className, inset, ...props}, ref) => ( + +)) +DropdownMenuLabel.displayName = DropdownMenuPrimitive.Label.displayName + +const DropdownMenuSeparator = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({className, ...props}, ref) => ( + +)) +DropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName + +const DropdownMenuShortcut = ({ + className, + ...props + }: React.HTMLAttributes) => { + return ( + + ) +} +DropdownMenuShortcut.displayName = "DropdownMenuShortcut" + +export { + DropdownMenu, + DropdownMenuTrigger, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuCheckboxItem, + DropdownMenuRadioItem, + DropdownMenuLabel, + DropdownMenuSeparator, + DropdownMenuShortcut, + DropdownMenuGroup, + DropdownMenuPortal, + DropdownMenuSub, + DropdownMenuSubContent, + DropdownMenuSubTrigger, + DropdownMenuRadioGroup, +} diff --git a/frontend/@/components/ui/input.tsx b/frontend/@/components/ui/input.tsx new file mode 100644 index 0000000..ceb94d3 --- /dev/null +++ b/frontend/@/components/ui/input.tsx @@ -0,0 +1,26 @@ +import * as React from "react" + +import {cn} from "Frontend/@/lib/utils" + +export interface InputProps + extends React.InputHTMLAttributes { +} + +const Input = React.forwardRef( + ({className, type, ...props}, ref) => { + return ( + + ) + } +) +Input.displayName = "Input" + +export {Input} diff --git a/frontend/@/components/ui/label.tsx b/frontend/@/components/ui/label.tsx new file mode 100644 index 0000000..56d6ce3 --- /dev/null +++ b/frontend/@/components/ui/label.tsx @@ -0,0 +1,24 @@ +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, + React.ComponentPropsWithoutRef & + VariantProps +>(({className, ...props}, ref) => ( + +)) +Label.displayName = LabelPrimitive.Root.displayName + +export {Label} diff --git a/frontend/@/components/ui/tooltip.tsx b/frontend/@/components/ui/tooltip.tsx new file mode 100644 index 0000000..a453542 --- /dev/null +++ b/frontend/@/components/ui/tooltip.tsx @@ -0,0 +1,28 @@ +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, + React.ComponentPropsWithoutRef +>(({className, sideOffset = 4, ...props}, ref) => ( + +)) +TooltipContent.displayName = TooltipPrimitive.Content.displayName + +export {Tooltip, TooltipTrigger, TooltipContent, TooltipProvider} diff --git a/frontend/@/hooks/useConfig.ts b/frontend/@/hooks/useConfig.ts index 58fd14a..d63176a 100644 --- a/frontend/@/hooks/useConfig.ts +++ b/frontend/@/hooks/useConfig.ts @@ -1,7 +1,7 @@ import {useAtom} from "jotai" import {atomWithStorage} from "jotai/utils" -import {Theme} from "@/registry/themes" +import {Theme} from "Frontend/@/registry/themes" type Config = { theme: Theme["name"] diff --git a/frontend/App.tsx b/frontend/App.tsx index cf3a47a..ddf9fef 100644 --- a/frontend/App.tsx +++ b/frontend/App.tsx @@ -2,15 +2,20 @@ import router from 'Frontend/routes.js'; import {AuthProvider} from 'Frontend/util/auth.js'; import {RouterProvider} from 'react-router-dom'; import "./main.css"; -import {ThemeProvider} from "@material-tailwind/react"; import {IconContext} from "@phosphor-icons/react"; import {StrictMode} from "react"; +import {ThemeProvider} from "Frontend/@/components/theme-provider"; export default function App() { return ( - + diff --git a/frontend/components/Input.tsx b/frontend/components/Input.tsx index 148e5bb..a2b890d 100644 --- a/frontend/components/Input.tsx +++ b/frontend/components/Input.tsx @@ -1,6 +1,7 @@ import {useField} from "formik"; -import {Input as MaterialInput, Typography} from "@material-tailwind/react"; import {XCircle} from "@phosphor-icons/react"; +import {Input as ShadcnInput} from "Frontend/@/components/ui/input"; +import {Label} from "Frontend/@/components/ui/label"; // @ts-ignore const Input = ({label, ...props}) => { @@ -8,25 +9,21 @@ const Input = ({label, ...props}) => { const [field, meta] = useField(props); return ( - <> - + + {(meta.touched && !!meta.error) ? - {meta.error} - : <> + : <> } - +
); } diff --git a/frontend/components/ProfileMenu.tsx b/frontend/components/ProfileMenu.tsx index 37f966e..cfed017 100644 --- a/frontend/components/ProfileMenu.tsx +++ b/frontend/components/ProfileMenu.tsx @@ -1,9 +1,14 @@ import {useState} from "react"; -import {Button, Menu, MenuHandler, MenuItem, MenuList} from "@material-tailwind/react"; import {useAuth} from "Frontend/util/auth"; -import {Avatar} from "@hilla/react-components/Avatar"; import {useNavigate} from "react-router-dom"; -import {CaretDown, CaretUp, GearFine, IconContext, Question, SignOut, User} from "@phosphor-icons/react"; +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"; export default function ProfileMenu() { const [isMenuOpen, setIsMenuOpen] = useState(false); @@ -36,38 +41,23 @@ export default function ProfileMenu() { ]; return ( - - - - - + + + + {state.user?.name?.substring(0, 2).toUpperCase()} + + + {profileMenuItems.map(({label, icon, onClick, showIf, color}) => { return ( (showIf === undefined || showIf === true) ? - + {icon}

{label}

-
: null + : null ); })} -
-
+ + ); } \ No newline at end of file diff --git a/frontend/components/theming/Theme.tsx b/frontend/components/theming/Theme.tsx deleted file mode 100644 index cf43c10..0000000 --- a/frontend/components/theming/Theme.tsx +++ /dev/null @@ -1,10 +0,0 @@ -export class Theme { - constructor( - public readonly name: string, - public readonly background: string, - public readonly primary: string, - public readonly secondary?: string, - public readonly tertiary?: string - ) { - } -} \ No newline at end of file diff --git a/frontend/components/theming/ThemePreview.tsx b/frontend/components/theming/ThemePreview.tsx index 3927824..6a74a90 100644 --- a/frontend/components/theming/ThemePreview.tsx +++ b/frontend/components/theming/ThemePreview.tsx @@ -1,11 +1,10 @@ -import {Theme} from "Frontend/components/theming/Theme"; -import {Typography} from "@material-tailwind/react"; +import {Theme} from "Frontend/@/registry/themes"; export default function ThemePreview({theme}: { theme: Theme }) { return (
- + @@ -18,7 +17,7 @@ export default function ThemePreview({theme}: { theme: Theme }) { - {theme.name} +

{theme.name}

); } diff --git a/frontend/components/theming/ThemeSwitcher.tsx b/frontend/components/theming/ThemeSwitcher.tsx deleted file mode 100644 index ea5af7c..0000000 --- a/frontend/components/theming/ThemeSwitcher.tsx +++ /dev/null @@ -1,24 +0,0 @@ -"use client" - -import * as React from "react" - -import {useConfig} from "@/hooks/use-config" - -export function ThemeSwitcher() { - const [config] = useConfig() - - React.useEffect(() => { - document.body.classList.forEach((className) => { - if (className.match(/^theme.*/)) { - document.body.classList.remove(className) - } - }) - - const theme = segment === "themes" ? config.theme : null - if (theme) { - return document.body.classList.add(`theme-${theme}`) - } - }, [segment, config]) - - return null -} diff --git a/frontend/components/theming/Themes.tsx b/frontend/components/theming/Themes.tsx deleted file mode 100644 index fc9e1e6..0000000 --- a/frontend/components/theming/Themes.tsx +++ /dev/null @@ -1,20 +0,0 @@ -import {Theme} from "Frontend/components/theming/Theme"; - -export class Themes { - public static LIGHT_DEFAULT = new Theme( - "Light default", - "#ffffff", - "#000000" - ) - - public static DARK_DEFAULT = new Theme( - "Dark default", - "#161B22", - "#ffffff" - ) - - public static all = [ - Themes.LIGHT_DEFAULT, - Themes.DARK_DEFAULT - ]; -} \ No newline at end of file diff --git a/frontend/components/wizard/Wizard.tsx b/frontend/components/wizard/Wizard.tsx index f556c9e..84fd220 100644 --- a/frontend/components/wizard/Wizard.tsx +++ b/frontend/components/wizard/Wizard.tsx @@ -1,7 +1,7 @@ import React, {ReactNode, useState} from "react"; import {Form, Formik, FormikBag, FormikHelpers} from "formik"; -import {Button, Spinner, Step, Stepper} from "@material-tailwind/react"; -import {ArrowLeft, ArrowRight, Check} from "@phosphor-icons/react"; +import {ArrowLeft, ArrowRight, Check, SpinnerGap} from "@phosphor-icons/react"; +import {Button} from "Frontend/@/components/ui/button"; const Wizard = ({children, initialValues, onSubmit}: { children: ReactNode, @@ -51,16 +51,14 @@ const Wizard = ({children, initialValues, onSubmit}: { {formik => (
- +

Step {stepNumber + 1} of {steps.length}

+ {/* {steps.map((child, index) => ( - {/*// @ts-ignore*/} {child.props.icon} ))} - +
*/}
{step} @@ -76,7 +74,7 @@ const Wizard = ({children, initialValues, onSubmit}: { type="submit" > {formik.isSubmitting ? - : isLastStep ? : + : isLastStep ? : }
diff --git a/frontend/components/wizard/WizardStep.tsx b/frontend/components/wizard/WizardStep.tsx index dcf8250..36b2c52 100644 --- a/frontend/components/wizard/WizardStep.tsx +++ b/frontend/components/wizard/WizardStep.tsx @@ -8,7 +8,4 @@ export default function WizardStep({children, icon, validationSchema}: { onSubmit?: (...values: any) => Promise }) { return children; -} - -//const WizardStep = ({children}: { children: Component }) => children; -//export default WizardStep; \ No newline at end of file +} \ No newline at end of file diff --git a/frontend/views/LoginView.tsx b/frontend/views/LoginView.tsx index 1b4a954..1071a85 100644 --- a/frontend/views/LoginView.tsx +++ b/frontend/views/LoginView.tsx @@ -1,8 +1,11 @@ import {useAuth} from "Frontend/util/auth"; import {useState} from "react"; import {Link, useNavigate} from "react-router-dom"; -import {Alert, Button, Card, Input, Spinner, Typography} from "@material-tailwind/react"; -import {XCircle} from "@phosphor-icons/react"; +import {SpinnerGap, XCircle} from "@phosphor-icons/react"; +import {Card} from "Frontend/@/components/ui/card"; +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(); @@ -19,21 +22,20 @@ export default function LoginView() { } return ( -
-
- +
+
{hasError && - } - className="mb-4 bg-red-500" - > - Wrong username and/or password - } + + + Error + Wrong username and/or password + + } { @@ -51,44 +53,32 @@ export default function LoginView() { }} > { + onChange={(event: any) => { setUsername(event.target.value); }} id="username" type="text" autoComplete="username" placeholder="" - size="lg" - className=" !border-t-blue-gray-200 focus:!border-t-gray-900" - labelProps={{ - className: "before:content-none after:content-none", - }} - crossOrigin="" //TODO: see https://github.com/creativetimofficial/material-tailwind/issues/427 /> { + onChange={(event: any) => { setPassword(event.target.value); }} id="current-password" type="password" autoComplete="current-password" placeholder="" - size="lg" - className=" !border-t-blue-gray-200 focus:!border-t-gray-900" - labelProps={{ - className: "before:content-none after:content-none", - }} - crossOrigin="" //TODO: see https://github.com/creativetimofficial/material-tailwind/issues/427 />
Forgot password? @@ -98,7 +88,7 @@ export default function LoginView() { className="w-28 h-12 flex justify-center" disabled={loading} > - {loading ? : "Log in"} + {loading ? : "Log in"}
diff --git a/frontend/views/MainLayout.tsx b/frontend/views/MainLayout.tsx index 652b766..2644577 100644 --- a/frontend/views/MainLayout.tsx +++ b/frontend/views/MainLayout.tsx @@ -1,8 +1,8 @@ import {useRouteMetadata} from 'Frontend/util/routing.js'; import {useEffect} from 'react'; -import {Navbar} from "@material-tailwind/react"; import ProfileMenu from "Frontend/components/ProfileMenu"; import {Outlet} from "react-router-dom"; +import {Card} from "Frontend/@/components/ui/card"; export default function MainLayout() { const currentTitle = `Gameyfin - ${useRouteMetadata()?.title}` ?? 'Gameyfin'; @@ -12,7 +12,7 @@ export default function MainLayout() { return ( <> - +
-
+ diff --git a/frontend/views/SetupView.tsx b/frontend/views/SetupView.tsx index 2fc8e95..e49a338 100644 --- a/frontend/views/SetupView.tsx +++ b/frontend/views/SetupView.tsx @@ -2,11 +2,11 @@ import React from 'react'; import * as Yup from 'yup'; import Wizard from "Frontend/components/wizard/Wizard"; import WizardStep from "Frontend/components/wizard/WizardStep"; -import {Card, Typography} from "@material-tailwind/react"; import Input from "Frontend/components/Input"; import {GearFine, HandWaving, Palette, User} from "@phosphor-icons/react"; -import {Themes} from "Frontend/components/theming/Themes"; import ThemePreview from "Frontend/components/theming/ThemePreview"; +import {Theme, themes} from "Frontend/@/registry/themes"; +import {Card} from "Frontend/@/components/ui/card"; const sleep = (ms: number) => new Promise(resolve => setTimeout(resolve, ms)); @@ -14,7 +14,7 @@ function WelcomeStep() { return (
- Welcome to Gameyfin 👋 +

Welcome to Gameyfin 👋

Gameyfin is a cutting-edge software tailored for gamers seeking efficient management of their video @@ -28,7 +28,7 @@ function WelcomeStep() { user-friendly design and adaptability, offering ample customization options to meet diverse user preferences.

- Let's get started! +
Let's get started!
); @@ -38,7 +38,7 @@ function ThemeStep() { return (
- {Themes.all.map((theme => ( + {themes.map(((theme: Theme) => ( )))}
@@ -50,9 +50,9 @@ function UserStep() { return (
- Create your account - This will set up the initial admin user account. -
+

Create your account

+

This will set up the initial admin user account.

+
- Settings - Configure your settings +

Settings

+

Configure your settings

); } const SetupView = () => ( -
-
- +
+ diff --git a/package-lock.json b/package-lock.json index 2fad59f..be9476d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -25,6 +25,11 @@ "@hilla/react-form": "2.5.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-label": "^2.0.2", + "@radix-ui/react-slot": "^1.0.2", + "@radix-ui/react-tooltip": "^1.0.7", "@vaadin/bundles": "24.3.0", "@vaadin/common-frontend": "0.0.19", "@vaadin/router": "1.7.5", @@ -36,6 +41,7 @@ "jotai": "^2.7.1", "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", @@ -2200,6 +2206,40 @@ "node": ">=12" } }, + "node_modules/@floating-ui/core": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.0.tgz", + "integrity": "sha512-PcF++MykgmTj3CIyOQbKA/hDzOAiqI3mhuoN44WRCopIs1sgoDoU4oty4Jtqaj/y3oDU6fnVSm4QG0a3t5i0+g==", + "dependencies": { + "@floating-ui/utils": "^0.2.1" + } + }, + "node_modules/@floating-ui/dom": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.6.3.tgz", + "integrity": "sha512-RnDthu3mzPlQ31Ss/BTwQ1zjzIhr3lk1gZB1OC56h/1vEtaXkESrOqL5fQVMfXpwGtRwX+YsZBdyHtJMQnkArw==", + "dependencies": { + "@floating-ui/core": "^1.0.0", + "@floating-ui/utils": "^0.2.0" + } + }, + "node_modules/@floating-ui/react-dom": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.0.8.tgz", + "integrity": "sha512-HOdqOt3R3OGeTKidaLvJKcgg75S6tibQ3Tif4eyd91QnIJWr0NLvoXFpJA/j8HqkFSL68GDca9AuyWEHlhyClw==", + "dependencies": { + "@floating-ui/dom": "^1.6.1" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@floating-ui/utils": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.1.tgz", + "integrity": "sha512-9TANp6GPoMtYzQdt54kfAyMmz1+osLlXdg2ENroU7zzrtflTLrrC/lgrIfaSe+Wu0b89GKccT7vxXA0MoAIO+Q==" + }, "node_modules/@hilla/form": { "version": "2.5.6", "resolved": "https://registry.npmjs.org/@hilla/form/-/form-2.5.6.tgz", @@ -2814,6 +2854,641 @@ "@webcomponents/shadycss": "^1.9.1" } }, + "node_modules/@radix-ui/primitive": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.0.1.tgz", + "integrity": "sha512-yQ8oGX2GVsEYMWGxcovu1uGWPCxV5BFfeeYxqPmuAzUyLT9qmaMXSAhXpb0WrspIeqYzdJpkh2vHModJPgRIaw==", + "dependencies": { + "@babel/runtime": "^7.13.10" + } + }, + "node_modules/@radix-ui/react-arrow": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.0.3.tgz", + "integrity": "sha512-wSP+pHsB/jQRaL6voubsQ/ZlrGBHHrOjmBnr19hxYgtS0WvAFwZhK2WP/YY5yF9uKECCEEDGxuLxq1NBK51wFA==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-primitive": "1.0.3" + }, + "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-avatar": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-avatar/-/react-avatar-1.0.4.tgz", + "integrity": "sha512-kVK2K7ZD3wwj3qhle0ElXhOjbezIgyl2hVvgwfIdexL3rN6zJmy5AqqIf+D31lxVppdzV8CjAfZ6PklkmInZLw==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-context": "1.0.1", + "@radix-ui/react-primitive": "1.0.3", + "@radix-ui/react-use-callback-ref": "1.0.1", + "@radix-ui/react-use-layout-effect": "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-collection": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.0.3.tgz", + "integrity": "sha512-3SzW+0PW7yBBoQlT8wNcGtaxaD0XSu0uLUFgrtHY08Acx05TaHaOmVLR73c0j/cqpDy53KBMO7s0dx2wmOIDIA==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@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-slot": "1.0.2" + }, + "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-compose-refs": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.0.1.tgz", + "integrity": "sha512-fDSBgd44FKHa1FRMU59qBMPFcl2PZE+2nmqunj+BWFyYYjnhIDWL2ItDs3rrbJDQOtzt5nIebLCQc4QRfz6LJw==", + "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-context": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.0.1.tgz", + "integrity": "sha512-ebbrdFoYTcuZ0v4wG5tedGnp9tzcV8awzsxYph7gXUyvnNLuTIcCk1q17JEbnVhXAKG9oX3KtchwiMIAYp9NLg==", + "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-direction": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.0.1.tgz", + "integrity": "sha512-RXcvnXgyvYvBEOhCBuddKecVkoMiI10Jcm5cTI7abJRAHYfFxeu+FBQs/DvdxSYucxR5mna0dNsL6QFlds5TMA==", + "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-dismissable-layer": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.0.5.tgz", + "integrity": "sha512-aJeDjQhywg9LBu2t/At58hCvr7pEm0o2Ke1x33B+MhjNmmZ17sy4KImo0KPLgsnc/zN7GPdce8Cnn0SWvwZO7g==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/primitive": "1.0.1", + "@radix-ui/react-compose-refs": "1.0.1", + "@radix-ui/react-primitive": "1.0.3", + "@radix-ui/react-use-callback-ref": "1.0.1", + "@radix-ui/react-use-escape-keydown": "1.0.3" + }, + "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-dropdown-menu": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dropdown-menu/-/react-dropdown-menu-2.0.6.tgz", + "integrity": "sha512-i6TuFOoWmLWq+M/eCLGd/bQ2HfAX1RJgvrBQ6AQLmzfvsLdefxbWu8G9zczcPFfcSPehz9GcpF6K9QYreFV8hA==", + "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-id": "1.0.1", + "@radix-ui/react-menu": "2.0.6", + "@radix-ui/react-primitive": "1.0.3", + "@radix-ui/react-use-controllable-state": "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-focus-guards": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.0.1.tgz", + "integrity": "sha512-Rect2dWbQ8waGzhMavsIbmSVCgYxkXLxxR3ZvCX79JOglzdEy4JXMb98lq4hPxUbLr77nP0UOGf4rcMU+s1pUA==", + "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-focus-scope": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.0.4.tgz", + "integrity": "sha512-sL04Mgvf+FmyvZeYfNu1EPAaaxD+aw7cYeIB9L9Fvq8+urhltTRaEo5ysKOpHuKPclsZcSUMKlN05x4u+CINpA==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-compose-refs": "1.0.1", + "@radix-ui/react-primitive": "1.0.3", + "@radix-ui/react-use-callback-ref": "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-id": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.0.1.tgz", + "integrity": "sha512-tI7sT/kqYp8p96yGWY1OAnLHrqDgzHefRBKQ2YAkBS5ja7QLcZ9Z/uY7bEjPUatf8RomoXM8/1sMj1IJaE5UzQ==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-use-layout-effect": "1.0.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-label": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-label/-/react-label-2.0.2.tgz", + "integrity": "sha512-N5ehvlM7qoTLx7nWPodsPYPgMzA5WM8zZChQg8nyFJKnDO5WHdba1vv5/H6IO5LtJMfD2Q3wh1qHFGNtK0w3bQ==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-primitive": "1.0.3" + }, + "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-menu": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@radix-ui/react-menu/-/react-menu-2.0.6.tgz", + "integrity": "sha512-BVkFLS+bUC8HcImkRKPSiVumA1VPOOEC5WBMiT+QAVsPzW1FJzI9KnqgGxVDPBcql5xXrHkD3JOVoXWEXD8SYA==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/primitive": "1.0.1", + "@radix-ui/react-collection": "1.0.3", + "@radix-ui/react-compose-refs": "1.0.1", + "@radix-ui/react-context": "1.0.1", + "@radix-ui/react-direction": "1.0.1", + "@radix-ui/react-dismissable-layer": "1.0.5", + "@radix-ui/react-focus-guards": "1.0.1", + "@radix-ui/react-focus-scope": "1.0.4", + "@radix-ui/react-id": "1.0.1", + "@radix-ui/react-popper": "1.1.3", + "@radix-ui/react-portal": "1.0.4", + "@radix-ui/react-presence": "1.0.1", + "@radix-ui/react-primitive": "1.0.3", + "@radix-ui/react-roving-focus": "1.0.4", + "@radix-ui/react-slot": "1.0.2", + "@radix-ui/react-use-callback-ref": "1.0.1", + "aria-hidden": "^1.1.1", + "react-remove-scroll": "2.5.5" + }, + "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-popper": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.1.3.tgz", + "integrity": "sha512-cKpopj/5RHZWjrbF2846jBNacjQVwkP068DfmgrNJXpvVWrOvlAmE9xSiy5OqeE+Gi8D9fP+oDhUnPqNMY8/5w==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@floating-ui/react-dom": "^2.0.0", + "@radix-ui/react-arrow": "1.0.3", + "@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-callback-ref": "1.0.1", + "@radix-ui/react-use-layout-effect": "1.0.1", + "@radix-ui/react-use-rect": "1.0.1", + "@radix-ui/react-use-size": "1.0.1", + "@radix-ui/rect": "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-portal": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.0.4.tgz", + "integrity": "sha512-Qki+C/EuGUVCQTOTD5vzJzJuMUlewbzuKyUy+/iHM2uwGiru9gZeBJtHAPKAEkB5KWGi9mP/CHKcY0wt1aW45Q==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-primitive": "1.0.3" + }, + "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-presence": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.0.1.tgz", + "integrity": "sha512-UXLW4UAbIY5ZjcvzjfRFo5gxva8QirC9hF7wRE4U5gz+TP0DbRk+//qyuAQ1McDxBt1xNMBTaciFGvEmJvAZCg==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-compose-refs": "1.0.1", + "@radix-ui/react-use-layout-effect": "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-primitive": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-1.0.3.tgz", + "integrity": "sha512-yi58uVyoAcK/Nq1inRY56ZSjKypBNKTa/1mcL8qdl6oJeEaDbOldlzrGn7P6Q3Id5d+SYNGc5AJgc4vGhjs5+g==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-slot": "1.0.2" + }, + "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-roving-focus": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.0.4.tgz", + "integrity": "sha512-2mUg5Mgcu001VkGy+FfzZyzbmuUWzgWkj3rvv4yu+mLw03+mTzbxZHvfcGyFp2b8EkQeMkpRQ5FiA2Vr2O6TeQ==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/primitive": "1.0.1", + "@radix-ui/react-collection": "1.0.3", + "@radix-ui/react-compose-refs": "1.0.1", + "@radix-ui/react-context": "1.0.1", + "@radix-ui/react-direction": "1.0.1", + "@radix-ui/react-id": "1.0.1", + "@radix-ui/react-primitive": "1.0.3", + "@radix-ui/react-use-callback-ref": "1.0.1", + "@radix-ui/react-use-controllable-state": "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-slot": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.0.2.tgz", + "integrity": "sha512-YeTpuq4deV+6DusvVUW4ivBgnkHwECUu0BiN43L5UCDFgdhsRUWAghhTF5MbvNTPzmiFOx90asDSUjWuCNapwg==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-compose-refs": "1.0.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "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", + "integrity": "sha512-lPh5iKNFVQ/jav/j6ZrWq3blfDJ0OH9R6FlNUHPMqdLuQ9vwDgFsRxvl8b7Asuy5c8xmoojHUxKHQSOAvMHxyw==", + "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-dismissable-layer": "1.0.5", + "@radix-ui/react-id": "1.0.1", + "@radix-ui/react-popper": "1.1.3", + "@radix-ui/react-portal": "1.0.4", + "@radix-ui/react-presence": "1.0.1", + "@radix-ui/react-primitive": "1.0.3", + "@radix-ui/react-slot": "1.0.2", + "@radix-ui/react-use-controllable-state": "1.0.1", + "@radix-ui/react-visually-hidden": "1.0.3" + }, + "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-use-callback-ref": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.0.1.tgz", + "integrity": "sha512-D94LjX4Sp0xJFVaoQOd3OO9k7tpBYNOXdVhkltUbGv2Qb9OXdrg/CpsjlZv7ia14Sylv398LswWBVVu5nqKzAQ==", + "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-controllable-state": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.0.1.tgz", + "integrity": "sha512-Svl5GY5FQeN758fWKrjM6Qb7asvXeiZltlT4U2gVfl8Gx5UAv2sMR0LWo8yhsIZh2oQ0eFdZ59aoOOMV7b47VA==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-use-callback-ref": "1.0.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-escape-keydown": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.0.3.tgz", + "integrity": "sha512-vyL82j40hcFicA+M4Ex7hVkB9vHgSse1ZWomAqV2Je3RleKGO5iM8KMOEtfoSB0PnIelMd2lATjTGMYqN5ylTg==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-use-callback-ref": "1.0.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-layout-effect": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.0.1.tgz", + "integrity": "sha512-v/5RegiJWYdoCvMnITBkNNx6bCj20fiaJnWtRkU18yITptraXjffz5Qbn05uOiQnOvi+dbkznkoaMltz1GnszQ==", + "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", + "integrity": "sha512-Cq5DLuSiuYVKNU8orzJMbl15TXilTnJKUCltMVQg53BQOF1/C5toAaGrowkgksdBQ9H+SRL23g0HDmg9tvmxXw==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/rect": "1.0.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-size": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-size/-/react-use-size-1.0.1.tgz", + "integrity": "sha512-ibay+VqrgcaI6veAojjofPATwledXiSmX+C0KrBk/xgpX9rBzPV3OsfwlhQdUOFbh+LKQorLYT+xTXW9V8yd0g==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-use-layout-effect": "1.0.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-visually-hidden": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.0.3.tgz", + "integrity": "sha512-D4w41yN5YRKtu464TLnByKzMDG/JlMPHtfZgQAu9v6mNakUqGUI9vUrfQKz8NK41VMm/xbZbh76NUTVtIYqOMA==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-primitive": "1.0.3" + }, + "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/rect": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/rect/-/rect-1.0.1.tgz", + "integrity": "sha512-fyrgCaedtvMg9NK3en0pnOYJdtfwxUcNolezkNPUsoX57X8oQk+NkqcvzHXD2uKNij6GXmWU9NDru2IWjrO4BQ==", + "dependencies": { + "@babel/runtime": "^7.13.10" + } + }, "node_modules/@remix-run/router": { "version": "1.15.3", "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.15.3.tgz", @@ -4871,6 +5546,17 @@ "sprintf-js": "~1.0.2" } }, + "node_modules/aria-hidden": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/aria-hidden/-/aria-hidden-1.2.4.tgz", + "integrity": "sha512-y+CcFFwelSXpLZk/7fMB2mUbGtX9lKycf1MWJ7CaTIERyitVlyQx6C+sxcROU2BAJ24OiZyK+8wj2i8AlBoS3A==", + "dependencies": { + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/array-buffer-byte-length": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.1.tgz", @@ -5334,14 +6020,6 @@ "url": "https://joebell.co.uk" } }, - "node_modules/class-variance-authority/node_modules/clsx": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.0.0.tgz", - "integrity": "sha512-rQ1+kcj+ttHG0MKVGBUXwayCCF1oh39BF5COIpRzuCEv8Mwjv0XucrI2ExNTOn9IlLifGClWQcU9BrZORvtw6Q==", - "engines": { - "node": ">=6" - } - }, "node_modules/classnames": { "version": "2.5.1", "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.5.1.tgz", @@ -5748,6 +6426,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/detect-node-es": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/detect-node-es/-/detect-node-es-1.1.0.tgz", + "integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==" + }, "node_modules/didyoumean": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", @@ -6333,6 +7016,14 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/get-nonce": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-nonce/-/get-nonce-1.0.1.tgz", + "integrity": "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==", + "engines": { + "node": ">=6" + } + }, "node_modules/get-own-enumerable-property-symbols": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz", @@ -6674,6 +7365,14 @@ "node": ">= 0.4" } }, + "node_modules/invariant": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", + "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", + "dependencies": { + "loose-envify": "^1.0.0" + } + }, "node_modules/is-array-buffer": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.4.tgz", @@ -7766,6 +8465,15 @@ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, + "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" + } + }, "node_modules/node-releases": { "version": "2.0.14", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz", @@ -8524,6 +9232,51 @@ "node": ">=0.10.0" } }, + "node_modules/react-remove-scroll": { + "version": "2.5.5", + "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.5.5.tgz", + "integrity": "sha512-ImKhrzJJsyXJfBZ4bzu8Bwpka14c/fQt0k+cyFp/PBhTfyDnU5hjOtM4AG/0AMyy8oKzOTR0lDgJIM7pYXI0kw==", + "dependencies": { + "react-remove-scroll-bar": "^2.3.3", + "react-style-singleton": "^2.2.1", + "tslib": "^2.1.0", + "use-callback-ref": "^1.3.0", + "use-sidecar": "^1.1.2" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/react-remove-scroll-bar": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.6.tgz", + "integrity": "sha512-DtSYaao4mBmX+HDo5YWYdBWQwYIQQshUV/dVxFxK+KM26Wjwp1gZ6rv6OC3oujI6Bfu6Xyg3TwK533AQutsn/g==", + "dependencies": { + "react-style-singleton": "^2.2.1", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/react-router": { "version": "6.22.3", "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.22.3.tgz", @@ -8554,6 +9307,28 @@ "react-dom": ">=16.8" } }, + "node_modules/react-style-singleton": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.1.tgz", + "integrity": "sha512-ZWj0fHEMyWkHzKYUr2Bs/4zU6XLmq9HsgBURm7g5pAVfyn49DgUiNgY2d4lXRlYSiCif9YBGpQleewkcqddc7g==", + "dependencies": { + "get-nonce": "^1.0.0", + "invariant": "^2.2.4", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/read-cache": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", @@ -10017,6 +10792,47 @@ "punycode": "^2.1.0" } }, + "node_modules/use-callback-ref": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.3.2.tgz", + "integrity": "sha512-elOQwe6Q8gqZgDA8mrh44qRTQqpIHDcZ3hXTLjBe1i4ph8XpNJnO+aQf3NaG+lriLopI4HMx9VjQLfPQ6vhnoA==", + "dependencies": { + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/use-sidecar": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.2.tgz", + "integrity": "sha512-epTbsLuzZ7lPClpz2TyryBfztm7m+28DlEv2ZCQ3MDr5ssiwyOwGH/e5F9CkfWjJ1t4clvI58yF822/GUkjjhw==", + "dependencies": { + "detect-node-es": "^1.1.0", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "^16.9.0 || ^17.0.0 || ^18.0.0", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", diff --git a/package.json b/package.json index 2be3b4a..893002e 100644 --- a/package.json +++ b/package.json @@ -20,6 +20,11 @@ "@hilla/react-form": "2.5.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-label": "^2.0.2", + "@radix-ui/react-slot": "^1.0.2", + "@radix-ui/react-tooltip": "^1.0.7", "@vaadin/bundles": "24.3.0", "@vaadin/common-frontend": "0.0.19", "@vaadin/router": "1.7.5", @@ -31,6 +36,7 @@ "jotai": "^2.7.1", "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", @@ -90,7 +96,19 @@ "@hilla/generator-typescript-cli": "$@hilla/generator-typescript-cli", "@phosphor-icons/react": "$@phosphor-icons/react", "formik": "$formik", - "yup": "$yup" + "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" }, "vaadin": { "dependencies": { @@ -134,6 +152,6 @@ "workbox-core": "7.0.0", "workbox-precaching": "7.0.0" }, - "hash": "9223d4c6f4cf3a6fd28b853d824fddc00718dbc1a3474e986f4dc2d69f294b6e" + "hash": "54c9ffc1de2ff554baf5ced1be2e704d4a354138bf9cbfca2d4fc83f126a4563" } } diff --git a/tailwind.config.js b/tailwind.config.ts similarity index 91% rename from tailwind.config.js rename to tailwind.config.ts index 823da3f..db74131 100644 --- a/tailwind.config.js +++ b/tailwind.config.ts @@ -1,6 +1,7 @@ -/** @type {import('tailwindcss').Config} */ -module.exports = { - darkMode: ["class"], +import {Config} from "tailwindcss/types/config"; + +export default { + darkMode: "class", content: [ './frontend/index.html', './frontend/**/*.{js,ts,jsx,tsx}', @@ -40,4 +41,4 @@ module.exports = { }, }, plugins: [require("tailwindcss-animate")], -} \ No newline at end of file +} satisfies Config; \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json index 1d3096d..bd1d7c5 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -24,9 +24,15 @@ "useDefineForClassFields": false, "baseUrl": "frontend", "paths": { - "@vaadin/flow-frontend": ["generated/jar-resources"], - "@vaadin/flow-frontend/*": ["generated/jar-resources/*"], - "Frontend/*": ["*"] + "@vaadin/flow-frontend": [ + "generated/jar-resources" + ], + "@vaadin/flow-frontend/*": [ + "generated/jar-resources/*" + ], + "Frontend/*": [ + "*" + ] } }, "include": [ diff --git a/vite.config.ts b/vite.config.ts index 58eb081..4abd1a3 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -1,15 +1,21 @@ import reactSwc from '@vitejs/plugin-react-swc'; -import type { UserConfigFn } from 'vite'; -import { overrideVaadinConfig } from './vite.generated'; +import type {UserConfigFn} from 'vite'; +import {overrideVaadinConfig} from './vite.generated'; +import path from "path"; const customConfig: UserConfigFn = (env) => ({ - // Here you can add custom Vite parameters - // https://vitejs.dev/config/ - plugins: [ - reactSwc({ - tsDecorators: true, - }), - ], + // Here you can add custom Vite parameters + // https://vitejs.dev/config/ + plugins: [ + reactSwc({ + tsDecorators: true, + }), + ], + resolve: { + alias: { + "@": path.resolve(__dirname, "./frontend"), + }, + }, }); export default overrideVaadinConfig(customConfig);