class CKEditorEditableComponent extends HTMLElement {
/**
* List of attributes that trigger updates when changed
*
* @static
* @returns {string[]} Array of attribute names to observe
*/
static get observedAttributes() {
return ['name'];
}
/**
* Gets the name of this editable region
*
* @returns {string} The name attribute value
*/
get name() {
// The default value is set mainly for decoupled editors where the name is not required.
return this.getAttribute('name') || 'editable';
}
/**
* Gets the actual editable DOM element
* @returns {HTMLDivElement|null} The div element containing editable content
*/
get editableElement() {
return this.querySelector('div');
}
/**
* Lifecycle callback when element is added to DOM
* Sets up the editable element and registers it with the parent editor
*
* @throws {Error} If not used as child of ckeditor-component
*/
connectedCallback() {
execIfDOMReady(() => {
const editorComponent = this.#queryEditorElement();
if (!editorComponent ) {
throw new Error('ckeditor-editable-component must be a child of ckeditor-component');
}
this.innerHTML = `
${this.innerHTML}
`;
this.style.display = 'block';
if (editorComponent.isDecoupled()) {
editorComponent.runAfterEditorReady(editor => {
this.appendChild(editor.ui.view[this.name].element);
});
} else {
if (!this.name) {
throw new Error('Editable component missing required "name" attribute');
}
editorComponent.editables[this.name] = this;
}
});
}
/**
* Lifecycle callback for attribute changes
* Handles name changes and propagates other attributes to editable element
*
* @param {string} name - Name of changed attribute
* @param {string|null} oldValue - Previous value
* @param {string|null} newValue - New value
*/
attributeChangedCallback(name, oldValue, newValue) {
if (oldValue === newValue) {
return;
}
if (name === 'name') {
if (!oldValue) {
return;
}
const editorComponent = this.#queryEditorElement();
if (editorComponent) {
editorComponent.editables[newValue] = editorComponent.editables[oldValue];
delete editorComponent.editables[oldValue];
}
} else {
this.editableElement.setAttribute(name, newValue);
}
}
/**
* Lifecycle callback when element is removed
* Un-registers this editable from the parent editor
*/
disconnectedCallback() {
const editorComponent = this.#queryEditorElement();
if (editorComponent) {
delete editorComponent.editables[this.name];
}
}
/**
* Finds the parent editor component
*
* @private
* @returns {CKEditorComponent|null} Parent editor component or null if not found
*/
#queryEditorElement() {
return this.closest('ckeditor-component') || document.body.querySelector('ckeditor-component');
}
}
customElements.define('ckeditor-editable-component', CKEditorEditableComponent);