diff --git a/projects/core/src/index.ts b/projects/core/src/index.ts index 5f5b574d7..ead10d94b 100644 --- a/projects/core/src/index.ts +++ b/projects/core/src/index.ts @@ -1,4 +1,3 @@ -export {MaskitoContentEditable} from './lib/classes'; export { MASKITO_DEFAULT_ELEMENT_PREDICATE, MASKITO_DEFAULT_OPTIONS, @@ -15,6 +14,7 @@ export { MaskitoPreprocessor, } from './lib/types'; export { + maskitoAdaptContentEditable, maskitoInitialCalibrationPlugin, maskitoPipe, maskitoStrictCompositionPlugin, diff --git a/projects/core/src/lib/classes/content-editable.ts b/projects/core/src/lib/classes/content-editable.ts deleted file mode 100644 index ce9d6fc61..000000000 --- a/projects/core/src/lib/classes/content-editable.ts +++ /dev/null @@ -1,58 +0,0 @@ -import type {MaskitoElement} from '../types'; -import {getContentEditableSelection, setContentEditableSelection} from '../utils'; - -// @ts-ignore Type MaskitoContentEditable is missing the following properties from type HTMLElement: accessKey, accessKeyLabel, autocapitalize, dir etc. -export class MaskitoContentEditable implements MaskitoElement { - protected maxLength = Infinity; - - constructor(private readonly element: HTMLElement) { - const proxyHost = this; - - /** - * We cannot just write `export class MaskitoContentEditable extends Proxy`. - * > The Proxy constructor does not have a prototype property because - * > proxy exotic objects do not have a [[Prototype]] internal slot that requires initialization. - */ - return new Proxy(element as any, { - get(target, prop: keyof HTMLElement) { - if (prop in proxyHost) { - return proxyHost[prop as keyof MaskitoContentEditable]; - } - - const nativeProperty = target[prop]; - - return typeof nativeProperty === 'function' - ? nativeProperty.bind(target) - : nativeProperty; - }, - set(target, prop: keyof HTMLElement, val, receiver) { - return Reflect.set( - prop in proxyHost ? proxyHost : target, - prop, - val, - receiver, - ); - }, - }); - } - - protected get value(): string { - return this.element.textContent || ''; - } - - protected set value(value) { - this.element.textContent = value; - } - - protected get selectionStart(): number | null { - return getContentEditableSelection(this.element)[0]; - } - - protected get selectionEnd(): number | null { - return getContentEditableSelection(this.element)[1]; - } - - protected setSelectionRange(from: number | null, to: number | null): void { - setContentEditableSelection(this.element, [from || 0, to || 0]); - } -} diff --git a/projects/core/src/lib/classes/index.ts b/projects/core/src/lib/classes/index.ts index 507fb6a4f..8a76a83b6 100644 --- a/projects/core/src/lib/classes/index.ts +++ b/projects/core/src/lib/classes/index.ts @@ -1,3 +1,2 @@ -export {MaskitoContentEditable} from './content-editable'; export {MaskHistory} from './mask-history'; export {MaskModel} from './mask-model/mask-model'; diff --git a/projects/core/src/lib/constants/default-element-predicate.ts b/projects/core/src/lib/constants/default-element-predicate.ts index bb292bb8b..d2cfcedfb 100644 --- a/projects/core/src/lib/constants/default-element-predicate.ts +++ b/projects/core/src/lib/constants/default-element-predicate.ts @@ -1,8 +1,8 @@ -import {MaskitoContentEditable} from '../classes'; -import type {MaskitoElement, MaskitoElementPredicate} from '../types'; +import type {MaskitoElementPredicate} from '../types'; +import {maskitoAdaptContentEditable} from '../utils'; export const MASKITO_DEFAULT_ELEMENT_PREDICATE: MaskitoElementPredicate = e => e.isContentEditable - ? (new MaskitoContentEditable(e) as unknown as MaskitoElement) + ? maskitoAdaptContentEditable(e) : e.querySelector('input,textarea') || (e as HTMLInputElement | HTMLTextAreaElement); diff --git a/projects/core/src/lib/utils/content-editable.ts b/projects/core/src/lib/utils/content-editable.ts new file mode 100644 index 000000000..7088bd298 --- /dev/null +++ b/projects/core/src/lib/utils/content-editable.ts @@ -0,0 +1,49 @@ +import {MaskitoElement, TextfieldLike} from '../types'; +import {getContentEditableSelection, setContentEditableSelection} from './index'; + +class ContentEditableAdapter implements TextfieldLike { + public maxLength = Infinity; + + constructor(private readonly element: HTMLElement) {} + + public get value(): string { + return this.element.textContent || ''; + } + + public set value(value) { + this.element.textContent = value; + } + + public get selectionStart(): number | null { + return getContentEditableSelection(this.element)[0]; + } + + public get selectionEnd(): number | null { + return getContentEditableSelection(this.element)[1]; + } + + public setSelectionRange(from: number | null, to: number | null): void { + setContentEditableSelection(this.element, [from || 0, to || 0]); + } +} + +export function maskitoAdaptContentEditable(element: HTMLElement): MaskitoElement { + const adapter = new ContentEditableAdapter(element); + + return new Proxy(element, { + get(target, prop: keyof HTMLElement) { + if (prop in adapter) { + return adapter[prop as keyof ContentEditableAdapter]; + } + + const nativeProperty = target[prop]; + + return typeof nativeProperty === 'function' + ? nativeProperty.bind(target) + : nativeProperty; + }, + set(target, prop: keyof HTMLElement, val, receiver) { + return Reflect.set(prop in adapter ? adapter : target, prop, val, receiver); + }, + }) as MaskitoElement; +} diff --git a/projects/core/src/lib/utils/index.ts b/projects/core/src/lib/utils/index.ts index 816b1e0c1..3849ab5e8 100644 --- a/projects/core/src/lib/utils/index.ts +++ b/projects/core/src/lib/utils/index.ts @@ -1,3 +1,4 @@ +export * from './content-editable'; export * from './dom/event-listener'; export * from './dom/get-content-editable-selection'; export * from './dom/history-events';