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 __spreadArrays = (this && this.__spreadArrays) || function () {
    for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length;
    for (var r = Array(s), k = 0, i = 0; i < il; i++)
        for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++)
            r[k] = a[j];
    return r;
};
import React, { PureComponent } from 'react';
import PropTypes from 'prop-types';
import './InputSelectDropdown.scss';
import ChevronDownSvg from '../Icons/ChevronDownSvg';
import XIconSvg from '../Icons/XIconSvg';
import InputCheckbox from '../InputCheckbox';
import AvatarIcon from '../Icons/AvatarIcon';
// W3 guide on implementing an accessible custom dropdown select
// https://www.w3.org/TR/wai-aria-practices/examples/listbox/listbox-collapsible.html
var InputSelectDropdown = /** @class */ (function (_super) {
    __extends(InputSelectDropdown, _super);
    function InputSelectDropdown(props) {
        var _this = _super.call(this, props) || this;
        _this.doesInputCacheMatchText = function (text) {
            if (!_this.state.inputCache) {
                return true;
            }
            var lowerCaseInputCache = _this.state.inputCache.toLowerCase();
            var lowerCaseText = text.toLowerCase();
            return lowerCaseText.includes(lowerCaseInputCache);
        };
        _this.focusFirstOption = function () { return _this.dropdownRef.current.firstChild.querySelector('input').focus(); };
        _this.focusLastOption = function () { return _this.dropdownRef.current.lastChild.querySelector('input').focus(); };
        _this.focusNextOption = function () {
            if (document.activeElement === _this.inputCacheRef.current) {
                _this.focusFirstOption();
                return;
            }
            if (!_this.dropdownRef.current || !_this.dropdownRef.current.contains(document.activeElement)) {
                return;
            }
            var focusedOption = document.activeElement.closest('.input-select__dropdown-option');
            if (!focusedOption) {
                return;
            }
            if (!focusedOption.nextSibling) {
                return;
            }
            focusedOption.nextSibling.querySelector('input').focus();
        };
        _this.focusPreviousOption = function () {
            if (!_this.dropdownRef.current || !_this.dropdownRef.current.contains(document.activeElement)) {
                return;
            }
            var focusedOption = document.activeElement.closest('.input-select__dropdown-option');
            if (!focusedOption) {
                return;
            }
            if (!focusedOption.previousSibling) {
                if (_this.inputCacheRef.current) {
                    _this.inputCacheRef.current.focus();
                }
                return;
            }
            focusedOption.previousSibling.querySelector('input').focus();
        };
        _this.getInputId = function (value) { return "input-select_" + _this.props.id + "_" + value; };
        _this.getSelectedCount = function () {
            if (_this.props.isMulti) {
                return _this.props.value.length;
            }
            return _this.props.value ?
                1 :
                0;
        };
        _this.handleButtonClick = function () { return _this.toggleDropdown(); };
        _this.handleButtonKeyUp = function (event) {
            var code = event.code, 
            // keyCode backup for shitty IE/Edge that don't support event.code
            keyCode = event.keyCode;
            // If event.code is undefined, we're on shitty IE or Edge so we gotta kick it old school with a deprecated API
            switch (code || keyCode) { // eslint-disable-line default-case, `don't update value if irrelevant key is pressed`
                case 'ArrowDown':
                case 40: // eslint-disable-line no-magic-numbers, `down keyCode, blame IE/Edge for not supporting event.code`
                case 'ArrowUp':
                case 38: // eslint-disable-line no-magic-numbers, `up keyCode, blame IE/Edge for not supporting event.code`
                    _this.openDropdown();
                    break;
            }
        };
        _this.handleBodyClick = function (event) {
            if (_this.wrapperRef.current && _this.wrapperRef.current.contains(event.target)) {
                return;
            }
            _this.closeDropdown();
        };
        _this.handleBodyFocus = function (event) {
            if (_this.wrapperRef.current && _this.wrapperRef.current.contains(event.target)) {
                return;
            }
            _this.closeDropdown();
        };
        _this.handleInputCacheChange = function (event) {
            return _this.setState({
                inputCache: event.target.value,
            });
        };
        _this.handleOptionChange = function (value, checked) {
            if (_this.props.isMulti) {
                _this.props.onChange(checked ? __spreadArrays(_this.props.value, [
                    value,
                ]) :
                    _this.props.value.filter(function (val) { return val !== value; }));
            }
            else {
                _this.props.onChange(checked ?
                    value :
                    null);
                if (checked) {
                    _this.closeDropdown();
                }
            }
        };
        _this.handleClearClicked = function () {
            _this.props.onChange(_this.props.isMulti ? [] : null);
            _this.setState({
                isDropdownOpen: false,
            });
            if (_this.buttonRef.current) {
                _this.buttonRef.current.focus();
            }
        };
        _this.handleDropdownKeyUp = function (event) {
            var code = event.code, 
            // keyCode backup for shitty IE/Edge that don't support event.code
            keyCode = event.keyCode;
            // eslint-disable-next-line default-case, `if irrelevant key is pressed, do nothing`
            switch (code || keyCode) {
                case 'Escape':
                // eslint-disable-next-line no-magic-numbers, no-fallthrough, `need use use key codes and fallthroughs for IE/Edge`
                case 27: // escape
                    _this.closeDropdown();
                    break;
                // eslint-disable-next-line no-magic-numbers, `need use use key code for home key`
                case 36: // home
                    _this.focusFirstOption();
                    break;
                // eslint-disable-next-line no-magic-numbers, `need use use key code for end key`
                case 35: // end
                    _this.focusLastOption();
                    break;
                case 'ArrowDown':
                case 40: // eslint-disable-line no-magic-numbers, `down keyCode, blame IE/Edge for not supporting event.code`
                    _this.focusNextOption();
                    break;
                case 'ArrowUp':
                case 38: // eslint-disable-line no-magic-numbers, `up keyCode, blame IE/Edge for not supporting event.code`
                    _this.focusPreviousOption();
                    break;
            }
        };
        _this.isValueSelected = function (value) {
            return _this.props.isMulti ?
                _this.props.value.includes(value) :
                _this.props.value === value;
        };
        _this.closeDropdown = function () {
            if (!_this.state.isDropdownOpen) {
                return;
            }
            _this.setState({
                isDropdownOpen: false,
                inputCache: '',
            }, function () {
                if (_this.wrapperRef.current && _this.wrapperRef.current.contains(document.activeElement) && _this.buttonRef.current) {
                    _this.buttonRef.current.focus();
                }
            });
        };
        _this.openDropdown = function () {
            if (_this.state.isDropdownOpen) {
                return;
            }
            _this.setState({
                isDropdownOpen: true,
            }, function () {
                if (_this.inputCacheRef.current) {
                    _this.inputCacheRef.current.focus();
                }
            });
        };
        _this.toggleDropdown = function () {
            return _this.state.isDropdownOpen ?
                _this.closeDropdown() :
                _this.openDropdown();
        };
        _this.renderButtonText = function () {
            if (_this.props.isMulti) {
                return (React.createElement(React.Fragment, null,
                    React.createElement("span", null, _this.props.label),
                    Boolean(_this.getSelectedCount()) &&
                        React.createElement("span", { "aria-label": _this.getSelectedCount() + " selected", className: "input-select__button-text-count" },
                            "(",
                            _this.getSelectedCount(),
                            ")")));
            }
            var selectedOption = _this.props.options.find(function (x) { return x.value === _this.props.value; });
            if (selectedOption) {
                return React.createElement("span", null, selectedOption.label || selectedOption.value);
            }
            return React.createElement("span", null, _this.props.label);
        };
        _this.state = {
            isDropdownOpen: false,
            inputCache: '',
        };
        _this.wrapperRef = React.createRef();
        _this.buttonRef = React.createRef();
        _this.dropdownRef = React.createRef();
        _this.dropdownWrapperRef = React.createRef();
        _this.inputCacheRef = React.createRef();
        return _this;
    }
    InputSelectDropdown.prototype.componentDidMount = function () {
        document.body.addEventListener('click', this.handleBodyClick);
        document.body.addEventListener('focus', this.handleBodyFocus, true);
    };
    InputSelectDropdown.prototype.componentWillUnmount = function () {
        document.body.removeEventListener('click', this.handleBodyClick);
        document.body.removeEventListener('focus', this.handleBodyFocus);
    };
    InputSelectDropdown.prototype.render = function () {
        var _this = this;
        return (
        // eslint-disable-next-line jsx-a11y/no-static-element-interactions, `catch-all key listener, children have roles`
        React.createElement("div", { className: [
                'input-select',
                'qa-input-select-dropdown',
                this.props.className,
                this.state.isDropdownOpen && 'is-open',
                this.props.isGrid ? 'input-select--grid' : 'input-select--list',
                this.props.isMulti ? 'input-select--multi' : 'input-select--single',
            ]
                .filter(Boolean)
                .join(' '), ref: this.wrapperRef },
            React.createElement("button", { "aria-haspopup": "true", className: "input-select__button qa-input-select-dropdown_button", onClick: this.handleButtonClick, onKeyUp: this.handleButtonKeyUp, ref: this.buttonRef, type: "button" },
                React.createElement("div", { className: "input-select__button-text" }, this.renderButtonText()),
                React.createElement("div", { className: "input-select__button-icon" },
                    React.createElement(ChevronDownSvg, null))),
            React.createElement("input", { className: [
                    'input-select__input-cache',
                    'qa-input-select-dropdown_input-cache',
                    this.state.inputCache && 'has-value',
                ]
                    .filter(Boolean)
                    .join(' '), onChange: this.handleInputCacheChange, onKeyUp: this.handleDropdownKeyUp, ref: this.inputCacheRef, title: "Search " + this.props.label + " filter options", type: "text", value: this.state.inputCache }),
            Boolean(this.getSelectedCount()) &&
                React.createElement("button", { className: "input-select__clear-button qa-input-select-dropdown_clear-button", onClick: this.handleClearClicked, title: "Clear " + this.props.label, type: "button" },
                    React.createElement(XIconSvg, null)),
            React.createElement("div", { "aria-hidden": !this.state.isDropdownOpen, className: "input-select__dropdown-wrapper", ref: this.dropdownWrapperRef },
                React.createElement("ul", { className: "input-select__dropdown", onKeyUp: this.handleDropdownKeyUp, ref: this.dropdownRef, role: "listbox" }, this.props.options.map(function (_a) {
                    var avatar = _a.avatar, disableIcon = _a.disableIcon, label = _a.label, value = _a.value;
                    return React.createElement("li", { "aria-selected": _this.isValueSelected(value), key: _this.getInputId(value), className: [
                            'input-select__dropdown-option',
                            _this.isValueSelected(value) && 'is-selected',
                            !_this.doesInputCacheMatchText(label || value) && 'is-filtered-out',
                        ]
                            .filter(Boolean)
                            .join(' '), role: "option" },
                        React.createElement(InputCheckbox, { checked: _this.isValueSelected(value), className: "input-select__dropdown-option-input-checkbox", disableIcon: disableIcon, icon: avatar && AvatarIcon, iconProps: {
                                avatar: avatar,
                                className: 'input-select__dropdown-option-input-checkbox-avatar',
                            }, label: label || value, onChange: _this.handleOptionChange.bind(_this, value), tabIndex: _this.state.isDropdownOpen ? 0 : -1 }));
                })))));
    };
    InputSelectDropdown.propTypes = {
        className: PropTypes.string,
        id: PropTypes.string.isRequired,
        isGrid: PropTypes.bool,
        isMulti: PropTypes.bool,
        label: PropTypes.node.isRequired,
        onChange: PropTypes.func.isRequired,
        options: PropTypes.arrayOf(PropTypes.shape({
            avatar: PropTypes.string,
            disableIcon: PropTypes.bool,
            label: PropTypes.string,
            value: PropTypes.oneOfType([
                PropTypes.number,
                PropTypes.string,
            ]).isRequired,
        })).isRequired,
        value: PropTypes.oneOfType([
            PropTypes.number,
            PropTypes.string,
            PropTypes.arrayOf(PropTypes.oneOfType([
                PropTypes.number,
                PropTypes.string,
            ])).isRequired,
        ]),
    };
    InputSelectDropdown.defaultProps = {
        className: '',
        isGrid: false,
    };
    return InputSelectDropdown;
}(PureComponent));
export default InputSelectDropdown;
//# sourceMappingURL=InputSelectDropdown.js.map