import React from 'react';
import StatusIndicator from '../StatusIndicator';
import { STATUS } from '../StatusIndicator/StatusIndicator.constants';
import clamp from 'lodash/clamp';
import debounce from 'lodash/debounce';
import isNaN from 'lodash/isNaN';
import { st, classes } from './InputArea.st.css.js';
import { dataAttr, dataHooks } from './constants';
import { filterObject } from '../utils/filterObject';
import { StatusContext, getStatusFromContext, getAriaAttributesFromContext, } from '../FormField/StatusContext';
import semanticClassNames from './InputArea.semanticClassNames';
/**
 * General inputArea container
 */
class InputArea extends React.PureComponent {
    constructor() {
        super(...arguments);
        this.textArea = null;
        this._computedStyle = null;
        this.state = {
            focus: false,
            counter: (this.props.value || this.props.defaultValue || '').length,
            computedRows: this.props.minRowsAutoGrow,
        };
        // For testing purposes only
        this._getDataAttr = ({ statusContext }) => {
            const { size, status, disabled, resizable, forceHover, forceFocus } = this.props;
            return filterObject({
                [dataAttr.SIZE]: size,
                [dataAttr.STATUS]: !disabled
                    ? getStatusFromContext(statusContext, status)
                    : null,
                [dataAttr.DISABLED]: !!disabled,
                [dataAttr.RESIZABLE]: !!resizable && !disabled,
                [dataAttr.HOVER]: !!forceHover,
                [dataAttr.FOCUS]: !!(forceFocus || this.state.focus),
            }, (_, value) => !!value);
        };
        this.focus = () => {
            this.textArea?.focus();
        };
        this.blur = () => {
            this.textArea?.blur();
        };
        this.select = () => {
            this.textArea?.select();
        };
        this.calculateComputedRows = () => {
            const { minRowsAutoGrow, maxRowsAutoGrow } = this.props;
            this.setState({ computedRows: 1 }, () => {
                const rowsCount = this._getRowsCount();
                const computedRows = clamp(rowsCount, minRowsAutoGrow || InputArea.MIN_ROWS, maxRowsAutoGrow || InputArea.MAX_ROWS);
                this.setState({
                    computedRows,
                });
            });
        };
        this._onFocus = (e) => {
            this.setState({ focus: true });
            this.props.onFocus && this.props.onFocus(e);
            if (this.props.autoSelect) {
                // Set timeout is needed here since onFocus is called before react
                // gets the reference for the input (specifically when autoFocus
                // is on. So setTimeout ensures we have the ref.input needed in select)
                setTimeout(() => this.select(), 0);
            }
        };
        this._onBlur = (e) => {
            this.setState({ focus: false });
            this.props.onBlur && this.props.onBlur(e);
        };
        this._onKeyDown = (e) => {
            this.props.onKeyDown && this.props.onKeyDown(e);
            if (e.key === 'Enter') {
                this.props.onEnterPressed && this.props.onEnterPressed(e);
            }
            else if (e.key === 'Escape') {
                this.props.onEscapePressed && this.props.onEscapePressed();
            }
        };
        this._onChange = (e) => {
            this.props.onChange && this.props.onChange(e);
        };
        this._onInput = () => {
            this.calculateComputedRows();
        };
        this._updateComputedStyle = debounce(() => {
            if (this.textArea) {
                this._computedStyle = window.getComputedStyle(this.textArea);
            }
        }, 500, { leading: true });
        this._getComputedStyle = () => {
            this._updateComputedStyle();
            return this._computedStyle;
        };
        this._getRowsCount = () => {
            const computedStyle = this._getComputedStyle();
            const fontSize = parseInt(computedStyle?.getPropertyValue('font-size') ?? '', 10);
            const lineHeight = parseInt(computedStyle?.getPropertyValue('line-height') ?? '', 10);
            let lineHeightValue;
            if (isNaN(lineHeight)) {
                if (isNaN(fontSize)) {
                    return InputArea.MIN_ROWS;
                }
                lineHeightValue = this._getDefaultLineHeight() ?? 0 * fontSize;
            }
            else {
                lineHeightValue = lineHeight;
            }
            const paddingTop = parseFloat(computedStyle?.getPropertyValue('padding-top') ?? '0');
            const paddingBottom = parseFloat(computedStyle?.getPropertyValue('padding-bottom') ?? '0');
            const scrollHeight = this.textArea?.scrollHeight ?? 0;
            const contentHeight = scrollHeight - paddingTop - paddingBottom;
            return Math.floor(contentHeight / lineHeightValue);
        };
        this._getDefaultLineHeight = () => {
            if (!this._defaultLineHeight && this.textArea) {
                const { parentNode } = this.textArea;
                const computedStyles = this._getComputedStyle();
                const fontFamily = computedStyles?.getPropertyValue('font-family');
                const fontSize = computedStyles?.getPropertyValue('font-size');
                const tempElement = document.createElement('span');
                const defaultStyles = 'position:absolute;display:inline;border:0;margin:0;padding:0;line-height:normal;';
                tempElement.setAttribute('style', `${defaultStyles}font-family:${fontFamily};font-size:${fontSize};`);
                tempElement.innerText = 'M';
                parentNode?.appendChild(tempElement);
                this._defaultLineHeight =
                    parseInt(tempElement.clientHeight.toString(), 10) /
                        parseInt(fontSize ?? '0', 10);
                tempElement.parentNode?.removeChild(tempElement);
            }
            return this._defaultLineHeight;
        };
    }
    componentDidMount() {
        const { autoFocus, autoGrow, value } = this.props;
        autoFocus && this._onFocus();
        if (autoGrow) {
            this.calculateComputedRows();
        }
        /*
         * autoFocus doesn't automatically selects text like focus do.
         * Therefore we set the selection range, but in order to support prior implementation we set the start position as the end in order to place the cursor there.
         */
        if (autoFocus && !!value) {
            this.textArea?.setSelectionRange(value.length, value.length);
        }
    }
    componentDidUpdate(prevProps) {
        const { minRowsAutoGrow, maxRowsAutoGrow, value, defaultValue, autoGrow, hasCounter, } = this.props;
        if (autoGrow &&
            (prevProps.minRowsAutoGrow !== minRowsAutoGrow ||
                prevProps.maxRowsAutoGrow !== maxRowsAutoGrow)) {
            this.calculateComputedRows();
        }
        if (hasCounter && prevProps.value !== value) {
            this.setState({
                counter: (value || defaultValue || '').length,
            });
        }
    }
    componentWillUnmount() {
        this._updateComputedStyle.cancel();
    }
    render() {
        const { dataHook, className, autoFocus, defaultValue, disabled, forceFocus, forceHover, id, name, onKeyUp, placeholder, readOnly, tabIndex, rows, autoGrow, value, required, minHeight, maxHeight, minLength, maxLength, resizable, hasCounter, size, tooltipPlacement, status, statusMessage, children, onCompositionStart, onCompositionEnd, dir, } = this.props;
        const inlineStyle = {};
        const rowsAttr = rows
            ? rows
            : autoGrow
                ? this.state.computedRows
                : undefined;
        const onInput = !rows && autoGrow ? this._onInput : undefined;
        if (minHeight) {
            inlineStyle.minHeight = minHeight;
        }
        if (maxHeight) {
            inlineStyle.maxHeight = maxHeight;
        }
        const ariaAttribute = {};
        Object.keys(this.props)
            .filter(key => key.startsWith('aria'))
            .map(key => (ariaAttribute['aria-' + key.substr(4).toLowerCase()] = this.props[key]));
        return (React.createElement(StatusContext.Consumer, null, statusContext => {
            const finalStatus = getStatusFromContext(statusContext, status);
            return (React.createElement("div", { "data-hook": dataHook, className: st(classes.root, {
                    disabled,
                    size,
                    status: finalStatus,
                    hasFocus: forceFocus || this.state.focus,
                    forceHover,
                    resizable,
                    readOnly,
                }, className), ...this._getDataAttr({ statusContext }) },
                React.createElement("div", { className: st(classes.inputArea, semanticClassNames.inputContainer) },
                    typeof children === 'function' ? (children({
                        className: '',
                        rows: rowsAttr || 0,
                        ref: ref => (this.textArea = ref),
                        onFocus: (e) => this._onFocus(e),
                        onBlur: (e) => this._onBlur(e),
                        onKeyDown: (e) => this._onKeyDown(e),
                        onInput,
                        dir,
                    })) : (React.createElement("textarea", { rows: rowsAttr, minLength: minLength, maxLength: maxLength, ref: ref => {
                            this.textArea = ref;
                        }, id: id, name: name, style: inlineStyle, defaultValue: defaultValue, disabled: disabled, value: value, required: required, onFocus: this._onFocus, onBlur: this._onBlur, onKeyDown: this._onKeyDown, onChange: this._onChange, onInput: onInput, placeholder: placeholder, tabIndex: tabIndex, autoFocus: autoFocus, onKeyUp: onKeyUp, ...getAriaAttributesFromContext(statusContext), ...ariaAttribute, readOnly: readOnly, onCompositionStart: onCompositionStart, onCompositionEnd: onCompositionEnd, dir: dir })),
                    hasCounter && maxLength && (React.createElement("span", { className: classes.counter, "data-hook": "counter" },
                        this.state.counter,
                        "/",
                        maxLength))),
                React.createElement("div", { className: classes.status }, (!!status || finalStatus === STATUS.LOADING) && !disabled && (React.createElement(StatusIndicator, { dataHook: dataHooks.tooltip, status: finalStatus ?? undefined, message: statusMessage, tooltipProps: {
                        placement: tooltipPlacement,
                    } })))));
        }));
    }
}
// For autoGrow prop min rows is 2 so the textarea does not look like an input
InputArea.MIN_ROWS = 2;
// For autoGrow prop max rows is Infinity to keep backwards compatibility
InputArea.MAX_ROWS = Number.POSITIVE_INFINITY;
InputArea.displayName = 'InputArea';
InputArea.defaultProps = {
    minRowsAutoGrow: InputArea.MIN_ROWS,
    maxRowsAutoGrow: InputArea.MAX_ROWS,
    size: 'medium',
};
export default InputArea;
//# sourceMappingURL=InputArea.js.map