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;
};
import React from 'react';
import throttle from 'lodash.throttle';
import warning from 'warning';
import { isKeyEnter } from '../../lib/events/keyboard/identifiers.js';
import { needsPolyfillPlaceholder } from '../../lib/needsPolyfillPlaceholder.js';
import * as LayoutEvents from '../../lib/LayoutEvents.js';
import { ThemeContext } from '../../lib/theming/ThemeContext.js';
import { RenderLayer } from '../../internal/RenderLayer/index.js';
import { ResizeDetector } from '../../internal/ResizeDetector/index.js';
import { isSafariWithTextareaBug } from '../../lib/client.js';
import { CommonWrapper } from '../../internal/CommonWrapper/index.js';
import { isTestEnv } from '../../lib/currentEnvironment.js';
import { rootNode } from '../../lib/rootNode/index.js';
import { createPropsGetter } from '../../lib/createPropsGetter.js';
import { withSize } from '../../lib/size/SizeDecorator.js';
import { withRenderEnvironment } from '../../lib/renderEnvironment/index.js';
import { getTextAreaHeight } from './TextareaHelpers.js';
import { getStyles } from './Textarea.styles.js';
import { TextareaCounter } from './TextareaCounter.js';
import { TextareaWithSafari17Workaround } from './TextareaWithSafari17Workaround.js';
export var DEFAULT_WIDTH = 250;
var AUTORESIZE_THROTTLE_DEFAULT_WAIT = 100;
export var TextareaDataTids = {
    root: 'Textarea__root',
    counter: 'TextareaCounter__root',
    helpIcon: 'TextareaCounter__helpIcon',
};
/**
 * Многострочное поле — это поле ввода, в котором текст отображается в несколько строк.
 */
