Skip to content

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/vue

Import 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

PropTypeDefaultDescription
asString'form'Element type to render (form or div)
idStringRequiredUnique identifier for the drawer
titleStringRequiredTitle displayed in the drawer header
showCloseBooleanfalseShows close button in header when true
scrolledDownBooleanfalseReverses content/footer order when scrolled
headerStickyBooleanfalseMakes the header stick to top when scrolling
spinningBooleanfalseShows loading state in submit button
submitLabelString'Save'Text for the submit button
cancelLabelString'Cancel'Text for the cancel button
panelClassString'max-w-xl'CSS class for drawer panel width

Events

EventArgumentsDescription
drawer:open-Emitted when drawer starts opening
drawer:close-Emitted when drawer close is requested
drawer:saveeventEmitted when form is submitted (if as="form")
drawer:closed-Emitted when 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>

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);
}