我可以将Polymer Elements / WebComponents与TinyMCE一起使用吗?

时间:2016-08-03 02:56:43

标签: javascript tinymce polymer web-component

我正在尝试构建一个自定义的TinyMCE编辑器来教授内容,这些内容允许某些块被包装为“活动”。内容块中将有多个活动,因此他们将ID作为主键等。

我的挑战是实现一个允许这样做的插件 - 理想情况下,我会使用短代码,但它们非常容易出错。我正在研究使用通过Polymer渲染的自定义HTML标签 - 可以这样做吗?

3 个答案:

答案 0 :(得分:0)

我在大约4个小时后完成了它。

需要对TinyMCE编辑器进行初始化,并支持自定义元素:

{
...
    extended_valid_elements : 'module-activity',
    custom_elements : 'module-activity',
    init_instance_callback: function(editor) {
        registerCustomWebComponents(tinymce.activeEditor.dom.doc);
    },
...
}

registerCustomWebComponents的样子:

function registerCustomWebComponents(doc) {
  doc.registerElement('module-activity', ModuleActivityHTMLElement);  
}

我最终定义了自定义HTML元素,然后定义了React组件,而不是将HTMl构建为字符串。

class ModuleActivity extends React.Component {
  constructor(props) {
    super(props);
    this.openActivityEdit = this.openActivityEdit.bind(this);
  }

  openActivityEdit() {

  }

  render() {
    return <div>
      <h3>Module Activity</h3>
      <button onClick={this.openActivityEdit}>Edit</button>
      <div dangerouslySetInnerHTML={{__html: this.props.contentHtml }} />
    </div>;
  }
}


class ModuleActivityHTMLElement extends HTMLElement {
  attachedCallback() {
    let self = this;
    var mountPoint = document.createElement('div');
    this.createShadowRoot().appendChild(mountPoint);
    ReactDOM.render(<ModuleActivity contentHtml={self.innerHTML}/>, mountPoint);
  }
}

答案 1 :(得分:0)

这里的答案是不要在聚合物中使用tinymce,tinymce严重依赖于文档的根,而dom会破坏它。

但是就像生活中的一切一样,一切都不会丢失...

在聚合物模板中使用一个对象,使该对象加载tinymce并解决文档根目录问题。然后,您就可以通过这种方式从对象加载后访问tinymce。

创建一个HTML文件以加载tinymce

<!DOCTYPE html>
<html>

<head>
    <script src="https://cloud.tinymce.com/stable/tinymce.min.js"></script>

    <style>
        html { height: 96%; }
        body { height: 96%; }
    </style>
</head>

<body>
    <textarea>Loading...</textarea>
    <script>
        var ta = document.querySelector('textarea');
        ta.tinymce = tinymce;
        var editorChangeHandler;
        tinymce.init({ selector: 'textarea', height: document.body.scrollHeight - 100, setup: function (editor) {
            editor.on('Paste Change input Undo Redo', function () {
                if (editorChangeHandler) { clearTimeout(editorChangeHandler); }
                editorChangeHandler = setTimeout(function () {
                    ta.dispatchEvent(new CustomEvent('save'));
                }, 2000);
            });
        } });
    </script>
</body>

</html>

现在,您只需要将一个对象添加到组件的模板中,即可使用对象的data属性加载html。

一旦加载,就可以访问它,查询对象dom并获取文本区域,添加事件侦听器以保存自定义事件,还可以预先设置内容,调整高度等。我只测试了相同的域,因此请注意,这可能会破坏跨域,但是无论如何您都希望与其他组件一起使用。

将对象添加到组件模板

<object id="editor" type="text/html" data="/src/lib/tinymce/tinymce.html"></object>

以某种方式进行预加载,抓取物品,设置高度并进行保存

ready() {
    super.ready();

    // wait five seconds before capturing input
    var interval = setInterval(() => {
        if (!this.$.editor.contentDocument.body) return;
        let ta = this.$.editor.contentDocument.body.querySelector('textarea');
        if (!ta || !ta.tinymce || !ta.tinymce.activeEditor) return;

        // clear interval now loaded
        window.clearInterval(interval);

        setTimeout(() => {
            // resize on window change
            window.addEventListener('resize', this._updateEditorSize.bind(this));

            // pre load
            ta.tinymce.activeEditor.setContent(this.element.value);

            // catch updates every few seconds, this will then have a 4 second debounce on save too naturally
            ta.addEventListener('save', (ev) => {
                this.set('element.value', ta.tinymce.activeEditor.getContent({ format: 'raw' }));
            });
        }, 250);
    }, 250);
}

这是适用于聚合物3和tinymce的工作方案,可以快速加载,自动调整大小并捕获对象的保存事件,而我不必从默认设置中更改tinymce。您还可以将这种方法用于其他方法,以避开某些嵌入式应用程序的影子dom。

答案 2 :(得分:0)

@liamzebedee提供的解决方案有些过时,这是一个带有ES6模块和标准Web组件的解决方案。

与上面的代码的主要区别是在TinyMCE <iframe/>中注入了定义Web组件的脚本。否则,可接受的标签将保持惰性。

首先,这是与TinyMCE初始化相关的代码:

const name = 'custom-element';
const attribute = 'view-mode';
const insertTag = '<custom-element view-mode="editing"></custom-element>';
const definitionFile = './custom-element.js';

tinymce.PluginManager.add(name, function(editor, url) {
    editor.ui.registry.addButton(name, {
        text: name,
        onAction: () => editor.insertContent(insertTag)
    });
});

tinymce.init({
    // ...

    custom_elements: name, // just the custom Web element names
    extended_valid_elements: `${name}[${attribute}]`, // names+attributes

    init_instance_callback: function(editor) {
        const edDoc = editor.getDoc();
        const scriptTag = edDoc.createElement('script');
        scriptTag.src = definitionFile;
        scriptTag.type = 'module’;
        // Injection of the definition file in the <iframe/> document
        edDoc.querySelector('head').appendChild(scriptTag);
    }
});

相应的(最小)Web组件定义为:

export class SimpleNumber extends HTMLElement {

    constructor() {
        super();

        // Add a simple <input/> field in the ShadowRoot
        this.attachShadow({mode: 'open'}).innerHTML = `
            <style>
                :host { display:inline-block; }
                input { border: 1px solid blue; font-size: 12pt; text-align: right; }
            </style>
            <input type="number" min="0" max="10" value="0" />
        `;

        // Provide some feedback when the  <input/> field value changes
        this.shadowRoot.querySelector('input').addEventListener('change', (event) => {
            const field = event.target;
            field.style.backgroundColor = field.value === '3' ? 'lightgreen' : 'orange';
        });
    }
}

if (window.customElements && !customElements.get('simple-number')){
    customElements.define('simple-number', SimpleNumber);
}

请注意,我无法在 codepen.io 上提供正在运行的示例(例如),因为我无法为自定义元素定义提供单独的JS文件(该文件需要插入{ {1}})…