Skip to content

Commit

Permalink
feat: Redesign app layout (#200)
Browse files Browse the repository at this point in the history
  • Loading branch information
evadecker authored Nov 18, 2024
1 parent 36a2abd commit 824da1c
Show file tree
Hide file tree
Showing 27 changed files with 395 additions and 355 deletions.
5 changes: 5 additions & 0 deletions .changeset/poor-seahorses-wonder.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"namesake": minor
---

Redesign app layout for optimal line length and taller scroll area
50 changes: 0 additions & 50 deletions src/components/AppHeader/AppHeader.tsx

This file was deleted.

1 change: 0 additions & 1 deletion src/components/AppHeader/index.ts

This file was deleted.

103 changes: 103 additions & 0 deletions src/components/AppSidebar/AppSidebar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
import { useAuthActions } from "@convex-dev/auth/react";
import { api } from "@convex/_generated/api";
import {
RiAccountCircleFill,
RiAddLine,
RiAdminLine,
RiSettings3Line,
} from "@remixicon/react";
import { useQuery } from "convex/react";
import { Badge } from "../Badge";
import { Button } from "../Button";
import { Link } from "../Link";
import { Logo } from "../Logo";
import { Menu, MenuItem, MenuTrigger } from "../Menu";
import { Tooltip } from "../Tooltip";
import { TooltipTrigger } from "../Tooltip";

type AppSidebarProps = {
children: React.ReactNode;
};

export const AppSidebar = ({ children }: AppSidebarProps) => {
const { signOut } = useAuthActions();
const user = useQuery(api.users.getCurrentUser);
const isAdmin = user?.role === "admin";

const handleSignOut = async () => {
await signOut();
};

return (
<nav className="w-80 flex flex-col shrink-0 sticky top-0 h-screen -ml-4 overflow-y-auto border-r border-gray-dim">
<div className="flex gap-2 items-center h-16 shrink-0 px-4 sticky top-0 bg-gray-app z-20">
<Link href={{ to: "/" }} className="p-1 -m-1">
<Logo className="h-[1.25rem]" />
</Link>
<Badge className="-mb-1" variant="waiting">
Beta
</Badge>
<div className="ml-auto">
<TooltipTrigger>
<Link
href={{ to: "/browse" }}
button={{
variant: "icon",
className: "-mr-1",
}}
>
<RiAddLine size={20} />
</Link>
<Tooltip placement="right">Browse and add quests</Tooltip>
</TooltipTrigger>
</div>
</div>
<div className="px-4 flex-1">{children}</div>
<div className="px-4 h-16 shrink-0 flex items-center sticky bottom-0 bg-gray-app">
<MenuTrigger>
<Button
aria-label="User settings"
variant="ghost"
icon={RiAccountCircleFill}
>
{user?.name}
</Button>
<Menu placement="top start">
<MenuItem
href="https://namesake.fyi/chat"
target="_blank"
rel="noreferrer"
>
Support&hellip;
</MenuItem>
<MenuItem onAction={handleSignOut}>Sign out</MenuItem>
</Menu>
</MenuTrigger>
<div className="flex ml-auto">
{isAdmin && (
<TooltipTrigger>
<Link
aria-label="Admin"
href={{ to: "/admin" }}
button={{ variant: "icon" }}
>
<RiAdminLine size={20} />
</Link>
<Tooltip placement="top">Admin</Tooltip>
</TooltipTrigger>
)}
<TooltipTrigger>
<Link
aria-label="Settings"
href={{ to: "/settings/overview" }}
button={{ variant: "icon" }}
>
<RiSettings3Line size={20} />
</Link>
<Tooltip placement="top">Settings</Tooltip>
</TooltipTrigger>
</div>
</div>
</nav>
);
};
1 change: 1 addition & 0 deletions src/components/AppSidebar/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "./AppSidebar";
4 changes: 2 additions & 2 deletions src/components/Badge/Badge.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,11 @@ const icon = tv({
},
});

