ckeditor5-phoenix 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +2 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.mjs +447 -0
- package/dist/index.mjs.map +1 -0
- package/dist/src/hooks/editable.d.ts +5 -0
- package/dist/src/hooks/editable.d.ts.map +1 -0
- package/dist/src/hooks/editable.test.d.ts +2 -0
- package/dist/src/hooks/editable.test.d.ts.map +1 -0
- package/dist/src/hooks/editor/editor.d.ts +5 -0
- package/dist/src/hooks/editor/editor.d.ts.map +1 -0
- package/dist/src/hooks/editor/editor.test.d.ts +2 -0
- package/dist/src/hooks/editor/editor.test.d.ts.map +1 -0
- package/dist/src/hooks/editor/editors-registry.d.ts +68 -0
- package/dist/src/hooks/editor/editors-registry.d.ts.map +1 -0
- package/dist/src/hooks/editor/editors-registry.test.d.ts +2 -0
- package/dist/src/hooks/editor/editors-registry.test.d.ts.map +1 -0
- package/dist/src/hooks/editor/index.d.ts +2 -0
- package/dist/src/hooks/editor/index.d.ts.map +1 -0
- package/dist/src/hooks/editor/typings.d.ts +63 -0
- package/dist/src/hooks/editor/typings.d.ts.map +1 -0
- package/dist/src/hooks/editor/utils/index.d.ts +7 -0
- package/dist/src/hooks/editor/utils/index.d.ts.map +1 -0
- package/dist/src/hooks/editor/utils/is-single-editing-like-editor.d.ts +9 -0
- package/dist/src/hooks/editor/utils/is-single-editing-like-editor.d.ts.map +1 -0
- package/dist/src/hooks/editor/utils/is-single-editing-like-editor.test.d.ts +2 -0
- package/dist/src/hooks/editor/utils/is-single-editing-like-editor.test.d.ts.map +1 -0
- package/dist/src/hooks/editor/utils/load-editor-constructor.d.ts +9 -0
- package/dist/src/hooks/editor/utils/load-editor-constructor.d.ts.map +1 -0
- package/dist/src/hooks/editor/utils/load-editor-constructor.test.d.ts +2 -0
- package/dist/src/hooks/editor/utils/load-editor-constructor.test.d.ts.map +1 -0
- package/dist/src/hooks/editor/utils/load-editor-plugins.d.ts +12 -0
- package/dist/src/hooks/editor/utils/load-editor-plugins.d.ts.map +1 -0
- package/dist/src/hooks/editor/utils/load-editor-plugins.test.d.ts +2 -0
- package/dist/src/hooks/editor/utils/load-editor-plugins.test.d.ts.map +1 -0
- package/dist/src/hooks/editor/utils/query-all-editor-editables.d.ts +16 -0
- package/dist/src/hooks/editor/utils/query-all-editor-editables.d.ts.map +1 -0
- package/dist/src/hooks/editor/utils/query-all-editor-editables.test.d.ts +2 -0
- package/dist/src/hooks/editor/utils/query-all-editor-editables.test.d.ts.map +1 -0
- package/dist/src/hooks/editor/utils/read-preset-or-throw.d.ts +9 -0
- package/dist/src/hooks/editor/utils/read-preset-or-throw.d.ts.map +1 -0
- package/dist/src/hooks/editor/utils/read-preset-or-throw.test.d.ts +2 -0
- package/dist/src/hooks/editor/utils/read-preset-or-throw.test.d.ts.map +1 -0
- package/dist/src/hooks/editor/utils/set-editor-editable-height.d.ts +9 -0
- package/dist/src/hooks/editor/utils/set-editor-editable-height.d.ts.map +1 -0
- package/dist/src/hooks/editor/utils/set-editor-editable-height.test.d.ts +2 -0
- package/dist/src/hooks/editor/utils/set-editor-editable-height.test.d.ts.map +1 -0
- package/dist/src/hooks/index.d.ts +6 -0
- package/dist/src/hooks/index.d.ts.map +1 -0
- package/dist/src/hooks/ui-part.d.ts +5 -0
- package/dist/src/hooks/ui-part.d.ts.map +1 -0
- package/dist/src/hooks/ui-part.test.d.ts +2 -0
- package/dist/src/hooks/ui-part.test.d.ts.map +1 -0
- package/dist/src/index.d.ts +3 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/shared/debounce.d.ts +2 -0
- package/dist/src/shared/debounce.d.ts.map +1 -0
- package/dist/src/shared/debounce.test.d.ts +2 -0
- package/dist/src/shared/debounce.test.d.ts.map +1 -0
- package/dist/src/shared/hook.d.ts +71 -0
- package/dist/src/shared/hook.d.ts.map +1 -0
- package/dist/src/shared/hook.test.d.ts +2 -0
- package/dist/src/shared/hook.test.d.ts.map +1 -0
- package/dist/src/shared/index.d.ts +5 -0
- package/dist/src/shared/index.d.ts.map +1 -0
- package/dist/src/shared/map-object-values.d.ts +11 -0
- package/dist/src/shared/map-object-values.d.ts.map +1 -0
- package/dist/src/shared/map-object-values.test.d.ts +2 -0
- package/dist/src/shared/map-object-values.test.d.ts.map +1 -0
- package/dist/src/shared/parse-int-if-not-null.d.ts +2 -0
- package/dist/src/shared/parse-int-if-not-null.d.ts.map +1 -0
- package/dist/src/shared/parse-int-if-not-null.test.d.ts +2 -0
- package/dist/src/shared/parse-int-if-not-null.test.d.ts.map +1 -0
- package/dist/src/types/index.d.ts +2 -0
- package/dist/src/types/index.d.ts.map +1 -0
- package/dist/src/types/required-by.type.d.ts +2 -0
- package/dist/src/types/required-by.type.d.ts.map +1 -0
- package/dist/test-utils/editor/create-editable-html-element.d.ts +12 -0
- package/dist/test-utils/editor/create-editable-html-element.d.ts.map +1 -0
- package/dist/test-utils/editor/create-editor-html-element.d.ts +13 -0
- package/dist/test-utils/editor/create-editor-html-element.d.ts.map +1 -0
- package/dist/test-utils/editor/create-editor-preset.d.ts +15 -0
- package/dist/test-utils/editor/create-editor-preset.d.ts.map +1 -0
- package/dist/test-utils/editor/create-ui-part-html-element.d.ts +6 -0
- package/dist/test-utils/editor/create-ui-part-html-element.d.ts.map +1 -0
- package/dist/test-utils/editor/get-test-editor-input.d.ts +5 -0
- package/dist/test-utils/editor/get-test-editor-input.d.ts.map +1 -0
- package/dist/test-utils/editor/index.d.ts +8 -0
- package/dist/test-utils/editor/index.d.ts.map +1 -0
- package/dist/test-utils/editor/is-editor-shown.d.ts +5 -0
- package/dist/test-utils/editor/is-editor-shown.d.ts.map +1 -0
- package/dist/test-utils/editor/wait-for-test-editor.d.ts +7 -0
- package/dist/test-utils/editor/wait-for-test-editor.d.ts.map +1 -0
- package/dist/test-utils/html.d.ts +28 -0
- package/dist/test-utils/html.d.ts.map +1 -0
- package/dist/test-utils/html.test.d.ts +2 -0
- package/dist/test-utils/html.test.d.ts.map +1 -0
- package/dist/test-utils/index.d.ts +3 -0
- package/dist/test-utils/index.d.ts.map +1 -0
- package/dist/vite.config.d.ts +3 -0
- package/dist/vite.config.d.ts.map +1 -0
- package/package.json +44 -0
- package/src/hooks/editable.test.ts +241 -0
- package/src/hooks/editable.ts +113 -0
- package/src/hooks/editor/editor.test.ts +333 -0
- package/src/hooks/editor/editor.ts +179 -0
- package/src/hooks/editor/editors-registry.test.ts +238 -0
- package/src/hooks/editor/editors-registry.ts +154 -0
- package/src/hooks/editor/index.ts +1 -0
- package/src/hooks/editor/typings.ts +72 -0
- package/src/hooks/editor/utils/index.ts +6 -0
- package/src/hooks/editor/utils/is-single-editing-like-editor.test.ts +40 -0
- package/src/hooks/editor/utils/is-single-editing-like-editor.ts +11 -0
- package/src/hooks/editor/utils/load-editor-constructor.test.ts +62 -0
- package/src/hooks/editor/utils/load-editor-constructor.ts +27 -0
- package/src/hooks/editor/utils/load-editor-plugins.test.ts +46 -0
- package/src/hooks/editor/utils/load-editor-plugins.ts +50 -0
- package/src/hooks/editor/utils/query-all-editor-editables.test.ts +140 -0
- package/src/hooks/editor/utils/query-all-editor-editables.ts +47 -0
- package/src/hooks/editor/utils/read-preset-or-throw.test.ts +162 -0
- package/src/hooks/editor/utils/read-preset-or-throw.ts +33 -0
- package/src/hooks/editor/utils/set-editor-editable-height.test.ts +131 -0
- package/src/hooks/editor/utils/set-editor-editable-height.ts +15 -0
- package/src/hooks/index.ts +9 -0
- package/src/hooks/ui-part.test.ts +151 -0
- package/src/hooks/ui-part.ts +89 -0
- package/src/index.ts +2 -0
- package/src/shared/debounce.test.ts +72 -0
- package/src/shared/debounce.ts +16 -0
- package/src/shared/hook.test.ts +131 -0
- package/src/shared/hook.ts +141 -0
- package/src/shared/index.ts +4 -0
- package/src/shared/map-object-values.test.ts +29 -0
- package/src/shared/map-object-values.ts +19 -0
- package/src/shared/parse-int-if-not-null.test.ts +25 -0
- package/src/shared/parse-int-if-not-null.ts +9 -0
- package/src/types/index.ts +1 -0
- package/src/types/required-by.type.ts +1 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use strict";var P=Object.create;var E=Object.defineProperty;var v=Object.getOwnPropertyDescriptor;var I=Object.getOwnPropertyNames;var A=Object.getPrototypeOf,H=Object.prototype.hasOwnProperty;var S=(o,t,e,i)=>{if(t&&typeof t=="object"||typeof t=="function")for(let n of I(t))!H.call(o,n)&&n!==e&&E(o,n,{get:()=>t[n],enumerable:!(i=v(t,n))||i.enumerable});return o};var m=(o,t,e)=>(e=o!=null?P(A(o)):{},S(t||!o||!o.__esModule?E(e,"default",{value:o,enumerable:!0}):e,o));Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});function k(o,t){let e=null;return(...i)=>{e&&clearTimeout(e),e=setTimeout(()=>{t(...i)},o)}}class f{el;liveSocket;pushEvent;pushEventTo;handleEvent}function p(o){return{mounted(){const t=new o;this.el.instance=t,t.el=this.el,t.liveSocket=this.liveSocket,t.pushEvent=(e,i,n)=>this.pushEvent(e,i,n),t.pushEventTo=(e,i,n,r)=>this.pushEventTo(e,i,n,r),t.handleEvent=(e,i)=>this.handleEvent(e,i),t.mounted?.()},beforeUpdate(){this.el.instance.beforeUpdate?.()},destroyed(){this.el.instance.destroyed?.()},disconnected(){this.el.instance.disconnected?.()},reconnected(){this.el.instance.reconnected?.()}}}function w(o,t){const e=Object.entries(o).map(([i,n])=>[i,t(n,i)]);return Object.fromEntries(e)}function V(o){if(o===null)return null;const t=Number.parseInt(o,10);return Number.isNaN(t)?null:t}class l{static the=new l;editors=new Map;callbacks=new Map;constructor(){}execute(t,e){const{callbacks:i,editors:n}=this,r=n.get(t);return r?Promise.resolve(e(r)):new Promise(s=>{const a=async c=>s(await e(c));this.callbacks.has(t)||i.set(t,[]),i.set(t,[...i.get(t),a])})}register(t,e){const{editors:i,callbacks:n}=this,r=n.get(t);if(i.has(t))throw new Error(`Editor with ID "${t}" is already registered.`);i.set(t,e),r&&(r.forEach(s=>s(e)),n.delete(t)),this.editors.size===1&&this.register(null,e)}unregister(t){const{editors:e,callbacks:i}=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),i.delete(t)}getEditors(){return Array.from(this.editors.values())}hasEditor(t){return this.editors.has(t)}waitForEditor(t){return this.execute(t,e=>e)}async destroyAllEditors(){const t=Array.from(this.editors.values()).map(e=>e.destroy());this.editors.clear(),this.callbacks.clear(),await Promise.all(t)}}class $ extends f{mountedPromise=null;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}async mounted(){const{editableId:t,editorId:e,rootName:i,initialValue:n}=this.attrs,r=this.el.querySelector(`#${t}_input`);this.mountedPromise=l.the.execute(e,s=>{const{ui:a,editing:c,model:h}=s;if(h.document.getRoot(i))return;s.addRoot(i,{isUndoable:!1,data:n});const u=this.el.querySelector("[data-cke-editable-content]"),d=a.view.createEditable(i,u);a.addEditable(d),c.view.forceRender(),r&&R(r,s,i)})}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,i=>{const n=i.model.document.getRoot(e);n&&(i.detachEditable(n),i.detachRoot(e,!1))})}}const C=p($);function R(o,t,e){const i=()=>{o.value=t.getData({rootName:e})};t.model.document.on("change:data",k(100,i)),i()}function g(o){return["inline","classic","balloon","decoupled"].includes(o)}async function T(o){const t=await import("ckeditor5"),i={inline:t.InlineEditor,balloon:t.BalloonEditor,classic:t.ClassicEditor,decoupled:t.DecoupledEditor,multiroot:t.MultiRootEditor}[o];if(!i)throw new Error(`Unsupported editor type: ${o}`);return i}async function N(o){const t=await import("ckeditor5");let e=null;const i=o.map(async n=>{const{[n]:r}=t;if(r)return r;if(!e)try{e=await import("ckeditor5-premium-features")}catch(a){console.error(`Failed to load premium package: ${a}`)}const{[n]:s}=e||{};if(s)return s;throw new Error(`Plugin "${n}" not found in base or premium packages.`)});return Promise.all(i)}function b(o){const t=document.querySelectorAll([`[data-cke-editor-id="${o}"][data-cke-editable-root-name]`,"[data-cke-editable-root-name]:not([data-cke-editor-id])"].join(", "));return Array.from(t).reduce((e,i)=>{const n=i.getAttribute("data-cke-editable-root-name"),r=i.getAttribute("data-cke-editable-initial-value")||"",s=i.querySelector("[data-cke-editable-content]");return!n||!s?e:{...e,[n]:{content:s,initialValue:r}}},Object.create({}))}const y=["inline","classic","balloon","decoupled","multiroot"];function O(o){const t=o.getAttribute("cke-preset");if(!t)throw new Error('CKEditor5 hook requires a "cke-preset" attribute on the element.');const{type:e,config:i,license:n}=JSON.parse(t);if(!e||!i||!n)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:i,license:n}}function j(o,t){const{editing:e}=o;e.view.change(i=>{i.setStyle("height",`${t}px`,e.view.document.getRoot())})}class U extends f{editorPromise=null;get attrs(){const t={editorId:this.el.getAttribute("id"),preset:O(this.el),editableHeight:V(this.el.getAttribute("cke-editable-height"))};return Object.defineProperty(this,"attrs",{value:t,writable:!1,configurable:!1,enumerable:!0}),t}async mounted(){this.editorPromise=this.createEditor(),l.the.register(this.attrs.editorId,await this.editorPromise)}async destroyed(){this.el.style.display="none",(await this.editorPromise)?.destroy(),this.editorPromise=null,l.the.unregister(this.attrs.editorId)}createEditor=async()=>{const{preset:t,editorId:e,editableHeight:i}=this.attrs,{type:n,license:r,config:{plugins:s,...a}}=t,c=await T(n),h=M(e,n),u=await c.create(h,{...a,initialData:D(e,n),licenseKey:r.key,plugins:await N(s)});if(g(n)){const d=document.getElementById(`${e}_input`);d&&x(d,u),i&&j(u,i)}return u}}function x(o,t){const e=()=>{o.value=t.getData()};t.model.document.on("change:data",k(250,e)),e()}function M(o,t){if(g(t))return document.getElementById(`${o}_editor`);const e=b(o);return w(e,({content:i})=>i)}function D(o,t){if(t==="decoupled"){const i=b(o).main?.initialValue;if(i)return i}if(g(t))return document.getElementById(o)?.getAttribute("cke-initial-value")||"";const e=b(o);return w(e,({initialValue:i})=>i)}const K=p(U);class q extends f{mountedPromise=null;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}async mounted(){const{editorId:t,name:e}=this.attrs;this.mountedPromise=l.the.execute(t,i=>{const{ui:n}=i,r=B(e),s=n.view[r];if(!s){console.error(`Unknown UI part name: "${e}". Supported names are "toolbar" and "menubar".`);return}this.el.appendChild(s.element)})}async destroyed(){this.el.style.display="none",await this.mountedPromise,this.mountedPromise=null,this.el.innerHTML=""}}function B(o){switch(o){case"toolbar":return"toolbar";case"menubar":return"menuBarView";default:return null}}const F=p(q),_={CKEditor5:K,CKEditable:C,CKUIPart:F};exports.EditorsRegistry=l;exports.Hooks=_;
|
|
2
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.cjs","sources":["../src/shared/debounce.ts","../src/shared/hook.ts","../src/shared/map-object-values.ts","../src/shared/parse-int-if-not-null.ts","../src/hooks/editor/editors-registry.ts","../src/hooks/editable.ts","../src/hooks/editor/utils/is-single-editing-like-editor.ts","../src/hooks/editor/utils/load-editor-constructor.ts","../src/hooks/editor/utils/load-editor-plugins.ts","../src/hooks/editor/utils/query-all-editor-editables.ts","../src/hooks/editor/typings.ts","../src/hooks/editor/utils/read-preset-or-throw.ts","../src/hooks/editor/utils/set-editor-editable-height.ts","../src/hooks/editor/editor.ts","../src/hooks/ui-part.ts","../src/hooks/index.ts"],"sourcesContent":["export function debounce<T extends (...args: any[]) => any>(\n delay: number,\n callback: T,\n): (...args: Parameters<T>) => void {\n let timeoutId: ReturnType<typeof setTimeout> | null = null;\n\n return (...args: Parameters<T>): void => {\n if (timeoutId) {\n clearTimeout(timeoutId);\n }\n\n timeoutId = setTimeout(() => {\n callback(...args);\n }, delay);\n };\n}\n","import type { Hook, LiveSocket } from 'phoenix_live_view';\n\nimport type { RequiredBy } from '../types';\n\n/**\n * An abstract class that provides a class-based API for creating Phoenix LiveView hooks.\n *\n * This class defines the structure and lifecycle methods of a hook, which can be extended\n * to implement custom client-side behavior that integrates with LiveView.\n */\nexport abstract class ClassHook {\n /**\n * The DOM element the hook is attached to.\n * It includes an `instance` property to hold the hook instance.\n */\n el: HTMLElement & { instance: Hook; };\n\n /**\n * The LiveView socket instance, providing connection to the server.\n */\n liveSocket: LiveSocket;\n\n /**\n * Pushes an event from the client to the LiveView server process.\n * @param _event The name of the event.\n * @param _payload The data to send with the event.\n * @param _callback An optional function to be called with the server's reply.\n */\n pushEvent!: (\n _event: string,\n _payload: any,\n _callback?: (reply: any, ref: number) => void,\n ) => void;\n\n /**\n * Pushes an event to another hook on the page.\n * @param _selector The CSS selector of the target element with the hook.\n * @param _event The name of the event.\n * @param _payload The data to send with the event.\n * @param _callback An optional function to be called with the reply.\n */\n pushEventTo!: (\n _selector: string,\n _event: string,\n _payload: any,\n _callback?: (reply: any, ref: number) => void,\n ) => void;\n\n /**\n * Registers a handler for an event pushed from the server.\n * @param _event The name of the event to handle.\n * @param _callback The function to execute when the event is received.\n */\n handleEvent!: (\n _event: string,\n _callback: (payload: any) => void,\n ) => void;\n\n /**\n * Called when the hook has been mounted to the DOM.\n * This is the ideal place for initialization code.\n */\n abstract mounted(): void;\n\n /**\n * Called when the element has been removed from the DOM.\n * Perfect for cleanup tasks.\n */\n abstract destroyed(): void;\n\n /**\n * Called before the element is updated by a LiveView patch.\n */\n beforeUpdate?(): void;\n\n /**\n * Called when the client has disconnected from the server.\n */\n disconnected?(): void;\n\n /**\n * Called when the client has reconnected to the server.\n */\n reconnected?(): void;\n}\n\n/**\n * A factory function that adapts a class-based hook to the object-based API expected by Phoenix LiveView.\n *\n * @param constructor The constructor of the class that extends the `Hook` abstract class.\n */\nexport function makeHook(constructor: new () => ClassHook): RequiredBy<Hook<any>, 'mounted' | 'destroyed'> {\n return {\n /**\n * The mounted lifecycle callback for the LiveView hook object.\n * It creates an instance of the user-defined hook class and sets up the necessary properties and methods.\n */\n mounted(this: any) {\n const instance = new constructor();\n\n this.el.instance = instance;\n\n instance.el = this.el;\n instance.liveSocket = this.liveSocket;\n\n instance.pushEvent = (event, payload, callback) => this.pushEvent(event, payload, callback);\n instance.pushEventTo = (selector, event, payload, callback) => this.pushEventTo(selector, event, payload, callback);\n instance.handleEvent = (event, callback) => this.handleEvent(event, callback);\n\n instance.mounted?.();\n },\n\n /**\n * The beforeUpdate lifecycle callback that delegates to the hook instance.\n */\n beforeUpdate(this: any) {\n this.el.instance.beforeUpdate?.();\n },\n\n /**\n * The destroyed lifecycle callback that delegates to the hook instance.\n */\n destroyed(this: any) {\n this.el.instance.destroyed?.();\n },\n\n /**\n * The disconnected lifecycle callback that delegates to the hook instance.\n */\n disconnected(this: any) {\n this.el.instance.disconnected?.();\n },\n\n /**\n * The reconnected lifecycle callback that delegates to the hook instance.\n */\n reconnected(this: any) {\n this.el.instance.reconnected?.();\n },\n };\n}\n","/**\n * Maps the values of an object using a provided mapper function.\n *\n * @param obj The object whose values will be mapped.\n * @param mapper A function that takes a value and its key, and returns a new value.\n * @template T The type of the original values in the object.\n * @template U The type of the new values in the object.\n * @returns A new object with the same keys as the original, but with values transformed by\n */\nexport function mapObjectValues<T, U>(\n obj: Record<string, T>,\n mapper: (value: T, key: string) => U,\n): Record<string, U> {\n const mappedEntries = Object\n .entries(obj)\n .map(([key, value]) => [key, mapper(value, key)] as const);\n\n return Object.fromEntries(mappedEntries);\n}\n","export function parseIntIfNotNull(value: string | null): number | null {\n if (value === null) {\n return null;\n }\n\n const parsed = Number.parseInt(value, 10);\n\n return Number.isNaN(parsed) ? null : parsed;\n}\n","import type { Editor } from 'ckeditor5';\n\nimport type { EditorId } from './typings';\n\n/**\n * Allows other hooks to communicate with specific editors.\n * It provides a way to register editors and execute callbacks on them when they are available.\n */\nexport class EditorsRegistry {\n static readonly the = new EditorsRegistry();\n\n /**\n * Map of registered editors.\n */\n private readonly editors = new Map<EditorId | null, Editor>();\n\n /**\n * Map of callbacks that are waiting for an editor to be registered.\n */\n private readonly callbacks = new Map<EditorId | null, EditorCallback<any>[]>();\n\n /**\n * Private constructor to enforce singleton pattern.\n */\n private constructor() {}\n\n /**\n * Executes a function on an editor.\n * If the editor is not yet registered, it will wait for it to be registered.\n *\n * @param editorId The ID of the editor.\n * @param fn The function to execute.\n * @returns A promise that resolves with the result of the function.\n */\n execute<T, E extends Editor = Editor>(editorId: EditorId | null, fn: (editor: E) => T): Promise<Awaited<T>> {\n const { callbacks, editors } = this;\n const editor = editors.get(editorId);\n\n if (editor) {\n return Promise.resolve(fn(editor as E));\n }\n\n return new Promise((resolve) => {\n const callback = async (editor: E) => resolve(await fn(editor));\n\n if (!this.callbacks.has(editorId)) {\n callbacks.set(editorId, []);\n }\n\n callbacks.set(editorId, [\n ...callbacks.get(editorId)!,\n callback,\n ]);\n });\n }\n\n /**\n * Registers an editor.\n *\n * @param editorId The ID of the editor.\n * @param editor The editor instance.\n */\n register(editorId: EditorId | null, editor: Editor): void {\n const { editors, callbacks } = this;\n const callbacksForEditor = callbacks.get(editorId);\n\n if (editors.has(editorId)) {\n throw new Error(`Editor with ID \"${editorId}\" is already registered.`);\n }\n\n editors.set(editorId, editor);\n\n if (callbacksForEditor) {\n callbacksForEditor.forEach(callback => callback(editor));\n callbacks.delete(editorId);\n }\n\n // Register the first editor as the default editor.\n // This is useful for editables that do not specify an editor ID.\n if (this.editors.size === 1) {\n this.register(null, editor);\n }\n }\n\n /**\n * Un-registers an editor.\n *\n * @param editorId The ID of the editor.\n */\n unregister(editorId: EditorId | null): void {\n const { editors, callbacks } = this;\n\n if (!editors.has(editorId)) {\n throw new Error(`Editor with ID \"${editorId}\" is not registered.`);\n }\n\n if (editorId && this.editors.get(null) === editors.get(editorId)) {\n this.unregister(null);\n }\n\n editors.delete(editorId);\n callbacks.delete(editorId);\n }\n\n /**\n * Gets all registered editors.\n */\n getEditors(): Editor[] {\n return Array.from(this.editors.values());\n }\n\n /**\n * Checks if an editor with the given ID is registered.\n *\n * @param editorId The ID of the editor.\n * @returns `true` if the editor is registered, `false` otherwise.\n */\n hasEditor(editorId: EditorId | null): boolean {\n return this.editors.has(editorId);\n }\n\n /**\n * Gets a promise that resolves with the editor instance for the given ID.\n * If the editor is not registered yet, it will wait for it to be registered.\n *\n * @param editorId The ID of the editor.\n * @returns A promise that resolves with the editor instance.\n */\n waitForEditor<E extends Editor>(editorId: EditorId | null): Promise<E> {\n return this.execute(editorId, editor => editor as E);\n }\n\n /**\n * Destroys all registered editors and clears the registry.\n * This will call the `destroy` method on each editor.\n */\n async destroyAllEditors() {\n const promises = (\n Array\n .from(this.editors.values())\n .map(editor => editor.destroy())\n );\n\n this.editors.clear();\n this.callbacks.clear();\n\n await Promise.all(promises);\n }\n}\n\n/**\n * Callback type for editor operations.\n */\ntype EditorCallback<E extends Editor = Editor> = (editor: E) => void;\n","import type { MultiRootEditor } from 'ckeditor5';\n\nimport { ClassHook, debounce, makeHook } from '../shared';\nimport { EditorsRegistry } from './editor/editors-registry';\n\n/**\n * Editable hook for Phoenix LiveView. It allows you to create editables for multi-root editors.\n */\nclass EditableHookImpl extends ClassHook {\n /**\n * The name of the hook.\n */\n private mountedPromise: Promise<void> | null = null;\n\n /**\n * Attributes for the editable instance.\n */\n private get attrs() {\n const value = {\n editableId: this.el.getAttribute('id')!,\n editorId: this.el.getAttribute('data-cke-editor-id') || null,\n rootName: this.el.getAttribute('data-cke-editable-root-name')!,\n initialValue: this.el.getAttribute('data-cke-editable-initial-value') || '',\n };\n\n Object.defineProperty(this, 'attrs', {\n value,\n writable: false,\n configurable: false,\n enumerable: true,\n });\n\n return value;\n }\n\n /**\n * Mounts the editable component.\n */\n override async mounted() {\n const { editableId, editorId, rootName, initialValue } = this.attrs;\n const input = this.el.querySelector<HTMLInputElement>(`#${editableId}_input`);\n\n // If the editor is not registered yet, we will wait for it to be registered.\n this.mountedPromise = EditorsRegistry.the.execute(editorId, (editor: MultiRootEditor) => {\n const { ui, editing, model } = editor;\n\n if (model.document.getRoot(rootName)) {\n return;\n }\n\n editor.addRoot(rootName, {\n isUndoable: false,\n data: initialValue,\n });\n\n const contentElement = this.el.querySelector('[data-cke-editable-content]') as HTMLElement | null;\n const editable = ui.view.createEditable(rootName, contentElement!);\n\n ui.addEditable(editable);\n editing.view.forceRender();\n\n if (input) {\n syncEditorRootToInput(input, editor, rootName);\n }\n });\n }\n\n /**\n * Destroys the editable component. Unmounts root from the editor.\n */\n override async destroyed() {\n const { editorId, rootName } = this.attrs;\n\n // Let's hide the element during destruction to prevent flickering.\n this.el.style.display = 'none';\n\n // Let's wait for the mounted promise to resolve before proceeding with destruction.\n await this.mountedPromise;\n this.mountedPromise = null;\n\n // Unmount root from the editor.\n await EditorsRegistry.the.execute(editorId, (editor: MultiRootEditor) => {\n const root = editor.model.document.getRoot(rootName);\n\n if (root) {\n editor.detachEditable(root);\n editor.detachRoot(rootName, false);\n }\n });\n }\n}\n\n/**\n * Phoenix LiveView hook for CKEditor 5 editable elements.\n */\nexport const EditableHook = makeHook(EditableHookImpl);\n\n/**\n * Synchronizes the editor's root data to the corresponding input element.\n * This is used to keep the input value in sync with the editor's content.\n *\n * @param input - The input element to synchronize with the editor.\n * @param editor - The CKEditor instance.\n * @param rootName - The name of the root to synchronize.\n */\nfunction syncEditorRootToInput(input: HTMLInputElement, editor: MultiRootEditor, rootName: string) {\n const sync = () => {\n input.value = editor.getData({ rootName });\n };\n\n editor.model.document.on('change:data', debounce(100, sync));\n sync();\n}\n","import type { EditorType } from '../typings';\n\n/**\n * Checks if the given editor type is one of the single editing-like editors.\n *\n * @param editorType - The type of the editor to check.\n * @returns `true` if the editor type is 'inline', 'classic', or 'balloon', otherwise `false`.\n */\nexport function isSingleEditingLikeEditor(editorType: EditorType): boolean {\n return ['inline', 'classic', 'balloon', 'decoupled'].includes(editorType);\n}\n","import type { EditorType } from '../typings';\n\n/**\n * Returns the constructor for the specified CKEditor5 editor type.\n *\n * @param type - The type of the editor to load.\n * @returns A promise that resolves to the editor constructor.\n */\nexport async function loadEditorConstructor(type: EditorType) {\n const PKG = await import('ckeditor5');\n\n const editorMap = {\n inline: PKG.InlineEditor,\n balloon: PKG.BalloonEditor,\n classic: PKG.ClassicEditor,\n decoupled: PKG.DecoupledEditor,\n multiroot: PKG.MultiRootEditor,\n } as const;\n\n const EditorConstructor = editorMap[type];\n\n if (!EditorConstructor) {\n throw new Error(`Unsupported editor type: ${type}`);\n }\n\n return EditorConstructor;\n}\n","import type { PluginConstructor } from 'ckeditor5';\n\nimport type { EditorPlugin } from '../typings';\n\n/**\n * Loads CKEditor plugins from base and premium packages.\n * First tries to load from the base 'ckeditor5' package, then falls back to 'ckeditor5-premium-features'.\n *\n * @param plugins - Array of plugin names to load\n * @returns Promise that resolves to an array of loaded Plugin instances\n * @throws Error if a plugin is not found in either package\n */\nexport async function loadEditorPlugins(plugins: EditorPlugin[]): Promise<PluginConstructor[]> {\n const basePackage: Record<string, any> = await import('ckeditor5');\n let premiumPackage: Record<string, any> | null = null;\n\n const loaders = plugins.map(async (plugin) => {\n // Let's first try to load the plugin from the base package.\n // Coverage is disabled due to Vitest issues with mocking dynamic imports.\n\n /* v8 ignore start */\n const { [plugin]: basePkgImport } = basePackage;\n\n if (basePkgImport) {\n return basePkgImport as PluginConstructor;\n }\n\n // Plugin not found in base package, try premium package.\n if (!premiumPackage) {\n try {\n premiumPackage = await import('ckeditor5-premium-features');\n }\n catch (error) {\n console.error(`Failed to load premium package: ${error}`);\n }\n }\n\n const { [plugin]: premiumPkgImport } = premiumPackage || {};\n\n if (premiumPkgImport) {\n return premiumPkgImport as PluginConstructor;\n }\n\n // Plugin not found in either package, throw an error.\n throw new Error(`Plugin \"${plugin}\" not found in base or premium packages.`);\n /* v8 ignore end */\n });\n\n return Promise.all(loaders);\n}\n","import type { EditorId } from '../typings';\n\n/**\n * Queries all editable elements within a specific editor instance.\n *\n * @param editorId The ID of the editor to query.\n * @returns An object mapping editable names to their corresponding elements and initial values.\n */\nexport function queryAllEditorEditables(editorId: EditorId): Record<string, EditableItem> {\n const iterator = document.querySelectorAll<HTMLElement>(\n [\n `[data-cke-editor-id=\"${editorId}\"][data-cke-editable-root-name]`,\n '[data-cke-editable-root-name]:not([data-cke-editor-id])',\n ]\n .join(', '),\n );\n\n return (\n Array\n .from(iterator)\n .reduce<Record<string, EditableItem>>((acc, element) => {\n const name = element.getAttribute('data-cke-editable-root-name');\n const initialValue = element.getAttribute('data-cke-editable-initial-value') || '';\n const content = element.querySelector('[data-cke-editable-content]') as HTMLElement;\n\n if (!name || !content) {\n return acc;\n }\n\n return {\n ...acc,\n [name]: {\n content,\n initialValue,\n },\n };\n }, Object.create({}))\n );\n}\n\n/**\n * Type representing an editable item within an editor.\n */\nexport type EditableItem = {\n content: HTMLElement;\n initialValue: string;\n};\n","/**\n * List of supported CKEditor5 editor types.\n */\nexport const EDITOR_TYPES = ['inline', 'classic', 'balloon', 'decoupled', 'multiroot'] as const;\n\n/**\n * Represents a unique identifier for a CKEditor5 editor instance.\n * This is typically the ID of the HTML element that the editor is attached to.\n */\nexport type EditorId = string;\n\n/**\n * Defines editor type supported by CKEditor5. It must match list of available\n * editor types specified in `preset/parser.ex` file.\n */\nexport type EditorType = (typeof EDITOR_TYPES)[number];\n\n/**\n * Represents a CKEditor5 plugin as a string identifier.\n */\nexport type EditorPlugin = string;\n\n/**\n * Configuration object for CKEditor5 editor instance.\n */\nexport type EditorConfig = {\n /**\n * Array of plugin identifiers to be loaded by the editor.\n */\n plugins: EditorPlugin[];\n\n /**\n * Other configuration options are flexible and can be any key-value pairs.\n */\n [key: string]: any;\n};\n\n/**\n * Represents a license key for CKEditor5.\n */\nexport type EditorLicense = {\n key: string;\n};\n\n/**\n * Configuration object for the CKEditor5 hook.\n */\nexport type EditorPreset = {\n /**\n * The type of CKEditor5 editor to use.\n * Must be one of the predefined types: 'inline', 'classic', 'balloon', 'decoupled', or 'multiroot'.\n */\n type: EditorType;\n\n /**\n * The configuration object for the CKEditor5 editor.\n * This should match the configuration expected by CKEditor5.\n */\n config: EditorConfig;\n\n /**\n * The license key for CKEditor5.\n * This is required for using CKEditor5 with a valid license.\n */\n license: EditorLicense;\n\n /**\n * Optional height for the editor, if applicable.\n * This can be used to set a specific height for the editor instance.\n */\n editableHeight?: number;\n};\n","import type { EditorPreset } from '../typings';\n\nimport { EDITOR_TYPES } from '../typings';\n\n/**\n * Reads the hook configuration from the element's attribute and parses it as JSON.\n *\n * @param element - The HTML element that contains the hook configuration.\n * @returns The parsed hook configuration.\n */\nexport function readPresetOrThrow(element: HTMLElement): EditorPreset {\n const attributeValue = element.getAttribute('cke-preset');\n\n if (!attributeValue) {\n throw new Error('CKEditor5 hook requires a \"cke-preset\" attribute on the element.');\n }\n\n const { type, config, license } = JSON.parse(attributeValue);\n\n if (!type || !config || !license) {\n throw new Error('CKEditor5 hook configuration must include \"editor\", \"config\", and \"license\" properties.');\n }\n\n if (!EDITOR_TYPES.includes(type)) {\n throw new Error(`Invalid editor type: ${type}. Must be one of: ${EDITOR_TYPES.join(', ')}.`);\n }\n\n return {\n type,\n config,\n license,\n };\n}\n","import type { Editor } from 'ckeditor5';\n\n/**\n * Sets the height of the editable area in the CKEditor instance.\n *\n * @param instance - The CKEditor instance to modify.\n * @param height - The height in pixels to set for the editable area.\n */\nexport function setEditorEditableHeight(instance: Editor, height: number): void {\n const { editing } = instance;\n\n editing.view.change((writer) => {\n writer.setStyle('height', `${height}px`, editing.view.document.getRoot()!);\n });\n}\n","import type { Editor } from 'ckeditor5';\n\nimport type { EditorId, EditorType } from './typings';\n\nimport {\n debounce,\n mapObjectValues,\n parseIntIfNotNull,\n} from '../../shared';\nimport { ClassHook, makeHook } from '../../shared/hook';\nimport { EditorsRegistry } from './editors-registry';\nimport {\n isSingleEditingLikeEditor,\n loadEditorConstructor,\n loadEditorPlugins,\n queryAllEditorEditables,\n readPresetOrThrow,\n setEditorEditableHeight,\n} from './utils';\n\n/**\n * Editor hook for Phoenix LiveView.\n *\n * This class is a hook that can be used with Phoenix LiveView to integrate\n * the CKEditor 5 WYSIWYG editor.\n */\nclass EditorHookImpl extends ClassHook {\n /**\n * The name of the hook.\n */\n private editorPromise: Promise<Editor> | null = null;\n\n /**\n * Attributes for the editor instance.\n */\n private get attrs() {\n const value = {\n editorId: this.el.getAttribute('id')!,\n preset: readPresetOrThrow(this.el),\n editableHeight: parseIntIfNotNull(this.el.getAttribute('cke-editable-height')),\n };\n\n Object.defineProperty(this, 'attrs', {\n value,\n writable: false,\n configurable: false,\n enumerable: true,\n });\n\n return value;\n }\n\n /**\n * Mounts the editor component.\n */\n override async mounted() {\n this.editorPromise = this.createEditor();\n\n EditorsRegistry.the.register(this.attrs.editorId, await this.editorPromise);\n }\n\n /**\n * Destroys the editor instance when the component is destroyed.\n * This is important to prevent memory leaks and ensure that the editor is properly cleaned up.\n */\n override async destroyed() {\n // Let's hide the element during destruction to prevent flickering.\n this.el.style.display = 'none';\n\n // Let's wait for the mounted promise to resolve before proceeding with destruction.\n (await this.editorPromise)?.destroy();\n this.editorPromise = null;\n\n EditorsRegistry.the.unregister(this.attrs.editorId);\n }\n\n /**\n * Creates the CKEditor instance.\n */\n private createEditor = async () => {\n const { preset, editorId, editableHeight } = this.attrs;\n const { type, license, config: { plugins, ...config } } = preset;\n\n const Constructor = await loadEditorConstructor(type);\n const rootEditables = getInitialRootsContentElements(editorId, type);\n\n const editor = await Constructor.create(\n rootEditables as any,\n {\n ...config,\n initialData: getInitialRootsValues(editorId, type),\n licenseKey: license.key,\n plugins: await loadEditorPlugins(plugins),\n },\n );\n\n if (isSingleEditingLikeEditor(type)) {\n const input = document.getElementById(`${editorId}_input`) as HTMLInputElement | null;\n\n if (input) {\n syncEditorToInput(input, editor);\n }\n\n if (editableHeight) {\n setEditorEditableHeight(editor, editableHeight);\n }\n }\n\n return editor;\n };\n}\n\n/**\n * Synchronizes the editor's content with a hidden input field.\n *\n * @param input The input element to synchronize with the editor.\n * @param editor The CKEditor instance.\n */\nfunction syncEditorToInput(input: HTMLInputElement, editor: Editor) {\n const sync = () => {\n input.value = editor.getData();\n };\n\n editor.model.document.on('change:data', debounce(250, sync));\n sync();\n}\n\n/**\n * Gets the initial root elements for the editor based on its type.\n *\n * @param editorId The editor's ID.\n * @param type The type of the editor.\n * @returns The root element(s) for the editor.\n */\nfunction getInitialRootsContentElements(editorId: EditorId, type: EditorType) {\n if (isSingleEditingLikeEditor(type)) {\n return document.getElementById(`${editorId}_editor`)!;\n }\n\n const editables = queryAllEditorEditables(editorId);\n\n return mapObjectValues(editables, ({ content }) => content);\n}\n\n/**\n * Gets the initial data for the roots of the editor. If the editor is a single editing-like editor,\n * it retrieves the initial value from the element's attribute. Otherwise, it returns an object mapping\n * editable names to their initial values.\n *\n * @param editorId The editor's ID.\n * @param type The type of the editor.\n * @returns The initial values for the editor's roots.\n */\nfunction getInitialRootsValues(editorId: EditorId, type: EditorType) {\n // If the editor is decoupled, the initial value might be specified in the `main` editable.\n if (type === 'decoupled') {\n const mainEditableValue = queryAllEditorEditables(editorId)['main']?.initialValue;\n\n if (mainEditableValue) {\n return mainEditableValue;\n }\n }\n\n // Let's check initial value assigned to the editor element.\n if (isSingleEditingLikeEditor(type)) {\n const initialValue = document.getElementById(editorId)?.getAttribute('cke-initial-value') || '';\n\n return initialValue;\n }\n\n const editables = queryAllEditorEditables(editorId);\n\n return mapObjectValues(editables, ({ initialValue }) => initialValue);\n}\n\n/**\n * Phoenix LiveView hook for CKEditor 5.\n */\nexport const EditorHook = makeHook(EditorHookImpl);\n","import { ClassHook, makeHook } from '../shared';\nimport { EditorsRegistry } from './editor/editors-registry';\n\n/**\n * UI Part hook for Phoenix LiveView. It allows you to create UI parts for multi-root editors.\n */\nclass UIPartHookImpl extends ClassHook {\n /**\n * The name of the hook.\n */\n private mountedPromise: Promise<void> | null = null;\n\n /**\n * Attributes for the editable instance.\n */\n private get attrs() {\n const value = {\n editorId: this.el.getAttribute('data-cke-editor-id') || null,\n name: this.el.getAttribute('data-cke-ui-part-name')!,\n };\n\n Object.defineProperty(this, 'attrs', {\n value,\n writable: false,\n configurable: false,\n enumerable: true,\n });\n\n return value;\n }\n\n /**\n * Mounts the editable component.\n */\n override async mounted() {\n const { editorId, name } = this.attrs;\n\n // If the editor is not registered yet, we will wait for it to be registered.\n this.mountedPromise = EditorsRegistry.the.execute(editorId, (editor) => {\n const { ui } = editor;\n\n const uiViewName = mapUIPartView(name);\n const uiPart = (ui.view as any)[uiViewName!];\n\n if (!uiPart) {\n console.error(`Unknown UI part name: \"${name}\". Supported names are \"toolbar\" and \"menubar\".`);\n return;\n }\n\n this.el.appendChild(uiPart.element);\n });\n }\n\n /**\n * Destroys the editable component. Unmounts root from the editor.\n */\n override async destroyed() {\n // Let's hide the element during destruction to prevent flickering.\n this.el.style.display = 'none';\n\n // Let's wait for the mounted promise to resolve before proceeding with destruction.\n await this.mountedPromise;\n this.mountedPromise = null;\n\n // Unmount all UI parts from the editor.\n this.el.innerHTML = '';\n }\n}\n\n/**\n * Maps the UI part name to the corresponding view in the editor.\n */\nfunction mapUIPartView(name: string): string | null {\n switch (name) {\n case 'toolbar':\n return 'toolbar';\n\n case 'menubar':\n return 'menuBarView';\n\n default:\n return null;\n }\n}\n\n/**\n * Phoenix LiveView hook for CKEditor 5 UI parts.\n */\nexport const UIPartHook = makeHook(UIPartHookImpl);\n","import { EditableHook } from './editable';\nimport { EditorHook } from './editor';\nimport { UIPartHook } from './ui-part';\n\nexport const Hooks = {\n CKEditor5: EditorHook,\n CKEditable: EditableHook,\n CKUIPart: UIPartHook,\n};\n"],"names":["debounce","delay","callback","timeoutId","args","ClassHook","makeHook","constructor","instance","event","payload","selector","mapObjectValues","obj","mapper","mappedEntries","key","value","parseIntIfNotNull","parsed","EditorsRegistry","editorId","fn","callbacks","editors","editor","resolve","callbacksForEditor","promises","EditableHookImpl","editableId","rootName","initialValue","input","ui","editing","model","contentElement","editable","syncEditorRootToInput","root","EditableHook","sync","isSingleEditingLikeEditor","editorType","loadEditorConstructor","type","PKG","EditorConstructor","loadEditorPlugins","plugins","basePackage","premiumPackage","loaders","plugin","basePkgImport","error","premiumPkgImport","queryAllEditorEditables","iterator","acc","element","name","content","EDITOR_TYPES","readPresetOrThrow","attributeValue","config","license","setEditorEditableHeight","height","writer","EditorHookImpl","preset","editableHeight","Constructor","rootEditables","getInitialRootsContentElements","getInitialRootsValues","syncEditorToInput","editables","mainEditableValue","EditorHook","UIPartHookImpl","uiViewName","mapUIPartView","uiPart","UIPartHook","Hooks"],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,447 @@
|
|
|
1
|
+
function E(n, t) {
|
|
2
|
+
let e = null;
|
|
3
|
+
return (...i) => {
|
|
4
|
+
e && clearTimeout(e), e = setTimeout(() => {
|
|
5
|
+
t(...i);
|
|
6
|
+
}, n);
|
|
7
|
+
};
|
|
8
|
+
}
|
|
9
|
+
class b {
|
|
10
|
+
/**
|
|
11
|
+
* The DOM element the hook is attached to.
|
|
12
|
+
* It includes an `instance` property to hold the hook instance.
|
|
13
|
+
*/
|
|
14
|
+
el;
|
|
15
|
+
/**
|
|
16
|
+
* The LiveView socket instance, providing connection to the server.
|
|
17
|
+
*/
|
|
18
|
+
liveSocket;
|
|
19
|
+
/**
|
|
20
|
+
* Pushes an event from the client to the LiveView server process.
|
|
21
|
+
* @param _event The name of the event.
|
|
22
|
+
* @param _payload The data to send with the event.
|
|
23
|
+
* @param _callback An optional function to be called with the server's reply.
|
|
24
|
+
*/
|
|
25
|
+
pushEvent;
|
|
26
|
+
/**
|
|
27
|
+
* Pushes an event to another hook on the page.
|
|
28
|
+
* @param _selector The CSS selector of the target element with the hook.
|
|
29
|
+
* @param _event The name of the event.
|
|
30
|
+
* @param _payload The data to send with the event.
|
|
31
|
+
* @param _callback An optional function to be called with the reply.
|
|
32
|
+
*/
|
|
33
|
+
pushEventTo;
|
|
34
|
+
/**
|
|
35
|
+
* Registers a handler for an event pushed from the server.
|
|
36
|
+
* @param _event The name of the event to handle.
|
|
37
|
+
* @param _callback The function to execute when the event is received.
|
|
38
|
+
*/
|
|
39
|
+
handleEvent;
|
|
40
|
+
}
|
|
41
|
+
function f(n) {
|
|
42
|
+
return {
|
|
43
|
+
/**
|
|
44
|
+
* The mounted lifecycle callback for the LiveView hook object.
|
|
45
|
+
* It creates an instance of the user-defined hook class and sets up the necessary properties and methods.
|
|
46
|
+
*/
|
|
47
|
+
mounted() {
|
|
48
|
+
const t = new n();
|
|
49
|
+
this.el.instance = t, t.el = this.el, t.liveSocket = this.liveSocket, t.pushEvent = (e, i, o) => this.pushEvent(e, i, o), t.pushEventTo = (e, i, o, r) => this.pushEventTo(e, i, o, r), t.handleEvent = (e, i) => this.handleEvent(e, i), t.mounted?.();
|
|
50
|
+
},
|
|
51
|
+
/**
|
|
52
|
+
* The beforeUpdate lifecycle callback that delegates to the hook instance.
|
|
53
|
+
*/
|
|
54
|
+
beforeUpdate() {
|
|
55
|
+
this.el.instance.beforeUpdate?.();
|
|
56
|
+
},
|
|
57
|
+
/**
|
|
58
|
+
* The destroyed lifecycle callback that delegates to the hook instance.
|
|
59
|
+
*/
|
|
60
|
+
destroyed() {
|
|
61
|
+
this.el.instance.destroyed?.();
|
|
62
|
+
},
|
|
63
|
+
/**
|
|
64
|
+
* The disconnected lifecycle callback that delegates to the hook instance.
|
|
65
|
+
*/
|
|
66
|
+
disconnected() {
|
|
67
|
+
this.el.instance.disconnected?.();
|
|
68
|
+
},
|
|
69
|
+
/**
|
|
70
|
+
* The reconnected lifecycle callback that delegates to the hook instance.
|
|
71
|
+
*/
|
|
72
|
+
reconnected() {
|
|
73
|
+
this.el.instance.reconnected?.();
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
function y(n, t) {
|
|
78
|
+
const e = Object.entries(n).map(([i, o]) => [i, t(o, i)]);
|
|
79
|
+
return Object.fromEntries(e);
|
|
80
|
+
}
|
|
81
|
+
function w(n) {
|
|
82
|
+
if (n === null)
|
|
83
|
+
return null;
|
|
84
|
+
const t = Number.parseInt(n, 10);
|
|
85
|
+
return Number.isNaN(t) ? null : t;
|
|
86
|
+
}
|
|
87
|
+
class l {
|
|
88
|
+
static the = new l();
|
|
89
|
+
/**
|
|
90
|
+
* Map of registered editors.
|
|
91
|
+
*/
|
|
92
|
+
editors = /* @__PURE__ */ new Map();
|
|
93
|
+
/**
|
|
94
|
+
* Map of callbacks that are waiting for an editor to be registered.
|
|
95
|
+
*/
|
|
96
|
+
callbacks = /* @__PURE__ */ new Map();
|
|
97
|
+
/**
|
|
98
|
+
* Private constructor to enforce singleton pattern.
|
|
99
|
+
*/
|
|
100
|
+
constructor() {
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Executes a function on an editor.
|
|
104
|
+
* If the editor is not yet registered, it will wait for it to be registered.
|
|
105
|
+
*
|
|
106
|
+
* @param editorId The ID of the editor.
|
|
107
|
+
* @param fn The function to execute.
|
|
108
|
+
* @returns A promise that resolves with the result of the function.
|
|
109
|
+
*/
|
|
110
|
+
execute(t, e) {
|
|
111
|
+
const { callbacks: i, editors: o } = this, r = o.get(t);
|
|
112
|
+
return r ? Promise.resolve(e(r)) : new Promise((s) => {
|
|
113
|
+
const a = async (c) => s(await e(c));
|
|
114
|
+
this.callbacks.has(t) || i.set(t, []), i.set(t, [
|
|
115
|
+
...i.get(t),
|
|
116
|
+
a
|
|
117
|
+
]);
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Registers an editor.
|
|
122
|
+
*
|
|
123
|
+
* @param editorId The ID of the editor.
|
|
124
|
+
* @param editor The editor instance.
|
|
125
|
+
*/
|
|
126
|
+
register(t, e) {
|
|
127
|
+
const { editors: i, callbacks: o } = this, r = o.get(t);
|
|
128
|
+
if (i.has(t))
|
|
129
|
+
throw new Error(`Editor with ID "${t}" is already registered.`);
|
|
130
|
+
i.set(t, e), r && (r.forEach((s) => s(e)), o.delete(t)), this.editors.size === 1 && this.register(null, e);
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Un-registers an editor.
|
|
134
|
+
*
|
|
135
|
+
* @param editorId The ID of the editor.
|
|
136
|
+
*/
|
|
137
|
+
unregister(t) {
|
|
138
|
+
const { editors: e, callbacks: i } = this;
|
|
139
|
+
if (!e.has(t))
|
|
140
|
+
throw new Error(`Editor with ID "${t}" is not registered.`);
|
|
141
|
+
t && this.editors.get(null) === e.get(t) && this.unregister(null), e.delete(t), i.delete(t);
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Gets all registered editors.
|
|
145
|
+
*/
|
|
146
|
+
getEditors() {
|
|
147
|
+
return Array.from(this.editors.values());
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Checks if an editor with the given ID is registered.
|
|
151
|
+
*
|
|
152
|
+
* @param editorId The ID of the editor.
|
|
153
|
+
* @returns `true` if the editor is registered, `false` otherwise.
|
|
154
|
+
*/
|
|
155
|
+
hasEditor(t) {
|
|
156
|
+
return this.editors.has(t);
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Gets a promise that resolves with the editor instance for the given ID.
|
|
160
|
+
* If the editor is not registered yet, it will wait for it to be registered.
|
|
161
|
+
*
|
|
162
|
+
* @param editorId The ID of the editor.
|
|
163
|
+
* @returns A promise that resolves with the editor instance.
|
|
164
|
+
*/
|
|
165
|
+
waitForEditor(t) {
|
|
166
|
+
return this.execute(t, (e) => e);
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Destroys all registered editors and clears the registry.
|
|
170
|
+
* This will call the `destroy` method on each editor.
|
|
171
|
+
*/
|
|
172
|
+
async destroyAllEditors() {
|
|
173
|
+
const t = Array.from(this.editors.values()).map((e) => e.destroy());
|
|
174
|
+
this.editors.clear(), this.callbacks.clear(), await Promise.all(t);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
class k extends b {
|
|
178
|
+
/**
|
|
179
|
+
* The name of the hook.
|
|
180
|
+
*/
|
|
181
|
+
mountedPromise = null;
|
|
182
|
+
/**
|
|
183
|
+
* Attributes for the editable instance.
|
|
184
|
+
*/
|
|
185
|
+
get attrs() {
|
|
186
|
+
const t = {
|
|
187
|
+
editableId: this.el.getAttribute("id"),
|
|
188
|
+
editorId: this.el.getAttribute("data-cke-editor-id") || null,
|
|
189
|
+
rootName: this.el.getAttribute("data-cke-editable-root-name"),
|
|
190
|
+
initialValue: this.el.getAttribute("data-cke-editable-initial-value") || ""
|
|
191
|
+
};
|
|
192
|
+
return Object.defineProperty(this, "attrs", {
|
|
193
|
+
value: t,
|
|
194
|
+
writable: !1,
|
|
195
|
+
configurable: !1,
|
|
196
|
+
enumerable: !0
|
|
197
|
+
}), t;
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Mounts the editable component.
|
|
201
|
+
*/
|
|
202
|
+
async mounted() {
|
|
203
|
+
const { editableId: t, editorId: e, rootName: i, initialValue: o } = this.attrs, r = this.el.querySelector(`#${t}_input`);
|
|
204
|
+
this.mountedPromise = l.the.execute(e, (s) => {
|
|
205
|
+
const { ui: a, editing: c, model: h } = s;
|
|
206
|
+
if (h.document.getRoot(i))
|
|
207
|
+
return;
|
|
208
|
+
s.addRoot(i, {
|
|
209
|
+
isUndoable: !1,
|
|
210
|
+
data: o
|
|
211
|
+
});
|
|
212
|
+
const u = this.el.querySelector("[data-cke-editable-content]"), d = a.view.createEditable(i, u);
|
|
213
|
+
a.addEditable(d), c.view.forceRender(), r && v(r, s, i);
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* Destroys the editable component. Unmounts root from the editor.
|
|
218
|
+
*/
|
|
219
|
+
async destroyed() {
|
|
220
|
+
const { editorId: t, rootName: e } = this.attrs;
|
|
221
|
+
this.el.style.display = "none", await this.mountedPromise, this.mountedPromise = null, await l.the.execute(t, (i) => {
|
|
222
|
+
const o = i.model.document.getRoot(e);
|
|
223
|
+
o && (i.detachEditable(o), i.detachRoot(e, !1));
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
const P = f(k);
|
|
228
|
+
function v(n, t, e) {
|
|
229
|
+
const i = () => {
|
|
230
|
+
n.value = t.getData({ rootName: e });
|
|
231
|
+
};
|
|
232
|
+
t.model.document.on("change:data", E(100, i)), i();
|
|
233
|
+
}
|
|
234
|
+
function p(n) {
|
|
235
|
+
return ["inline", "classic", "balloon", "decoupled"].includes(n);
|
|
236
|
+
}
|
|
237
|
+
async function I(n) {
|
|
238
|
+
const t = await import("ckeditor5"), i = {
|
|
239
|
+
inline: t.InlineEditor,
|
|
240
|
+
balloon: t.BalloonEditor,
|
|
241
|
+
classic: t.ClassicEditor,
|
|
242
|
+
decoupled: t.DecoupledEditor,
|
|
243
|
+
multiroot: t.MultiRootEditor
|
|
244
|
+
}[n];
|
|
245
|
+
if (!i)
|
|
246
|
+
throw new Error(`Unsupported editor type: ${n}`);
|
|
247
|
+
return i;
|
|
248
|
+
}
|
|
249
|
+
async function A(n) {
|
|
250
|
+
const t = await import("ckeditor5");
|
|
251
|
+
let e = null;
|
|
252
|
+
const i = n.map(async (o) => {
|
|
253
|
+
const { [o]: r } = t;
|
|
254
|
+
if (r)
|
|
255
|
+
return r;
|
|
256
|
+
if (!e)
|
|
257
|
+
try {
|
|
258
|
+
e = await import("ckeditor5-premium-features");
|
|
259
|
+
} catch (a) {
|
|
260
|
+
console.error(`Failed to load premium package: ${a}`);
|
|
261
|
+
}
|
|
262
|
+
const { [o]: s } = e || {};
|
|
263
|
+
if (s)
|
|
264
|
+
return s;
|
|
265
|
+
throw new Error(`Plugin "${o}" not found in base or premium packages.`);
|
|
266
|
+
});
|
|
267
|
+
return Promise.all(i);
|
|
268
|
+
}
|
|
269
|
+
function m(n) {
|
|
270
|
+
const t = document.querySelectorAll(
|
|
271
|
+
[
|
|
272
|
+
`[data-cke-editor-id="${n}"][data-cke-editable-root-name]`,
|
|
273
|
+
"[data-cke-editable-root-name]:not([data-cke-editor-id])"
|
|
274
|
+
].join(", ")
|
|
275
|
+
);
|
|
276
|
+
return Array.from(t).reduce((e, i) => {
|
|
277
|
+
const o = i.getAttribute("data-cke-editable-root-name"), r = i.getAttribute("data-cke-editable-initial-value") || "", s = i.querySelector("[data-cke-editable-content]");
|
|
278
|
+
return !o || !s ? e : {
|
|
279
|
+
...e,
|
|
280
|
+
[o]: {
|
|
281
|
+
content: s,
|
|
282
|
+
initialValue: r
|
|
283
|
+
}
|
|
284
|
+
};
|
|
285
|
+
}, /* @__PURE__ */ Object.create({}));
|
|
286
|
+
}
|
|
287
|
+
const g = ["inline", "classic", "balloon", "decoupled", "multiroot"];
|
|
288
|
+
function H(n) {
|
|
289
|
+
const t = n.getAttribute("cke-preset");
|
|
290
|
+
if (!t)
|
|
291
|
+
throw new Error('CKEditor5 hook requires a "cke-preset" attribute on the element.');
|
|
292
|
+
const { type: e, config: i, license: o } = JSON.parse(t);
|
|
293
|
+
if (!e || !i || !o)
|
|
294
|
+
throw new Error('CKEditor5 hook configuration must include "editor", "config", and "license" properties.');
|
|
295
|
+
if (!g.includes(e))
|
|
296
|
+
throw new Error(`Invalid editor type: ${e}. Must be one of: ${g.join(", ")}.`);
|
|
297
|
+
return {
|
|
298
|
+
type: e,
|
|
299
|
+
config: i,
|
|
300
|
+
license: o
|
|
301
|
+
};
|
|
302
|
+
}
|
|
303
|
+
function V(n, t) {
|
|
304
|
+
const { editing: e } = n;
|
|
305
|
+
e.view.change((i) => {
|
|
306
|
+
i.setStyle("height", `${t}px`, e.view.document.getRoot());
|
|
307
|
+
});
|
|
308
|
+
}
|
|
309
|
+
class $ extends b {
|
|
310
|
+
/**
|
|
311
|
+
* The name of the hook.
|
|
312
|
+
*/
|
|
313
|
+
editorPromise = null;
|
|
314
|
+
/**
|
|
315
|
+
* Attributes for the editor instance.
|
|
316
|
+
*/
|
|
317
|
+
get attrs() {
|
|
318
|
+
const t = {
|
|
319
|
+
editorId: this.el.getAttribute("id"),
|
|
320
|
+
preset: H(this.el),
|
|
321
|
+
editableHeight: w(this.el.getAttribute("cke-editable-height"))
|
|
322
|
+
};
|
|
323
|
+
return Object.defineProperty(this, "attrs", {
|
|
324
|
+
value: t,
|
|
325
|
+
writable: !1,
|
|
326
|
+
configurable: !1,
|
|
327
|
+
enumerable: !0
|
|
328
|
+
}), t;
|
|
329
|
+
}
|
|
330
|
+
/**
|
|
331
|
+
* Mounts the editor component.
|
|
332
|
+
*/
|
|
333
|
+
async mounted() {
|
|
334
|
+
this.editorPromise = this.createEditor(), l.the.register(this.attrs.editorId, await this.editorPromise);
|
|
335
|
+
}
|
|
336
|
+
/**
|
|
337
|
+
* Destroys the editor instance when the component is destroyed.
|
|
338
|
+
* This is important to prevent memory leaks and ensure that the editor is properly cleaned up.
|
|
339
|
+
*/
|
|
340
|
+
async destroyed() {
|
|
341
|
+
this.el.style.display = "none", (await this.editorPromise)?.destroy(), this.editorPromise = null, l.the.unregister(this.attrs.editorId);
|
|
342
|
+
}
|
|
343
|
+
/**
|
|
344
|
+
* Creates the CKEditor instance.
|
|
345
|
+
*/
|
|
346
|
+
createEditor = async () => {
|
|
347
|
+
const { preset: t, editorId: e, editableHeight: i } = this.attrs, { type: o, license: r, config: { plugins: s, ...a } } = t, c = await I(o), h = S(e, o), u = await c.create(
|
|
348
|
+
h,
|
|
349
|
+
{
|
|
350
|
+
...a,
|
|
351
|
+
initialData: N(e, o),
|
|
352
|
+
licenseKey: r.key,
|
|
353
|
+
plugins: await A(s)
|
|
354
|
+
}
|
|
355
|
+
);
|
|
356
|
+
if (p(o)) {
|
|
357
|
+
const d = document.getElementById(`${e}_input`);
|
|
358
|
+
d && C(d, u), i && V(u, i);
|
|
359
|
+
}
|
|
360
|
+
return u;
|
|
361
|
+
};
|
|
362
|
+
}
|
|
363
|
+
function C(n, t) {
|
|
364
|
+
const e = () => {
|
|
365
|
+
n.value = t.getData();
|
|
366
|
+
};
|
|
367
|
+
t.model.document.on("change:data", E(250, e)), e();
|
|
368
|
+
}
|
|
369
|
+
function S(n, t) {
|
|
370
|
+
if (p(t))
|
|
371
|
+
return document.getElementById(`${n}_editor`);
|
|
372
|
+
const e = m(n);
|
|
373
|
+
return y(e, ({ content: i }) => i);
|
|
374
|
+
}
|
|
375
|
+
function N(n, t) {
|
|
376
|
+
if (t === "decoupled") {
|
|
377
|
+
const i = m(n).main?.initialValue;
|
|
378
|
+
if (i)
|
|
379
|
+
return i;
|
|
380
|
+
}
|
|
381
|
+
if (p(t))
|
|
382
|
+
return document.getElementById(n)?.getAttribute("cke-initial-value") || "";
|
|
383
|
+
const e = m(n);
|
|
384
|
+
return y(e, ({ initialValue: i }) => i);
|
|
385
|
+
}
|
|
386
|
+
const R = f($);
|
|
387
|
+
class T extends b {
|
|
388
|
+
/**
|
|
389
|
+
* The name of the hook.
|
|
390
|
+
*/
|
|
391
|
+
mountedPromise = null;
|
|
392
|
+
/**
|
|
393
|
+
* Attributes for the editable instance.
|
|
394
|
+
*/
|
|
395
|
+
get attrs() {
|
|
396
|
+
const t = {
|
|
397
|
+
editorId: this.el.getAttribute("data-cke-editor-id") || null,
|
|
398
|
+
name: this.el.getAttribute("data-cke-ui-part-name")
|
|
399
|
+
};
|
|
400
|
+
return Object.defineProperty(this, "attrs", {
|
|
401
|
+
value: t,
|
|
402
|
+
writable: !1,
|
|
403
|
+
configurable: !1,
|
|
404
|
+
enumerable: !0
|
|
405
|
+
}), t;
|
|
406
|
+
}
|
|
407
|
+
/**
|
|
408
|
+
* Mounts the editable component.
|
|
409
|
+
*/
|
|
410
|
+
async mounted() {
|
|
411
|
+
const { editorId: t, name: e } = this.attrs;
|
|
412
|
+
this.mountedPromise = l.the.execute(t, (i) => {
|
|
413
|
+
const { ui: o } = i, r = x(e), s = o.view[r];
|
|
414
|
+
if (!s) {
|
|
415
|
+
console.error(`Unknown UI part name: "${e}". Supported names are "toolbar" and "menubar".`);
|
|
416
|
+
return;
|
|
417
|
+
}
|
|
418
|
+
this.el.appendChild(s.element);
|
|
419
|
+
});
|
|
420
|
+
}
|
|
421
|
+
/**
|
|
422
|
+
* Destroys the editable component. Unmounts root from the editor.
|
|
423
|
+
*/
|
|
424
|
+
async destroyed() {
|
|
425
|
+
this.el.style.display = "none", await this.mountedPromise, this.mountedPromise = null, this.el.innerHTML = "";
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
function x(n) {
|
|
429
|
+
switch (n) {
|
|
430
|
+
case "toolbar":
|
|
431
|
+
return "toolbar";
|
|
432
|
+
case "menubar":
|
|
433
|
+
return "menuBarView";
|
|
434
|
+
default:
|
|
435
|
+
return null;
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
const O = f(T), U = {
|
|
439
|
+
CKEditor5: R,
|
|
440
|
+
CKEditable: P,
|
|
441
|
+
CKUIPart: O
|
|
442
|
+
};
|
|
443
|
+
export {
|
|
444
|
+
l as EditorsRegistry,
|
|
445
|
+
U as Hooks
|
|
446
|
+
};
|
|
447
|
+
//# sourceMappingURL=index.mjs.map
|