From 65dd7ad1e91f01f3ee1250fcb18fafb4c0429463 Mon Sep 17 00:00:00 2001 From: Dan Brown Date: Sat, 19 Mar 2022 17:12:56 +0000 Subject: [PATCH] Changed to a psuedo-style approach for tasklist in wysiwyg --- resources/js/wysiwyg/config.js | 2 +- resources/js/wysiwyg/plugins-tasklist.js | 116 +++++++---------------- resources/sass/_tinymce.scss | 25 +++++ 3 files changed, 58 insertions(+), 85 deletions(-) diff --git a/resources/js/wysiwyg/config.js b/resources/js/wysiwyg/config.js index fab6a3886..e75e4f712 100644 --- a/resources/js/wysiwyg/config.js +++ b/resources/js/wysiwyg/config.js @@ -207,7 +207,7 @@ export function build(options) { statusbar: false, menubar: false, paste_data_images: false, - extended_valid_elements: 'pre[*],svg[*],div[drawio-diagram],details[*],summary[*],div[*],li[class]', + extended_valid_elements: 'pre[*],svg[*],div[drawio-diagram],details[*],summary[*],div[*],li[class|checked]', automatic_uploads: false, custom_elements: 'doc-root,code-block', valid_children: [ diff --git a/resources/js/wysiwyg/plugins-tasklist.js b/resources/js/wysiwyg/plugins-tasklist.js index 07f934463..4070575d9 100644 --- a/resources/js/wysiwyg/plugins-tasklist.js +++ b/resources/js/wysiwyg/plugins-tasklist.js @@ -1,60 +1,27 @@ -/** - * @param {Editor} editor - */ -function defineTaskListCustomElement(editor) { - const doc = editor.getDoc(); - const win = doc.defaultView; - - class TaskListElement extends win.HTMLElement { - constructor() { - super(); - // this.attachShadow({mode: 'open'}); - // - // const input = doc.createElement('input'); - // input.setAttribute('type', 'checkbox'); - // input.setAttribute('disabled', 'disabled'); - // - // if (this.hasAttribute('selected')) { - // input.setAttribute('selected', 'selected'); - // } - // - // this.shadowRoot.append(input); - // this.shadowRoot.close(); - } - } - - win.customElements.define('task-list-item', TaskListElement); -} - /** * @param {Editor} editor * @param {String} url */ -function register(editor, url) { - // editor.on('NewBlock', ({ newBlock}) => { - // ensureElementHasCheckbox(newBlock); - // }); +function register(editor, url) { editor.on('PreInit', () => { - defineTaskListCustomElement(editor); - - editor.parser.addNodeFilter('li', function(elms) { - for (const elem of elms) { - if (elem.attributes.map.class === 'task-list-item') { - replaceTaskListNode(elem); + editor.parser.addNodeFilter('li', function(nodes) { + for (const node of nodes) { + if (node.attributes.map.class === 'task-list-item') { + parseTaskListNode(node); } } }); - // editor.serializer.addNodeFilter('li', function(elms) { - // for (const elem of elms) { - // if (elem.attributes.map.class === 'task-list-item') { - // ensureNodeHasCheckbox(elem); - // } - // } - // }); + editor.serializer.addNodeFilter('li', function(nodes) { + for (const node of nodes) { + if (node.attributes.map.class === 'task-list-item') { + serializeTaskListNode(node); + } + } + }); }); @@ -63,57 +30,38 @@ function register(editor, url) { /** * @param {AstNode} node */ -function replaceTaskListNode(node) { - - const taskListItem = new tinymce.html.Node.create('task-list-item', { - }); +function parseTaskListNode(node) { + // Force task list item class + node.attr('class', 'task-list-item'); + // Copy checkbox status and remove checkbox within editor for (const child of node.children()) { - if (node.name !== 'input') { - taskListItem.append(child); + if (child.name === 'input') { + if (child.attr('checked') === 'checked') { + node.attr('checked', 'checked'); + } + child.remove(); } } - - node.replace(taskListItem); } -// /** -// * @param {Element} elem -// */ -// function ensureElementHasCheckbox(elem) { -// const hasCheckbox = elem.querySelector(':scope > input[type="checkbox"]') !== null; -// if (hasCheckbox) { -// return; -// } -// -// const input = elem.ownerDocument.createElement('input'); -// input.setAttribute('type', 'checkbox'); -// input.setAttribute('disabled', 'disabled'); -// elem.prepend(input); -// } - /** - * @param {AstNode} elem + * @param {AstNode} node */ -function ensureNodeHasCheckbox(elem) { - // Stop if there's already an input - if (elem.firstChild && elem.firstChild.name === 'input') { - return; +function serializeTaskListNode(node) { + const isChecked = node.attr('checked') === 'checked'; + node.attr('checked', null); + + const inputAttrs = {type: 'checkbox', disabled: 'disabled'}; + if (isChecked) { + inputAttrs.checked = 'checked'; } - const input = new tinymce.html.Node.create('input', { - type: 'checkbox', - disabled: 'disabled', - }); - - if (elem.firstChild) { - elem.insert(input, elem.firstChild, true); - } else { - elem.append(input); - } + const checkbox = new tinymce.html.Node.create('input', inputAttrs); + checkbox.shortEnded = true; + node.firstChild ? node.insert(checkbox, node.firstChild, true) : node.append(checkbox); } - /** * @param {WysiwygConfigOptions} options * @return {register} diff --git a/resources/sass/_tinymce.scss b/resources/sass/_tinymce.scss index 6add27f45..c4848561a 100644 --- a/resources/sass/_tinymce.scss +++ b/resources/sass/_tinymce.scss @@ -112,4 +112,29 @@ body.page-content.mce-content-body { } .tox-menu .tox-collection__item-label { line-height: normal !important; +} + +/** + * Fake task list checkboxes + */ +.page-content.mce-content-body .task-list-item > input[type="checkbox"] { + display: none; +} +.page-content.mce-content-body .task-list-item:before { + content: ''; + display: inline-block; + border: 2px solid #CCC; + width: 12px; + height: 12px; + border-radius: 2px; + margin-right: 8px; + vertical-align: text-top; + cursor: pointer; +} + +.page-content.mce-content-body .task-list-item[checked]:before { + background-color: #CCC; + background-image: url('data:image/svg+xml;utf8,'); + background-position: 50% 50%; + background-size: 100% 100%; } \ No newline at end of file