DigitalConsumer.ISS/src/ux-components/alert/alert.vue
2024-07-15 10:49:58 -04:00

231 lines
6.8 KiB
Vue

<template>
<div
class="alert fade show text-center mb-0 py-2 px-4"
role="alert"
:class="[
isDismissible ? 'alert-dismissible' : '',
alertClass,
cssClassNameForCmsWidget,
]">
<p class="m-0 fw-bold alert-heading">
{{ alertHeadline }}
</p>
<template
v-for="paragraph in splitAlertCopyForParagraphTag"
:key="paragraph">
<p
v-if="!doesCopyContainRouterLink(paragraph) && !doesCopyContainTextLink(paragraph)"
class="m-0 text-body small"
v-html="paragraph"></p>
<p
v-else
class="m-0 text-body small">
<template v-if="doesCopyContainRouterLink(paragraph)">
<textBlock
:customText="paragraph"
class="mb-1"
marginTopSizeOverride="0" />
</template>
<template
v-for="copy in splitCopyOnCMSPlaceHolder(paragraph)"
v-else
:key="copy">
<span v-if="doesCopyContainTextLink(copy)">
<textLink
linkType="text"
:text="getRouterLinkDisplayTextFromCopy(copy)"
href="#!"
:data-bs-target="'#' + getRouterLinkRouteFromCopy(copy)"
@click-event="
$emit('textLinkClicked', getRouterLinkRouteFromCopy(copy))
" />
</span>
<span
v-else
v-html="copy"></span>
</template>
</p>
</template>
<button
type="button"
class="btn-close p-2"
data-bs-dismiss="alert"
aria-label="Close">
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 23.7 23.7"
xml:space="preserve">
<path
d="m23.24 2.7-9.15 9.15L23.24 21a1.581 1.581 0 0 1-1.12 2.7c-.42 0-.82-.16-1.12-.46l-9.15-9.15-9.15 9.15c-.3.3-.7.46-1.12.46A1.581 1.581 0 0 1 .46 21l8.47-8.47.68-.68L.46 2.7c-.62-.62-.62-1.62 0-2.24.62-.62 1.62-.62 2.24 0l8.47 8.47.68.68L21 .46a1.57 1.57 0 0 1 2.23 0c.63.62.63 1.62.01 2.24z" />
</svg>
</button>
</div>
</template>
<script>
import {
doesCopyContainRouterLink,
doesCopyContainTextLink,
splitCopyOnCMSPlaceHolder,
getRouterLinkRouteFromCopy,
getRouterLinkDisplayTextFromCopy,
splitCMSCopyOnParagraphTag
} from '@/helpers/cms-content-helper';
import applicationConfig from '@/constants/application-config';
import textBlock from '@/digital-components/text-block/text-block.vue';
import textLink from '@/ux-components/text-link/text-link.vue';
export default {
// eslint-disable-next-line vue/multi-word-component-names
name: 'alert',
components: {
textBlock,
textLink
},
props: {
isDismissible: Boolean,
/*
alertClass class names:
alert-success (green)
alert-danger (red)
alert-warning (yellow)
alert-info (blue)
*/
alertClass: String,
cmsWidgetName: {
type: String,
default(rawProps) {
if (!rawProps.cmsWidgetName) {
window.console.log('Error: Missing a CMS Widget Name (required field)');
}
return 'widgetUndefined';
}
},
manualHeadline: String,
manualCopy: String,
shouldScrollToOnMount: {
type: Boolean,
default: true
}
},
computed: {
pageQueryString() {
return applicationConfig.PAGE_QUERYSTRING;
},
alertHeadline() {
return this.manualHeadline
? this.manualHeadline
: this.getCmsContent(this.cmsWidgetName, 'HeadlineText');
},
alertCopy() {
return this.manualCopy
? this.manualCopy
: this.getCmsContent(this.cmsWidgetName, 'BodyText');
},
splitAlertCopyForParagraphTag() {
return splitCMSCopyOnParagraphTag(this.alertCopy);
}
},
mounted() {
this.ensureAlertIsInViewPort();
},
methods: {
doesCopyContainRouterLink,
doesCopyContainTextLink,
splitCopyOnCMSPlaceHolder,
getRouterLinkRouteFromCopy,
getRouterLinkDisplayTextFromCopy,
ensureAlertIsInViewPort() {
if (this.shouldScrollToOnMount && this.$el.style?.display !== 'none') {
const footerHeight = this.getFooterInfoBoxHeight();
if (!this.isAlertInViewport(footerHeight)) {
this.$el.scrollIntoView(true); // 'true' attempts to scroll element to top of viewport
}
}
},
isAlertInViewport(footerHeight) {
const rect = this.$el.getBoundingClientRect();
return (
rect.top >= 0
// remove footerHeight from window height to avoid items being hidden behind footer
&& rect.bottom
<= (window.innerHeight - footerHeight
|| document.documentElement.clientHeight - footerHeight)
);
}
}
};
</script>
<style lang="scss" scoped>
.alert {
a {
text-decoration: none;
}
border-color: transparent;
button {
display: none;
}
.btn-close {
background: none;
opacity: 1;
width: 0.75rem;
height: 0.75rem;
}
&.alert-dismissible {
button {
display: flex;
top: 2px;
right: 2px;
}
}
&.alert-info {
background-color: $blue-100;
.alert-heading {
color: $blue-700;
}
svg {
fill: $blue-700;
width: 1rem;
height: 1rem;
}
}
&.alert-danger {
background-color: $red-100;
.alert-heading {
color: $red-600;
}
svg {
fill: $red-600;
width: 1rem;
height: 1rem;
}
}
&.alert-warning {
background-color: $yellow-100;
.alert-heading {
color: $yellow-600;
}
svg {
fill: $yellow-600;
width: 1rem;
height: 1rem;
}
}
&.alert-success {
background-color: $green-100;
.alert-heading {
color: $green-700;
}
svg {
fill: $green-700;
width: 1rem;
height: 1rem;
}
}
& p {
font-size: 0.875rem;
margin-bottom: 0.25rem !important;
}
}
</style>