HextaUI

Sheet

Herhangi bir kenardan kayarak gelen bir panel; kaydırarak kapatma, kaydırma kilidi ve üst üste iç içe geçme desteği vardır.

import { Button } from "@/components/ui/button"
import {
  Sheet,
  SheetBody,
  SheetClose,
  SheetContent,
  SheetDescription,
  SheetFooter,
  SheetHeader,
  SheetTitle,
  SheetTrigger,
} from "@/components/ui/sheet"

export function SheetDemo() {
  return (
    <Sheet>
      <SheetTrigger render={<Button variant="outline" />}>
        Edit profile
      </SheetTrigger>
      <SheetContent>
        <SheetHeader>
          <SheetTitle>Edit profile</SheetTitle>
          <SheetDescription>
            Changes are saved to your account when you click save.
          </SheetDescription>
        </SheetHeader>
        <SheetBody>
          <div className="flex flex-col gap-4">
            <div className="flex flex-col gap-1.5">
              <label htmlFor="sheet-demo-name" className="text-sm font-medium">
                Name
              </label>
              <input
                id="sheet-demo-name"
                defaultValue="Olivia Martin"
                className="h-9 rounded-md border border-input bg-transparent px-3 text-sm transition-shadow outline-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-focus-ring focus-visible:outline-hidden pointer-coarse:text-touch"
              />
            </div>
            <div className="flex flex-col gap-1.5">
              <label
                htmlFor="sheet-demo-username"
                className="text-sm font-medium"
              >
                Username
              </label>
              <input
                id="sheet-demo-username"
                defaultValue="@olivia"
                className="h-9 rounded-md border border-input bg-transparent px-3 text-sm transition-shadow outline-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-focus-ring focus-visible:outline-hidden pointer-coarse:text-touch"
              />
            </div>
          </div>
        </SheetBody>
        <SheetFooter>
          <SheetClose render={<Button variant="outline" />}>Cancel</SheetClose>
          <SheetClose render={<Button />}>Save changes</SheetClose>
        </SheetFooter>
      </SheetContent>
    </Sheet>
  )
}
pnpm dlx shadcn@latest add https://hextaui.com/r/sheet.json

Bileşeni, HextaUI tema token'larını ve bileşenin bağımlı olduğu tüm HextaUI bileşenlerini ekler.

import {
  Sheet,
  SheetBody,
  SheetClose,
  SheetContent,
  SheetDescription,
  SheetFooter,
  SheetHeader,
  SheetTitle,
  SheetTrigger,
} from "@/components/ui/sheet"
<Sheet>
  <SheetTrigger render={<Button variant="outline" />}>Open</SheetTrigger>
  <SheetContent>
    <SheetHeader>
      <SheetTitle>Edit profile</SheetTitle>
      <SheetDescription>Make changes to your profile.</SheetDescription>
    </SheetHeader>
    <SheetBody>{/* content */}</SheetBody>
    <SheetFooter>
      <SheetClose render={<Button />}>Save changes</SheetClose>
    </SheetFooter>
  </SheetContent>
</Sheet>
Sheet
├── SheetTrigger
└── SheetContent
    ├── SheetHeader
    │   ├── SheetTitle
    │   └── SheetDescription
    ├── SheetBody
    └── SheetFooter
        └── SheetClose

Kenarlar

Herhangi bir kenardan kayarak girmesi için side ayarlayın. Her sheet kendi kenarına doğru kaydırılarak geri alınabilir ve kapatma hareketi parmağınızı ivmeyle izler.

import { Button } from "@/components/ui/button"
import {
  Sheet,
  SheetClose,
  SheetContent,
  SheetDescription,
  SheetFooter,
  SheetHeader,
  SheetTitle,
  SheetTrigger,
} from "@/components/ui/sheet"

const sides = ["top", "right", "bottom", "left"] as const