var Textarea = /** @class */ (function (_super) {
    __extends(Textarea, _super);
    function Textarea() {
        var _this = _super !== null && _super.apply(this, arguments) || this;
        _this.clear = function () {
            if (_this.node) {
                _this.node.value = '';
            }
            if (_this.fakeNode) {
                _this.fakeNode.value = '';
            }
        };
        _this.getProps = createPropsGetter(Textarea_1.defaultProps);
        _this.state = {
            needsPolyfillPlaceholder: needsPolyfillPlaceholder,
            isCounterVisible: false,
        };
        _this.reflowCounter = function () {
            if (_this.counter) {
                _this.counter.reflow();
            }
        };
        _this.selectAllId = null;
        /** start - инициирует последовательное изменение числового значения: начинает повторяющееся увеличение/уменьшение, обычно используется при удерживании кнопки «+» или «−» для числового Input. end - останавливает ранее запущенное числоизменение, инициируемое numberStart.
         * [Документация](https://developer.mozilla.org/en-US/docs/Web/API/HTMLInputElement/setSelectionRange)
         * @public
         */
        _this.setSelectionRange = function (start, end) {
            var _a;
            if (!_this.node) {
                warning(false, 'Cannot call "setSelectionRange" on unmounted Input');
                return;
            }
            if (((_a = _this.globalObject.document) === null || _a === void 0 ? void 0 : _a.activeElement) !== _this.node) {
                _this.focus();
            }
            _this.node.setSelectionRange(start, end);
        };
        /** Переводит фокус в поле (если ещё не в фокусе) и выделяет весь текст в нём.
         * Работает с типами `text`, `password`, `tel`, `search`, `url`.
         * @public
         */
        _this.selectAll = function () {
            if (_this.node) {
                _this.setSelectionRange(0, _this.node.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.renderMain = function (props) {
            var _a, _b;
            var _c, _d;
            var _e = props.width, width = _e === void 0 ? DEFAULT_WIDTH : _e, error = props.error, warning = props.warning, size = props.size, autoResize = props.autoResize, resize = props.resize, onCut = props.onCut, onPaste = props.onPaste, maxRows = props.maxRows, onFocus = props.onFocus, selectAllOnFocus = props.selectAllOnFocus, placeholder = props.placeholder, onValueChange = props.onValueChange, showLengthCounter = props.showLengthCounter, lengthCounter = props.lengthCounter, counterHelp = props.counterHelp, extraRow = props.extraRow, disableAnimations = props.disableAnimations, disabled = props.disabled, textareaProps = __rest(props, ["width", "error", "warning", "size", "autoResize", "resize", "onCut", "onPaste", "maxRows", "onFocus", "selectAllOnFocus", "placeholder", "onValueChange", "showLengthCounter", "lengthCounter", "counterHelp", "extraRow", "disableAnimations", "disabled"]);
            var isCounterVisible = _this.state.isCounterVisible;
            var rootProps = {
                style: {
                    width: width,
                },
            };
            var textareaClassNames = _this.cx(_this.getTextareaSizeClassName(), (_a = {},
                _a[_this.styles.textarea(_this.theme)] = true,
                _a[_this.styles.hovering(_this.theme)] = !error && !warning,
                _a[_this.styles.disabled(_this.theme)] = disabled,
                _a[_this.styles.error(_this.theme)] = !!error,
                _a[_this.styles.warning(_this.theme)] = !!warning,
                _a[_this.styles.disableAnimations()] = _this.isAnimationsDisabled(),
                _a));
            var textareaStyle = {
                resize: autoResize ? 'none' : resize,
                textAlign: _this.props.align,
            };
            var placeholderPolyfill = null;
            if (_this.state.needsPolyfillPlaceholder && !textareaProps.value && !textareaProps.defaultValue) {
                placeholderPolyfill = React.createElement("span", { className: _this.styles.placeholder() }, placeholder);
            }
            var fakeTextarea = null;
            if (autoResize) {
                var fakeProps = {
                    value: _this.props.value,
                    defaultValue: _this.props.defaultValue,
                    className: _this.cx(textareaClassNames, _this.styles.fake()),
                    readOnly: true,
                };
                fakeTextarea = React.createElement("textarea", __assign({}, fakeProps, { ref: _this.refFake }));
            }
            var counter = showLengthCounter && isCounterVisible && _this.node && (React.createElement(TextareaCounter, { textarea: _this.node, size: _this.size, help: counterHelp, value: textareaProps.value, length: (_d = (_c = textareaProps.maxLength) !== null && _c !== void 0 ? _c : lengthCounter) !== null && _d !== void 0 ? _d : 0, onCloseHelp: _this.handleCloseCounterHelp, ref: _this.refCounter }));
            var Component = isSafariWithTextareaBug ? TextareaWithSafari17Workaround : 'textarea';
            return (React.createElement(RenderLayer, { onFocusOutside: _this.handleCloseCounterHelp, onClickOutside: _this.handleCloseCounterHelp, active: _this.state.isCounterVisible },
                React.createElement("label", __assign({ "data-tid": TextareaDataTids.root }, rootProps, { className: _this.cx(_this.getRootSizeClassName(), (_b = {},
                        _b[_this.styles.root()] = true,
                        _b)) }),
                    placeholderPolyfill,
                    React.createElement(ResizeDetector, { onResize: _this.reflowCounter, alignBaseline: true },
                        React.createElement(Component, __assign({}, textareaProps, { className: textareaClassNames, style: textareaStyle, placeholder: !placeholderPolyfill ? placeholder : undefined, ref: _this.ref, onChange: _this.handleChange, onCut: _this.handleCut, onPaste: _this.handlePaste, onFocus: _this.handleFocus, onKeyDown: _this.handleKeyDown, disabled: disabled }), _this.props.children)),
                    fakeTextarea,
                    counter)));
        };
        _this.handleCloseCounterHelp = function () { return _this.setState({ isCounterVisible: false }); };
        _this.handleKeyDown = function (e) {
            // Edge bug: textarea maxlength doesn't work after new line
            // https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/18833616/
            var value = _this.props.value !== undefined ? _this.props.value.toString() : null;
            var isBlockEnter = isKeyEnter(e) && value !== null && value.length === _this.props.maxLength;
            if (isBlockEnter) {
                e.preventDefault();
            }
            if (_this.props.onKeyDown) {
                _this.props.onKeyDown(e);
            }
        };
        _this.handleChange = function (e) {
            if (needsPolyfillPlaceholder) {
                var fieldIsEmpty = e.target.value === '';
                if (_this.state.needsPolyfillPlaceholder !== fieldIsEmpty) {
                    _this.setState({ needsPolyfillPlaceholder: fieldIsEmpty });
                }
            }
            if (_this.props.onValueChange) {
                _this.props.onValueChange(e.target.value);
            }
            if (_this.props.autoResize) {
                _this.autoResize();
            }
            if (_this.props.onChange) {
                _this.props.onChange(e);
            }
            _this.reflowCounter();
        };
        _this.ref = function (element) {
            _this.node = element;
        };
        _this.refFake = function (element) {
            _this.fakeNode = element;
        };
        _this.refCounter = function (ref) {
            _this.counter = ref;
        };
        _this.autoResizeHandler = function () {
            var fakeNode = _this.fakeNode;
            if (!fakeNode) {
                return;
            }
            var node = _this.node;
            if (!node) {
                return;
            }
            if (_this.props.value === undefined) {
                fakeNode.value = node.value;
            }
            var _a = _this.getProps(), rows = _a.rows, maxRows = _a.maxRows;
            if (rows === undefined || maxRows === undefined) {
                return;
            }
            var _b = getTextAreaHeight(_this.globalObject, {
                node: fakeNode,
                minRows: typeof rows === 'number' ? rows : parseInt(rows, 10),
                maxRows: typeof maxRows === 'number' ? maxRows : parseInt(maxRows, 10),
                extraRow: _this.getProps().extraRow,
            }) || {}, height = _b.height, exceededMaxHeight = _b.exceededMaxHeight;
            if (height === undefined || exceededMaxHeight === undefined) {
                return;
            }
            node.style.height = height + 'px';
            node.style.overflowY = exceededMaxHeight ? 'scroll' : 'hidden';
            fakeNode.style.overflowY = exceededMaxHeight ? 'scroll' : 'hidden';
        };
        _this.autoResize = throttle(_this.autoResizeHandler, _this.getAutoResizeThrottleWait());
        _this.handleCut = function (event) {
            if (_this.props.autoResize) {
                _this.autoResize();
            }
            if (_this.props.onCut) {
                _this.props.onCut(event);
            }
            _this.reflowCounter();
        };
        _this.handlePaste = function (event) {
            if (_this.props.autoResize) {
                _this.autoResize();
            }
            if (_this.props.onPaste) {
                _this.props.onPaste(event);
            }
            _this.reflowCounter();
        };
        _this.handleFocus = function (event) {
            _this.setState({ isCounterVisible: true });
            if (_this.props.selectAllOnFocus) {
                // https://github.com/facebook/react/issues/7769
                _this.node && _this.selectAll();
            }
            if (_this.props.onFocus) {
                _this.props.onFocus(event);
            }
        };
        return _this;
    }
    Textarea_1 = Textarea;
    Textarea.prototype.getRootSizeClassName = function () {
        switch (this.size) {
            case 'large':
                return this.styles.rootLarge(this.theme);
            case 'medium':
                return this.styles.rootMedium(this.theme);
            case 'small':
            default:
                return this.styles.rootSmall(this.theme);
        }
    };
    Textarea.prototype.getTextareaSizeClassName = function () {
        switch (this.size) {
            case 'large':
                return this.styles.textareaLarge(this.theme);
            case 'medium':
                return this.styles.textareaMedium(this.theme);
            case 'small':
            default:
                return this.styles.textareaSmall(this.theme);
        }
    };
    Textarea.prototype.getAutoResizeThrottleWait = function (props) {
        if (props === void 0) { props = this.props; }
        // NOTE: При отключении анимации остается эффект дергания при авто-ресайзе из-за троттлинга расчета высоты
        // Поэтому выставляем таймаут троттла в ноль. Подробности - https://github.com/skbkontur/retail-ui/issues/2120
        return this.isAnimationsDisabled(props) ? 0 : AUTORESIZE_THROTTLE_DEFAULT_WAIT;
    };
    Textarea.prototype.isAnimationsDisabled = function (_a) {
        var _b = _a === void 0 ? this.props : _a, disableAnimations = _b.disableAnimations, extraRow = _b.extraRow;
        return disableAnimations || !extraRow;
    };
    Textarea.prototype.componentDidMount = function () {
        this.textareaObserver = this.globalObject.MutationObserver
            ? new this.globalObject.MutationObserver(this.reflowCounter)
            : null;
        if (this.props.autoResize) {
            this.autoResize();
            this.layoutEvents = LayoutEvents.addListener(this.autoResize, this.globalObject);
        }
        if (this.node && this.props.showLengthCounter && this.textareaObserver) {
            this.textareaObserver.observe(this.node, { attributes: true });
        }
    };
    Textarea.prototype.componentWillUnmount = function () {
        if (this.layoutEvents) {
            this.layoutEvents.remove();
        }
        if (this.props.showLengthCounter && this.textareaObserver) {
            this.textareaObserver.disconnect();
        }
        this.cancelDelayedSelectAll();
    };
    Textarea.prototype.componentDidUpdate = function (prevProps) {
        if (this.getAutoResizeThrottleWait() !== this.getAutoResizeThrottleWait(prevProps)) {
            this.autoResize.cancel();
            this.autoResize = throttle(this.autoResizeHandler, this.getAutoResizeThrottleWait());
        }
        var _a = this.getProps(), rows = _a.rows, maxRows = _a.maxRows;
        if (this.props.autoResize &&
            (rows !== prevProps.rows || maxRows !== prevProps.maxRows || this.props.value !== prevProps.value)) {
            this.autoResize();
        }
    };
    Textarea.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));
        }));
    };
    /** Программно устанавливает фокус на поле.
     * @public
     */
    Textarea.prototype.focus = function () {
        if (this.node) {
            this.node.focus();
        }
    };
    /** Программно снимает фокус с поля.
     * @public
     */
    Textarea.prototype.blur = function () {
        if (this.node) {
            this.node.blur();
        }
    };
    Textarea.prototype.getNode = function () {
        return this.node;
    };
    var Textarea_1;
    Textarea.__KONTUR_REACT_UI__ = 'Textarea';
    Textarea.displayName = 'Textarea';
    Textarea.defaultProps = {
        rows: 3,
        maxRows: 15,
        extraRow: true,
        disableAnimations: isTestEnv,
    };
    Textarea = Textarea_1 = __decorate([
        withRenderEnvironment,
        rootNode,
        withSize
    ], Textarea);
    return Textarea;
}(React.Component));
export { Textarea };
//# sourceMappingURL=Textarea.js.map