Drawer Component
The Drawer component is a versatile sliding panel component built with Vue 3 and Headless UI. It provides a customizable slide-in interface perfect for forms, details panels, or any content that needs to appear from the side of the screen.
Installation
To use the Drawer component you need to install the @headlessui/vue package.
bash
yarn add @robuust-digital/vue-components @headlessui/vueImport the drawer from the dialog package
js
import { Drawer } from '@robuust-digital/vue-components/dialogs';Import CSS
css
@import "@robuust-digital/vue-components/dialogs/css";Basic Usage
Import and use Drawer in your Vue components:
vue
<template>
<Drawer
title="Example Drawer"
:show="isOpen"
@drawer:close="isOpen = false"
>
<p>Drawer content goes here</p>
</Drawer>
</template>
<script setup>
import { ref } from 'vue';
import { Drawer } from '@robuust-digital/vue-components/dialogs';
const isOpen = ref(false);
</script>Props
| Prop | Type | Default | Description |
|---|---|---|---|
| as | String | 'form' | Element type to render (form or div) |
| id | String | Required | Unique identifier for the drawer |
| title | String | Required | Title displayed in the drawer header |
| showClose | Boolean | false | Shows close button in header when true |
| scrolledDown | Boolean | false | Reverses content/footer order when scrolled |
| headerSticky | Boolean | false | Makes the header stick to top when scrolling |
| spinning | Boolean | false | Shows loading state in submit button |
| submitLabel | String | 'Save' | Text for the submit button |
| cancelLabel | String | 'Cancel' | Text for the cancel button |
| panelClass | String | 'max-w-xl' | CSS class for drawer panel width |
| resetHeader | Boolean | false | Resets / hides default header and contents |
| resetFooter | Boolean | false | Resets / hides default footer and contents |
Events
| Event | Arguments | Description |
|---|---|---|
| drawer:open | - | Emitted when drawer starts opening |
| drawer:opened | - | Emitted when open Transition finishes |
| drawer:close | - | Emitted when drawer close is requested |
| drawer:save | event | Emitted when form is submitted (if as="form") |
| drawer:closed | - | Emitted when close Transition finishes |
Slots
Default Slot
The main content area of the drawer.
vue
<template>
<Drawer title="Example">
<p>Main content here</p>
</Drawer>
</template>Header Slot
Customize the header section:
vue
<template>
<Drawer title="Example">
<template #header="{ title, showClose }">
<div class="custom-header">
<h2>{{ title }}</h2>
<button v-if="showClose" @click="isOpen = false">Close</button>
</div>
</template>
</Drawer>
</template>Title slot
Customize the title:
vue
<template>
<Drawer title="Example">
<template #title="{ title, id, dialogTitle }">
<div>
<Component :is="dialogTitle" :id="id">
{{ title }}
</Component>
<p class="text-sm font-light">
Little custom subtitle
</p>
</div>
</template>
</Drawer>
</template>Close slot
Customize the close button:
vue
<template>
<Drawer title="Example">
<template #close="{ icon, emitClose }">
<button @click="emitClose" type="button">
<Component :is="icon" aria-hidden="true" />
</button>
</template>
</Drawer>
</template>Footer Slot
Customize the footer actions:
vue
<template>
<Drawer title="Example">
<template #footer="{ loading }">
<ButtonBase label="Custom Cancel" @click="isOpen = false" />
<ButtonBase
type="submit"
label="Custom Save"
:spinning="loading"
/>
</template>
</Drawer>
</template>Examples
Basic Form Drawer
vue
<template>
<ButtonBase label="Open Drawer" @click="isOpen = true" />
<Drawer
title="Create User"
:show="isOpen"
@drawer:close="isOpen = false"
@drawer:save="handleSave"
>
<div class="space-y-4">
<input v-model="formData.name" type="text" placeholder="Name" />
<input v-model="formData.email" type="email" placeholder="Email" />
</div>
</Drawer>
</template>
<script setup>
import { ref } from 'vue';
const isOpen = ref(false);
const formData = ref({
name: '',
email: '',
});
const handleSave = (e) => {
console.log('Form submitted:', formData.value);
isOpen.value = false;
};
</script>Custom Width Drawer
vue
<template>
<Drawer
title="Wide Drawer"
panel-class="max-w-2xl"
:show="isOpen"
@drawer:close="isOpen = false"
>
<p>This drawer uses a custom width class.</p>
</Drawer>
</template>Loading State
vue
<template>
<Drawer
title="Saving..."
:show="isOpen"
:spinning="isSaving"
@drawer:close="isOpen = false"
@drawer:save="handleSave"
>
<p>The footer submit button will show a spinner when spinning is true.</p>
</Drawer>
</template>CSS Customization ⚡️
To customize the drawer styles global
css
:root {
/* Available variables */
--rvc-drawer-backdrop-bg-color: var(--rvc-dialog-backdrop-bg-color);
--rvc-drawer-padding-x: var(--rvc-dialog-padding-x);
--rvc-drawer-padding-y: var(--rvc-dialog-padding-y);
--rvc-drawer-border-color: var(--rvc-base-border-color);
--rvc-drawer-border-width: var(--rvc-base-border-width);
--rvc-drawer-border-style: var(--rvc-base-border-style);
--rvc-drawer-header-bg-color: var(--rvc-dialog-header-bg-color);
--rvc-drawer-title-font-size: var(--rvc-dialog-title-font-size);
--rvc-drawer-title-font-weight: var(--rvc-dialog-title-font-weight);
--rvc-drawer-title-font-family: var(--rvc-dialog-title-font-family);
--rvc-drawer-title-color: var(--rvc-dialog-title-color);
--rvc-drawer-close-size: var(--rvc-dialog-close-size);
--rvc-drawer-close-color: var(--rvc-dialog-close-color);
--rvc-drawer-close-color-hover: var(--rvc-dialog-close-color-hover);
--rvc-drawer-content-bg-color: var(--rvc-dialog-content-bg-color);
--rvc-drawer-footer-bg-color: var(--rvc-dialog-footer-bg-color);
--rvc-drawer-footer-gap: var(--rvc-dialog-footer-gap);
--rvc-drawer-footer-justify-content: space-between;
--rvc-drawer-transition-timing-function: var(--rvc-dialog-transition-timing-function);
--rvc-drawer-transition-duration: var(--rvc-dialog-transition-duration);
}