var __extends = (this && this.__extends) || (function () {
    var extendStatics = function (d, b) {
        extendStatics = Object.setPrototypeOf ||
            ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
            function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
        return extendStatics(d, b);
    };
    return function (d, b) {
        if (typeof b !== "function" && b !== null)
            throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
        extendStatics(d, b);
        function __() { this.constructor = d; }
        d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
    };
})();
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 __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
};
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;
};
// TODO: Enable this rule in functional components.
import invariant from 'invariant';
import React, { createRef } from 'react';
import warning from 'warning';
import { isKeyBackspace, isKeyDelete, someKeys } from '../../lib/events/keyboard/identifiers.js';
import { ThemeContext } from '../../lib/theming/ThemeContext.js';
import { CommonWrapper } from '../../internal/CommonWrapper/index.js';
import { getRootNode, rootNode } from '../../lib/rootNode/index.js';
import { createPropsGetter } from '../../lib/createPropsGetter.js';
import { FocusControlWrapper } from '../../internal/FocusControlWrapper/index.js';
import { ClearCrossIcon } from '../../internal/ClearCrossIcon/ClearCrossIcon.js';
import { catchUnreachableWarning } from '../../lib/typeGuards.js';
import { blink } from '../../lib/blink.js';
import { withSize } from '../../lib/size/SizeDecorator.js';
import { withRenderEnvironment } from '../../lib/renderEnvironment/index.js';
import { getStyles } from './Input.styles.js';
import { InputLayout } from './InputLayout/InputLayout.js';
export var inputTypes = ['password', 'text', 'number', 'tel', 'search', 'time', 'date', 'url', 'email'];
export var selectionAllowedTypes = ['text', 'password', 'tel', 'search', 'url'];
export var selectionErrorMessage = function (type, allowedTypes) {
    if (allowedTypes === void 0) { allowedTypes = selectionAllowedTypes; }
    return "<Input />. Selection is not supported by the type \"".concat(type, "\". Types that support selection: ").concat(allowedTypes
        .map(function (i) { return "\"".concat(i, "\""); })
        .join(', '), ". Reason: https://developer.mozilla.org/en-US/docs/Web/API/HTMLInputElement/setSelectionRange.");
};
export var calculateClearCrossShowedState = function (_a) {
    var showClearIcon = _a.showClearIcon, notEmptyValue = _a.notEmptyValue, focused = _a.focused, hovered = _a.hovered;
    switch (showClearIcon) {
        case 'always':
            return notEmptyValue;
        case 'auto':
            return Boolean((focused || hovered) && notEmptyValue);
        case 'never':
            return false;
        default:
            return catchUnreachableWarning(showClearIcon, false);
    }
};
export var InputDataTids = {
    root: 'Input__root',
    clearCross: 'Input__clearCross',
};
/**
 * Поле ввода позволяет ввести или отредактировать значение.
 */
