245 lines
6.3 KiB
Vue
245 lines
6.3 KiB
Vue
<template>
|
|
<iframe
|
|
ref="iframeRef"
|
|
:srcdoc="htmlContent"
|
|
class="prose-f block h-10 max-h-[500px] w-full"
|
|
/>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { ref, watch } from 'vue'
|
|
|
|
const props = defineProps({
|
|
content: {
|
|
type: String,
|
|
required: true,
|
|
},
|
|
})
|
|
|
|
const files = import.meta.globEager('/src/index.css', { query: '?inline' })
|
|
const css = files['/src/index.css'].default
|
|
|
|
const iframeRef = ref(null)
|
|
const _content = ref(props.content)
|
|
|
|
const parser = new DOMParser()
|
|
const doc = parser.parseFromString(_content.value, 'text/html')
|
|
|
|
const gmailReplyToContent = doc.querySelectorAll('div.gmail_quote')
|
|
const outlookReplyToContent = doc.querySelectorAll('div#appendonsend')
|
|
const replyToContent = doc.querySelectorAll('p.reply-to-content')
|
|
|
|
if (gmailReplyToContent.length) {
|
|
_content.value = parseReplyToContent(doc, 'div.gmail_quote', true)
|
|
} else if (outlookReplyToContent.length) {
|
|
_content.value = parseReplyToContent(doc, 'div#appendonsend')
|
|
} else if (replyToContent.length) {
|
|
_content.value = parseReplyToContent(doc, 'p.reply-to-content')
|
|
}
|
|
|
|
function parseReplyToContent(doc, selector, forGmail = false) {
|
|
function handleAllInstances(doc) {
|
|
const replyToContentElements = doc.querySelectorAll(selector)
|
|
if (replyToContentElements.length === 0) return
|
|
const replyToContentElement = replyToContentElements[0]
|
|
replaceReplyToContent(replyToContentElement, forGmail)
|
|
handleAllInstances(doc)
|
|
}
|
|
|
|
handleAllInstances(doc)
|
|
|
|
return doc.body.innerHTML
|
|
}
|
|
|
|
function replaceReplyToContent(replyToContentElement, forGmail) {
|
|
if (!replyToContentElement) return
|
|
let randomId = Math.random().toString(36).substring(2, 7)
|
|
const wrapper = doc.createElement('div')
|
|
wrapper.classList.add('replied-content')
|
|
|
|
const collapseLabel = doc.createElement('label')
|
|
collapseLabel.classList.add('collapse')
|
|
collapseLabel.setAttribute('for', randomId)
|
|
collapseLabel.innerHTML = '...'
|
|
wrapper.appendChild(collapseLabel)
|
|
|
|
const collapseInput = doc.createElement('input')
|
|
collapseInput.setAttribute('id', randomId)
|
|
collapseInput.setAttribute('class', 'replyCollapser')
|
|
collapseInput.setAttribute('type', 'checkbox')
|
|
wrapper.appendChild(collapseInput)
|
|
|
|
if (forGmail) {
|
|
const prevSibling = replyToContentElement.previousElementSibling
|
|
if (prevSibling && prevSibling.tagName === 'BR') {
|
|
prevSibling.remove()
|
|
}
|
|
let cloned = replyToContentElement.cloneNode(true)
|
|
cloned.classList.remove('gmail_quote')
|
|
wrapper.appendChild(cloned)
|
|
} else {
|
|
const allSiblings = Array.from(replyToContentElement.parentElement.children)
|
|
const replyToContentIndex = allSiblings.indexOf(replyToContentElement)
|
|
const followingSiblings = allSiblings.slice(replyToContentIndex + 1)
|
|
|
|
if (followingSiblings.length === 0) return
|
|
|
|
let clonedFollowingSiblings = followingSiblings.map((sibling) =>
|
|
sibling.cloneNode(true),
|
|
)
|
|
|
|
const div = doc.createElement('div')
|
|
div.append(...clonedFollowingSiblings)
|
|
|
|
wrapper.append(div)
|
|
|
|
// Remove all siblings after the reply-to-content element
|
|
for (let i = replyToContentIndex + 1; i < allSiblings.length; i++) {
|
|
replyToContentElement.parentElement.removeChild(allSiblings[i])
|
|
}
|
|
}
|
|
|
|
replyToContentElement.parentElement.replaceChild(
|
|
wrapper,
|
|
replyToContentElement,
|
|
)
|
|
}
|
|
|
|
const htmlContent = `
|
|
<!DOCTYPE html>
|
|
<html>
|
|
<head>
|
|
<style>
|
|
${css}
|
|
|
|
.replied-content .collapse {
|
|
margin: 10px 0 10px 0;
|
|
visibility: visible;
|
|
cursor: pointer;
|
|
display: flex;
|
|
font-size: larger;
|
|
font-weight: 700;
|
|
height: 12px;
|
|
line-height: 0.1;
|
|
background: #e8eaed;
|
|
width: 23px;
|
|
justify-content: center;
|
|
border-radius: 5px;
|
|
}
|
|
|
|
.replied-content .collapse:hover {
|
|
background: #dadce0;
|
|
}
|
|
|
|
.replied-content .collapse + input {
|
|
display: none;
|
|
}
|
|
.replied-content .collapse + input + div {
|
|
display: none;
|
|
}
|
|
.replied-content .collapse + input:checked + div {
|
|
display: block;
|
|
}
|
|
|
|
.email-content {
|
|
word-break: break-word;
|
|
}
|
|
.email-content
|
|
:is(:where(table):not(:where([class~='not-prose'], [class~='not-prose']
|
|
*))) {
|
|
table-layout: auto;
|
|
}
|
|
|
|
.email-content
|
|
:where(table):not(:where([class~='not-prose'], [class~='not-prose'] *)) {
|
|
width: unset;
|
|
table-layout: auto;
|
|
text-align: unset;
|
|
margin-top: unset;
|
|
margin-bottom: unset;
|
|
font-size: unset;
|
|
line-height: unset;
|
|
}
|
|
|
|
/* tr */
|
|
|
|
.email-content
|
|
:where(tbody tr):not(:where([class~='not-prose'], [class~='not-prose']
|
|
*)) {
|
|
border-bottom-width: 0;
|
|
border-bottom-color: transparent;
|
|
}
|
|
|
|
/* td */
|
|
|
|
.email-content
|
|
:is(:where(td):not(:where([class~='not-prose'], [class~='not-prose'] *))) {
|
|
position: unset;
|
|
border-width: 0;
|
|
border-color: transparent;
|
|
padding: 0;
|
|
}
|
|
|
|
.email-content
|
|
:where(tbody td):not(:where([class~='not-prose'], [class~='not-prose']
|
|
*)) {
|
|
vertical-align: revert;
|
|
}
|
|
|
|
/* image */
|
|
.email-content
|
|
:is(:where(img):not(:where([class~='not-prose'], [class~='not-prose']
|
|
*))) {
|
|
border-width: 0;
|
|
}
|
|
|
|
.email-content
|
|
:where(img):not(:where([class~='not-prose'], [class~='not-prose'] *)) {
|
|
margin: 0;
|
|
}
|
|
|
|
/* before & after */
|
|
|
|
.email-content
|
|
:where(blockquote
|
|
p:first-of-type):not(:where([class~='not-prose'], [class~='not-prose']
|
|
*))::before {
|
|
content: none;
|
|
}
|
|
|
|
.email-content
|
|
:where(blockquote
|
|
p:last-of-type):not(:where([class~='not-prose'], [class~='not-prose']
|
|
*))::after {
|
|
content: none;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div ref="emailContentRef" class="email-content prose-f">${_content.value}</div>
|
|
</body>
|
|
</html>
|
|
`
|
|
|
|
watch(iframeRef, (iframe) => {
|
|
if (iframe) {
|
|
iframe.onload = () => {
|
|
const emailContent =
|
|
iframe.contentWindow.document.querySelector('.email-content')
|
|
let parent = emailContent.closest('html')
|
|
|
|
iframe.style.height = parent.offsetHeight + 1 + 'px'
|
|
|
|
let replyCollapsers = emailContent.querySelectorAll('.replyCollapser')
|
|
if (replyCollapsers.length) {
|
|
replyCollapsers.forEach((replyCollapser) => {
|
|
replyCollapser.addEventListener('change', () => {
|
|
iframe.style.height = parent.offsetHeight + 1 + 'px'
|
|
})
|
|
})
|
|
}
|
|
}
|
|
}
|
|
})
|
|
</script>
|