Sheet

An enterprise-level side sheet component based on BrConfigProvider for global theme configuration. It supports multiple pop-out directions, modal locking, and keyboard accessibility, suitable for form editing, detail viewing, and menu expansion scenarios.

Component Features

  • 🧭 Multi-direction Popover: Supports sliding out from top, bottom, left, and right directions, flexibly adapting to various page layouts.
  • ♿️ Accessibility: Forces focus trapping, supports ESC to close, Enter to confirm, and Tab key to switch focus.
  • 📱 Responsive Design: Full-screen adaptation on small screens, fixed width on large screens, providing an excellent cross-device experience.
  • 🔌 Flexible Integration: Seamlessly integrates with BrButton, BrForm, BrTable, etc.
  • 🔄 State Management: Supports v-model two-way binding to control the display and hiding of the sheet.

Basic Usage

The most basic sheet, pops out from the right by default.

<script setup lang="ts">
import { BrButton, BrSheet, BrSheetTrigger, BrSheetContent, BrSheetHeader, BrSheetTitle, BrSheetDescription, BrSheetFooter, BrSheetClose } from '@breezeui/vue'
</script>

<template>
  <BrSheet>
    <BrSheetTrigger as-child>
      <BrButton variant="outline">Open Sheet</BrButton>
    </BrSheetTrigger>
    <BrSheetContent>
      <BrSheetHeader>
        <BrSheetTitle>Edit Profile</BrSheetTitle>
        <BrSheetDescription>
          Make changes to your profile here. Click save when you're done.
        </BrSheetDescription>
      </BrSheetHeader>
      <div class="grid gap-4 py-4">
        <div class="h-32 rounded-md border border-dashed flex items-center justify-center text-muted-foreground text-sm">
          Form Content
        </div>
      </div>
      <BrSheetFooter>
        <BrSheetClose as-child>
          <BrButton type="submit">Save changes</BrButton>
        </BrSheetClose>
      </BrSheetFooter>
    </BrSheetContent>
  </BrSheet>
</template>

Side Positions

Use the side property to slide out from the top, bottom, left, and right directions.

<script setup lang="ts">
import { BrButton, BrSheet, BrSheetTrigger, BrSheetContent, BrSheetHeader, BrSheetTitle } from '@breezeui/vue'

const sides = ['top', 'right', 'bottom', 'left'] as const
</script>

<template>
  <div class="flex flex-wrap gap-4">
    <BrSheet v-for="side in sides" :key="side">
      <BrSheetTrigger as-child>
        <BrButton variant="outline" class="capitalize">{{ side }}</BrButton>
      </BrSheetTrigger>
      <BrSheetContent :side="side">
        <BrSheetHeader>
          <BrSheetTitle>Sheet - {{ side }}</BrSheetTitle>
        </BrSheetHeader>
        <div class="py-4 text-sm text-muted-foreground">
          Sheet content sliding from the {{ side }}.
        </div>
      </BrSheetContent>
    </BrSheet>
  </div>
</template>

Custom Size

Supports setting the width or height of the sheet to adapt to different content display needs.

<script setup lang="ts">
import { BrButton, BrSheet, BrSheetTrigger, BrSheetContent, BrSheetHeader, BrSheetTitle } from '@breezeui/vue'
</script>

<template>
  <div class="flex flex-wrap gap-4">
    <BrSheet>
      <BrSheetTrigger as-child>
        <BrButton variant="outline">Custom Width (700px)</BrButton>
      </BrSheetTrigger>
      <BrSheetContent class="sm:max-w-[700px]">
        <BrSheetHeader>
          <BrSheetTitle>Wide Sheet</BrSheetTitle>
        </BrSheetHeader>
        <div class="py-4 text-sm text-muted-foreground leading-relaxed">
          Customize the maximum width of the sheet on large screens using the Tailwind class <code>sm:max-w-[700px]</code>. This is useful when you need to display wider forms or complex data.
        </div>
      </BrSheetContent>
    </BrSheet>

    <BrSheet>
      <BrSheetTrigger as-child>
        <BrButton variant="outline">Full Screen Sheet</BrButton>
      </BrSheetTrigger>
      <BrSheetContent class="w-screen sm:max-w-none">
        <BrSheetHeader>
          <BrSheetTitle>Full Screen Display</BrSheetTitle>
        </BrSheetHeader>
        <div class="py-4 text-sm text-muted-foreground leading-relaxed">
          Override the default max-width limit with <code>w-screen sm:max-w-none</code> to make the sheet occupy the entire screen.
        </div>
      </BrSheetContent>
    </BrSheet>
  </div>
</template>

Scroll Area

When there is a lot of content, you can use the BrScrollArea component inside the sheet to achieve partial scrolling.

<script setup lang="ts">
import { BrButton, BrSheet, BrSheetTrigger, BrSheetContent, BrSheetHeader, BrSheetTitle, BrScrollArea } from '@breezeui/vue'
</script>

