feat(ui): add scroll area component and navigation enhancements
Add ScrollArea UI component using Radix UI for better content scrolling. Enhance header navigation with back button and conditional home button display. Update dependencies including @radix-ui/react-scroll-area and reorganize @nontara/server.
This commit is contained in:
@@ -9,9 +9,11 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@nontara/language-codes": "workspace:*",
|
"@nontara/language-codes": "workspace:*",
|
||||||
|
"@nontara/server": "workspace:*",
|
||||||
"@radix-ui/react-avatar": "^1.1.10",
|
"@radix-ui/react-avatar": "^1.1.10",
|
||||||
"@radix-ui/react-dialog": "^1.1.15",
|
"@radix-ui/react-dialog": "^1.1.15",
|
||||||
"@radix-ui/react-dropdown-menu": "^2.1.16",
|
"@radix-ui/react-dropdown-menu": "^2.1.16",
|
||||||
|
"@radix-ui/react-scroll-area": "^1.2.10",
|
||||||
"@radix-ui/react-select": "^2.2.6",
|
"@radix-ui/react-select": "^2.2.6",
|
||||||
"@radix-ui/react-separator": "^1.1.7",
|
"@radix-ui/react-separator": "^1.1.7",
|
||||||
"@radix-ui/react-slot": "^1.2.3",
|
"@radix-ui/react-slot": "^1.2.3",
|
||||||
@@ -26,7 +28,6 @@
|
|||||||
"@trpc/client": "^11.5.0",
|
"@trpc/client": "^11.5.0",
|
||||||
"@trpc/server": "^11.5.0",
|
"@trpc/server": "^11.5.0",
|
||||||
"@trpc/tanstack-react-query": "^11.5.0",
|
"@trpc/tanstack-react-query": "^11.5.0",
|
||||||
"@nontara/server": "workspace:*",
|
|
||||||
"better-auth": "^1.3.10",
|
"better-auth": "^1.3.10",
|
||||||
"class-variance-authority": "^0.7.1",
|
"class-variance-authority": "^0.7.1",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { Link, useRouterState } from "@tanstack/react-router";
|
import { Link, useRouter, useRouterState } from "@tanstack/react-router";
|
||||||
import {
|
import {
|
||||||
|
ArrowLeft,
|
||||||
Folder,
|
Folder,
|
||||||
FolderTree,
|
FolderTree,
|
||||||
Home,
|
Home,
|
||||||
@@ -115,8 +116,9 @@ NavigationSheetContent.displayName = "NavigationSheetContent";
|
|||||||
function HeaderBar() {
|
function HeaderBar() {
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
const handleNavigate = useCallback(() => setOpen(false), []);
|
const handleNavigate = useCallback(() => setOpen(false), []);
|
||||||
const router = useRouterState();
|
const router = useRouter();
|
||||||
const pathname = router.location.pathname;
|
const routerState = useRouterState();
|
||||||
|
const pathname = routerState.location.pathname;
|
||||||
|
|
||||||
if (pathname.endsWith("dashboard") || pathname.endsWith("dashboard/")) {
|
if (pathname.endsWith("dashboard") || pathname.endsWith("dashboard/")) {
|
||||||
return;
|
return;
|
||||||
@@ -125,6 +127,30 @@ function HeaderBar() {
|
|||||||
return (
|
return (
|
||||||
<header className="sticky top-0 z-50 flex items-center justify-between border-border border-b bg-muted/95 px-5 py-3 shadow-sm backdrop-blur supports-[backdrop-filter]:bg-muted/80">
|
<header className="sticky top-0 z-50 flex items-center justify-between border-border border-b bg-muted/95 px-5 py-3 shadow-sm backdrop-blur supports-[backdrop-filter]:bg-muted/80">
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-3">
|
||||||
|
{pathname !== "/home" && (
|
||||||
|
<>
|
||||||
|
<Button
|
||||||
|
type="button"
|
||||||
|
variant="ghost"
|
||||||
|
size="icon"
|
||||||
|
aria-label="Go back"
|
||||||
|
onClick={() => router.history.back()}
|
||||||
|
>
|
||||||
|
<ArrowLeft className="size-5" aria-hidden="true" />
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
type="button"
|
||||||
|
variant="ghost"
|
||||||
|
size="icon"
|
||||||
|
aria-label="Go to home"
|
||||||
|
asChild
|
||||||
|
>
|
||||||
|
<Link to="/home">
|
||||||
|
<Home className="size-5" aria-hidden="true" />
|
||||||
|
</Link>
|
||||||
|
</Button>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
<Sheet open={open} onOpenChange={setOpen}>
|
<Sheet open={open} onOpenChange={setOpen}>
|
||||||
<SheetTrigger asChild>
|
<SheetTrigger asChild>
|
||||||
<Button
|
<Button
|
||||||
@@ -141,6 +167,8 @@ function HeaderBar() {
|
|||||||
<NavigationSheetContent onNavigate={handleNavigate} />
|
<NavigationSheetContent onNavigate={handleNavigate} />
|
||||||
</SheetContent>
|
</SheetContent>
|
||||||
</Sheet>
|
</Sheet>
|
||||||
|
{pathname === "/home" && (
|
||||||
|
<Link to="/home" className="flex items-center">
|
||||||
<img
|
<img
|
||||||
src={logoSrc}
|
src={logoSrc}
|
||||||
alt="Nontara logo"
|
alt="Nontara logo"
|
||||||
@@ -150,6 +178,8 @@ function HeaderBar() {
|
|||||||
decoding="async"
|
decoding="async"
|
||||||
draggable={false}
|
draggable={false}
|
||||||
/>
|
/>
|
||||||
|
</Link>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<Button
|
<Button
|
||||||
|
|||||||
56
apps/web/src/components/ui/scroll-area.tsx
Normal file
56
apps/web/src/components/ui/scroll-area.tsx
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
import * as ScrollAreaPrimitive from "@radix-ui/react-scroll-area";
|
||||||
|
import type * as React from "react";
|
||||||
|
|
||||||
|
import { cn } from "@/lib/utils";
|
||||||
|
|
||||||
|
function ScrollArea({
|
||||||
|
className,
|
||||||
|
children,
|
||||||
|
...props
|
||||||
|
}: React.ComponentProps<typeof ScrollAreaPrimitive.Root>) {
|
||||||
|
return (
|
||||||
|
<ScrollAreaPrimitive.Root
|
||||||
|
data-slot="scroll-area"
|
||||||
|
className={cn("relative", className)}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
<ScrollAreaPrimitive.Viewport
|
||||||
|
data-slot="scroll-area-viewport"
|
||||||
|
className="size-full rounded-[inherit] outline-none transition-[color,box-shadow] focus-visible:outline-1 focus-visible:ring-[3px] focus-visible:ring-ring/50"
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</ScrollAreaPrimitive.Viewport>
|
||||||
|
<ScrollBar />
|
||||||
|
<ScrollAreaPrimitive.Corner />
|
||||||
|
</ScrollAreaPrimitive.Root>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function ScrollBar({
|
||||||
|
className,
|
||||||
|
orientation = "vertical",
|
||||||
|
...props
|
||||||
|
}: React.ComponentProps<typeof ScrollAreaPrimitive.ScrollAreaScrollbar>) {
|
||||||
|
return (
|
||||||
|
<ScrollAreaPrimitive.ScrollAreaScrollbar
|
||||||
|
data-slot="scroll-area-scrollbar"
|
||||||
|
orientation={orientation}
|
||||||
|
className={cn(
|
||||||
|
"flex touch-none select-none p-px transition-colors",
|
||||||
|
orientation === "vertical" &&
|
||||||
|
"h-full w-2.5 border-l border-l-transparent",
|
||||||
|
orientation === "horizontal" &&
|
||||||
|
"h-2.5 flex-col border-t border-t-transparent",
|
||||||
|
className,
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
<ScrollAreaPrimitive.ScrollAreaThumb
|
||||||
|
data-slot="scroll-area-thumb"
|
||||||
|
className="relative flex-1 rounded-full bg-border"
|
||||||
|
/>
|
||||||
|
</ScrollAreaPrimitive.ScrollAreaScrollbar>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export { ScrollArea, ScrollBar };
|
||||||
@@ -11,6 +11,7 @@ import {
|
|||||||
import { TanStackRouterDevtools } from "@tanstack/react-router-devtools";
|
import { TanStackRouterDevtools } from "@tanstack/react-router-devtools";
|
||||||
import type { TRPCOptionsProxy } from "@trpc/tanstack-react-query";
|
import type { TRPCOptionsProxy } from "@trpc/tanstack-react-query";
|
||||||
import { ThemeProvider } from "next-themes";
|
import { ThemeProvider } from "next-themes";
|
||||||
|
import { ScrollArea, ScrollBar } from "@/components/ui/scroll-area";
|
||||||
import { Toaster } from "@/components/ui/sonner";
|
import { Toaster } from "@/components/ui/sonner";
|
||||||
import type { AppRouter } from "../../../server/src/trpc";
|
import type { AppRouter } from "../../../server/src/trpc";
|
||||||
import appCss from "../index.css?url";
|
import appCss from "../index.css?url";
|
||||||
@@ -57,11 +58,14 @@ function RootDocument() {
|
|||||||
enableSystem
|
enableSystem
|
||||||
disableTransitionOnChange
|
disableTransitionOnChange
|
||||||
>
|
>
|
||||||
<div className="grid h-svh grid-rows-[auto_1fr]">
|
<ScrollArea className="h-svh w-full">
|
||||||
|
<div className="grid min-h-svh grid-rows-[auto_1fr]">
|
||||||
{/* <Header />
|
{/* <Header />
|
||||||
{isFetching ? <Loader /> : <Outlet />} */}
|
{isFetching ? <Loader /> : <Outlet />} */}
|
||||||
<Outlet />
|
<Outlet />
|
||||||
</div>
|
</div>
|
||||||
|
<ScrollBar orientation="vertical" />
|
||||||
|
</ScrollArea>
|
||||||
<Toaster richColors />
|
<Toaster richColors />
|
||||||
</ThemeProvider>
|
</ThemeProvider>
|
||||||
<TanStackRouterDevtools position="bottom-left" />
|
<TanStackRouterDevtools position="bottom-left" />
|
||||||
|
|||||||
5
bun.lock
5
bun.lock
@@ -41,6 +41,7 @@
|
|||||||
"@radix-ui/react-avatar": "^1.1.10",
|
"@radix-ui/react-avatar": "^1.1.10",
|
||||||
"@radix-ui/react-dialog": "^1.1.15",
|
"@radix-ui/react-dialog": "^1.1.15",
|
||||||
"@radix-ui/react-dropdown-menu": "^2.1.16",
|
"@radix-ui/react-dropdown-menu": "^2.1.16",
|
||||||
|
"@radix-ui/react-scroll-area": "^1.2.10",
|
||||||
"@radix-ui/react-select": "^2.2.6",
|
"@radix-ui/react-select": "^2.2.6",
|
||||||
"@radix-ui/react-separator": "^1.1.7",
|
"@radix-ui/react-separator": "^1.1.7",
|
||||||
"@radix-ui/react-slot": "^1.2.3",
|
"@radix-ui/react-slot": "^1.2.3",
|
||||||
@@ -67,8 +68,8 @@
|
|||||||
"react-dom": "19.1.0",
|
"react-dom": "19.1.0",
|
||||||
"sonner": "^2.0.3",
|
"sonner": "^2.0.3",
|
||||||
"tailwind-merge": "^3.3.1",
|
"tailwind-merge": "^3.3.1",
|
||||||
"tailwindcss": "^4.1.13",
|
"tailwindcss": "^4.1.3",
|
||||||
"tw-animate-css": "^1.4.0",
|
"tw-animate-css": "^1.2.5",
|
||||||
"vite-tsconfig-paths": "^5.1.4",
|
"vite-tsconfig-paths": "^5.1.4",
|
||||||
"zod": "^4.0.2",
|
"zod": "^4.0.2",
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user