124 lines
4.7 KiB
JavaScript
124 lines
4.7 KiB
JavaScript
import { Component, onMounted, onWillDestroy, useRef, useState, useSubEnv } from "@odoo/owl";
|
|
import { Editor } from "./editor";
|
|
import { Toolbar } from "./main/toolbar/toolbar";
|
|
import { useChildRef, useSpellCheck } from "@web/core/utils/hooks";
|
|
import { LocalOverlayContainer } from "./local_overlay_container";
|
|
import { uniqueId } from "@web/core/utils/functions";
|
|
|
|
/**
|
|
* @typedef { import("./editor").EditorConfig } EditorConfig
|
|
**/
|
|
|
|
function copyCssRules(sourceDoc, targetDoc) {
|
|
for (const sheet of sourceDoc.styleSheets) {
|
|
const rules = [];
|
|
for (const r of sheet.cssRules) {
|
|
rules.push(r.cssText);
|
|
}
|
|
const cssRules = rules.join(" ");
|
|
const styleTag = targetDoc.createElement("style");
|
|
styleTag.appendChild(targetDoc.createTextNode(cssRules));
|
|
targetDoc.head.appendChild(styleTag);
|
|
}
|
|
}
|
|
|
|
export class Wysiwyg extends Component {
|
|
static template = "html_editor.Wysiwyg";
|
|
static components = { Toolbar, LocalOverlayContainer };
|
|
static props = {
|
|
config: { type: Object, optional: true },
|
|
class: { type: String, optional: true },
|
|
contentClass: { type: String, optional: true }, // on editable element
|
|
style: { type: String, optional: true },
|
|
toolbar: { type: Boolean, optional: true },
|
|
iframe: { type: Boolean, optional: true },
|
|
copyCss: { type: Boolean, optional: true },
|
|
onLoad: { type: Function, optional: true },
|
|
onBlur: { type: Function, optional: true },
|
|
dynamicPlaceholder: { type: Boolean, optional: true },
|
|
};
|
|
|
|
static defaultProps = {
|
|
onLoad: () => {},
|
|
onBlur: () => {},
|
|
};
|
|
|
|
setup() {
|
|
this.state = useState({
|
|
showToolbar: false,
|
|
});
|
|
this.overlayRef = useChildRef();
|
|
useSubEnv({
|
|
localOverlayContainerKey: uniqueId("wysiwyg"),
|
|
});
|
|
const contentRef = useRef("content");
|
|
this.editor = this.props.editor;
|
|
const config = this.getEditorConfig();
|
|
this.editor = new Editor(config, this.env.services);
|
|
this.props.onLoad(this.editor);
|
|
useSpellCheck({
|
|
refName: "content",
|
|
});
|
|
|
|
onMounted(() => {
|
|
// now that component is mounted, editor is attached to el, and
|
|
// plugins are started, so we can allow the toolbar to be displayed
|
|
this.state.showToolbar = true;
|
|
/** @type { any } **/
|
|
const el = contentRef.el;
|
|
|
|
if (el.tagName === "IFRAME") {
|
|
// grab the inner body instead
|
|
const attachEditor = () => {
|
|
if (!this.editor.isDestroyed) {
|
|
if (this.props.copyCss) {
|
|
copyCssRules(document, el.contentDocument);
|
|
}
|
|
const additionalClasses = el.dataset.class?.trim().split(" ");
|
|
if (additionalClasses) {
|
|
for (const c of additionalClasses) {
|
|
el.contentDocument.body.classList.add(c);
|
|
}
|
|
}
|
|
this.editor.attachTo(el.contentDocument.body);
|
|
}
|
|
};
|
|
if (el.contentDocument.readyState === "complete") {
|
|
attachEditor();
|
|
} else {
|
|
// in firefox, iframe is not immediately available. we need to wait
|
|
// for it to be ready before mounting editor
|
|
el.addEventListener(
|
|
"load",
|
|
() => {
|
|
attachEditor();
|
|
this.render();
|
|
},
|
|
{ once: true }
|
|
);
|
|
}
|
|
} else {
|
|
this.editor.attachTo(el);
|
|
}
|
|
});
|
|
onWillDestroy(() => this.editor.destroy(true));
|
|
}
|
|
|
|
getEditorConfig() {
|
|
return {
|
|
...this.props.config,
|
|
// TODO ABD TODO @phoenix: check if there is too much info in the wysiwyg env.
|
|
// i.e.: env has X because of parent component,
|
|
// embedded component descendant sometimes uses X from env which is set conditionally:
|
|
// -> it will override the one one from the parent => OK.
|
|
// -> it will not => the embedded component still has X in env because of its ancestors => Issue.
|
|
embeddedComponentInfo: { app: this.__owl__.app, env: this.env },
|
|
localOverlayContainers: {
|
|
key: this.env.localOverlayContainerKey,
|
|
ref: this.overlayRef,
|
|
},
|
|
disableFloatingToolbar: this.props.toolbar,
|
|
};
|
|
}
|
|
}
|