var Input = /** @class */ (function (_super) {
    __extends(Input, _super);
    function Input() {
        var _this = _super !== null && _super.apply(this, arguments) || this;
        _this.getProps = createPropsGetter(Input_1.defaultProps);
        _this.selectAllId = null;
        _this.input = null;
        _this.labelRef = createRef();
        _this.getClearCrossShowed = function (_a) {
            var _b;
            var focused = _a.focused, hovered = _a.hovered, hasInitialValue = _a.hasInitialValue;
            if (_this.props.disabled) {
                return false;
            }
            return calculateClearCrossShowedState({
                showClearIcon: _this.getProps().showClearIcon,
                notEmptyValue: Boolean(((_b = _this.input) === null || _b === void 0 ? void 0 : _b.value) || hasInitialValue),
                focused: focused,
                hovered: hovered,
            });
        };
        _this.state = {
            focused: false,
            hovered: false,
            clearCrossShowed: _this.getClearCrossShowed({
                focused: false,
                hasInitialValue: Boolean(_this.props.value || _this.props.defaultValue),
            }),
        };
        /**
         * Переводит фокус в поле (если ещё не в фокусе) и выделяет весь текст в нём.
         * Работает с типами `text`, `password`, `tel`, `search`, `url`.
         * @public
         */
        _this.selectAll = function () { return _this._selectAll(); };
        _this.delaySelectAll = function () {
            var _a, _b, _c;
            return (_this.selectAllId = (_c = (_b = (_a = _this.globalObject).requestAnimationFrame) === null || _b === void 0 ? void 0 : _b.call(_a, _this._selectAll)) !== null && _c !== void 0 ? _c : null);
        };
        _this._selectAll = function () {
            if (_this.input) {
                _this.setSelectionRange(0, _this.input.value.length);
            }
        };
        _this.cancelDelayedSelectAll = function () {
            var _a, _b;
            if (_this.selectAllId) {
                (_b = (_a = _this.globalObject).cancelAnimationFrame) === null || _b === void 0 ? void 0 : _b.call(_a, _this.selectAllId);
                _this.selectAllId = null;
            }
        };
        _this.getInput = function (inputProps) {
            if (_this.props.element) {
                return React.cloneElement(_this.props.element, inputProps);
            }
            return React.createElement('input', inputProps);
        };
        _this.renderMain = function (props) {
            var _a, _b;
            var onMouseEnter = props.onMouseEnter, onMouseLeave = props.onMouseLeave, onMouseOver = props.onMouseOver, onKeyDown = props.onKeyDown, onKeyPress = props.onKeyPress, onValueChange = props.onValueChange, width = props.width, error = props.error, role = props.role, warning = props.warning, leftIcon = props.leftIcon, rightIcon = props.rightIcon, borderless = props.borderless, value = props.value, align = props.align, type = props.type, size = props.size, placeholder = props.placeholder, selectAllOnFocus = props.selectAllOnFocus, disabled = props.disabled, onUnexpectedInput = props.onUnexpectedInput, prefix = props.prefix, suffix = props.suffix, corners = props.corners, ariaDescribedby = props["aria-describedby"], ariaControls = props["aria-controls"], ariaLabel = props["aria-label"], element = props.element, showClearIcon = props.showClearIcon, rest = __rest(props, ["onMouseEnter", "onMouseLeave", "onMouseOver", "onKeyDown", "onKeyPress", "onValueChange", "width", "error", "role", "warning", "leftIcon", "rightIcon", "borderless", "value", "align", "type", "size", "placeholder", "selectAllOnFocus", "disabled", "onUnexpectedInput", "prefix", "suffix", "corners", 'aria-describedby', 'aria-controls', 'aria-label', "element", "showClearIcon"]);
            var focused = _this.state.focused;
            var labelProps = {
                className: _this.cx(_this.styles.root(_this.theme), _this.getSizeClassName(), (_a = {},
                    _a[_this.styles.focus(_this.theme)] = focused && !warning && !error,
                    _a[_this.styles.hovering(_this.theme)] = !focused && !disabled && !warning && !error && !borderless,
                    _a[_this.styles.borderless()] = borderless && !focused,
                    _a[_this.styles.disabled(_this.theme)] = disabled,
                    _a[_this.styles.warning(_this.theme)] = warning,
                    _a[_this.styles.error(_this.theme)] = error,
                    _a)),
                'aria-controls': ariaControls,
                style: __assign({ width: width }, corners),
                onMouseEnter: _this.handleMouseEnter,
                onMouseLeave: _this.handleMouseLeave,
                onMouseOver: onMouseOver,
                ref: _this.labelRef,
            };
            var inputProps = __assign(__assign({}, rest), { className: _this.cx(_this.styles.input(_this.theme), (_b = {},
                    _b[_this.styles.inputFocus(_this.theme)] = focused,
                    _b[_this.styles.inputDisabled(_this.theme)] = disabled,
                    _b)), value: value, role: role, onChange: _this.handleChange, onFocus: _this.handleFocus, onKeyDown: _this.handleKeyDown, onKeyPress: _this.handleKeyPress, onBlur: _this.handleBlur, style: { textAlign: align }, ref: _this.refInput, type: type, placeholder: placeholder, disabled: disabled, 'aria-describedby': ariaDescribedby, 'aria-label': ariaLabel });
            var input = (React.createElement(FocusControlWrapper, { onBlurWhenDisabled: _this.resetFocus }, _this.getInput(inputProps)));
            var getRightIcon = function () {
                return _this.state.clearCrossShowed ? (React.createElement(ClearCrossIcon, { "data-tid": InputDataTids.clearCross, size: _this.size, onClick: _this.handleClearInput })) : (rightIcon);
            };
            return (React.createElement(InputLayout, { leftIcon: leftIcon, rightIcon: getRightIcon(), prefix: prefix, suffix: suffix, labelProps: labelProps, context: { disabled: Boolean(disabled), focused: focused, size: _this.size } }, input));
        };
        _this.refInput = function (element) {
            if (element && 'input' in element) {
                _this.input = element.input;
            }
            else {
                _this.input = element;
            }
        };
        _this.handleClearInput = function () {
            if (_this.input) {
                _this.input.value = '';
            }
            _this.setState({ clearCrossShowed: false });
            if (_this.props.onValueChange) {
                _this.props.onValueChange('');
            }
        };
        _this.handleChange = function (event) {
            if (_this.props.onValueChange) {
                _this.props.onValueChange(event.target.value);
            }
            if (_this.props.onChange) {
                _this.props.onChange(event);
            }
            var clearCrossShowed = _this.getClearCrossShowed({ focused: _this.state.focused, hovered: _this.state.hovered });
            if (_this.state.clearCrossShowed !== clearCrossShowed) {
                _this.setState({
                    clearCrossShowed: clearCrossShowed,
                });
            }
        };
        _this.handleMouseEnter = function (e) {
            _this.setState({
                hovered: true,
                clearCrossShowed: _this.getClearCrossShowed({ focused: _this.state.focused, hovered: true }),
            });
            if (_this.props.onMouseEnter) {
                _this.props.onMouseEnter(e);
            }
        };
        _this.handleMouseLeave = function (e) {
            _this.setState({
                hovered: false,
                clearCrossShowed: _this.getClearCrossShowed({ focused: _this.state.focused, hovered: false }),
            });
            if (_this.props.onMouseLeave) {
                _this.props.onMouseLeave(e);
            }
        };
        _this.handleFocus = function (event) {
            _this.setState({
                focused: true,
                clearCrossShowed: _this.getClearCrossShowed({ focused: true, hovered: _this.state.hovered }),
            });
            if (_this.props.selectAllOnFocus) {
                _this.input && _this.selectAll();
            }
            if (_this.props.onFocus) {
                _this.props.onFocus(event);
            }
        };
        _this.handleKeyDown = function (e) {
            if (_this.props.onKeyDown) {
                _this.props.onKeyDown(e);
            }
            var isDeleteKey = someKeys(isKeyBackspace, isKeyDelete)(e);
            if (!e.currentTarget.value && isDeleteKey && !e.repeat) {
                _this.handleUnexpectedInput();
            }
        };
        _this.handleKeyPress = function (event) {
            if (_this.props.onKeyPress) {
                _this.props.onKeyPress(event);
            }
            if (_this.props.maxLength === event.currentTarget.value.length) {
                _this.handleUnexpectedInput(event.currentTarget.value);
            }
        };
        _this.handleUnexpectedInput = function (value) {
            if (value === void 0) { value = _this.props.value || ''; }
            if (_this.props.onUnexpectedInput) {
                _this.props.onUnexpectedInput(value);
            }
            else {
                _this.blink();
            }
        };
        _this.resetFocus = function () { return _this.setState({ focused: false }); };
        _this.handleBlur = function (event) {
            var _a, _b, _c;
            var showClearIcon = _this.props.showClearIcon;
            if (showClearIcon && ((_a = getRootNode(_this)) === null || _a === void 0 ? void 0 : _a.contains(event.relatedTarget))) {
                _this.setState({ focused: false });
            }
            else {
                var clearCrossShowed = _this.getClearCrossShowed({ focused: false, hovered: _this.state.hovered });
                _this.setState({
                    focused: false,
                    clearCrossShowed: clearCrossShowed,
                });
                (_c = (_b = _this.props).onBlur) === null || _c === void 0 ? void 0 : _c.call(_b, event);
            }
        };
        return _this;
    }
    Input_1 = Input;
    Input.prototype.componentWillUnmount = function () {
        this.cancelDelayedSelectAll();
    };
    /** Программно устанавливает фокус на поле.
     * Появляется фокусная рамка, элемент получает клавиатурные события и воспринимается как текущий элемент для чтения скринридерами.
     * @public
     */
    Input.prototype.focus = function () {
        invariant(this.input, 'Cannot call "focus" because Input is not mounted');
        this.input.focus();
    };
    /** Программно снимает фокус с поля.
     * @public
     */
    Input.prototype.blur = function () {
        invariant(this.input, 'Cannot call "blur" because Input is not mounted');
        this.input.blur();
    };
    /** Возвращает DOM-узел поля ввода.
     * @public
     */
    Input.prototype.getNode = function () {
        return this.input;
    };
    /** Кратковременно визуально подсвечивает поле ввода, чтобы привлечь внимание пользователя.
     * @public
     */
    Input.prototype.blink = function () {
        blink({ el: this.labelRef.current, blinkColor: this.theme.inputBlinkColor });
    };
    /** start - инициирует последовательное изменение числового значения: начинает повторяющееся увеличение/уменьшение, обычно используется при удерживании кнопки «+» или «−» для числового Input. end - останавливает ранее запущенное числоизменение, инициируемое numberStart.
     * [Документация](https://developer.mozilla.org/en-US/docs/Web/API/HTMLInputElement/setSelectionRange)
     * @public
     * @param {number} start
     * @param {number} end
     */
    Input.prototype.setSelectionRange = function (start, end) {
        var _a, _b;
        // https://github.com/facebook/react/issues/7769
        // https://developer.mozilla.org/en-US/docs/Web/API/HTMLInputElement/setSelectionRange
        if (!selectionAllowedTypes.includes(this.getProps().type)) {
            warning(false, selectionErrorMessage(this.getProps().type));
            return;
        }
        if (!this.input) {
            warning(false, 'Cannot call "setSelectionRange" on unmounted Input');
            return;
        }
        if (((_a = this.globalObject.document) === null || _a === void 0 ? void 0 : _a.activeElement) !== this.input) {
            this.focus();
        }
        (_b = this.input) === null || _b === void 0 ? void 0 : _b.setSelectionRange(start, end);
    };
    Input.prototype.render = function () {
        var _this = this;
        this.styles = getStyles(this.emotion);
        return (React.createElement(ThemeContext.Consumer, null, function (theme) {
            _this.theme = theme;
            return (React.createElement(CommonWrapper, __assign({ rootNodeRef: _this.setRootNode }, _this.getProps()), _this.renderMain));
        }));
    };
    Input.prototype.getSizeClassName = function () {
        var _a, _b, _c;
        switch (this.size) {
            case 'large':
                return this.cx((_a = {},
                    _a[this.styles.sizeLarge(this.theme)] = true,
                    _a));
            case 'medium':
                return this.cx((_b = {},
                    _b[this.styles.sizeMedium(this.theme)] = true,
                    _b));
            case 'small':
            default:
                return this.cx((_c = {},
                    _c[this.styles.sizeSmall(this.theme)] = true,
                    _c));
        }
    };
    var Input_1;
    Input.__KONTUR_REACT_UI__ = 'Input';
    Input.displayName = 'Input';
    Input.defaultProps = {
        type: 'text',
        showClearIcon: 'never',
    };
    Input = Input_1 = __decorate([
        withRenderEnvironment,
        rootNode,
        withSize
    ], Input);
    return Input;
}(React.Component));
export { Input };
//# sourceMappingURL=Input.js.map