/*
 * Copyright 2017 Palantir Technologies, Inc. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
import classNames from "classnames";
import * as React from "react";
import { AbstractPureComponent2, DISPLAYNAME_PREFIX, InputGroup, Keys, Popover, PopoverInteractionKind, Position, refHandler, setRef, } from "@blueprintjs/core";
import { Classes } from "../../common";
import { QueryList } from "../query-list/queryList";
export class Suggest extends AbstractPureComponent2 {
    constructor() {
        super(...arguments);
        this.state = {
            isOpen: (this.props.popoverProps != null && this.props.popoverProps.isOpen) || false,
            selectedItem: this.getInitialSelectedItem(),
        };
        this.TypedQueryList = QueryList.ofType();
        this.inputElement = null;
        this.queryList = null;
        this.handleInputRef = refHandler(this, "inputElement", this.props.inputProps?.inputRef);
        this.handleQueryListRef = (ref) => (this.queryList = ref);
        this.renderQueryList = (listProps) => {
            const { fill, inputProps = {}, popoverProps = {} } = this.props;
            const { isOpen, selectedItem } = this.state;
            const { handleKeyDown, handleKeyUp } = listProps;
            const { autoComplete = "off", placeholder = "Search..." } = inputProps;
            const selectedItemText = selectedItem ? this.props.inputValueRenderer(selectedItem) : "";
            // placeholder shows selected item while open.
            const inputPlaceholder = isOpen && selectedItemText ? selectedItemText : placeholder;
            // value shows query when open, and query remains when closed if nothing is selected.
            // if resetOnClose is enabled, then hide query when not open. (see handlePopoverOpening)
            const inputValue = isOpen
                ? listProps.query
                : selectedItemText || (this.props.resetOnClose ? "" : listProps.query);
            if (fill) {
                popoverProps.fill = true;
                inputProps.fill = true;
            }
            return (
            /* eslint-disable-next-line deprecation/deprecation */
            React.createElement(Popover, Object.assign({ autoFocus: false, enforceFocus: false, isOpen: isOpen, position: Position.BOTTOM_LEFT }, popoverProps, { className: classNames(listProps.className, popoverProps.className), interactionKind: PopoverInteractionKind.CLICK, onInteraction: this.handlePopoverInteraction, popoverClassName: classNames(Classes.SELECT_POPOVER, popoverProps.popoverClassName), onOpening: this.handlePopoverOpening, onOpened: this.handlePopoverOpened }),
                React.createElement(InputGroup, Object.assign({ autoComplete: autoComplete, disabled: this.props.disabled }, inputProps, { inputRef: this.handleInputRef, onChange: listProps.handleQueryChange, onFocus: this.handleInputFocus, onKeyDown: this.getTargetKeyDownHandler(handleKeyDown), onKeyUp: this.getTargetKeyUpHandler(handleKeyUp), placeholder: inputPlaceholder, value: inputValue })),
                React.createElement("div", { onKeyDown: handleKeyDown, onKeyUp: handleKeyUp }, listProps.itemList)));
        };
        this.selectText = () => {
            // wait until the input is properly focused to select the text inside of it
            this.requestAnimationFrame(() => {
                this.inputElement?.setSelectionRange(0, this.inputElement.value.length);
            });
        };
        this.handleInputFocus = (event) => {
            this.selectText();
            // TODO can we leverage Popover.openOnTargetFocus for this?
            if (!this.props.openOnKeyDown) {
                this.setState({ isOpen: true });
            }
            this.props.inputProps?.onFocus?.(event);
        };
        this.handleItemSelect = (item, event) => {
            let nextOpenState;
            if (!this.props.closeOnSelect) {
                this.inputElement?.focus();
                this.selectText();
                nextOpenState = true;
            }
            else {
                this.inputElement?.blur();
                nextOpenState = false;
            }
            // the internal state should only change when uncontrolled.
            if (this.props.selectedItem === undefined) {
                this.setState({
                    isOpen: nextOpenState,
                    selectedItem: item,
                });
            }
            else {
                // otherwise just set the next open state.
                this.setState({ isOpen: nextOpenState });
            }
            this.props.onItemSelect?.(item, event);
        };
        // Popover interaction kind is CLICK, so this only handles click events.
        // Note that we defer to the next animation frame in order to get the latest document.activeElement
        this.handlePopoverInteraction = (nextOpenState, event) => this.requestAnimationFrame(() => {
            const isInputFocused = this.inputElement === document.activeElement;
            if (this.inputElement != null && !isInputFocused) {
                // the input is no longer focused, we should close the popover
                this.setState({ isOpen: false });
            }
            this.props.popoverProps?.onInteraction?.(nextOpenState, event);
        });
        this.handlePopoverOpening = (node) => {
            // reset query before opening instead of when closing to prevent flash of unfiltered items.
            // this is a limitation of the interactions between QueryList state and Popover transitions.
            if (this.props.resetOnClose && this.queryList) {
                this.queryList.setQuery("", true);
            }
            this.props.popoverProps?.onOpening?.(node);
        };
        this.handlePopoverOpened = (node) => {
            // scroll active item into view after popover transition completes and all dimensions are stable.
            if (this.queryList != null) {
                this.queryList.scrollActiveItemIntoView();
            }
            this.props.popoverProps?.onOpened?.(node);
        };
        this.getTargetKeyDownHandler = (handleQueryListKeyDown) => {
            return (evt) => {
                // HACKHACK: https://github.com/palantir/blueprint/issues/4165
                // eslint-disable-next-line deprecation/deprecation
                const { which } = evt;
                if (which === Keys.ESCAPE || which === Keys.TAB) {
                    this.inputElement?.blur();
                    this.setState({ isOpen: false });
                }
                else if (this.props.openOnKeyDown &&
                    which !== Keys.BACKSPACE &&
                    which !== Keys.ARROW_LEFT &&
                    which !== Keys.ARROW_RIGHT) {
                    this.setState({ isOpen: true });
                }
                if (this.state.isOpen) {
                    handleQueryListKeyDown?.(evt);
                }
                this.props.inputProps?.onKeyDown?.(evt);
            };
        };
        this.getTargetKeyUpHandler = (handleQueryListKeyUp) => {
            return (evt) => {
                if (this.state.isOpen) {
                    handleQueryListKeyUp?.(evt);
                }
                this.props.inputProps?.onKeyUp?.(evt);
            };
        };
    }
    static ofType() {
        return Suggest;
    }
    render() {
        // omit props specific to this component, spread the rest.
        const { disabled, inputProps, popoverProps, ...restProps } = this.props;
        return (React.createElement(this.TypedQueryList, Object.assign({}, restProps, { initialActiveItem: this.props.selectedItem ?? undefined, onItemSelect: this.handleItemSelect, ref: this.handleQueryListRef, renderer: this.renderQueryList })));
    }
    componentDidUpdate(prevProps, prevState) {
        if (prevProps.inputProps?.inputRef !== this.props.inputProps?.inputRef) {
            setRef(prevProps.inputProps?.inputRef, null);
            this.handleInputRef = refHandler(this, "inputElement", this.props.inputProps?.inputRef);
            setRef(this.props.inputProps?.inputRef, this.inputElement);
        }
        // If the selected item prop changes, update the underlying state.
        if (this.props.selectedItem !== undefined && this.props.selectedItem !== this.state.selectedItem) {
            this.setState({ selectedItem: this.props.selectedItem });
        }
        if (this.state.isOpen === false && prevState.isOpen === true) {
            // just closed, likely by keyboard interaction
            // wait until the transition ends so there isn't a flash of content in the popover
            /* eslint-disable-next-line deprecation/deprecation */
            const timeout = this.props.popoverProps?.transitionDuration ?? Popover.defaultProps.transitionDuration;
            setTimeout(() => this.maybeResetActiveItemToSelectedItem(), timeout);
        }
        if (this.state.isOpen && !prevState.isOpen && this.queryList != null) {
            this.queryList.scrollActiveItemIntoView();
        }
    }
    getInitialSelectedItem() {
        // controlled > uncontrolled > default
        if (this.props.selectedItem !== undefined) {
            return this.props.selectedItem;
        }
        else if (this.props.defaultSelectedItem !== undefined) {
            return this.props.defaultSelectedItem;
        }
        else {
            return null;
        }
    }
    maybeResetActiveItemToSelectedItem() {
        const shouldResetActiveItemToSelectedItem = this.props.activeItem === undefined && this.state.selectedItem !== null && !this.props.resetOnSelect;
        if (this.queryList !== null && shouldResetActiveItemToSelectedItem) {
            this.queryList.setActiveItem(this.props.selectedItem ?? this.state.selectedItem);
        }
    }
}
Suggest.displayName = `${DISPLAYNAME_PREFIX}.Suggest`;
Suggest.defaultProps = {
    closeOnSelect: true,
    fill: false,
    openOnKeyDown: false,
    resetOnClose: false,
};
//# sourceMappingURL=suggest.js.map