diff --git a/resources/js/wysiwyg/config.js b/resources/js/wysiwyg/config.js index 13d15e1c5..7fa3b0f26 100644 --- a/resources/js/wysiwyg/config.js +++ b/resources/js/wysiwyg/config.js @@ -210,6 +210,16 @@ body { }`.trim().replace('\n', ''); } +// Custom "Document Root" element, a custom element to identify/define +// block that may act as another "editable body". +// Using a custom node means we can identify and add/remove these as desired +// without affecting user content. +class DocRootElement extends HTMLDivElement { + constructor() { + super(); + } +} + /** * @param {WysiwygConfigOptions} options * @return {Object} @@ -218,8 +228,10 @@ export function build(options) { // Set language window.tinymce.addI18n(options.language, options.translationMap); - + // Build toolbar content const {toolbar, groupButtons: toolBarGroupButtons} = buildToolbar(options); + // Define our custom root node + customElements.define('doc-root', DocRootElement, {extends: 'div'}); // Return config object return { @@ -242,9 +254,10 @@ export function build(options) { statusbar: false, menubar: false, paste_data_images: false, - extended_valid_elements: 'pre[*],svg[*],div[drawio-diagram],details[*],summary[*]', + extended_valid_elements: 'pre[*],svg[*],div[drawio-diagram],details[*],summary[*],doc-root', automatic_uploads: false, - valid_children: "-div[p|h1|h2|h3|h4|h5|h6|blockquote],+div[pre],+div[img]", + custom_elements: 'doc-root', + valid_children: "-div[p|h1|h2|h3|h4|h5|h6|blockquote|div],+div[pre],+div[img],+doc-root[p|h1|h2|h3|h4|h5|h6|blockquote|pre|img|ul|ol],-doc-root[doc-root|#text]", plugins: gatherPlugins(options), imagetools_toolbar: 'imageoptions', contextmenu: false, diff --git a/resources/js/wysiwyg/plugins-details.js b/resources/js/wysiwyg/plugins-details.js index 83a29a29d..0f089fc8e 100644 --- a/resources/js/wysiwyg/plugins-details.js +++ b/resources/js/wysiwyg/plugins-details.js @@ -178,13 +178,16 @@ function setupElementFilters(editor) { * @param {tinymce.html.Node} detailsEl */ function ensureDetailsWrappedInEditable(detailsEl) { + unwrapDetailsEditable(detailsEl); + detailsEl.attr('contenteditable', 'false'); - const wrap = tinymce.html.Node.create('div', {detailswrap: 'true', contenteditable: 'true'}); + const wrap = tinymce.html.Node.create('doc-root', {contenteditable: 'true'}); for (const child of detailsEl.children()) { if (child.name !== 'summary') { wrap.append(child); } } + detailsEl.append(wrap); } @@ -193,11 +196,17 @@ function ensureDetailsWrappedInEditable(detailsEl) { */ function unwrapDetailsEditable(detailsEl) { detailsEl.attr('contenteditable', null); + let madeUnwrap = false; for (const child of detailsEl.children()) { - if (child.attr('detailswrap')) { + if (child.name === 'doc-root') { child.unwrap(); + madeUnwrap = true; } } + + if (madeUnwrap) { + unwrapDetailsEditable(detailsEl); + } } diff --git a/resources/sass/_pages.scss b/resources/sass/_pages.scss index 494937299..4c54c1045 100755 --- a/resources/sass/_pages.scss +++ b/resources/sass/_pages.scss @@ -136,7 +136,8 @@ body.tox-fullscreen, body.markdown-fullscreen { } details { - border: 1px solid #DDD; + border: 1px solid; + @include lightDark(border-color, #DDD, #555); margin-bottom: 1em; padding: $-s; } @@ -146,11 +147,13 @@ body.tox-fullscreen, body.markdown-fullscreen { margin-right: -$-s; margin-bottom: -$-s; font-weight: bold; - background-color: #EEEEEE; + @include lightDark(background-color, #EEE, #333); padding: $-xs $-s; } details[open] > summary { - margin-bottom: 0; + margin-bottom: $-s; + border-bottom: 1px solid; + @include lightDark(border-color, #DDD, #555); } details > summary + * { margin-top: .2em; diff --git a/resources/sass/_tinymce.scss b/resources/sass/_tinymce.scss index 57bb69754..ecb258a53 100644 --- a/resources/sass/_tinymce.scss +++ b/resources/sass/_tinymce.scss @@ -17,6 +17,11 @@ display: block; } +// Default styles for our custom root nodes +.page-content.mce-content-body doc-root { + display: block; +} + // In editor line height override .page-content.mce-content-body p { line-height: 1.6; @@ -41,7 +46,7 @@ body.page-content.mce-content-body { .page-content.mce-content-body details summary { pointer-events: none; } -.page-content.mce-content-body details [detailswrap] { +.page-content.mce-content-body details doc-root { padding: $-s; margin-left: (2px - $-s); margin-right: (2px - $-s);