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;
};
import React from 'react';
import invariant from 'invariant';
import debounce from 'lodash.debounce';
import { isKeyArrowDown, isKeyArrowUp, isKeyArrowVertical, isKeyEnter, isKeyEscape, isKeySpace, } from '../../lib/events/keyboard/identifiers.js';
import { locale } from '../../lib/locale/decorators.js';
import { reactGetTextContent } from '../../lib/reactGetTextContent.js';
import { Button } from '../Button/index.js';
import { filterProps } from '../../lib/filterProps.js';
import { Input } from '../Input/index.js';
import { Menu } from '../../internal/Menu/index.js';
import { MenuItem } from '../MenuItem/index.js';
import { MenuSeparator } from '../MenuSeparator/index.js';
import { RenderLayer } from '../../internal/RenderLayer/index.js';
import { createPropsGetter } from '../../lib/createPropsGetter.js';
import { getRandomID, isFunction, isNonNullable, isReactUINode } from '../../lib/utils.js';
import { ThemeContext } from '../../lib/theming/ThemeContext.js';
import { CommonWrapper } from '../../internal/CommonWrapper/index.js';
import { MobilePopup } from '../../internal/MobilePopup/index.js';
import { responsiveLayout } from '../ResponsiveLayout/decorator.js';
import { getRootNode, rootNode } from '../../lib/rootNode/index.js';
import { ThemeFactory } from '../../lib/theming/ThemeFactory.js';
import { getStyles as getLinkStyles } from '../Link/Link.styles.js';
import { Popup } from '../../internal/Popup/index.js';
import { ZIndex } from '../../internal/ZIndex/index.js';
import { getMenuPositions } from '../../lib/getMenuPositions.js';
import { withSize } from '../../lib/size/SizeDecorator.js';
import { withRenderEnvironment } from '../../lib/renderEnvironment/index.js';
import { ArrowDownIcon } from './ArrowDownIcon.js';
import { Item } from './Item.js';
import { SelectLocaleHelper } from './locale/index.js';
import { getStyles } from './Select.styles.js';
import { getSelectTheme } from './selectTheme.js';
import { SelectDataTids } from './tids.js';
export * from './tids.js';
var PASS_BUTTON_PROPS = {
    id: true,
    disabled: true,
    error: true,
    use: true,
    size: true,
    warning: true,
    corners: true,
    onMouseEnter: true,
    onMouseLeave: true,
    onMouseOver: true,
};
export var SelectIds = {
    menu: SelectDataTids.menu,
};
/**
 * Раскрывающийся список позволяет выбрать значение из заранее известного набора вариантов.
 */
