166 lines
3.6 KiB
Vue
166 lines
3.6 KiB
Vue
<script setup lang="ts">
|
|
import { computed } from 'vue'
|
|
import DOMPurify from 'dompurify'
|
|
|
|
const props = defineProps<{
|
|
content: string
|
|
rich?: boolean
|
|
}>()
|
|
|
|
const safeHtml = computed(() => DOMPurify.sanitize(props.content, {
|
|
ALLOWED_TAGS: [
|
|
'p', 'br', 'strong', 'b', 'em', 'i', 'u', 's',
|
|
'h2', 'h3', 'h4', 'ul', 'ol', 'li', 'blockquote', 'a',
|
|
'figure', 'figcaption', 'img',
|
|
'table', 'thead', 'tbody', 'tfoot', 'tr', 'th', 'td', 'colgroup', 'col',
|
|
],
|
|
ALLOWED_ATTR: [
|
|
'href', 'target', 'rel',
|
|
'src', 'alt', 'title', 'width', 'height', 'class', 'style',
|
|
'colspan', 'rowspan', 'scope',
|
|
],
|
|
ALLOW_DATA_ATTR: false,
|
|
}))
|
|
</script>
|
|
|
|
<template>
|
|
<div v-if="rich" class="rich-message-content" v-html="safeHtml" />
|
|
<p v-else class="plain-message-content">{{ content }}</p>
|
|
</template>
|
|
|
|
<style scoped>
|
|
.plain-message-content {
|
|
margin: 0;
|
|
white-space: pre-wrap;
|
|
}
|
|
|
|
.rich-message-content {
|
|
overflow-wrap: anywhere;
|
|
color: inherit;
|
|
font-size: inherit;
|
|
line-height: 1.75;
|
|
}
|
|
|
|
.rich-message-content::after {
|
|
display: block;
|
|
clear: both;
|
|
content: '';
|
|
}
|
|
|
|
.rich-message-content :deep(p),
|
|
.rich-message-content :deep(ul),
|
|
.rich-message-content :deep(ol),
|
|
.rich-message-content :deep(blockquote) {
|
|
margin: 0 0 .65em;
|
|
}
|
|
|
|
.rich-message-content :deep(p:last-child),
|
|
.rich-message-content :deep(ul:last-child),
|
|
.rich-message-content :deep(ol:last-child),
|
|
.rich-message-content :deep(blockquote:last-child) {
|
|
margin-bottom: 0;
|
|
}
|
|
|
|
.rich-message-content :deep(h2),
|
|
.rich-message-content :deep(h3),
|
|
.rich-message-content :deep(h4) {
|
|
margin: .8em 0 .35em;
|
|
color: var(--ink);
|
|
font-family: inherit;
|
|
}
|
|
|
|
.rich-message-content :deep(h2) { font-size: 1.25em; }
|
|
.rich-message-content :deep(h3) { font-size: 1.12em; }
|
|
.rich-message-content :deep(h4) { font-size: 1em; }
|
|
|
|
.rich-message-content :deep(ul),
|
|
.rich-message-content :deep(ol) {
|
|
padding-left: 1.5em;
|
|
}
|
|
|
|
.rich-message-content :deep(blockquote) {
|
|
padding: .55em .9em;
|
|
border-left: 3px solid var(--teal);
|
|
background: #f3f8f7;
|
|
}
|
|
|
|
.rich-message-content :deep(a) {
|
|
color: var(--indigo);
|
|
text-decoration: underline;
|
|
text-underline-offset: 2px;
|
|
}
|
|
|
|
.rich-message-content :deep(figure) {
|
|
margin: .9em auto;
|
|
}
|
|
|
|
.rich-message-content :deep(figure.image) {
|
|
max-width: 100%;
|
|
}
|
|
|
|
.rich-message-content :deep(img) {
|
|
display: block;
|
|
max-width: 100%;
|
|
height: auto;
|
|
margin: 0 auto;
|
|
border-radius: 6px;
|
|
}
|
|
|
|
.rich-message-content :deep(figcaption) {
|
|
margin-top: .4em;
|
|
color: var(--muted);
|
|
font-size: .86em;
|
|
line-height: 1.5;
|
|
text-align: center;
|
|
}
|
|
|
|
.rich-message-content :deep(.image-style-align-left),
|
|
.rich-message-content :deep(.image-style-block-align-left) {
|
|
margin-right: 1.25em;
|
|
margin-left: 0;
|
|
}
|
|
|
|
.rich-message-content :deep(.image-style-align-right),
|
|
.rich-message-content :deep(.image-style-block-align-right),
|
|
.rich-message-content :deep(.image-style-side) {
|
|
margin-right: 0;
|
|
margin-left: auto;
|
|
}
|
|
|
|
.rich-message-content :deep(.image-style-wrap-text.image-style-align-left) {
|
|
float: left;
|
|
}
|
|
|
|
.rich-message-content :deep(.image-style-wrap-text.image-style-align-right),
|
|
.rich-message-content :deep(.image-style-side) {
|
|
float: right;
|
|
margin-left: 1.25em;
|
|
}
|
|
|
|
.rich-message-content :deep(figure.table) {
|
|
max-width: 100%;
|
|
overflow-x: auto;
|
|
}
|
|
|
|
.rich-message-content :deep(table) {
|
|
width: 100%;
|
|
border-collapse: collapse;
|
|
background: #fff;
|
|
}
|
|
|
|
.rich-message-content :deep(th),
|
|
.rich-message-content :deep(td) {
|
|
min-width: 4.5em;
|
|
padding: .55em .7em;
|
|
border: 1px solid var(--line);
|
|
vertical-align: top;
|
|
}
|
|
|
|
.rich-message-content :deep(th) {
|
|
background: #f3f5f8;
|
|
color: var(--ink);
|
|
font-weight: 650;
|
|
text-align: left;
|
|
}
|
|
</style>
|