/**
 * @license
 * Copyright 2016 Google Inc.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
import { __assign, __extends } from "tslib";
import { MDCFoundation } from '../../../base/foundation';
import { cssClasses, Direction, EventSource, jumpChipKeys, navigationKeys, strings } from './constants';
var emptyClientRect = {
    bottom: 0,
    height: 0,
    left: 0,
    right: 0,
    top: 0,
    width: 0,
};
var FocusBehavior;
(function (FocusBehavior) {
    FocusBehavior[FocusBehavior["SHOULD_FOCUS"] = 0] = "SHOULD_FOCUS";
    FocusBehavior[FocusBehavior["SHOULD_NOT_FOCUS"] = 1] = "SHOULD_NOT_FOCUS";
})(FocusBehavior || (FocusBehavior = {}));
var MDCChipFoundation = /** @class */ (function (_super) {
    __extends(MDCChipFoundation, _super);
    function MDCChipFoundation(adapter) {
        var _this = _super.call(this, __assign(__assign({}, MDCChipFoundation.defaultAdapter), adapter)) || this;
        /** Whether a trailing icon click should immediately trigger exit/removal of the chip. */
        _this.shouldRemoveOnTrailingIconClick = true;
        /**
         * Whether the primary action should receive focus on click. Should only be
         * set to true for clients who programmatically give focus to a different
         * element on the page when a chip is clicked (like a menu).
         */
        _this.shouldFocusPrimaryActionOnClick = true;
        return _this;
    }
    Object.defineProperty(MDCChipFoundation, "strings", {
        get: function () {
            return strings;
        },
        enumerable: false,
        configurable: true
    });
    Object.defineProperty(MDCChipFoundation, "cssClasses", {
        get: function () {
            return cssClasses;
        },
        enumerable: false,
        configurable: true
    });
    Object.defineProperty(MDCChipFoundation, "defaultAdapter", {
        get: function () {
            return {
                addClass: function () { return undefined; },
                addClassToLeadingIcon: function () { return undefined; },
                eventTargetHasClass: function () { return false; },
                focusPrimaryAction: function () { return undefined; },
                focusTrailingAction: function () { return undefined; },
                getAttribute: function () { return null; },
                getCheckmarkBoundingClientRect: function () { return emptyClientRect; },
                getComputedStyleValue: function () { return ''; },
                getRootBoundingClientRect: function () { return emptyClientRect; },
                hasClass: function () { return false; },
                hasLeadingIcon: function () { return false; },
                isRTL: function () { return false; },
                isTrailingActionNavigable: function () { return false; },
                notifyEditFinish: function () { return undefined; },
                notifyEditStart: function () { return undefined; },
                notifyInteraction: function () { return undefined; },
                notifyNavigation: function () { return undefined; },
                notifyRemoval: function () { return undefined; },
                notifySelection: function () { return undefined; },
                notifyTrailingIconInteraction: function () { return undefined; },
                removeClass: function () { return undefined; },
                removeClassFromLeadingIcon: function () { return undefined; },
                removeTrailingActionFocus: function () { return undefined; },
                setPrimaryActionAttr: function () { return undefined; },
                setStyleProperty: function () { return undefined; },
            };
        },
        enumerable: false,
        configurable: true
    });
    MDCChipFoundation.prototype.isSelected = function () {
        return this.adapter.hasClass(cssClasses.SELECTED);
    };
    MDCChipFoundation.prototype.isEditable = function () {
        return this.adapter.hasClass(cssClasses.EDITABLE);
    };
    MDCChipFoundation.prototype.isEditing = function () {
        return this.adapter.hasClass(cssClasses.EDITING);
    };
    MDCChipFoundation.prototype.setSelected = function (selected) {
        this.setSelectedImpl(selected);
        this.notifySelection(selected);
    };
    MDCChipFoundation.prototype.setSelectedFromChipSet = function (selected, shouldNotifyClients) {
        this.setSelectedImpl(selected);
        if (shouldNotifyClients) {
            this.notifyIgnoredSelection(selected);
        }
    };
    MDCChipFoundation.prototype.getShouldRemoveOnTrailingIconClick = function () {
        return this.shouldRemoveOnTrailingIconClick;
    };
    MDCChipFoundation.prototype.setShouldRemoveOnTrailingIconClick = function (shouldRemove) {
        this.shouldRemoveOnTrailingIconClick = shouldRemove;
    };
    MDCChipFoundation.prototype.setShouldFocusPrimaryActionOnClick = function (shouldFocus) {
        this.shouldFocusPrimaryActionOnClick = shouldFocus;
    };
    MDCChipFoundation.prototype.getDimensions = function () {
        var _this = this;
        var getRootRect = function () { return _this.adapter.getRootBoundingClientRect(); };
        var getCheckmarkRect = function () {
            return _this.adapter.getCheckmarkBoundingClientRect();
        };
        // When a chip has a checkmark and not a leading icon, the bounding rect changes in size depending on the current
        // size of the checkmark.
        if (!this.adapter.hasLeadingIcon()) {
            var checkmarkRect = getCheckmarkRect();
            if (checkmarkRect) {
                var rootRect = getRootRect();
                // Checkmark is a square, meaning the client rect's width and height are identical once the animation completes.
                // However, the checkbox is initially hidden by setting the width to 0.
                // To account for an initial width of 0, we use the checkbox's height instead (which equals the end-state width)
                // when adding it to the root client rect's width.
                return {
                    bottom: rootRect.bottom,
                    height: rootRect.height,
                    left: rootRect.left,
                    right: rootRect.right,
                    top: rootRect.top,
                    width: rootRect.width + checkmarkRect.height,
                };
            }
        }
        return getRootRect();
    };
    /**
     * Begins the exit animation which leads to removal of the chip.
     */
    MDCChipFoundation.prototype.beginExit = function () {
        this.adapter.addClass(cssClasses.CHIP_EXIT);
    };
    MDCChipFoundation.prototype.handleClick = function () {
        this.adapter.notifyInteraction();
        this.setPrimaryActionFocusable(this.getFocusBehavior());
    };
    MDCChipFoundation.prototype.handleDoubleClick = function () {
        if (this.isEditable()) {
            this.startEditing();
        }
    };
    /**
     * Handles a transition end event on the root element.
     */
    MDCChipFoundation.prototype.handleTransitionEnd = function (evt) {
        var _this = this;
        // Handle transition end event on the chip when it is about to be removed.
        var shouldHandle = this.adapter.eventTargetHasClass(evt.target, cssClasses.CHIP_EXIT);
        var widthIsAnimating = evt.propertyName === 'width';
        var opacityIsAnimating = evt.propertyName === 'opacity';
        if (shouldHandle && opacityIsAnimating) {
            // See: https://css-tricks.com/using-css-transitions-auto-dimensions/#article-header-id-5
            var chipWidth_1 = this.adapter.getComputedStyleValue('width');
            // On the next frame (once we get the computed width), explicitly set the chip's width
            // to its current pixel width, so we aren't transitioning out of 'auto'.
            requestAnimationFrame(function () {
                _this.adapter.setStyleProperty('width', chipWidth_1);
                // To mitigate jitter, start transitioning padding and margin before width.
                _this.adapter.setStyleProperty('padding', '0');
                _this.adapter.setStyleProperty('margin', '0');
                // On the next frame (once width is explicitly set), transition width to 0.
                requestAnimationFrame(function () {
                    _this.adapter.setStyleProperty('width', '0');
                });
            });
            return;
        }
        if (shouldHandle && widthIsAnimating) {
            this.removeFocus();
            var removedAnnouncement = this.adapter.getAttribute(strings.REMOVED_ANNOUNCEMENT_ATTRIBUTE);
            this.adapter.notifyRemoval(removedAnnouncement);
        }
        // Handle a transition end event on the leading icon or checkmark, since the transition end event bubbles.
        if (!opacityIsAnimating) {
            return;
        }
        var shouldHideLeadingIcon = this.adapter.eventTargetHasClass(evt.target, cssClasses.LEADING_ICON) &&
            this.adapter.hasClass(cssClasses.SELECTED);
        var shouldShowLeadingIcon = this.adapter.eventTargetHasClass(evt.target, cssClasses.CHECKMARK) &&
            !this.adapter.hasClass(cssClasses.SELECTED);
        if (shouldHideLeadingIcon) {
            this.adapter.addClassToLeadingIcon(cssClasses.HIDDEN_LEADING_ICON);
            return;
        }
        if (shouldShowLeadingIcon) {
            this.adapter.removeClassFromLeadingIcon(cssClasses.HIDDEN_LEADING_ICON);
            return;
        }
    };
    MDCChipFoundation.prototype.handleFocusIn = function (evt) {
        // Early exit if the event doesn't come from the primary action
        if (!this.eventFromPrimaryAction(evt)) {
            return;
        }
        this.adapter.addClass(cssClasses.PRIMARY_ACTION_FOCUSED);
    };
    MDCChipFoundation.prototype.handleFocusOut = function (evt) {
        // Early exit if the event doesn't come from the primary action
        if (!this.eventFromPrimaryAction(evt)) {
            return;
        }
        if (this.isEditing()) {
            this.finishEditing();
        }
        this.adapter.removeClass(cssClasses.PRIMARY_ACTION_FOCUSED);
    };
    /**
     * Handles an interaction event on the trailing icon element. This is used to
     * prevent the ripple from activating on interaction with the trailing icon.
     */
    MDCChipFoundation.prototype.handleTrailingActionInteraction = function () {
        this.adapter.notifyTrailingIconInteraction();
        this.removeChip();
    };
    /**
     * Handles a keydown event from the root element.
     */
    MDCChipFoundation.prototype.handleKeydown = function (evt) {
        if (this.isEditing()) {
            if (this.shouldFinishEditing(evt)) {
                evt.preventDefault();
                this.finishEditing();
            }
            // When editing, the foundation should only handle key events that finish
            // the editing process.
            return;
        }
        if (this.isEditable()) {
            if (this.shouldStartEditing(evt)) {
                evt.preventDefault();
                this.startEditing();
            }
        }
        if (this.shouldNotifyInteraction(evt)) {
            this.adapter.notifyInteraction();
            this.setPrimaryActionFocusable(this.getFocusBehavior());
            return;
        }
        if (this.isDeleteAction(evt)) {
            evt.preventDefault();
            this.removeChip();
            return;
        }
        // Early exit if the key is not usable
        if (!navigationKeys.has(evt.key)) {
            return;
        }
        // Prevent default behavior for movement keys which could include scrolling
        evt.preventDefault();
        this.focusNextAction(evt.key, EventSource.PRIMARY);
    };
    MDCChipFoundation.prototype.handleTrailingActionNavigation = function (evt) {
        this.focusNextAction(evt.detail.key, EventSource.TRAILING);
    };
    /**
     * Called by the chip set to remove focus from the chip actions.
     */
    MDCChipFoundation.prototype.removeFocus = function () {
        this.adapter.setPrimaryActionAttr(strings.TAB_INDEX, '-1');
        this.adapter.removeTrailingActionFocus();
    };
    /**
     * Called by the chip set to focus the primary action.
     *
     */
    MDCChipFoundation.prototype.focusPrimaryAction = function () {
        this.setPrimaryActionFocusable(FocusBehavior.SHOULD_FOCUS);
    };
    /**
     * Called by the chip set to focus the trailing action (if present), otherwise
     * gives focus to the trailing action.
     */
    MDCChipFoundation.prototype.focusTrailingAction = function () {
        var trailingActionIsNavigable = this.adapter.isTrailingActionNavigable();
        if (trailingActionIsNavigable) {
            this.adapter.setPrimaryActionAttr(strings.TAB_INDEX, '-1');
            this.adapter.focusTrailingAction();
            return;
        }
        this.focusPrimaryAction();
    };
    MDCChipFoundation.prototype.setPrimaryActionFocusable = function (focusBehavior) {
        this.adapter.setPrimaryActionAttr(strings.TAB_INDEX, '0');
        if (focusBehavior === FocusBehavior.SHOULD_FOCUS) {
            this.adapter.focusPrimaryAction();
        }
        this.adapter.removeTrailingActionFocus();
    };
    MDCChipFoundation.prototype.getFocusBehavior = function () {
        if (this.shouldFocusPrimaryActionOnClick) {
            return FocusBehavior.SHOULD_FOCUS;
        }
        return FocusBehavior.SHOULD_NOT_FOCUS;
    };
    MDCChipFoundation.prototype.focusNextAction = function (key, source) {
        var isTrailingActionNavigable = this.adapter.isTrailingActionNavigable();
        var dir = this.getDirection(key);
        // Early exit if the key should jump chips
        if (jumpChipKeys.has(key) || !isTrailingActionNavigable) {
            this.adapter.notifyNavigation(key, source);
            return;
        }
        if (source === EventSource.PRIMARY && dir === Direction.RIGHT) {
            this.focusTrailingAction();
            return;
        }
        if (source === EventSource.TRAILING && dir === Direction.LEFT) {
            this.focusPrimaryAction();
            return;
        }
        this.adapter.notifyNavigation(key, EventSource.NONE);
    };
    MDCChipFoundation.prototype.getDirection = function (key) {
        var isRTL = this.adapter.isRTL();
        var isLeftKey = key === strings.ARROW_LEFT_KEY || key === strings.IE_ARROW_LEFT_KEY;
        var isRightKey = key === strings.ARROW_RIGHT_KEY || key === strings.IE_ARROW_RIGHT_KEY;
        if (!isRTL && isLeftKey || isRTL && isRightKey) {
            return Direction.LEFT;
        }
        return Direction.RIGHT;
    };
    MDCChipFoundation.prototype.removeChip = function () {
        if (this.shouldRemoveOnTrailingIconClick) {
            this.beginExit();
        }
    };
    MDCChipFoundation.prototype.shouldStartEditing = function (evt) {
        return this.eventFromPrimaryAction(evt) && evt.key === strings.ENTER_KEY;
    };
    MDCChipFoundation.prototype.shouldFinishEditing = function (evt) {
        return evt.key === strings.ENTER_KEY;
    };
    MDCChipFoundation.prototype.shouldNotifyInteraction = function (evt) {
        return evt.key === strings.ENTER_KEY || evt.key === strings.SPACEBAR_KEY;
    };
    MDCChipFoundation.prototype.isDeleteAction = function (evt) {
        var isDeletable = this.adapter.hasClass(cssClasses.DELETABLE);
        return isDeletable &&
            (evt.key === strings.BACKSPACE_KEY || evt.key === strings.DELETE_KEY ||
                evt.key === strings.IE_DELETE_KEY);
    };
    MDCChipFoundation.prototype.setSelectedImpl = function (selected) {
        if (selected) {
            this.adapter.addClass(cssClasses.SELECTED);
            this.adapter.setPrimaryActionAttr(strings.ARIA_CHECKED, 'true');
        }
        else {
            this.adapter.removeClass(cssClasses.SELECTED);
            this.adapter.setPrimaryActionAttr(strings.ARIA_CHECKED, 'false');
        }
    };
    MDCChipFoundation.prototype.notifySelection = function (selected) {
        this.adapter.notifySelection(selected, false);
    };
    MDCChipFoundation.prototype.notifyIgnoredSelection = function (selected) {
        this.adapter.notifySelection(selected, true);
    };
    MDCChipFoundation.prototype.eventFromPrimaryAction = function (evt) {
        return this.adapter.eventTargetHasClass(evt.target, cssClasses.PRIMARY_ACTION);
    };
    MDCChipFoundation.prototype.startEditing = function () {
        this.adapter.addClass(cssClasses.EDITING);
        this.adapter.notifyEditStart();
    };
    MDCChipFoundation.prototype.finishEditing = function () {
        this.adapter.removeClass(cssClasses.EDITING);
        this.adapter.notifyEditFinish();
    };
    return MDCChipFoundation;
}(MDCFoundation));
export { MDCChipFoundation };
// tslint:disable-next-line:no-default-export Needed for backward compatibility with MDC Web v0.44.0 and earlier.
export default MDCChipFoundation;
//# sourceMappingURL=foundation.js.map