Skip to content

Commit 9e66bb7

Browse files
feat(mail): add option to align images
This code is from the original PR that I split into two β€” it contained both dark mode fixes and image alignment, so I had to separate them. It should work, but it still needs a bit more work β€” especially regarding the signature settings. Signed-off-by: TobiΓ‘Ε‘ VaΕ‘Ε₯Γ‘k <tobias.vastak@gmail.com>
1 parent cb28034 commit 9e66bb7

File tree

5 files changed

+84
-6
lines changed

5 files changed

+84
-6
lines changed

β€Žsrc/components/AppSettingsMenu.vueβ€Ž

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -312,7 +312,9 @@
312312
:is-form="true"
313313
size="normal">
314314
<NcInputField :value.sync="localTextBlock.title" :label="t('mail','Title of the text block')" />
315-
<TextEditor v-model="localTextBlock.content"
315+
<TextEditor
316+
ref="textEditor"
317+
v-model="localTextBlock.content"
316318
:is-bordered="true"
317319
:html="true"
318320
:placeholder="t('mail','Content of the text block')"
@@ -654,6 +656,7 @@ export default {
654656
this.trapElements.push(element)
655657
},
656658
newTextBlock() {
659+
this.localTextBlock.content = this.$refs.textEditor.convertImageClassesToInlineStyles(this.localTextBlock.content)
657660
this.mainStore.createTextBlock({ ...this.localTextBlock })
658661
this.textBlockDialogOpen = false
659662
this.localTextBlock = {

β€Žsrc/components/Composer.vueβ€Ž

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1138,8 +1138,10 @@ export default {
11381138
isPgpMime: this.encrypt,
11391139
}
11401140
1141-
if (data.isHtml) {
1142-
data.bodyHtml = this.bodyVal
1141+
if (data.isHtml && this.$refs.editor && this.$refs.editor.convertImageClassesToInlineStyles) {
1142+
data.bodyHtml = this.$refs.editor.convertImageClassesToInlineStyles(this.bodyVal)
1143+
} else if (!data.isHtml) {
1144+
data.bodyPlain = toPlain(html(this.bodyVal)).value
11431145
} else {
11441146
data.bodyPlain = toPlain(html(this.bodyVal)).value
11451147
}

β€Žsrc/components/SignatureSettings.vueβ€Ž

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,13 @@
2323
label="label"
2424
track-by="id"
2525
@option:selected="changeIdentity" />
26+
<div v-if="toolbarElement" ref="toolbarContainer" class="toolbar"></div>
2627
<TextEditor v-model="signature"
2728
:html="true"
2829
:placeholder="t('mail', 'Signature …')"
2930
:bus="bus"
30-
@show-toolbar="handleShowToolbar" />
31+
@show-toolbar="handleShowToolbar"
32+
ref="textEditor" />
3133
<p v-if="isLargeSignature" class="warning-large-signature">
3234
{{ t('mail', 'Your signature is larger than 2 MB. This may affect the performance of your editor.') }}
3335
</p>
@@ -84,6 +86,7 @@ export default {
8486
identity: null,
8587
signature: '',
8688
signatureAboveQuote: this.account.signatureAboveQuote,
89+
toolbarElement: null,
8790
}
8891
},
8992
computed: {
@@ -125,6 +128,12 @@ export default {
125128
}
126129
},
127130
},
131+
toolbarElement(newEl) {
132+
if (newEl && this.$refs.toolbarContainer) {
133+
this.$refs.toolbarContainer.innerHTML = ''
134+
this.$refs.toolbarContainer.appendChild(newEl)
135+
}
136+
},
128137
beforeMount() {
129138
this.changeIdentity(this.identities[0])
130139
},
@@ -145,7 +154,7 @@ export default {
145154
146155
const payload = {
147156
account: this.account,
148-
signature: this.signature,
157+
signature: this.$refs.textEditor.convertImageClassesToInlineStyles(this.signature),
149158
}
150159
151160
if (this.identity.id > -1) {
@@ -215,6 +224,7 @@ export default {
215224
display: block;
216225
padding: 0;
217226
margin-bottom: 23px;
227+
height: 100%;
218228
}
219229
220230
.ck-balloon-panel {

β€Žsrc/components/TextEditor.vueβ€Ž

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,11 @@ import ImagePlugin from '@ckeditor/ckeditor5-image/src/image.js'
4747
import FindAndReplace from '@ckeditor/ckeditor5-find-and-replace/src/findandreplace.js'
4848
import ImageResizePlugin from '@ckeditor/ckeditor5-image/src/imageresize.js'
4949
import ImageUploadPlugin from '@ckeditor/ckeditor5-image/src/imageupload.js'
50+
import ImageStylePlugin from '@ckeditor/ckeditor5-image/src/imagestyle.js'
51+
import ImageToolbarPlugin from '@ckeditor/ckeditor5-image/src/imagetoolbar';
52+
import ImageUtilsPlugin from '@ckeditor/ckeditor5-image/src/imageutils';
53+
import ImageCaptionPlugin from '@ckeditor/ckeditor5-image/src/imagecaption';
54+
import ImageTextAlternativePlugin from '@ckeditor/ckeditor5-image/src/imagetextalternative';
5055
import GeneralHtmlSupport from '@ckeditor/ckeditor5-html-support/src/generalhtmlsupport.js'
5156
import { DropdownView } from '@ckeditor/ckeditor5-ui'
5257
import MailPlugin from '../ckeditor/mail/MailPlugin.js'
@@ -129,6 +134,11 @@ export default {
129134
ImagePlugin,
130135
ImageUploadPlugin,
131136
ImageResizePlugin,
137+
ImageStylePlugin,
138+
ImageToolbarPlugin,
139+
ImageUtilsPlugin,
140+
ImageCaptionPlugin,
141+
ImageTextAlternativePlugin,
132142
ListProperties,
133143
FontPlugin,
134144
RemoveFormat,
@@ -196,6 +206,18 @@ export default {
196206
},
197207
],
198208
},
209+
image: {
210+
toolbar: [
211+
'imageStyle:alignLeft',
212+
'imageStyle:alignCenter',
213+
'imageStyle:alignRight',
214+
'|',
215+
'imageTextAlternative',
216+
],
217+
styles: [
218+
'alignLeft', 'alignCenter', 'alignRight',
219+
],
220+
},
199221
},
200222
}
201223
},
@@ -278,6 +300,44 @@ export default {
278300
279301
return itemElement
280302
},
303+
convertImageClassesToInlineStyles(html) {
304+
const div = document.createElement('div');
305+
div.innerHTML = html;
306+
307+
div.querySelectorAll('figure.image').forEach(figure => {
308+
// Keep the original style attribute
309+
let baseStyle = figure.getAttribute('style') || '';
310+
let alignmentStyle = 'display:block;margin-top:1em;margin-bottom:1em;';
311+
312+
if (figure.classList.contains('image-style-align-left')) {
313+
alignmentStyle += 'margin-left:0;margin-right:auto;';
314+
} else if (figure.classList.contains('image-style-align-right')) {
315+
alignmentStyle += 'margin-left:auto;margin-right:0;';
316+
} else if (figure.classList.contains('image-style-align-center')) {
317+
alignmentStyle += 'margin-left:auto;margin-right:auto;text-align:center;';
318+
}
319+
320+
// Combine original styles with alignment styles
321+
const combinedStyle = `${baseStyle.trim()}${!baseStyle.endsWith(';') ? ';' : ''}${alignmentStyle}`;
322+
figure.setAttribute('style', combinedStyle);
323+
324+
// IMPORTANT: Do NOT remove alignment classes
325+
// so CKEditor can reuse them when reopening the content
326+
327+
// Adjust the <img> inside the figure to ensure correct display in email clients
328+
const img = figure.querySelector('img');
329+
if (img) {
330+
const baseImgStyle = img.getAttribute('style') || '';
331+
const imgStyle = 'display:block;margin:0 auto;max-width:100%;height:auto;border:0;';
332+
img.setAttribute(
333+
'style',
334+
`${baseImgStyle.trim()}${!baseImgStyle.endsWith(';') ? ';' : ''}${imgStyle}`
335+
);
336+
}
337+
});
338+
339+
return div.innerHTML;
340+
},
281341
overrideDropdownPositionsToNorth(editor, toolbarView) {
282342
const {
283343
south, north, southEast, southWest, northEast, northWest,

β€Žsrc/components/textBlocks/ListItem.vueβ€Ž

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,9 @@
3535
{{ localTextBlock.title }}
3636
</p>
3737
<NcInputField v-else :value.sync="localTextBlock.title" :label="t('mail','Title of the text block')" />
38-
<TextEditor v-model="localTextBlock.content"
38+
<TextEditor
39+
ref="textEditor"
40+
v-model="localTextBlock.content"
3941
:is-bordered="!shared"
4042
:html="true"
4143
:read-only="shared"
@@ -352,6 +354,7 @@ export default {
352354
async saveTextBlock() {
353355
this.saveLoading = true
354356
try {
357+
this.localTextBlock.content = this.$refs.textEditor.convertImageClassesToInlineStyles(this.localTextBlock.content)
355358
await this.mainStore.patchTextBlock(this.localTextBlock)
356359
this.saveLoading = false
357360
this.editModalOpen = false

0 commit comments

Comments
Β (0)