294 lines
8.3 KiB
Vue
294 lines
8.3 KiB
Vue
<template>
|
|
<!-- Modal -->
|
|
<div
|
|
:id="modalId"
|
|
class="modal fade modal-component"
|
|
tabindex="-1"
|
|
aria-labelledby="ModalComponentLabel"
|
|
aria-hidden="true"
|
|
@keydown.enter="onKeyDown"
|
|
v-on="{
|
|
'hidden.bs.modal': onModalClosed,
|
|
'shown.bs.modal': onModalOpened
|
|
}">
|
|
<div class="modal-dialog"
|
|
:class="{
|
|
'modal-dialog-edge': modalPosition === 'edge',
|
|
'modal-dialog-centered': modalPosition === 'center'
|
|
}">
|
|
<div
|
|
class="modal-content"
|
|
role="dialog"
|
|
aria-modal="true">
|
|
<div class="modal-header mb-2 mt-2">
|
|
<span
|
|
v-if="headerText"
|
|
class="modal-title d-flex justify-content-center pb-0 w-100"
|
|
:innerHTML="headerText">
|
|
</span>
|
|
<button
|
|
ref="closeButton"
|
|
type="button"
|
|
class="btn-close"
|
|
aria-label="Close"
|
|
@mousedown="closeModal"
|
|
@keypress.space="closeModal"></button>
|
|
</div>
|
|
<div class="modal-body ps-5 pe-5 pb-5 pt-1">
|
|
<slot></slot>
|
|
</div>
|
|
<div class="modal-footer px-5 py-4">
|
|
<buttonMain
|
|
id="modalbtn"
|
|
ref="modalButtonMain"
|
|
:variant="buttonVariant"
|
|
class="w-100"
|
|
:buttonText="footerButtonText"
|
|
pushToGA
|
|
:class="[
|
|
(isButtonDisabled || isFooterButtonDisabled) &&
|
|
'form-test-invalid'
|
|
]"
|
|
@clickEvent="validateAndEmit" />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script>
|
|
import ButtonMain from '@/ux-components/button-main/button-main.vue';
|
|
import { Modal } from 'bootstrap';
|
|
import { useForm } from 'vee-validate';
|
|
|
|
export default {
|
|
// eslint-disable-next-line vue/multi-word-component-names
|
|
name: 'modal',
|
|
components: {
|
|
ButtonMain
|
|
},
|
|
props: {
|
|
modalId: String,
|
|
headerText: String,
|
|
footerButtonText: String,
|
|
onModalOpenedCallback: {
|
|
type: Function
|
|
},
|
|
onModalClosedCallback: {
|
|
type: Function
|
|
},
|
|
isButtonDisabled: Boolean,
|
|
buttonVariant: {
|
|
type: String,
|
|
default: 'success'
|
|
},
|
|
modalPosition: {
|
|
type: String,
|
|
default: 'edge'
|
|
}
|
|
},
|
|
emits: ['footer-button-event', 'isModalOpened'],
|
|
setup(props) {
|
|
const modalId = props.modalId
|
|
? props.modalId
|
|
: `modal-${crypto.randomUUID()}`;
|
|
|
|
const { meta, validate, resetForm } = useForm();
|
|
|
|
// TODO: Correct Duplicate key 'modalId' issue.
|
|
// Probably just rename the prop. -br
|
|
return {
|
|
// eslint-disable-next-line vue/no-dupe-keys
|
|
modalId,
|
|
meta,
|
|
validate,
|
|
resetForm
|
|
};
|
|
},
|
|
computed: {
|
|
isFooterButtonDisabled() {
|
|
if (!this.meta.touched) {
|
|
return !this.meta.valid;
|
|
}
|
|
return !this.meta.dirty || !this.meta.valid;
|
|
}
|
|
},
|
|
watch: {
|
|
$route() {
|
|
this.closeModal();
|
|
}
|
|
},
|
|
methods: {
|
|
async validateAndEmit() {
|
|
const validationResult = await this.validate();
|
|
if (validationResult.valid) {
|
|
this.$emit('footer-button-event');
|
|
}
|
|
},
|
|
async onKeyDown(e) {
|
|
e.preventDefault();
|
|
document.getElementById('modalbtn')?.focus();
|
|
this.$refs.modalButtonMain.clicked();
|
|
document.getElementById(this.modalId)?.focus();
|
|
},
|
|
onModalOpened() {
|
|
this.onModalOpenedCallback?.();
|
|
},
|
|
onModalClosed() {
|
|
this.onModalClosedCallback?.();
|
|
},
|
|
openModal() {
|
|
const modal = Modal.getOrCreateInstance(document.getElementById(this.modalId));
|
|
modal?.show();
|
|
this.$emit('isModalOpened', true);
|
|
},
|
|
closeModal() {
|
|
const modal = Modal.getInstance(document.getElementById(this.modalId));
|
|
modal?.hide();
|
|
this.$emit('isModalOpened', false);
|
|
}
|
|
}
|
|
};
|
|
</script>
|
|
|
|
<style lang="scss">
|
|
@import "@/styles/ux-variables-svg-strings.scss";
|
|
|
|
.modal {
|
|
top: auto;
|
|
bottom: 0;
|
|
h5,
|
|
strong,
|
|
.subheader-text {
|
|
color: $black;
|
|
}
|
|
.modal-header {
|
|
padding: 1.5rem 0 0.5rem 0;
|
|
border-bottom: none;
|
|
.btn-close {
|
|
position: absolute;
|
|
right: 1rem;
|
|
top: 1rem;
|
|
background-image: url($svg-modal-header-button-close);
|
|
opacity: 1;
|
|
}
|
|
|
|
.modal-title {
|
|
font-weight: 500;
|
|
font-size: 1.25rem;
|
|
line-height: 2rem;
|
|
color: $black;
|
|
}
|
|
}
|
|
.modal-footer {
|
|
position: sticky;
|
|
width: 100%;
|
|
bottom: 0;
|
|
z-index: 5;
|
|
border-radius: 0;
|
|
background-color: $gray-100;
|
|
|
|
> .navigation-link {
|
|
color: $black;
|
|
font-weight: $font-weight-bold;
|
|
line-height: 1.625rem;
|
|
white-space: nowrap;
|
|
align-items: center;
|
|
text-decoration: underline;
|
|
text-underline-offset: .3125rem;
|
|
}
|
|
}
|
|
&.modal-component {
|
|
.modal-dialog {
|
|
display: flex;
|
|
max-width: 47.9375rem;
|
|
.modal-content {
|
|
margin: 1.5rem auto 0 auto;
|
|
.modal-body {
|
|
.col {
|
|
max-width: 100%;
|
|
}
|
|
.modal-sub-body {
|
|
color: $gray-600;
|
|
}
|
|
ul {
|
|
margin-bottom: 0;
|
|
}
|
|
p {
|
|
&:last-child {
|
|
margin-bottom: 0;
|
|
}
|
|
}
|
|
@include media-breakpoint-up(md) {
|
|
overflow: auto;
|
|
flex: none;
|
|
}
|
|
h5 {
|
|
text-align: left;
|
|
line-height: 2rem;
|
|
}
|
|
}
|
|
}
|
|
&.modal-dialog-edge {
|
|
min-height: 100%;
|
|
margin: 0 auto;
|
|
align-items: flex-end;
|
|
@include media-breakpoint-up(md) {
|
|
align-items: inherit;
|
|
width: 23.5rem;
|
|
position: absolute;
|
|
top: 0;
|
|
right: 0;
|
|
transform: translate(100%);
|
|
}
|
|
.modal-content {
|
|
box-shadow: 0px 1rem 3rem -1rem rgba(0, 0, 0, 0.25);
|
|
border-radius: 1.5rem 1.5rem 0 0;
|
|
@include media-breakpoint-up(md) {
|
|
box-shadow: none;
|
|
border-radius: 0;
|
|
margin: 0 auto;
|
|
}
|
|
}
|
|
}
|
|
&.modal-dialog-centered {
|
|
.modal-content {
|
|
box-shadow: 0px 1rem 3rem -1rem rgba(0, 0, 0, 0.25);
|
|
border-radius: 1.5rem;
|
|
overflow: auto;
|
|
}
|
|
.modal-header {
|
|
justify-content: center;
|
|
}
|
|
}
|
|
}
|
|
&.show .modal-dialog {
|
|
&.modal-dialog-edge {
|
|
@include media-breakpoint-up(md) {
|
|
transform: translateY(0);
|
|
}
|
|
}
|
|
}
|
|
.modal-footer {
|
|
border-top: none;
|
|
|
|
button {
|
|
margin: 0;
|
|
}
|
|
}
|
|
}
|
|
@include media-breakpoint-up(md) {
|
|
&.fade {
|
|
transition: opacity 0.25s linear;
|
|
}
|
|
}
|
|
}
|
|
body {
|
|
.modal-backdrop {
|
|
height: 100%;
|
|
&.show {
|
|
opacity: 0.4;
|
|
}
|
|
}
|
|
}
|
|
</style>
|