export function SheetSides() {
  return (
    <div className="grid grid-cols-2 gap-2">
      {sides.map((side) => (
        <Sheet key={side}>
          <SheetTrigger render={<Button variant="outline" />}>
            {side}
          </SheetTrigger>
          <SheetContent side={side}>
            <SheetHeader>
              <SheetTitle>Opens from the {side}</SheetTitle>
              <SheetDescription>
                Swipe towards the {side} edge or press Esc to close it.
              </SheetDescription>
            </SheetHeader>
            <SheetFooter>
              <SheetClose render={<Button />}>Done</SheetClose>
            </SheetFooter>
          </SheetContent>
        </Sheet>
      ))}
    </div>
  )
}

Uzun içerik

<SheetBody /> kendi başına kayar, böylece içerik ne kadar uzarsa uzasın başlık ve alt bilgi yerinde kalır.

import { Button } from "@/components/ui/button"
import {
  Sheet,
  SheetBody,
  SheetClose,
  SheetContent,
  SheetDescription,
  SheetFooter,
  SheetHeader,
  SheetTitle,
  SheetTrigger,
} from "@/components/ui/sheet"

export function SheetLongContent() {
  return (
    <Sheet>
      <SheetTrigger render={<Button variant="outline" />}>
        Release notes
      </SheetTrigger>
      <SheetContent>
        <SheetHeader>
          <SheetTitle>What’s new</SheetTitle>
          <SheetDescription>Version 2.4.0</SheetDescription>
        </SheetHeader>
        <SheetBody>
          <div className="flex flex-col gap-3 text-sm text-muted-foreground">
            {Array.from({ length: 24 }, (_, index) => (
              <p key={index}>
                {index + 1}. Improved performance of the dashboard charts and
                fixed an issue where filters reset after navigation.
              </p>
            ))}
          </div>
        </SheetBody>
        <SheetFooter>
          <SheetClose render={<Button />}>Got it</SheetClose>
        </SheetFooter>
      </SheetContent>
    </Sheet>
  )
}

Kapatma düğmesi olmadan

Alt bilginin zaten bir çıkış yolu varsa showCloseButton={false} ayarlayın. Escape, arka plan ve kaydırarak kapatma yine de kapatır.

import { Button } from "@/components/ui/button"
import {
  Sheet,
  SheetClose,
  SheetContent,
  SheetDescription,
  SheetFooter,
  SheetHeader,
  SheetTitle,
  SheetTrigger,
} from "@/components/ui/sheet"

export function SheetNoCloseButton() {
  return (
    <Sheet>
      <SheetTrigger render={<Button variant="outline" />}>Share</SheetTrigger>
      <SheetContent side="bottom" showCloseButton={false}>
        <SheetHeader>
          <SheetTitle>Share link</SheetTitle>
          <SheetDescription>
            Anyone with the link can view this document.
          </SheetDescription>
        </SheetHeader>
        <SheetFooter>
          <SheetClose render={<Button />}>Done</SheetClose>
        </SheetFooter>
      </SheetContent>
    </Sheet>
  )
}

İç içe

Bir sheet'ten açılan sheet veya alert dialog üstte katmanlanır. Üst öğe hafifçe geri ölçeklenir ve her ek katman daha açık bir arka plan ekler; böylece yığın okunabilir kalır. Escape yalnızca en üst katmanı kapatır.

import {
  AlertDialog,
  AlertDialogAction,
  AlertDialogCancel,
  AlertDialogContent,
  AlertDialogDescription,
  AlertDialogFooter,
  AlertDialogHeader,
  AlertDialogTitle,
  AlertDialogTrigger,
} from "@/components/ui/alert-dialog"
import { Button } from "@/components/ui/button"
import {
  Sheet,
  SheetBody,
  SheetContent,
  SheetDescription,
  SheetHeader,
  SheetTitle,
  SheetTrigger,
} from "@/components/ui/sheet"