<template>
  <BrSheet>
    <BrSheetTrigger as-child>
      <BrButton variant="outline">With Long Content</BrButton>
    </BrSheetTrigger>
    <BrSheetContent class="flex flex-col">
      <BrSheetHeader>
        <BrSheetTitle>Terms of Service</BrSheetTitle>
      </BrSheetHeader>
      
      <BrScrollArea class="flex-1 -mx-6 px-6 mt-4">
        <div class="space-y-4 pb-4">
          <p v-for="i in 10" :key="i" class="text-sm text-muted-foreground leading-relaxed">
            This is a placeholder text to demonstrate scrolling of long content. In a real application, detailed terms of service, privacy policies, or complex forms with many fields might be placed here. Using the BrScrollArea component ensures the content scrolls elegantly within the limited space, while keeping the header title fixed and visible. This is paragraph {{ i }}.
          </p>
        </div>
      </BrScrollArea>
    </BrSheetContent>
  </BrSheet>
</template>

Async Close

Combined with controlled mode, the sheet can be locked when submitting a form or executing an asynchronous operation.

<script setup lang="ts">
import { ref } from 'vue'
import { BrButton, BrSheet, BrSheetTrigger, BrSheetContent, BrSheetHeader, BrSheetTitle, BrSheetDescription, BrSheetFooter } from '@breezeui/vue'

const open = ref(false)
const loading = ref(false)

const handleSave = async () => {
  loading.value = true
  // 模拟异步请求
  await new Promise(resolve => setTimeout(resolve, 1500))
  loading.value = false
  open.value = false
}
</script>

<template>
  <BrSheet v-model:open="open">
    <BrSheetTrigger as-child>
      <BrButton variant="outline">Async Submit</BrButton>
    </BrSheetTrigger>
    <BrSheetContent 
      @interact-outside="(e) => loading && e.preventDefault()" 
      @escape-key-down="(e) => loading && e.preventDefault()"
    >
      <BrSheetHeader>
        <BrSheetTitle>Async Operation</BrSheetTitle>
        <BrSheetDescription>
          Clicking save will wait 1.5 seconds before closing the sheet. During this time, closing by clicking outside or pressing ESC is disabled.
        </BrSheetDescription>
      </BrSheetHeader>
      
      <div class="py-8 flex justify-center">
        <div v-if="loading" class="text-sm text-muted-foreground animate-pulse">
          Processing, please wait...
        </div>
        <div v-else class="text-sm text-muted-foreground">
          Ready, click save below to start.
        </div>
      </div>

      <BrSheetFooter>
        <BrButton :loading="loading" @click="handleSave">Save</BrButton>
      </BrSheetFooter>
    </BrSheetContent>
  </BrSheet>
</template>

API Reference

BrSheet

PropTypeDefaultDescription
openbooleanfalseControls the display and hiding of the sheet, supports v-model:open two-way binding
defaultOpenbooleanfalseInitial display state (uncontrolled mode)
modalbooleantrueWhether it is a modal sheet (shows overlay, disables background interaction)

BrSheetContent

PropTypeDefaultDescription
side'top' | 'right' | 'bottom' | 'left''right'The direction the sheet pops out
showClosebooleantrueWhether to show the close button in the top right corner
classstring-Custom class name, used to override default styles

Events (BrSheetContent)

Event NameTypeDescription
escapeKeyDown[event: KeyboardEvent]Triggered when ESC key is pressed, event.preventDefault() can be called to prevent default close behavior
pointerDownOutside[event: PointerDownOutsideEvent]Triggered when clicking the external area, event.preventDefault() can be called to prevent default close behavior
interactOutside[event: Event]Triggered when interacting with the external area (combines focus and pointer)

SheetManager

Exports getZIndex(), nextZIndex(), resetZIndex() for developers to handle complex hierarchical relationships.

Theme Customization

The style of the BrSheet component completely relies on the global design Tokens provided by BrConfigProvider. You can override CSS variables at the entry of the project or at any level to achieve customization:

:root {
  /* Override sheet background color */
  --background: 0 0% 100%;
  /* Override border radius */
  --radius: 0.5rem;
  /* Override shadow style */
  --shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1);
  /* Animation transition duration */
  --transition-duration: 300ms;
}

.dark {
  --background: 222.2 84% 4.9%;
}

Running Instructions and Notes

  1. Modal Rules: Default modal="true", background content cannot be scrolled or interacted with, focus is locked inside the sheet.
  2. z-index Management: In multiple instances, the z-index of the overlay and content will automatically increment via SheetManager (base is 50).
  3. ESC Shortcut: Default supports ESC key to close, can listen to @escapeKeyDown on BrSheetContent to prevent default behavior.
  4. External Click Close: Clicking the overlay layer closes the sheet by default, can be disabled by listening to @interactOutside="(e) => e.preventDefault()".
  5. Async Close: Combined with v-model:open control, buttons and external clicks can be disabled to ensure the safe execution of asynchronous requests.