Packages

CKEditor 5 integration for Phoenix Framework

Retired package: Renamed - Moved to ckeditor5-phoenix

Current section

Files

Jump to
ckeditor5 dist index.mjs.map
Raw

dist/index.mjs.map

{"version":3,"file":"index.mjs","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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"}