UNPKG

@ckeditor/ckeditor5-react

Version:

Official React component for CKEditor 5 – the best browser-based rich text editor.

1 lines 117 kB
{"version":3,"file":"index.umd.cjs","sources":["../src/lifecycle/LifeCycleElementSemaphore.ts","../src/context/setCKEditorReactContextMetadata.ts","../src/hooks/useIsMountedRef.ts","../src/hooks/useRefSafeCallback.ts","../src/context/useInitializedCKEditorsMap.ts","../src/context/ckeditorcontext.tsx","../src/ckeditor.tsx","../src/lifecycle/useLifeCycleSemaphoreSyncRef.tsx","../src/utils/mergeRefs.ts","../src/hooks/useInstantEffect.ts","../src/hooks/useInstantEditorEffect.ts","../src/useMultiRootEditor.tsx","../src/hooks/useIsUnmountedRef.ts","../src/hooks/useAsyncCallback.ts","../src/hooks/useAsyncValue.ts","../src/cloud/useCKEditorCloud.tsx","../src/cloud/withCKEditorCloud.tsx"],"sourcesContent":["/**\n * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.\n * For licensing, see LICENSE.md.\n */\n\nimport { createDefer, once, type Defer } from '@ckeditor/ckeditor5-integrations-common';\n\n/**\n * This class is utilized to pause the initialization of an editor when another instance is already present on a specified element.\n * It is engineered to address the following issues:\n *\n *\t* Rapid changes in component properties often lead to the re-initialization of the editor, which can trigger\n *\t the `editor-source-element-already-used` exception. This occurs because the editor is still in the process of initializing\n *\t when the component decides to destroy it. This semaphore waits for the editor to fully initialize before destroying it, thereby\n *\t allowing a new instance of the editor to be attached to the specified element.\n *\n *\t* Rapid mounting and unmounting in strict mode frequently results in the `editor-source-element-already-used` exception\n *\t being thrown by the editor. This is due to React reusing the underlying DOM element during the mounting and unmounting of components\n *\t (especially if the same component is being mounted and unmounted). Consequently, a race condition arises. The first render begins to\n *\t attach the editor (in async mode), and shortly thereafter, it is destroyed and a new instance of the component is initialized.\n *\t This semaphore, by utilizing a static semaphores promises map, retains information about whether the element is used by a previous\n *\t instance of the editor and resumes execution when it is freed.\n *\n *\t* The process involves starting up many editors that are no longer needed and are immediately removed in the following rerenders.\n *\t This can cause the editor’s initialization performance to slow down. The initialization of the editor is skipped when numerous\n *\t rerenders occur within a short time-frame while using this semaphore. An example of this could be a situation with 4 rerenders\n *\t occurring within a 10ms period. This semaphore will likely batch these calls, and instead of initializing 4 editors, only 2 will be\n *\t initialized (the first and the last one).\n */\nexport class LifeCycleElementSemaphore<R> {\n\t/**\n\t * This is a map of elements associated with promises. It informs the semaphore that the underlying HTML element, used as a key,\n\t * is currently in use by another editor. Each element is assigned a promise, which allows for the easy chaining of new\n\t * editor instances on an element that is already in use by another instance. The process works as follows:\n\t *\n\t * \t1. If an element is being used by an editor, then the initialization of a new editor\n\t * \t instance is chained using the `.then()` method of the Promise.\n\t *\n\t * \t2. If the editor associated with the underlying element is destroyed, then `Promise.resolve()` is called\n\t * \t and the previously assigned `.then()` editor callback is executed.\n\t *\n\t * @see {@link #lock} for more detailed information on the implementation.\n\t */\n\tprivate static readonly _semaphores = new Map<HTMLElement, Promise<void>>();\n\n\t/**\n\t * This should define async methods for initializing and destroying the editor.\n\t * Essentially, it's an async version of basic React lifecycle methods like `componentDidMount`, `componentWillUnmount`.\n\t *\n\t * \t* Result of {@link LifeCycleAsyncOperators#mount} method is passed to {@link LifeCycleAsyncOperators#unmount} as an argument.\n\t */\n\tprivate readonly _lifecycle: LifeCycleAsyncOperators<R>;\n\n\t/**\n\t * This is the element instance that the editor uses for mounting. This element should contain the `ckeditorInstance` member\n\t * once the editor has been successfully mounted to it. The semaphore ensures that a new instance of the editor, which will\n\t * be assigned to this element by the {@link #_lifecycle:mount} method, will always be initialized after the successful\n\t * destruction of the underlying `ckeditorInstance` that was previously mounted on this element.\n\t */\n\tprivate readonly _element: HTMLElement;\n\n\t/**\n\t * This is the lock mechanism utilized by the {@link #lock} and {@link #release} methods.\n\t *\n\t * \t* If the editor is not yet mounted and is awaiting mounting (for instance, when another editor is\n\t * \t occupying the element), then it is null.\n\t *\n\t * \t* When the editor is mounted on the element, this variable holds an unresolved promise that will be\n\t * \t resolved after the editor is destroyed.\n\t *\n\t * \t* Once the editor is destroyed (and it was previously mounted), the promise is resolved.\n\t */\n\tprivate _releaseLock: Defer<void> | null = null;\n\n\t/**\n\t * This is the result of the {@link #_lifecycle:mount} function. This value should be reset to `null`\n\t * once the semaphore is released. It is utilized to store certain data that must be removed following\n\t * the destruction of the editor. This data may include the editor's instance, the assigned watchdog,\n\t * or handles for additional window listeners.\n\t */\n\tprivate _value: R | null = null;\n\n\t/**\n\t * This is a list of callbacks that are triggered if the semaphore {@link #_lifecycle:mount} method executes successfully.\n\t * It is utilized in scenarios where we need to assign certain properties to an editor that is currently in the process of mounting.\n\t * An instance of such usage could be two-way binding. We aim to prevent the loss of all `setData` calls if the editor has not\n\t * yet been mounted, therefore these calls will be executed immediately following the completion of the mounting process.\n\t */\n\tprivate _afterMountCallbacks: Array<LifeCycleAfterMountCallback<R>> = [];\n\n\t/**\n\t * This represents the actual mounting state of the semaphore. It is primarily used by the {@link #release} method to\n\t * determine whether the initialization of the editor should be skipped or, if the editor is already initialized, the editor\n\t * should be destroyed.\n\t *\n\t * \t* If `destroyedBeforeInitialization` is true, then the {@link #release} method was invoked before the editor began to mount.\n\t * \t This often occurs in strict mode when we assign a promise to the {@link LifeCycleEditorElementSemaphore#_semaphores} map\n\t * \t and the assigned `mount` callback has not yet been called. In this scenario, it is safe to skip the initialization of the editor\n\t * \t and simply release the semaphore.\n\t *\n\t *\t* If `mountingInProgress` is a Promise, then the {@link #release} method was invoked after the initialization of the editor and\n\t \t the editor must be destroyed before the semaphore is released.\n\t*/\n\tprivate _state: LifeCycleState<R> = {\n\t\tdestroyedBeforeInitialization: false,\n\t\tmountingInProgress: null\n\t};\n\n\tconstructor( element: HTMLElement, lifecycle: LifeCycleAsyncOperators<R> ) {\n\t\tthis._element = element;\n\t\tthis._lifecycle = lifecycle;\n\t\tthis._lock();\n\t}\n\n\t/**\n\t * Getter for {@link #_value}.\n\t */\n\tpublic get value(): R | null {\n\t\treturn this._value;\n\t}\n\n\t/**\n\t * Occasionally, the Watchdog restarts the editor instance, resulting in a new instance being assigned to the semaphore.\n\t * In terms of race conditions, it's generally safer to simply override the semaphore value rather than recreating it\n\t * with a different one.\n\t */\n\tpublic unsafeSetValue( value: R ): void {\n\t\tthis._value = value;\n\n\t\tthis._afterMountCallbacks.forEach( callback => callback( value ) );\n\t\tthis._afterMountCallbacks = [];\n\t}\n\n\t/**\n\t * This registers a callback that will be triggered after the editor has been successfully mounted.\n\t *\n\t * \t* If the editor is already mounted, the callback will be executed immediately.\n\t *\t* If the editor is in the process of mounting, the callback will be executed upon successful mounting.\n\t* \t* If the editor is never mounted, the passed callback will not be executed.\n\t* \t* If an exception is thrown within the callback, it will be re-thrown in the semaphore.\n\t*/\n\tpublic runAfterMount( callback: LifeCycleAfterMountCallback<R> ): void {\n\t\tconst { _value, _afterMountCallbacks } = this;\n\n\t\tif ( _value ) {\n\t\t\tcallback( _value );\n\t\t} else {\n\t\t\t_afterMountCallbacks.push( callback );\n\t\t}\n\t}\n\n\t/**\n\t * This method is used to inform other components that the {@link #_element} will be used by the editor,\n\t * which is initialized by the {@link #_lifecycle} methods.\n\t *\n\t * \t* If an editor is already present on the provided element, the initialization of the current one\n\t * \t will be postponed until the previous one is destroyed.\n\t *\n\t * \t* If the element is empty and does not have an editor attached to it, the currently locked editor will\n\t * \t be mounted immediately.\n\t *\n\t * After the successful initialization of the editor and the assignment of the {@link #_value} member,\n\t * the `onReady` lifecycle method is called.\n\t *\n\t * *Important note:*\n\t *\n\t * It’s really important to keep this method *sync*. If we make this method *async*, it won’t work well because\n\t * it will cause problems when we’re trying to set up the {@link LifeCycleEditorElementSemaphore#_semaphores} map entries.\n\t */\n\tprivate _lock(): void {\n\t\tconst { _semaphores } = LifeCycleElementSemaphore;\n\t\tconst { _state, _element, _lifecycle } = this;\n\n\t\t// This promise signifies that the previous editor is still attached to the current element.\n\t\t// Upon successful resolution, it will indicate that it is safe to assume that the element has\n\t\t// no assigned editor instance and can be reinitialized.\n\t\tconst prevElementSemaphore = _semaphores.get( _element ) || Promise.resolve( null );\n\n\t\t// This is a lock that will be resolved after the `release` method is called. Due to this lock,\n\t\t// the promise will never be resolved until the editor is destroyed.\n\t\tconst releaseLock = createDefer();\n\t\tthis._releaseLock = releaseLock;\n\n\t\t// This is the initialization of the editor that occurs after the previous editor has been detached from the specified element.\n\t\t//\n\t\t// If the `release` method was called before the initialization of the current editor instance, then it will be skipped.\n\t\t// This situation occurs quite frequently when we have three or more rerenders in a row, and it doesn't make sense to initialize\n\t\t// the second editor because it will be overridden anyway by the third one.\n\t\tconst newElementSemaphore = prevElementSemaphore\n\t\t\t.then( () => {\n\t\t\t\tif ( _state.destroyedBeforeInitialization ) {\n\t\t\t\t\treturn Promise.resolve( undefined );\n\t\t\t\t}\n\n\t\t\t\t// This variable will be used later in the `release` method to determine\n\t\t\t\t// whether the editor is being destroyed prior to initialization.\n\t\t\t\t_state.mountingInProgress = _lifecycle.mount().then( mountResult => {\n\t\t\t\t\tif ( mountResult ) {\n\t\t\t\t\t\tthis.unsafeSetValue( mountResult );\n\t\t\t\t\t}\n\n\t\t\t\t\treturn mountResult;\n\t\t\t\t} );\n\n\t\t\t\treturn _state.mountingInProgress;\n\t\t\t} )\n\t\t\t.then( async mountResult => {\n\t\t\t\t// Everything is fine, all ready callback might be fired here.\n\t\t\t\tif ( mountResult && _lifecycle.afterMount ) {\n\t\t\t\t\tawait _lifecycle.afterMount( {\n\t\t\t\t\t\telement: _element,\n\t\t\t\t\t\tmountResult\n\t\t\t\t\t} );\n\t\t\t\t}\n\t\t\t} )\n\n\t\t\t// It will be released after destroying of editor by the {@link #_release method}.\n\t\t\t.then( () => releaseLock.promise )\n\n\t\t\t// Prevent hanging of semaphore during mount, just assume that everything is fine\n\t\t\t.catch( error => {\n\t\t\t\tconsole.error( 'Semaphore mounting error:', error );\n\t\t\t} )\n\n\t\t\t// Remove semaphore from map if released.\n\t\t\t.then( () => {\n\t\t\t\tif ( _semaphores.get( _element ) === newElementSemaphore ) {\n\t\t\t\t\t_semaphores.delete( _element );\n\t\t\t\t}\n\t\t\t} );\n\n\t\t_semaphores.set( _element, newElementSemaphore );\n\t}\n\n\t/**\n\t * Inverse of {@link #_lock} method that tries to destroy attached editor.\n\t *\n\t * \t* If editor is being already attached to element (or is in attaching process) then after fully initialization of editor\n\t * \t destroy is performed and semaphore is released. The {@link #_lifecycle} unmount method is called.\n\t *\n\t * \t* If editor is being destroyed before initialization then it does nothing but sets `destroyedBeforeInitialization` flag that\n\t * \t will be later checked by {@link #_lock} method in initialization. The {@link #_lifecycle} unmount method is not called.\n\t *\n\t * *Important note:*\n\t *\n\t * It’s really important to keep this method *sync*. If we make this method *async*, it won’t work well because\n\t * it will cause problems when we’re trying to set up the {@link LifeCycleEditorElementSemaphore#_semaphores} map entries.\n\t */\n\tpublic readonly release = once( () => {\n\t\tconst { _releaseLock, _state, _element, _lifecycle } = this;\n\n\t\tif ( _state.mountingInProgress ) {\n\t\t\t_state.mountingInProgress\n\t\t\t\t.then( () => _lifecycle.unmount( {\n\t\t\t\t\telement: _element,\n\n\t\t\t\t\t// Mount result might be overridden by watchdog during restart so use instance variable.\n\t\t\t\t\tmountResult: this.value!\n\t\t\t\t} ) )\n\n\t\t\t\t// Prevent hanging of semaphore during unmount, just assume that everything is fine\n\t\t\t\t.catch( error => {\n\t\t\t\t\tconsole.error( 'Semaphore unmounting error:', error );\n\t\t\t\t} )\n\n\t\t\t\t.then( _releaseLock!.resolve )\n\t\t\t\t.then( () => {\n\t\t\t\t\tthis._value = null;\n\t\t\t\t} );\n\t\t} else {\n\t\t\t_state.destroyedBeforeInitialization = true;\n\t\t\t_releaseLock!.resolve();\n\t\t}\n\t} );\n}\n\nexport type LifeCycleAfterMountCallback<R> = ( mountResult: R ) => void;\n\ntype LifeCycleState<R> = {\n\tdestroyedBeforeInitialization: boolean;\n\tmountingInProgress: Promise<R> | null;\n};\n\ntype LifeCyclePostMountAttrs<R> = {\n\telement: HTMLElement;\n\tmountResult: R;\n};\n\nexport type LifeCycleAsyncOperators<R> = {\n\tmount: () => Promise<R>;\n\tafterMount?: ( result: LifeCyclePostMountAttrs<R> ) => Promise<void> | void;\n\tunmount: ( result: LifeCyclePostMountAttrs<R> ) => Promise<void>;\n};\n","/**\n * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.\n * For licensing, see LICENSE.md.\n */\n\nimport type { Config, EditorConfig } from 'ckeditor5';\n\n/**\n * The symbol cannot be used as a key because config getters require strings as keys.\n */\nconst ReactContextMetadataKey = '$__CKEditorReactContextMetadata';\n\n/**\n * Sets the metadata in the object.\n *\n * @param metadata The metadata to set.\n * @param object The object to set the metadata in.\n * @returns The object with the metadata set.\n */\nexport function withCKEditorReactContextMetadata(\n\tmetadata: CKEditorConfigContextMetadata,\n\tconfig: EditorConfig\n): EditorConfig & { [ ReactContextMetadataKey ]: CKEditorConfigContextMetadata } {\n\treturn {\n\t\t...config,\n\t\t[ ReactContextMetadataKey ]: metadata\n\t};\n}\n\n/**\n * Tries to extract the metadata from the object.\n *\n * @param object The object to extract the metadata from.\n */\nexport function tryExtractCKEditorReactContextMetadata( object: Config<any> ): CKEditorConfigContextMetadata | null {\n\treturn object.get( ReactContextMetadataKey );\n}\n\n/**\n * The metadata that is stored in the React context.\n */\nexport type CKEditorConfigContextMetadata = {\n\n\t/**\n\t * The name of the editor in the React context. It'll be later used in the `useInitializedCKEditorsMap` hook\n\t * to track the editor initialization and destruction events.\n\t */\n\tname?: string;\n\n\t/**\n\t * Any additional metadata that can be stored in the context.\n\t */\n\t[x: string | number | symbol]: unknown;\n};\n","/**\n * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.\n * For licensing, see LICENSE.md.\n */\n\nimport { useEffect, useRef, type MutableRefObject } from 'react';\n\n/**\n * Custom hook that returns a mutable ref object indicating whether the component is mounted or not.\n *\n * @returns The mutable ref object.\n */\nexport const useIsMountedRef = (): MutableRefObject<boolean> => {\n\tconst mountedRef = useRef<boolean>( false );\n\n\tuseEffect( () => {\n\t\tmountedRef.current = true;\n\n\t\treturn () => {\n\t\t\tmountedRef.current = false;\n\t\t};\n\t}, [] );\n\n\treturn mountedRef;\n};\n","/**\n * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.\n * For licensing, see LICENSE.md.\n */\n\nimport { useCallback, useRef } from 'react';\n\n/**\n * Hook that guarantees that returns constant reference for passed function.\n * Useful for preventing closures from capturing cached scope variables (avoiding the stale closure problem).\n */\nexport const useRefSafeCallback = <A extends Array<unknown>, R>( fn: ( ...args: A ) => R ): typeof fn => {\n\tconst callbackRef = useRef<typeof fn>();\n\tcallbackRef.current = fn;\n\n\treturn useCallback(\n\t\t( ...args: A ): R => ( callbackRef.current as typeof fn )( ...args ),\n\t\t[]\n\t);\n};\n","/**\n * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.\n * For licensing, see LICENSE.md.\n */\n\nimport { useEffect } from 'react';\nimport { useRefSafeCallback } from '../hooks/useRefSafeCallback.js';\n\nimport type { CollectionAddEvent, Context, ContextWatchdog, Editor } from 'ckeditor5';\nimport type { ContextWatchdogValue } from './ckeditorcontext.js';\n\nimport {\n\ttryExtractCKEditorReactContextMetadata,\n\ttype CKEditorConfigContextMetadata\n} from './setCKEditorReactContextMetadata.js';\n\n/**\n * A hook that listens for the editor initialization and destruction events and updates the editors map.\n *\n * @param config The configuration of the hook.\n * @param config.currentContextWatchdog The current context watchdog value.\n * @param config.onChangeInitializedEditors The function that updates the editors map.\n * @example\n * ```ts\n * useInitializedCKEditorsMap( {\n * \tcurrentContextWatchdog,\n * \tonChangeInitializedEditors: ( editors, context ) => {\n * \t\tconsole.log( 'Editors:', editors );\n * \t}\n * } );\n * ```\n */\nexport const useInitializedCKEditorsMap = <TContext extends Context>(\n\t{\n\t\tcurrentContextWatchdog,\n\t\tonChangeInitializedEditors\n\t}: InitializedContextEditorsConfig<TContext>\n): void => {\n\t// We need to use the safe callback to prevent the stale closure problem.\n\tconst onChangeInitializedEditorsSafe = useRefSafeCallback( onChangeInitializedEditors || ( () => {} ) );\n\n\tuseEffect( () => {\n\t\tif ( currentContextWatchdog.status !== 'initialized' ) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst { watchdog } = currentContextWatchdog;\n\t\tconst editors = watchdog?.context?.editors;\n\n\t\tif ( !editors ) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Get the initialized editors from\n\t\tconst getInitializedContextEditors = () => [ ...editors ].reduce<InitializedEditorsMap>(\n\t\t\t( map, editor ) => {\n\t\t\t\tif ( editor.state !== 'ready' ) {\n\t\t\t\t\treturn map;\n\t\t\t\t}\n\n\t\t\t\tconst metadata = tryExtractCKEditorReactContextMetadata( editor.config );\n\t\t\t\tconst nameOrId = metadata?.name ?? editor.id;\n\n\t\t\t\tmap[ nameOrId ] = {\n\t\t\t\t\tinstance: editor,\n\t\t\t\t\tmetadata\n\t\t\t\t};\n\n\t\t\t\treturn map;\n\t\t\t},\n\t\t\tObject.create( {} ) // Prevent the prototype pollution.\n\t\t);\n\n\t\t// The function that is called when the editor status changes.\n\t\tconst onEditorStatusChange = () => {\n\t\t\tonChangeInitializedEditorsSafe(\n\t\t\t\tgetInitializedContextEditors(),\n\t\t\t\twatchdog\n\t\t\t);\n\t\t};\n\n\t\t// Add the existing editors to the map.\n\t\tconst onAddEditor = ( _: unknown, editor: Editor ) => {\n\t\t\teditor.once( 'ready', onEditorStatusChange, { priority: 'lowest' } );\n\t\t\teditor.once( 'destroy', onEditorStatusChange, { priority: 'lowest' } );\n\t\t};\n\n\t\teditors.on<CollectionAddEvent<Editor>>( 'add', onAddEditor );\n\n\t\treturn () => {\n\t\t\teditors.off( 'add', onAddEditor );\n\t\t};\n\t}, [ currentContextWatchdog ] );\n};\n\n/**\n * A map of initialized editors.\n */\ntype InitializedEditorsMap = Record<string, {\n\tinstance: Editor;\n\tmetadata: CKEditorConfigContextMetadata | null;\n}>;\n\n/**\n * The configuration of the `useInitializedCKEditorsMap` hook.\n */\nexport type InitializedContextEditorsConfig<TContext extends Context> = {\n\n\t/**\n\t * The current context watchdog value.\n\t */\n\tcurrentContextWatchdog: ContextWatchdogValue<TContext>;\n\n\t/**\n\t * The callback called when the editors map changes.\n\t */\n\tonChangeInitializedEditors?: (\n\t\teditors: InitializedEditorsMap,\n\t\twatchdog: ContextWatchdog<TContext>\n\t) => void;\n};\n","/**\n * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.\n * For licensing, see LICENSE.md.\n */\n\nimport React, {\n\tuseRef, useContext, useState, useEffect,\n\ttype PropsWithChildren,\n\ttype ReactElement\n} from 'react';\n\nimport { uid } from '@ckeditor/ckeditor5-integrations-common';\nimport { useIsMountedRef } from '../hooks/useIsMountedRef';\nimport {\n\tuseInitializedCKEditorsMap,\n\ttype InitializedContextEditorsConfig\n} from './useInitializedCKEditorsMap';\n\nimport type {\n\tContextWatchdog,\n\tWatchdogConfig,\n\tContext,\n\tContextConfig\n} from 'ckeditor5';\n\nexport const ContextWatchdogContext = React.createContext<ContextWatchdogValue | null>( null );\n\n/**\n * Custom hook that returns the CKEditor Watchdog context value.\n */\nexport const useCKEditorWatchdogContext = (): ContextWatchdogValue | null =>\n\tuseContext( ContextWatchdogContext );\n\n/**\n * A React component that provides a context for CKEditor.\n */\nconst CKEditorContext = <TContext extends Context = Context>( props: Props<TContext> ): ReactElement | null => {\n\tconst {\n\t\tid, context, watchdogConfig,\n\t\tchildren, config, onReady,\n\t\tcontextWatchdog: ContextWatchdogConstructor,\n\t\tisLayoutReady = true,\n\t\tonChangeInitializedEditors,\n\t\tonError = ( error, details ) => console.error( error, details )\n\t} = props;\n\n\tconst isMountedRef = useIsMountedRef();\n\tconst prevWatchdogInitializationIDRef = useRef<string | null>( null );\n\n\t// The currentContextWatchdog state is set to 'initializing' because it is checked later in the CKEditor component\n\t// which is waiting for the full initialization of the context watchdog.\n\tconst [ currentContextWatchdog, setCurrentContextWatchdog ] = useState<ContextWatchdogValue<TContext>>( {\n\t\tstatus: 'initializing'\n\t} );\n\n\t// Lets initialize the context watchdog when the layout is ready.\n\tuseEffect( () => {\n\t\tif ( isLayoutReady ) {\n\t\t\tinitializeContextWatchdog();\n\t\t} else {\n\t\t\tsetCurrentContextWatchdog( {\n\t\t\t\tstatus: 'initializing'\n\t\t\t} );\n\t\t}\n\t}, [ id, isLayoutReady ] );\n\n\t// Cleanup the context watchdog when the component is unmounted. Abort if the watchdog is not initialized.\n\tuseEffect( () => () => {\n\t\tif ( currentContextWatchdog.status === 'initialized' ) {\n\t\t\tcurrentContextWatchdog.watchdog.destroy();\n\t\t}\n\t}, [ currentContextWatchdog ] );\n\n\t// Listen for the editor initialization and destruction events and call the onChangeInitializedEditors function.\n\tuseInitializedCKEditorsMap( {\n\t\tcurrentContextWatchdog,\n\t\tonChangeInitializedEditors\n\t} );\n\n\t/**\n\t * Regenerates the initialization ID by generating a random ID and updating the previous watchdog initialization ID.\n\t * This is necessary to ensure that the state update is performed only if the current initialization ID matches the previous one.\n\t * This helps to avoid race conditions and ensures that the correct context watchdog is associated with the component.\n\t *\n\t * @returns The regenerated initialization ID.\n\t */\n\tfunction regenerateInitializationID() {\n\t\tprevWatchdogInitializationIDRef.current = uid();\n\n\t\treturn prevWatchdogInitializationIDRef.current;\n\t}\n\n\t/**\n\t * Checks if the state can be updated based on the provided initialization ID.\n\t *\n\t * @param initializationID The initialization ID to compare with the previous one.\n\t * @returns A boolean indicating whether the state can be updated.\n\t */\n\tfunction canUpdateState( initializationID: string ) {\n\t\treturn prevWatchdogInitializationIDRef.current === initializationID && isMountedRef.current;\n\t}\n\n\t/**\n\t * Initializes the context watchdog.\n\t *\n\t * @returns Watchdog instance.\n\t */\n\tfunction initializeContextWatchdog() {\n\t\t// The prevWatchdogInitializationID variable is used to keep track of the previous initialization ID.\n\t\t// It is used to ensure that the state update is performed only if the current initialization ID matches the previous one.\n\t\t// This helps to avoid race conditions and ensures that the correct context watchdog is associated with the component.\n\t\tconst watchdogInitializationID = regenerateInitializationID();\n\t\tconst contextWatchdog = new ContextWatchdogConstructor( context!, watchdogConfig );\n\n\t\t// Handle error event from context watchdog.\n\t\tcontextWatchdog.on( 'error', ( _, errorEvent ) => {\n\t\t\t/* istanbul ignore else -- @preserve */\n\t\t\tif ( canUpdateState( watchdogInitializationID ) ) {\n\t\t\t\tonError( errorEvent.error, {\n\t\t\t\t\tphase: 'runtime',\n\t\t\t\t\twillContextRestart: errorEvent.causesRestart\n\t\t\t\t} );\n\t\t\t}\n\t\t} );\n\n\t\t// Handle state change event from context watchdog.\n\t\tcontextWatchdog.on( 'stateChange', () => {\n\t\t\tif ( onReady && contextWatchdog.state === 'ready' && canUpdateState( watchdogInitializationID ) ) {\n\t\t\t\tonReady(\n\t\t\t\t\tcontextWatchdog.context! as TContext,\n\t\t\t\t\tcontextWatchdog\n\t\t\t\t);\n\t\t\t}\n\t\t} );\n\n\t\t// Create the context watchdog and initialize it with the provided config.\n\t\tcontextWatchdog\n\t\t\t.create( config )\n\t\t\t.then( () => {\n\t\t\t\t// Check if the state update is still valid and update the current context watchdog.\n\t\t\t\tif ( canUpdateState( watchdogInitializationID ) ) {\n\t\t\t\t\tsetCurrentContextWatchdog( {\n\t\t\t\t\t\tstatus: 'initialized',\n\t\t\t\t\t\twatchdog: contextWatchdog\n\t\t\t\t\t} );\n\t\t\t\t} else {\n\t\t\t\t\t// Destroy the context watchdog if the state update is no longer valid.\n\t\t\t\t\tcontextWatchdog.destroy();\n\t\t\t\t}\n\t\t\t} )\n\t\t\t.catch( error => {\n\t\t\t\t// Update the current context watchdog with the error status.\n\t\t\t\tif ( canUpdateState( watchdogInitializationID ) ) {\n\t\t\t\t\t// Handle error during context watchdog initialization.\n\t\t\t\t\tonError( error, {\n\t\t\t\t\t\tphase: 'initialization',\n\t\t\t\t\t\twillContextRestart: false\n\t\t\t\t\t} );\n\n\t\t\t\t\tsetCurrentContextWatchdog( {\n\t\t\t\t\t\tstatus: 'error',\n\t\t\t\t\t\terror\n\t\t\t\t\t} );\n\t\t\t\t}\n\t\t\t} );\n\n\t\treturn contextWatchdog;\n\t}\n\n\treturn (\n\t\t<ContextWatchdogContext.Provider value={currentContextWatchdog}>\n\t\t\t{children}\n\t\t</ContextWatchdogContext.Provider>\n\t);\n};\n\n/**\n * Checks if the given object is of type ContextWatchdogValue.\n *\n * @param obj The object to be checked.\n * @returns True if the object is of type ContextWatchdogValue, false otherwise.\n */\nexport const isContextWatchdogValue = ( obj: any ): obj is ContextWatchdogValue =>\n\t!!obj && typeof obj === 'object' && 'status' in obj && [ 'initializing', 'initialized', 'error' ].includes( obj.status );\n\n/**\n * Checks if the provided object is a context watchdog value with the specified status.\n */\nexport const isContextWatchdogValueWithStatus = <S extends ContextWatchdogValueStatus>( status: S ) =>\n\t( obj: any ): obj is ExtractContextWatchdogValueByStatus<S> =>\n\t\tisContextWatchdogValue( obj ) && obj.status === status;\n\n/**\n * Checks if the context watchdog is currently initializing.\n */\nexport const isContextWatchdogInitializing = isContextWatchdogValueWithStatus( 'initializing' );\n\n/**\n * Checks if the provided object is a fully initialized context watchdog value. It prevents race conditions between\n * watchdog state that is not fully synchronized with the context state. For example, the watchdog state can be 'destroyed'\n * while the context is still being initialized because context setState is pending.\n */\nexport const isContextWatchdogReadyToUse = ( obj: any ): obj is ExtractContextWatchdogValueByStatus<'initialized'> => (\n\tisContextWatchdogValueWithStatus( 'initialized' )( obj ) &&\n\tobj.watchdog.state === 'ready'\n);\n\n/**\n * Represents the value of the ContextWatchdog in the CKEditor context.\n */\nexport type ContextWatchdogValue<TContext extends Context = Context> =\n\t| {\n\t\tstatus: 'initializing';\n\t}\n\t| {\n\t\tstatus: 'initialized';\n\t\twatchdog: ContextWatchdog<TContext>;\n\t}\n\t| {\n\t\tstatus: 'error';\n\t\terror: ErrorDetails;\n\t};\n\n/**\n * Represents the status of the ContextWatchdogValue.\n */\nexport type ContextWatchdogValueStatus = ContextWatchdogValue[ 'status' ];\n\n/**\n * Extracts a specific type of `ContextWatchdogValue` based on its status.\n */\nexport type ExtractContextWatchdogValueByStatus<S extends ContextWatchdogValueStatus> = Extract<\n\tContextWatchdogValue,\n\t{ status: S }\n>;\n\n/**\n * Props for the CKEditorContext component.\n */\nexport type Props<TContext extends Context> =\n\t& PropsWithChildren\n\t& Pick<InitializedContextEditorsConfig<TContext>, 'onChangeInitializedEditors'>\n\t& {\n\t\tid?: string;\n\t\tisLayoutReady?: boolean;\n\t\tcontext?: { create( ...args: any ): Promise<TContext> };\n\t\tcontextWatchdog: typeof ContextWatchdog<TContext>;\n\t\twatchdogConfig?: WatchdogConfig;\n\t\tconfig?: ContextConfig;\n\t\tonReady?: ( context: TContext, watchdog: ContextWatchdog<TContext> ) => void;\n\t\tonError?: ( error: Error, details: ErrorDetails ) => void;\n\t};\n\ntype ErrorDetails = {\n\tphase: 'initialization' | 'runtime';\n\twillContextRestart: boolean;\n};\n\nexport default CKEditorContext;\n","/**\n * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.\n * For licensing, see LICENSE.md.\n */\n\n/* globals window */\n\nimport React from 'react';\nimport PropTypes, { type InferProps, type Validator } from 'prop-types';\n\nimport type {\n\tEventInfo,\n\tEditor,\n\tEditorConfig,\n\tDocumentChangeEvent,\n\tEditorWatchdog,\n\tContextWatchdog,\n\tWatchdogConfig,\n\tEditorCreatorFunction\n} from 'ckeditor5';\n\nimport type { EditorSemaphoreMountResult } from './lifecycle/LifeCycleEditorSemaphore';\n\nimport { uid } from '@ckeditor/ckeditor5-integrations-common';\nimport { LifeCycleElementSemaphore } from './lifecycle/LifeCycleElementSemaphore';\n\nimport {\n\twithCKEditorReactContextMetadata,\n\ttype CKEditorConfigContextMetadata\n} from './context/setCKEditorReactContextMetadata';\n\nimport {\n\tContextWatchdogContext,\n\tisContextWatchdogInitializing,\n\tisContextWatchdogReadyToUse\n} from './context/ckeditorcontext';\n\nconst REACT_INTEGRATION_READ_ONLY_LOCK_ID = 'Lock from React integration (@ckeditor/ckeditor5-react)';\n\n// eslint-disable-next-line @typescript-eslint/ban-types\nexport default class CKEditor<TEditor extends Editor> extends React.Component<Props<TEditor>> {\n\t/**\n\t * After mounting the editor, the variable will contain a reference to the created editor.\n\t * @see: https://ckeditor.com/docs/ckeditor5/latest/api/module_core_editor_editor-Editor.html\n\t */\n\tprivate domContainer = React.createRef<HTMLDivElement>();\n\n\t/**\n\t * Unlocks element in editor semaphore after destroy editor instance.\n\t */\n\tprivate editorSemaphore: LifeCycleElementSemaphore<EditorSemaphoreMountResult<TEditor>> | null = null;\n\n\tconstructor( props: Props<TEditor> ) {\n\t\tsuper( props );\n\n\t\tthis._checkVersion();\n\t}\n\n\t/**\n\t * Checks if the CKEditor version used in the application is compatible with the component.\n\t */\n\tprivate _checkVersion(): void {\n\t\tconst { CKEDITOR_VERSION } = window;\n\n\t\tif ( !CKEDITOR_VERSION ) {\n\t\t\treturn console.warn( 'Cannot find the \"CKEDITOR_VERSION\" in the \"window\" scope.' );\n\t\t}\n\n\t\tconst [ major ] = CKEDITOR_VERSION.split( '.' ).map( Number );\n\n\t\tif ( major >= 42 || CKEDITOR_VERSION.startsWith( '0.0.0' ) ) {\n\t\t\treturn;\n\t\t}\n\n\t\tconsole.warn( 'The <CKEditor> component requires using CKEditor 5 in version 42+ or nightly build.' );\n\t}\n\n\tprivate get _semaphoreValue(): EditorSemaphoreMountResult<TEditor> | null {\n\t\tconst { editorSemaphore } = this;\n\n\t\treturn editorSemaphore ? editorSemaphore.value : null;\n\t}\n\n\t/**\n\t * An watchdog instance.\n\t */\n\tpublic get watchdog(): EditorWatchdog<TEditor> | EditorWatchdogAdapter<TEditor> | null {\n\t\tconst { _semaphoreValue } = this;\n\n\t\treturn _semaphoreValue ? _semaphoreValue.watchdog : null;\n\t}\n\n\t/**\n\t * An editor instance.\n\t */\n\tpublic get editor(): Editor | null {\n\t\tconst { _semaphoreValue } = this;\n\n\t\treturn _semaphoreValue ? _semaphoreValue.instance : null;\n\t}\n\n\t/**\n\t * The CKEditor component should not be updated by React itself.\n\t * However, if the component identifier changes, the whole structure should be created once again.\n\t */\n\tpublic override shouldComponentUpdate( nextProps: Readonly<Props<TEditor>> ): boolean {\n\t\tconst { props, editorSemaphore } = this;\n\n\t\t// Only when the component identifier changes the whole structure should be re-created once again.\n\t\tif ( nextProps.id !== props.id ) {\n\t\t\treturn true;\n\t\t}\n\n\t\tif ( nextProps.disableWatchdog !== props.disableWatchdog ) {\n\t\t\treturn true;\n\t\t}\n\n\t\tif ( editorSemaphore ) {\n\t\t\teditorSemaphore.runAfterMount( ( { instance } ) => {\n\t\t\t\tif ( this._shouldUpdateEditorData( props, nextProps, instance ) ) {\n\t\t\t\t\tinstance.data.set( nextProps.data! );\n\t\t\t\t}\n\t\t\t} );\n\n\t\t\tif ( 'disabled' in nextProps ) {\n\t\t\t\teditorSemaphore.runAfterMount( ( { instance } ) => {\n\t\t\t\t\tif ( nextProps.disabled ) {\n\t\t\t\t\t\tinstance.enableReadOnlyMode( REACT_INTEGRATION_READ_ONLY_LOCK_ID );\n\t\t\t\t\t} else {\n\t\t\t\t\t\tinstance.disableReadOnlyMode( REACT_INTEGRATION_READ_ONLY_LOCK_ID );\n\t\t\t\t\t}\n\t\t\t\t} );\n\t\t\t}\n\t\t}\n\n\t\treturn false;\n\t}\n\n\t/**\n\t * Initialize the editor when the component is mounted.\n\t */\n\tpublic override componentDidMount(): void {\n\t\tif ( !isContextWatchdogInitializing( this.context ) ) {\n\t\t\tthis._initLifeCycleSemaphore();\n\t\t}\n\t}\n\n\t/**\n\t * Re-render the entire component once again. The old editor will be destroyed and the new one will be created.\n\t */\n\tpublic override componentDidUpdate(): void {\n\t\tif ( !isContextWatchdogInitializing( this.context ) ) {\n\t\t\tthis._initLifeCycleSemaphore();\n\t\t}\n\t}\n\n\t/**\n\t * Destroy the editor before unmounting the component.\n\t */\n\tpublic override componentWillUnmount(): void {\n\t\tthis._unlockLifeCycleSemaphore();\n\t}\n\n\t/**\n\t * Async destroy attached editor and unlock element semaphore.\n\t */\n\tprivate _unlockLifeCycleSemaphore() {\n\t\tif ( this.editorSemaphore ) {\n\t\t\tthis.editorSemaphore.release();\n\t\t\tthis.editorSemaphore = null;\n\t\t}\n\t}\n\n\t/**\n\t * Unlocks previous editor semaphore and creates new one..\n\t */\n\tprivate _initLifeCycleSemaphore() {\n\t\tthis._unlockLifeCycleSemaphore();\n\t\tthis.editorSemaphore = new LifeCycleElementSemaphore( this.domContainer.current!, {\n\t\t\tmount: async () => this._initializeEditor(),\n\t\t\tafterMount: ( { mountResult } ) => {\n\t\t\t\tconst { onReady } = this.props;\n\n\t\t\t\tif ( onReady && this.domContainer.current !== null ) {\n\t\t\t\t\tonReady( mountResult.instance );\n\t\t\t\t}\n\t\t\t},\n\t\t\tunmount: async ( { element, mountResult } ) => {\n\t\t\t\tconst { onAfterDestroy } = this.props;\n\n\t\t\t\ttry {\n\t\t\t\t\tawait this._destroyEditor( mountResult );\n\n\t\t\t\t\t/**\n\t\t\t\t\t * Make sure that nothing left in actual editor element. There can be custom integrations that\n\t\t\t\t\t * appends something to container. Let's reset element every update cycle before mounting another\n\t\t\t\t\t * editor instance.\n\t\t\t\t\t */\n\t\t\t\t\telement.innerHTML = '';\n\t\t\t\t} finally {\n\t\t\t\t\t/**\n\t\t\t\t\t * Broadcast information about destroying current instance. It is useful for removing duplicated\n\t\t\t\t\t * toolbars in decoupled editor mode.\n\t\t\t\t\t */\n\t\t\t\t\tif ( onAfterDestroy ) {\n\t\t\t\t\t\tonAfterDestroy( mountResult.instance );\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t} );\n\t}\n\n\t/**\n\t * Render a <div> element which will be replaced by CKEditor.\n\t */\n\tpublic override render(): React.ReactNode {\n\t\treturn (\n\t\t\t<div ref={ this.domContainer }></div>\n\t\t);\n\t}\n\n\t/**\n\t * Initializes the editor by creating a proper watchdog and initializing it with the editor's configuration.\n\t */\n\tprivate async _initializeEditor(): Promise<EditorSemaphoreMountResult<TEditor>> {\n\t\tif ( this.props.disableWatchdog ) {\n\t\t\tconst instance = await this._createEditor( this.domContainer.current!, this._getConfig() );\n\n\t\t\treturn {\n\t\t\t\tinstance: instance as TEditor,\n\t\t\t\twatchdog: null\n\t\t\t};\n\t\t}\n\n\t\tconst watchdog = ( () => {\n\t\t\t// There is small delay where React did not update the context yet but watchdog is already destroyed.\n\t\t\t// However editor should be created again in such case, after receiving new context.\n\t\t\tif ( isContextWatchdogReadyToUse( this.context ) ) {\n\t\t\t\treturn new EditorWatchdogAdapter( this.context.watchdog );\n\t\t\t}\n\n\t\t\treturn new this.props.editor.EditorWatchdog( this.props.editor, this.props.watchdogConfig );\n\t\t} )() as EditorWatchdogAdapter<TEditor>;\n\n\t\tconst totalRestartsRef = {\n\t\t\tcurrent: 0\n\t\t};\n\n\t\twatchdog.setCreator( async ( el, config ) => {\n\t\t\tconst { editorSemaphore } = this;\n\t\t\tconst { onAfterDestroy } = this.props;\n\n\t\t\tif ( totalRestartsRef.current > 0 && onAfterDestroy && editorSemaphore?.value?.instance ) {\n\t\t\t\tonAfterDestroy( editorSemaphore.value.instance );\n\t\t\t}\n\n\t\t\tconst instance = await this._createEditor( el as any, config );\n\n\t\t\t// The editor semaphore can be unavailable at this stage. There is a small chance that the component\n\t\t\t// was destroyed while watchdog was initializing new instance of editor. In such case, we should not\n\t\t\t// call any callbacks or set any values to the semaphore.\n\t\t\tif ( editorSemaphore && totalRestartsRef.current > 0 ) {\n\t\t\t\teditorSemaphore.unsafeSetValue( {\n\t\t\t\t\tinstance,\n\t\t\t\t\twatchdog\n\t\t\t\t} );\n\n\t\t\t\tsetTimeout( () => {\n\t\t\t\t\tif ( this.props.onReady ) {\n\t\t\t\t\t\tthis.props.onReady( watchdog!.editor as TEditor );\n\t\t\t\t\t}\n\t\t\t\t} );\n\t\t\t}\n\n\t\t\ttotalRestartsRef.current++;\n\t\t\treturn instance;\n\t\t} );\n\n\t\twatchdog.on( 'error', ( _, { error, causesRestart } ) => {\n\t\t\tconst onError = this.props.onError || console.error;\n\t\t\tonError( error, { phase: 'runtime', willEditorRestart: causesRestart } );\n\t\t} );\n\n\t\tawait watchdog\n\t\t\t.create( this.domContainer.current!, this._getConfig() )\n\t\t\t.catch( error => {\n\t\t\t\tconst onError = this.props.onError || console.error;\n\t\t\t\tonError( error, { phase: 'initialization', willEditorRestart: false } );\n\t\t\t} );\n\n\t\treturn {\n\t\t\twatchdog,\n\t\t\tinstance: watchdog!.editor\n\t\t};\n\t}\n\n\t/**\n\t * Creates an editor from the element and configuration.\n\t *\n\t * @param element The source element.\n\t * @param config CKEditor 5 editor configuration.\n\t */\n\tprivate _createEditor( element: HTMLElement | string | Record<string, string>, config: EditorConfig ): Promise<TEditor> {\n\t\tconst { contextItemMetadata } = this.props;\n\n\t\tif ( contextItemMetadata ) {\n\t\t\tconfig = withCKEditorReactContextMetadata( contextItemMetadata, config );\n\t\t}\n\n\t\treturn this.props.editor.create( element as HTMLElement, config )\n\t\t\t.then( editor => {\n\t\t\t\tif ( 'disabled' in this.props ) {\n\t\t\t\t\t// Switch to the read-only mode if the `[disabled]` attribute is specified.\n\t\t\t\t\t/* istanbul ignore else -- @preserve */\n\t\t\t\t\tif ( this.props.disabled ) {\n\t\t\t\t\t\teditor.enableReadOnlyMode( REACT_INTEGRATION_READ_ONLY_LOCK_ID );\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tconst modelDocument = editor.model.document;\n\t\t\t\tconst viewDocument = editor.editing.view.document;\n\n\t\t\t\tmodelDocument.on<DocumentChangeEvent>( 'change:data', event => {\n\t\t\t\t\t/* istanbul ignore else -- @preserve */\n\t\t\t\t\tif ( this.props.onChange ) {\n\t\t\t\t\t\tthis.props.onChange( event, editor );\n\t\t\t\t\t}\n\t\t\t\t} );\n\n\t\t\t\tviewDocument.on( 'focus', event => {\n\t\t\t\t\t/* istanbul ignore else -- @preserve */\n\t\t\t\t\tif ( this.props.onFocus ) {\n\t\t\t\t\t\tthis.props.onFocus( event, editor );\n\t\t\t\t\t}\n\t\t\t\t} );\n\n\t\t\t\tviewDocument.on( 'blur', event => {\n\t\t\t\t\t/* istanbul ignore else -- @preserve */\n\t\t\t\t\tif ( this.props.onBlur ) {\n\t\t\t\t\t\tthis.props.onBlur( event, editor );\n\t\t\t\t\t}\n\t\t\t\t} );\n\n\t\t\t\treturn editor;\n\t\t\t} );\n\t}\n\n\t/**\n\t * Destroys the editor by destroying the watchdog.\n\t */\n\tprivate async _destroyEditor( initializeResult: EditorSemaphoreMountResult<Editor> ): Promise<void> {\n\t\tconst { watchdog, instance } = initializeResult;\n\n\t\treturn new Promise<void>( ( resolve, reject ) => {\n\t\t\t// It may happen during the tests that the watchdog instance is not assigned before destroying itself. See: #197.\n\t\t\t//\n\t\t\t// Additionally, we need to find a way to detect if the whole context has been destroyed. As `componentWillUnmount()`\n\t\t\t// could be fired by <CKEditorContext /> and <CKEditor /> at the same time, this `setTimeout()` makes sure\n\t\t\t// that <CKEditorContext /> component will be destroyed first, so during the code execution\n\t\t\t// the `ContextWatchdog#state` would have a correct value. See `EditorWatchdogAdapter#destroy()` for more information.\n\t\t\t/* istanbul ignore next -- @preserve */\n\t\t\tsetTimeout( async () => {\n\t\t\t\ttry {\n\t\t\t\t\tif ( watchdog ) {\n\t\t\t\t\t\tawait watchdog.destroy();\n\t\t\t\t\t\treturn resolve();\n\t\t\t\t\t}\n\n\t\t\t\t\tif ( instance ) {\n\t\t\t\t\t\tawait instance.destroy();\n\t\t\t\t\t\treturn resolve();\n\t\t\t\t\t}\n\n\t\t\t\t\tresolve();\n\t\t\t\t} catch ( e ) {\n\t\t\t\t\tconsole.error( e );\n\t\t\t\t\treject( e );\n\t\t\t\t}\n\t\t\t} );\n\t\t} );\n\t}\n\n\t/**\n\t * Returns true when the editor should be updated.\n\t *\n\t * @param prevProps Previous react's properties.\n\t * @param nextProps React's properties.\n\t * @param editor Current editor instance.\n\t */\n\tprivate _shouldUpdateEditorData( prevProps: Readonly<Props<TEditor>>, nextProps: Readonly<Props<TEditor>>, editor: TEditor ): boolean {\n\t\t// Check whether `nextProps.data` is equal to `this.props.data` is required if somebody defined the `#data`\n\t\t// property as a static string and updated a state of component when the editor's content has been changed.\n\t\t// If we avoid checking those properties, the editor's content will back to the initial value because\n\t\t// the state has been changed and React will call this method.\n\t\tif ( prevProps.data === nextProps.data ) {\n\t\t\treturn false;\n\t\t}\n\n\t\t// We should not change data if the editor's content is equal to the `#data` property.\n\t\tif ( editor.data.get() === nextProps.data ) {\n\t\t\treturn false;\n\t\t}\n\n\t\treturn true;\n\t}\n\n\t/**\n\t * Returns the editor configuration.\n\t */\n\tprivate _getConfig(): EditorConfig {\n\t\tconst config = this.props.config || {};\n\n\t\tif ( this.props.data && config.initialData ) {\n\t\t\tconsole.warn(\n\t\t\t\t'Editor data should be provided either using `config.initialData` or `content` property. ' +\n\t\t\t\t'The config value takes precedence over `content` property and will be used when both are specified.'\n\t\t\t);\n\t\t}\n\n\t\t// Merge two possible ways of providing data into the `config.initialData` field.\n\t\treturn {\n\t\t\t...config,\n\t\t\tinitialData: config.initialData || this.props.data || ''\n\t\t};\n\t}\n\n\tpublic static override contextType = ContextWatchdogContext;\n\n\t// Properties definition.\n\tpublic static propTypes = {\n\t\teditor: PropTypes.func.isRequired as unknown as Validator<{ create( ...args: any ): Promise<any> }>,\n\t\tdata: PropTypes.string,\n\t\tconfig: PropTypes.object,\n\t\tdisableWatchdog: PropTypes.bool,\n\t\twatchdogConfig: PropTypes.object,\n\t\tonChange: PropTypes.func,\n\t\tonReady: PropTypes.func,\n\t\tonFocus: PropTypes.func,\n\t\tonBlur: PropTypes.func,\n\t\tonError: PropTypes.func,\n\t\tdisabled: PropTypes.bool,\n\t\tid: PropTypes.any\n\t};\n}\n\n/**\n * TODO this is type space definition for props, the CKEditor.propTypes is a run-time props validation that should match.\n */\nexport interface Props<TEditor extends Editor> extends InferProps<typeof CKEditor.propTypes> {\n\teditor: {\n\t\tcreate( ...args: any ): Promise<TEditor>;\n\t\tEditorWatchdog: typeof EditorWatchdog;\n\t\tContextWatchdog: typeof ContextWatchdog;\n\t};\n\tcontextItemMetadata?: CKEditorConfigContextMetadata;\n\tconfig?: EditorConfig;\n\twatchdogConfig?: WatchdogConfig;\n\tdisableWatchdog?: boolean;\n\tonReady?: ( editor: TEditor ) => void;\n\tonAfterDestroy?: ( editor: TEditor ) => void;\n\tonError?: ( error: Error, details: ErrorDetails ) => void;\n\tonChange?: ( event: EventInfo, editor: TEditor ) => void;\n\tonFocus?: ( event: EventInfo, editor: TEditor ) => void;\n\tonBlur?: ( event: EventInfo, editor: TEditor ) => void;\n}\n\ninterface ErrorDetails {\n\tphase: 'initialization' | 'runtime';\n\twillEditorRestart?: boolean;\n}\n\n/**\n * An adapter aligning the context watchdog API to the editor watchdog API for easier usage.\n */\nexport class EditorWatchdogAdapter<TEditor extends Editor> {\n\t/**\n\t * The context watchdog instance that will be wrapped into editor watchdog API.\n\t */\n\tprivate readonly _contextWatchdog: ContextWatchdog;\n\n\t/**\n\t * A unique id for the adapter to distinguish editor items when using the context watchdog API.\n\t */\n\tprivate readonly _id: string;\n\n\t/**\n\t * A watchdog's editor creator function.\n\t */\n\tprivate _creator?: EditorCreatorFunction;\n\n\t/**\n\t * @param contextWatchdog The context watchdog instance that will be wrapped into editor watchdog API.\n\t */\n\tconstructor( contextWatchdog: ContextWatchdog ) {\n\t\tthis._contextWatchdog = contextWatchdog;\n\t\tthis._id = uid();\n\t}\n\n\t/**\n\t * @param creator A watchdog's editor creator function.\n\t */\n\tpublic setCreator( creator: EditorCreatorFunction ): void {\n\t\tthis._creator = creator;\n\t}\n\n\t/**\n\t * Adds an editor configuration to the context watchdog registry. Creates an instance of it.\n\t *\n\t * @param sourceElementOrData A source element or data for the new editor.\n\t * @param config CKEditor 5 editor config.\n\t */\n\tpublic create( sourceElementOrData: HTMLElement | string, config: EditorConfig ): Promise<unknown> {\n\t\treturn this._contextWatchdog.add( {\n\t\t\tsourceElementOrData,\n\t\t\tconfig,\n\t\t\tcreator: this._creator!,\n\t\t\tid: this._id,\n\t\t\ttype: 'editor'\n\t\t} );\n\t}\n\n\t/**\n\t * Creates a listener that is attached to context watchdog's item and run when the context watchdog fires.\n\t * Currently works only for the `error` event.\n\t */\n\tpublic on( _: string, callback: ( _: null, data: { error: Error; causesRestart?: boolean } ) => void ): void {\n\t\t// Assume that the event name was error.\n\t\tthis._contextWatchdog.on( 'itemError', ( _, { itemId, error } ) => {\n\t\t\tif ( itemId === this._id ) {\n\t\t\t\tcallback( null, { error, causesRestart: undefined } );\n\t\t\t}\n\t\t} );\n\t}\n\n\tpublic destroy(): Promise<unknown> {\n\t\t// Destroying an editor instance after destroying the Context is handled in the `ContextWatchdog` class.\n\t\t// As `EditorWatchdogAdapter` is an adapter, we should not destroy the editor manually.\n\t\t// Otherwise, it causes that the editor is destroyed twice. However, there is a case, when the editor\n\t\t// needs to be removed from the context, without destroying the context itself. We may assume the following\n\t\t// relations with `ContextWatchdog#state`:\n\t\t//\n\t\t// a) `ContextWatchdog#state` === 'ready' - context is not destroyed; it's safe to destroy the editor manually.\n\t\t// b) `ContextWatchdog#state` === 'destroyed' - context is destroyed; let `ContextWatchdog` handle the whole process.\n\t\t//\n\t\t// See #354 for more information.\n\t\tif ( this._contextWatchdog.state === 'ready' ) {\n\t\t\treturn this._contextWatchdog.remove( this._id );\n\t\t}\n\n\t\treturn Promise.resolve();\n\t}\n\n\t/**\n\t * An editor instance.\n\t */\n\tpublic get editor(): TEditor {\n\t\treturn this._contextWatchdog.getItem( this._id ) as TEditor;\n\t}\n}\n","/**\n * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.\n * For licensing, see LICENSE.md.\n */\n\nimport { useRef, useState, type RefObject } from 'react';\nimport type { LifeCycleElementSemaphore, LifeCycleAfterMountCallback } from './LifeCycleElementSemaphore';\n\n/**\n * When using the `useState` approach, a new instance of the semaphore must