Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
86 changes: 86 additions & 0 deletions frontend/app/components/MobileHeaderMenu/MobileHeaderMenu.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import {
BookIcon,
HomeIcon,
LogInIcon,
LogOutIcon,
MenuIcon,
StarIcon,
} 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">
<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
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