diff --git a/packages/ckeditor5-math/src/ui/mathliveinputview.ts b/packages/ckeditor5-math/src/ui/mathliveinputview.ts index b5f36e94e..a3863077c 100644 --- a/packages/ckeditor5-math/src/ui/mathliveinputview.ts +++ b/packages/ckeditor5-math/src/ui/mathliveinputview.ts @@ -1,94 +1,103 @@ import { View, type Locale } from 'ckeditor5'; import 'mathlive'; // Import side-effects only (registers the tag) +/** + * Interface describing the custom element. + */ +interface MathFieldElement extends HTMLElement { + value: string; + readOnly: boolean; + mathVirtualKeyboardPolicy: string; + +} + /** * A wrapper for the MathLive component. - * Uses 'any' typing to avoid TypeScript module resolution errors. */ export default class MathLiveInputView extends View { - /** - * The current LaTeX value. - * @observable - */ - public declare value: string | null; + /** + * The current LaTeX value. + * @observable + */ + public declare value: string | null; - /** - * Read-only state. - * @observable - */ - public declare isReadOnly: boolean; + /** + * Read-only state. + * @observable + */ + public declare isReadOnly: boolean; - /** - * Reference to the DOM element (typed as any to prevent TS errors). - */ - public mathfield: any = null; + /** + * Reference to the DOM element. + * Typed as MathFieldElement | null for proper TS support. + */ + public mathfield: MathFieldElement | null = null; - constructor( locale: Locale ) { - super( locale ); + constructor( locale: Locale ) { + super( locale ); - this.set( 'value', null ); - this.set( 'isReadOnly', false ); + this.set( 'value', null ); + this.set( 'isReadOnly', false ); - this.setTemplate( { - tag: 'div', - attributes: { - class: [ 'ck', 'ck-mathlive-input' ] - } - } ); - } + this.setTemplate( { + tag: 'div', + attributes: { + class: [ 'ck', 'ck-mathlive-input' ] + } + } ); + } - public override render(): void { - super.render(); + public override render(): void { + super.render(); - // 1. Create element using DOM API instead of Class constructor - // This avoids "Module has no exported member" errors. - const mathfield = document.createElement( 'math-field' ) as any; + // 1. Create element with the specific type + const mathfield = document.createElement( 'math-field' ) as MathFieldElement; - // 2. Configure Options - mathfield.mathVirtualKeyboardPolicy = 'manual'; + // 2. Configure Options + mathfield.mathVirtualKeyboardPolicy = 'manual'; - // Disable sounds - const MathfieldElement = customElements.get( 'math-field' ); - if ( MathfieldElement ) { - ( MathfieldElement as any ).soundsDirectory = null; - ( MathfieldElement as any ).plonkSound = null; - } + // Disable sounds + const MathfieldConstructor = customElements.get( 'math-field' ); + if ( MathfieldConstructor ) { + ( MathfieldConstructor as any ).soundsDirectory = null; + ( MathfieldConstructor as any ).plonkSound = null; + } - // 3. Set Initial State - mathfield.value = this.value ?? ''; - mathfield.readOnly = this.isReadOnly; + // 3. Set Initial State + mathfield.value = this.value ?? ''; + mathfield.readOnly = this.isReadOnly; - // 4. Bind Events (DOM -> Observable) - mathfield.addEventListener( 'input', () => { - const val = mathfield.value; - this.value = val.length ? val : null; - } ); + // 4. Bind Events (DOM -> Observable) + mathfield.addEventListener( 'input', () => { + const val = mathfield.value; + this.value = val.length ? val : null; + } ); - // 5. Bind Events (Observable -> DOM) - this.on( 'change:value', ( _evt, _name, nextValue ) => { - if ( mathfield.value !== nextValue ) { - mathfield.value = nextValue ?? ''; - } - } ); + // 5. Bind Events (Observable -> DOM) + this.on( 'change:value', ( _evt, _name, nextValue ) => { + if ( mathfield.value !== nextValue ) { + mathfield.value = nextValue ?? ''; + } + } ); - this.on( 'change:isReadOnly', ( _evt, _name, nextValue ) => { - mathfield.readOnly = nextValue; - } ); + this.on( 'change:isReadOnly', ( _evt, _name, nextValue ) => { + mathfield.readOnly = nextValue; + } ); - // 6. Mount - this.element?.appendChild( mathfield ); - this.mathfield = mathfield; - } + // 6. Mount + this.element?.appendChild( mathfield ); + this.mathfield = mathfield; + } - public focus(): void { - this.mathfield?.focus(); - } + public focus(): void { + this.mathfield?.focus(); + } - public override destroy(): void { - if ( this.mathfield ) { - this.mathfield.remove(); - this.mathfield = null; - } - super.destroy(); - } + public override destroy(): void { + if ( this.mathfield ) { + this.mathfield.remove(); + this.mathfield = null; + } + super.destroy(); + } } diff --git a/packages/ckeditor5-math/src/ui/rawlatexinputview.ts b/packages/ckeditor5-math/src/ui/rawlatexinputview.ts index 81593ec76..cc468b434 100644 --- a/packages/ckeditor5-math/src/ui/rawlatexinputview.ts +++ b/packages/ckeditor5-math/src/ui/rawlatexinputview.ts @@ -4,51 +4,51 @@ import { LabeledFieldView, createLabeledTextarea, type Locale, type TextareaView * A labeled textarea view for direct LaTeX code editing. */ export default class RawLatexInputView extends LabeledFieldView { - /** - * The current LaTeX value. - * @observable - */ - public declare value: string; + /** + * The current LaTeX value. + * @observable + */ + public declare value: string; - /** - * Whether the input is in read-only mode. - * @observable - */ - public declare isReadOnly: boolean; + /** + * Whether the input is in read-only mode. + * @observable + */ + public declare isReadOnly: boolean; - constructor( locale: Locale ) { - super( locale, createLabeledTextarea ); + constructor( locale: Locale ) { + super( locale, createLabeledTextarea ); - this.set( 'value', '' ); - this.set( 'isReadOnly', false ); + this.set( 'value', '' ); + this.set( 'isReadOnly', false ); - const fieldView = this.fieldView; + const fieldView = this.fieldView; - // 1. Sync: DOM (Textarea) -> Observable - // We listen to the native 'input' event on the child view - fieldView.on( 'input', () => { - if ( fieldView.element ) { - this.value = fieldView.element.value; - } - } ); + // 1. Sync: DOM (Textarea) -> Observable + fieldView.on( 'input', () => { + // We cast strictly to HTMLTextAreaElement to access '.value' safely + const textarea = fieldView.element as HTMLTextAreaElement; + if ( textarea ) { + this.value = textarea.value; + } + } ); - // 2. Sync: Observable -> DOM (Textarea) - this.on( 'change:value', () => { - // Check for difference to avoid cursor jumping or unnecessary updates - if ( fieldView.element && fieldView.element.value !== this.value ) { - fieldView.element.value = this.value; - } - } ); + // 2. Sync: Observable -> DOM (Textarea) + this.on( 'change:value', () => { + const textarea = fieldView.element as HTMLTextAreaElement; + // Check for difference to avoid cursor jumping + if ( textarea && textarea.value !== this.value ) { + textarea.value = this.value; + } + } ); - // 3. Sync: ReadOnly State - this.on( 'change:isReadOnly', () => { - if ( fieldView.element ) { - fieldView.element.readOnly = this.isReadOnly; - } - } ); - } + // 3. Sync: ReadOnly State + this.on( 'change:isReadOnly', ( _evt, _name, nextValue ) => { + fieldView.isReadOnly = nextValue; + } ); + } - public override render(): void { - super.render(); - } + public override render(): void { + super.render(); + } }