Vue Modal and Offcanvas Component
My Modal and Offcanvas components for Vue using Boostrap 5.3.
ParentView.vue:
<script setup lang="ts">
import { ref } from 'vue'
import Modal from './Modal.vue'
import Offcanvas from './Offcanvas.vue'
const showModal = ref(false);
const showOffcanvas = ref(false);
</script>
<template>
<button class="btn btn-primary mb-4" @click="showModal = true">Open Modal</button> <br/>
<button class="btn btn-primary mb-4" @click="showOffcanvas = true">Open Offcanvas</button> <br/>
<pre>
showModal: {{ showModal }}
showOffcanvas: {{ showOffcanvas }}
</pre>
<Modal v-model="showModal" title="My Fancy Modal">
My showModal Content
<template #footer>
<button class="btn btn-primary" @click="showModal = false">Close</button>
</template>
</Modal>
<Offcanvas v-model="showOffcanvas">
My Offcanvas Content
<button class="btn btn-primary" @click="showOffcanvas = false">Close</button>
</Offcanvas>
</template>
Modal.vue:
<script setup lang="ts">
import { ref, onMounted, onBeforeUnmount, watch } from 'vue'
import { Modal } from 'bootstrap';
const props = defineProps({
title: String,
});
const modelValue = defineModel();
const emit = defineEmits(['close', 'closed']);
let BSObject = null;
const el = ref();
function close() {
modelValue.value = false;
emit('close');
}
function closed() {
modelValue.value = false;
emit('closed');
}
onMounted(() => {
BSObject = new Modal(el.value);
el.value.addEventListener('hide.bs.modal', close)
el.value.addEventListener('hidden.bs.modal', closed)
if (modelValue.value===true) {
BSObject.show();
}
} )
onBeforeUnmount(() => {
if (BSObject) {
BSObject.dispose();
}
el.value.removeEventListener('hide.bs.modal', close)
el.value.removeEventListener('hidden.bs.modal', closed)
})
watch(
modelValue,
(val) => {
if (!BSObject) return;
val ? BSObject.show() : BSObject.hide()
},
{ immediate:true }
)
</script>
<template>
<div ref="el" class="modal fade" tabindex="-1">
<div class="modal-dialog modal-dialog-centered modal-lg">
<div class="modal-content">
<div class="modal-header">
<h5 v-if="props.title" class="modal-title">{{ props.title }}</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<slot />
</div>
<div v-if="$slots.footer" class="modal-footer">
<slot name="footer" />
</div>
</div>
</div>
</div>
</template>
Offcanvas.vue:
<script setup lang="ts">
import { ref, onMounted, onBeforeUnmount, watch } from 'vue'
import { Offcanvas } from 'bootstrap';
const props = defineProps({
title: String,
});
const modelValue = defineModel();
const emit = defineEmits(['close', 'closed']);
let BSObject = null;
const el = ref();
function close() {
modelValue.value = false;
emit('close');
}
function closed() {
modelValue.value = false;
emit('closed');
}
onMounted(() => {
BSObject = new Offcanvas(el.value);
el.value.addEventListener('hide.bs.offcanvas', close)
el.value.addEventListener('hidden.bs.offcanvas', closed)
if (modelValue.value===true) {
BSObject.show();
}
} )
onBeforeUnmount(() => {
if (BSObject) {
BSObject.dispose();
}
el.value.removeEventListener('hide.bs.offcanvas', close)
el.value.removeEventListener('hidden.bs.offcanvas', closed)
})
watch(
modelValue,
(val) => {
if (!BSObject) return;
val ? BSObject.show() : BSObject.hide()
},
{ immediate:true }
)
</script>
<template>
<div ref="el" class="offcanvas offcanvas-end" tabindex="-1" id="offcanvasExample" aria-labelledby="offcanvasExampleLabel">
<div class="offcanvas-header">
<h5 v-if="props.title" class="offcanvas-title" id="offcanvasExampleLabel">{{ props.title }}</h5>
<button type="button" class="btn-close text-reset" data-bs-dismiss="offcanvas" aria-label="Close"></button>
</div>
<div class="offcanvas-body">
<slot />
</div>
</div>
</template>