6
6
style ="
7
7
mask-image : linear-gradient (
8
8
to bottom ,
9
- black calc (100% - 30 px ),
9
+ black calc (100% - 20 px ),
10
10
transparent 100%
11
11
);
12
12
"
@@ -27,6 +27,90 @@ const files = import.meta.globEager('/src/index.css', { query: '?inline' })
27
27
const css = files[' /src/index.css' ].default
28
28
29
29
const iframeRef = ref (null )
30
+ const _content = ref (props .content )
31
+
32
+ const parser = new DOMParser ()
33
+ const doc = parser .parseFromString (_content .value , ' text/html' )
34
+
35
+ const gmailReplyToContent = doc .querySelectorAll (' div.gmail_quote' )
36
+ const outlookReplyToContent = doc .querySelectorAll (' div#appendonsend' )
37
+ const replyToContent = doc .querySelectorAll (' p.reply-to-content' )
38
+
39
+ if (gmailReplyToContent .length ) {
40
+ _content .value = parseReplyToContent (doc, ' div.gmail_quote' , true )
41
+ } else if (outlookReplyToContent .length ) {
42
+ _content .value = parseReplyToContent (doc, ' div#appendonsend' )
43
+ } else if (replyToContent .length ) {
44
+ _content .value = parseReplyToContent (doc, ' p.reply-to-content' )
45
+ }
46
+
47
+ function parseReplyToContent (doc , selector , forGmail = false ) {
48
+ function handleAllInstances (doc ) {
49
+ const replyToContentElements = doc .querySelectorAll (selector)
50
+ if (replyToContentElements .length === 0 ) return
51
+ const replyToContentElement = replyToContentElements[0 ]
52
+ replaceReplyToContent (replyToContentElement, forGmail)
53
+ handleAllInstances (doc)
54
+ }
55
+
56
+ handleAllInstances (doc)
57
+
58
+ return doc .body .innerHTML
59
+ }
60
+
61
+ function replaceReplyToContent (replyToContentElement , forGmail ) {
62
+ if (! replyToContentElement) return
63
+ let randomId = Math .random ().toString (36 ).substring (2 , 7 )
64
+ const wrapper = doc .createElement (' div' )
65
+ wrapper .classList .add (' replied-content' )
66
+
67
+ const collapseLabel = doc .createElement (' label' )
68
+ collapseLabel .classList .add (' collapse' )
69
+ collapseLabel .setAttribute (' for' , randomId)
70
+ collapseLabel .innerHTML = ' ...'
71
+ wrapper .appendChild (collapseLabel)
72
+
73
+ const collapseInput = doc .createElement (' input' )
74
+ collapseInput .setAttribute (' id' , randomId)
75
+ collapseInput .setAttribute (' class' , ' replyCollapser' )
76
+ collapseInput .setAttribute (' type' , ' checkbox' )
77
+ wrapper .appendChild (collapseInput)
78
+
79
+ if (forGmail) {
80
+ const prevSibling = replyToContentElement .previousElementSibling
81
+ if (prevSibling && prevSibling .tagName === ' BR' ) {
82
+ prevSibling .remove ()
83
+ }
84
+ let cloned = replyToContentElement .cloneNode (true )
85
+ cloned .classList .remove (' gmail_quote' )
86
+ wrapper .appendChild (cloned)
87
+ } else {
88
+ const allSiblings = Array .from (replyToContentElement .parentElement .children )
89
+ const replyToContentIndex = allSiblings .indexOf (replyToContentElement)
90
+ const followingSiblings = allSiblings .slice (replyToContentIndex + 1 )
91
+
92
+ if (followingSiblings .length === 0 ) return
93
+
94
+ let clonedFollowingSiblings = followingSiblings .map ((sibling ) =>
95
+ sibling .cloneNode (true ),
96
+ )
97
+
98
+ const div = doc .createElement (' div' )
99
+ div .append (... clonedFollowingSiblings)
100
+
101
+ wrapper .append (div)
102
+
103
+ // Remove all siblings after the reply-to-content element
104
+ for (let i = replyToContentIndex + 1 ; i < allSiblings .length ; i++ ) {
105
+ replyToContentElement .parentElement .removeChild (allSiblings[i])
106
+ }
107
+ }
108
+
109
+ replyToContentElement .parentElement .replaceChild (
110
+ wrapper,
111
+ replyToContentElement,
112
+ )
113
+ }
30
114
31
115
const htmlContent = `
32
116
<!DOCTYPE html>
@@ -35,6 +119,35 @@ const htmlContent = `
35
119
<style>
36
120
${ css}
37
121
122
+ .replied-content .collapse {
123
+ margin: 10px 0 10px 0;
124
+ visibility: visible;
125
+ cursor: pointer;
126
+ display: flex;
127
+ font-size: larger;
128
+ font-weight: 700;
129
+ height: 12px;
130
+ line-height: 0.1;
131
+ background: #e8eaed;
132
+ width: 23px;
133
+ justify-content: center;
134
+ border-radius: 5px;
135
+ }
136
+
137
+ .replied-content .collapse:hover {
138
+ background: #dadce0;
139
+ }
140
+
141
+ .replied-content .collapse + input {
142
+ display: none;
143
+ }
144
+ .replied-content .collapse + input + div {
145
+ display: none;
146
+ }
147
+ .replied-content .collapse + input:checked + div {
148
+ display: block;
149
+ }
150
+
38
151
.email-content {
39
152
word-break: break-word;
40
153
}
@@ -110,7 +223,7 @@ const htmlContent = `
110
223
</style>
111
224
</head>
112
225
<body>
113
- <div ref="emailContentRef" class="email-content prose-f">${ props . content } </div>
226
+ <div ref="emailContentRef" class="email-content prose-f">${ _content . value } </div>
114
227
</body>
115
228
</html>
116
229
`
@@ -120,7 +233,18 @@ watch(iframeRef, (iframe) => {
120
233
iframe .onload = () => {
121
234
const emailContent =
122
235
iframe .contentWindow .document .querySelector (' .email-content' )
123
- iframe .style .height = emailContent .offsetHeight + 25 + ' px'
236
+ let parent = emailContent .closest (' html' )
237
+
238
+ iframe .style .height = parent .offsetHeight + ' px'
239
+
240
+ let replyCollapsers = emailContent .querySelectorAll (' .replyCollapser' )
241
+ if (replyCollapsers .length ) {
242
+ replyCollapsers .forEach ((replyCollapser ) => {
243
+ replyCollapser .addEventListener (' change' , () => {
244
+ iframe .style .height = parent .offsetHeight + ' px'
245
+ })
246
+ })
247
+ }
124
248
}
125
249
}
126
250
})
0 commit comments