{"version":3,"sources":["components/combo-box/combo-box.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAI1C,OAAO,UAAwC,MAAM,sBAAsB,CAAC;AAC5E,OAAO,cAAc,MAAM,kBAAkB,CAAC;AAG9C,OAAO,EAAE,qBAAqB,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAI3F;;;;;;;;;;;GAWG;AACH,cACM,UAAW,SAAQ,UAAU;IACjC;;OAEG;IACH,SAAS,CAAC,iBAAiB,SAAM;IAEjC,SAAS,CAAC,yBAAyB,UAAS;IAE5C;;OAEG;IAEH,OAAO,CAAC,gBAAgB,CAAoB;IAE5C;;OAEG;IAEH,OAAO,CAAC,SAAS,CAAe;IAEhC;;OAEG;IAEH,OAAO,CAAC,oBAAoB,CAAe;IAE3C;;;OAGG;IACH,SAAS,CAAC,sBAAsB,CAAC,IAAI,KAAA;IAKrC;;;;;OAKG;IACH,SAAS,CAAC,mBAAmB,CAAC,IAAI,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO;IAK/E;;OAEG;IACH,SAAS,CAAC,YAAY;IA8BtB,SAAS,CAAC,iBAAiB,CAAC,KAAK,EAAE,UAAU;IAQ7C,SAAS,CAAC,oBAAoB,CAAC,KAAK,EAAE,aAAa;IAenD;;OAEG;IACH,SAAS,CAAC,8BAA8B;IASxC,SAAS,CAAC,8BAA8B,CAAC,IAAI,CAAC,EAAE,cAAc;IAiB9D,SAAS,CAAC,mBAAmB,CAAC,YAAY,CAAC,EAAE,cAAc;IAY3D,SAAS,CAAC,qBAAqB,IAAI,cAAc;IAiBjD,SAAS,CAAC,8BAA8B,IAAI,cAAc,GAAG,IAAI;IAgBjE;;OAEG;IAEH,mBAAmB,SAAM;IAEzB;;OAEG;IAEH,UAAU,SAAM;IAEhB;;OAEG;IAEH,WAAW,EAAG,CAAC,IAAI,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC;IAEnE,YAAY,CAAC,iBAAiB,KAAA;IAS9B,OAAO;IAQP,MAAM,CAAC,YAAY,cAAsB;IAEzC;;OAEG;IACH,MAAM,KAAK,uBAAuB,WAEjC;IAED;;OAEG;IACH,MAAM,KAAK,YAAY,WAEtB;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,UAAU,CAAC","file":"combo-box.d.ts","sourcesContent":["/**\n * @license\n *\n * Copyright IBM Corp. 2019, 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 { TemplateResult } from 'lit-html';\nimport { html, property, query, customElement } from 'lit-element';\nimport Close16 from '@carbon/icons/lib/close/16';\nimport { findIndex, forEach } from '../../globals/internal/collection-helpers';\nimport BXDropdown, { DROPDOWN_KEYBOARD_ACTION } from '../dropdown/dropdown';\nimport BXComboBoxItem from './combo-box-item';\nimport styles from './combo-box.scss';\n\nexport { DROPDOWN_COLOR_SCHEME, DROPDOWN_SIZE, DROPDOWN_TYPE } from '../dropdown/dropdown';\n\nconst { prefix } = settings;\n\n/**\n * Combo box.\n * @element bx-combo-box\n * @fires bx-combo-box-beingselected\n *   The custom event fired before a combo box item is selected upon a user gesture.\n *   Cancellation of this event stops changing the user-initiated selection.\n * @fires bx-combo-box-beingtoggled\n *   The custom event fired before the open state of this combo box is toggled upon a user gesture.\n *   Cancellation of this event stops the user-initiated toggling.\n * @fires bx-combo-box-selected - The custom event fired after a combo box item is selected upon a user gesture.\n * @fires bx-combo-box-toggled - The custom event fired after the open state of this combo box is toggled upon a user gesture.\n */\n@customElement(`${prefix}-combo-box`)\nclass BXComboBox extends BXDropdown {\n  /**\n   * The text content that should be set to the `<input>` for filtering.\n   */\n  protected _filterInputValue = '';\n\n  protected _shouldTriggerBeFocusable = false;\n\n  /**\n   * The `<input>` for filtering.\n   */\n  @query('input')\n  private _filterInputNode!: HTMLInputElement;\n\n  /**\n   * The menu containing all selectable items.\n   */\n  @query('#menu-body')\n  private _itemMenu!: HTMLElement;\n\n  /**\n   * The selection button.\n   */\n  @query('#selection-button')\n  private _selectionButtonNode!: HTMLElement;\n\n  /**\n   * @param item A combo box item.\n   * @returns `true` if the given combo box item matches the query text user types.\n   */\n  protected _testItemWithQueryText(item) {\n    return (this.itemMatches || this._defaultItemMatches)(item, this._filterInputNode.value);\n  }\n\n  /* eslint-disable class-methods-use-this */\n  /**\n   * The default item matching callback.\n   * @param item The combo box item.\n   * @param queryText The query text user types.\n   * @returns `true` if the given combo box item matches the given query text.\n   */\n  protected _defaultItemMatches(item: BXComboBoxItem, queryText: string): boolean {\n    return item.textContent!.toLowerCase().indexOf(queryText.toLowerCase()) >= 0;\n  }\n  /* eslint-enable class-methods-use-this */\n\n  /**\n   * Handles `input` event on the `<input>` for filtering.\n   */\n  protected _handleInput() {\n    const items = this.querySelectorAll((this.constructor as typeof BXComboBox).selectorItem);\n    const index = !this._filterInputNode.value ? -1 : findIndex(items, this._testItemWithQueryText, this);\n    forEach(items, (item, i) => {\n      if (i === index) {\n        const menuRect = this._itemMenu?.getBoundingClientRect();\n        const itemRect = item.getBoundingClientRect();\n\n        if (menuRect && itemRect) {\n          const isViewable = menuRect!.top <= itemRect?.top && itemRect?.bottom <= menuRect?.top + this._itemMenu!.clientHeight;\n          if (!isViewable) {\n            const scrollTop = itemRect?.top - menuRect?.top;\n            const scrollBot = itemRect?.bottom - menuRect?.bottom;\n\n            if (Math.abs(scrollTop) < Math.abs(scrollBot)) {\n              this._itemMenu!.scrollTop += scrollTop;\n            } else {\n              this._itemMenu!.scrollTop += scrollBot;\n            }\n          }\n        }\n      }\n      (item as BXComboBoxItem).highlighted = i === index;\n    });\n    const { _filterInputNode: filterInput } = this;\n    this._filterInputValue = !filterInput ? '' : filterInput.value;\n    this.open = true;\n    this.requestUpdate(); // If the only change is to `_filterInputValue`, auto-update doesn't happen\n  }\n\n  protected _handleClickInner(event: MouseEvent) {\n    if (this._selectionButtonNode?.contains(event.target as Node)) {\n      this._handleUserInitiatedClearInput();\n    } else {\n      super._handleClickInner(event);\n    }\n  }\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    if (\n      this._selectionButtonNode?.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 {\n      super._handleKeypressInner(event);\n    }\n  }\n\n  /**\n   * Handles user-initiated clearing the `<input>` for filtering.\n   */\n  protected _handleUserInitiatedClearInput() {\n    forEach(this.querySelectorAll((this.constructor as typeof BXComboBox).selectorItem), item => {\n      (item as BXComboBoxItem).highlighted = false;\n    });\n    this._filterInputValue = '';\n    this._filterInputNode.focus();\n    this._handleUserInitiatedSelectItem();\n  }\n\n  protected _handleUserInitiatedSelectItem(item?: BXComboBoxItem) {\n    if (item && !this._selectionShouldChange(item)) {\n      // Escape hatch for `shouldUpdate()` logic that updates `._filterInputValue()` when selection changes,\n      // given we want to update the `<input>` and close the dropdown even if selection doesn't update.\n      // Use case:\n      // 1. Select the 2nd item in combo box drop down\n      // 2. Type some text in the `<input>`\n      // 3. Re-select the 2nd item in combo box drop down,\n      //    the `<input>` has to updated with the 2nd item and the dropdown should be closed,\n      //    even if there is no change in the selected value\n      this._filterInputValue = item.textContent || '';\n      this.open = false;\n      this.requestUpdate();\n    }\n    super._handleUserInitiatedSelectItem(item);\n  }\n\n  protected _selectionDidChange(itemToSelect?: BXComboBoxItem) {\n    this.value = !itemToSelect ? '' : itemToSelect.value;\n    forEach(this.querySelectorAll((this.constructor as typeof BXDropdown).selectorItemSelected), item => {\n      (item as BXComboBoxItem).selected = false;\n    });\n    if (itemToSelect) {\n      itemToSelect.selected = true;\n      this._assistiveStatusText = this.selectedItemAssistiveText;\n    }\n    this._handleUserInitiatedToggle(false);\n  }\n\n  protected _renderTriggerContent(): TemplateResult {\n    const { disabled, inputLabel, triggerContent, _filterInputValue: filterInputValue, _handleInput: handleInput } = this;\n    return html`\n      <input\n        id=\"trigger-label\"\n        class=\"${prefix}--text-input\"\n        ?disabled=${disabled}\n        placeholder=\"${triggerContent}\"\n        .value=${filterInputValue}\n        role=\"combobox\"\n        aria-label=\"${inputLabel}\"\n        aria-controls=\"menu-body\"\n        aria-autocomplete=\"list\"\n        @input=${handleInput} />\n    `;\n  }\n\n  protected _renderFollowingTriggerContent(): TemplateResult | void {\n    const { clearSelectionLabel, _filterInputValue: filterInputValue } = this;\n    return filterInputValue.length === 0\n      ? undefined\n      : html`\n          <div\n            id=\"selection-button\"\n            role=\"button\"\n            class=\"${prefix}--list-box__selection\"\n            tabindex=\"0\"\n            title=\"${clearSelectionLabel}\">\n            ${Close16({ 'aria-label': clearSelectionLabel })}\n          </div>\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   * The `aria-label` attribute for the `<input>` for filtering.\n   */\n  @property({ attribute: 'input-label' })\n  inputLabel = '';\n\n  /**\n   * The custom item matching callback.\n   */\n  @property({ attribute: false })\n  itemMatches!: (item: BXComboBoxItem, queryText: string) => boolean;\n\n  shouldUpdate(changedProperties) {\n    super.shouldUpdate(changedProperties);\n    const { _selectedItemContent: selectedItemContent } = this;\n    if (selectedItemContent && changedProperties.has('value')) {\n      this._filterInputValue = selectedItemContent?.textContent || '';\n    }\n    return true;\n  }\n\n  updated() {\n    const { _listBoxNode: listBoxNode } = this;\n    if (listBoxNode) {\n      listBoxNode.classList.add(`${prefix}--combo-box`);\n    }\n  }\n\n  // For combo box, open/selection with space key is disabled given the input box should take it over\n  static TRIGGER_KEYS = new Set(['Enter']);\n\n  /**\n   * A selector that will return highlighted items.\n   */\n  static get selectorItemHighlighted() {\n    return `${prefix}-combo-box-item[highlighted]`;\n  }\n\n  /**\n   * A selector that will return combo box items.\n   */\n  static get selectorItem() {\n    return `${prefix}-combo-box-item`;\n  }\n\n  /**\n   * A selector that will return selected items.\n   */\n  static get selectorItemSelected() {\n    return `${prefix}-combo-box-item[selected]`;\n  }\n\n  /**\n   * The name of the custom event fired before this combo box item is being toggled upon a user gesture.\n   * Cancellation of this event stops the user-initiated action of toggling this combo box item.\n   */\n  static get eventBeforeToggle() {\n    return `${prefix}-combo-box-beingtoggled`;\n  }\n\n  /**\n   * The name of the custom event fired after this combo box item is toggled upon a user gesture.\n   */\n  static get eventToggle() {\n    return `${prefix}-combo-box-toggled`;\n  }\n\n  /**\n   * The name of the custom event fired before a combo box 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}-combo-box-beingselected`;\n  }\n\n  /**\n   * The name of the custom event fired after a a combo box item is selected upon a user gesture.\n   */\n  static get eventSelect() {\n    return `${prefix}-combo-box-selected`;\n  }\n\n  static styles = styles;\n}\n\nexport default BXComboBox;\n"]}