export function Badge({ icon: Icon, ...props }: BadgeProps) {
export function Badge({ icon: Icon, className, ...props }: BadgeProps) {
return (
<div
{...props}
className={badge({ variant: props.variant, size: props.size })}
className={badge({ variant: props.variant, size: props.size, className })}
>
{Icon && <Icon className={icon({ variant: props.variant })} />}
{props.children}
Expand Down
2 changes: 1 addition & 1 deletion src/components/Button/Button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export interface ButtonProps extends AriaButtonProps {

export const buttonStyles = tv({
extend: focusRing,
base: "py-2 text-sm font-medium whitespace-nowrap rounded-lg flex gap-1 items-center justify-center border border-black/10 dark:border-white/10 cursor-pointer",
base: "py-2 text-sm font-medium whitespace-nowrap rounded-lg flex gap-1.5 items-center justify-center border border-black/10 dark:border-white/10 cursor-pointer",
variants: {
variant: {
primary: "bg-purple-solid text-white",
Expand Down
12 changes: 6 additions & 6 deletions src/components/Container/Container.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import { twMerge } from "tailwind-merge";
import { tv } from "tailwind-variants";

export interface ContainerProps {
className?: string;
children: React.ReactNode;
}

const containerStyles = tv({
base: "max-w-full w-[1200px] px-4 lg:px-6 xl:px-8 mx-auto",
});

export function Container({ className, children }: ContainerProps) {
return (
<div className={twMerge("w-full flex-1 p-6 mx-auto", className)}>
{children}
</div>
);
return <div className={containerStyles({ className })}>{children}</div>;
}
4 changes: 2 additions & 2 deletions src/components/Nav/Nav.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export const Nav = ({ routes }: NavProps) => {
const matchRoute = useMatchRoute();

return (
<nav className="flex flex-col w-[160px] shrink-0 pt-5 pl-6">
<div>
{routes.map(({ href, label, icon }) => {
const current = matchRoute({ ...href, fuzzy: true });
const Icon = icon;
Expand All @@ -38,6 +38,6 @@ export const Nav = ({ routes }: NavProps) => {
</Link>
);
})}
</nav>
</div>
);
};
9 changes: 0 additions & 9 deletions src/components/PageHeader/PageHeader.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,19 +28,10 @@ export const Badged: Story = {
},
};

export const Subtitle: Story = {
args: {
title: "Court Order",
badge: <Badge>MA</Badge>,
subtitle: "Case #123456",
},
};

export const Actions: Story = {
args: {
title: "Court Order",
badge: <Badge>MA</Badge>,
subtitle: "Case #123456",
children: <Button>Mark complete</Button>,
},
};
5 changes: 1 addition & 4 deletions src/components/PageHeader/PageHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ export interface PageHeaderProps {
title: string;
icon?: RemixiconComponentType;
badge?: React.ReactNode;
subtitle?: string;
children?: React.ReactNode;
className?: string;
}
Expand All @@ -14,14 +13,13 @@ export const PageHeader = ({
title,
icon: Icon,
badge,
subtitle,
children,
className,
}: PageHeaderProps) => {
return (
<header
className={twMerge(
"h-12 flex shrink-0 items-center justify-between gap-6 text-gray-normal",
"h-16 flex bg-gray-app shrink-0 items-center justify-between gap-6 text-gray-normal sticky top-0 z-20",
className,
)}
>
Expand All @@ -31,7 +29,6 @@ export const PageHeader = ({
<h1 className="text-xl font-medium">{title}</h1>
{badge}
</div>
{subtitle && <p className="text-gray-dim">{subtitle}</p>}
</div>
<div className="flex items-center gap-2">{children}</div>
</header>
Expand Down
7 changes: 6 additions & 1 deletion src/components/SearchField/SearchField.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,14 @@ export interface SearchFieldProps extends AriaSearchFieldProps {
label?: string;
description?: string;
errorMessage?: string | ((validation: ValidationResult) => string);
placeholder?: string;
}

export function SearchField({
label,
description,
errorMessage,
placeholder,
...props
}: SearchFieldProps) {
return (
Expand All @@ -40,7 +42,10 @@ export function SearchField({
aria-hidden
className="w-4 h-4 ml-3 text-gray-dim forced-colors:text-[ButtonText] group-disabled:opacity-50 forced-colors:group-disabled:text-[GrayText]"
/>
<Input className="[&::-webkit-search-cancel-button]:hidden" />
<Input
placeholder={placeholder}
className="[&::-webkit-search-cancel-button]:hidden"
/>
<Button
variant="icon"
className="mr-1 w-7 h-7 p-0 group-empty:invisible"
Expand Down
2 changes: 1 addition & 1 deletion src/components/StatusSelect/StatusSelect.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ export function StatusSelect({ status, isCore, onChange }: StatusSelectProps) {
<RiArrowDropDownFill size={16} className="right-0" />
</Button>
<Menu
placement="bottom start"
placement="bottom end"
selectionMode="single"
selectedKeys={selectedStatus}
disallowEmptySelection
Expand Down
2 changes: 1 addition & 1 deletion src/components/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
export * from "./AlertDialog";
export * from "./AnimateChangeInHeight";
export * from "./AppHeader";
export * from "./AppSidebar";
export * from "./Badge";
export * from "./Banner";
export * from "./Breadcrumbs";
Expand Down
2 changes: 1 addition & 1 deletion src/components/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ export const focusRing = tv({
variants: {
isFocusVisible: {
false: "outline-0",
true: "outline-2",
true: "outline-2 z-99",
},
},
});
Expand Down
15 changes: 1 addition & 14 deletions src/routes/__root.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ import {
} from "@tanstack/react-router";
import type { ConvexAuthState } from "convex/react";
import { useTheme } from "next-themes";
import React, { Suspense } from "react";
import { RouterProvider } from "react-aria-components";
import { Toaster } from "sonner";

Expand All @@ -40,16 +39,6 @@ function RootRoute() {
const router = useRouter();
const { theme } = useTheme();

const TanStackRouterDevtools =
process.env.NODE_ENV === "production"
? () => null // Render nothing in production
: React.lazy(() =>
// Lazy load in development
import("@tanstack/router-devtools").then((res) => ({
default: res.TanStackRouterDevtools,
})),
);

return (
// TODO: Improve this API
// https://github.com/adobe/react-spectrum/issues/6587
Expand All @@ -64,13 +53,11 @@ function RootRoute() {
}
>
<Outlet />
<Suspense>
<TanStackRouterDevtools />
</Suspense>
<Toaster
theme={theme as "light" | "dark" | "system"}
offset={16}
gap={8}
position="bottom-left"
toastOptions={{
unstyled: true,
classNames: {
Expand Down
4 changes: 1 addition & 3 deletions src/routes/_authenticated.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { AppHeader } from "@/components";
import { Navigate, Outlet, createFileRoute } from "@tanstack/react-router";
import { useConvexAuth } from "convex/react";

Expand All @@ -12,8 +11,7 @@ function AuthenticatedRoute() {
if (!isAuthenticated) return <Navigate to="/signin" />;

return (
<main className="flex flex-col h-screen text-gray-normal">
<AppHeader />
<main className="text-gray-normal">
<Outlet />
</main>
);
Expand Down
Loading

0 comments on commit 824da1c

Please sign in to comment.