Current section
Files
Jump to
Current section
Files
dist/index.cjs.map
{"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":"2hBAAO,SAASA,EACdC,EACAC,EACkC,CAClC,IAAIC,EAAkD,KAEtD,MAAO,IAAIC,IAA8B,CACnCD,GACF,aAAaA,CAAS,EAGxBA,EAAY,WAAW,IAAM,CAC3BD,EAAS,GAAGE,CAAI,CAClB,EAAGH,CAAK,CACV,CACF,CCLO,MAAeI,CAAU,CAK9B,GAKA,WAQA,UAaA,YAYA,WA+BF,CAOO,SAASC,EAASC,EAAkF,CACzG,MAAO,CAKL,SAAmB,CACjB,MAAMC,EAAW,IAAID,EAErB,KAAK,GAAG,SAAWC,EAEnBA,EAAS,GAAK,KAAK,GACnBA,EAAS,WAAa,KAAK,WAE3BA,EAAS,UAAY,CAACC,EAAOC,EAASR,IAAa,KAAK,UAAUO,EAAOC,EAASR,CAAQ,EAC1FM,EAAS,YAAc,CAACG,EAAUF,EAAOC,EAASR,IAAa,KAAK,YAAYS,EAAUF,EAAOC,EAASR,CAAQ,EAClHM,EAAS,YAAc,CAACC,EAAOP,IAAa,KAAK,YAAYO,EAAOP,CAAQ,EAE5EM,EAAS,UAAA,CACX,EAKA,cAAwB,CACtB,KAAK,GAAG,SAAS,eAAA,CACnB,EAKA,WAAqB,CACnB,KAAK,GAAG,SAAS,YAAA,CACnB,EAKA,cAAwB,CACtB,KAAK,GAAG,SAAS,eAAA,CACnB,EAKA,aAAuB,CACrB,KAAK,GAAG,SAAS,cAAA,CACnB,CAAA,CAEJ,CCnIO,SAASI,EACdC,EACAC,EACmB,CACnB,MAAMC,EAAgB,OACnB,QAAQF,CAAG,EACX,IAAI,CAAC,CAACG,EAAKC,CAAK,IAAM,CAACD,EAAKF,EAAOG,EAAOD,CAAG,CAAC,CAAU,EAE3D,OAAO,OAAO,YAAYD,CAAa,CACzC,CClBO,SAASG,EAAkBD,EAAqC,CACrE,GAAIA,IAAU,KACZ,OAAO,KAGT,MAAME,EAAS,OAAO,SAASF,EAAO,EAAE,EAExC,OAAO,OAAO,MAAME,CAAM,EAAI,KAAOA,CACvC,CCAO,MAAMC,CAAgB,CAC3B,OAAgB,IAAM,IAAIA,EAKT,YAAc,IAKd,cAAgB,IAKzB,aAAc,CAAC,CAUvB,QAAsCC,EAA2BC,EAA2C,CAC1G,KAAM,CAAE,UAAAC,EAAW,QAAAC,CAAA,EAAY,KACzBC,EAASD,EAAQ,IAAIH,CAAQ,EAEnC,OAAII,EACK,QAAQ,QAAQH,EAAGG,CAAW,CAAC,EAGjC,IAAI,QAASC,GAAY,CAC9B,MAAMxB,EAAW,MAAOuB,GAAcC,EAAQ,MAAMJ,EAAGG,CAAM,CAAC,EAEzD,KAAK,UAAU,IAAIJ,CAAQ,GAC9BE,EAAU,IAAIF,EAAU,EAAE,EAG5BE,EAAU,IAAIF,EAAU,CACtB,GAAGE,EAAU,IAAIF,CAAQ,EACzBnB,CAAA,CACD,CACH,CAAC,CACH,CAQA,SAASmB,EAA2BI,EAAsB,CACxD,KAAM,CAAE,QAAAD,EAAS,UAAAD,CAAA,EAAc,KACzBI,EAAqBJ,EAAU,IAAIF,CAAQ,EAEjD,GAAIG,EAAQ,IAAIH,CAAQ,EACtB,MAAM,IAAI,MAAM,mBAAmBA,CAAQ,0BAA0B,EAGvEG,EAAQ,IAAIH,EAAUI,CAAM,EAExBE,IACFA,EAAmB,QAAQzB,GAAYA,EAASuB,CAAM,CAAC,EACvDF,EAAU,OAAOF,CAAQ,GAKvB,KAAK,QAAQ,OAAS,GACxB,KAAK,SAAS,KAAMI,CAAM,CAE9B,CAOA,WAAWJ,EAAiC,CAC1C,KAAM,CAAE,QAAAG,EAAS,UAAAD,CAAA,EAAc,KAE/B,GAAI,CAACC,EAAQ,IAAIH,CAAQ,EACvB,MAAM,IAAI,MAAM,mBAAmBA,CAAQ,sBAAsB,EAG/DA,GAAY,KAAK,QAAQ,IAAI,IAAI,IAAMG,EAAQ,IAAIH,CAAQ,GAC7D,KAAK,WAAW,IAAI,EAGtBG,EAAQ,OAAOH,CAAQ,EACvBE,EAAU,OAAOF,CAAQ,CAC3B,CAKA,YAAuB,CACrB,OAAO,MAAM,KAAK,KAAK,QAAQ,QAAQ,CACzC,CAQA,UAAUA,EAAoC,CAC5C,OAAO,KAAK,QAAQ,IAAIA,CAAQ,CAClC,CASA,cAAgCA,EAAuC,CACrE,OAAO,KAAK,QAAQA,EAAUI,GAAUA,CAAW,CACrD,CAMA,MAAM,mBAAoB,CACxB,MAAMG,EACJ,MACG,KAAK,KAAK,QAAQ,QAAQ,EAC1B,IAAIH,GAAUA,EAAO,QAAA,CAAS,EAGnC,KAAK,QAAQ,MAAA,EACb,KAAK,UAAU,MAAA,EAEf,MAAM,QAAQ,IAAIG,CAAQ,CAC5B,CACF,CC5IA,MAAMC,UAAyBxB,CAAU,CAI/B,eAAuC,KAK/C,IAAY,OAAQ,CAClB,MAAMY,EAAQ,CACZ,WAAY,KAAK,GAAG,aAAa,IAAI,EACrC,SAAU,KAAK,GAAG,aAAa,oBAAoB,GAAK,KACxD,SAAU,KAAK,GAAG,aAAa,6BAA6B,EAC5D,aAAc,KAAK,GAAG,aAAa,iCAAiC,GAAK,EAAA,EAG3E,cAAO,eAAe,KAAM,QAAS,CACnC,MAAAA,EACA,SAAU,GACV,aAAc,GACd,WAAY,EAAA,CACb,EAEMA,CACT,CAKA,MAAe,SAAU,CACvB,KAAM,CAAE,WAAAa,EAAY,SAAAT,EAAU,SAAAU,EAAU,aAAAC,CAAA,EAAiB,KAAK,MACxDC,EAAQ,KAAK,GAAG,cAAgC,IAAIH,CAAU,QAAQ,EAG5E,KAAK,eAAiBV,EAAgB,IAAI,QAAQC,EAAWI,GAA4B,CACvF,KAAM,CAAE,GAAAS,EAAI,QAAAC,EAAS,MAAAC,CAAA,EAAUX,EAE/B,GAAIW,EAAM,SAAS,QAAQL,CAAQ,EACjC,OAGFN,EAAO,QAAQM,EAAU,CACvB,WAAY,GACZ,KAAMC,CAAA,CACP,EAED,MAAMK,EAAiB,KAAK,GAAG,cAAc,6BAA6B,EACpEC,EAAWJ,EAAG,KAAK,eAAeH,EAAUM,CAAe,EAEjEH,EAAG,YAAYI,CAAQ,EACvBH,EAAQ,KAAK,YAAA,EAETF,GACFM,EAAsBN,EAAOR,EAAQM,CAAQ,CAEjD,CAAC,CACH,CAKA,MAAe,WAAY,CACzB,KAAM,CAAE,SAAAV,EAAU,SAAAU,CAAA,EAAa,KAAK,MAGpC,KAAK,GAAG,MAAM,QAAU,OAGxB,MAAM,KAAK,eACX,KAAK,eAAiB,KAGtB,MAAMX,EAAgB,IAAI,QAAQC,EAAWI,GAA4B,CACvE,MAAMe,EAAOf,EAAO,MAAM,SAAS,QAAQM,CAAQ,EAE/CS,IACFf,EAAO,eAAee,CAAI,EAC1Bf,EAAO,WAAWM,EAAU,EAAK,EAErC,CAAC,CACH,CACF,CAKO,MAAMU,EAAenC,EAASuB,CAAgB,EAUrD,SAASU,EAAsBN,EAAyBR,EAAyBM,EAAkB,CACjG,MAAMW,EAAO,IAAM,CACjBT,EAAM,MAAQR,EAAO,QAAQ,CAAE,SAAAM,EAAU,CAC3C,EAEAN,EAAO,MAAM,SAAS,GAAG,cAAezB,EAAS,IAAK0C,CAAI,CAAC,EAC3DA,EAAA,CACF,CCxGO,SAASC,EAA0BC,EAAiC,CACzE,MAAO,CAAC,SAAU,UAAW,UAAW,WAAW,EAAE,SAASA,CAAU,CAC1E,CCFA,eAAsBC,EAAsBC,EAAkB,CAC5D,MAAMC,EAAM,KAAM,QAAO,WAAW,EAU9BC,EARY,CAChB,OAAQD,EAAI,aACZ,QAASA,EAAI,cACb,QAASA,EAAI,cACb,UAAWA,EAAI,gBACf,UAAWA,EAAI,eAAA,EAGmBD,CAAI,EAExC,GAAI,CAACE,EACH,MAAM,IAAI,MAAM,4BAA4BF,CAAI,EAAE,EAGpD,OAAOE,CACT,CCdA,eAAsBC,EAAkBC,EAAuD,CAC7F,MAAMC,EAAmC,KAAM,QAAO,WAAW,EACjE,IAAIC,EAA6C,KAEjD,MAAMC,EAAUH,EAAQ,IAAI,MAAOI,GAAW,CAK5C,KAAM,CAAE,CAACA,CAAM,EAAGC,GAAkBJ,EAEpC,GAAII,EACF,OAAOA,EAIT,GAAI,CAACH,EACH,GAAI,CACFA,EAAiB,KAAM,QAAO,4BAA4B,CAC5D,OACOI,EAAO,CACZ,QAAQ,MAAM,mCAAmCA,CAAK,EAAE,CAC1D,CAGF,KAAM,CAAE,CAACF,CAAM,EAAGG,CAAA,EAAqBL,GAAkB,CAAA,EAEzD,GAAIK,EACF,OAAOA,EAIT,MAAM,IAAI,MAAM,WAAWH,CAAM,0CAA0C,CAE7E,CAAC,EAED,OAAO,QAAQ,IAAID,CAAO,CAC5B,CCzCO,SAASK,EAAwBrC,EAAkD,CACxF,MAAMsC,EAAW,SAAS,iBACxB,CACE,wBAAwBtC,CAAQ,kCAChC,yDAAA,EAEC,KAAK,IAAI,CAAA,EAGd,OACE,MACG,KAAKsC,CAAQ,EACb,OAAqC,CAACC,EAAKC,IAAY,CACtD,MAAMC,EAAOD,EAAQ,aAAa,6BAA6B,EACzD7B,EAAe6B,EAAQ,aAAa,iCAAiC,GAAK,GAC1EE,EAAUF,EAAQ,cAAc,6BAA6B,EAEnE,MAAI,CAACC,GAAQ,CAACC,EACLH,EAGF,CACL,GAAGA,EACH,CAACE,CAAI,EAAG,CACN,QAAAC,EACA,aAAA/B,CAAA,CACF,CAEJ,EAAG,OAAO,OAAO,CAAA,CAAE,CAAC,CAE1B,CCnCO,MAAMgC,EAAe,CAAC,SAAU,UAAW,UAAW,YAAa,WAAW,ECO9E,SAASC,EAAkBJ,EAAoC,CACpE,MAAMK,EAAiBL,EAAQ,aAAa,YAAY,EAExD,GAAI,CAACK,EACH,MAAM,IAAI,MAAM,kEAAkE,EAGpF,KAAM,CAAE,KAAApB,EAAM,OAAAqB,EAAQ,QAAAC,GAAY,KAAK,MAAMF,CAAc,EAE3D,GAAI,CAACpB,GAAQ,CAACqB,GAAU,CAACC,EACvB,MAAM,IAAI,MAAM,yFAAyF,EAG3G,GAAI,CAACJ,EAAa,SAASlB,CAAI,EAC7B,MAAM,IAAI,MAAM,wBAAwBA,CAAI,qBAAqBkB,EAAa,KAAK,IAAI,CAAC,GAAG,EAG7F,MAAO,CACL,KAAAlB,EACA,OAAAqB,EACA,QAAAC,CAAA,CAEJ,CCxBO,SAASC,EAAwB7D,EAAkB8D,EAAsB,CAC9E,KAAM,CAAE,QAAAnC,GAAY3B,EAEpB2B,EAAQ,KAAK,OAAQoC,GAAW,CAC9BA,EAAO,SAAS,SAAU,GAAGD,CAAM,KAAMnC,EAAQ,KAAK,SAAS,QAAA,CAAU,CAC3E,CAAC,CACH,CCYA,MAAMqC,UAAuBnE,CAAU,CAI7B,cAAwC,KAKhD,IAAY,OAAQ,CAClB,MAAMY,EAAQ,CACZ,SAAU,KAAK,GAAG,aAAa,IAAI,EACnC,OAAQgD,EAAkB,KAAK,EAAE,EACjC,eAAgB/C,EAAkB,KAAK,GAAG,aAAa,qBAAqB,CAAC,CAAA,EAG/E,cAAO,eAAe,KAAM,QAAS,CACnC,MAAAD,EACA,SAAU,GACV,aAAc,GACd,WAAY,EAAA,CACb,EAEMA,CACT,CAKA,MAAe,SAAU,CACvB,KAAK,cAAgB,KAAK,aAAA,EAE1BG,EAAgB,IAAI,SAAS,KAAK,MAAM,SAAU,MAAM,KAAK,aAAa,CAC5E,CAMA,MAAe,WAAY,CAEzB,KAAK,GAAG,MAAM,QAAU,QAGvB,MAAM,KAAK,gBAAgB,QAAA,EAC5B,KAAK,cAAgB,KAErBA,EAAgB,IAAI,WAAW,KAAK,MAAM,QAAQ,CACpD,CAKQ,aAAe,SAAY,CACjC,KAAM,CAAE,OAAAqD,EAAQ,SAAApD,EAAU,eAAAqD,CAAA,EAAmB,KAAK,MAC5C,CAAE,KAAA5B,EAAM,QAAAsB,EAAS,OAAQ,CAAE,QAAAlB,EAAS,GAAGiB,CAAA,CAAO,EAAMM,EAEpDE,EAAc,MAAM9B,EAAsBC,CAAI,EAC9C8B,EAAgBC,EAA+BxD,EAAUyB,CAAI,EAE7DrB,EAAS,MAAMkD,EAAY,OAC/BC,EACA,CACE,GAAGT,EACH,YAAaW,EAAsBzD,EAAUyB,CAAI,EACjD,WAAYsB,EAAQ,IACpB,QAAS,MAAMnB,EAAkBC,CAAO,CAAA,CAC1C,EAGF,GAAIP,EAA0BG,CAAI,EAAG,CACnC,MAAMb,EAAQ,SAAS,eAAe,GAAGZ,CAAQ,QAAQ,EAErDY,GACF8C,EAAkB9C,EAAOR,CAAM,EAG7BiD,GACFL,EAAwB5C,EAAQiD,CAAc,CAElD,CAEA,OAAOjD,CACT,CACF,CAQA,SAASsD,EAAkB9C,EAAyBR,EAAgB,CAClE,MAAMiB,EAAO,IAAM,CACjBT,EAAM,MAAQR,EAAO,QAAA,CACvB,EAEAA,EAAO,MAAM,SAAS,GAAG,cAAezB,EAAS,IAAK0C,CAAI,CAAC,EAC3DA,EAAA,CACF,CASA,SAASmC,EAA+BxD,EAAoByB,EAAkB,CAC5E,GAAIH,EAA0BG,CAAI,EAChC,OAAO,SAAS,eAAe,GAAGzB,CAAQ,SAAS,EAGrD,MAAM2D,EAAYtB,EAAwBrC,CAAQ,EAElD,OAAOT,EAAgBoE,EAAW,CAAC,CAAE,QAAAjB,CAAA,IAAcA,CAAO,CAC5D,CAWA,SAASe,EAAsBzD,EAAoByB,EAAkB,CAEnE,GAAIA,IAAS,YAAa,CACxB,MAAMmC,EAAoBvB,EAAwBrC,CAAQ,EAAE,MAAS,aAErE,GAAI4D,EACF,OAAOA,CAEX,CAGA,GAAItC,EAA0BG,CAAI,EAGhC,OAFqB,SAAS,eAAezB,CAAQ,GAAG,aAAa,mBAAmB,GAAK,GAK/F,MAAM2D,EAAYtB,EAAwBrC,CAAQ,EAElD,OAAOT,EAAgBoE,EAAW,CAAC,CAAE,aAAAhD,CAAA,IAAmBA,CAAY,CACtE,CAKO,MAAMkD,EAAa5E,EAASkE,CAAc,EC5KjD,MAAMW,UAAuB9E,CAAU,CAI7B,eAAuC,KAK/C,IAAY,OAAQ,CAClB,MAAMY,EAAQ,CACZ,SAAU,KAAK,GAAG,aAAa,oBAAoB,GAAK,KACxD,KAAM,KAAK,GAAG,aAAa,uBAAuB,CAAA,EAGpD,cAAO,eAAe,KAAM,QAAS,CACnC,MAAAA,EACA,SAAU,GACV,aAAc,GACd,WAAY,EAAA,CACb,EAEMA,CACT,CAKA,MAAe,SAAU,CACvB,KAAM,CAAE,SAAAI,EAAU,KAAAyC,CAAA,EAAS,KAAK,MAGhC,KAAK,eAAiB1C,EAAgB,IAAI,QAAQC,EAAWI,GAAW,CACtE,KAAM,CAAE,GAAAS,GAAOT,EAET2D,EAAaC,EAAcvB,CAAI,EAC/BwB,EAAUpD,EAAG,KAAakD,CAAW,EAE3C,GAAI,CAACE,EAAQ,CACX,QAAQ,MAAM,0BAA0BxB,CAAI,iDAAiD,EAC7F,MACF,CAEA,KAAK,GAAG,YAAYwB,EAAO,OAAO,CACpC,CAAC,CACH,CAKA,MAAe,WAAY,CAEzB,KAAK,GAAG,MAAM,QAAU,OAGxB,MAAM,KAAK,eACX,KAAK,eAAiB,KAGtB,KAAK,GAAG,UAAY,EACtB,CACF,CAKA,SAASD,EAAcvB,EAA6B,CAClD,OAAQA,EAAA,CACN,IAAK,UACH,MAAO,UAET,IAAK,UACH,MAAO,cAET,QACE,OAAO,IAAA,CAEb,CAKO,MAAMyB,EAAajF,EAAS6E,CAAc,ECpFpCK,EAAQ,CACnB,UAAWN,EACX,WAAYzC,EACZ,SAAU8C,CACZ"}