var __assign = (this && this.__assign) || function () {
    __assign = Object.assign || function(t) {
        for (var s, i = 1, n = arguments.length; i < n; i++) {
            s = arguments[i];
            for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
                t[p] = s[p];
        }
        return t;
    };
    return __assign.apply(this, arguments);
};
var __rest = (this && this.__rest) || function (s, e) {
    var t = {};
    for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
        t[p] = s[p];
    if (s != null && typeof Object.getOwnPropertySymbols === "function")
        for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
            if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
                t[p[i]] = s[p[i]];
        }
    return t;
};
import React, { useContext, useEffect, useImperativeHandle, useRef, useState } from 'react';
import { useEmotion, useStyles } from '../../lib/renderEnvironment/index.js';
import { forwardRefAndName } from '../../lib/forwardRefAndName.js';
import { Input } from '../Input/index.js';
import { isKeyBackspace, isKeyDelete } from '../../lib/events/keyboard/identifiers.js';
import { ThemeContext } from '../../lib/theming/ThemeContext.js';
import { getStyles, globalClasses } from './MaskedInput.styles.js';
import { getDefinitions, getMaskChar } from './MaskedInput.helpers.js';
import { ColorableInputElement } from './ColorableInputElement/index.js';
import { FixedIMaskInput } from './FixedIMaskInput.js';
export var getSafeMaskInputType = function (type) {
    if (!type) {
        return type;
    }
    switch (type) {
        case 'number':
        case 'date':
        case 'time':
        case 'password':
            return 'text';
        default:
            return type;
    }
};
/** Поле ввода, которое ограничивает формат вводимого значения по заданной маске. Такое поле облегчает пользователю ввод и снижает количество ошибок.
 */
