5 votes

Puis-je utiliser des éléments Polymer / WebComponents avec TinyMCE ?

J'essaie de créer un éditeur TinyMCE personnalisé pour le contenu d'enseignement qui permettra à certains blocs d'être enveloppés comme une "activité". Il y aura plusieurs activités dans un bloc de contenu, donc elles auront des ID comme clés primaires, etc.

Mon défi est de mettre en œuvre un plugin qui permette cela. Idéalement, j'utiliserais des shortcodes, mais ils sont très sujets aux erreurs. J'ai envisagé d'utiliser des balises HTML personnalisées qui seraient rendues par Polymer - est-ce possible ?

2voto

liamzebedee Points 2109

Je l'ai complètement résolu après environ 4 heures.

L'éditeur TinyMCE doit être initialisé avec le support des éléments personnalisés de la manière suivante :

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

Dónde registerCustomWebComponents ressemble :

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

J'ai fini par définir l'élément HTML personnalisé puis par définir un composant React plutôt que de construire le HTMl sous forme de chaîne.

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);
  }
}

0voto

Paul Smith Points 115

La réponse ici est de ne pas utiliser tinymce en polymère, tinymce est fortement dépendant de la racine du document et le shadow dom va le casser.

Mais comme tout dans la vie, tout n'est pas perdu...

Utilisez un objet dans votre modèle de polymère, faites charger l'objet par tinymce et résolvez le problème de Racine du document. Vous pouvez ensuite accéder à tinymce à partir de l'objet une fois chargé de la manière suivante.

Créer un fichier HTML pour charger 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>

Maintenant, il vous suffit d'ajouter un objet au modèle de votre composant, d'utiliser l'attribut data de l'objet pour charger le html.

Une fois qu'il est chargé, vous pouvez y accéder, interroger le domaine de l'objet et récupérer le textarea, ajouter un écouteur d'événement pour enregistrer l'événement personnalisé et également prédéfinir le contenu, ajuster la hauteur, etc. Je n'ai testé que ce même domaine, donc soyez conscient que cela peut être cassé dans un autre domaine, mais vous voulez servir à côté des autres composants de toute façon.

Ajoutez votre objet à votre modèle de composant

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

Et un moyen de précharger, de saisir des éléments, de régler la hauteur et de rattraper la sauvegarde.

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);
}

Il s'agit d'un scénario fonctionnel pour polymer 3 et tinymce, qui se charge rapidement, se redimensionne automatiquement et capte l'événement d'enregistrement de l'objet, sans que je doive modifier tinymce par rapport à sa configuration par défaut. Vous pouvez également utiliser cette méthode pour d'autres moyens de contourner le shadow dom avec certaines applications embarquées.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X