"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
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;
};
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
var __rest = (this && this.__rest) || function (s, e) {
    var t = {};
    for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
        t[p] = s[p];
    if (s != null && typeof Object.getOwnPropertySymbols === "function")
        for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
            if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
                t[p[i]] = s[p[i]];
        }
    return t;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.Autosuggest = void 0;
exports.renderTaxonIcons = renderTaxonIcons;
const React = __importStar(require("react"));
const react_dom_1 = require("react-dom");
const PropTypes = __importStar(require("prop-types"));
const react_spinner_1 = __importDefault(require("react-spinner"));
const utils_1 = require("../../utils");
const components_1 = require("../components");
const ReactContext_1 = __importDefault(require("../../ReactContext"));
const InformalTaxonGroupChooserWidget_1 = require("./InformalTaxonGroupChooserWidget");
const utils_2 = require("@rjsf/utils");
function renderTaxonIcons(suggestion, prepend) {
    const { finnish, checklist } = suggestion || {};
    const flagIcon = finnish ? React.createElement("img", { src: "https://cdn.laji.fi/images/icons/flag_fi_small.png", width: "16", className: "flag-img" }) : null;
    const gbifIcon = checklist === "MR.2" ? React.createElement("img", { src: "https://cdn.laji.fi/images/icons/gbif.svg", width: "18" }) : null;
    return flagIcon || gbifIcon
        ? React.createElement(React.Fragment, null,
            prepend || null,
            flagIcon,
            gbifIcon)
        : null;
}
class _AutosuggestWidget extends React.Component {
    render() {
        if (this.props.options) {
            switch (this.props.options.autosuggestField) {
                case "taxa":
                case "taxon":
                    return React.createElement(TaxonAutosuggestWidget, Object.assign({}, this.props, { options: Object.assign(Object.assign({}, this.props.options), { autosuggestField: "taxa" }) }));
                case "unit":
                    return React.createElement(UnitAutosuggestWidget, Object.assign({}, this.props, { basePath: "/shorthand/unit/trip-report" }));
                case "person":
                    return React.createElement(FriendsAutosuggestWidget, Object.assign({}, this.props, { options: Object.assign(Object.assign({}, this.props.options), { autosuggestField: "persons" }) }));
                case "friends":
                case "persons":
                    return React.createElement(FriendsAutosuggestWidget, Object.assign({}, this.props));
                case "organization":
                    return React.createElement(OrganizationAutosuggestWidget, Object.assign({}, this.props));
                case "collection":
                    return React.createElement(CollectionAutosuggestWidget, Object.assign({}, this.props));
                case "dataset":
                    return React.createElement(DatasetAutosuggestWidget, Object.assign({}, this.props));
                default:
                    return React.createElement(RangeAutosuggestWidget, Object.assign({}, this.props));
            }
        }
        return React.createElement(Autosuggest, Object.assign({}, this.props));
    }
    formatValue(value, options, parentProps) {
        if (options) {
            let component = undefined;
            switch (options.autosuggestField) {
                case "taxon":
                case "taxa":
                    component = TaxonAutosuggestWidget;
                    break;
                case "unit":
                    component = UnitAutosuggestWidget;
                    break;
                case "friends":
                case "person":
                case "persons":
                    component = FriendsAutosuggestWidget;
                    break;
                case "organization":
                    component = OrganizationAutosuggestWidget;
                    break;
                case "collection":
                    component = CollectionAutosuggestWidget;
                    break;
                case "dataset":
                    component = DatasetAutosuggestWidget;
                    break;
                default:
                    component = RangeAutosuggestWidget;
            }
            if (component) {
                return React.createElement(SimpleValueRenderer, { component: component, value: value, formContext: parentProps.formContext });
            }
        }
    }
}
_AutosuggestWidget.propTypes = {
    schema: PropTypes.shape({
        type: PropTypes.oneOf(["string"])
    }).isRequired,
    value: PropTypes.string
};
exports.default = _AutosuggestWidget;
class SimpleValueRenderer extends React.Component {
    constructor(props) {
        super(props);
        this.state = { inputValue: this.props.value, loading: true };
    }
    componentDidMount() {
        const { getSuggestionFromValue, isValueSuggested } = this.props.component.prototype;
        if (getSuggestionFromValue && isValueSuggested) {
            this.isValueSuggested = isValueSuggested.bind(this);
            getSuggestionFromValue.call(this, this.props.value).then((suggestion) => {
                this.setState({ inputValue: suggestion.value, loading: false });
            }).catch(() => this.setState({ loading: false }));
        }
    }
    render() {
        return this.state.loading ? React.createElement(react_spinner_1.default, null) : React.createElement("span", null, this.state.inputValue);
    }
}
function TaxonAutosuggest(ComposedComponent) {
    var _a;
    return _a = class TaxonAutosuggestWidget extends ComposedComponent {
            constructor(props) {
                super(props);
                this.renderSuccessGlyph = () => {
                    const { Glyphicon } = this.context.theme;
                    return React.createElement(Glyphicon, { style: { pointerEvents: "none" }, glyph: "ok", className: "form-control-feedback" });
                };
                this.parseChooseImages = (chooseImages) => {
                    return chooseImages.map(taxonIDOrObj => typeof taxonIDOrObj === "string" ? { id: taxonIDOrObj } : taxonIDOrObj);
                };
                this.renderChooseImages = () => {
                    const chooseImages = this.state && this.state.chooseImages || this.props.options.chooseImages;
                    const { orWriteSpeciesNameLabel } = (0, utils_1.getUiOptions)(this.props);
                    const { or } = this.props.formContext.translations;
                    const { Row, Col } = this.context.theme;
                    return (React.createElement(Row, null,
                        React.createElement(Col, { xs: 12 },
                            React.createElement("div", { className: "laji-form-medias" }, this.parseChooseImages(chooseImages).map(taxonIDObj => React.createElement(TaxonImgChooser, { id: taxonIDObj.id, key: taxonIDObj.id, url: taxonIDObj.url, onSelect: this.onTaxonImgSelected, formContext: this.props.formContext })))),
                        React.createElement(Col, { xs: 12 },
                            React.createElement("label", null,
                                or,
                                " ",
                                orWriteSpeciesNameLabel || this.props.formContext.translations.orWriteSpeciesName))));
                };
                this.onTaxonImgSelected = (taxonID, taxon) => {
                    this.autosuggestRef.selectSuggestion(Object.assign({ key: taxonID, value: taxon.vernacularName }, taxon));
                };
                this.setAutosuggestRef = (elem) => {
                    this.autosuggestRef = elem;
                };
                this.getSuggestionFromValue = this.getSuggestionFromValue.bind(this);
                this.isValueSuggested = this.isValueSuggested.bind(this);
                if (super.renderSuggestion) {
                    this.renderSuggestion = super.renderSuggestion.bind(this);
                }
                else {
                    this.renderSuggestion = this.renderSuggestion.bind(this);
                }
            }
            componentDidMount() {
                this.mounted = true;
            }
            componentWillUnmount() {
                this.mounted = false;
            }
            componentDidUpdate(prevProps) {
                const { value, options = {} } = this.props;
                if (value !== prevProps.value && options.getSuggestionFromValue && options.chooseImages) {
                    options.getSuggestionFromValue(value).then(suggestion => {
                        if (!this.mounted)
                            return;
                        const found = this.parseChooseImages(options.chooseImages).find(({ id }) => suggestion.key === id);
                        this.setState({ chooseImages: found ? [found] : undefined });
                    });
                }
            }
            getSuggestionFromValue(value) {
                return __awaiter(this, void 0, void 0, function* () {
                    if (this.isValueSuggested(value)) {
                        const { vernacularName, scientificName } = yield this.props.formContext.apiClient.get(`/taxa/${value}`);
                        if (vernacularName !== undefined) {
                            return { value: vernacularName, key: value };
                        }
                        if (scientificName !== undefined) {
                            return { value: scientificName, key: value };
                        }
                    }
                    else {
                        throw new Error("Unknown taxon");
                    }
                });
            }
            isValueSuggested(value) {
                return !(0, utils_1.isEmptyString)(value) && !!value.match(/MX\.\d+/);
            }
            parseInputValue(inputValue) {
                return inputValue.replace(/ sp(\.|p)?\.?$/, "");
            }
            renderSuggestion(suggestion) {
                const renderedSuggestion = "autocompleteDisplayName" in suggestion
                    && React.createElement("span", { dangerouslySetInnerHTML: { __html: suggestion.autocompleteDisplayName } })
                    || React.createElement(React.Fragment, null,
                        suggestion.value,
                        renderTaxonIcons(suggestion));
                return React.createElement("span", { className: "simple-option" }, renderedSuggestion);
            }
            render() {
                const { props } = this;
                const { options: propsOptions } = props, propsWithoutOptions = __rest(props, ["options"]);
                const options = {
                    getSuggestionFromValue: this.getSuggestionFromValue,
                    isValueSuggested: this.isValueSuggested,
                    Wrapper: TaxonWrapper,
                    renderSuccessGlyph: this.renderSuccessGlyph,
                    renderSuggestion: this.renderSuggestion,
                    parseInputValue: this.parseInputValue
                };
                const _options = Object.assign(Object.assign(Object.assign(Object.assign({}, options), propsWithoutOptions), (propsOptions || {})), { query: Object.assign(Object.assign(Object.assign({}, (options.query || {})), (propsWithoutOptions.query || {})), (propsOptions.query || {})) });
                if (propsOptions.chooseImages && !(props.value && props.formContext.uiSchemaContext.isEdit)) {
                    _options.renderExtra = this.renderChooseImages;
                }
                return React.createElement(Autosuggest, Object.assign({ ref: this.setAutosuggestRef }, _options));
            }
        },
        _a.contextType = ReactContext_1.default,
        _a;
}
let TaxonAutosuggestWidget = class TaxonAutosuggestWidget extends React.Component {
};
TaxonAutosuggestWidget = __decorate([
    TaxonAutosuggest
], TaxonAutosuggestWidget);
let UnitAutosuggestWidget = class UnitAutosuggestWidget extends React.Component {
    constructor(props) {
        super(props);
        this.renderSuggestion = this.renderSuggestion.bind(this);
    }
    renderSuggestion(suggestion) {
        const { count, maleIndividualCount, femaleIndividualCount } = suggestion.interpretedFrom;
        const [countElem, maleElem, femaleElem] = [count, maleIndividualCount, femaleIndividualCount].map(val => val && React.createElement("span", { className: "text-muted" }, val));
        const taxonName = suggestion.unit.identifications[0].taxon;
        const name = suggestion.isNonMatching
            ? React.createElement("span", { className: "text-muted" },
                taxonName,
                " ",
                React.createElement("i", null,
                    "(",
                    this.props.formContext.translations.unknownSpeciesName,
                    ")"))
            : taxonName;
        return React.createElement("span", null,
            countElem,
            countElem && " ",
            name,
            maleElem && " ",
            maleElem,
            femaleElem && " ",
            femaleElem,
            renderTaxonIcons(suggestion));
    }
};
UnitAutosuggestWidget = __decorate([
    TaxonAutosuggest
], UnitAutosuggestWidget);
class FriendsAutosuggestWidget extends React.Component {
    constructor(props) {
        super(props);
        this.prepareSuggestion = (suggestion) => {
            const { isAdmin } = this.props.formContext.uiSchemaContext;
            const { showID } = (0, utils_1.getUiOptions)(this.props);
            return isAdmin && showID
                ? Object.assign(Object.assign({}, suggestion), { value: `${suggestion.value} (${suggestion.key})` }) : suggestion;
        };
        this.findExactMatch = (suggestions, inputValue) => {
            return suggestions.find(suggestion => (suggestion && suggestion.value.toLowerCase() === inputValue.trim().toLowerCase()));
        };
        this.renderSuccessGlyph = () => {
            const { Glyphicon } = this.context.theme;
            return (React.createElement(Glyphicon, { style: { pointerEvents: "none" }, glyph: "user", className: "form-control-feedback" }));
        };
        this.getSuggestionFromValue = this.getSuggestionFromValue.bind(this);
        this.isValueSuggested = this.isValueSuggested.bind(this);
    }
    getSuggestionFromValue(value) {
        return __awaiter(this, void 0, void 0, function* () {
            const { showID } = (0, utils_1.getUiOptions)(this.props);
            const { isAdmin } = this.props.formContext.uiSchemaContext;
            if (this.isValueSuggested(value)) {
                const { fullName, group, id } = yield this.props.formContext.apiClient.get(`/person/${value}`);
                if (fullName) {
                    const addGroup = str => group ? `${str} (${group})` : str;
                    const addID = str => isAdmin && showID ? `${str} (${id})` : str;
                    return {
                        value: addID(addGroup(fullName)),
                        key: value
                    };
                }
            }
            else {
                throw new Error("Unknown friend");
            }
        });
    }
    isValueSuggested(value) {
        return !(0, utils_1.isEmptyString)(value) && value.match(/MA\.\d+/);
    }
    render() {
        const _a = this.props, { options: propsOptions } = _a, propsWithoutOptions = __rest(_a, ["options"]);
        const options = {
            query: Object.assign({ includeSelf: true }, propsOptions.queryOptions),
            getSuggestionFromValue: this.getSuggestionFromValue,
            isValueSuggested: this.isValueSuggested,
            Wrapper: FriendsWrapper,
            renderSuccessGlyph: this.renderSuccessGlyph,
            findExactMatch: this.findExactMatch,
            prepareSuggestion: this.prepareSuggestion
        };
        return React.createElement(Autosuggest, Object.assign({}, options, propsWithoutOptions, propsOptions));
    }
}
FriendsAutosuggestWidget.contextType = ReactContext_1.default;
class OrganizationAutosuggestWidget extends React.Component {
    render() {
        return (React.createElement(BasicAutosuggestWidget, Object.assign({}, this.props, this.props.options, { nameField: "fullName", validValueRegexp: "MOS.\\d+", cache: false, Wrapper: OrganizationWrapper })));
    }
}
OrganizationAutosuggestWidget.contextType = ReactContext_1.default;
class CollectionAutosuggestWidget extends React.Component {
    render() {
        return (React.createElement(BasicAutosuggestWidget, Object.assign({}, this.props, this.props.options, { nameField: "collectionName", validValueRegexp: "HR.\\d+", cache: false, Wrapper: CollectionWrapper })));
    }
}
CollectionAutosuggestWidget.contextType = ReactContext_1.default;
class DatasetAutosuggestWidget extends React.Component {
    render() {
        return (React.createElement(BasicAutosuggestWidget, Object.assign({}, this.props, this.props.options, { nameField: "datasetName", validValueRegexp: "GX.\\d+", cache: false, Wrapper: DatasetWrapper })));
    }
}
DatasetAutosuggestWidget.contextType = ReactContext_1.default;
class BasicAutosuggestWidget extends React.Component {
    constructor(props) {
        super(props);
        this.findExactMatch = (suggestions, inputValue) => {
            return suggestions.find(suggestion => (suggestion && suggestion.value.toLowerCase() === inputValue.trim().toLowerCase()));
        };
        this.getSuggestionFromValue = this.getSuggestionFromValue.bind(this);
        this.isValueSuggested = this.isValueSuggested.bind(this);
    }
    getSuggestionFromValue(value) {
        return __awaiter(this, void 0, void 0, function* () {
            const { autosuggestField, nameField } = this.props;
            if (this.isValueSuggested(value)) {
                const result = yield this.props.formContext.apiClient.get(`/${autosuggestField}/by-id/${value}`, undefined, this.props.cache);
                if (result[nameField]) {
                    return {
                        value: result[nameField],
                        key: value
                    };
                }
            }
            else {
                throw new Error("Not autosuggested value");
            }
        });
    }
    isValueSuggested(value) {
        const regexp = new RegExp(this.props.validValueRegexp);
        return !(0, utils_1.isEmptyString)(value) && value.match(regexp);
    }
    render() {
        const options = {
            query: Object.assign({ includeSelf: true }, this.props.queryOptions),
            getSuggestionFromValue: this.getSuggestionFromValue,
            isValueSuggested: this.isValueSuggested,
            Wrapper: this.props.Wrapper,
            findExactMatch: this.findExactMatch
        };
        return React.createElement(Autosuggest, Object.assign({}, options, this.props));
    }
}
BasicAutosuggestWidget.contextType = ReactContext_1.default;
BasicAutosuggestWidget.propTypes = {
    autosuggestField: PropTypes.string.isRequired,
    allowNonsuggestedValue: PropTypes.bool,
    onSuggestionSelected: PropTypes.func,
    onUnsuggestedSelected: PropTypes.func,
    onInputChange: PropTypes.func,
    uiSchema: PropTypes.object,
    nameField: PropTypes.string,
    validValueRegexp: PropTypes.string,
    cache: PropTypes.bool,
    Wrapper: PropTypes.object
};
BasicAutosuggestWidget.defaultProps = {
    nameField: "name",
    validValueRegexp: "",
    cache: true
};
class RangeAutosuggestWidget extends React.Component {
    render() {
        const _a = this.props, { options: propsOptions } = _a, propsWithoutOptions = __rest(_a, ["options"]);
        return React.createElement(Autosuggest, Object.assign({ highlightFirstSuggestion: true }, propsWithoutOptions, propsOptions));
    }
}
class Autosuggest extends React.Component {
    constructor(props) {
        super(props);
        this.isValueSuggested = (props) => {
            const { isValueSuggested, suggestionReceive, onSuggestionSelected } = props;
            if (!onSuggestionSelected && suggestionReceive !== "key")
                return undefined;
            return isValueSuggested ? isValueSuggested(props.value, props.valueContext) : undefined;
        };
        this.wrapperRef = React.createRef();
        this.keyFunctions = {
            autosuggestToggle: () => {
                if (this.props.onToggle) {
                    this.onToggle();
                    return true;
                }
                return false;
            }
        };
        this.triggerConvert = (props) => {
            const { value, getSuggestionFromValue, valueContext } = props;
            if ((0, utils_1.isEmptyString)(value) || !getSuggestionFromValue) {
                if (this.state.suggestion && Object.keys(this.state.suggestion).length > 0) {
                    this.setState({ suggestion: undefined, inputValue: value, suggestionForValue: value });
                }
                return;
            }
            this.setState({ isLoading: true });
            getSuggestionFromValue(value, valueContext).then(suggestion => {
                if (!this.mounted)
                    return;
                this.setState({ suggestion, inputValue: this.getSuggestionValue(suggestion), isLoading: false, suggestionForValue: value });
            }).catch(() => {
                if (!this.mounted)
                    return;
                this.setState({ isLoading: false });
            });
        };
        this.getSuggestionValue = (suggestion) => {
            const { getSuggestionValue } = this.props;
            const def = suggestion.value;
            return getSuggestionValue
                ? getSuggestionValue(suggestion, def)
                : def;
        };
        this.renderSuggestion = (suggestion, inputValue) => {
            let { renderSuggestion } = this.props;
            if (!renderSuggestion)
                renderSuggestion = suggestion => suggestion.value;
            return (React.createElement("span", null, renderSuggestion(suggestion, inputValue)));
        };
        this.selectSuggestion = (suggestion) => {
            const { onSuggestionSelected, onChange, suggestionReceive } = this.props;
            const afterStateChange = () => {
                if (onSuggestionSelected) {
                    onSuggestionSelected(suggestion, this.mounted);
                }
                else {
                    if (this.mounted) {
                        this.setState({ suggestionForValue: suggestion[suggestionReceive] }, () => onChange(suggestion[suggestionReceive]));
                    }
                    else {
                        onChange(suggestion[suggestionReceive]);
                    }
                }
            };
            const state = { suggestion, inputValue: this.getSuggestionValue(suggestion) };
            this.mounted
                ? this.setState(state, afterStateChange)
                : afterStateChange();
        };
        this.selectUnsuggested = (inputValue) => {
            if ((0, utils_1.isEmptyString)(inputValue) && (0, utils_1.isEmptyString)(this.props.value))
                return;
            const value = !(0, utils_1.isEmptyString)(inputValue) ? inputValue : undefined;
            const { onUnsuggestedSelected, onChange } = this.props;
            const afterStateChange = () => {
                onUnsuggestedSelected ?
                    onUnsuggestedSelected(value) :
                    onChange(value);
            };
            const state = { inputValue, suggestion: undefined, suggestionForValue: inputValue };
            this.mounted
                ? this.setState(state, afterStateChange)
                : afterStateChange();
        };
        this.onSuggestionSelected = (suggestion) => {
            this.selectSuggestion(suggestion);
        };
        this.onUnsuggestedSelected = (inputValue) => {
            this.selectUnsuggested(inputValue);
        };
        this.findExactMatch = (suggestions, inputValue = "") => {
            if (!Array.isArray(suggestions))
                suggestions = [suggestions];
            const { findExactMatch } = this.props;
            return findExactMatch
                ? findExactMatch(suggestions, inputValue)
                : suggestions.find(suggestion => (suggestion && suggestion.value.toLowerCase() === inputValue.trim().toLowerCase() && !suggestion.isNonMatching));
        };
        this.findTheOnlyOneMatch = (suggestions) => {
            if (!Array.isArray(suggestions))
                suggestions = [suggestions];
            const filtered = suggestions.filter(suggestion => !suggestion || !suggestion.isNonMatching);
            if (filtered.length === 1) {
                return filtered[0];
            }
        };
        this.findNonMatching = (suggestions) => {
            if (!Array.isArray(suggestions))
                suggestions = [suggestions];
            const filtered = suggestions.filter(suggestion => suggestion && suggestion.isNonMatching);
            if (filtered.length === 1) {
                return filtered[0];
            }
        };
        this.onInputChange = (e, reason, callback) => {
            let { value } = e.target;
            if (this.props.inputProps && this.props.inputProps.onChange) {
                e.persist && e.persist();
                value = this.props.inputProps.onChange(e, reason, callback);
            }
            this.setState({ inputValue: value }, callback);
        };
        this.onSuggestionsFetchRequested = ({ value }, debounce = true) => {
            if (value === undefined || value === null)
                value = "";
            value = this.props.parseInputValue ? this.props.parseInputValue(value) : value;
            if (value.length < (this.props.minFetchLength !== undefined ? this.props.minFetchLength : 2)) {
                this.setState({ suggestions: [] });
                return;
            }
            const { autosuggestField, query = {} } = this.props;
            this.setState({ isLoading: true });
            const request = () => __awaiter(this, void 0, void 0, function* () {
                let timestamp = Date.now();
                this.promiseTimestamp = timestamp;
                try {
                    let suggestionsResponse = yield this.apiClient.get(this.props.basePath || "/autocomplete" + "/" + autosuggestField, { query: Object.assign({ query: value, matchType: "exact,partial", includeHidden: false }, query) }, this.props.cache);
                    // Hack for laji-form in Kotka, since it uses API that isn't laji-api and might have different response signature.
                    let suggestions = Array.isArray(suggestionsResponse) ? suggestionsResponse : suggestionsResponse.results;
                    if (this.props.prepareSuggestion) {
                        suggestions = suggestions.map(s => this.props.prepareSuggestion(s));
                    }
                    if (timestamp !== this.promiseTimestamp) {
                        return;
                    }
                    this.mounted
                        ? this.setState({ isLoading: false, suggestions }, () => this.afterBlurAndFetch(suggestions))
                        : this.afterBlurAndFetch(suggestions);
                }
                catch (e) {
                    this.mounted
                        ? this.setState({ isLoading: false }, this.afterBlurAndFetch)
                        : this.afterBlurAndFetch();
                }
            });
            if (this.timeout) {
                clearTimeout(this.timeout);
            }
            if (debounce) {
                this.timeout = this.props.formContext.setTimeout(request, 100);
            }
            else {
                request();
            }
        };
        this.onFocus = (e) => {
            this.setState({ focused: true });
            (0, utils_1.triggerParentComponent)("onFocus", e, this.props.inputProps);
        };
        this.onBlur = (e, blurSelected = false) => {
            this.setState({ focused: false }, () => {
                if (!blurSelected) {
                    this._valueForBlurAndFetch = this.state.inputValue;
                    this.afterBlurAndFetch(this.state.suggestions);
                }
                (0, utils_1.triggerParentComponent)("onBlur", e, this.props.inputProps);
                const overlay = this.wrapperRef.current;
                if (overlay && overlay.overlayTriggerRef && overlay.overlayTriggerRef.hide) {
                    setTimeout(() => {
                        overlay && overlay.overlayTriggerRef && overlay.overlayTriggerRef.hide();
                    }, 1);
                }
            });
        };
        this.afterBlurAndFetch = (suggestions, callback) => {
            const { inputValue = "" } = this.state;
            const { _valueForBlurAndFetch = "" } = this;
            if (this.mounted
                && (this.state.focused || this._valueForBlurAndFetch === undefined || this.state.isLoading)
                || (this.props.controlledValue && _valueForBlurAndFetch !== this.state.inputValue)) {
                return;
            }
            const parsedInputValue = this.props.parseInputValue ? this.props.parseInputValue(inputValue) : inputValue;
            if (this.state.suggestion && inputValue === this.getSuggestionValue(this.state.suggestion)) {
                callback && callback();
                return;
            }
            const { selectOnlyOne, selectOnlyNonMatchingBeforeUnsuggested = true, informalTaxonGroups, informalTaxonGroupsValue, allowNonsuggestedValue } = this.props;
            const exactMatch = this.findExactMatch(suggestions, parsedInputValue);
            const onlyOneMatch = selectOnlyOne ? this.findTheOnlyOneMatch(suggestions) : undefined;
            const nonMatching = selectOnlyNonMatchingBeforeUnsuggested ? this.findNonMatching(suggestions) : undefined;
            const valueDidntChangeAndHasInformalTaxonGroup = this.props.value === inputValue && informalTaxonGroups && informalTaxonGroupsValue && informalTaxonGroupsValue.length;
            if (this.highlightedSuggestionOnBlur) {
                this.selectSuggestion(this.highlightedSuggestionOnBlur);
            }
            else if (onlyOneMatch) {
                this.selectSuggestion(onlyOneMatch);
            }
            else if (exactMatch) {
                this.selectSuggestion(Object.assign(Object.assign({}, exactMatch), { value: inputValue }));
            }
            else if (nonMatching && !valueDidntChangeAndHasInformalTaxonGroup) {
                this.selectSuggestion(nonMatching);
            }
            else if (!valueDidntChangeAndHasInformalTaxonGroup && allowNonsuggestedValue) {
                this.selectUnsuggested(parsedInputValue);
            }
            else if (!allowNonsuggestedValue) {
                this.setState({ inputValue: "" }, () => this.props.onChange && this.props.onChange(undefined));
            }
            callback && callback();
        };
        this.setRef = (elem) => {
            this.autosuggestRef = elem;
        };
        this.onInformalTaxonGroupsOpened = (open) => {
            this.setState({ informalTaxonGroupsOpen: open });
        };
        this.onInformalTaxonGroupSelected = (id) => {
            this.setState({ informalTaxonGroupsOpen: false });
            this.props.onInformalTaxonGroupSelected && this.props.onInformalTaxonGroupSelected(id);
        };
        this.onInformalTaxonGroupHide = () => {
            this.setState({ informalTaxonGroupsOpen: false });
        };
        this.onToggle = () => {
            if (!this.mounted)
                return;
            this.props.onToggle(!this.props.toggled);
            setTimeout(() => this.props.formContext.utils.focusById(this.props.id), 1); // Refocus input
        };
        this.onKeyDown = this.props.formContext.utils.keyboardClick(this.onToggle);
        this.isSuggested = () => {
            const { suggestion } = this.state;
            return !!suggestion && this.isValueSuggested(this.props);
        };
        this.renderInput = (inputProps) => {
            let { value, renderSuccessGlyph, informalTaxonGroups, renderInformalTaxonGroupSelector = true, taxonGroupID, onToggle, displayValidationState = true, Wrapper } = this.props;
            let validationState = null;
            const { translations, lang } = this.props.formContext;
            const { suggestion } = this.state;
            const { Glyphicon } = this.context.theme;
            const isSuggested = this.isSuggested();
            if (displayValidationState && !(0, utils_1.isEmptyString)(value) && isSuggested !== undefined) {
                validationState = isSuggested ? "success" : "warning";
            }
            const getGlyphNameFromState = (state) => {
                switch (state) {
                    case "success":
                        return "ok";
                    case "warning":
                        return "warning-sign";
                    case "error":
                        return "remove";
                    default:
                        return "";
                }
            };
            const getGlyph = (state) => {
                if (!displayValidationState) {
                    return null;
                }
                const glyph = getGlyphNameFromState(state);
                return glyph ? (React.createElement(Glyphicon, { key: glyph, style: { pointerEvents: "none" }, glyph: glyph, className: "form-control-feedback" })) : null;
            };
            let glyph = undefined;
            if (!this.state.focused && !this.state.isLoading && (!onToggle || !this.state.focused)) {
                glyph = (validationState === "success" && renderSuccessGlyph) ?
                    renderSuccessGlyph(value) : getGlyph(validationState);
            }
            const addon = informalTaxonGroups && renderInformalTaxonGroupSelector
                ? React.createElement(InformalTaxonGroupsAddon, { key: "informal", taxonGroupID: taxonGroupID, onClear: this.onInformalTaxonGroupSelected, open: this.state.informalTaxonGroupsOpen, onOpen: this.onInformalTaxonGroupsOpened, onToggle: onToggle, formContext: this.props.formContext })
                : null;
            const getTogglerTooltip = () => {
                const { toggleable } = (0, utils_1.getUiOptions)(this.props.uiSchema);
                let tooltip = typeof toggleable.tooltip === "string"
                    ? toggleable.tooltip
                    : `${translations[this.props.toggled ? "StopShorthand" : "StartShorthand"]}. ${translations.ShorthandHelp}`;
                if (tooltip === "") {
                    return tooltip;
                }
                const { shortcuts } = this.props.formContext.services.keyHandler;
                Object.keys(shortcuts).some(keyCombo => {
                    if (shortcuts[keyCombo].fn == "autosuggestToggle") {
                        tooltip = `[${(0, utils_1.stringifyKeyCombo)(keyCombo)}]: ${tooltip}`;
                        return true;
                    }
                });
                return tooltip;
            };
            const getToggleGlyph = () => {
                const { toggleable } = (0, utils_1.getUiOptions)(this.props.uiSchema);
                const props = {
                    className: (0, utils_1.classNames)("autosuggest-input-addon", "power-user-addon", this.props.toggled && "active"),
                    onMouseDown: this.onToggle,
                    onKeyDown: this.onKeyDown
                };
                if (toggleable && toggleable.glyphClass) {
                    return (React.createElement(components_1.Button, Object.assign({}, props),
                        React.createElement("span", { className: toggleable.glyphClass })));
                }
                return React.createElement(components_1.GlyphButton, Object.assign({ glyph: "flash" }, props));
            };
            const toggler = onToggle
                ? (React.createElement(components_1.TooltipComponent, { tooltip: getTogglerTooltip(), key: "toggler" }, getToggleGlyph())) : null;
            const { inputValue = "" } = this.state;
            const input = (React.createElement(components_1.FetcherInput, Object.assign({ value: inputValue, glyph: glyph, loading: this.state.isLoading, validationState: validationState, extra: [addon, toggler] }, inputProps)));
            let component = input;
            if (displayValidationState && Wrapper) {
                component = (React.createElement(Wrapper, { isSuggested: isSuggested, suggestion: suggestion, options: (0, utils_1.getUiOptions)(this.props.uiSchema), id: this.props.id, value: suggestion && suggestion.key, inputValue: value, ref: this.wrapperRef, formContext: this.props.formContext }, component));
            }
            if (informalTaxonGroups) {
                component = (React.createElement(React.Fragment, null,
                    component,
                    this.state.informalTaxonGroupsOpen && React.createElement(InformalTaxonGroupChooserWidget_1.InformalTaxonGroupChooser, { modal: true, onHide: this.onInformalTaxonGroupHide, onSelected: this.onInformalTaxonGroupSelected, formContext: this.props.formContext, lang: lang })));
            }
            return component;
        };
        const isSuggested = this.isValueSuggested(props);
        this.state = {
            isLoading: false,
            suggestions: [],
            focused: false,
            inputValue: props.value !== undefined ? props.value : "",
            suggestion: isSuggested ? {} : undefined,
        };
        this.apiClient = this.props.formContext.apiClient;
    }
    componentDidMount() {
        this.mounted = true;
        this.triggerConvert(this.props);
        this.props.formContext.services.keyHandler.addKeyHandler(this.props.id, this.keyFunctions);
    }
    componentWillUnmount() {
        this.mounted = false;
        this.props.formContext.services.keyHandler.removeKeyHandler(this.props.id, this.keyFunctions);
    }
    componentDidUpdate(prevProps) {
        if (this.props.controlledValue) {
            return;
        }
        const valueChanged = this.state.suggestion && this.props && prevProps.value !== this.props.value && this.props.value !== this.state.suggestionForValue;
        const valueContextChanged = !(0, utils_2.deepEquals)(prevProps.valueContext, this.props.valueContext);
        if (valueChanged) {
            this.props.getSuggestionFromValue(this.props.value, this.props.valueContext).then(suggestion => {
                this.setState({ inputValue: this.getSuggestionValue(suggestion), suggestion });
            }, () => {
                const state = { suggestion: undefined };
                if (!this.props.allowNonsuggestedValue) {
                    state.inputValue = "";
                }
                this.setState(state);
            });
        }
        else if (valueContextChanged) {
            if (this.triggerConvertTimeout) {
                clearTimeout(this.triggerConvertTimeout);
            }
            // debounce trigger convert function so that it's not called too frequently
            this.triggerConvertTimeout = this.props.formContext.setTimeout(() => {
                if (!this.mounted)
                    return;
                this.triggerConvert(this.props);
            }, 100);
        }
    }
    render() {
        const { props } = this;
        let { suggestions, inputValue = "" } = this.state;
        const inputProps = Object.assign(Object.assign({ id: this.props.id, value: inputValue, disabled: props.disabled || props.readonly, placeholder: props.placeholder }, (this.props.inputProps || {})), { onChange: this.onInputChange, onBlur: this.onBlur, onFocus: this.onFocus, autoComplete: "off" });
        let cssClasses = {
            suggestionsContainer: "rw-popup-container rw-popup-transition",
            suggestionsContainerOpen: "rw-popup",
            suggestionsList: "rw-list",
            suggestion: "rw-list-option",
            suggestionHighlighted: "rw-list-option rw-state-focus rw-state-selected"
        };
        const highlightFirstSuggestion = "highlightFirstSuggestion" in this.props
            ? this.props.highlightFirstSuggestion
            : !this.props.allowNonsuggestedValue;
        const { renderExtra } = props;
        return React.createElement(React.Fragment, null,
            renderExtra && renderExtra(),
            React.createElement("div", { className: (0, utils_1.classNames)("autosuggest-wrapper", props.wrapperClassName) },
                React.createElement(ReactAutosuggest, { id: `${this.props.id}-autosuggest`, inputProps: inputProps, renderInputComponent: this.renderInput, suggestions: suggestions, renderSuggestion: this.renderSuggestion, onSuggestionsFetchRequested: this.onSuggestionsFetchRequested, onSuggestionsClearRequested: this.onSuggestionsClearRequested, onSuggestionSelected: this.onSuggestionSelected, onUnsuggestedSelected: this.onUnsuggestedSelected, highlightFirstSuggestion: highlightFirstSuggestion, suggestionsOpenOnFocus: !this.isSuggested(), getSuggestionValue: this.getSuggestionValue, theme: cssClasses, formContext: this.props.formContext, ref: this.setRef })));
    }
}
exports.Autosuggest = Autosuggest;
Autosuggest.contextType = ReactContext_1.default;
Autosuggest.propTypes = {
    autosuggestField: PropTypes.string,
    allowNonsuggestedValue: PropTypes.bool,
    onSuggestionSelected: PropTypes.func,
    onUnsuggestedSelected: PropTypes.func,
    onInputChange: PropTypes.func,
    uiSchema: PropTypes.object,
    informalTaxonGroups: PropTypes.string,
    onInformalTaxonGroupSelected: PropTypes.func,
    cache: PropTypes.bool,
    valueContext: PropTypes.object
};
Autosuggest.defaultProps = {
    allowNonsuggestedValue: true,
    suggestionReceive: "key",
    cache: true
};
class _TaxonWrapper extends React.Component {
    constructor(props) {
        super(props);
        this.state = { converted: false };
    }
    componentDidMount() {
        this.mounted = true;
        this.fetch(this.props.value);
    }
    componentWillUnmount() {
        this.mounted = false;
    }
    UNSAFE_componentWillReceiveProps({ value }) {
        this.fetch(value);
    }
    fetch(value) {
        return __awaiter(this, void 0, void 0, function* () {
            if (!value) {
                this.setState({ scientificName: "", cursiveName: false });
            }
            else {
                this.props.formContext.apiClient.get(`/taxa/${value}`).then(({ scientificName, cursiveName, vernacularName, taxonRank, informalGroups, finnish, checklist }) => {
                    if (!this.mounted)
                        return;
                    this.setState({ value, taxonRank, informalTaxonGroups: informalGroups, taxon: { scientificName, vernacularName, cursiveName, finnish, checklist } });
                    (0, InformalTaxonGroupChooserWidget_1.getInformalGroups)(this.props.formContext.apiClient).then(({ informalTaxonGroupsById }) => {
                        if (!this.mounted)
                            return;
                        this.setState({ informalTaxonGroupsById });
                    });
                });
                const parents = (yield this.props.formContext.apiClient.get(`/taxa/${value}/parents`)).results;
                if (!this.mounted)
                    return;
                const state = { order: undefined, family: undefined };
                for (let parent of parents) {
                    const { vernacularName, scientificName, cursiveName } = parent;
                    if (parent.taxonRank === "MX.order") {
                        state.order = { vernacularName, scientificName, cursiveName };
                    }
                    else if (parent.taxonRank === "MX.family") {
                        state.family = { vernacularName, scientificName, cursiveName };
                    }
                    if (state.order && state.family) {
                        this.setState(state);
                        break;
                    }
                }
                this.setState(Object.assign(Object.assign({}, state), { higherThanOrder: !state.order && !state.family }));
                const taxonRanks = (yield this.props.formContext.apiClient.get("/metadata/properties/MX.taxonRank/alt")).results;
                if (!this.mounted)
                    return;
                this.setState({ taxonRanks: (0, utils_1.dictionarify)(taxonRanks, function getKey(rank) { return rank.id; }, function getValue(rank) { return rank.value; }) });
            }
        });
    }
    render() {
        const { id, formContext, value, children, placement, inputValue, isSuggested } = this.props;
        const { taxon = {}, taxonRanks, taxonRank, order, family, higherThanOrder, informalTaxonGroupsById = {}, informalTaxonGroups = [] } = this.state;
        const { Popover, Tooltip } = this.context.theme;
        const tooltipElem = (React.createElement(Tooltip, { id: `${id}-popover-tooltip` }, formContext.translations.OpenSpeciedCard));
        let imageID = informalTaxonGroups[0];
        if (informalTaxonGroupsById[imageID] && informalTaxonGroupsById[imageID].parent) {
            imageID = informalTaxonGroupsById[imageID].parent.id;
        }
        const loading = !taxonRank || !(order || family || higherThanOrder) || !taxonRanks;
        const popover = (0, utils_1.isEmptyString)(inputValue)
            ? React.createElement(React.Fragment, null)
            : inputValue && isSuggested === false
                ? React.createElement(Tooltip, { id: `${this.props.id}-tooltip` }, this.props.formContext.translations.UnknownName)
                : (React.createElement(Popover, { id: `${id}-popover` },
                    React.createElement("div", { className: `laji-form taxon-popover informal-group-image ${imageID}` },
                        React.createElement("div", null,
                            React.createElement(ReactContext_1.default.Provider, { value: this.context },
                                React.createElement(components_1.OverlayTrigger, { overlay: tooltipElem },
                                    React.createElement("a", { href: `http://tun.fi/${value}`, target: "_blank", rel: "noopener noreferrer" },
                                        React.createElement(TaxonName, Object.assign({}, taxon)),
                                        React.createElement("br", null)))),
                            React.createElement("strong", null,
                                formContext.translations.taxonomicRank,
                                ":"),
                            " ",
                            taxonRanks && taxonRank ? taxonRanks[taxonRank] : "",
                            React.createElement("br", null),
                            !higherThanOrder ? (React.createElement(React.Fragment, null,
                                React.createElement("strong", null,
                                    formContext.translations.taxonGroups,
                                    ":"),
                                React.createElement("ul", null,
                                    order && React.createElement("li", null,
                                        React.createElement(TaxonName, Object.assign({}, order))),
                                    family && React.createElement("li", null,
                                        React.createElement(TaxonName, Object.assign({}, family)))))) : React.createElement(React.Fragment, null,
                                React.createElement("br", null),
                                React.createElement("br", null))),
                        loading && React.createElement(react_spinner_1.default, null))));
        return (React.createElement(components_1.OverlayTrigger, { hoverable: true, placement: placement, contextId: this.props.formContext.contextId, overlay: popover, ref: this.props.overlayRef, formContext: this.props.formContext }, children));
    }
}
_TaxonWrapper.contextType = ReactContext_1.default;
const TaxonWrapper = React.forwardRef((props, ref) => React.createElement(_TaxonWrapper, Object.assign({}, props, { overlayRef: ref })));
class InformalTaxonGroupsAddon extends React.Component {
    constructor(props) {
        super(props);
        this.onClear = (e) => {
            e.stopPropagation();
            if (this.props.onClear)
                this.props.onClear(undefined);
        };
        this.toggle = () => {
            if (this.props.onOpen)
                this.props.onOpen(!this.props.open);
        };
        this.onKeyDown = this.props.formContext.utils.keyboardClick(this.toggle);
        this.renderGlyph = () => {
            const { taxonGroupID } = this.props;
            let imageID = taxonGroupID;
            const { informalTaxonGroupsById = {} } = this.state;
            if (informalTaxonGroupsById[taxonGroupID] && informalTaxonGroupsById[taxonGroupID].parent) {
                imageID = informalTaxonGroupsById[taxonGroupID].parent.id;
            }
            const buttonProps = {
                onClick: this.toggle,
                onKeyDown: this.onKeyDown
            };
            return taxonGroupID
                ? (React.createElement("span", { className: "btn btn-default informal-taxon-group-chooser-addon active" },
                    React.createElement("div", null,
                        React.createElement("span", Object.assign({}, buttonProps, { className: `informal-group-image ${imageID}` })),
                        React.createElement("button", { className: "close", onClick: this.onClear }, "\u00D7")))) : (React.createElement(components_1.GlyphButton, Object.assign({}, buttonProps, { glyph: "menu-hamburger", className: "autosuggest-input-addon informal-taxon-group-chooser" })));
        };
        this.state = {};
    }
    componentDidMount() {
        this.mounted = true;
        (0, InformalTaxonGroupChooserWidget_1.getInformalGroups)(this.props.formContext.apiClient).then(({ informalTaxonGroupsById }) => {
            if (!this.mounted)
                return;
            this.setState({ informalTaxonGroupsById });
        });
    }
    componentWillUnmount() {
        this.mounted = false;
    }
    render() {
        if (!this.onKeyDown) { // Context not available before first render, so we initialize the key handler here.
            this.onKeyDown = this.context.utils.keyboardClick(this.toggle);
        }
        return (React.createElement(components_1.TooltipComponent, { tooltip: this.props.taxonGroupID && this.state.informalTaxonGroupsById ? this.state.informalTaxonGroupsById[this.props.taxonGroupID].name : this.props.formContext.translations.PickInformalTaxonGroup }, this.renderGlyph()));
    }
}
InformalTaxonGroupsAddon.contextType = ReactContext_1.default;
class TaxonImgChooser extends React.Component {
    constructor(props) {
        super(props);
        this.showModal = () => {
            this.setState({ modal: true });
        };
        this.hideModal = () => {
            this.setState({ modal: false });
        };
        this.onSelected = () => {
            this.props.onSelect(this.props.id, this.state.taxon);
            this.hideModal();
        };
        this.state = {};
    }
    componentDidMount() {
        this.mounted = true;
        this.props.formContext.apiClient.get(`/taxa/${this.props.id}`, { query: { includeMedia: true } }).then(taxon => {
            if (!this.mounted)
                return;
            if (taxon.multimedia && taxon.multimedia.length) {
                const [media] = taxon.multimedia;
                this.setState({
                    taxon: taxon,
                    thumbnail: this.props.url || media.squareThumbnailURL || media.thumbnailURL,
                    large: this.props.url || media.largeURL
                });
            }
            else {
                this.setState({ taxon });
            }
        });
    }
    componentWillUnmount() {
        this.mounted = false;
    }
    render() {
        const { thumbnail, taxon, modal } = this.state;
        const { translations } = this.props.formContext;
        const { Modal } = this.context.theme;
        return (React.createElement("div", { className: "laji-form-medias" },
            React.createElement("div", { className: "taxon-img media-container interactive", style: { backgroundImage: `url(${thumbnail})` }, onClick: this.showModal, tabIndex: 0 },
                !taxon && React.createElement("div", { className: "media-loading" },
                    React.createElement(react_spinner_1.default, null)),
                React.createElement("span", null, taxon && taxon.vernacularName || "")),
            modal &&
                React.createElement(Modal, { dialogClassName: "laji-form media-modal", show: true, onHide: this.hideModal },
                    React.createElement(Modal.Body, null,
                        React.createElement("img", { src: this.state.large }),
                        React.createElement("div", null,
                            React.createElement("h3", { className: "text-center" },
                                React.createElement(TaxonName, Object.assign({}, this.state.taxon, { finnish: false }))),
                            React.createElement(components_1.Button, { block: true, onClick: this.onSelected }, translations.Select),
                            React.createElement(components_1.Button, { block: true, onClick: this.hideModal }, translations.Cancel))))));
    }
}
TaxonImgChooser.contextType = ReactContext_1.default;
const TaxonName = ({ scientificName, vernacularName = "", cursiveName, finnish, checklist }) => {
    const _scientificName = vernacularName && scientificName
        ? `(${scientificName})`
        : (scientificName || "");
    return (React.createElement(React.Fragment, null,
        `${vernacularName}${vernacularName ? " " : ""}`,
        cursiveName ? React.createElement("i", null, _scientificName) : _scientificName,
        renderTaxonIcons({ finnish, checklist }, " ")));
};
class ReactAutosuggest extends React.Component {
    constructor(props) {
        super(props);
        this.getDisplayValue = (suggestion) => {
            return this.props.getSuggestionValue
                ? this.props.getSuggestionValue(suggestion)
                : suggestion.value;
        };
        this.onInputFocus = (e) => {
            this.setState({ focused: true, hideSuggestions: false });
            this.requestFetch(this.state.inputValue);
            this.props.inputProps && this.props.inputProps.onFocus && this.props.inputProps.onFocus(e);
        };
        this.onInputTryBlur = (e) => {
            if (this.suggestionMouseDownFlag) {
                return;
            }
            this.onBlur(e);
        };
        this.onListBlur = (e) => {
            this.onBlur(e);
        };
        this.onInputKeyDown = (e) => {
            var _a, _b;
            let state;
            switch (e.key) {
                case "ArrowDown":
                    e.preventDefault();
                    if (this.state.focusedIdx >= (this.props.suggestions || []).length - 1) {
                        break;
                    }
                    state = {
                        focusedIdx: typeof this.state.focusedIdx === "number"
                            ? this.state.focusedIdx + 1
                            : (this.props.suggestions || []).length
                                ? 0
                                : undefined,
                        touched: true
                    };
                    if (state.focusedIdx !== undefined) {
                        state.inputValue = this.getDisplayValue(this.props.suggestions[state.focusedIdx]);
                    }
                    this.setState(state);
                    break;
                case "ArrowUp":
                    e.preventDefault();
                    state = { focusedIdx: this.state.focusedIdx > 0 ? this.state.focusedIdx - 1 : undefined, touched: true };
                    if (state.focusedIdx !== undefined) {
                        state.inputValue = this.getDisplayValue(this.props.suggestions[state.focusedIdx]);
                    }
                    this.setState(state);
                    break;
                case "Enter":
                    if (e.altKey || e.ctrlKey || e.shiftKey) {
                        return;
                    }
                    e.preventDefault();
                    this.inputElem.blur();
                    this.inputElem.focus();
                    break;
                case "Control":
                case "Meta":
                case "Alt":
                case "Shift":
                case "CapsLock":
                    break;
                default:
                    this.setState({ touched: true });
            }
            (_b = (_a = this.props.inputProps) === null || _a === void 0 ? void 0 : _a.onKeyDown) === null || _b === void 0 ? void 0 : _b.call(_a, e);
        };
        this.onInputChange = (e) => {
            this._onInputChange(e.target.value, "keystroke");
        };
        this._onInputChange = (value, reason) => {
            const callback = reason === "click"
                ? () => { }
                : () => this.requestFetch(this.state.inputValue);
            this.props.inputProps && this.props.inputProps.onChange
                ? this.props.inputProps.onChange({ target: { value } }, reason, callback)
                : this.setState({ inputValue: value }, callback);
        };
        this.setInputRef = (elem) => {
            this.inputElem = (0, react_dom_1.findDOMNode)(elem);
        };
        this.onSuggestionMouseDown = () => {
            this.suggestionMouseDownFlag = true;
        };
        this.onSuggestionMouseUp = (e) => {
            var _a, _b;
            this.suggestionMouseDownFlag = false;
            const suggestion = this.getSuggestionFromClick(e);
            this.onSuggestionSelected(suggestion);
            this.setState({ focused: false, focusedIdx: undefined, touched: false });
            (_b = (_a = this.props.inputProps) === null || _a === void 0 ? void 0 : _a.onBlur) === null || _b === void 0 ? void 0 : _b.call(_a, e, true);
        };
        this.getSuggestionFromClick = ({ target }) => {
            let idx;
            while (typeof idx !== "string") {
                idx = target.getAttribute("data-idx");
                target = target.parentElement;
            }
            return this.props.suggestions[idx];
        };
        this.listRef = React.createRef();
        this.state = {
            inputValue: props.inputProps.value
        };
    }
    UNSAFE_componentWillReceiveProps(props) {
        if ("value" in props.inputProps) {
            this.setState({ inputValue: props.inputProps.value });
        }
    }
    render() {
        return (React.createElement("div", { onFocus: this.onContainerFocus, onBlur: this.onContainerBlur },
            this.renderInput(),
            this.renderSuggestions()));
    }
    onBlur(e) {
        var _a, _b;
        if (e.relatedTarget && e.relatedTarget === (0, react_dom_1.findDOMNode)(this.listRef.current)) {
            return;
        }
        const suggestion = (this.props.suggestions || [])[this.state.focusedIdx];
        suggestion && this.onSuggestionSelected(this.props.suggestions[this.state.focusedIdx]);
        this.setState({ focused: false, focusedIdx: undefined, touched: false });
        (_b = (_a = this.props.inputProps) === null || _a === void 0 ? void 0 : _a.onBlur) === null || _b === void 0 ? void 0 : _b.call(_a, e, !!suggestion);
    }
    renderInput() {
        const { inputProps, renderInputComponent = this.renderDefaultInputComponent } = this.props;
        return renderInputComponent(Object.assign(Object.assign({}, inputProps), { value: this.state.inputValue, onChange: this.onInputChange, onFocus: this.onInputFocus, onBlur: this.onInputTryBlur, onKeyDown: this.onInputKeyDown, ref: this.setInputRef }));
    }
    renderDefaultInputComponent(props) {
        return React.createElement("input", Object.assign({}, props));
    }
    renderSuggestions() {
        if ((!this.props.suggestionsOpenOnFocus && !this.state.touched)
            || !this.state.focused
            || this.state.hideSuggestions
            || !(this.props.suggestions || []).length) {
            return null;
        }
        const { suggestion, suggestionsList, suggestionsContainer, suggestionsContainerOpen, suggestionHighlighted } = this.props.theme || {};
        return (React.createElement("div", { className: (0, utils_1.classNames)(suggestionsContainer, suggestionsContainerOpen) },
            React.createElement("ul", { className: suggestionsList, tabIndex: -1, ref: this.listRef, onBlur: this.onListBlur }, this.props.suggestions.map((s, i) => React.createElement("li", { key: i, className: (0, utils_1.classNames)(suggestion, this.state.focusedIdx === i && suggestionHighlighted), onMouseDown: this.onSuggestionMouseDown, onMouseUp: this.onSuggestionMouseUp, "data-idx": i }, this.props.renderSuggestion(s))))));
    }
    onSuggestionSelected(suggestion) {
        this.setState({ inputValue: suggestion.value, hideSuggestions: true });
        this.props.onSuggestionSelected && this.props.onSuggestionSelected(suggestion);
    }
    requestFetch(value) {
        this.props.onSuggestionsFetchRequested({ value });
    }
}
ReactAutosuggest.contextType = ReactContext_1.default;
const getWrapper = (unknownValueLabel) => React.forwardRef(({ formContext, children, id, inputValue, isSuggested }, ref) => {
    const { Tooltip } = React.useContext(ReactContext_1.default).theme;
    if (!inputValue || isSuggested) {
        return children;
    }
    const tooltip = (React.createElement(Tooltip, { id: `${id}-tooltip` }, formContext.translations[unknownValueLabel]));
    return (React.createElement(components_1.OverlayTrigger, { overlay: tooltip, placement: "top", ref: ref }, children));
});
const FriendsWrapper = getWrapper("UnknownName");
const OrganizationWrapper = getWrapper("UnknownOrganization");
const CollectionWrapper = getWrapper("UnknownCollection");
const DatasetWrapper = getWrapper("UnknownTag");