export var MaskedInput = forwardRefAndName('MaskedInput', function MaskedInput(props, ref) {
    var mask = props.mask, maskChar = props.maskChar, formatChars = props.formatChars, _a = props.alwaysShowMask, alwaysShowMask = _a === void 0 ? false : _a, _b = props.colored, colored = _b === void 0 ? true : _b, _c = props.imaskProps, customIMaskProps = _c === void 0 ? {} : _c, _d = props.unmask, unmask = _d === void 0 ? false : _d, onValueChange = props.onValueChange, onUnexpectedInput = props.onUnexpectedInput, onChange = props.onChange, onBeforePasteValue = props.onBeforePasteValue, element = props.element, className = props.className, 
    // @ts-expect-error: могут передавать игнорируя ошибку
    maxLength = props.maxLength, inputProps = __rest(props, ["mask", "maskChar", "formatChars", "alwaysShowMask", "colored", "imaskProps", "unmask", "onValueChange", "onUnexpectedInput", "onChange", "onBeforePasteValue", "element", "className", "maxLength"]);
    var theme = useContext(ThemeContext);
    var cx = useEmotion().cx;
    var styles = useStyles(getStyles);
    var inputRef = useRef(null);
    var _e = useState(false), focused = _e[0], setFocused = _e[1];
    var prevValue = useRef(props.value || String(props.defaultValue) || '');
    var prevSelectionStart = useRef(null);
    useImperativeHandle(ref, function () {
        return inputRef.current &&
            Object.assign(inputRef.current, {
                selectAll: function () {
                    var _a, _b;
                    (_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.focus();
                    (_b = inputRef.current) === null || _b === void 0 ? void 0 : _b.delaySelectAll();
                },
            });
    }, []);
    useEffect(function () {
        var _a;
        // Для корректной работы onUnexpectedInput надо знать предыдущий value,
        // но imask при монтировании не вызывает onAccept, если value невалиден или laze=false.
        // Поэтому актуальный value при монтировании надо получать вручную
        if ((_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.input) {
            prevValue.current = inputRef.current.input.value;
            prevSelectionStart.current = inputRef.current.input.selectionStart;
        }
    }, []);
    var imaskProps = getCompatibleIMaskProps();
    return (React.createElement(Input, __assign({ ref: inputRef }, inputProps, { onFocus: handleFocus, onBlur: handleBlur, onInput: handleInput, onKeyDown: handleKeyDown, onPaste: handlePaste, className: cx(globalClasses.root, className, styles.root(theme)), element: colored ? (React.createElement(ColorableInputElement, { showOnFocus: false, alwaysShowMask: alwaysShowMask },
            React.createElement(FixedIMaskInput, __assign({}, imaskProps, { onAccept: handleAccept })))) : (React.createElement(FixedIMaskInput, __assign({}, imaskProps, { onAccept: handleAccept }))) })));
    function handlePaste(event) {
        var _a, _b, _c;
        if (onBeforePasteValue && onValueChange) {
            event.preventDefault();
            onValueChange === null || onValueChange === void 0 ? void 0 : onValueChange(onBeforePasteValue((_b = (_a = event.clipboardData) === null || _a === void 0 ? void 0 : _a.getData('text')) !== null && _b !== void 0 ? _b : ''));
        }
        (_c = props.onPaste) === null || _c === void 0 ? void 0 : _c.call(props, event);
    }
    function getCompatibleIMaskProps() {
        return __assign({ mask: mask.replace(/0/g, '{\\0}'), placeholderChar: getMaskChar(maskChar), definitions: getDefinitions(formatChars), 
            // FIXME: Должно быть eager=true, но в imask ломается удаление по delete
            eager: 'append', overwrite: 'shift', lazy: !alwaysShowMask && (props.disabled || !focused), unmask: unmask }, customIMaskProps);
    }
    function handleAccept() {
        var args = [];
        for (var _i = 0; _i < arguments.length; _i++) {
            args[_i] = arguments[_i];
        }
        var value = args[0], e = args[2];
        // Метод onAccept может вызываться при монтировании, если не задан проп defaultValue.
        // Но нативный input никогда не вызывает onChange при монтировании.
        // Наше событие onValueChange в Input вывается в тех же случаях, что и нативный onChange,
        // поэтому чтобы сохранить консинстентность будем ориентироваться на наличие аргумента e.
        // Он содержит нативное событие, вызвавшее изменение.
        e && (onValueChange === null || onValueChange === void 0 ? void 0 : onValueChange(value));
        !e && (prevValue.current = value);
    }
    /**
     * Отслеживаем неожиданные нажатия
     * handleAccept не вызывается когда значение с маской не меняется
     * Сначала вызывается handleAccept, затем handleInput
     */
    function handleInput(e) {
        var _a;
        var _b = e.currentTarget, value = _b.value, selectionStart = _b.selectionStart;
        // При вводе неожиданных символов или удалении каретка может перепрыгивать фиксированные символы.
        // Такие случаи не расцениваем как неожиданный ввод, т.к. пользователь может намеренно их вводить.
        if (prevValue.current === value && selectionStart === prevSelectionStart.current) {
            handleUnexpectedInput(value);
        }
        prevValue.current = value;
        prevSelectionStart.current = selectionStart;
        (_a = props.onInput) === null || _a === void 0 ? void 0 : _a.call(props, e);
    }
    function handleFocus(e) {
        var _a, _b;
        setFocused(true);
        (_a = props.onFocus) === null || _a === void 0 ? void 0 : _a.call(props, e);
        // Если value из пропсов отличается от value, которое получит input после обработки,
        // то imask будет ставить каретку за последним валидным символом.
        props.selectAllOnFocus && ((_b = inputRef.current) === null || _b === void 0 ? void 0 : _b.delaySelectAll());
    }
    function handleUnexpectedInput(value) {
        var _a;
        var blink = ((_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.blink.bind(inputRef.current)) || (function () { return undefined; });
        onUnexpectedInput ? onUnexpectedInput(value, blink) : blink();
    }
    function handleBlur(e) {
        var _a;
        setFocused(false);
        (_a = props.onBlur) === null || _a === void 0 ? void 0 : _a.call(props, e);
    }
    function handleKeyDown(e) {
        var _a;
        var _b = e.currentTarget, value = _b.value, selectionStart = _b.selectionStart, selectionEnd = _b.selectionEnd;
        if ((isKeyBackspace(e) && selectionStart === 0 && selectionEnd === 0) ||
            (isKeyDelete(e) && prevSelectionStart.current === value.length)) {
            // Случаи, когда нажатие клавиш не тригерит `onInput`
            handleUnexpectedInput(value);
            prevValue.current = e.currentTarget.value;
        }
        prevSelectionStart.current = selectionStart;
        (_a = props.onKeyDown) === null || _a === void 0 ? void 0 : _a.call(props, e);
    }
});
//# sourceMappingURL=MaskedInput.js.map