import React, { Component, createRef, Fragment } from 'react';
import classNames from 'classnames';
import { SoftSelectableSearch } from './selectable_search';
import { SoftSelectableMessage } from './selectable_message';
import { SoftSelectableList } from './selectable_list';
import { SoftLoadingChart } from '../Loading';
import { getMatchingOptions } from './matching_options';
import { comboBoxKeyCodes } from '../../services';
import { TAB } from '../../services/key_codes';
import { SoftI18n } from '../I18n';
export class SoftSelectable extends Component {
    constructor(props) {
        super(props);
        this.state = {
            activeOptionIndex: null,
            searchValue: null,
            visibleOptions: null,
            onKeyDown: null
        };
        this.hasActiveOption = () => {
            return this.state.activeOptionIndex != null;
        };
        this.onKeyDown = (e) => {
            const optionsList = this.optionsListRef.current;
            switch (e.keyCode) {
                case comboBoxKeyCodes.UP:
                    e.preventDefault();
                    e.stopPropagation();
                    this.incrementActiveOptionIndex(-1);
                    break;
                case comboBoxKeyCodes.DOWN:
                    e.preventDefault();
                    e.stopPropagation();
                    this.incrementActiveOptionIndex(1);
                    break;
                case comboBoxKeyCodes.ENTER:
                    e.stopPropagation();
                    if (this.state.activeOptionIndex != null && optionsList) {
                        optionsList.onAddOrRemoveOption(this.state.visibleOptions[this.state.activeOptionIndex]);
                    }
                    break;
                case TAB:
                    // Disallow tabbing when the user is navigating the options.
                    // TODO: Can we force the tab to the next sibling element?
                    if (this.hasActiveOption()) {
                        e.preventDefault();
                        e.stopPropagation();
                    }
                    break;
                default:
                    if (this.props.onKeyDown) {
                        this.props.onKeyDown(e);
                    }
                    this.clearActiveOption();
            }
        };
        this.incrementActiveOptionIndex = (amount) => {
            // If there are no options available, do nothing.
            if (!this.state.visibleOptions.length) {
                return;
            }
            // @ts-ignore
            this.setState(({ activeOptionIndex, visibleOptions }) => {
                let nextActiveOptionIndex;
                if (activeOptionIndex == null) {
                    // If this is the beginning of the user's keyboard navigation of the menu, then we'll focus
                    // either the first or last item.
                    nextActiveOptionIndex = amount < 0 ? visibleOptions.length - 1 : 0;
                }
                else {
                    nextActiveOptionIndex = activeOptionIndex + amount;
                    if (nextActiveOptionIndex < 0) {
                        nextActiveOptionIndex = visibleOptions.length - 1;
                    }
                    else if (nextActiveOptionIndex === visibleOptions.length) {
                        nextActiveOptionIndex = 0;
                    }
                }
                // Group titles are included in option list but are not selectable
                // Skip group title options
                const direction = amount > 0 ? 1 : -1;
                while (visibleOptions[nextActiveOptionIndex].isGroupLabel) {
                    nextActiveOptionIndex = nextActiveOptionIndex + direction;
                    if (nextActiveOptionIndex < 0) {
                        nextActiveOptionIndex = visibleOptions.length - 1;
                    }
                    else if (nextActiveOptionIndex === visibleOptions.length) {
                        nextActiveOptionIndex = 0;
                    }
                }
                return { activeOptionIndex: nextActiveOptionIndex };
            });
        };
        this.clearActiveOption = () => {
            this.setState({
                activeOptionIndex: undefined,
            });
        };
        this.onSearchChange = (visibleOptions, searchValue) => {
            this.setState({
                visibleOptions,
                searchValue,
            });
        };
        this.onContainerBlur = () => {
            this.clearActiveOption();
        };
        this.onOptionClick = (options) => {
            this.setState((state) => ({
                visibleOptions: getMatchingOptions(options, state.searchValue),
            }));
            if (this.props.onChange) {
                this.props.onChange(options);
            }
        };
        this.optionsListRef = createRef();
        const { options, singleSelection } = props;
        const initialSearchValue = '';
        // @ts-ignore
        const visibleOptions = getMatchingOptions(options, initialSearchValue);
        // ensure that the currently selected single option is active if it is in the visibleOptions
        const selectedOptions = options.filter(option => option.checked);
        let activeOptionIndex;
        if (singleSelection && selectedOptions.length === 1) {
            if (visibleOptions.includes(selectedOptions[0])) {
                activeOptionIndex = visibleOptions.indexOf(selectedOptions[0]);
            }
        }
        this.state = {
            activeOptionIndex,
            searchValue: initialSearchValue,
            visibleOptions,
            onKeyDown: null
        };
    }
    render() {
        const { id, children, className, options, onChange, searchable, searchProps, singleSelection, isLoading, listProps, renderOption, height, allowExclusions, ...rest } = this.props;
        const { searchValue, visibleOptions, activeOptionIndex } = this.state;
        let messageContent;
        if (isLoading) {
            messageContent = (React.createElement(Fragment, null,
                React.createElement(SoftLoadingChart, { size: "m", mono: true }),
                React.createElement("br", null),
                React.createElement("p", null,
                    React.createElement(SoftI18n, { token: "euiSelectable.loadingOptions", default: "Loading options" }))));
        }
        else if (searchValue && visibleOptions.length === 0) {
            messageContent = (React.createElement("p", null,
                React.createElement(SoftI18n, { token: "euiSelectable.noMatchingOptions", default: "{searchValue} doesn't match any options", values: { searchValue: React.createElement("strong", null, searchValue) } })));
        }
        else if (!options.length) {
            messageContent = (React.createElement("p", null,
                React.createElement(SoftI18n, { token: "euiSelectable.noAvailableOptions", default: "No options available" })));
        }
        const classes = classNames('euiSelectable', {
            'euiSelectable-fullHeight': height === 'full',
        }, className);
        const search = searchable ? (React.createElement(SoftSelectableSearch, Object.assign({ key: "listSearch", options: options, onChange: this.onSearchChange }, searchProps))) : (undefined);
        const list = messageContent ? (React.createElement(SoftSelectableMessage, { key: "listMessage" }, messageContent)) : (React.createElement(SoftSelectableList, Object.assign({ key: "list", options: options, visibleOptions: visibleOptions, searchValue: searchValue, activeOptionIndex: activeOptionIndex, onOptionClick: this.onOptionClick, singleSelection: singleSelection, ref: this.optionsListRef, renderOption: renderOption, height: height, allowExclusions: allowExclusions }, listProps)));
        return (React.createElement("div", Object.assign({ className: classes, onKeyDown: this.onKeyDown, onBlur: this.onContainerBlur }, rest), children && children(list, search)));
    }
}
SoftSelectable.defaultProps = {
    options: [],
    singleSelection: false,
};
//# sourceMappingURL=SoftSelectable.js.map