388 lines
12 KiB
Vue
388 lines
12 KiB
Vue
<template>
|
|
<div
|
|
class="alert fade show"
|
|
role="alert"
|
|
:class="[
|
|
isDismissible ? 'alert-dismissible' : '',
|
|
alertClass,
|
|
cssClassNameForCmsWidget,
|
|
]">
|
|
<div
|
|
class="alert-header"
|
|
:class="{
|
|
'collapsible-header': isCollapsible
|
|
}"
|
|
@click="toggleCollapse">
|
|
<div class="alert-icon-container">
|
|
<svg
|
|
class="alert-icon"
|
|
xmlns="http://www.w3.org/2000/svg"
|
|
width="20"
|
|
height="21"
|
|
viewBox="0 0 20 21">
|
|
<path
|
|
fill-rule="nonzero"
|
|
d="M10 .5c5.523 0 10 4.477 10 10s-4.477 10-10 10-10-4.477-10-10S4.477.5 10 .5zm.043 13.6a1
|
|
1 0 1 0 0 2 1 1 0 0 0 0-2zm.77-9.2h-1.54l-.088.009a.502.502 0 0 0-.299.193.66.66 0 0
|
|
0-.125.47l.747 6.806.017.096c.065.249.263.425.495.426l.084-.008c.22-.042.396-.246.427-.51l.793-6.805.004-.102a.651.651
|
|
0 0 0-.127-.37.489.489 0 0 0-.388-.205z" />
|
|
</svg>
|
|
</div>
|
|
<template v-if="alertHeadline">
|
|
<div class="alert-headline-text-container">
|
|
<span class="m-0 alert-header-text d-block">
|
|
{{ alertHeadline }}
|
|
</span>
|
|
<slot name="headline-line-two"></slot>
|
|
</div>
|
|
</template>
|
|
<div class="alert-icon-container">
|
|
<button
|
|
v-if="isCollapsible"
|
|
class="btn-collapse"
|
|
type="button">
|
|
<svg
|
|
xmlns="http://www.w3.org/2000/svg"
|
|
width="15"
|
|
height="9"
|
|
viewBox="0 0 15 9"
|
|
class="btn-collapse-icon"
|
|
:class="{ 'rotated': !collapsed }">
|
|
<path
|
|
fill-rule="nonzero"
|
|
d="M7.5 0a.806.806 0 0 0-.593.265L.246 7.455a.957.957 0 0 0 0 1.28.796.796 0 0 0
|
|
1.185 0l6.07-6.55 6.068 6.55a.796.796 0 0 0 1.186 0 .957.957 0 0 0 0-1.28L8.093.265A.806.806
|
|
0 0 0 7.5 0" />
|
|
</svg>
|
|
</button>
|
|
<button
|
|
v-if="isDismissible"
|
|
type="button"
|
|
class="btn-dismiss"
|
|
data-bs-dismiss="alert"
|
|
aria-label="Close">
|
|
<svg
|
|
xmlns="http://www.w3.org/2000/svg"
|
|
viewBox="0 0 23.7 23.7"
|
|
class="btn-dismiss-icon"
|
|
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>
|
|
</div>
|
|
<div
|
|
v-show="alertCopy"
|
|
:id="collapseId"
|
|
class="collapse"
|
|
:class="{ 'show': !startCollapsed }">
|
|
<div class="alert-body">
|
|
<template
|
|
v-for="paragraph in splitAlertCopyForParagraphTag"
|
|
:key="paragraph">
|
|
<p
|
|
v-if="!doesCopyContainRouterLink(paragraph) && !doesCopyContainTextLink(paragraph)"
|
|
class="m-0 text-body"
|
|
v-html="paragraph"></p>
|
|
<p
|
|
v-else
|
|
class="m-0 text-body">
|
|
<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)"
|
|
:preventDefaultOnClick="preventDefaultOnClick"
|
|
@clickEvent="$emit('textLinkClicked', getRouterLinkRouteFromCopy(copy))" />
|
|
</span>
|
|
<span
|
|
v-else
|
|
v-html="copy"></span>
|
|
</template>
|
|
</p>
|
|
</template>
|
|
</div>
|
|
</div>
|
|
</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';
|
|
import { Collapse } from 'bootstrap';
|
|
|
|
export default {
|
|
name: 'alert',
|
|
components: {
|
|
textBlock,
|
|
textLink
|
|
},
|
|
props: {
|
|
isDismissible: Boolean,
|
|
isCollapsible: 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,
|
|
manualHeadlineLineTwo: {
|
|
type: String,
|
|
default: ''
|
|
},
|
|
manualCopy: String,
|
|
shouldScrollToOnMount: {
|
|
type: Boolean,
|
|
default: true
|
|
},
|
|
startCollapsed: {
|
|
type: Boolean,
|
|
default: false
|
|
},
|
|
preventDefaultOnClick: { type: Boolean, required: false, default: false }
|
|
},
|
|
emits: ['textLinkClicked'],
|
|
data() {
|
|
return {
|
|
collapsed: this.startCollapsed || false,
|
|
collapseElement: null
|
|
};
|
|
},
|
|
computed: {
|
|
pageQueryString() {
|
|
return applicationConfig.PAGE_QUERYSTRING;
|
|
},
|
|
alertHeadline() {
|
|
return this.manualHeadline
|
|
? this.manualHeadline
|
|
: this.getCmsContent(this.cmsWidgetName, 'HeadlineText');
|
|
},
|
|
alertHeadlineLineTwo() {
|
|
return this.manualHeadlineLineTwo
|
|
? this.manualHeadlineLineTwo
|
|
: '';
|
|
},
|
|
alertCopy() {
|
|
return this.manualCopy
|
|
? this.manualCopy
|
|
: this.getCmsContent(this.cmsWidgetName, 'BodyText');
|
|
},
|
|
hasManualHeadlineLineTwo() {
|
|
return this.manualHeadlineLineTwo !== '';
|
|
},
|
|
splitAlertCopyForParagraphTag() {
|
|
return splitCMSCopyOnParagraphTag(this.alertCopy);
|
|
},
|
|
collapseId() {
|
|
return this.isCollapsible
|
|
? `${this.cmsWidgetName}-collapse`
|
|
: null;
|
|
}
|
|
},
|
|
mounted() {
|
|
this.ensureAlertIsInViewPort();
|
|
if (this.isCollapsible) {
|
|
this.collapseElement = Collapse.getOrCreateInstance(document.getElementById(this.collapseId), { toggle: false });
|
|
}
|
|
},
|
|
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)
|
|
);
|
|
},
|
|
toggleCollapse() {
|
|
if (!this.isCollapsible) {
|
|
return;
|
|
}
|
|
this.collapsed = !this.collapsed;
|
|
if (this.collapsed) {
|
|
this.collapseElement.hide();
|
|
} else {
|
|
this.collapseElement.show();
|
|
}
|
|
}
|
|
}
|
|
};
|
|
</script>
|
|
|
|
<style lang="scss" scoped>
|
|
.alert {
|
|
border: 1px solid;
|
|
padding: 0rem;
|
|
margin-bottom: 2rem;
|
|
.alert-headline-text-container {
|
|
flex-grow: 1;
|
|
padding: 0rem 0.625rem;
|
|
|
|
.alert-header-text {
|
|
padding: 0;
|
|
}
|
|
}
|
|
.alert-header {
|
|
display: flex;
|
|
align-items: flex-start;
|
|
justify-content: center;
|
|
flex-direction: row;
|
|
padding: .75rem 1rem .75rem 1rem;
|
|
font-weight: 600;
|
|
color: $black;
|
|
&.collapsible-header {
|
|
cursor: pointer;
|
|
}
|
|
}
|
|
.alert-icon-container {
|
|
display: flex;
|
|
align-items: center;
|
|
height: 1.625rem;
|
|
}
|
|
.alert-icon {
|
|
min-width: 1rem;
|
|
max-height: 1rem;
|
|
}
|
|
.alert-header-text {
|
|
flex-grow: 1;
|
|
padding: 0rem .625rem;
|
|
}
|
|
.btn-collapse {
|
|
display: flex;
|
|
border: none;
|
|
background: none;
|
|
min-width: 1rem;
|
|
max-height: 1rem;
|
|
padding: 0;
|
|
}
|
|
.btn-collapse-icon {
|
|
height: 100%;
|
|
width: 100%;
|
|
transition: transform 0.3s;
|
|
transform: rotate(180deg);
|
|
&.rotated {
|
|
transform: none;
|
|
}
|
|
}
|
|
.btn-dismiss {
|
|
display: flex;
|
|
border: none;
|
|
background: none;
|
|
min-width: 1rem;
|
|
max-height: 1rem;
|
|
padding: 0;
|
|
}
|
|
.btn-dismiss-icon {
|
|
height: 100%;
|
|
width: 100%;
|
|
}
|
|
a {
|
|
font-weight: $font-weight-normal;
|
|
}
|
|
border-color: transparent;
|
|
.alert-body {
|
|
margin: 0 1rem;
|
|
padding: .5rem 1rem .75rem 1rem;
|
|
border-top: 1px solid;
|
|
color: $darker-gray;
|
|
line-height: 1.625rem;
|
|
font-weight: 400;
|
|
}
|
|
&.alert-info {
|
|
background-color: $blue-100;
|
|
border-color: $blue-200;
|
|
.alert-body {
|
|
border-top-color: $blue-200;
|
|
}
|
|
svg {
|
|
fill: $blue-700;
|
|
width: 1rem;
|
|
height: 1rem;
|
|
}
|
|
}
|
|
&.alert-danger {
|
|
background-color: $alert-red-bg;
|
|
border-color: $alert-red-color;
|
|
.alert-body {
|
|
border-top-color: $alert-red-color;
|
|
}
|
|
svg {
|
|
fill: $alert-red-color;
|
|
width: 1rem;
|
|
height: 1rem;
|
|
}
|
|
}
|
|
&.alert-warning {
|
|
background-color: $alert-yellow-bg;
|
|
border-color: $alert-yellow-color;
|
|
.alert-body {
|
|
border-top-color: $alert-yellow-color;
|
|
}
|
|
svg {
|
|
fill: $alert-yellow-color;
|
|
}
|
|
}
|
|
&.alert-success {
|
|
background-color: $green-100;
|
|
border-color: $green-200;
|
|
.alert-body {
|
|
border-top-color: $green-200;
|
|
}
|
|
svg {
|
|
fill: $green-700;
|
|
width: 1rem;
|
|
height: 1rem;
|
|
}
|
|
}
|
|
}
|
|
</style>
|