/**
 * Copyright IBM Corp. 2026
 *
 * This source code is licensed under the Apache-2.0 license found in the
 * LICENSE file in the root directory of this source tree.
 */

import "../../globals/settings.js";
import { filter, forEach } from "../../globals/internal/collection-helpers.js";
import { carbonElement } from "../../globals/decorators/carbon-element.js";
import { __decorate } from "../../_virtual/_@oxc-project_runtime@0.126.0/helpers/decorate.js";
import iconLoader from "../../globals/internal/icon-loader.js";
import HostListener from "../../globals/decorators/host-listener.js";
import { DROPDOWN_DIRECTION, DROPDOWN_KEYBOARD_ACTION, DROPDOWN_SIZE, DROPDOWN_TYPE } from "../dropdown/defs.js";
import dropdown_default from "../dropdown/dropdown.js";
import { SELECTION_FEEDBACK_OPTION } from "./defs.js";
import multi_select_default$1 from "./multi-select.scss.js";
import { html } from "lit";
import { property, query } from "lit/decorators.js";
import { classMap } from "lit/directives/class-map.js";
import Close16 from "@carbon/icons/es/close/16.js";
//#region src/components/multi-select/multi-select.ts
/**
* Copyright IBM Corp. 2020, 2026
*
* This source code is licensed under the Apache-2.0 license found in the
* LICENSE file in the root directory of this source tree.
*/
/**
* Multi select.
*
* @element cds-multi-select
* @fires cds-multi-select-beingselected
*   The custom event fired before a multi select item is selected upon a user gesture.
*   Cancellation of this event stops changing the user-initiated selection.
* @fires cds-multi-select-selected - The custom event fired after a multi select item is selected upon a user gesture.
* @fires cds-multi-select-beingtoggled
*   The custom event fired before the open state of this multi select is toggled upon a user gesture.
*   Cancellation of this event stops the user-initiated toggling.
* @fires cds-multi-select-toggled
*   The custom event fired after the open state of this multi select is toggled upon a user gesture.
*/
let CDSMultiSelect = class CDSMultiSelect extends dropdown_default {
	constructor(..._args) {
		super(..._args);
		this._selectedItemsCount = 0;
		this.clearSelectionLabel = "";
		this.clearSelectionDescription = "Total items selected: ";
		this.clearSelectionText = "To clear selection, press Delete or Backspace.";
		this.locale = "en";
		this.selectAll = false;
		this.selectionFeedback = "top-after-reopen";
		this.defaultCompareItems = (itemA, itemB, { locale }) => {
			return itemA.localeCompare(itemB, locale, { numeric: true });
		};
		this.compareItems = this.defaultCompareItems;
		this.getItemValue = (item) => item instanceof Element ? item.getAttribute("value") ?? "" : "";
		this.defaultSortItems = (menuItems, { values, compareItems, locale = "en" }) => {
			return Array.from(menuItems).sort((itemA, itemB) => {
				const hasItemA = values.includes(this.getItemValue(itemA));
				const hasItemB = values.includes(this.getItemValue(itemB));
				if (hasItemA && !hasItemB) return -1;
				if (hasItemB && !hasItemA) return 1;
				return compareItems(itemA.textContent?.trim() ?? "", itemB.textContent?.trim() ?? "", { locale });
			});
		};
		this.sortItems = this.defaultSortItems;
	}
	get _supportsMenuInputFiltering() {
		return Boolean(this.filterable);
	}
	willUpdate(changedProperties) {
		this._shouldTriggerBeFocusable = !this.filterable;
		super.willUpdate(changedProperties);
	}
	get _menuInputNode() {
		return this.filterable ? this._filterInputNode ?? null : null;
	}
	_clearMenuInputFiltering() {
		if (this.filterable) this._handleUserInitiatedClearInput();
	}
	_shouldClearMenuInputOnEscape({ menuOpen, isInputTarget }) {
		if (!isInputTarget) return false;
		if (menuOpen) return false;
		if (!menuOpen) {
			if (this._selectedItemsCount > 0) this._handleUserInitiatedSelectItem();
			return Boolean(this._filterInputNode?.value);
		}
		return false;
	}
	_selectionShouldChange(itemToSelect) {
		return Boolean(this.value || itemToSelect);
	}
	_selectionDidChange(itemToSelect) {
		const allItems = Array.from(this.querySelectorAll(this.constructor.selectorItem));
		if (itemToSelect?.isSelectAll && itemToSelect.indeterminate) {
			allItems.forEach((i) => {
				i.selected = false;
				i.indeterminate = false;
			});
			this.value = "";
			return;
		}
		if (itemToSelect) if (itemToSelect.isSelectAll) {
			(this.filterable ? Array.from(this.querySelectorAll(this.constructor.selectorItemResults)) : allItems).forEach((i) => {
				if (!i.isSelectAll && !i.disabled) i.selected = !itemToSelect.selected;
				i.indeterminate = false;
			});
			itemToSelect.selected = !itemToSelect.selected;
		} else itemToSelect.selected = !itemToSelect.selected;
		else {
			forEach(this.querySelectorAll(this.constructor.selectorItemSelected), (item) => {
				item.selected = false;
			});
			this._handleUserInitiatedToggle(false);
		}
		if (this.selectAll) this._computeSelectAllState();
		this.value = filter(this.querySelectorAll(this.constructor.selectorItem), (item) => item.selected).map((item) => item.value).join(",");
	}
	_shouldCloseAfterSelection(item) {
		return !item;
	}
	_handleClickInner(event) {
		const clickedItem = event.target.closest(`cds-multi-select-item`);
		if (this._selectionButtonNode?.contains(event.target) && !this.readOnly) {
			this._handleUserInitiatedSelectItem();
			if (this.filterable) this._filterInputNode.focus();
			else this._triggerNode.focus();
		} else if (clickedItem && !clickedItem.hasAttribute("disabled")) {
			this.querySelectorAll(`cds-multi-select-item`).forEach((el) => el.removeAttribute("highlighted"));
			clickedItem.setAttribute("highlighted", "");
			this._handleUserInitiatedSelectItem(clickedItem);
			this.setAttribute("item-clicked", "");
			if (this.filterable) this._filterInputNode.focus();
		} else if (this._clearButtonNode?.contains(event.target)) this._handleUserInitiatedClearInput();
		else if (!event.target?.matches(this.constructor.aiLabelItem) && !event.target?.matches(this.constructor.slugItem)) {
			super._handleClickInner(event);
			if (this.filterable) {
				if (!this.open && this._filterInputNode) this._clearInput();
				this._filterInputNode.focus();
			}
		}
	}
	/**
	* Clears selections on Escape click
	*/
	_handleKeydownInner(event) {
		const { key } = event;
		if (key === "Escape" && !this.filterable && !this.open && this._selectedItemsCount > 0) {
			this._handleUserInitiatedSelectItem();
			return;
		}
		super._handleKeydownInner(event);
	}
	_handleFocusOut(event) {
		if (this.filterable && this._filterInputNode && !this.contains(event.relatedTarget)) this._clearInput();
		super._handleFocusOut(event);
	}
	/**
	* Handler for the `keypress` event, ensures filter still works upon entering space
	*/
	_handleKeypressInner(event) {
		const { key } = event;
		const action = this.constructor.getAction(key);
		const { TRIGGERING } = DROPDOWN_KEYBOARD_ACTION;
		if (this._clearButtonNode?.contains(event.target) && (action === TRIGGERING || key === " ")) this._handleUserInitiatedClearInput();
		else if (this._selectionButtonNode?.contains(event.target)) {
			this._handleUserInitiatedSelectItem();
			this.open = true;
			if (this.filterable) this._filterInputNode.focus();
			else this._triggerNode.focus();
		} else if (this.filterable) this._handleKeypressInnerFlterable(event);
		else super._handleKeypressInner(event);
	}
	_handleMouseoverInner(event) {
		const item = this._getDropdownItemFromEvent(event);
		const isFiltering = this.filterable && Boolean(this._filterInputNode?.value.length);
		if (!item || isFiltering || !item.hasAttribute("selected") || !item.hasAttribute("highlighted")) return;
		super._handleMouseoverInner(event);
	}
	_handleMouseleaveInner(event) {
		const constructor = this.constructor;
		const isFiltering = this.filterable && Boolean(this._filterInputNode?.value.length);
		const highlightedItem = this.querySelector(constructor.selectorItemHighlighted);
		if (isFiltering || highlightedItem?.hasAttribute("selected")) return;
		super._handleMouseleaveInner(event);
	}
	/**
	* Special andler for the `keypress` event, ensures space selection for filterable
	* variation is disabled
	*/
	_handleKeypressInnerFlterable(event) {
		const { key } = event;
		const action = this.constructor.getAction(key);
		if (!this.open) switch (action) {
			case "triggering":
				this._handleUserInitiatedToggle(true);
				break;
			default: break;
		}
		else switch (key) {
			case "Enter":
				{
					const constructor = this.constructor;
					const highlightedItem = this.querySelector(constructor.selectorItemHighlighted);
					if (highlightedItem) this._handleUserInitiatedSelectItem(highlightedItem);
					else this._handleUserInitiatedToggle(false);
				}
				break;
			default: break;
		}
	}
	_renderTitleLabel() {
		const { clearSelectionDescription, clearSelectionText, disabled, hideLabel, titleText, _selectedItemsCount: selectedItemsCount, _slotTitleTextNode: slotTitleTextNode, _handleSlotchangeLabelText: handleSlotchangeLabelText } = this;
		return html`
      <label
        part="title-text"
        class="${classMap({
			[`cds--label`]: true,
			[`cds--label--disabled`]: disabled,
			[`cds--visually-hidden`]: hideLabel
		})}"
        ?hidden="${!(titleText || slotTitleTextNode && slotTitleTextNode.assignedNodes().length > 0)}">
        <slot name="title-text" @slotchange="${handleSlotchangeLabelText}"
          >${titleText}</slot
        >
        ${selectedItemsCount > 0 ? html`
              <span class="${"cds"}--visually-hidden">
                ${clearSelectionDescription} ${selectedItemsCount},
                ${clearSelectionText}
              </span>
            ` : null}
      </label>
    `;
	}
	_renderPrecedingLabel() {
		const { disabled, readOnly, clearSelectionLabel, _selectedItemsCount: selectedItemsCount } = this;
		const selectionButtonClasses = classMap({
			[`cds--list-box__selection`]: true,
			[`cds--list-box__selection--multi`]: true,
			[`cds--tag`]: true,
			[`cds--tag--filter`]: true,
			[`cds--tag--high-contrast`]: true,
			[`cds--tag--disabled`]: disabled
		});
		return selectedItemsCount === 0 ? void 0 : html`
          <div
            id="selection-button"
            role="button"
            class="${selectionButtonClasses}"
            tabindex="-1"
            aria-disabled=${readOnly}
            title="${clearSelectionLabel}">
            ${selectedItemsCount}
            ${iconLoader(Close16, {
			"aria-label": clearSelectionLabel,
			class: `cds--tag__close-icon`
		})}
          </div>
        `;
	}
	/**
	@returns The main content of the trigger button.
	*/
	_renderLabel() {
		const { label, value, _selectedItemContent: selectedItemContent } = this;
		const inputClasses = classMap({
			[`cds--text-input`]: true,
			[`cds--text-input--empty`]: !value
		});
		return !this.filterable ? html`
          <span id="trigger-label" class="${"cds"}--list-box__label"
            >${selectedItemContent || label}</span
          >
        ` : html`
          <input
            id="trigger-label"
            class="${inputClasses}"
            placeholder="${label}"
            role="combobox"
            aria-controls="menu-body"
            aria-expanded="${String(this.open)}"
            aria-autocomplete="list"
            @input="${this._handleInput}" />
        `;
	}
	_renderFollowingLabel() {
		const { clearSelectionLabel, _filterInputNode: filterInputNode } = this;
		return filterInputNode && filterInputNode.value.length > 0 && this.filterable ? html`
          <div
            id="clear-button"
            role="button"
            class="${"cds"}--list-box__selection"
            tabindex="-1"
            title="${clearSelectionLabel}">
            ${iconLoader(Close16, { "aria-label": clearSelectionLabel })}
          </div>
        ` : void 0;
	}
	/**
	* Handles `input` event on the `<input>` for filtering.
	*/
	_handleInput() {
		const items = this.querySelectorAll(this.constructor.selectorItem);
		const inputValue = this._filterInputNode.value.toLocaleLowerCase();
		this.toggleAttribute("has-value", inputValue.length > 0);
		if (!this.open && inputValue.length > 0) this.open = true;
		forEach(items, (item) => {
			if (item.isSelectAll) {
				item.removeAttribute("filtered");
				return;
			}
			if (!item.innerText.toLocaleLowerCase().includes(inputValue)) {
				item.setAttribute("filtered", "");
				item.removeAttribute("highlighted");
			} else item.removeAttribute("filtered");
		});
		this.requestUpdate();
		if (this.selectAll) {
			const selectAllItem = this.querySelector(`cds-multi-select-item[is-select-all]`);
			if (selectAllItem) if (Array.from(this.querySelectorAll(this.constructor.selectorItemResults)).filter((i) => !i.isSelectAll && !i.disabled).length === 0) selectAllItem.setAttribute("filtered", "");
			else {
				selectAllItem.removeAttribute("filtered");
				this._computeSelectAllState();
			}
		}
		const constructor = this.constructor;
		const visibleItems = Array.from(this.querySelectorAll(constructor.selectorItemResults));
		if (visibleItems.length > 0) {
			visibleItems.forEach((i) => i.removeAttribute("highlighted"));
			this.toggleAttribute("item-clicked", inputValue.length > 0);
			const first = visibleItems[0];
			first.setAttribute("highlighted", "");
			first.focus();
		} else this._filterInputNode.focus();
	}
	/**
	* Navigate through dropdown items.
	*
	* @param direction `-1` to navigate backward, `1` to navigate forward.
	*/
	_navigate(direction) {
		super._navigate(direction);
		if (this.filterable) this.setAttribute("item-clicked", "");
		else this._triggerNode.classList.add("no-focus-style");
	}
	/**
	* Handles user-initiated clearing the `<input>` for filtering.
	*/
	_handleUserInitiatedClearInput() {
		const constructor = this.constructor;
		const items = this.querySelectorAll(constructor.selectorItemFiltered);
		this._filterInputNode.value = "";
		this._filterInputNode.focus();
		forEach(items, (item) => {
			item.removeAttribute("filtered");
		});
		this._filterInputNode.dispatchEvent(new Event("input", {
			bubbles: true,
			composed: true
		}));
	}
	/**
	* The CSS class list for multi-select listbox
	*/
	get _classes() {
		const { disabled, size, type, invalid, readOnly, open, warn, _selectedItemsCount: selectedItemsCount } = this;
		const inline = type === "inline";
		const isInteractive = !readOnly && !disabled;
		const normalizedProps = {
			invalid: isInteractive && invalid,
			warn: isInteractive && !invalid && warn
		};
		return classMap({
			[`cds--multi-select`]: true,
			[`cds--list-box`]: true,
			[`cds--list-box--disabled`]: disabled,
			[`cds--list-box--inline`]: inline,
			[`cds--list-box--expanded`]: open,
			[`cds--list-box--${size}`]: size,
			[`cds--multi-select--invalid`]: normalizedProps.invalid,
			[`cds--multi-select--warn`]: normalizedProps.warn,
			[`cds--multi-select--inline`]: inline,
			[`cds--multi-select--readonly`]: readOnly,
			[`cds--multi-select--selected`]: selectedItemsCount > 0,
			[`cds--list-box__wrapper--decorator`]: this._hasAILabel,
			[`cds--multi-select--selectall`]: this.selectAll
		});
	}
	shouldUpdate(changedProperties) {
		const { selectorItem, aiLabelItem, slugItem } = this.constructor;
		const aiLabel = this.querySelector(aiLabelItem) || this.querySelector(slugItem);
		const items = this.querySelectorAll(selectorItem);
		const { value, locale } = this;
		const values = !value ? [] : value.split(",");
		if (changedProperties.has("size")) forEach(this.querySelectorAll(selectorItem), (elem) => {
			elem.size = this.size;
		});
		if (changedProperties.has("value")) {
			forEach(items, (elem) => {
				elem.selected = values.indexOf(elem.value) >= 0;
			});
			this._selectedItemsCount = filter(items, (elem) => values.indexOf(elem.value) >= 0 && !elem.isSelectAll).length;
		}
		if ((changedProperties.has("value") || changedProperties.has("compareItems") || changedProperties.has("sortItems") || changedProperties.has("locale")) && this.selectionFeedback === "top") {
			const sortedMenuItems = this.sortItems(items, {
				values,
				compareItems: this.compareItems,
				locale
			});
			if (aiLabel) sortedMenuItems.unshift(aiLabel);
			this.replaceChildren(...sortedMenuItems);
		}
		if ((changedProperties.has("open") || changedProperties.has("compareItems") || changedProperties.has("sortItems") || changedProperties.has("locale")) && this.selectionFeedback === "top-after-reopen") {
			const sortedMenuItems = this.sortItems(items, {
				values,
				compareItems: this.compareItems,
				locale
			});
			if (aiLabel) sortedMenuItems.unshift(aiLabel);
			sortedMenuItems.forEach((item) => {
				this.appendChild(item);
			});
		}
		return true;
	}
	updated(changedProperties) {
		super.updated(changedProperties);
		if (changedProperties.has("open") && this.open) {
			const selectedItems = Array.from(this.querySelectorAll(`cds-multi-select-item[selected]`));
			if (selectedItems.length > 0) {
				let itemToFocus = null;
				if (this.selectAll) itemToFocus = this.querySelector(`cds-multi-select-item[is-select-all]`);
				if (!itemToFocus) itemToFocus = selectedItems[0];
				this.setAttribute("item-clicked", "");
				itemToFocus.focus();
				itemToFocus.setAttribute("highlighted", "");
			} else if (this.filterable) this._filterInputNode.focus();
			else this._triggerNode.focus();
		}
		if (this.selectAll && changedProperties.has("open") && this.open) {
			const items = Array.from(this.querySelectorAll("cds-multi-select-item"));
			const selectAllItem = items.find((i) => i.isSelectAll);
			if (selectAllItem) {
				this.appendChild(selectAllItem);
				items.filter((i) => i !== selectAllItem).forEach((i) => this.appendChild(i));
			}
		}
		Array.from(this.querySelectorAll("cds-multi-select-item")).forEach((item, index) => {
			if (index === 0 && !item.hasAttribute("is-select-all")) item.setAttribute("flush-top", "");
			else if (index === 1 && this.selectAll) item.setAttribute("flush-top", "");
			else item.removeAttribute("flush-top");
		});
		if (changedProperties.has("open") && !this.open) {
			this._triggerNode.classList.remove("no-focus-style");
			this.removeAttribute("item-clicked");
		}
	}
	firstUpdated(changedProperties) {
		super.firstUpdated?.(changedProperties);
		if (!this.selectAll) return;
		this.shadowRoot.querySelector("slot:not([name])").addEventListener("slotchange", () => this._computeSelectAllState());
	}
	/**
	* Computes the state of the select all option and sets it to either
	* 'selected' or 'indeterminate'
	*/
	_computeSelectAllState() {
		if (!this.selectAll) return;
		const allItems = Array.from(this.querySelectorAll(this.constructor.selectorItem));
		const selectAllItem = allItems.find((i) => i.isSelectAll);
		if (!selectAllItem || selectAllItem.hasAttribute("filtered")) return;
		const enabledItems = allItems.filter((i) => !i.isSelectAll && !i.disabled).filter((i) => !this.filterable || !i.hasAttribute("filtered"));
		const selectedCount = enabledItems.filter((i) => i.selected).length;
		const allSelected = selectedCount === enabledItems.length;
		selectAllItem.selected = allSelected;
		selectAllItem.indeterminate = selectedCount > 0 && !allSelected;
	}
	/**
	* Clears the filterable input field
	*/
	_clearInput() {
		this._filterInputNode.value = "";
		this.toggleAttribute("has-value", false);
		forEach(this.querySelectorAll(this.constructor.selectorItemFiltered), (item) => {
			item.removeAttribute("filtered");
		});
	}
	connectedCallback() {
		super.connectedCallback();
		/**
		* Detect if multi-select already has initially selected items
		*/
		this.value = filter(this.querySelectorAll(this.constructor.selectorItem), (item) => item.hasAttribute("selected")).map((item) => item.getAttribute("value")).join(",");
	}
	/**
	* A selector that will return menu body.
	*/
	static get selectorMenuBody() {
		return `div[part="menu-body"]`;
	}
	/**
	* A selector that will return highlighted items.
	*/
	static get selectorItemHighlighted() {
		return `cds-multi-select-item[highlighted]`;
	}
	/**
	* A selector that will return multi select items.
	* We use a separate property from `.itemTagName` due to the nature in difference of tag name vs. selector.
	*/
	static get selectorItem() {
		return `cds-multi-select-item`;
	}
	/**
	* A selector that will return remaining items after a filter.
	*/
	static get selectorItemFiltered() {
		return `cds-multi-select-item[filtered]`;
	}
	/**
	* A selector that will return remaining items after a filter.
	*/
	static get selectorItemResults() {
		return `cds-multi-select-item:not([filtered])`;
	}
	/**
	* A selector that will return selected items.
	*/
	static get selectorItemSelected() {
		return `cds-multi-select-item[selected]`;
	}
	/**
	* The name of the custom event fired before this multi select item is being toggled upon a user gesture.
	* Cancellation of this event stops the user-initiated action of toggling this multi select item.
	*/
	static get eventBeforeToggle() {
		return `cds-multi-select-beingtoggled`;
	}
	/**
	* The name of the custom event fired after this multi select item is toggled upon a user gesture.
	*/
	static get eventToggle() {
		return `cds-multi-select-toggled`;
	}
	/**
	* The name of the custom event fired before a multi select item is selected upon a user gesture.
	* Cancellation of this event stops changing the user-initiated selection.
	*/
	static get eventBeforeSelect() {
		return `cds-multi-select-beingselected`;
	}
	/**
	* The name of the custom event fired after a a multi select item is selected upon a user gesture.
	*/
	static get eventSelect() {
		return `cds-multi-select-selected`;
	}
	static {
		this.styles = multi_select_default$1;
	}
};
__decorate([property({ type: Boolean })], CDSMultiSelect.prototype, "filterable", void 0);
__decorate([query("#clear-button")], CDSMultiSelect.prototype, "_clearButtonNode", void 0);
__decorate([query("#selection-button")], CDSMultiSelect.prototype, "_selectionButtonNode", void 0);
__decorate([query("input")], CDSMultiSelect.prototype, "_filterInputNode", void 0);
__decorate([query(`.cds--list-box__field`)], CDSMultiSelect.prototype, "_triggerNode", void 0);
__decorate([HostListener("focusout")], CDSMultiSelect.prototype, "_handleFocusOut", null);
__decorate([property({ attribute: "clear-selection-label" })], CDSMultiSelect.prototype, "clearSelectionLabel", void 0);
__decorate([property({ attribute: "clear-selection-description" })], CDSMultiSelect.prototype, "clearSelectionDescription", void 0);
__decorate([property({ attribute: "clear-selection-text" })], CDSMultiSelect.prototype, "clearSelectionText", void 0);
__decorate([property()], CDSMultiSelect.prototype, "locale", void 0);
__decorate([property({
	type: Boolean,
	reflect: true,
	attribute: "select-all"
})], CDSMultiSelect.prototype, "selectAll", void 0);
__decorate([property({ attribute: "selection-feedback" })], CDSMultiSelect.prototype, "selectionFeedback", void 0);
__decorate([property({ attribute: false })], CDSMultiSelect.prototype, "compareItems", void 0);
__decorate([property({ attribute: false })], CDSMultiSelect.prototype, "sortItems", void 0);
CDSMultiSelect = __decorate([carbonElement(`cds-multi-select`)], CDSMultiSelect);
var multi_select_default = CDSMultiSelect;
//#endregion
export { DROPDOWN_DIRECTION, DROPDOWN_SIZE, DROPDOWN_TYPE, SELECTION_FEEDBACK_OPTION, multi_select_default as default };

//# sourceMappingURL=multi-select.js.map