Components
Slideout
Extends the Dialog component to display content that complements the main content of the screen.
Source Code
Copy the following code into your project.
<template>
<HTransitionRoot appear :show="isOpen" as="template">
<HDialog as="div" @close="closeOnOutsideClick && close()" :class="cn(parentWrapper)">
<HTransitionChild
:appear="appear"
v-if="overlay"
as="template"
enter="duration-300 ease-out"
leave="duration-200 ease-in"
enter-from="opacity-0"
leave-to="opacity-0"
>
<div class="fixed inset-0 bg-background/60 backdrop-blur" />
</HTransitionChild>
<div>
<HTransitionChild
as="template"
v-bind="transitionClass"
enter="transform transition ease-in-out duration-300"
leave="transform transition ease-in-out duration-200"
>
<HDialogPanel
:class="
cn(
'relative flex w-full flex-1 flex-col bg-background focus:outline-none',
$attrs.class
)
"
>
<slot :close="close" />
</HDialogPanel>
</HTransitionChild>
</div>
</HDialog>
</HTransitionRoot>
</template>
<script setup lang="ts">
defineOptions({ inheritAttrs: false });
const props = withDefaults(
defineProps<{
/**
* The side of the screen the slideout will appear from.
*/
side?: "left" | "right" | "top" | "bottom";
/**
* Whether the transition should run on initial mount.
* @default true
*/
appear?: boolean;
/**
* Control the state of the slideout
*/
modelValue?: boolean;
/**
* Whether to show the overlay
* @default true
*/
overlay?: boolean;
/**
* Whether to close the slideout when clicking outside of it
* @default true
*/
closeOnOutsideClick?: boolean;
}>(),
{
appear: true,
overlay: true,
closeOnOutsideClick: true,
side: "left",
}
);
const emits = defineEmits(["update:modelValue", "close"]);
// Internal state of the slideout
const isOpen = computed({
get() {
return props.modelValue;
},
set(value) {
emits("update:modelValue", value);
},
});
// Function used to close the slideout
const close = () => {
isOpen.value = false;
emits("close");
};
// Transition classes based on the side the slideout is appearing from
const transitionClass = computed(() => {
return {
enterFrom:
props.side === "left"
? "-translate-x-full"
: props.side === "right"
? "translate-x-full"
: props.side === "top"
? "-translate-y-full"
: "translate-y-full",
leaveTo:
props.side === "left"
? "-translate-x-full"
: props.side === "right"
? "translate-x-full"
: props.side === "top"
? "-translate-y-full"
: "translate-y-full",
};
});
// Parent wrapper classes based on the side the slideout is appearing from
const parentWrapper = computed(() => {
return `fixed inset-0 flex z-50 ${props.side === "right" && "justify-end"} ${
props.side === "left" && "justify-start"
} ${props.side === "top" && "items-start"} ${props.side === "bottom" && "items-end"}`;
});
</script>
Usage
Sides
Table of contents