mirror of
https://github.com/BookStackApp/BookStack.git
synced 2025-05-01 15:09:51 +00:00
WYSWIYG: Allowed video/embed alignment controls
Required a lot of working around TinyMCE since it added a preview/wrapper element in the editor which complicates things. Added view new "fixes.js" file so large hacks to default TinyMCe functionality are kept in one place.
This commit is contained in:
parent
56d07f1909
commit
5395ca2f00
4 changed files with 89 additions and 7 deletions
resources
|
@ -13,6 +13,7 @@ import {getPlugin as getImagemanagerPlugin} from './plugins-imagemanager';
|
||||||
import {getPlugin as getAboutPlugin} from './plugins-about';
|
import {getPlugin as getAboutPlugin} from './plugins-about';
|
||||||
import {getPlugin as getDetailsPlugin} from './plugins-details';
|
import {getPlugin as getDetailsPlugin} from './plugins-details';
|
||||||
import {getPlugin as getTasklistPlugin} from './plugins-tasklist';
|
import {getPlugin as getTasklistPlugin} from './plugins-tasklist';
|
||||||
|
import {handleEmbedAlignmentChanges} from './fixes';
|
||||||
|
|
||||||
const styleFormats = [
|
const styleFormats = [
|
||||||
{title: 'Large Header', format: 'h2', preview: 'color: blue;'},
|
{title: 'Large Header', format: 'h2', preview: 'color: blue;'},
|
||||||
|
@ -35,9 +36,9 @@ const styleFormats = [
|
||||||
];
|
];
|
||||||
|
|
||||||
const formats = {
|
const formats = {
|
||||||
alignleft: {selector: 'p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li,table,img', classes: 'align-left'},
|
alignleft: {selector: 'p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li,table,img,iframe,video,span', classes: 'align-left'},
|
||||||
aligncenter: {selector: 'p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li,table,img', classes: 'align-center'},
|
aligncenter: {selector: 'p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li,table,img,iframe,video,span', classes: 'align-center'},
|
||||||
alignright: {selector: 'p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li,table,img', classes: 'align-right'},
|
alignright: {selector: 'p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li,table,img,iframe,video,span', classes: 'align-right'},
|
||||||
calloutsuccess: {block: 'p', exact: true, attributes: {class: 'callout success'}},
|
calloutsuccess: {block: 'p', exact: true, attributes: {class: 'callout success'}},
|
||||||
calloutinfo: {block: 'p', exact: true, attributes: {class: 'callout info'}},
|
calloutinfo: {block: 'p', exact: true, attributes: {class: 'callout info'}},
|
||||||
calloutwarning: {block: 'p', exact: true, attributes: {class: 'callout warning'}},
|
calloutwarning: {block: 'p', exact: true, attributes: {class: 'callout warning'}},
|
||||||
|
@ -177,6 +178,8 @@ function getSetupCallback(options) {
|
||||||
setupFilters(editor);
|
setupFilters(editor);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
handleEmbedAlignmentChanges(editor);
|
||||||
|
|
||||||
// Custom handler hook
|
// Custom handler hook
|
||||||
window.$events.emitPublic(options.containerElement, 'editor-tinymce::setup', {editor});
|
window.$events.emitPublic(options.containerElement, 'editor-tinymce::setup', {editor});
|
||||||
|
|
||||||
|
|
55
resources/js/wysiwyg/fixes.js
Normal file
55
resources/js/wysiwyg/fixes.js
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
/**
|
||||||
|
* Handle alignment for embed (iframe/video) content.
|
||||||
|
* TinyMCE built-in handling doesn't work well for these when classes are used for
|
||||||
|
* alignment, since the editor wraps these elements in a non-editable preview span
|
||||||
|
* which looses tracking and setting of alignment options.
|
||||||
|
* Here we manually manage these properties and formatting events, by effectively
|
||||||
|
* syncing the alignment classes to the parent preview span.
|
||||||
|
* @param {Editor} editor
|
||||||
|
*/
|
||||||
|
export function handleEmbedAlignmentChanges(editor) {
|
||||||
|
function updateClassesForPreview(previewElem) {
|
||||||
|
const mediaTarget = previewElem.querySelector('iframe, video');
|
||||||
|
if (!mediaTarget) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const alignmentClasses = [...mediaTarget.classList.values()].filter(c => c.startsWith('align-'));
|
||||||
|
const previewAlignClasses = [...previewElem.classList.values()].filter(c => c.startsWith('align-'));
|
||||||
|
previewElem.classList.remove(...previewAlignClasses);
|
||||||
|
previewElem.classList.add(...alignmentClasses);
|
||||||
|
}
|
||||||
|
|
||||||
|
editor.on('SetContent', () => {
|
||||||
|
const previewElems = editor.dom.select('span.mce-preview-object');
|
||||||
|
for (const previewElem of previewElems) {
|
||||||
|
updateClassesForPreview(previewElem);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
editor.on('FormatApply', event => {
|
||||||
|
const isAlignment = event.format.startsWith('align');
|
||||||
|
if (!event.node || !event.node.matches('.mce-preview-object')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const realTarget = event.node.querySelector('iframe, video');
|
||||||
|
if (isAlignment && realTarget) {
|
||||||
|
const className = (editor.formatter.get(event.format)[0]?.classes || [])[0];
|
||||||
|
const toAdd = !realTarget.classList.contains(className);
|
||||||
|
|
||||||
|
const wrapperClasses = (event.node.getAttribute('data-mce-p-class') || '').split(' ');
|
||||||
|
const wrapperClassesFiltered = wrapperClasses.filter(c => !c.startsWith('align-'));
|
||||||
|
if (toAdd) {
|
||||||
|
wrapperClassesFiltered.push(className);
|
||||||
|
}
|
||||||
|
|
||||||
|
const classesToApply = wrapperClassesFiltered.join(' ');
|
||||||
|
event.node.setAttribute('data-mce-p-class', classesToApply);
|
||||||
|
|
||||||
|
realTarget.setAttribute('class', classesToApply);
|
||||||
|
editor.formatter.apply(event.format, {}, realTarget);
|
||||||
|
updateClassesForPreview(event.node);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
|
@ -11,24 +11,24 @@
|
||||||
.align-left {
|
.align-left {
|
||||||
text-align: left;
|
text-align: left;
|
||||||
}
|
}
|
||||||
img.align-left, table.align-left {
|
img.align-left, table.align-left, iframe.align-left, video.align-left {
|
||||||
float: left !important;
|
float: left !important;
|
||||||
margin: $-xs $-m $-m 0;
|
margin: $-xs $-m $-m 0;
|
||||||
}
|
}
|
||||||
.align-right {
|
.align-right {
|
||||||
text-align: right !important;
|
text-align: right !important;
|
||||||
}
|
}
|
||||||
img.align-right, table.align-right {
|
img.align-right, table.align-right, iframe.align-right, video.align-right {
|
||||||
float: right !important;
|
float: right !important;
|
||||||
margin: $-xs 0 $-xs $-s;
|
margin: $-xs 0 $-xs $-s;
|
||||||
}
|
}
|
||||||
.align-center {
|
.align-center {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
img.align-center {
|
img.align-center, video.align-center, iframe.align-center {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
img.align-center, table.align-center {
|
img.align-center, table.align-center, iframe.align-center, video.align-center {
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
margin-right: auto;
|
margin-right: auto;
|
||||||
}
|
}
|
||||||
|
|
|
@ -72,6 +72,30 @@ body.page-content.mce-content-body {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Allow alignment to be reflected in media embed wrappers
|
||||||
|
.page-content.mce-content-body .mce-preview-object.align-right {
|
||||||
|
float: right !important;
|
||||||
|
margin: $-xs 0 $-xs $-s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-content.mce-content-body .mce-preview-object.align-left {
|
||||||
|
float: left !important;
|
||||||
|
margin: $-xs $-m $-m 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-content.mce-content-body .mce-preview-object.align-center {
|
||||||
|
display: block;
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-content.mce-content-body .mce-preview-object iframe,
|
||||||
|
.page-content.mce-content-body .mce-preview-object video {
|
||||||
|
display: block;
|
||||||
|
margin: 0 !important;
|
||||||
|
float: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Dark Mode Overrides
|
* Dark Mode Overrides
|
||||||
*/
|
*/
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue