"use strict";
var __extends = (this && this.__extends) || function (d, b) {
    for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
    function __() { this.constructor = d; }
    d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
var React = require('react');
var PeoplePicker_Props_1 = require('./PeoplePicker.Props');
var Persona_1 = require('../../Persona');
var Spinner_1 = require('../../Spinner');
var string_1 = require('../../utilities/string');
var FocusZone_1 = require('../../FocusZone');
var css_1 = require('../../utilities/css');
var KeyCodes_1 = require('../../utilities/KeyCodes');
var EventGroup_1 = require('../../utilities/eventGroup/EventGroup');
var DomUtils_1 = require('../../utilities/DomUtils');
require('./PeoplePicker.scss');
var INVALID_INDEX = -1;
var PeoplePicker = (function (_super) {
    __extends(PeoplePicker, _super);
    function PeoplePicker(props) {
        _super.call(this, props);
        this._suggestionsCount = 0;
        this._focusedPersonaIndex = INVALID_INDEX;
        this._events = new EventGroup_1.EventGroup(this);
        this._activatePeoplePicker = this._activatePeoplePicker.bind(this);
        this._dismissPeoplePicker = this._dismissPeoplePicker.bind(this);
        this._addPersonaToSelectedList = this._addPersonaToSelectedList.bind(this);
        this._searchForMoreResults = this._searchForMoreResults.bind(this);
        this._onSearchFieldTextChanged = this._onSearchFieldTextChanged.bind(this);
        this._onSearchFieldKeyDown = this._onSearchFieldKeyDown.bind(this);
        this._onFocusCapture = this._onFocusCapture.bind(this);
        this._removeSelectedPersona = this._removeSelectedPersona.bind(this);
        this._onSelectedPersonaFocus = this._onSelectedPersonaFocus.bind(this);
        this._onSearchBoxKeyDown = this._onSearchBoxKeyDown.bind(this);
        var selectedPersonas = props.initialItems ? props.initialItems : [];
        this.state = {
            isActive: false,
            isSearching: false,
            searchTextValue: '',
            highlightedSearchResultIndex: INVALID_INDEX,
            selectedPersonas: selectedPersonas
        };
    }
    PeoplePicker.prototype.componentDidMount = function () {
        this._events.on(window, 'focus', this._onFocusCapture, true);
        this._events.on(window, 'click', this._onClickCapture, true);
        this._events.on(window, 'touchstart', this._onClickCapture, true);
    };
    PeoplePicker.prototype.componentWillUnmount = function () {
        this._events.dispose();
    };
    PeoplePicker.prototype.componentDidUpdate = function () {
        this._setScollPosition();
        var suggestions = this.props.suggestions;
        if (this.state.isActive && this._suggestionsCount !== suggestions.length) {
            this._setSelectedSearchResultIndex(0);
        }
        this._suggestionsCount = suggestions.length;
        // if the selected persona is out of range after an update, it means the user deleted it
        // and we need to set focus on the last one (which isn't handled by the FocusZone).
        // Unless there are no more personas, then set focus on the input field.
        var selectedPersonaCount = this.state.selectedPersonas.length;
        if (this._focusedPersonaIndex !== INVALID_INDEX && this._focusedPersonaIndex >= selectedPersonaCount) {
            if (selectedPersonaCount > 0) {
                this._focusedPersonaIndex = selectedPersonaCount - 1;
                this.refs['persona' + this._focusedPersonaIndex].focus();
            }
            else {
                this._focusedPersonaIndex = INVALID_INDEX;
                this.refs.searchField.focus();
            }
        }
    };
    PeoplePicker.prototype.render = function () {
        var _this = this;
        var type = this.props.type;
        var searchField = this._renderSearchField();
        var searchResults = type === PeoplePicker_Props_1.PeoplePickerType.memberList ? this._renderSearchResultsForMemberList() : this._renderSearchResults();
        // Render the selected personas.
        // There are two layouts to choose from, based on the Persona type.
        var selectedPersonas = type === PeoplePicker_Props_1.PeoplePickerType.memberList ? null : this._renderSelectedPersonas();
        var memberList = type === PeoplePicker_Props_1.PeoplePickerType.memberList ? this._renderSelectedPersonasAsMemberList() : null;
        var className = css_1.css('ms-PeoplePicker', {
            'is-active': this.state.isActive,
            'ms-PeoplePicker--compact': type === PeoplePicker_Props_1.PeoplePickerType.compact,
            'ms-PeoplePicker--membersList': type === PeoplePicker_Props_1.PeoplePickerType.memberList,
        });
        return (React.createElement("div", {className: className, ref: 'root', key: 'root'}, React.createElement("div", {className: 'ms-PeoplePicker-searchBox', ref: function (searchBox) { return _this.refs._searchBox = searchBox; }, onKeyDown: this._onSearchBoxKeyDown}, React.createElement(FocusZone_1.FocusZone, {onActiveElementChanged: this._onSelectedPersonaFocus, ref: function (focusZone) { return _this.refs.focusZone = focusZone; }}, selectedPersonas, searchField)), searchResults, memberList));
    };
    PeoplePicker.prototype._onSearchBoxKeyDown = function (ev) {
        switch (ev.which) {
            // remove focused persona
            case KeyCodes_1.KeyCodes.backspace:
            case KeyCodes_1.KeyCodes.del:
                if (this._focusedPersonaIndex !== INVALID_INDEX) {
                    this._removeSelectedPersona(this._focusedPersonaIndex);
                    ev.stopPropagation();
                    ev.preventDefault();
                }
        }
    };
    /**
     *
     */
    PeoplePicker.prototype._onSelectedPersonaFocus = function (element, ev) {
        // store a reference to this element
        // in keydown handler, if there's a focused persona, we want to delete it on certain key press events
        var index = element.getAttribute('data-selection-index');
        if (index) {
            this._focusedPersonaIndex = Number(index);
        }
    };
    /**
     * Handles closing the people picker whenever focus is lost
     */
    PeoplePicker.prototype._onFocusCapture = function (ev) {
        // onBlur, relatedTarget refers to the element that got focus
        var target = ev.target;
        if (!DomUtils_1.elementContains(this.refs.root, target)) {
            this._dismissPeoplePicker();
        }
    };
    /**
     * Handles closing the people picker whenever focus is lost through mouse.
     */
    PeoplePicker.prototype._onClickCapture = function (ev) {
        if (!this.refs.searchField.contains(ev.target)
            && !this.refs.pickerResults.contains(ev.target)) {
            this._dismissPeoplePicker();
        }
    };
    /**
     * Click handler for when the user clicks on the Search For Results button.
     */
    PeoplePicker.prototype._searchForMoreResults = function (event) {
        var onSearchForMoreResults = this.props.onSearchForMoreResults;
        this.setState({
            'isSearching': true
        });
        event.preventDefault();
        event.stopPropagation();
        if (typeof onSearchForMoreResults !== 'undefined') {
            onSearchForMoreResults(this.refs.searchField.value);
        }
    };
    /**
     * Opens the people picker dropdown.
     */
    PeoplePicker.prototype._activatePeoplePicker = function () {
        this.setState({
            'isActive': true,
            'highlightedSearchResultIndex': INVALID_INDEX,
        });
        this._highlightedSearchResult = undefined;
    };
    /**
     * Closes the people picker dropdown.
     */
    PeoplePicker.prototype._dismissPeoplePicker = function () {
        this.setState({
            'isActive': false,
            'isSearching': false,
            'highlightedSearchResultIndex': INVALID_INDEX,
        });
        this._highlightedSearchResult = undefined;
    };
    /**
     *
     */
    PeoplePicker.prototype._removeSuggestedPersona = function (index, personaInfo) {
        var onRemoveSuggestion = this.props.onRemoveSuggestion;
        if (onRemoveSuggestion) {
            onRemoveSuggestion(index, personaInfo);
        }
    };
    /**
     * Selects the persona, dismisses the people picker, and clears out the search field.
     */
    PeoplePicker.prototype._addPersonaToSelectedList = function (personaInfo) {
        var selectedPersonas = this.state.selectedPersonas;
        var onItemAdded = this.props.onItemAdded;
        if (onItemAdded) {
            onItemAdded(personaInfo);
        }
        selectedPersonas.push(personaInfo);
        this._dismissPeoplePicker();
        this.refs.searchField.value = '';
        this.setState({
            searchTextValue: '',
            selectedPersonas: selectedPersonas
        });
        this.refs.searchField.focus();
        this._onSearchFieldTextChanged();
    };
    /**
     * Creates a new persona based on what the user has typed (non search result persona)
     */
    PeoplePicker.prototype._addManualPersonaToSelectedList = function () {
        var newPersonaName = this.state.searchTextValue;
        if (newPersonaName.length > 0) {
            var personaInfo = {
                imageInitials: newPersonaName.charAt(0).toUpperCase(),
                primaryText: newPersonaName,
                secondaryText: newPersonaName
            };
            this._addPersonaToSelectedList(personaInfo);
        }
    };
    /**
     * Handles keyboard inputs for the PeoplePicker.
     */
    PeoplePicker.prototype._onSearchFieldKeyDown = function (ev) {
        var type = this.props.type;
        var _a = this.state, isActive = _a.isActive, highlightedSearchResultIndex = _a.highlightedSearchResultIndex, selectedPersonas = _a.selectedPersonas;
        switch (ev.which) {
            // Enter behavior:
            // - Adds the highlighted persona from the search results (autocomplete)
            // - creates a new persona from the user's input (not from the search results)
            case KeyCodes_1.KeyCodes.enter:
                if (isActive && highlightedSearchResultIndex !== INVALID_INDEX) {
                    this._addPersonaToSelectedList(this._highlightedSearchResult);
                }
                else {
                    this._addManualPersonaToSelectedList();
                }
                break;
            // Escape behavior:
            // - closes the people picker if it is open
            case KeyCodes_1.KeyCodes.escape:
                if (isActive) {
                    this._dismissPeoplePicker();
                }
                break;
            // Backspace behavior:
            // - closes the people picker if it is open
            // - sets focus to the last selected persona if people picker is closed
            // - removes the focused persona
            case KeyCodes_1.KeyCodes.backspace:
                // allow normal event handling when there is text entered
                if (this.refs.searchField.value.length !== 0) {
                    return; // continue propagation
                }
                if (isActive) {
                    this._dismissPeoplePicker();
                }
                else if (selectedPersonas.length > 0 && type !== PeoplePicker_Props_1.PeoplePickerType.memberList) {
                    var index = selectedPersonas.length - 1;
                    this.refs['persona' + index].focus();
                }
                break;
            // Up behavior:
            // - Moves the focus through the people picker dropdown if it is open
            // - Blurs out of the search field so that the Focus Zone sets focus on a selected personas
            case KeyCodes_1.KeyCodes.up:
                if (isActive && highlightedSearchResultIndex !== INVALID_INDEX) {
                    this._setSelectedSearchResultIndex(highlightedSearchResultIndex - 1);
                }
                else {
                    return; // continue propagation
                }
                break;
            // Down behavior:
            // - Activates the people picker if it is not open
            // - Moves the focus through the people picker dropdown if it is open
            case KeyCodes_1.KeyCodes.down:
                if (isActive) {
                    this._setSelectedSearchResultIndex(highlightedSearchResultIndex + 1);
                }
                else {
                    this._activatePeoplePicker();
                    this._setSelectedSearchResultIndex(0);
                }
                break;
            // Left behavior:
            // - Default cursor behavior if there is any text entered
            // - Blurs out of the search field so that the Focus Zone sets focus on a selected personas
            case KeyCodes_1.KeyCodes.left:
                if (this.refs.searchField.value.length !== 0) {
                    ev.stopPropagation();
                }
                return; // continue propagation
            // Tab behavior:
            // - Adds the highlighted persona from the search results (autocomplete)
            // - Shift-tab out of the FocusZone
            case KeyCodes_1.KeyCodes.tab:
                // allow default behavior for shift tab
                if (ev.shiftKey) {
                    return;
                }
                if (isActive && highlightedSearchResultIndex !== INVALID_INDEX) {
                    this._addPersonaToSelectedList(this._highlightedSearchResult);
                }
                else {
                    return; // continue propagation
                }
                break;
            // Semicolon and comma behavior:
            // - creates a new persona from the user's input (not from the search results)
            case KeyCodes_1.KeyCodes.semicolon:
            case KeyCodes_1.KeyCodes.comma:
                this._addManualPersonaToSelectedList();
                break;
            // Default keyboard behavior
            // - If any key is pressed on the search field input, activate the people picker
            // and set the first search result as selected.
            default:
                if (!isActive) {
                    this._activatePeoplePicker();
                }
                return; // continue propagation
        }
        // Only stop propagation if the event was handles and we didn't return
        ev.stopPropagation();
        ev.preventDefault();
    };
    /**
     * Sets which persona in the search results is currently selected/highlighted.
     */
    PeoplePicker.prototype._setSelectedSearchResultIndex = function (index) {
        var highlightedSearchResultIndex = this.state.highlightedSearchResultIndex;
        var suggestions = this.props.suggestions;
        if (suggestions.length > 0) {
            // Cap index to stay in bounds of available search results
            index = Math.max(0, Math.min(suggestions.length - 1, index));
        }
        else {
            index = INVALID_INDEX;
        }
        if (index !== highlightedSearchResultIndex) {
            // Set the selected option.
            this.setState({
                highlightedSearchResultIndex: index
            });
        }
    };
    /**
     * Handles changes in the input text box value, so we can notify the host
     * of the search value change.
     */
    PeoplePicker.prototype._onSearchFieldTextChanged = function () {
        var onSearchFieldChanged = this.props.onSearchFieldChanged;
        var textValue = this.refs.searchField.value;
        this.setState({
            searchTextValue: textValue
        });
        if (typeof onSearchFieldChanged !== 'undefined') {
            onSearchFieldChanged(textValue);
        }
    };
    /**
     * Handles keeping the currently selected persona in view.
     * If there's no search result selected, then reset the scroll to 0.
     */
    PeoplePicker.prototype._setScollPosition = function () {
        var selectedSearchResult = this.refs.selectedSearchResult;
        var newTop = 0;
        if (selectedSearchResult) {
            var selectedResultTop = selectedSearchResult.offsetTop;
            var menuItemHeight = selectedSearchResult.clientHeight;
            var currentTop = this.refs.pickerResultGroups.scrollTop;
            var totalHeight = this.refs.pickerResultGroups.clientHeight;
            newTop = currentTop;
            // check to scroll down
            var amountCutOffDown = (currentTop + totalHeight) - (selectedResultTop + menuItemHeight);
            if (amountCutOffDown < 0) {
                newTop = currentTop - amountCutOffDown;
            }
            // check to scroll up
            var amountCutOffUp = selectedResultTop - menuItemHeight;
            if (amountCutOffUp < currentTop) {
                newTop = amountCutOffUp;
            }
        }
        // set the new scroll
        this.refs.pickerResultGroups.scrollTop = newTop;
    };
    /**
     * Removes one of the selected personas
     */
    PeoplePicker.prototype._removeSelectedPersona = function (index) {
        var selectedPersonas = this.state.selectedPersonas;
        var onItemRemoved = this.props.onItemRemoved;
        if (onItemRemoved) {
            onItemRemoved(selectedPersonas[index]);
        }
        selectedPersonas.splice(index, 1);
        this.setState({
            selectedPersonas: selectedPersonas
        });
    };
    /**
     * Renders a list of personas using the list of selected personas, for the Member List variant.
     */
    PeoplePicker.prototype._renderSelectedPersonasAsMemberList = function () {
        var _this = this;
        var selectedPersonas = this.state.selectedPersonas;
        var addedMemberCountFormatText = this.props.addedMemberCountFormatText;
        var count = selectedPersonas.length;
        var className = css_1.css('ms-PeoplePicker-selected', {
            'is-active': count > 0
        });
        var id = 0;
        return React.createElement("div", {className: className}, addedMemberCountFormatText ?
            React.createElement("div", {className: 'ms-PeoplePicker-selectedHeader'}, React.createElement("span", {className: 'ms-PeoplePicker-selectedCount'}, string_1.format(addedMemberCountFormatText, count))) : React.createElement("div", {className: 'ms-PeoplePicker-memberListTopMargin'}), React.createElement("ul", {className: 'ms-PeoplePicker-selectedPeople'}, React.createElement(FocusZone_1.FocusZone, null, selectedPersonas.map(function (child) {
            return (React.createElement("li", {className: 'ms-PeoplePicker-selectedPerson', key: id++}, React.createElement(Persona_1.Persona, React.__spread({}, child, {size: Persona_1.PersonaSize.small, presence: child.presence ? child.presence : Persona_1.PersonaPresence.online})), React.createElement("button", {className: 'ms-PeoplePicker-resultAction', onClick: function () {
                _this._removeSelectedPersona(selectedPersonas.indexOf(child));
            }}, React.createElement("i", {className: 'ms-Icon ms-Icon--x'}))));
        }))));
    };
    /**
     * Renders a list of personas using the list of selected personas. Uses the default layout
     * of displaying the personas in the search box.
     */
    PeoplePicker.prototype._renderSelectedPersonas = function () {
        var _this = this;
        var id = 0;
        var selectedPersonas = this.state.selectedPersonas;
        return selectedPersonas.map(function (child) {
            var key = id++;
            return (React.createElement("div", {className: 'ms-PeoplePicker-persona', ref: 'persona' + key, key: key, "data-selection-index": key, "data-is-focusable": true, tabIndex: -1}, React.createElement("div", {className: 'ms-PeoplePicker-personaContent'}, React.createElement(Persona_1.Persona, React.__spread({}, child, {size: Persona_1.PersonaSize.extraSmall, presence: child.presence ? child.presence : Persona_1.PersonaPresence.online})), React.createElement("button", {className: 'ms-PeoplePicker-personaRemove', tabIndex: -1, "data-is-focusable": false, onClick: function () {
                _this._removeSelectedPersona(selectedPersonas.indexOf(child));
            }}, React.createElement("i", {className: 'ms-Icon ms-Icon--x'})))));
        });
    };
    /**
     * Renders the search field, which is the input element inside the searchbox.
     */
    PeoplePicker.prototype._renderSearchField = function () {
        var _this = this;
        return (React.createElement("input", {className: 'ms-PeoplePicker-searchField', type: 'text', ref: 'searchField', key: 'searchField', "data-is-focusable": true, onFocus: function () { _this._focusedPersonaIndex = INVALID_INDEX; }, onChange: this._onSearchFieldTextChanged, onKeyDown: this._onSearchFieldKeyDown}));
    };
    /**
     * Renders the popup search results
     */
    PeoplePicker.prototype._renderSearchResults = function () {
        var _this = this;
        var _a = this.props, suggestions = _a.suggestions, searchCategoryName = _a.searchCategoryName, noResultsText = _a.noResultsText, type = _a.type, isConnected = _a.isConnected, primarySearchText = _a.primarySearchText, secondarySearchText = _a.secondarySearchText, disconnectedText = _a.disconnectedText, canSearchMore = _a.canSearchMore;
        var isSearching = this.state.isSearching;
        // Generate a result group section for each item in the array of suggestions
        var resultItemId = 0;
        var resultGroupId = 0;
        var searchResultItems = [];
        suggestions.forEach(function (persona) {
            searchResultItems.push(_this._renderSearchResultItem(persona, resultItemId++));
        });
        var searchResults = (React.createElement("div", {className: 'ms-PeoplePicker-resultGroup', key: resultGroupId++}, React.createElement("div", {className: 'ms-PeoplePicker-resultGroupTitle'}, suggestions.length > 0 ? searchCategoryName : noResultsText), React.createElement("ul", {className: 'ms-PeoplePicker-resultList'}, searchResultItems)));
        var searchMoreClassName = css_1.css('ms-PeoplePicker-searchMore', {
            'is-searching': isSearching,
            'ms-PeoplePicker-searchMore--disconnected': !isConnected
        });
        var searchMoreButtonClassName = css_1.css('ms-PeoplePicker-searchMoreBtn', {
            'ms-PeoplePicker-searchMoreBtn--compact': type === PeoplePicker_Props_1.PeoplePickerType.compact
        });
        var searchIconClassName = css_1.css('ms-Icon', {
            'ms-Icon--search': isConnected,
            'ms-Icon--alert': !isConnected
        });
        var searchMore = canSearchMore ? (React.createElement("div", {className: searchMoreClassName, onClick: isConnected ? this._searchForMoreResults : null}, React.createElement("button", {className: searchMoreButtonClassName}, isSearching ? React.createElement(Spinner_1.Spinner, {type: Spinner_1.SpinnerType.large}) :
            React.createElement("div", {className: 'ms-PeoplePicker-searchMoreIcon'}, React.createElement("i", {className: searchIconClassName})), isConnected ? React.createElement("div", {className: 'ms-PeoplePicker-searchMoreSecondary'}, secondarySearchText) : null, React.createElement("div", {className: 'ms-PeoplePicker-searchMorePrimary'}, isSearching ? 'Searching for ' + this.refs.searchField.value : isConnected ? primarySearchText : disconnectedText)))) : undefined;
        return (React.createElement("div", {className: 'ms-PeoplePicker-results', key: 'pickerResults', ref: function (pickerResults) { return _this.refs.pickerResults = pickerResults; }}, React.createElement("div", {className: 'ms-PeoplePicker-resultGroups', ref: 'pickerResultGroups'}, searchResults), searchMore));
    };
    /**
     * Renders the popup search results, for the Member List variant.
     */
    PeoplePicker.prototype._renderSearchResultsForMemberList = function () {
        var _this = this;
        var suggestions = this.props.suggestions;
        // MemberList variant doesn't show groups
        var resultItemId = 0;
        var searchResultItems = [];
        suggestions.forEach(function (persona) {
            searchResultItems.push(_this._renderSearchResultItem(persona, resultItemId++));
        });
        var searchResults = (React.createElement("div", {className: 'ms-PeoplePicker-resultGroup'}, React.createElement("ul", {className: 'ms-PeoplePicker-resultList'}, searchResultItems)));
        return (React.createElement("div", {className: 'ms-PeoplePicker-results', key: 'pickerResults', ref: 'pickerResults'}, React.createElement("div", {className: 'ms-PeoplePicker-resultGroups', ref: 'pickerResultGroups'}, searchResults)));
    };
    /**
     * Renders a single persona as part of a list to be displayed in the search results.
     */
    PeoplePicker.prototype._renderSearchResultItem = function (personaInfo, id) {
        var _this = this;
        var type = this.props.type;
        var isSelected = id === this.state.highlightedSearchResultIndex;
        var buttonClassName = css_1.css('ms-PeoplePicker-resultBtn', {
            'ms-PeoplePicker-resultBtn--compact': type === PeoplePicker_Props_1.PeoplePickerType.compact,
            'ms-PeoplePicker-resultBtn--selected': isSelected
        });
        if (isSelected) {
            this._highlightedSearchResult = personaInfo;
        }
        var personaSize = type === PeoplePicker_Props_1.PeoplePickerType.compact ? Persona_1.PersonaSize.extraSmall : Persona_1.PersonaSize.regular;
        return (React.createElement("li", {className: 'ms-PeoplePicker-result', key: id, ref: isSelected ? 'selectedSearchResult' : null}, React.createElement("div", {role: 'button', className: buttonClassName}, React.createElement(Persona_1.Persona, React.__spread({}, personaInfo, {presence: personaInfo.presence ? personaInfo.presence : Persona_1.PersonaPresence.online, size: personaSize, onMouseDown: function () { _this._addPersonaToSelectedList(personaInfo); }, onClick: function () { _this._addPersonaToSelectedList(personaInfo); }})), type !== PeoplePicker_Props_1.PeoplePickerType.memberList ?
            React.createElement("button", {className: 'ms-PeoplePicker-resultAction', tabIndex: -1, onClick: function () { _this._removeSuggestedPersona(id, personaInfo); }}, React.createElement("i", {className: 'ms-Icon ms-Icon--x'}))
            : null)));
    };
    PeoplePicker.defaultProps = {
        type: PeoplePicker_Props_1.PeoplePickerType.normal,
        isConnected: true,
        canSearchMore: true
    };
    return PeoplePicker;
}(React.Component));
exports.PeoplePicker = PeoplePicker;

//# sourceMappingURL=PeoplePicker.js.map