export function SheetNested() {
  return (
    <Sheet>
      <SheetTrigger render={<Button variant="outline" />}>
        Workspace settings
      </SheetTrigger>
      <SheetContent>
        <SheetHeader>
          <SheetTitle>Workspace settings</SheetTitle>
          <SheetDescription>
            Manage members and the danger zone.
          </SheetDescription>
        </SheetHeader>
        <SheetBody>
          <div className="flex flex-col items-start gap-2">
            <Sheet>
              <SheetTrigger render={<Button variant="outline" />}>
                Manage members
              </SheetTrigger>
              <SheetContent>
                <SheetHeader>
                  <SheetTitle>Members</SheetTitle>
                  <SheetDescription>
                    The parent sheet steps back while this one is open.
                  </SheetDescription>
                </SheetHeader>
              </SheetContent>
            </Sheet>
            <AlertDialog>
              <AlertDialogTrigger render={<Button variant="destructive" />}>
                Delete workspace
              </AlertDialogTrigger>
              <AlertDialogContent>
                <AlertDialogHeader>
                  <AlertDialogTitle>Delete workspace?</AlertDialogTitle>
                  <AlertDialogDescription>
                    All projects in this workspace will be removed.
                  </AlertDialogDescription>
                </AlertDialogHeader>
                <AlertDialogFooter>
                  <AlertDialogCancel>Cancel</AlertDialogCancel>
                  <AlertDialogAction variant="destructive">
                    Delete
                  </AlertDialogAction>
                </AlertDialogFooter>
              </AlertDialogContent>
            </AlertDialog>
          </div>
        </SheetBody>
      </SheetContent>
    </Sheet>
  )
}

Kontrollü

Trigger olmadan her yerden açmak için open ve onOpenChange geçin.

"use client"

import * as React from "react"

import { Button } from "@/components/ui/button"
import {
  Sheet,
  SheetBody,
  SheetClose,
  SheetContent,
  SheetDescription,
  SheetFooter,
  SheetHeader,
  SheetTitle,
} from "@/components/ui/sheet"

export function SheetControlled() {
  const [open, setOpen] = React.useState(false)

  return (
    <div className="flex flex-wrap items-center gap-3">
      <Button variant="outline" onClick={() => setOpen(true)}>
        Open from state
      </Button>
      <span className="text-sm text-muted-foreground">
        {open ? "Open" : "Closed"}
      </span>
      <Sheet open={open} onOpenChange={setOpen}>
        <SheetContent>
          <SheetHeader>
            <SheetTitle>Edit profile</SheetTitle>
            <SheetDescription>
              Changes are saved to your account when you click save.
            </SheetDescription>
          </SheetHeader>
          <SheetBody>
            <div className="flex flex-col gap-4">
              <div className="flex flex-col gap-1.5">
                <label
                  htmlFor="sheet-controlled-name"
                  className="text-sm font-medium"
                >
                  Name
                </label>
                <input
                  id="sheet-controlled-name"
                  defaultValue="Olivia Martin"
                  className="h-9 rounded-md border border-input bg-transparent px-3 text-sm transition-shadow outline-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-focus-ring focus-visible:outline-hidden pointer-coarse:text-touch"
                />
              </div>
              <div className="flex flex-col gap-1.5">
                <label
                  htmlFor="sheet-controlled-username"
                  className="text-sm font-medium"
                >
                  Username
                </label>
                <input
                  id="sheet-controlled-username"
                  defaultValue="@olivia"
                  className="h-9 rounded-md border border-input bg-transparent px-3 text-sm transition-shadow outline-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-focus-ring focus-visible:outline-hidden pointer-coarse:text-touch"
                />
              </div>
            </div>
          </SheetBody>
          <SheetFooter>
            <SheetClose render={<Button variant="outline" />}>
              Cancel
            </SheetClose>
            <SheetClose render={<Button />}>Save changes</SheetClose>
          </SheetFooter>
        </SheetContent>
      </Sheet>
    </div>
  )
}

