💭

【NextJs14】NextJs14 と 便利なライブラリ【#9Shadcn-ui Sheet 】

2023/12/20に公開1

【#9Shadcn-ui Sheet 】

YouTube: https://youtu.be/SbPJHmmMk08

https://youtu.be/SbPJHmmMk08

今回は「Sheet」コンポーネントを使用して、
サイドバーを実装します。

https://ui.shadcn.com/docs/components/sheet

npx shadcn-ui@latest add sheet
npx shadcn-ui@latest add separator
app/(main)/protected/page.tsx
import { currentUser, UserProfile } from "@clerk/nextjs";
import { dark } from "@clerk/themes";
import Image from "next/image";

const ProtectedPage = async () => {
  const user = await currentUser();

  return (
    <div className="flex flex-col p-10">
      <ul className="flex flex-col p-6">
        <li>
          User Name: {user?.lastName} {user?.firstName}
        </li>
        <li>User Email: {user?.emailAddresses?.[0].emailAddress}</li>
        <li className="flex gap-x-2">
          User Image:{" "}
          <Image src={user?.imageUrl!} width={30} height={30} alt="User" />
        </li>
      </ul>
      <UserProfile
        appearance={{
          baseTheme: dark,
          elements: {
            card: {
              // boxShadow: "none",
            },
          },
        }}
      />
    </div>
  );
};

export default ProtectedPage;
app/(main)/layout.tsx
import React from "react";
import { MainNavbar } from "./_components/main-navbar";
import { MainSidebar } from "./_components/main-sidebar";

const MainLayout = ({ children }: { children: React.ReactNode }) => {
  return (
    <>
      <MainNavbar />
      <main className="flex w-full relative overflow-hidden overflow-y-auto">
        <div className="w-[200px] shrink-0 hidden lg:block">
          <MainSidebar />
        </div>
        {children}
      </main>
    </>
  );
};

export default MainLayout;
app/(main)/_components/main-sidebar.tsx
import { Separator } from "@/components/ui/separator";
import { Database, Home, Image, User } from "lucide-react";

export const MainSidebar = () => {
  return (
    <div className="w-full flex py-4 px-4 flex-col items-center">
      <div className="w-full">
        <h3 className="text-lg">Menu</h3>
      </div>
      <Separator className="bg-slate-400 mt-2" />
      <div className="w-full py-4">
        <ul>
          <li className="flex items-center group text-neutral-600 dark:text-neutral-400 hover:bg-slate-700 rounded-sm p-2 cursor-pointer">
            <Home className="h-5 w-5 mr-3 group-hover:text-white" />
            <span className="group-hover:text-white">Home</span>
          </li>
          <li className="flex items-center group text-neutral-600 dark:text-neutral-400 hover:bg-slate-700 rounded-sm p-2 cursor-pointer">
            <User className="h-5 w-5 mr-3 group-hover:text-white" />
            <span className="group-hover:text-white">Account</span>
          </li>
          <li className="flex items-center group text-neutral-600 dark:text-neutral-400 hover:bg-slate-700 rounded-sm p-2 cursor-pointer">
            <Image className="h-5 w-5 mr-3 group-hover:text-white" />
            <span className="group-hover:text-white">Images</span>
          </li>
          <li className="flex items-center group text-neutral-600 dark:text-neutral-400 hover:bg-slate-700 rounded-sm p-2 cursor-pointer">
            <Database className="h-5 w-5 mr-3 group-hover:text-white" />
            <span className="group-hover:text-white">Data</span>
          </li>
        </ul>
      </div>
    </div>
  );
};
app/(main)/_components/main-sidebar.tsx
import { Sheet, SheetContent, SheetTrigger } from "@/components/ui/sheet";
import { MainSidebar } from "./main-sidebar";
import { Menu } from "lucide-react";

export const MainMobileSidebar = () => {
  return (
    <Sheet>
      <SheetTrigger>
        <Menu />
      </SheetTrigger>
      <SheetContent side="left" className="w-[240px] dark:bg-slate-900">
        <div>Main Page</div>
        <MainSidebar />
      </SheetContent>
    </Sheet>
  );
};
app/(main)/_components/main-navbar.tsx
import { ModeToggle } from "@/components/mode-toggle-button";
import { UserButton } from "@clerk/nextjs";

import { MainMobileSidebar } from "./main-mobile-sidebar";

export const MainNavbar = () => {
  return (
    <nav className="w-full h-auto px-4 py-3 flex items-center justify-between bg-slate-200 dark:bg-slate-900">
      <div className="hidden lg:block">Main Page</div>
      <div className="block lg:hidden">
        <MainMobileSidebar />
      </div>
      <div className="flex items-center justify-center gap-x-2">
        <ModeToggle />
        <UserButton afterSignOutUrl="/" />
      </div>
    </nav>
  );
};

https://lucide.dev/guide/packages/lucide-react

https://www.radix-ui.com/

Discussion

onjonj

いつも記事を参考にさせていただいています。
nextjs周りのclerkやshadcn-uiを利用した解説にとても感謝しております。

一つ問題が解決できず、質問させていただきます。
shadcn-uiのsheetに関してです。

desktop環境で常にsidebarが表示されている時は問題無いのですが、
mobile環境で、main-mobile-sidebarを表示させ、任意のリンクでurlを遷移させても、
main-mobile-sidebar自体は表示されたままとなる問題が発生しています。
通常urlが変われば、一旦sidebarは非表示としたいところですが、実現方法がわかりません。

Shadcn-ui sheetのページ
https://ui.shadcn.com/docs/components/sheet
や、Radixのページ
https://www.radix-ui.com/primitives/docs/components/dialog
のページを参考に、
<SheetClose asChild>の内側にbuttonやButtonを設置することでsheetをhideすることが、
可能なことは確認できましたが、そうすると記事内で多用されているnext/linkを使うことができず、
行き詰ってしまいました。

解決方法があれば、ご教授お願い致します。

追記:その後、<SheetClose asChild>内に<Link>(next/link)タグではなく、普通の<a>タグを置くことでsheetをhideすることを確認しました。釈然としませんが、先に進みたいと思います。
ありがとうございました。