DocsComponentsSheet
Sheet
A panel that slides in from an edge of the screen.
Alpine.jsdepends on button6 files
<pb-ui.sheet name="profile">
<pb-slot:trigger><pb-ui.button variant="outline">Open</pb-ui.button></pb-slot:trigger>
<pb-ui.sheet-header>
<pb-ui.sheet-title>Edit profile</pb-ui.sheet-title>
<pb-ui.sheet-description>Save when you are done.</pb-ui.sheet-description>
</pb-ui.sheet-header>
<div class="grid gap-4 px-4">
<div class="grid gap-2"><pb-ui.label for="sname">Name</pb-ui.label><pb-ui.input id="sname" value="Ada Lovelace" /></div>
<div class="grid gap-2"><pb-ui.label for="suser">Username</pb-ui.label><pb-ui.input id="suser" value="@ada" /></div>
</div>
<pb-ui.sheet-footer>
<pb-ui.button>Save</pb-ui.button>
<pb-ui.sheet-close><pb-ui.button variant="outline">Close</pb-ui.button></pb-ui.sheet-close>
</pb-ui.sheet-footer>
</pb-ui.sheet>Installation
pyblade-ui add sheet- Install the dependencies
button and Alpine.js, loaded in your
<head>. - Copy these files into your projectcomponents/ui/sheet.html
@props({'name': '', 'trigger': '', 'side': 'right', 'open': False}) <div data-slot="sheet" x-data="{ open: {{ str(bool(open)).lower() }} }" x-on:keydown.escape.window="open = false" x-on:sheet-open.window="$event.detail === '{{ name }}' && (open = true)" x-on:sheet-close.window="$event.detail === '{{ name }}' && (open = false)" x-effect="document.body.classList.toggle('overflow-hidden', open)" class="contents"> <span data-slot="sheet-trigger" class="contents" x-on:click="open = true">{{ trigger }}</span> <div x-show="open" x-cloak class="fixed inset-0 z-50"> <div data-slot="sheet-overlay" x-show="open" x-transition.opacity.duration.300ms class="fixed inset-0 bg-black/50" x-on:click="open = false"></div> <div role="dialog" aria-modal="true" data-slot="sheet-content" x-show="open" x-transition:enter="transition duration-300 ease-out" x-transition:leave="transition duration-200 ease-in" x-transition:enter-start="{{ {'right': 'translate-x-full', 'left': '-translate-x-full', 'top': '-translate-y-full', 'bottom': 'translate-y-full'}[side] }}" x-transition:leave-end="{{ {'right': 'translate-x-full', 'left': '-translate-x-full', 'top': '-translate-y-full', 'bottom': 'translate-y-full'}[side] }}" {{ attributes.cn( "fixed z-50 flex flex-col gap-4 bg-background shadow-lg", { 'right': 'inset-y-0 right-0 h-full w-3/4 border-l sm:max-w-sm', 'left': 'inset-y-0 left-0 h-full w-3/4 border-r sm:max-w-sm', 'top': 'inset-x-0 top-0 h-auto border-b', 'bottom': 'inset-x-0 bottom-0 h-auto border-t', }[side], ) }}> {{ slot }} <button type="button" x-on:click="open = false" class="absolute top-4 right-4 cursor-pointer rounded-xs opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-ring focus:ring-offset-2 focus:outline-hidden [&_svg]:size-4"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M18 6 6 18"/><path d="m6 6 12 12"/></svg> <span class="sr-only">Close</span> </button> </div> </div> </div>components/ui/sheet_header.html<div data-slot="sheet-header" {{ attributes.cn("flex flex-col gap-1.5 p-4") }}>{{ slot }}</div>components/ui/sheet_footer.html<div data-slot="sheet-footer" {{ attributes.cn("mt-auto flex flex-col gap-2 p-4") }}>{{ slot }}</div>components/ui/sheet_title.html<h2 data-slot="sheet-title" {{ attributes.cn("font-semibold text-foreground") }}>{{ slot }}</h2>components/ui/sheet_description.html<p data-slot="sheet-description" {{ attributes.cn("text-sm text-muted-foreground") }}>{{ slot }}</p>components/ui/sheet_close.html<span data-slot="sheet-close" x-on:click="open = false" {{ attributes.cn("contents") }}>{{ slot }}</span> - Update the path to fit your project
The
componentsfolder is the one set inpyblade.toml.
Usage
The tags <pb-ui.sheet> <pb-ui.sheet-header> <pb-ui.sheet-footer> <pb-ui.sheet-title> <pb-ui.sheet-description> <pb-ui.sheet-close> are available in all your templates.
<pb-ui.sheet side="right">
<pb-slot:trigger><pb-ui.button variant="outline">Open</pb-ui.button></pb-slot:trigger>
<pb-ui.sheet-header>
<pb-ui.sheet-title>Edit profile</pb-ui.sheet-title>
<pb-ui.sheet-description>Save when you are done.</pb-ui.sheet-description>
</pb-ui.sheet-header>
<pb-ui.sheet-footer>
<pb-ui.sheet-close><pb-ui.button variant="outline">Close</pb-ui.button></pb-ui.sheet-close>
</pb-ui.sheet-footer>
</pb-ui.sheet>
{# Or from anywhere: $dispatch('sheet-open', 'name') #}Examples
Sides
<div class="grid grid-cols-2 gap-2">
@for(side in ['top', 'right', 'bottom', 'left'])
<pb-ui.sheet :side="side">
<pb-slot:trigger><pb-ui.button variant="outline" class="w-full">{{ side }}</pb-ui.button></pb-slot:trigger>
<pb-ui.sheet-header>
<pb-ui.sheet-title>{{ side.capitalize() }} panel</pb-ui.sheet-title>
<pb-ui.sheet-description>It slides in from this edge of the screen.</pb-ui.sheet-description>
</pb-ui.sheet-header>
</pb-ui.sheet>
@endfor
</div>Props
| Prop | Default |
|---|---|
| name | '' |
| trigger | '' |
| side | 'right' |
| open | False |
Any other attribute (class, id, x-on:click, hx-post…) is passed to the root element; your Tailwind classes win over the component's.