text: /**
   * @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 { KEY, normalizeKey } from '../dom/keyboard';
  import { Corner } from '../menu-surface/constants';
  import { cssClasses, numbers, strings } from './constants';
  var MDCSelectFoundation = /** @class */ (function (_super) {
      __extends(MDCSelectFoundation, _super);
      /* istanbul ignore next: optional argument is not a branch statement */
      /**
       * @param adapter
       * @param foundationMap Map from subcomponent names to their subfoundations.
       */
      function MDCSelectFoundation(adapter, foundationMap) {
          if (foundationMap === void 0) { foundationMap = {}; }
          var _this = _super.call(this, __assign(__assign({}, MDCSelectFoundation.defaultAdapter), adapter)) || this;
          // Disabled state
          _this.disabled = false;
          // isMenuOpen is used to track the state of the menu by listening to the
          // MDCMenuSurface:closed event For reference, menu.open will return false if
          // the menu is still closing, but isMenuOpen returns false only after the menu
          // has closed
          _this.isMenuOpen = false;
          // By default, select is invalid if it is required but no value is selected.
          _this.useDefaultValidation = true;
          _this.customValidity = true;
          _this.lastSelectedIndex = numbers.UNSET_INDEX;
          _this.clickDebounceTimeout = 0;
          _this.recentlyClicked = false;
          _this.leadingIcon = foundationMap.leadingIcon;
          _this.helperText = foundationMap.helperText;
          return _this;
      }
      Object.defineProperty(MDCSelectFoundation, "cssClasses", {
          get: function () {
              return cssClasses;
          },
          enumerable: false,
          configurable: true
      });
      Object.defineProperty(MDCSelectFoundation, "numbers", {
          get: function () {
              return numbers;
          },
          enumerable: false,
          configurable: true
      });
      Object.defineProperty(MDCSelectFoundation, "strings", {
          get: function () {
              return strings;
          },
          enumerable: false,
          configurable: true
      });
      Object.defineProperty(MDCSelectFoundation, "defaultAdapter", {
          /**
           * See {@link MDCSelectAdapter} for typing information on parameters and return types.
           */
          get: function () {
              // tslint:disable:object-literal-sort-keys Methods should be in the same order as the adapter interface.
              return {
                  addClass: function () { return undefined; },
                  removeClass: function () { return undefined; },
                  hasClass: function () { return false; },
                  activateBottomLine: function () { return undefined; },
                  deactivateBottomLine: function () { return undefined; },
                  getSelectedIndex: function () { return -1; },
                  setSelectedIndex: function () { return undefined; },
                  hasLabel: function () { return false; },
                  floatLabel: function () { return undefined; },
                  getLabelWidth: function () { return 0; },
                  setLabelRequired: function () { return undefined; },
                  hasOutline: function () { return false; },
                  notchOutline: function () { return undefined; },
                  closeOutline: function () { return undefined; },
                  setRippleCenter: function () { return undefined; },
                  notifyChange: function () { return undefined; },
                  setSelectedText: function () { return undefined; },
                  isSelectAnchorFocused: function () { return false; },
                  getSelectAnchorAttr: function () { return ''; },
                  setSelectAnchorAttr: function () { return undefined; },
                  removeSelectAnchorAttr: function () { return undefined; },
                  addMenuClass: function () { return undefined; },
                  removeMenuClass: function () { return undefined; },
                  openMenu: function () { return undefined; },
                  closeMenu: function () { return undefined; },
                  getAnchorElement: function () { return null; },
                  setMenuAnchorElement: function () { return undefined; },
                  setMenuAnchorCorner: function () { return undefined; },
                  setMenuWrapFocus: function () { return undefined; },
                  focusMenuItemAtIndex: function () { return undefined; },
                  getMenuItemCount: function () { return 0; },
                  getMenuItemValues: function () { return []; },
                  getMenuItemTextAtIndex: function () { return ''; },
                  isTypeaheadInProgress: function () { return false; },
                  typeaheadMatchItem: function () { return -1; },
              };
              // tslint:enable:object-literal-sort-keys
          },
          enumerable: false,
          configurable: true
      });
      /** Returns the index of the currently selected menu item, or -1 if none. */
      MDCSelectFoundation.prototype.getSelectedIndex = function () {
          return this.adapter.getSelectedIndex();
      };
      MDCSelectFoundation.prototype.setSelectedIndex = function (index, closeMenu, skipNotify) {
          if (closeMenu === void 0) { closeMenu = false; }
          if (skipNotify === void 0) { skipNotify = false; }
          if (index >= this.adapter.getMenuItemCount()) {
              return;
          }
          if (index === numbers.UNSET_INDEX) {
              this.adapter.setSelectedText('');
          }
          else {
              this.adapter.setSelectedText(this.adapter.getMenuItemTextAtIndex(index).trim());
          }
          this.adapter.setSelectedIndex(index);
          if (closeMenu) {
              this.adapter.closeMenu();
          }
          if (!skipNotify && this.lastSelectedIndex !== index) {
              this.handleChange();
          }
          this.lastSelectedIndex = index;
      };
      MDCSelectFoundation.prototype.setValue = function (value, skipNotify) {
          if (skipNotify === void 0) { skipNotify = false; }
          var index = this.adapter.getMenuItemValues().indexOf(value);
          this.setSelectedIndex(index, /** closeMenu */ false, skipNotify);
      };
      MDCSelectFoundation.prototype.getValue = function () {
          var index = this.adapter.getSelectedIndex();
          var menuItemValues = this.adapter.getMenuItemValues();
          return index !== numbers.UNSET_INDEX ? menuItemValues[index] : '';
      };
      MDCSelectFoundation.prototype.getDisabled = function () {
          return this.disabled;
      };
      MDCSelectFoundation.prototype.setDisabled = function (isDisabled) {
          this.disabled = isDisabled;
          if (this.disabled) {
              this.adapter.addClass(cssClasses.DISABLED);
              this.adapter.closeMenu();
          }
          else {
              this.adapter.removeClass(cssClasses.DISABLED);
          }
          if (this.leadingIcon) {
              this.leadingIcon.setDisabled(this.disabled);
          }
          if (this.disabled) {
              // Prevent click events from focusing select. Simply pointer-events: none
              // is not enough since screenreader clicks may bypass this.
              this.adapter.removeSelectAnchorAttr('tabindex');
          }
          else {
              this.adapter.setSelectAnchorAttr('tabindex', '0');
          }
          this.adapter.setSelectAnchorAttr('aria-disabled', this.disabled.toString());
      };
      /** Opens the menu. */
      MDCSelectFoundation.prototype.openMenu = function () {
          this.adapter.addClass(cssClasses.ACTIVATED);
          this.adapter.openMenu();
          this.isMenuOpen = true;
          this.adapter.setSelectAnchorAttr('aria-expanded', 'true');
      };
      /**
       * @param content Sets the content of the helper text.
       */
      MDCSelectFoundation.prototype.setHelperTextContent = function (content) {
          if (this.helperText) {
              this.helperText.setContent(content);
          }
      };
      /**
       * Re-calculates if the notched outline should be notched and if the label
       * should float.
       */
      MDCSelectFoundation.prototype.layout = function () {
          if (this.adapter.hasLabel()) {
              var optionHasValue = this.getValue().length > 0;
              var isFocused = this.adapter.hasClass(cssClasses.FOCUSED);
              var shouldFloatAndNotch = optionHasValue || isFocused;
              var isRequired = this.adapter.hasClass(cssClasses.REQUIRED);
              this.notchOutline(shouldFloatAndNotch);
              this.adapter.floatLabel(shouldFloatAndNotch);
              this.adapter.setLabelRequired(isRequired);
          }
      };
      /**
       * Synchronizes the list of options with the state of the foundation. Call
       * this whenever menu options are dynamically updated.
       */
      MDCSelectFoundation.prototype.layoutOptions = function () {
          var menuItemValues = this.adapter.getMenuItemValues();
          var selectedIndex = menuItemValues.indexOf(this.getValue());
          this.setSelectedIndex(selectedIndex, /** closeMenu */ false, /** skipNotify */ true);
      };
      MDCSelectFoundation.prototype.handleMenuOpened = function () {
          if (this.adapter.getMenuItemValues().length === 0) {
              return;
          }
          // Menu should open to the last selected element, should open to first menu item otherwise.
          var selectedIndex = this.getSelectedIndex();
          var focusItemIndex = selectedIndex >= 0 ? selectedIndex : 0;
          this.adapter.focusMenuItemAtIndex(focusItemIndex);
      };
      MDCSelectFoundation.prototype.handleMenuClosing = function () {
          this.adapter.setSelectAnchorAttr('aria-expanded', 'false');
      };
      MDCSelectFoundation.prototype.handleMenuClosed = function () {
          this.adapter.removeClass(cssClasses.ACTIVATED);
          this.isMenuOpen = false;
          // Unfocus the select if menu is closed without a selection
          if (!this.adapter.isSelectAnchorFocused()) {
              this.blur();
          }
      };
      /**
       * Handles value changes, via change event or programmatic updates.
       */
      MDCSelectFoundation.prototype.handleChange = function () {
          this.layout();
          this.adapter.notifyChange(this.getValue());
          var isRequired = this.adapter.hasClass(cssClasses.REQUIRED);
          if (isRequired && this.useDefaultValidation) {
              this.setValid(this.isValid());
          }
      };
      MDCSelectFoundation.prototype.handleMenuItemAction = function (index) {
          this.setSelectedIndex(index, /** closeMenu */ true);
      };
      /**
       * Handles focus events from select element.
       */
      MDCSelectFoundation.prototype.handleFocus = function () {
          this.adapter.addClass(cssClasses.FOCUSED);
          this.layout();
          this.adapter.activateBottomLine();
      };
      /**
       * Handles blur events from select element.
       */
      MDCSelectFoundation.prototype.handleBlur = function () {
          if (this.isMenuOpen) {
              return;
          }
          this.blur();
      };
      MDCSelectFoundation.prototype.handleClick = function (normalizedX) {
          if (this.disabled || this.recentlyClicked) {
              return;
          }
          this.setClickDebounceTimeout();
          if (this.isMenuOpen) {
              this.adapter.closeMenu();
              return;
          }
          this.adapter.setRippleCenter(normalizedX);
          this.openMenu();
      };
      /**
       * Handles keydown events on select element. Depending on the type of
       * character typed, does typeahead matching or opens menu.
       */
      MDCSelectFoundation.prototype.handleKeydown = function (event) {
          if (this.isMenuOpen || !this.adapter.hasClass(cssClasses.FOCUSED)) {
              return;
          }
          var isEnter = normalizeKey(event) === KEY.ENTER;
          var isSpace = normalizeKey(event) === KEY.SPACEBAR;
          var arrowUp = normalizeKey(event) === KEY.ARROW_UP;
          var arrowDown = normalizeKey(event) === KEY.ARROW_DOWN;
          var isModifier = event.ctrlKey || event.metaKey;
          // Typeahead
          if (!isModifier &&
              (!isSpace && event.key && event.key.length === 1 ||
                  isSpace && this.adapter.isTypeaheadInProgress())) {
              var key = isSpace ? ' ' : event.key;
              var typeaheadNextIndex = this.adapter.typeaheadMatchItem(key, this.getSelectedIndex());
              if (typeaheadNextIndex >= 0) {
                  this.setSelectedIndex(typeaheadNextIndex);
              }
              event.preventDefault();
              return;
          }
          if (!isEnter && !isSpace && !arrowUp && !arrowDown) {
              return;
          }
          // Increment/decrement index as necessary and open menu.
          if (arrowUp && this.getSelectedIndex() > 0) {
              this.setSelectedIndex(this.getSelectedIndex() - 1);
          }
          else if (arrowDown &&
              this.getSelectedIndex() < this.adapter.getMenuItemCount() - 1) {
              this.setSelectedIndex(this.getSelectedIndex() + 1);
          }
          this.openMenu();
          event.preventDefault();
      };
      /**
       * Opens/closes the notched outline.
       */
      MDCSelectFoundation.prototype.notchOutline = function (openNotch) {
          if (!this.adapter.hasOutline()) {
              return;
          }
          var isFocused = this.adapter.hasClass(cssClasses.FOCUSED);
          if (openNotch) {
              var labelScale = numbers.LABEL_SCALE;
              var labelWidth = this.adapter.getLabelWidth() * labelScale;
              this.adapter.notchOutline(labelWidth);
          }
          else if (!isFocused) {
              this.adapter.closeOutline();
          }
      };
      /**
       * Sets the aria label of the leading icon.
       */
      MDCSelectFoundation.prototype.setLeadingIconAriaLabel = function (label) {
          if (this.leadingIcon) {
              this.leadingIcon.setAriaLabel(label);
          }
      };
      /**
       * Sets the text content of the leading icon.
       */
      MDCSelectFoundation.prototype.setLeadingIconContent = function (content) {
          if (this.leadingIcon) {
              this.leadingIcon.setContent(content);
          }
      };
      MDCSelectFoundation.prototype.getUseDefaultValidation = function () {
          return this.useDefaultValidation;
      };
      MDCSelectFoundation.prototype.setUseDefaultValidation = function (useDefaultValidation) {
          this.useDefaultValidation = useDefaultValidation;
      };
      MDCSelectFoundation.prototype.setValid = function (isValid) {
          if (!this.useDefaultValidation) {
              this.customValidity = isValid;
          }
          this.adapter.setSelectAnchorAttr('aria-invalid', (!isValid).toString());
          if (isValid) {
              this.adapter.removeClass(cssClasses.INVALID);
              this.adapter.removeMenuClass(cssClasses.MENU_INVALID);
          }
          else {
              this.adapter.addClass(cssClasses.INVALID);
              this.adapter.addMenuClass(cssClasses.MENU_INVALID);
          }
          this.syncHelperTextValidity(isValid);
      };
      MDCSelectFoundation.prototype.isValid = function () {
          if (this.useDefaultValidation &&
              this.adapter.hasClass(cssClasses.REQUIRED) &&
              !this.adapter.hasClass(cssClasses.DISABLED)) {
              // See notes for required attribute under https://www.w3.org/TR/html52/sec-forms.html#the-select-element
              // TL;DR: Invalid if no index is selected, or if the first index is selected and has an empty value.
              return this.getSelectedIndex() !== numbers.UNSET_INDEX &&
                  (this.getSelectedIndex() !== 0 || Boolean(this.getValue()));
          }
          return this.customValidity;
      };
      MDCSelectFoundation.prototype.setRequired = function (isRequired) {
          if (isRequired) {
              this.adapter.addClass(cssClasses.REQUIRED);
          }
          else {
              this.adapter.removeClass(cssClasses.REQUIRED);
          }
          this.adapter.setSelectAnchorAttr('aria-required', isRequired.toString());
          this.adapter.setLabelRequired(isRequired);
      };
      MDCSelectFoundation.prototype.getRequired = function () {
          return this.adapter.getSelectAnchorAttr('aria-required') === 'true';
      };
      MDCSelectFoundation.prototype.init = function () {
          var anchorEl = this.adapter.getAnchorElement();
          if (anchorEl) {
              this.adapter.setMenuAnchorElement(anchorEl);
              this.adapter.setMenuAnchorCorner(Corner.BOTTOM_START);
          }
          this.adapter.setMenuWrapFocus(false);
          this.setDisabled(this.adapter.hasClass(cssClasses.DISABLED));
          this.syncHelperTextValidity(!this.adapter.hasClass(cssClasses.INVALID));
          this.layout();
          this.layoutOptions();
      };
      /**
       * Unfocuses the select component.
       */
      MDCSelectFoundation.prototype.blur = function () {
          this.adapter.removeClass(cssClasses.FOCUSED);
          this.layout();
          this.adapter.deactivateBottomLine();
          var isRequired = this.adapter.hasClass(cssClasses.REQUIRED);
          if (isRequired && this.useDefaultValidation) {
              this.setValid(this.isValid());
          }
      };
      MDCSelectFoundation.prototype.syncHelperTextValidity = function (isValid) {
          if (!this.helperText) {
              return;
          }
          this.helperText.setValidity(isValid);
          var helperTextVisible = this.helperText.isVisible();
          var helperTextId = this.helperText.getId();
          if (helperTextVisible && helperTextId) {
              this.adapter.setSelectAnchorAttr(strings.ARIA_DESCRIBEDBY, helperTextId);
          }
          else {
              // Needed because screenreaders will read labels pointed to by
              // `aria-describedby` even if they are `aria-hidden`.
              this.adapter.removeSelectAnchorAttr(strings.ARIA_DESCRIBEDBY);
          }
      };
      MDCSelectFoundation.prototype.setClickDebounceTimeout = function () {
          var _this = this;
          clearTimeout(this.clickDebounceTimeout);
          this.clickDebounceTimeout = setTimeout(function () {
              _this.recentlyClicked = false;
          }, numbers.CLICK_DEBOUNCE_TIMEOUT_MS);
          this.recentlyClicked = true;
      };
      return MDCSelectFoundation;
  }(MDCFoundation));
  export { MDCSelectFoundation };
  // tslint:disable-next-line:no-default-export Needed for backward compatibility with MDC Web v0.44.0 and earlier.
  export default MDCSelectFoundation;
  //# sourceMappingURL=foundation.js.map