Ayrık tetikleyiciler

createSheetHandle ile birden çok tetikleyici arasında tek bir sheet paylaşın. Her tetikleyici, sheet'in bir fonksiyon alt öğesiyle işlediği bir payload geçirir.

"use client"

import { Button } from "@/components/ui/button"
import {
  createSheetHandle,
  Sheet,
  SheetContent,
  SheetDescription,
  SheetHeader,
  SheetTitle,
  SheetTrigger,
} from "@/components/ui/sheet"

const orders = createSheetHandle<{ id: string; status: string }>()

export function SheetDetached() {
  return (
    <div className="flex flex-wrap justify-center gap-2">
      <SheetTrigger
        handle={orders}
        payload={{ id: "#1042", status: "Shipped" }}
        render={<Button variant="outline" size="sm" />}
      >
        Order #1042
      </SheetTrigger>
      <SheetTrigger
        handle={orders}
        payload={{ id: "#1043", status: "Processing" }}
        render={<Button variant="outline" size="sm" />}
      >
        Order #1043
      </SheetTrigger>
      <Sheet handle={orders}>
        {({ payload }) => (
          <SheetContent>
            <SheetHeader>
              <SheetTitle>Order {payload?.id}</SheetTitle>
              <SheetDescription>Status: {payload?.status}</SheetDescription>
            </SheetHeader>
          </SheetContent>
        )}
      </Sheet>
    </div>
  )
}

Sağdan sola

dir="rtl" ile sağ taraf sol kenara yansır, oradan kayarak girer ve aynı şekilde kaydırılarak kapanır.

import { Button } from "@/components/ui/button"
import {
  Sheet,
  SheetClose,
  SheetContent,
  SheetDescription,
  SheetFooter,
  SheetHeader,
  SheetTitle,
  SheetTrigger,
} from "@/components/ui/sheet"

export function SheetRtl() {
  return (
    <div dir="rtl">
      <Sheet>
        <SheetTrigger render={<Button variant="outline" />}>افتح</SheetTrigger>
        <SheetContent dir="rtl">
          <SheetHeader>
            <SheetTitle>تعديل الملف الشخصي</SheetTitle>
            <SheetDescription>
              يتم حفظ التغييرات عند النقر على حفظ.
            </SheetDescription>
          </SheetHeader>
          <SheetFooter>
            <SheetClose render={<Button variant="outline" />}>إلغاء</SheetClose>
            <SheetClose render={<Button />}>حفظ</SheetClose>
          </SheetFooter>
        </SheetContent>
      </Sheet>
    </div>
  )
}
TuşAction
EnterSpaceTetikleyicide sheet'i açar ve odağı içine taşır.
TabShift + TabOdaklanabilir öğeler arasında gezinir. Odak sheet'in içinde kalır.
EscEn üstteki sheet'i kapatır ve odağı tetikleyicisine döndürür.
  • Sheet modal bir dialog'dur. <SheetTitle /> onu etiketler ve <SheetDescription /> tanımlar, bu yüzden her zaman bir başlık ekleyin.
  • Sheet açıkken sayfa kaydırma kilitlenir ve arkasındaki içerik yardımcı teknolojilerden gizlenir.
  • Kapatma düğmesi “Close” olarak etiketlenmiştir. İç kenardaki tutamaç sheet'in sürüklenebildiğini gösterir ve yardımcı teknolojilerden gizlidir.
  • Azaltılmış hareket açıkken sheet kayarak değil solarak girer ve çıkar.

Base UI drawer üzerine kuruludur. Her parça, sardığı primitive'in prop'larını kabul eder.

