- Remove modalPosition related code now that all modals are centered - Allow parent components to set text of the close link - Updates styling for specific modals so that they match their Heritage counterparts
313 lines
8.6 KiB
Vue
313 lines
8.6 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">
|
|
<div
|
|
class="modal-content"
|
|
role="dialog"
|
|
aria-modal="true">
|
|
<div class="modal-header">
|
|
<span
|
|
v-if="headerText"
|
|
class="modal-title d-flex pb-0 w-100"
|
|
:innerHTML="headerText">
|
|
</span>
|
|
<button
|
|
v-if="displayCloseButton"
|
|
ref="closeButton"
|
|
type="button"
|
|
class="btn-close"
|
|
aria-label="Close"
|
|
@mousedown="closeModal"
|
|
@keypress.space="closeModal"></button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<slot></slot>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<buttonMain
|
|
v-if="displayFooterButton"
|
|
id="modalbtn"
|
|
ref="modalButtonMain"
|
|
class="w-100"
|
|
:buttonText="footerButtonText"
|
|
pushToGA
|
|
:variant="buttonVariant"
|
|
:class="[
|
|
(isButtonDisabled || isFooterButtonDisabled) &&
|
|
'form-test-invalid'
|
|
]"
|
|
@clickEvent="validateAndEmit" />
|
|
<textLink
|
|
v-if="displayCloseLink"
|
|
class="mt-4 mb-0"
|
|
linkType="text"
|
|
:text="closeLinkText"
|
|
href="#!"
|
|
@click="closeModal" />
|
|
</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';
|
|
import textLink from '@/ux-components/text-link/text-link.vue';
|
|
|
|
export default {
|
|
name: 'modal',
|
|
components: {
|
|
ButtonMain,
|
|
textLink
|
|
},
|
|
props: {
|
|
modalId: String,
|
|
headerText: String,
|
|
footerButtonText: String,
|
|
onModalOpenedCallback: {
|
|
type: Function
|
|
},
|
|
onModalClosedCallback: {
|
|
type: Function
|
|
},
|
|
isButtonDisabled: Boolean,
|
|
buttonVariant: {
|
|
type: String,
|
|
default: 'success'
|
|
},
|
|
allowInvalidSubmit: {
|
|
type: Boolean,
|
|
default: false
|
|
},
|
|
displayFooterButton: {
|
|
type: Boolean,
|
|
default: true
|
|
},
|
|
displayCloseButton: {
|
|
type: Boolean,
|
|
default: true
|
|
},
|
|
displayCloseLink: {
|
|
type: Boolean,
|
|
default: false
|
|
},
|
|
closeLinkText: {
|
|
type: String,
|
|
default: 'Close window'
|
|
}
|
|
},
|
|
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 {
|
|
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.allowInvalidSubmit) {
|
|
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 {
|
|
position: fixed;
|
|
top: 0;
|
|
right: 0;
|
|
bottom: 0;
|
|
left: 0;
|
|
padding-right: 1rem;
|
|
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: 400;
|
|
font-size: 1.25rem;
|
|
line-height: 2rem;
|
|
color: $black;
|
|
padding-left: 1.5rem;
|
|
}
|
|
}
|
|
.modal-footer {
|
|
position: sticky;
|
|
width: 100%;
|
|
bottom: 0;
|
|
z-index: 5;
|
|
border-radius: 0;
|
|
justify-content: center;
|
|
|
|
> .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;
|
|
width: 37.5rem;
|
|
max-width: unset;
|
|
min-height: auto;
|
|
.modal-content {
|
|
box-shadow: 0px 1rem 3rem -1rem rgba(0, 0, 0, 0.25);
|
|
border: 1px solid;
|
|
border-radius: 5px;
|
|
overflow: auto;
|
|
}
|
|
.modal-content {
|
|
margin: 0 auto;
|
|
.modal-body {
|
|
padding: 0 1.5rem 0 1.5rem;
|
|
.col {
|
|
max-width: 100%;
|
|
}
|
|
.modal-sub-body {
|
|
color: $gray-600;
|
|
}
|
|
ul {
|
|
margin-bottom: 0;
|
|
padding-left: 20px;
|
|
}
|
|
li {
|
|
margin-bottom: 0.5rem;
|
|
line-height: 22px;
|
|
}
|
|
p {
|
|
&:last-child {
|
|
margin-bottom: 0;
|
|
}
|
|
}
|
|
@include media-breakpoint-up(md) {
|
|
overflow: auto;
|
|
flex: none;
|
|
}
|
|
h5 {
|
|
text-align: left;
|
|
line-height: 2rem;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
.modal-footer {
|
|
border-top: none;
|
|
padding: .625rem 1.5rem 1.5rem 1.5rem;
|
|
|
|
button {
|
|
margin: 0;
|
|
background-color: $white;
|
|
color: $blue;
|
|
border-color: $blue;
|
|
font-weight: 400;
|
|
&:hover {
|
|
background-color: #167cac;
|
|
color: $white;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
@include media-breakpoint-up(md) {
|
|
&.fade {
|
|
transition: opacity 0.25s linear;
|
|
}
|
|
}
|
|
@include media-breakpoint-down(md) {
|
|
&.modal-component {
|
|
.modal-dialog {
|
|
width: auto;
|
|
max-width: unset;
|
|
margin: .625rem;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
body {
|
|
.modal-backdrop {
|
|
height: 100%;
|
|
&.show {
|
|
opacity: 0.4;
|
|
}
|
|
}
|
|
}
|
|
</style>
|