diff --git a/src/json-crdt-peritext-ui/dom/CompositionController.ts b/src/json-crdt-peritext-ui/dom/CompositionController.ts new file mode 100644 index 0000000000..e4bcc9652e --- /dev/null +++ b/src/json-crdt-peritext-ui/dom/CompositionController.ts @@ -0,0 +1,49 @@ +import type {PeritextEventTarget} from '../events/PeritextEventTarget'; +import type {UiLifeCycles} from './types'; +import type {Peritext} from '../../json-crdt-extensions/peritext'; + +export interface CompositionControllerOpts { + source: HTMLElement; + txt: Peritext; + et: PeritextEventTarget; +} + +export class CompositionController implements UiLifeCycles { + public composing = false; + public data: string = ''; + + public constructor(public readonly opts: CompositionControllerOpts) {} + + /** -------------------------------------------------- {@link UiLifeCycles} */ + + public start(): void { + const el = this.opts.source; + el.addEventListener('compositionstart', this.onStart); + el.addEventListener('compositionupdate', this.onUpdate); + el.addEventListener('compositionend', this.onEnd); + } + + public stop(): void { + const el = this.opts.source; + el.removeEventListener('compositionstart', this.onStart); + el.removeEventListener('compositionupdate', this.onUpdate); + el.removeEventListener('compositionend', this.onEnd); + } + + private onStart = (event: CompositionEvent): void => { + this.composing = true; + this.data = event.data; + }; + + private onUpdate = (event: CompositionEvent): void => { + this.composing = true; + this.data = event.data; + }; + + private onEnd = (event: CompositionEvent): void => { + this.composing = false; + this.data = ''; + const text = event.data; + if (text) this.opts.et.insert(text); + }; +} diff --git a/src/json-crdt-peritext-ui/events/PeritextDomController.ts b/src/json-crdt-peritext-ui/events/PeritextDomController.ts index eb6a5fc4f7..3aa7bef220 100644 --- a/src/json-crdt-peritext-ui/events/PeritextDomController.ts +++ b/src/json-crdt-peritext-ui/events/PeritextDomController.ts @@ -4,6 +4,7 @@ import {RichTextController} from '../dom/RichTextController'; import {PeritextEventDefaults} from './PeritextEventDefaults'; import {PeritextEventTarget} from './PeritextEventTarget'; import {KeyController} from '../dom/KeyController'; +import {CompositionController} from '../dom/CompositionController'; import type {UiLifeCycles} from '../dom/types'; import type {Peritext} from '../../json-crdt-extensions'; @@ -15,6 +16,7 @@ export interface PeritextDomControllerOpts { export class PeritextDomController implements UiLifeCycles { public readonly et: PeritextEventTarget; public readonly keys: KeyController; + public readonly comp: CompositionController; public readonly input: InputController; public readonly selection: SelectionController; public readonly richText: RichTextController; @@ -25,7 +27,8 @@ export class PeritextDomController implements UiLifeCycles { const defaults = new PeritextEventDefaults(txt, et); et.defaults = defaults; const keys = (this.keys = new KeyController()); - this.input = new InputController({et, source, txt}); + const comp = this.comp = new CompositionController({et, source, txt}); + this.input = new InputController({et, source, txt, comp}); this.selection = new SelectionController({et, source, txt, keys}); this.richText = new RichTextController({et, source, txt}); } @@ -34,6 +37,7 @@ export class PeritextDomController implements UiLifeCycles { public start(): void { this.keys.start(); + this.comp.start(); this.input.start(); this.selection.start(); this.richText.start(); @@ -41,6 +45,7 @@ export class PeritextDomController implements UiLifeCycles { public stop(): void { this.keys.stop(); + this.comp.stop(); this.input.stop(); this.selection.stop(); this.richText.stop();