PropTürVarsayılan
defaultOpen
booleanfalse
open
boolean–
onOpenChange
(open: boolean, details) => void–
onOpenChangeCompleteAçma ya da kapatma animasyonu bittikten sonra çağrılır.
(open: boolean) => void–
modal
boolean | "trap-focus"true
disablePointerDismissalArka plana tıklandığında açık tutun.
booleanfalse
swipeDirectionİçeriğin tarafı ve yönünden otomatik ayarlanır.
"up" | "down" | "left" | "right"–
handle
SheetHandle<Payload>–
children
ReactNode | ({ payload }) => ReactNode–
PropTürVarsayılan
handle
SheetHandle<Payload>–
payload
Payload–
render
ReactElement | (props, state) => ReactElement<button>
ÖznitelikAçıklama
data-slot="sheet-trigger"CSS'te tetikleyiciyi hedefleyin.
data-popup-openSheet'i açıkken bulunur.

Portal'ı, arka planı, viewport'u ve açılır pencereyi, ayrıca sürükleme tutamacını ve kapatma düğmesini işler.

PropTürVarsayılan
side
"top" | "right" | "bottom" | "left""right"
showCloseButton
booleantrue
dirSheet'in yönünü belirler. right ve left rtl'de yansıtılır.
"ltr" | "rtl"–
initialFocus
boolean | RefObject | (type) => HTMLElement | boolean–
finalFocus
boolean | RefObject | (type) => HTMLElement | boolean–
classNameYan sheet'ler %75 genişliğindedir, sm breakpoint'inden itibaren en fazla 24rem.
string | (state) => string–
render
ReactElement | (props, state) => ReactElement<div>
ÖznitelikAçıklama
data-slot="sheet-content"Sheet paneli.
data-sideAçıldığı kenar.
data-openSheet açıkken bulunur.
data-starting-styleAnimasyonla belirirken bulunur.
data-ending-styleAnimasyonla kaybolurken bulunur.
data-swipingSürüklenirken bulunur.
data-swipe-directionOnu kapatan yön.
data-nested-drawer-openÜstte iç içe bir sheet açıkken bulunur.
data-slot="sheet-handle"İç kenardaki sürükleme tutamacı.
data-slot="sheet-close-button"Yerleşik kapatma düğmesi.
--drawer-swipe-movement-xYatay sürükleme mesafesi. Bir -y değişkeni de vardır.
--nested-drawersÜstte kaç iç içe sheet'in açık olduğu.

<SheetContent /> tarafından işlenir. İç içe katmanlar daha açık bir arka plan alır.

ÖznitelikAçıklama
data-slot="sheet-overlay"Arka plan.
data-nestedİç içe katmanların arka planlarında bulunur.

Sheet'i düzenleyen düz <div> öğeleri. Başlık kapatma düğmesine yer bırakır, gövde kayar ve kalan yüksekliği alır, alt bilgi ise küçük ekranlarda eylemlerini üst üste dizer ve sm breakpoint'inden itibaren sonda hizalar.

ÖznitelikAçıklama
data-slot="sheet-header"Başlık ve açıklama.
data-slot="sheet-body"Kaydırılabilir içerik.
data-slot="sheet-footer"Eylemler.
PropTürVarsayılan
render
ReactElement | (props, state) => ReactElement<h2>
ÖznitelikAçıklama
data-slot="sheet-title"Sheet'i etiketler.
PropTürVarsayılan
render
ReactElement | (props, state) => ReactElement<p>
ÖznitelikAçıklama
data-slot="sheet-description"Sheet'i tanımlar.
PropTürVarsayılan
render
ReactElement | (props, state) => ReactElement<button>
ÖznitelikAçıklama
data-slot="sheet-close"Basıldığında sheet'i kapatır.

createSheetHandle<Payload>(), bir <Sheet /> öğesini başka bir yerde render edilen tetikleyicilere bağlayan bir tanıtıcı döndürür. Bileşeninizin dışında, yalnızca bir kez oluşturun.

Kullanıldığı bloklar

Sheet üzerine kurulan bloklar.