"use strict";
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
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 (b.hasOwnProperty(p)) d[p] = b[p]; };
        return extendStatics(d, b);
    };
    return function (d, b) {
        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 __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.CalendarInput = void 0;
/* eslint-disable max-len */
var react_1 = __importDefault(require("react"));
var format_1 = __importDefault(require("date-fns/format"));
var bem_react_classname_1 = require("bem-react-classname");
var icons_classic_1 = require("@alfalab/icons-classic");
var cn_1 = require("../cn");
var calendar_1 = __importDefault(require("../calendar/calendar"));
var icon_button_1 = __importDefault(require("../icon-button/icon-button"));
var input_1 = __importDefault(require("../input/input"));
var mq_1 = __importDefault(require("../mq/mq"));
var popup_1 = __importDefault(require("../popup/popup"));
var popup_header_1 = __importDefault(require("../popup-header/popup-header"));
var keyboard_code_1 = __importDefault(require("../lib/keyboard-code"));
var window_1 = require("../lib/window");
var utils_1 = require("./utils");
var performance_1 = __importDefault(require("../performance"));
/**
 * NB: В нативном календаре нельзя менять формат даты. Приемлем только YYYY-MM-DD формат.
 * https://www.w3.org/TR/html-markup/input.date.html#input.date.attrs.value
 * https://tools.ietf.org/html/rfc3339#section-5.6
*/
var CUSTOM_DATE_FORMAT = 'DD.MM.YYYY';
var NATIVE_DATE_FORMAT = 'YYYY-MM-DD';
var IS_BROWSER = typeof window !== 'undefined';
var SUPPORTS_INPUT_TYPE_DATE = IS_BROWSER && utils_1.isInputDateSupported();
var maskFormatCharacters = {
    2: {
        validate: function (char) {
            return /[1-2]/.test(char);
        },
    },
    3: {
        validate: function (char) {
            return /[0-3]/.test(char);
        },
    },
};
/**
 * Компонент для ввода даты.
 */
var CalendarInput = /** @class */ (function (_super) {
    __extends(CalendarInput, _super);
    function CalendarInput() {
        var _this = _super !== null && _super.apply(this, arguments) || this;
        _this.cn = bem_react_classname_1.createCn('calendar-input');
        _this.state = {
            isMobile: false,
            isInputFocused: false,
            isCalendarFocused: false,
            opened: false,
            value: _this.props.defaultValue || '',
            month: utils_1.calculateMonth(_this.props.value || _this.props.defaultMonth, CUSTOM_DATE_FORMAT, _this.props.calendar ? _this.props.calendar.earlierLimit : undefined, _this.props.calendar ? _this.props.calendar.laterLimit : undefined),
        };
        _this.handleCalendarChange = function (value, formatted, isTriggeredByKeyboard) {
            if (!isTriggeredByKeyboard) {
                _this.changeCloseTimeoutId = setTimeout(function () {
                    _this.calendar.blur(); // FF не испускает событие `blur` когда элементы становятся невидимыми, делаем это явно
                    _this.setState({
                        opened: false,
                    });
                    _this.changeCloseTimeoutId = null;
                }, 0);
            }
            _this.setState({ value: formatted });
            if (_this.props.onCalendarChange) {
                _this.props.onCalendarChange(formatted);
            }
            if (_this.props.onChange) {
                _this.props.onChange(formatted, value);
            }
        };
        _this.handleCalendarMonthChange = function (month) {
            _this.setState({
                month: month,
            });
        };
        _this.handleCalendarFocus = function (event) {
            _this.changeFocused({ isCalendarFocused: true }, event);
        };
        _this.handleCalendarBlur = function (event) {
            _this.changeFocused({ isCalendarFocused: false }, event);
        };
        _this.handleCalendarKeyDown = function (event) {
            switch (event.which) {
                case keyboard_code_1.default.ESCAPE:
                    event.preventDefault();
                    _this.customCalendarTarget.focus();
                    break;
                case keyboard_code_1.default.ENTER:
                case keyboard_code_1.default.SPACE:
                    event.preventDefault();
                    _this.setState({
                        opened: false,
                    });
                    break;
            }
            if (_this.props.onCalendarKeyDown) {
                _this.props.onCalendarKeyDown(event);
            }
            if (_this.props.onKeyDown) {
                _this.props.onKeyDown(event);
            }
        };
        _this.handleIconButtonClick = function () {
            _this.customCalendarTarget.focus();
        };
        _this.handleCustomInputChange = function (value) {
            var month = utils_1.calculateMonth(value, CUSTOM_DATE_FORMAT, _this.props.calendar ? _this.props.calendar.earlierLimit : undefined, _this.props.calendar ? _this.props.calendar.laterLimit : undefined);
            _this.setState({ value: value });
            // Изменяет месяц в календаре в соответствии с введёной в поле валидной датой
            if (value && value.length === CUSTOM_DATE_FORMAT.length && month !== _this.state.month) {
                _this.setState({ month: month });
            }
            if (_this.props.onInputChange) {
                _this.props.onInputChange(value);
            }
            if (_this.props.onChange) {
                _this.props.onChange(value, utils_1.parseDate(value, CUSTOM_DATE_FORMAT));
            }
        };
        _this.handleNativeInputChange = function (event) {
            var value = utils_1.changeDateFormat(event.target.value, NATIVE_DATE_FORMAT, CUSTOM_DATE_FORMAT);
            // Детектим нажатие `сlear` в нативном календаре
            if (_this.state.value === value) {
                value = '';
            }
            _this.setState({ value: value });
            if (_this.props.onInputChange) {
                _this.props.onInputChange(value);
            }
            if (_this.props.onChange) {
                _this.props.onChange(value, utils_1.parseDate(value, CUSTOM_DATE_FORMAT));
            }
        };
        _this.handleCustomInputFocus = function (event) {
            _this.changeFocused({ isInputFocused: true }, event);
            if (_this.props.onInputFocus) {
                _this.props.onInputFocus(event);
            }
        };
        _this.handleNativeInputFocus = function (event) {
            // Копируем пришедший из аргументов SyntheticEvent для дальнейшего редактирования
            var resultEvent = __assign(__assign({}, event), { 
                // Трансформируем нативную YYYY-MM-DD дату в кастомный формат на вывод в коллбэках
                target: { value: utils_1.changeDateFormat(event.target.value, NATIVE_DATE_FORMAT, CUSTOM_DATE_FORMAT) } });
            _this.changeFocused({ isInputFocused: true }, resultEvent);
            if (_this.props.onInputFocus) {
                _this.props.onInputFocus(resultEvent);
            }
        };
        _this.handleCustomInputBlur = function (event) {
            _this.changeFocused({ isInputFocused: false }, event);
            if (_this.props.onInputBlur) {
                _this.props.onInputBlur(event);
            }
        };
        _this.handleNativeInputBlur = function (event) {
            // Копируем пришедший из аргументов SyntheticEvent для дальнейшего редактирования
            var resultEvent = __assign(__assign({}, event), { 
                // Трансформируем нативную YYYY-MM-DD дату в кастомный формат на вывод в коллбэках
                target: { value: utils_1.changeDateFormat(event.target.value, NATIVE_DATE_FORMAT, CUSTOM_DATE_FORMAT) } });
            _this.changeFocused({ isInputFocused: false }, resultEvent);
            if (_this.props.onInputBlur) {
                _this.props.onInputBlur(resultEvent);
            }
        };
        _this.handleInputKeyDown = function (event) {
            switch (event.which) {
                case keyboard_code_1.default.DOWN_ARROW: {
                    event.preventDefault();
                    var value = _this.props.value === undefined ? _this.state.value : _this.props.value;
                    _this.setState({
                        opened: true,
                        month: utils_1.calculateMonth(value, CUSTOM_DATE_FORMAT, _this.props.calendar ? _this.props.calendar.earlierLimit : undefined, _this.props.calendar ? _this.props.calendar.laterLimit : undefined),
                    });
                    _this.calendar.focus();
                    break;
                }
                case keyboard_code_1.default.ESCAPE: {
                    event.preventDefault();
                    _this.setState({
                        opened: false,
                    });
                    break;
                }
            }
            if (_this.props.onInputKeyDown) {
                _this.props.onInputKeyDown(event);
            }
            if (_this.props.onKeyDown) {
                _this.props.onKeyDown(event);
            }
        };
        _this.handleMqMatchChange = function (isMatched) {
            _this.setState({
                isMobile: isMatched,
            });
        };
        _this.handleMobileWrapperClick = function () {
            _this.setOpened(true);
        };
        _this.handlePopupCloserClick = function () {
            _this.setOpened(false);
        };
        return _this;
    }
    CalendarInput.prototype.componentDidMount = function () {
        if (this.calendarPopup) {
            var element = void 0;
            if (this.nativeCalendarTarget) {
                element = this.nativeCalendarTarget;
            }
            else if (this.customCalendarTarget) {
                element = this.customCalendarTarget.getBoxNode();
            }
            if (element) {
                this.calendarPopup.setTarget(element);
            }
        }
    };
    CalendarInput.prototype.componentWillUnmount = function () {
        if (this.timeoutId) {
            clearTimeout(this.timeoutId);
            this.timeoutId = null;
        }
        if (this.changeCloseTimeoutId) {
            clearTimeout(this.changeCloseTimeoutId);
            this.changeCloseTimeoutId = null;
        }
    };
    CalendarInput.prototype.render = function () {
        var _this = this;
        var value = this.props.value === undefined ? this.state.value : this.props.value;
        var commonProps = {
            disabled: this.props.disabled,
            tabIndex: this.props.tabIndex,
            formNoValidate: true,
        };
        var nativeProps = {
            min: this.props.calendar
                && this.props.calendar.earlierLimit
                && format_1.default(this.props.calendar.earlierLimit, NATIVE_DATE_FORMAT),
            max: this.props.calendar
                && this.props.calendar.laterLimit
                && format_1.default(this.props.calendar.laterLimit, NATIVE_DATE_FORMAT),
        };
        var wrapperProps = this.isMobilePopup() && !this.props.disabled
            ? {
                role: 'button',
                tabIndex: 0,
                onClick: this.handleMobileWrapperClick,
            }
            : {};
        return (react_1.default.createElement("span", { className: this.cn({ width: this.props.width }), "data-test-id": this.props['data-test-id'] },
            react_1.default.createElement("span", __assign({}, wrapperProps),
                react_1.default.createElement(mq_1.default, { query: "--small-only", touch: true, onMatchChange: this.handleMqMatchChange }, this.canBeNative()
                    && (react_1.default.createElement("input", __assign({ ref: function (nativeCalendarTarget) {
                            _this.nativeCalendarTarget = nativeCalendarTarget;
                        } }, commonProps, nativeProps, { className: this.cn('native-control'), type: "date", value: utils_1.changeDateFormat(value, CUSTOM_DATE_FORMAT, NATIVE_DATE_FORMAT), onBlur: this.handleNativeInputBlur, onChange: this.handleNativeInputChange, onFocus: this.handleNativeInputFocus })))),
                react_1.default.createElement(input_1.default, __assign({ ref: function (customCalendarTarget) {
                        _this.customCalendarTarget = customCalendarTarget;
                    } }, commonProps, { autocomplete: this.props.autocomplete, className: this.cn('custom-control'), disabledAttr: this.isNativeInput() || this.isMobilePopup(), focused: this.state.isInputFocused || this.state.isCalendarFocused, mask: "31.11.2111", maskFormatCharacters: maskFormatCharacters, size: this.props.size, type: "tel", pattern: "[0-9.]*", label: this.props.label, placeholder: this.props.placeholder, hint: this.props.hint, error: this.props.error, resetError: this.props.resetError, value: value, view: this.props.view, width: this.props.width, id: this.props.id, name: this.props.name, leftAddons: this.props.leftAddons, rightAddons: this.props.rightAddons, onBlur: this.handleCustomInputBlur, onChange: this.handleCustomInputChange, onFocus: this.handleCustomInputFocus, onKeyDown: this.handleInputKeyDown, icon: this.props.withIcon
                        && (react_1.default.createElement(icon_button_1.default, { onClick: this.handleIconButtonClick }, this.renderIcon())) }))),
            this.renderPopup(value)));
    };
    CalendarInput.prototype.renderPopup = function (value) {
        var _this = this;
        var opened = this.props.opened === undefined ? this.state.opened : this.props.opened;
        if (this.isSimpleInput()) {
            opened = false;
        }
        return (react_1.default.createElement(popup_1.default, { ref: function (calendarPopup) {
                _this.calendarPopup = calendarPopup;
            }, for: this.props.name, visible: opened, directions: this.props.directions, target: this.isMobilePopup() ? 'screen' : 'anchor', header: this.isMobilePopup() && this.renderMobileHeader(), padded: false },
            react_1.default.createElement("div", { className: this.cn('calendar-wrapper', { mobile: this.isMobilePopup() }) },
                react_1.default.createElement(calendar_1.default, __assign({ ref: function (calendar) {
                        _this.calendar = calendar;
                    }, month: this.state.month }, this.props.calendar, { value: utils_1.parseDate(value, CUSTOM_DATE_FORMAT), onBlur: this.handleCalendarBlur, onFocus: this.handleCalendarFocus, onKeyDown: this.handleCalendarKeyDown, onValueChange: this.handleCalendarChange, onMonthChange: this.handleCalendarMonthChange })))));
    };
    CalendarInput.prototype.renderMobileHeader = function () {
        return (react_1.default.createElement(popup_header_1.default, { size: this.props.size, title: this.props.mobileTitle || this.props.label || 'Выберите дату', onCloserClick: this.handlePopupCloserClick }));
    };
    /**
     * Устанавливает фокус на поле ввода, открывает календарь.
     */
    CalendarInput.prototype.focus = function () {
        var targetRef = this.nativeCalendarTarget || this.customCalendarTarget;
        targetRef.focus();
    };
    /**
     * Убирает фокус с поля ввода.
     */
    CalendarInput.prototype.blur = function () {
        var targetRef = this.nativeCalendarTarget || this.customCalendarTarget;
        targetRef.blur();
    };
    /**
     * Скроллит страницу до поля ввода.
     */
    CalendarInput.prototype.scrollTo = function () {
        this.customCalendarTarget.scrollTo();
    };
    CalendarInput.prototype.canBeNative = function () {
        return SUPPORTS_INPUT_TYPE_DATE && this.props.mobileMode === 'native';
    };
    CalendarInput.prototype.isNativeInput = function () {
        return this.state.isMobile && this.canBeNative();
    };
    CalendarInput.prototype.isMobilePopup = function () {
        return this.state.isMobile && this.props.mobileMode === 'popup';
    };
    CalendarInput.prototype.isSimpleInput = function () {
        return this.state.isMobile && this.props.mobileMode === 'input';
    };
    CalendarInput.prototype.changeFocused = function (focusedState, event) {
        var newState = __assign({ isInputFocused: this.state.isInputFocused, isCalendarFocused: this.state.isCalendarFocused }, focusedState);
        // При переключении фокуса с поля ввода на календарь событие `blur` у поля ввода иногда происходит перед фокусом календаря
        // Поэтому проверяем элемент который получит фокус после блюра и если он внутри календаря - оставляем сфокусированное состояние.
        var relatedTarget = event.relatedTarget // не поддерживается в FF и IE10 https://github.com/facebook/react/issues/2011
            || event.explicitOriginalTarget // не поддерживается в IE
            || document.activeElement; // В IE вернет не <calendar> а конкретную ноду, на которую пришел фокус
        var calendarWillReceiveFocus = !window_1.isNodeOutsideElement(relatedTarget, this.calendar.getNode());
        var newFocused = newState.isInputFocused
            || newState.isCalendarFocused
            || calendarWillReceiveFocus;
        this.setState(focusedState);
        var newOpened = false;
        if (newFocused) {
            if (this.props.onFocus) {
                this.props.onFocus(event);
            }
            newOpened = true;
        }
        else if (this.props.onBlur) {
            this.props.onBlur(event);
        }
        if (!this.isNativeInput()) {
            this.setOpened(newOpened);
        }
    };
    CalendarInput.prototype.setOpened = function (opened) {
        var _this = this;
        if (this.timeoutId) {
            clearTimeout(this.timeoutId);
        }
        this.timeoutId = setTimeout(function () {
            var value = _this.props.value === undefined ? _this.state.value : _this.props.value;
            // eslint-disable-next-line react/no-access-state-in-setstate
            var newMonth = _this.state.opened === opened
                // eslint-disable-next-line react/no-access-state-in-setstate
                ? _this.state.month
                : utils_1.calculateMonth(value || _this.props.defaultMonth, CUSTOM_DATE_FORMAT, _this.props.calendar ? _this.props.calendar.earlierLimit : undefined, _this.props.calendar ? _this.props.calendar.laterLimit : undefined);
            _this.setState({
                opened: opened,
                month: newMonth,
            });
            _this.timeoutId = null;
        }, 0);
    };
    CalendarInput.prototype.renderIcon = function () {
        switch (this.props.size) {
            case 's':
                return react_1.default.createElement(icons_classic_1.CalendarSIcon, null);
            case 'l':
                return react_1.default.createElement(icons_classic_1.CalendarLIcon, null);
            case 'xl':
                return react_1.default.createElement(icons_classic_1.CalendarXlIcon, null);
            case 'm':
            default:
                return react_1.default.createElement(icons_classic_1.CalendarMIcon, null);
        }
    };
    CalendarInput.defaultProps = {
        withIcon: true,
        directions: ['bottom-left', 'bottom-right', 'top-left', 'top-right'],
        placeholder: '00.00.0000',
        mobileMode: 'native',
    };
    CalendarInput = __decorate([
        performance_1.default(true)
    ], CalendarInput);
    return CalendarInput;
}(react_1.default.Component));
exports.CalendarInput = CalendarInput;
exports.default = cn_1.withTheme(CalendarInput);

//# sourceMappingURL=calendar-input.js.map
