{"version":3,"sources":["components/multi-select/multi-select.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,EAAwC,cAAc,EAAE,MAAM,aAAa,CAAC;AAGnF,OAAO,UAAwC,MAAM,sBAAsB,CAAC;AAC5E,OAAO,iBAAiB,MAAM,qBAAqB,CAAC;AAGpD,OAAO,EAAE,qBAAqB,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAI3F;;;;;;;;;;;;GAYG;AACH,cACM,aAAc,SAAQ,UAAU;IAEpC,UAAU,MAAC;IAEX;;OAEG;IACH,OAAO,CAAC,mBAAmB,CAAK;IAEhC;;OAEG;IAEH,OAAO,CAAC,gBAAgB,CAAe;IAEvC;;OAEG;IAEH,OAAO,CAAC,oBAAoB,CAAe;IAE3C;;OAEG;IAEH,OAAO,CAAC,gBAAgB,CAAoB;IAE5C;;OAEG;IAEH,OAAO,CAAC,YAAY,CAAe;IAEnC,SAAS,CAAC,sBAAsB,CAAC,YAAY,CAAC,EAAE,iBAAiB;IAKjE,SAAS,CAAC,mBAAmB,CAAC,YAAY,CAAC,EAAE,iBAAiB;IAoB9D,SAAS,CAAC,iBAAiB,CAAC,KAAK,EAAE,UAAU;IAoB7C;;OAEG;IACH,SAAS,CAAC,oBAAoB,CAAC,KAAK,EAAE,aAAa;IA0BnD;;;OAGG;IAEH,SAAS,CAAC,6BAA6B,CAAC,KAAK,EAAE,aAAa;IA8B5D,SAAS,CAAC,8BAA8B;IAgBxC;;OAEG;IACH,SAAS,CAAC,qBAAqB,IAAI,cAAc;IAgBjD,SAAS,CAAC,8BAA8B,IAAI,cAAc,GAAG,IAAI;IAWjE;;OAEG;IACH,SAAS,CAAC,YAAY;IAoBtB;;;OAGG;IACH,SAAS,CAAC,SAAS,CAAC,SAAS,EAAE,MAAM;IAwBrC;;OAEG;IACH,SAAS,CAAC,8BAA8B;IAWxC;;OAEG;IAEH,mBAAmB,SAAM;IAEzB;;OAEG;IAEH,2BAA2B,SAAyB;IAEpD;;OAEG;IAEH,0BAA0B,SAA2B;IAErD,YAAY,CAAC,iBAAiB,KAAA;IAoB9B;;;OAGG;IACH,OAAO,CAAC,MAAM,KAAK,wBAAwB,GAE1C;IAED;;OAEG;IACH,MAAM,KAAK,uBAAuB,WAEjC;IAED;;;OAGG;IACH,MAAM,KAAK,YAAY,WAEtB;IAED;;OAEG;IACH,MAAM,KAAK,oBAAoB,WAE9B;IAED;;OAEG;IACH,MAAM,KAAK,mBAAmB,WAE7B;IAED;;OAEG;IACH,MAAM,KAAK,oBAAoB,WAE9B;IAED;;;OAGG;IACH,MAAM,KAAK,iBAAiB,WAE3B;IAED;;OAEG;IACH,MAAM,KAAK,WAAW,WAErB;IAED;;;OAGG;IACH,MAAM,KAAK,iBAAiB,WAE3B;IAED;;OAEG;IACH,MAAM,KAAK,WAAW,WAErB;IAED,MAAM,CAAC,MAAM,MAAU;CACxB;AAED,eAAe,aAAa,CAAC","file":"multi-select.d.ts","sourcesContent":["/**\n * @license\n *\n * Copyright IBM Corp. 2020, 2021\n *\n * This source code is licensed under the Apache-2.0 license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\nimport settings from 'carbon-components/es/globals/js/settings';\nimport { html, property, query, customElement, TemplateResult } from 'lit-element';\nimport Close16 from '@carbon/icons/lib/close/16';\nimport { filter, forEach, indexOf } from '../../globals/internal/collection-helpers';\nimport BXDropdown, { DROPDOWN_KEYBOARD_ACTION } from '../dropdown/dropdown';\nimport BXMultiSelectItem from './multi-select-item';\nimport styles from './multi-select.scss';\n\nexport { DROPDOWN_COLOR_SCHEME, DROPDOWN_SIZE, DROPDOWN_TYPE } from '../dropdown/dropdown';\n\nconst { prefix } = settings;\n\n/**\n * Multi select.\n * @element bx-multi-select\n * @fires bx-multi-select-beingselected\n *   The custom event fired before a multi select item is selected upon a user gesture.\n *   Cancellation of this event stops changing the user-initiated selection.\n * @fires bx-multi-select-selected - The custom event fired after a multi select item is selected upon a user gesture.\n * @fires bx-multi-select-beingtoggled\n *   The custom event fired before the open state of this multi select is toggled upon a user gesture.\n *   Cancellation of this event stops the user-initiated toggling.\n * @fires bx-multi-select-toggled\n *   The custom event fired after the open state of this multi select is toggled upon a user gesture.\n */\n@customElement(`${prefix}-multi-select`)\nclass BXMultiSelect extends BXDropdown {\n  @property({ type: Boolean })\n  filterable;\n\n  /**\n   * The count of selected items.\n   */\n  private _selectedItemsCount = 0;\n\n  /**\n   * The clear button.\n   */\n  @query('#clear-button')\n  private _clearButtonNode!: HTMLElement;\n\n  /**\n   * The selection button.\n   */\n  @query('#selection-button')\n  private _selectionButtonNode!: HTMLElement;\n\n  /**\n   * The `<input>` for filtering.\n   */\n  @query('input')\n  private _filterInputNode!: HTMLInputElement;\n\n  /**\n   * The trigger button.\n   */\n  @query(`.${prefix}--list-box__field`)\n  private _triggerNode!: HTMLElement;\n\n  protected _selectionShouldChange(itemToSelect?: BXMultiSelectItem) {\n    // If we are selecting an item, assumes we always toggle\n    return Boolean(this.value || itemToSelect);\n  }\n\n  protected _selectionDidChange(itemToSelect?: BXMultiSelectItem) {\n    if (itemToSelect) {\n      itemToSelect.selected = !itemToSelect.selected;\n      this._assistiveStatusText = itemToSelect.selected ? this.selectedItemAssistiveText : this.unselectedItemAssistiveText;\n    } else {\n      forEach(this.querySelectorAll((this.constructor as typeof BXMultiSelect).selectorItemSelected), item => {\n        (item as BXMultiSelectItem).selected = false;\n      });\n      this._handleUserInitiatedToggle(false);\n      this._assistiveStatusText = this.unselectedAllAssistiveText;\n    }\n    // Change in `.selected` hasn't been reflected to the corresponding attribute yet\n    this.value = filter(\n      this.querySelectorAll((this.constructor as typeof BXMultiSelect).selectorItem),\n      item => (item as BXMultiSelectItem).selected\n    )\n      .map(item => (item as BXMultiSelectItem).value)\n      .join(',');\n  }\n\n  protected _handleClickInner(event: MouseEvent) {\n    if (this._selectionButtonNode?.contains(event.target as Node)) {\n      this._handleUserInitiatedSelectItem();\n      if (this.filterable) {\n        this._filterInputNode.focus();\n      } else {\n        this._triggerNode.focus();\n      }\n    } else if (this._clearButtonNode?.contains(event.target as Node)) {\n      this._handleUserInitiatedClearInput();\n    } else {\n      const shouldIgnoreClickInner = elem =>\n        elem.closest && elem.closest((this.constructor as typeof BXMultiSelect).selectorIgnoreClickInner);\n      if (!event.composedPath().some(shouldIgnoreClickInner)) {\n        super._handleClickInner(event);\n      }\n      if (this.filterable) this._filterInputNode.focus();\n    }\n  }\n\n  /**\n   * Handler for the `keypress` event, ensures filter still works upon entering space\n   */\n  protected _handleKeypressInner(event: KeyboardEvent) {\n    const { key } = event;\n    const action = (this.constructor as typeof BXDropdown).getAction(key);\n    const { TRIGGERING } = DROPDOWN_KEYBOARD_ACTION;\n\n    if (\n      this._clearButtonNode?.contains(event.target as Node) &&\n      // Space key should be handled by `<input>` unless \"clear selection\" button has focus\n      (action === TRIGGERING || key === ' ')\n    ) {\n      this._handleUserInitiatedClearInput();\n    } else if (this._selectionButtonNode?.contains(event.target as Node)) {\n      this._handleUserInitiatedSelectItem();\n      this.open = true;\n      if (this.filterable) {\n        this._filterInputNode.focus();\n      } else {\n        this._triggerNode.focus();\n      }\n    } else if (this.filterable) {\n      this._handleKeypressInnerFlterable(event);\n    } else {\n      super._handleKeypressInner(event);\n    }\n  }\n\n  /**\n   * Special andler for the `keypress` event, ensures space selection for filterable\n   * variation is disabled\n   */\n\n  protected _handleKeypressInnerFlterable(event: KeyboardEvent) {\n    const { key } = event;\n    const action = (this.constructor as typeof BXDropdown).getAction(key);\n    if (!this.open) {\n      switch (action) {\n        case DROPDOWN_KEYBOARD_ACTION.TRIGGERING:\n          this._handleUserInitiatedToggle(true);\n          break;\n        default:\n          break;\n      }\n    } else {\n      switch (key) {\n        case 'Enter':\n          {\n            const constructor = this.constructor as typeof BXDropdown;\n            const highlightedItem = this.querySelector(constructor.selectorItemHighlighted) as BXMultiSelectItem;\n            if (highlightedItem) {\n              this._handleUserInitiatedSelectItem(highlightedItem);\n            } else {\n              this._handleUserInitiatedToggle(false);\n            }\n          }\n          break;\n        default:\n          break;\n      }\n    }\n  }\n\n  protected _renderPrecedingTriggerContent() {\n    const { clearSelectionLabel, _selectedItemsCount: selectedItemsCount } = this;\n    return selectedItemsCount === 0\n      ? undefined\n      : html`\n          <div\n            id=\"selection-button\"\n            role=\"button\"\n            class=\"${prefix}--list-box__selection ${prefix}--list-box__selection--multi ${prefix}--tag--filter\"\n            tabindex=\"0\"\n            title=\"${clearSelectionLabel}\">\n            ${selectedItemsCount} ${Close16({ 'aria-label': clearSelectionLabel })}\n          </div>\n        `;\n  }\n\n  /**\n    @returns The main content of the trigger button.\n   */\n  protected _renderTriggerContent(): TemplateResult {\n    const { triggerContent, _selectedItemContent: selectedItemContent } = this;\n    return !this.filterable\n      ? html` <span id=\"trigger-label\" class=\"${prefix}--list-box__label\">${selectedItemContent || triggerContent}</span> `\n      : html`\n          <input\n            id=\"trigger-label\"\n            class=\"${prefix}--text-input\"\n            placeholder=\"${triggerContent}\"\n            role=\"combobox\"\n            aria-controls=\"menu-body\"\n            aria-autocomplete=\"list\"\n            @input=\"${this._handleInput}\" />\n        `;\n  }\n\n  protected _renderFollowingTriggerContent(): TemplateResult | void {\n    const { clearSelectionLabel, _filterInputNode: filterInputNode } = this;\n    return filterInputNode && filterInputNode.value.length > 0 && this.filterable\n      ? html`\n          <div id=\"clear-button\" role=\"button\" class=\"${prefix}--list-box__selection\" tabindex=\"0\" title=\"${clearSelectionLabel}\">\n            ${Close16({ 'aria-label': clearSelectionLabel })}\n          </div>\n        `\n      : undefined;\n  }\n\n  /**\n   * Handles `input` event on the `<input>` for filtering.\n   */\n  protected _handleInput() {\n    const items = this.querySelectorAll((this.constructor as typeof BXMultiSelect).selectorItem);\n    const inputValue = this._filterInputNode.value.toLocaleLowerCase();\n\n    if (!this.open) this.open = true;\n\n    forEach(items, item => {\n      const itemValue = (item as HTMLElement).innerText.toLocaleLowerCase();\n\n      if (!itemValue.includes(inputValue)) {\n        (item as BXMultiSelectItem).setAttribute('filtered', '');\n        (item as BXMultiSelectItem).removeAttribute('highlighted');\n      } else {\n        (item as BXMultiSelectItem).removeAttribute('filtered');\n      }\n    });\n\n    this.requestUpdate();\n  }\n\n  /**\n   * Navigate through dropdown items.\n   * @param direction `-1` to navigate backward, `1` to navigate forward.\n   */\n  protected _navigate(direction: number) {\n    if (!this.filterable) {\n      this._triggerNode.focus();\n      super._navigate(direction);\n    } else {\n      // only navigate through remaining item\n      const constructor = this.constructor as typeof BXMultiSelect;\n      const items = this.querySelectorAll(constructor.selectorItemResults);\n      const highlightedItem = this.querySelector(constructor.selectorItemHighlighted);\n      const highlightedIndex = indexOf(items, highlightedItem!);\n\n      let nextIndex = highlightedIndex + direction;\n      if (nextIndex < 0) {\n        nextIndex = items.length - 1;\n      }\n      if (nextIndex >= items.length) {\n        nextIndex = 0;\n      }\n      forEach(items, (item, i) => {\n        (item as BXMultiSelectItem).highlighted = i === nextIndex;\n      });\n    }\n  }\n\n  /**\n   * Handles user-initiated clearing the `<input>` for filtering.\n   */\n  protected _handleUserInitiatedClearInput() {\n    const constructor = this.constructor as typeof BXMultiSelect;\n    const items = this.querySelectorAll(constructor.selectorItemFiltered);\n    this._filterInputNode.value = '';\n    this.open = true;\n    this._filterInputNode.focus();\n    forEach(items, item => {\n      (item as BXMultiSelectItem).removeAttribute('filtered');\n    });\n  }\n\n  /**\n   * The `aria-label` attribute for the icon to clear selection.\n   */\n  @property({ attribute: 'clear-selection-label' })\n  clearSelectionLabel = '';\n\n  /**\n   * An assistive text for screen reader to announce, telling that an item is unselected.\n   */\n  @property({ attribute: 'unselected-item-assistive-text' })\n  unselectedItemAssistiveText = 'Unselected an item.';\n\n  /**\n   * An assistive text for screen reader to announce, telling that all items are unselected.\n   */\n  @property({ attribute: 'unselected-all-assistive-text' })\n  unselectedAllAssistiveText = 'Unselected all items.';\n\n  shouldUpdate(changedProperties) {\n    const { selectorItem } = this.constructor as typeof BXMultiSelect;\n    if (changedProperties.has('size')) {\n      forEach(this.querySelectorAll(selectorItem), elem => {\n        (elem as BXMultiSelectItem).size = this.size;\n      });\n    }\n    if (changedProperties.has('value')) {\n      const { value } = this;\n      const values = !value ? [] : value.split(',');\n      // Updates selection beforehand because our rendering logic for `<bx-multi-select>` looks for selected items via `qSA()`\n      const items = this.querySelectorAll(selectorItem);\n      forEach(items, elem => {\n        (elem as BXMultiSelectItem).selected = values.indexOf((elem as BXMultiSelectItem).value) >= 0;\n      });\n      this._selectedItemsCount = filter(items, elem => values.indexOf((elem as BXMultiSelectItem).value) >= 0).length;\n    }\n    return true;\n  }\n\n  /**\n   * A selector to ignore the `click` events from.\n   * Primary for the checkbox label where the `click` event will happen from the associated check box.\n   */\n  private static get selectorIgnoreClickInner() {\n    return `.${prefix}--checkbox-label`;\n  }\n\n  /**\n   * A selector that will return highlighted items.\n   */\n  static get selectorItemHighlighted() {\n    return `${prefix}-multi-select-item[highlighted]`;\n  }\n\n  /**\n   * A selector that will return multi select items.\n   * We use a separate property from `.itemTagName` due to the nature in difference of tag name vs. selector.\n   */\n  static get selectorItem() {\n    return `${prefix}-multi-select-item`;\n  }\n\n  /**\n   * A selector that will return remaining items after a filter.\n   */\n  static get selectorItemFiltered() {\n    return `${prefix}-multi-select-item[filtered]`;\n  }\n\n  /**\n   * A selector that will return remaining items after a filter.\n   */\n  static get selectorItemResults() {\n    return `${prefix}-multi-select-item:not([filtered])`;\n  }\n\n  /**\n   * A selector that will return selected items.\n   */\n  static get selectorItemSelected() {\n    return `${prefix}-multi-select-item[selected]`;\n  }\n\n  /**\n   * The name of the custom event fired before this multi select item is being toggled upon a user gesture.\n   * Cancellation of this event stops the user-initiated action of toggling this multi select item.\n   */\n  static get eventBeforeToggle() {\n    return `${prefix}-multi-select-beingtoggled`;\n  }\n\n  /**\n   * The name of the custom event fired after this multi select item is toggled upon a user gesture.\n   */\n  static get eventToggle() {\n    return `${prefix}-multi-select-toggled`;\n  }\n\n  /**\n   * The name of the custom event fired before a multi select item is selected upon a user gesture.\n   * Cancellation of this event stops changing the user-initiated selection.\n   */\n  static get eventBeforeSelect() {\n    return `${prefix}-multi-select-beingselected`;\n  }\n\n  /**\n   * The name of the custom event fired after a a multi select item is selected upon a user gesture.\n   */\n  static get eventSelect() {\n    return `${prefix}-multi-select-selected`;\n  }\n\n  static styles = styles;\n}\n\nexport default BXMultiSelect;\n"]}