var Select = /** @class */ (function (_super) {
    __extends(Select, _super);
    // Suggested solutions break current behavior
    // eslint-disable-next-line @typescript-eslint/ban-types
    function Select() {
        var _this = _super !== null && _super.apply(this, arguments) || this;
        _this.state = {
            opened: false,
            value: _this.props.defaultValue,
            searchPattern: '',
        };
        _this.menuId = SelectIds.menu + getRandomID();
        _this.buttonElement = null;
        _this.getProps = createPropsGetter(Select_1.defaultProps);
        /** Открывает раскрывающийся список.
         * @public
         */
        _this.open = function () {
            if (!_this.state.opened) {
                _this.setState({ opened: true });
                if (_this.props.onOpen) {
                    _this.props.onOpen();
                }
            }
        };
        /** Закрывает раскрывающийся список.
         * @public
         */
        _this.close = function () {
            if (_this.state.opened) {
                _this.setState({ opened: false, searchPattern: '' });
                if (_this.props.onClose) {
                    _this.props.onClose();
                }
            }
        };
        /** Программно устанавливает фокус на кнопку.
         * Появляется фокусная рамка, элемент получает клавиатурные события и воспринимается как текущий элемент для чтения скринридерами.
         * @public
         */
        _this.focus = function () {
            if (_this.buttonElement && _this.buttonElement.focus) {
                _this.buttonElement.focus();
            }
        };
        _this.getDefaultButtonParams = function () {
            var _a = _this.renderLabel(), label = _a.label, isPlaceholder = _a.isPlaceholder;
            var buttonParams = {
                opened: _this.state.opened,
                label: label,
                isPlaceholder: isPlaceholder,
                onClick: _this.toggle,
                onKeyDown: _this.handleKey,
                size: _this.size,
                disabled: _this.getProps().disabled,
            };
            return buttonParams;
        };
        _this.getSearch = function () {
            return (React.createElement("div", { className: _this.styles.search(), onKeyDown: _this.handleKey },
                React.createElement(Input, { ref: _this.debouncedFocusInput, onValueChange: _this.handleSearch, width: "100%" })));
        };
        _this.getMobileSearch = function () {
            return (React.createElement(Input, { autoFocus: true, value: _this.state.searchPattern, ref: _this.debouncedFocusInput, onValueChange: _this.handleSearch, width: "100%" }));
        };
        _this.getMenuItems = function (value) {
            var isMobile = _this.isMobileLayout;
            var size = _this.size;
            return _this.mapItems(function (iValue, item, i, comment) {
                if (isFunction(item)) {
                    var element = item();
                    if (React.isValidElement(element)) {
                        return React.cloneElement(element, { key: i, isMobile: isMobile, size: size });
                    }
                    return null;
                }
                if (React.isValidElement(item)) {
                    if (isReactUINode('MenuItem', item)) {
                        return React.cloneElement(item, { key: i, isMobile: isMobile, size: size });
                    }
                    if (isReactUINode('MenuHeader', item)) {
                        return React.cloneElement(item, { size: size });
                    }
                    return React.cloneElement(item, { key: i });
                }
                return (React.createElement(MenuItem, { key: i, state: _this.areValuesEqual(iValue, value) ? 'selected' : null, onClick: _this.select.bind(_this, iValue), comment: comment, isMobile: isMobile, size: _this.size }, _this.getProps().renderItem(iValue, item)));
            });
        };
        _this.popupGetParent = function () {
            return getRootNode(_this);
        };
        // fix cases when an Input is rendered in portal
        // https://github.com/skbkontur/retail-ui/issues/1995
        _this.focusInput = function (input) { return input === null || input === void 0 ? void 0 : input.focus(); };
        _this.debouncedFocusInput = debounce(_this.focusInput);
        _this.refMenu = function (menu) {
            _this.menu = menu;
        };
        _this.toggle = function () {
            if (_this.state.opened) {
                _this.close();
            }
            else {
                _this.open();
            }
        };
        _this.handleKey = function (e) {
            if (!_this.state.opened) {
                if (isKeySpace(e) || isKeyArrowVertical(e)) {
                    e.preventDefault();
                    _this.open();
                }
            }
            else {
                switch (true) {
                    case isKeyEscape(e):
                        _this.focus();
                        _this.close();
                        break;
                    case isKeyArrowUp(e):
                        e.preventDefault();
                        if (_this.menu) {
                            _this.menu.up();
                        }
                        break;
                    case isKeyArrowDown(e):
                        e.preventDefault();
                        if (_this.menu) {
                            _this.menu.down();
                        }
                        break;
                    case isKeyEnter(e):
                        e.preventDefault(); // To prevent form submission.
                        if (_this.menu) {
                            _this.menu.enter(e);
                        }
                        break;
                }
            }
            if (_this.props.onKeyDown) {
                _this.props.onKeyDown(e);
            }
        };
        _this.handleSearch = function (value) {
            var _a;
            _this.setState({ searchPattern: value });
            (_a = _this.menu) === null || _a === void 0 ? void 0 : _a.highlightItem(0);
        };
        _this.buttonRef = function (element) {
            _this.buttonElement = element;
        };
        _this.getButton = function (buttonParams) {
            var _a;
            var button = _this.props._renderButton
                ? _this.props._renderButton(buttonParams)
                : _this.renderDefaultButton(buttonParams);
            var buttonElement = React.Children.only(button);
            return React.isValidElement(buttonElement)
                ? React.cloneElement(buttonElement, {
                    ref: _this.buttonRef,
                    onFocus: _this.props.onFocus,
                    onBlur: _this.props.onBlur,
                    size: _this.size,
                    'aria-describedby': _this.props['aria-describedby'],
                    'aria-expanded': _this.state.opened ? 'true' : 'false',
                    'aria-controls': _this.menuId,
                    'aria-label': (_a = buttonElement.props['aria-label']) !== null && _a !== void 0 ? _a : _this.props['aria-label'],
                })
                : buttonElement;
        };
        return _this;
    }
    Select_1 = Select;
    Select.prototype.componentDidUpdate = function (_prevProps, prevState) {
        var _a, _b, _c, _d;
        if (!prevState.opened && this.state.opened) {
            (_b = (_a = this.globalObject).addEventListener) === null || _b === void 0 ? void 0 : _b.call(_a, 'popstate', this.close);
        }
        if (prevState.opened && !this.state.opened) {
            (_d = (_c = this.globalObject).removeEventListener) === null || _d === void 0 ? void 0 : _d.call(_c, 'popstate', this.close);
        }
    };
    Select.prototype.render = function () {
        var _this = this;
        this.linkStyles = getLinkStyles(this.emotion);
        this.styles = getStyles(this.emotion);
        return (React.createElement(ThemeContext.Consumer, null, function (theme) {
            _this.theme = ThemeFactory.create({
                menuOffsetY: theme.selectMenuOffsetY,
            }, theme);
            return React.createElement(ThemeContext.Provider, { value: _this.theme }, _this.renderMain());
        }));
    };
    Select.prototype.getMenuRenderer = function () {
        if (this.props.disabled) {
            return null;
        }
        if (this.isMobileLayout) {
            return this.renderMobileMenu();
        }
        if (this.state.opened) {
            return this.renderMenu();
        }
        return null;
    };
    Select.prototype.renderMain = function () {
        var _a;
        var _b;
        var buttonParams = this.getDefaultButtonParams();
        var dataTid = (_b = this.getProps()['data-tid']) !== null && _b !== void 0 ? _b : SelectDataTids.root;
        var button = (React.createElement(ThemeContext.Provider, { value: getSelectTheme(this.theme, this.props) }, this.getButton(buttonParams)));
        var isMobile = this.isMobileLayout;
        var style = {
            width: this.props.width,
            maxWidth: this.props.maxWidth || undefined,
        };
        var root = (React.createElement("span", { "data-tid": dataTid, className: this.cx((_a = {}, _a[this.styles.root()] = true, _a[this.styles.rootMobile(this.theme)] = isMobile, _a)), style: style },
            button,
            this.getMenuRenderer()));
        return (React.createElement(CommonWrapper, __assign({ rootNodeRef: this.setRootNode }, this.props),
            React.createElement(RenderLayer, { onClickOutside: this.close, onFocusOutside: this.close, active: isMobile ? false : this.state.opened }, root)));
    };
    Select.prototype.renderLabel = function () {
        var _a, _b;
        var value = this.getValue();
        var item = this.getItemByValue(value);
        if (isNonNullable(value)) {
            return {
                label: this.getProps().renderValue(value, item),
                isPlaceholder: false,
            };
        }
        return {
            label: React.createElement("span", null, (_a = this.props.placeholder) !== null && _a !== void 0 ? _a : (_b = this.locale) === null || _b === void 0 ? void 0 : _b.placeholder),
            isPlaceholder: true,
        };
    };
    Select.prototype.getLeftIconClass = function (size) {
        if (this.getProps().use === 'link') {
            return this.styles.leftIconLink(this.theme);
        }
        switch (size) {
            case 'large':
                return this.styles.leftIconLarge(this.theme);
            case 'medium':
                return this.styles.leftIconMedium(this.theme);
            case 'small':
            default:
                return this.styles.leftIconSmall(this.theme);
        }
    };
    Select.prototype.renderDefaultButton = function (params) {
        var _a, _b, _c;
        var buttonProps = __assign(__assign({}, filterProps(this.props, PASS_BUTTON_PROPS)), { align: 'left', disabled: this.props.disabled, width: '100%', onClick: params.onClick, onKeyDown: params.onKeyDown, active: params.opened, size: params.size });
        var use = this.getProps().use;
        var labelProps = {
            'data-tid': SelectDataTids.label,
            className: this.cx((_a = {},
                _a[this.styles.label()] = use !== 'link',
                _a[this.styles.placeholder(this.theme)] = params.isPlaceholder,
                _a[this.styles.customUsePlaceholder()] = params.isPlaceholder && use !== 'default',
                _a[this.styles.placeholderDisabled(this.theme)] = params.isPlaceholder && this.props.disabled,
                _a)),
            style: {
                paddingRight: this.getSelectIconGap(),
            },
        };
        var useIsCustom = use !== 'default';
        var icon = React.createElement(ArrowDownIcon, { size: this.size });
        return (React.createElement(Button, __assign({}, buttonProps),
            React.createElement("div", { className: this.cx(this.styles.selectButtonContainer(), (_b = {},
                    _b[this.linkStyles.root(this.theme)] = use === 'link',
                    _b)) },
                this.props._icon && React.createElement("div", { className: this.getLeftIconClass(this.size) }, this.props._icon),
                React.createElement("span", __assign({}, labelProps), params.label),
                React.createElement("div", { className: this.cx(this.styles.arrowWrap(this.theme), (_c = {},
                        _c[this.styles.arrowDisabled(this.theme)] = this.props.disabled,
                        _c[this.styles.customUseArrow()] = useIsCustom,
                        _c)) }, icon))));
    };
    Select.prototype.getSelectIconGap = function () {
        var _this = this;
        var getArrowPadding = function () {
            switch (_this.size) {
                case 'large':
                    return _this.theme.selectIconGapLarge;
                case 'medium':
                    return _this.theme.selectIconGapMedium;
                case 'small':
                default:
                    return _this.theme.selectIconGapSmall;
            }
        };
        var arrowLeftPadding = parseFloat(getArrowPadding()) || 0;
        return arrowLeftPadding;
    };
    Select.prototype.renderMenu = function () {
        var search = this.props.search ? this.getSearch() : null;
        var value = this.getValue();
        var _a = this.getProps(), menuWidth = _a.menuWidth, menuPos = _a.menuPos, menuAlign = _a.menuAlign, positions = _a.positions;
        return (React.createElement(Popup, { opened: true, hasShadow: true, id: this.menuId, "data-tid": SelectDataTids.menu, positions: positions !== null && positions !== void 0 ? positions : getMenuPositions(menuPos, menuAlign), anchorElement: this.popupGetParent(), priority: ZIndex.priorities.PopupMenu, disablePortal: this.props.disablePortal, margin: parseInt(this.theme.menuOffsetY) - 1, width: menuWidth, minWidth: menuWidth === undefined ? '100%' : undefined, popupOffset: this.props.menuOffset },
            React.createElement(Menu, { hasMargin: false, ref: this.refMenu, onItemClick: this.close, maxHeight: this.props.maxMenuHeight, align: menuAlign },
                search,
                this.getMenuItems(value))));
    };
    Select.prototype.renderMobileMenu = function () {
        var search = this.props.search ? this.getMobileSearch() : null;
        var value = this.getValue();
        return (React.createElement(MobilePopup, { headerChildComponent: search, caption: this.props.mobileMenuHeaderText, onCloseRequest: this.close, opened: this.state.opened },
            React.createElement(Menu, { onItemClick: this.close, disableScrollContainer: true, maxHeight: 'auto' }, this.getMenuItems(value))));
    };
    Select.prototype.select = function (value) {
        var _a, _b;
        this.focus();
        this.setState({ opened: false, value: value });
        if (!this.areValuesEqual(this.getValue(), value)) {
            (_b = (_a = this.props).onValueChange) === null || _b === void 0 ? void 0 : _b.call(_a, value);
        }
    };
    Select.prototype.getValue = function () {
        if (this.props.value !== undefined) {
            return this.props.value;
        }
        return this.state.value;
    };
    Select.prototype.mapItems = function (fn) {
        var items = this.props.items;
        if (!items) {
            return [];
        }
        var pattern = this.state.searchPattern && this.state.searchPattern.toLowerCase();
        var result = [];
        var index = 0;
        for (var _i = 0, items_1 = items; _i < items_1.length; _i++) {
            var entry = items_1[_i];
            var _a = normalizeEntry(entry), value = _a[0], item = _a[1], comment = _a[2];
            if (!pattern || this.getProps().filterItem(value, item, pattern)) {
                result.push(fn(value, item, index, comment));
                ++index;
            }
        }
        return result;
    };
    Select.prototype.getItemByValue = function (value) {
        if (value === null || value === undefined) {
            return null;
        }
        var items = this.props.items || [];
        for (var _i = 0, items_2 = items; _i < items_2.length; _i++) {
            var entry = items_2[_i];
            var _a = normalizeEntry(entry), itemValue = _a[0], item = _a[1];
            if (this.areValuesEqual(itemValue, value)) {
                return item;
            }
        }
        return null;
    };
    Select.prototype.areValuesEqual = function (value1, value2) {
        return isNonNullable(value1) && isNonNullable(value2) && this.getProps().areValuesEqual(value1, value2);
    };
    var Select_1;
    Select.__KONTUR_REACT_UI__ = 'Select';
    Select.displayName = 'Select';
    Select.defaultProps = {
        renderValue: renderValue,
        renderItem: renderItem,
        areValuesEqual: areValuesEqual,
        filterItem: filterItem,
        use: 'default',
    };
    Select.Item = Item;
    Select.SEP = function () { return React.createElement(MenuSeparator, null); };
    Select.staticElement = function (element) {
        invariant(React.isValidElement(element) || typeof element === 'function', 'Select.staticElement(element) expects element to be a valid react element.');
        return element;
    };
    Select = Select_1 = __decorate([
        withRenderEnvironment,
        responsiveLayout,
        rootNode,
        locale('Select', SelectLocaleHelper),
        withSize
        // Suggested solutions break current behavior
        // eslint-disable-next-line @typescript-eslint/ban-types
    ], Select);
    return Select;
}(React.Component));
export { Select };
function renderValue(value, item) {
    return item;
}
function renderItem(value, item) {
    return item;
}
function areValuesEqual(value1, value2) {
    return value1 === value2;
}
function normalizeEntry(entry) {
    if (Array.isArray(entry)) {
        return entry;
    }
    return [entry, entry, undefined];
}
var getTextFromItem = function (item) {
    if (typeof item === 'string') {
        return item;
    }
    if (isFunction(item)) {
        return getTextFromItem(item());
    }
    if (React.isValidElement(item)) {
        return reactGetTextContent(item);
    }
    if (typeof item === 'number') {
        return item.toString(10);
    }
    return '';
};
function filterItem(value, item, pattern) {
    if (item === Select.SEP) {
        return false;
    }
    var itemText = getTextFromItem(item);
    if (!itemText) {
        return false;
    }
    return itemText.toLowerCase().indexOf(pattern) !== -1;
}
//# sourceMappingURL=Select.js.map