Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions frontend/app/components/Header/Header.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Link } from "react-router";
import { authClient } from "~/api/auth";
import { MobileHeaderMenu } from "~/components/MobileHeaderMenu/MobileHeaderMenu";
import { Button, buttonVariants } from "~/components/ui/Button";
import { DarkModeToggle } from "~/components/ui/DarkModeToggle";
import { cn } from "~/lib/utils";
Expand All @@ -14,7 +15,7 @@ function Header() {
<h1 className="m-0 text-2xl">Readr</h1>
</Link>

<nav aria-label="Main">
<nav aria-label="Main" className="hidden md:block">
<Link to="/" className={buttonVariants({ variant: "ghost" })}>
Home
</Link>
Expand All @@ -32,8 +33,10 @@ function Header() {
</nav>
</section>

<div className="flex items-center gap-4">
<DarkModeToggle />
<MobileHeaderMenu />

<div className="hidden md:flex items-center gap-4">
<DarkModeToggle variant="button" />
{!isPending && session ? (
<>
<Link
Expand Down
91 changes: 91 additions & 0 deletions frontend/app/components/MobileHeaderMenu/MobileHeaderMenu.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import {
BookIcon,
HomeIcon,
LogInIcon,
LogOutIcon,
MenuIcon,
StarIcon,
UserIcon,
} from "lucide-react";
import { Link } from "react-router";
import { authClient } from "~/api/auth";
import { Button } from "~/components/ui/Button";
import { DarkModeToggle } from "~/components/ui/DarkModeToggle";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuGroup,
DropdownMenuItem,
DropdownMenuLabel,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from "~/components/ui/DropdownMenu";

function MobileHeaderMenu() {
const { data: session, isPending } = authClient.useSession();

return (
<DropdownMenu>
<DropdownMenuTrigger
render={
<Button
variant="outline"
size="icon"
className="md:hidden"
aria-label="Open menu"
>
<MenuIcon />
</Button>
}
/>
<DropdownMenuContent align="end" className="md:hidden">
<DropdownMenuItem render={<Link to="/" />}>
<HomeIcon />
Home
</DropdownMenuItem>
<DropdownMenuItem render={<Link to="/books" />}>
<BookIcon />
Books
</DropdownMenuItem>
{!isPending && session && (
<DropdownMenuItem render={<Link to="/profile" />}>
<StarIcon />
Favorites
</DropdownMenuItem>
)}
<DarkModeToggle variant="dropdownMenu" />
<DropdownMenuSeparator />
{!isPending && session ? (
<DropdownMenuGroup>
<DropdownMenuLabel>
Signed in as {session.user.name}
</DropdownMenuLabel>
<DropdownMenuItem render={<Link to="/profile" />}>
<UserIcon />
Profile
</DropdownMenuItem>
<DropdownMenuItem
variant="destructive"
onClick={() => {
void (async () => {
await authClient.signOut();
location.reload();
})();
}}
>
<LogOutIcon />
Log out
</DropdownMenuItem>
</DropdownMenuGroup>
) : (
<DropdownMenuItem render={<Link to="/login" />}>
<LogInIcon />
Log in
</DropdownMenuItem>
)}
</DropdownMenuContent>
</DropdownMenu>
);
}

export { MobileHeaderMenu };
33 changes: 29 additions & 4 deletions frontend/app/components/ui/DarkModeToggle.tsx
Original file line number Diff line number Diff line change
@@ -1,23 +1,48 @@
import { Moon, Sun } from "lucide-react";
import { MoonIcon, SunIcon, SunMoonIcon } from "lucide-react";
import { Button } from "~/components/ui/Button";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuSub,
DropdownMenuSubContent,
DropdownMenuSubTrigger,
DropdownMenuTrigger,
} from "~/components/ui/DropdownMenu";
import { useTheme } from "~/lib/themeProvider";

function DarkModeToggle() {
function DarkModeToggle({ variant }: { variant: "button" | "dropdownMenu" }) {
const { setTheme } = useTheme();

if (variant === "dropdownMenu") {
return (
<DropdownMenuSub>
<DropdownMenuSubTrigger>
<SunMoonIcon />
Theme
</DropdownMenuSubTrigger>
<DropdownMenuSubContent>
<DropdownMenuItem onClick={() => setTheme("light")}>
Light
</DropdownMenuItem>
<DropdownMenuItem onClick={() => setTheme("dark")}>
Dark
</DropdownMenuItem>
<DropdownMenuItem onClick={() => setTheme("system")}>
System
</DropdownMenuItem>
</DropdownMenuSubContent>
</DropdownMenuSub>
);
}

return (
<DropdownMenu>
<DropdownMenuTrigger
render={
<Button variant="outline" size="icon">
<Sun className="h-[1.2rem] w-[1.2rem] scale-100 rotate-0 transition-all dark:scale-0 dark:-rotate-90" />
<Moon className="absolute h-[1.2rem] w-[1.2rem] scale-0 rotate-90 transition-all dark:scale-100 dark:rotate-0" />
<SunIcon className="h-[1.2rem] w-[1.2rem] scale-100 rotate-0 transition-all dark:scale-0 dark:-rotate-90" />
<MoonIcon className="absolute h-[1.2rem] w-[1.2rem] scale-0 rotate-90 transition-all dark:scale-100 dark:rotate-0" />
<span className="sr-only">Toggle theme</span>
</Button>
}
Expand Down
2 changes: 2 additions & 0 deletions frontend/app/components/ui/Field.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,8 @@ function FieldError({
<ul className="ml-4 flex list-disc flex-col gap-1">
{uniqueErrors.map(
(error, index) =>
// We do not have any other data available to use as a key
// eslint-disable-next-line @eslint-react/no-array-index-key
error?.message && <li key={index}>{error.message}</li>,
)}
</ul>
Expand Down
8 changes: 6 additions & 2 deletions frontend/app/components/ui/Toast.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -98,9 +98,11 @@ function ToastDescription({
);
}

const defaultToastActionRender = <Button variant="outline" size="sm" />;

function ToastAction({
className,
render = <Button variant="outline" size="sm" />,
render = defaultToastActionRender,
...props
}: ToastPrimitive.Action.Props) {
return (
Expand All @@ -113,10 +115,12 @@ function ToastAction({
);
}

const defaultToastCloseRender = <Button variant="ghost" size="icon-sm" />;

function ToastClose({
className,
children,
render = <Button variant="ghost" size="icon-sm" />,
render = defaultToastCloseRender,
...props
}: ToastPrimitive.Close.Props) {
return (
Expand Down