Current section

Files

Jump to
ckeditor5_phoenix dist index.mjs
Raw

dist/index.mjs

function f(i, t) {
let e = null;
return (...n) => {
e && clearTimeout(e), e = setTimeout(() => {
t(...n);
}, i);
};
}
class p {
/**
* The DOM element the hook is attached to.
* It includes an `instance` property to hold the hook instance.
*/
el;
/**
* The LiveView socket instance, providing connection to the server.
*/
liveSocket;
/**
* Pushes an event from the client to the LiveView server process.
* @param _event The name of the event.
* @param _payload The data to send with the event.
* @param _callback An optional function to be called with the server's reply.
*/
pushEvent;
/**
* Pushes an event to another hook on the page.
* @param _selector The CSS selector of the target element with the hook.
* @param _event The name of the event.
* @param _payload The data to send with the event.
* @param _callback An optional function to be called with the reply.
*/
pushEventTo;
/**
* Registers a handler for an event pushed from the server.
* @param _event The name of the event to handle.
* @param _callback The function to execute when the event is received.
*/
handleEvent;
}
function E(i) {
return {
/**
* The mounted lifecycle callback for the LiveView hook object.
* It creates an instance of the user-defined hook class and sets up the necessary properties and methods.
*/
mounted() {
const t = new i();
this.el.instance = t, t.el = this.el, t.liveSocket = this.liveSocket, t.pushEvent = (e, n, o) => this.pushEvent?.(e, n, o), t.pushEventTo = (e, n, o, s) => this.pushEventTo?.(e, n, o, s), t.handleEvent = (e, n) => this.handleEvent?.(e, n), t.mounted?.();
},
/**
* The beforeUpdate lifecycle callback that delegates to the hook instance.
*/
beforeUpdate() {
this.el.instance.beforeUpdate?.();
},
/**
* The destroyed lifecycle callback that delegates to the hook instance.
*/
destroyed() {
this.el.instance.destroyed?.();
},
/**
* The disconnected lifecycle callback that delegates to the hook instance.
*/
disconnected() {
this.el.instance.disconnected?.();
},
/**
* The reconnected lifecycle callback that delegates to the hook instance.
*/
reconnected() {
this.el.instance.reconnected?.();
}
};
}
function v(i, t) {
const e = Object.entries(i).map(([n, o]) => [n, t(o, n)]);
return Object.fromEntries(e);
}
function w(i) {
if (i === null)
return null;
const t = Number.parseInt(i, 10);
return Number.isNaN(t) ? null : t;
}
class l {
static the = new l();
/**
* Map of registered editors.
*/
editors = /* @__PURE__ */ new Map();
/**
* Map of callbacks that are waiting for an editor to be registered.
*/
callbacks = /* @__PURE__ */ new Map();
/**
* Private constructor to enforce singleton pattern.
*/
constructor() {
}
/**
* Executes a function on an editor.
* If the editor is not yet registered, it will wait for it to be registered.
*
* @param editorId The ID of the editor.
* @param fn The function to execute.
* @returns A promise that resolves with the result of the function.
*/
execute(t, e) {
const { callbacks: n, editors: o } = this, s = o.get(t);
return s ? Promise.resolve(e(s)) : new Promise((r) => {
const a = async (c) => r(await e(c));
this.callbacks.has(t) || n.set(t, []), n.set(t, [
...n.get(t),
a
]);
});
}
/**
* Registers an editor.
*
* @param editorId The ID of the editor.
* @param editor The editor instance.
*/
register(t, e) {
const { editors: n, callbacks: o } = this, s = o.get(t);
if (n.has(t))
throw new Error(`Editor with ID "${t}" is already registered.`);
n.set(t, e), s && (s.forEach((r) => r(e)), o.delete(t)), this.editors.size === 1 && this.register(null, e);
}
/**
* Un-registers an editor.
*
* @param editorId The ID of the editor.
*/
unregister(t) {
const { editors: e, callbacks: n } = this;
if (!e.has(t))
throw new Error(`Editor with ID "${t}" is not registered.`);
t && this.editors.get(null) === e.get(t) && this.unregister(null), e.delete(t), n.delete(t);
}
/**
* Gets all registered editors.
*/
getEditors() {
return Array.from(this.editors.values());
}
/**
* Checks if an editor with the given ID is registered.
*
* @param editorId The ID of the editor.
* @returns `true` if the editor is registered, `false` otherwise.
*/
hasEditor(t) {
return this.editors.has(t);
}
/**
* Gets a promise that resolves with the editor instance for the given ID.
* If the editor is not registered yet, it will wait for it to be registered.
*
* @param editorId The ID of the editor.
* @returns A promise that resolves with the editor instance.
*/
waitForEditor(t) {
return this.execute(t, (e) => e);
}
/**
* Destroys all registered editors and clears the registry.
* This will call the `destroy` method on each editor.
*/
async destroyAllEditors() {
const t = Array.from(this.editors.values()).map((e) => e.destroy());
this.editors.clear(), this.callbacks.clear(), await Promise.all(t);
}
}
class P extends p {
/**
* The name of the hook.
*/
mountedPromise = null;
/**
* Attributes for the editable instance.
*/
get attrs() {
const t = {
editableId: this.el.getAttribute("id"),
editorId: this.el.getAttribute("data-cke-editor-id") || null,
rootName: this.el.getAttribute("data-cke-editable-root-name"),
initialValue: this.el.getAttribute("data-cke-editable-initial-value") || ""
};
return Object.defineProperty(this, "attrs", {
value: t,
writable: !1,
configurable: !1,
enumerable: !0
}), t;
}
/**
* Mounts the editable component.
*/
async mounted() {
const { editableId: t, editorId: e, rootName: n, initialValue: o } = this.attrs, s = this.el.querySelector(`#${t}_input`);
this.mountedPromise = l.the.execute(e, (r) => {
const { ui: a, editing: c, model: h } = r;
if (h.document.getRoot(n))
return;
r.addRoot(n, {
isUndoable: !1,
data: o
});
const m = this.el.querySelector("[data-cke-editable-content]"), b = a.view.createEditable(n, m);
a.addEditable(b), c.view.forceRender(), s && A(s, r, n);
});
}
/**
* Destroys the editable component. Unmounts root from the editor.
*/
async destroyed() {
const { editorId: t, rootName: e } = this.attrs;
this.el.style.display = "none", await this.mountedPromise, this.mountedPromise = null, await l.the.execute(t, (n) => {
const o = n.model.document.getRoot(e);
o && (n.detachEditable(o), n.detachRoot(e, !1));
});
}
}
const I = E(P);
function A(i, t, e) {
const n = () => {
i.value = t.getData({ rootName: e });
};
t.model.document.on("change:data", f(100, n)), n();
}
function k(i) {
return ["inline", "classic", "balloon", "decoupled"].includes(i);
}
async function C(i) {
const t = await import("ckeditor5"), n = {
inline: t.InlineEditor,
balloon: t.BalloonEditor,
classic: t.ClassicEditor,
decoupled: t.DecoupledEditor,
multiroot: t.MultiRootEditor
}[i];
if (!n)
throw new Error(`Unsupported editor type: ${i}`);
return n;
}
async function V(i) {
const t = await import("ckeditor5");
let e = null;
const n = i.map(async (o) => {
const { [o]: s } = t;
if (s)
return s;
if (!e)
try {
e = await import("ckeditor5-premium-features");
} catch (a) {
console.error(`Failed to load premium package: ${a}`);
}
const { [o]: r } = e || {};
if (r)
return r;
throw new Error(`Plugin "${o}" not found in base or premium packages.`);
});
return Promise.all(n);
}
function g(i) {
const t = document.querySelectorAll(
[
`[data-cke-editor-id="${i}"][data-cke-editable-root-name]`,
"[data-cke-editable-root-name]:not([data-cke-editor-id])"
].join(", ")
);
return Array.from(t).reduce((e, n) => {
const o = n.getAttribute("data-cke-editable-root-name"), s = n.getAttribute("data-cke-editable-initial-value") || "", r = n.querySelector("[data-cke-editable-content]");
return !o || !r ? e : {
...e,
[o]: {
content: r,
initialValue: s
}
};
}, /* @__PURE__ */ Object.create({}));
}
const y = ["inline", "classic", "balloon", "decoupled", "multiroot"];
function H(i) {
const t = i.getAttribute("cke-preset");
if (!t)
throw new Error('CKEditor5 hook requires a "cke-preset" attribute on the element.');
const { type: e, config: n, license: o } = JSON.parse(t);
if (!e || !n || !o)
throw new Error('CKEditor5 hook configuration must include "editor", "config", and "license" properties.');
if (!y.includes(e))
throw new Error(`Invalid editor type: ${e}. Must be one of: ${y.join(", ")}.`);
return {
type: e,
config: n,
license: o
};
}
function R(i, t) {
const { editing: e } = i;
e.view.change((n) => {
n.setStyle("height", `${t}px`, e.view.document.getRoot());
});
}
class $ extends p {
/**
* The name of the hook.
*/
editorPromise = null;
/**
* Attributes for the editor instance.
*/
get attrs() {
const t = {
editorId: this.el.getAttribute("id"),
preset: H(this.el),
editableHeight: w(this.el.getAttribute("cke-editable-height")),
changeEvent: this.el.getAttribute("cke-change-event") !== null,
saveDebounceMs: w(this.el.getAttribute("cke-save-debounce-ms")) ?? 400
};
return Object.defineProperty(this, "attrs", {
value: t,
writable: !1,
configurable: !1,
enumerable: !0
}), t;
}
/**
* Mounts the editor component.
*/
async mounted() {
return this.editorPromise = this.createEditor(), l.the.register(this.attrs.editorId, await this.editorPromise), this;
}
/**
* Destroys the editor instance when the component is destroyed.
* This is important to prevent memory leaks and ensure that the editor is properly cleaned up.
*/
async destroyed() {
this.el.style.display = "none", (await this.editorPromise)?.destroy(), this.editorPromise = null, l.the.unregister(this.attrs.editorId);
}
/**
* Creates the CKEditor instance.
*/
async createEditor() {
const { preset: t, editorId: e, editableHeight: n, changeEvent: o, saveDebounceMs: s } = this.attrs, { type: r, license: a, config: { plugins: c, ...h } } = t, m = await C(r), b = O(e, r), u = await m.create(
b,
{
...h,
initialData: T(e, r),
licenseKey: a.key,
plugins: await V(c)
}
);
if (o && this.setupContentPush(e, u, s), this.handleEvent("ckeditor5:set-data", ({ data: d }) => {
u.setData(d);
}), k(r)) {
const d = document.getElementById(`${e}_input`);
d && D(d, u, s), n && R(u, n);
}
return u;
}
/**
* Setups the content push event for the editor.
*/
setupContentPush(t, e, n) {
const o = () => {
this.pushEvent(
"ckeditor5:change",
{
editorId: t,
data: S(e)
}
);
};
e.model.document.on("change:data", f(n, o)), o();
}
}
function S(i) {
return i.model.document.getRootNames().reduce((e, n) => (e[n] = i.getData({ rootName: n }), e), /* @__PURE__ */ Object.create({}));
}
function D(i, t, e) {
const n = () => {
const o = t.getData();
i.value = o, i.dispatchEvent(new Event("input", { bubbles: !0 }));
};
t.model.document.on("change:data", f(e, n)), N(i)?.addEventListener("submit", n), n();
}
function N(i) {
return i.closest("form");
}
function O(i, t) {
if (k(t))
return document.getElementById(`${i}_editor`);
const e = g(i);
return v(e, ({ content: n }) => n);
}
function T(i, t) {
if (t === "decoupled") {
const n = g(i).main?.initialValue;
if (n)
return n;
}
if (k(t))
return document.getElementById(i)?.getAttribute("cke-initial-value") || "";
const e = g(i);
return v(e, ({ initialValue: n }) => n);
}
const j = E($);
class x extends p {
/**
* The name of the hook.
*/
mountedPromise = null;
/**
* Attributes for the editable instance.
*/
get attrs() {
const t = {
editorId: this.el.getAttribute("data-cke-editor-id") || null,
name: this.el.getAttribute("data-cke-ui-part-name")
};
return Object.defineProperty(this, "attrs", {
value: t,
writable: !1,
configurable: !1,
enumerable: !0
}), t;
}
/**
* Mounts the editable component.
*/
async mounted() {
const { editorId: t, name: e } = this.attrs;
this.mountedPromise = l.the.execute(t, (n) => {
const { ui: o } = n, s = U(e), r = o.view[s];
if (!r) {
console.error(`Unknown UI part name: "${e}". Supported names are "toolbar" and "menubar".`);
return;
}
this.el.appendChild(r.element);
});
}
/**
* Destroys the editable component. Unmounts root from the editor.
*/
async destroyed() {
this.el.style.display = "none", await this.mountedPromise, this.mountedPromise = null, this.el.innerHTML = "";
}
}
function U(i) {
switch (i) {
case "toolbar":
return "toolbar";
case "menubar":
return "menuBarView";
default:
return null;
}
}
const M = E(x), K = {
CKEditor5: j,
CKEditable: I,
CKUIPart: M
};
export {
l as EditorsRegistry,
K as Hooks
};
//# sourceMappingURL=index.mjs.map