import { isObjEmpty } from "@web3r/flowerkit/obj"; import { createElement } from "react"; import { IMask } from "react-imask"; import { presets, setPresets } from "./config"; import type { TMaskPresets } from "./config"; import { MaskedInput as MaskedInputComponent } from "../../components/maskedInput"; import type { TMaskedRef } from "../../components/maskedInput"; import { constants } from "../../constants"; import { Collection } from "../collection"; import type { TCollectionElement, TCollectionItemDefaultCfg } from "../collection"; import { CollectionReactItem } from "../collectionReactItem"; import type { TValidationEvent } from "../forms/validation"; type TCollectionArgs = [ string, typeof MaskedInput, boolean? ]; type TCollectionPreset = [ { presets?: TMaskPresets; } ]; const isValidationEvent = (event: Event): event is TValidationEvent => { return event instanceof CustomEvent && typeof event.detail === "object" && event.detail !== null; }; /** * Класс для рендеринга элемента коллекции поля с маской для статичной верстки */ export class MaskedInput extends CollectionReactItem { constructor(el: TCollectionElement, params?: TCollectionItemDefaultCfg) { if (!(el instanceof HTMLElement)) { throw new TypeError(`[MaskedInput] HTMLElement is expected, but got "${el?.constructor?.name || typeof el}"`); } super(el, MaskedInputCollection.selector); this.render(); this.#bindEvents(); } render() { this.onBeforeMount(); this.root.render(createElement(MaskedInputComponent, { onUnmount: this.onUnmount.bind(this), onMount: this.onMount.bind(this), ref: this.ref, ...this.cfg, })); } #onValidation(event: Event) { if (isValidationEvent(event)) { event.detail.updateValidationUI?.(); } } #bindEvents() { this.instance.addEventListener(constants.formBubbles.inputInvalid, (e) => this.#onValidation(e)); this.instance.addEventListener(constants.formBubbles.inputValid, (e) => this.#onValidation(e)); } } /** * Плагин для автоматического рендера полей с маской. Является коллекцией. * @module MapCollection * @memberof Collection * @example * //
*/ export class MaskedInputCollection extends Collection { /** * Экспорт конструктора `IMask`, который используется в текущей реализации коллекции. * Поле позволяет переиспользовать библиотеку масок в пользовательском коде. * @type {typeof IMask} */ static iMask = IMask; /** * Признак регистрации коллекции в глобальном namespace Core. * @type {boolean} */ static isApp = true; /** * CSS-селектор контейнеров MaskedInput для автоподключения коллекции. * @type {string} */ static selector = "[data-js-masked-input]"; /** * Получает пресеты готовых масок * @returns {{email: {mask: (function(*): boolean)}, phone: {mask: string, unmask: boolean}, digits: {mask: RegExp}, number: {mask: NumberConstructor, thousandsSeparator: string, unmask: boolean}, date: {mask: DateConstructor, lazy: boolean, autofix: boolean, pattern: string}, rub: {mask: string, unmask: boolean, prepare: ((function(*, *): (string|*))|*), blocks: {num: {unmask: boolean, mask: NumberConstructor, thousandsSeparator: string, radix: string, scale: number, padFractionalZeros: boolean, normalizeZeros: boolean, signed: boolean}}}, letters: {mask: RegExp}}} */ static get presets() { return presets; } /** * Добавляет новые пресеты к существующим маскам или изменяет текущие * @param value{Object=} группа пресетов в виде обеъкта * @example * // В поле `MaskedInputCollection.iMask` доступен конструктор `react-imask` * MaskedInputCollection.preset = { * // добавление нового пресета * "myMask": { * mask: "000" * }, * // перезапись существующего пресета * "email": { * // ... * } * }; */ static set presets (value: TMaskPresets) { if (typeof value === "object" && !!value && !isObjEmpty(value)) { setPresets(value); } else { console.error(`[MaskedInputCollection] Can't apply new presets, it must be non-empty object `, value); } } constructor(...args: TCollectionArgs | TCollectionPreset) { const defaultSelector = MaskedInputCollection.selector; const defaultPlugin = MaskedInput; const defaultIsInit = true; // if user custom presets if (args.length === 1 && typeof args[0] === "object" && args[0] !== null && !!args[0].presets) { console.error(`[MaskedInputCollection] Please use "MaskedInputCollection.presets = { /* presets */ }" directly before plugin call`); MaskedInputCollection.presets = args[0].presets; super(defaultSelector, defaultPlugin, defaultIsInit); return; } if (args.length && typeof args[0] === "string" && typeof args[1] === "function") { const selector = args[0]; const plugin = args[1]; const isInit = typeof args[2] === "boolean" ? args[2] : defaultIsInit; super(selector, plugin, isInit); return; } super(defaultSelector, defaultPlugin, defaultIsInit); } }