import { h, Host } from "@stencil/core";
import { computePosition, flip, offset, shift, size, autoUpdate } from "@floating-ui/dom";
import { debounceEvent } from "../../utils/utils";
import { isSpecTest, messageId, assignDescription } from "../../utils/form";
import { danger, enlarge } from "@pine-ds/icons/icons";
/**
 * @slot (default) - Static option elements for the multiselect
 * @slot empty - Custom empty state message when no options match
 * @slot loading - Custom loading indicator
 * @part trigger - The trigger surface that opens the dropdown panel
 * @part pill-toggle - The chevron button inside the inline pill trigger
 */
export class PdsMultiselect {
    constructor() {
        /**
         * Placeholder text for the input field.
         */
        this.placeholder = 'Select...';
        /**
         * Placeholder text for the search input inside the dropdown panel.
         * @default 'Find...'
         */
        this.searchPlaceholder = 'Find...';
        /**
         * Whether to close the panel after an option is selected.
         * Defaults to `false` (panel stays open for multi-select).
         */
        this.closePanelOnSelect = false;
        /**
         * Array of selected option values.
         */
        this.value = [];
        /**
         * Determines whether or not the multiselect is disabled.
         */
        this.disabled = false;
        /**
         * HTTP method for async requests.
         */
        this.asyncMethod = 'GET';
        /**
         * Debounce delay in milliseconds for search/fetch.
         */
        this.debounce = 300;
        /**
         * Timeout in milliseconds for async fetch requests.
         */
        this.fetchTimeout = 30000;
        /**
         * Maximum height of the dropdown before scrolling.
         */
        this.maxHeight = '300px';
        /**
         * Width of the trigger button (and reference for dropdown positioning).
         */
        this.triggerWidth = '100%';
        /**
         * Minimum width of the dropdown panel.
         */
        this.minWidth = '250px';
        /**
         * Visually hides the label but keeps it accessible.
         */
        this.hideLabel = false;
        /**
         * Hides the selected items summary section in the dropdown panel.
         */
        this.hideSelectedItems = false;
        /**
         * Display mode for selected items outside the dropdown panel.
         * `'count'` shows "N item(s)" text in the trigger (default). `'pill'` renders
         * selected items as dismissible pds-chip tags.
         * @default 'count'
         */
        this.selectedDisplay = 'count';
        /**
         * Position of pill chips when `selectedDisplay` is `'pill'`.
         * `'inline'` places chips inside the trigger; `'below'` places chips in a
         * flex-wrap row directly below the trigger.
         * @default 'inline'
         */
        this.pillPosition = 'inline';
        /**
         * Maximum chips shown inline before collapsing to a "+N more" badge.
         * Only applies when `selectedDisplay='pill'` and `pillPosition='inline'`.
         * @default 3
         */
        this.maxInlinePills = 3;
        /**
         * If true, the multiselect is required.
         */
        this.required = false;
        /**
         * Whether the component is currently loading async options.
         */
        this.loading = false;
        /**
         * CSRF header name for authenticated requests. Defaults to `X-CSRF-Token`.
         */
        this.csrfHeaderName = 'X-CSRF-Token';
        // Internal state
        this.isOpen = false;
        this.searchQuery = '';
        this.highlightedIndex = -1;
        this.internalOptions = [];
        this.selectedItems = [];
        this.currentPage = 1;
        this.hasMore = false;
        this.creating = false;
        this.removalAnnouncement = '';
        // Flag to prevent focusout from closing during open transition
        this.isOpening = false;
        // Flag to suppress dismiss event when panel closes due to selection (not user dismissal)
        this.isClosingViaSelection = false;
        // Flag to track if initial async fetch has been triggered (prevents double fetch)
        this.initialAsyncFetchTriggered = false;
        // Flag to track if value changed during loading and needs resolution after fetch completes
        this.pendingUnresolvedFetch = false;
        this.handleTriggerClick = (e) => {
            if (this.disabled)
                return;
            // In inline-pill mode, ignore clicks that originated inside a pds-chip.
            // Chip close-button clicks are handled by handlePillRemove; chip body clicks should not toggle the panel.
            if (e && this.selectedDisplay === 'pill' && this.pillPosition === 'inline') {
                const fromChip = e.composedPath().some(el => { var _a; return ((_a = el.tagName) === null || _a === void 0 ? void 0 : _a.toLowerCase()) === 'pds-chip'; });
                if (fromChip)
                    return;
            }
            if (this.isOpen) {
                this.closeDropdown();
            }
            else {
                this.openDropdown();
            }
        };
        this.handleTriggerKeyDown = (e) => {
            // Ignore keydown events that bubbled from child elements (e.g. chip close buttons).
            // Only handle keys when the trigger itself is the event target.
            if (e.target !== this.triggerEl)
                return;
            switch (e.key) {
                case 'ArrowDown':
                case 'ArrowUp':
                case 'Enter':
                case ' ':
                    e.preventDefault();
                    if (!this.isOpen) {
                        this.openDropdown();
                    }
                    break;
            }
        };
        this.handleSearchInputChange = (e) => {
            const target = e.target;
            this.searchQuery = target.value;
            this.highlightedIndex = -1;
            // Emit search event for consumer-managed async
            this.pdsMultiselectSearch.emit({ query: this.searchQuery });
            // Fetch from async URL if configured
            if (this.asyncUrl) {
                this.debouncedFetchAsyncOptions(this.searchQuery, 1);
            }
        };
        this.handleSearchInputKeyDown = (e) => {
            var _a, _b, _c;
            const filteredOptions = this.getFilteredOptions();
            switch (e.key) {
                case 'ArrowDown': {
                    e.preventDefault();
                    let nextIndex = this.highlightedIndex + 1;
                    while (nextIndex < filteredOptions.length && ((_a = filteredOptions[nextIndex]) === null || _a === void 0 ? void 0 : _a.disabled)) {
                        nextIndex++;
                    }
                    if (nextIndex < filteredOptions.length) {
                        this.highlightedIndex = nextIndex;
                        this.scrollOptionIntoView();
                    }
                    break;
                }
                case 'ArrowUp': {
                    e.preventDefault();
                    // Clamp to 0 so ArrowUp from uninitialised state (-1) attempts the first option;
                    // stays at -1 if that option is disabled.
                    let prevIndex = Math.max(this.highlightedIndex - 1, 0);
                    while (prevIndex > 0 && ((_b = filteredOptions[prevIndex]) === null || _b === void 0 ? void 0 : _b.disabled)) {
                        prevIndex--;
                    }
                    if (!((_c = filteredOptions[prevIndex]) === null || _c === void 0 ? void 0 : _c.disabled)) {
                        this.highlightedIndex = prevIndex;
                        this.scrollOptionIntoView();
                    }
                    break;
                }
                case 'Enter':
                    e.preventDefault();
                    if (this.highlightedIndex >= 0) {
                        const option = filteredOptions[this.highlightedIndex];
                        if (option) {
                            this.selectOption(option);
                        }
                    }
                    break;
                // Escape is handled by the global @Listen('keydown') handler
                case 'Tab':
                    this.closeDropdown();
                    break;
            }
        };
        this.handleContainerFocusOut = () => {
            // Use setTimeout to delay the check - this allows click events and focus transitions to complete
            // before we decide to close the dropdown
            setTimeout(() => {
                var _a;
                // Don't close if we're in the middle of opening, already closed,
                // or closing due to a selection (not a user dismissal)
                if (!this.isOpen || this.isOpening || this.isClosingViaSelection)
                    return;
                const activeElement = document.activeElement;
                // Check if focus is within our component's shadow root
                const isInShadowRoot = (_a = this.el.shadowRoot) === null || _a === void 0 ? void 0 : _a.contains(activeElement);
                // Also check if focus is on the host element itself
                const isOnHost = activeElement === this.el;
                if (!isInShadowRoot && !isOnHost) {
                    this.pdsMultiselectDismiss.emit();
                    this.closeDropdown();
                }
            }, 0);
        };
        this.handleOptionMouseDown = (option) => (e) => {
            e.preventDefault(); // Prevent focus change
            this.toggleOption(option);
        };
        this.handleOptionMouseEnter = (index, option) => () => {
            if (option.disabled)
                return;
            this.highlightedIndex = index;
        };
        this.handleScroll = (e) => {
            if (!this.asyncUrl || !this.hasMore || this.loading)
                return;
            const target = e.target;
            const scrollBottom = target.scrollHeight - target.scrollTop - target.clientHeight;
            // Load more when near bottom (within 50px)
            if (scrollBottom < 50) {
                this.pdsMultiselectLoadOptions.emit({
                    query: this.searchQuery,
                    page: this.currentPage + 1,
                });
                this.debouncedFetchAsyncOptions(this.searchQuery, this.currentPage + 1);
            }
        };
        this.handlePillRemove = (item) => () => {
            var _a, _b;
            if (this.disabled)
                return;
            const optionId = String(item.id);
            this.value = this.value.filter(v => v !== optionId);
            this.syncSelectedItems();
            this.pdsMultiselectChange.emit({ values: this.value, items: this.selectedItems });
            // Clear first so screen readers re-announce even when the same item is removed twice
            this.removalAnnouncement = '';
            queueMicrotask(() => { this.removalAnnouncement = `${item.text} removed`; });
            this.isClosingViaSelection = true;
            if (this.isOpen) {
                (_a = this.searchInputEl) === null || _a === void 0 ? void 0 : _a.focus();
            }
            else {
                (_b = this.triggerEl) === null || _b === void 0 ? void 0 : _b.focus();
            }
            setTimeout(() => { this.isClosingViaSelection = false; }, 0);
        };
    }
    connectedCallback() {
        // Initialize ElementInternals for form association (only once per element instance)
        if (this.el.attachInternals && !this.internals) {
            this.internals = this.el.attachInternals();
        }
    }
    componentWillLoad() {
        this.originalSearchEmitter = this.pdsMultiselectSearch;
        this.syncSelectedItems();
    }
    componentDidLoad() {
        this.setupDebounce();
        this.setupMutationObserver();
        this.setupSlotChangeListener();
        this.updateFormValue();
        // Ensure preselected values sync after DOM is fully ready
        // This handles cases where slot content loads after initial render (e.g., in docs/MDX)
        requestAnimationFrame(() => {
            this.updateOptionsFromSlot();
            this.syncSelectedItems();
        });
        // If we have preselected values and asyncUrl, fetch options to resolve them
        // This ensures the trigger shows "X items" instead of placeholder on initial render
        if (this.asyncUrl && this.ensureValueArray().length > 0) {
            this.initialAsyncFetchTriggered = true;
            this.fetchOptions('', 1);
        }
    }
    setupSlotChangeListener() {
        var _a;
        const slot = (_a = this.el.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelector('slot:not([name])');
        if (slot) {
            slot.addEventListener('slotchange', () => {
                this.updateOptionsFromSlot();
                this.syncSelectedItems();
            });
            // Also call it immediately in case content is already slotted
            this.updateOptionsFromSlot();
        }
    }
    disconnectedCallback() {
        var _a, _b;
        (_a = this.observer) === null || _a === void 0 ? void 0 : _a.disconnect();
        (_b = this.cleanupAutoUpdate) === null || _b === void 0 ? void 0 : _b.call(this);
        this.clearAsyncFetchState();
    }
    setupDebounce() {
        const { pdsMultiselectSearch, debounce, originalSearchEmitter } = this;
        this.pdsMultiselectSearch = debounce === undefined
            ? originalSearchEmitter !== null && originalSearchEmitter !== void 0 ? originalSearchEmitter : pdsMultiselectSearch
            : debounceEvent(pdsMultiselectSearch, debounce);
    }
    valueChanged(newValue) {
        // Handle JSON string values (from HTML attributes)
        if (typeof newValue === 'string') {
            try {
                const parsed = JSON.parse(newValue);
                if (Array.isArray(parsed)) {
                    this.value = parsed;
                    return; // The assignment will trigger this watcher again with the array
                }
            }
            catch (_a) {
                // Not valid JSON, treat as single value
                this.value = newValue ? [newValue] : [];
                return;
            }
        }
        this.syncSelectedItems();
        this.updateFormValue();
        // If using asyncUrl and some values couldn't be resolved, fetch options
        // This handles programmatic value changes where the options aren't loaded yet
        if (this.asyncUrl) {
            const valueArray = this.ensureValueArray();
            const hasUnresolvedValues = valueArray.length > 0 && this.selectedItems.length < valueArray.length;
            if (hasUnresolvedValues) {
                if (this.loading) {
                    // Mark that we need to re-check after current fetch completes
                    this.pendingUnresolvedFetch = true;
                }
                else {
                    this.fetchOptions('', 1);
                }
            }
        }
    }
    optionsChanged() {
        if (this.options) {
            this.internalOptions = [...this.options];
        }
    }
    internalOptionsChanged() {
        // Re-sync selected items when options become available
        // This handles the case where value is set before options are loaded (e.g., from slot)
        this.syncSelectedItems();
    }
    /**
     * Sets focus on the trigger button.
     */
    async setFocus() {
        var _a;
        (_a = this.triggerEl) === null || _a === void 0 ? void 0 : _a.focus();
    }
    /**
     * Clears all selected values and resets the component.
     */
    async clear() {
        const hadValues = this.value.length > 0 || this.searchQuery !== '';
        this.value = [];
        this.searchQuery = '';
        this.syncSelectedItems();
        this.updateFormValue();
        if (hadValues) {
            this.pdsMultiselectChange.emit({ values: [], items: [] });
        }
    }
    /**
     * Handle global keyboard events for accessibility.
     * Closes dropdown on Escape key press regardless of focus location.
     */
    handleWindowKeyDown(event) {
        var _a;
        if (!this.isOpen)
            return;
        if (event.key === 'Escape') {
            event.preventDefault();
            this.pdsMultiselectDismiss.emit();
            this.closeDropdown();
            (_a = this.triggerEl) === null || _a === void 0 ? void 0 : _a.focus();
        }
    }
    setupMutationObserver() {
        this.observer = new MutationObserver(() => {
            this.updateOptionsFromSlot();
        });
        this.observer.observe(this.el, {
            childList: true,
            subtree: true,
        });
    }
    updateOptionsFromSlot() {
        var _a;
        const slot = (_a = this.el.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelector('slot:not([name])');
        if (!slot)
            return;
        const options = [];
        slot.assignedElements({ flatten: true }).forEach(el => {
            var _a;
            if (el.tagName === 'OPTGROUP') {
                const optgroup = el;
                const groupLabel = optgroup.label;
                const groupDisabled = optgroup.disabled;
                optgroup.querySelectorAll('option').forEach((opt) => {
                    var _a;
                    const option = {
                        id: opt.value,
                        text: ((_a = opt.textContent) === null || _a === void 0 ? void 0 : _a.trim()) || opt.value,
                        disabled: opt.disabled || groupDisabled || undefined,
                    };
                    if (groupLabel) {
                        option.group = groupLabel;
                    }
                    options.push(option);
                });
            }
            else if (el.tagName === 'OPTION') {
                const opt = el;
                options.push({
                    id: opt.value,
                    text: ((_a = opt.textContent) === null || _a === void 0 ? void 0 : _a.trim()) || opt.value,
                    disabled: opt.disabled || undefined,
                });
            }
        });
        // Only update if we actually found options AND we're not using async/external options
        // Don't clear internalOptions if slot returns empty (might be mid-DOM-update)
        if (options.length > 0 && !this.asyncUrl && !this.options) {
            this.internalOptions = options;
        }
    }
    clearAsyncFetchState() {
        var _a;
        if (this.fetchDebounceTimer !== undefined) {
            window.clearTimeout(this.fetchDebounceTimer);
            this.fetchDebounceTimer = undefined;
        }
        if (this.fetchTimeoutTimer !== undefined) {
            window.clearTimeout(this.fetchTimeoutTimer);
            this.fetchTimeoutTimer = undefined;
        }
        (_a = this.abortController) === null || _a === void 0 ? void 0 : _a.abort();
        this.abortController = undefined;
    }
    debouncedFetchAsyncOptions(query, page = 1) {
        var _a;
        if (!this.asyncUrl)
            return;
        if (this.fetchDebounceTimer !== undefined) {
            window.clearTimeout(this.fetchDebounceTimer);
        }
        const delay = Math.max(0, (_a = this.debounce) !== null && _a !== void 0 ? _a : 0);
        this.fetchDebounceTimer = window.setTimeout(() => {
            this.fetchDebounceTimer = undefined;
            this.fetchOptions(query, page);
        }, delay);
    }
    syncSelectedItems() {
        // Ensure value is an array (may be string from HTML attribute)
        const valueArray = this.ensureValueArray();
        const allOptions = this.getAllOptions();
        // Map values to options, preserving existing selectedItems for values not yet in options
        const newSelectedItems = [];
        const existingItemsMap = new Map(this.selectedItems.map(item => [String(item.id), item]));
        valueArray.forEach(val => {
            // First try to find in available options
            const option = allOptions.find(opt => String(opt.id) === String(val));
            if (option) {
                newSelectedItems.push(option);
            }
            else if (existingItemsMap.has(String(val))) {
                // If not in options but exists in current selectedItems, preserve it
                // This handles the case where async data hasn't loaded yet or newly created items
                newSelectedItems.push(existingItemsMap.get(String(val)));
            }
            // Note: We don't create placeholders for values without matching options.
            // This ensures selectedItems remains empty until options are actually loaded,
            // which matches the expected behavior for preselected values.
        });
        this.selectedItems = newSelectedItems;
    }
    ensureValueArray() {
        // Handle JSON string values passed via HTML attribute
        if (typeof this.value === 'string') {
            try {
                const parsed = JSON.parse(this.value);
                if (Array.isArray(parsed)) {
                    this.value = parsed;
                    return parsed;
                }
            }
            catch (_a) {
                // Not valid JSON, treat as single value
                const singleValue = this.value;
                this.value = singleValue ? [singleValue] : [];
                return this.value;
            }
        }
        return Array.isArray(this.value) ? this.value : [];
    }
    getAllOptions() {
        return this.options || this.internalOptions;
    }
    getFilteredOptions() {
        const allOptions = this.getAllOptions();
        const query = this.searchQuery.toLowerCase();
        const filtered = allOptions.filter(opt => {
            // Filter by search query only - don't filter out selected items
            if (query) {
                return opt.text.toLowerCase().includes(query);
            }
            return true;
        });
        // Add create option if enabled and no matches found
        if (this.createUrl && this.searchQuery.trim() && filtered.length === 0) {
            return [{
                    id: '__create__',
                    text: this.searchQuery.trim(),
                    isCreateOption: true,
                }];
        }
        return filtered;
    }
    updateFormValue() {
        var _a;
        if (isSpecTest())
            return;
        if ((_a = this.internals) === null || _a === void 0 ? void 0 : _a.setFormValue) {
            // Ensure value is an array before iterating
            const valueArray = this.ensureValueArray();
            // Submit as multiple values with same name (native select multiple behavior)
            const formData = new FormData();
            valueArray.forEach(val => {
                if (this.name) {
                    formData.append(this.name, val);
                }
            });
            this.internals.setFormValue(formData);
            // Update validity state for required validation
            if (this.required && valueArray.length === 0) {
                this.internals.setValidity({ valueMissing: true }, 'Please select at least one option.', this.triggerEl);
            }
            else {
                this.internals.setValidity({});
            }
        }
    }
    getCsrfToken() {
        // Use provided token if available
        if (this.csrfToken) {
            return this.csrfToken;
        }
        // Try to read from meta tag
        const metaTag = document.querySelector('meta[name="csrf-token"]');
        return (metaTag === null || metaTag === void 0 ? void 0 : metaTag.content) || null;
    }
    async fetchOptions(query, page = 1) {
        var _a;
        if (!this.asyncUrl)
            return;
        // Abort any pending request and clear existing timeout
        (_a = this.abortController) === null || _a === void 0 ? void 0 : _a.abort();
        if (this.fetchTimeoutTimer !== undefined) {
            window.clearTimeout(this.fetchTimeoutTimer);
            this.fetchTimeoutTimer = undefined;
        }
        this.abortController = new AbortController();
        this.loading = true;
        // Set up timeout for fetch request
        this.fetchTimeoutTimer = window.setTimeout(() => {
            var _a;
            (_a = this.abortController) === null || _a === void 0 ? void 0 : _a.abort();
            this.fetchTimeoutTimer = undefined;
        }, this.fetchTimeout);
        try {
            // Guard against malformed asyncUrl
            let url;
            try {
                url = new URL(this.asyncUrl, window.location.origin);
            }
            catch (urlError) {
                throw new TypeError(`Invalid asyncUrl: ${this.asyncUrl}`);
            }
            if (this.asyncMethod === 'GET') {
                url.searchParams.set('search', query);
                url.searchParams.set('page', String(page));
            }
            const csrfToken = this.getCsrfToken();
            const headers = {
                'Content-Type': 'application/json',
                'Accept': 'application/json',
            };
            if (csrfToken) {
                const headerName = this.csrfHeaderName || 'X-CSRF-Token';
                headers[headerName] = csrfToken;
            }
            const response = await fetch(url.toString(), Object.assign({ method: this.asyncMethod, signal: this.abortController.signal, headers }, (this.asyncMethod === 'POST' && {
                body: JSON.stringify({ search: query, page }),
            })));
            // Clear timeout on successful response
            if (this.fetchTimeoutTimer !== undefined) {
                window.clearTimeout(this.fetchTimeoutTimer);
                this.fetchTimeoutTimer = undefined;
            }
            if (!response.ok)
                throw new Error('Failed to fetch options');
            const data = await response.json();
            const formattedResults = data.results.map(item => {
                // If formatResult is provided, use it
                if (this.formatResult) {
                    return this.formatResult(item);
                }
                // Defensive check: ensure item is an object before spreading
                if (typeof item === 'object' && item !== null) {
                    return Object.assign({ id: item.id, text: item.text }, item);
                }
                // Handle primitives by converting to safe shape
                return {
                    id: String(item),
                    text: String(item),
                };
            });
            if (page === 1) {
                this.internalOptions = formattedResults;
            }
            else {
                this.internalOptions = [...this.internalOptions, ...formattedResults];
            }
            this.hasMore = data.totalCount ? this.internalOptions.length < data.totalCount : false;
            this.currentPage = page;
        }
        catch (error) {
            // Clear timeout on error
            if (this.fetchTimeoutTimer !== undefined) {
                window.clearTimeout(this.fetchTimeoutTimer);
                this.fetchTimeoutTimer = undefined;
            }
            if (error.name !== 'AbortError') {
                console.error('PdsMultiselect: Failed to fetch options', error);
            }
        }
        finally {
            this.loading = false;
            // Check if value changed during loading and still has unresolved values
            if (this.pendingUnresolvedFetch) {
                this.pendingUnresolvedFetch = false;
                const valueArray = this.ensureValueArray();
                const hasUnresolvedValues = valueArray.length > 0 && this.selectedItems.length < valueArray.length;
                if (hasUnresolvedValues) {
                    this.fetchOptions('', 1);
                }
            }
        }
    }
    async createOption(query) {
        if (!this.createUrl || !query.trim())
            return;
        // Reentrancy guard: prevent duplicate POSTs if creation is already in-flight
        if (this.creating)
            return;
        this.creating = true;
        try {
            const url = new URL(this.createUrl, window.location.origin);
            const csrfToken = this.getCsrfToken();
            const headers = {
                'Content-Type': 'application/json',
                'Accept': 'application/json',
            };
            if (csrfToken) {
                const headerName = this.csrfHeaderName || 'X-CSRF-Token';
                headers[headerName] = csrfToken;
            }
            const response = await fetch(url.toString(), {
                method: 'POST',
                headers,
                body: JSON.stringify({ text: query.trim() }),
            });
            if (!response.ok)
                throw new Error('Failed to create option');
            const data = await response.json();
            const newOption = Object.assign({ id: data.id, text: data.text }, data);
            // Add to internal options first
            this.internalOptions = [...this.internalOptions, newOption];
            // Select the new option
            this.value = [...this.value, String(newOption.id)];
            // Sync selected items to update display with new option
            this.syncSelectedItems();
            // Emit create event
            this.pdsMultiselectCreate.emit({
                query: query.trim(),
                newOption,
            });
            // Emit change event with synced items
            this.pdsMultiselectChange.emit({
                values: this.value,
                items: this.selectedItems,
            });
            // Clear search and keep dropdown open
            this.searchQuery = '';
            this.highlightedIndex = -1;
            // Focus back on search input
            requestAnimationFrame(() => {
                var _a;
                (_a = this.searchInputEl) === null || _a === void 0 ? void 0 : _a.focus();
            });
        }
        catch (error) {
            console.error('PdsMultiselect: Failed to create option', error);
        }
        finally {
            this.creating = false;
        }
    }
    openDropdown() {
        if (this.disabled)
            return;
        this.isOpening = true;
        this.isOpen = true;
        this.highlightedIndex = -1;
        // Trigger initial fetch if async (skip if already fetching from componentDidLoad)
        if (this.asyncUrl && this.internalOptions.length === 0 && !this.initialAsyncFetchTriggered) {
            this.debouncedFetchAsyncOptions(this.searchQuery, 1);
        }
        // Reset the flag so subsequent opens can fetch if needed
        this.initialAsyncFetchTriggered = false;
        requestAnimationFrame(() => {
            var _a;
            this.positionDropdown();
            // Focus the search input after the panel is positioned
            (_a = this.searchInputEl) === null || _a === void 0 ? void 0 : _a.focus();
            // Clear the opening flag after focus has moved
            setTimeout(() => {
                this.isOpening = false;
            }, 50);
        });
    }
    closeDropdown() {
        this.isOpen = false;
        this.highlightedIndex = -1;
        this.searchQuery = '';
        // Reset the selection-close guard after the focusout handler's setTimeout has resolved
        if (this.isClosingViaSelection) {
            setTimeout(() => {
                this.isClosingViaSelection = false;
            }, 0);
        }
        // Clean up auto-update
        if (this.cleanupAutoUpdate) {
            this.cleanupAutoUpdate();
            this.cleanupAutoUpdate = undefined;
        }
    }
    positionDropdown() {
        if (!this.containerEl || !this.panelEl)
            return;
        // In inline pill mode, triggerEl is the small chevron button — use the full-width
        // wrapper div as the Floating UI reference so the panel aligns with the whole trigger.
        const referenceEl = this.pillInlineTriggerEl || this.triggerEl || this.containerEl;
        const { minWidth, panelWidth } = this;
        const updatePosition = () => {
            computePosition(referenceEl, this.panelEl, {
                placement: 'bottom-start',
                strategy: 'absolute',
                middleware: [
                    offset(12),
                    flip(),
                    shift({ padding: 8 }),
                    size({
                        apply: ({ rects, elements }) => {
                            Object.assign(elements.floating.style, {
                                width: panelWidth !== null && panelWidth !== void 0 ? panelWidth : `${rects.reference.width}px`,
                                minWidth,
                            });
                        },
                    }),
                ],
            }).then(({ x, y }) => {
                if (this.panelEl) {
                    this.panelEl.style.left = `${x}px`;
                    this.panelEl.style.top = `${y}px`;
                }
            });
        };
        // Initial position
        updatePosition();
        // Set up auto-update for window resize and scroll
        const cleanupAutoUpdate = autoUpdate(referenceEl, this.panelEl, updatePosition);
        this.cleanupAutoUpdate = () => {
            cleanupAutoUpdate();
            this.clearAsyncFetchState();
        };
    }
    scrollOptionIntoView() {
        requestAnimationFrame(() => {
            var _a;
            const highlighted = (_a = this.listboxEl) === null || _a === void 0 ? void 0 : _a.querySelector(`[data-index="${this.highlightedIndex}"]`);
            highlighted === null || highlighted === void 0 ? void 0 : highlighted.scrollIntoView({ block: 'nearest', behavior: 'smooth' });
        });
    }
    toggleOption(option) {
        var _a, _b;
        if (option.disabled)
            return;
        // Handle create option
        if (option.isCreateOption) {
            // Prevent multiple create calls while one is in-flight
            if (!this.creating) {
                this.createOption(this.searchQuery);
            }
            return;
        }
        const optionId = String(option.id);
        const isSelected = this.value.includes(optionId);
        if (isSelected) {
            // Remove from selection
            this.value = this.value.filter(v => v !== optionId);
        }
        else {
            // Add to selection
            if (this.maxSelections && this.value.length >= this.maxSelections) {
                return;
            }
            this.value = [...this.value, optionId];
        }
        // Sync selected items to ensure no duplicates and accurate state
        this.syncSelectedItems();
        // Emit change event with synced items
        this.pdsMultiselectChange.emit({
            values: this.value,
            items: this.selectedItems,
        });
        if (this.closePanelOnSelect) {
            this.isClosingViaSelection = true;
            this.closeDropdown();
            (_a = this.triggerEl) === null || _a === void 0 ? void 0 : _a.focus();
        }
        else {
            // Keep focus on search input, don't close dropdown
            (_b = this.searchInputEl) === null || _b === void 0 ? void 0 : _b.focus();
        }
    }
    selectOption(option) {
        // For keyboard navigation - toggle the option
        this.toggleOption(option);
    }
    getGroupedRenderItems(filteredOptions) {
        const items = [];
        let currentGroup = null;
        filteredOptions.forEach((option, index) => {
            const group = option.group;
            if (group !== undefined) {
                if (!currentGroup || currentGroup.group !== group) {
                    currentGroup = { type: 'group', group, options: [] };
                    items.push(currentGroup);
                }
                currentGroup.options.push({ option, index });
            }
            else {
                currentGroup = null;
                items.push({ type: 'option', option, index });
            }
        });
        return items;
    }
    renderOption(option, index, valueArray) {
        const isSelected = valueArray.includes(String(option.id));
        const isCreateOption = option.isCreateOption;
        const isDisabled = option.disabled;
        const isHighlighted = index === this.highlightedIndex && !isCreateOption;
        const optionId = `${this.componentId}-option-${index}`;
        const isCreateDisabled = isCreateOption && this.creating;
        return (h("li", { key: String(option.id), id: optionId, class: {
                'pds-multiselect__option': true,
                'pds-multiselect__option--highlighted': isHighlighted,
                'pds-multiselect__option--selected': isSelected,
                'pds-multiselect__option--create': isCreateOption,
                'pds-multiselect__option--disabled': isDisabled || isCreateDisabled,
            }, role: "option", "aria-selected": isSelected ? 'true' : 'false', "aria-disabled": isDisabled || isCreateDisabled ? 'true' : undefined, "aria-label": isCreateOption ? `Create new tag: ${option.text}` : undefined, "data-index": index, onMouseDown: this.handleOptionMouseDown(option), onMouseEnter: this.handleOptionMouseEnter(index, option) }, isCreateOption ? (h("pds-box", { class: "pds-multiselect__create-option", "align-items": "center", gap: "xs" }, h("pds-icon", { name: "add", size: "small" }), h("pds-text", null, "Add \"", option.text, "\""))) : (h("pds-checkbox", { componentId: `${this.componentId}-checkbox-${index}`, checked: isSelected, label: option.text, disabled: isDisabled, style: { pointerEvents: 'none' } }))));
    }
    renderSelectedItemsList() {
        if (this.hideSelectedItems || this.selectedItems.length === 0)
            return null;
        return (h("div", { class: "pds-multiselect__selected-section" }, h("ul", { class: "pds-multiselect__selected-list", role: "list" }, this.selectedItems.map(item => (h("li", { key: String(item.id), class: "pds-multiselect__selected-item" }, item.text))))));
    }
    renderDropdown() {
        if (!this.isOpen)
            return null;
        const filteredOptions = this.getFilteredOptions();
        const valueArray = this.ensureValueArray();
        const hasSlottedEmpty = !!this.el.querySelector('[slot="empty"]');
        const hasSlottedLoading = !!this.el.querySelector('[slot="loading"]');
        return (h("div", { class: "pds-multiselect__panel", ref: el => (this.panelEl = el), style: { minWidth: this.minWidth } }, h("div", { class: "pds-multiselect__search" }, h("pds-icon", { name: "search", size: "small" }), h("input", { ref: el => (this.searchInputEl = el), type: "text", class: "pds-multiselect__search-input", placeholder: this.searchPlaceholder, value: this.searchQuery, "aria-label": "Search options", "aria-controls": `${this.componentId}-listbox`, "aria-activedescendant": this.highlightedIndex >= 0 ? `${this.componentId}-option-${this.highlightedIndex}` : undefined, role: "combobox", "aria-haspopup": "listbox", "aria-expanded": "true", "aria-autocomplete": "list", autocomplete: "off", onInput: this.handleSearchInputChange, onKeyDown: this.handleSearchInputKeyDown })), this.renderSelectedItemsList(), h("ul", { class: "pds-multiselect__listbox", role: "listbox", "aria-multiselectable": "true", "aria-label": this.label || 'Options', id: `${this.componentId}-listbox`, ref: el => (this.listboxEl = el), style: { maxHeight: this.maxHeight }, onScroll: this.handleScroll }, this.loading && (h("li", { class: "pds-multiselect__loading", role: "presentation" }, hasSlottedLoading ? (h("slot", { name: "loading" })) : (h("pds-loader", { size: "small" })))), !this.loading && filteredOptions.length === 0 && (h("li", { class: "pds-multiselect__empty", role: "presentation" }, hasSlottedEmpty ? (h("slot", { name: "empty" })) : (h("span", null, "No options found")))), this.getGroupedRenderItems(filteredOptions).map((item, itemIndex) => {
            if (item.type === 'group') {
                return (h("li", { key: `group-${itemIndex}`, role: "group", "aria-label": item.group, class: "pds-multiselect__group" }, h("span", { class: "pds-multiselect__group-header", "aria-hidden": "true" }, item.group), h("ul", { class: "pds-multiselect__group-list", role: "presentation" }, item.options.map(({ option, index }) => this.renderOption(option, index, valueArray)))));
            }
            return this.renderOption(item.option, item.index, valueArray);
        }), this.hasMore && !this.loading && (h("li", { class: "pds-multiselect__load-more", role: "presentation" }, h("pds-loader", { size: "small" }))))));
    }
    renderInlinePills() {
        const hasSelections = this.selectedItems.length > 0;
        if (!hasSelections) {
            return (h("span", { class: "pds-multiselect__trigger-text pds-multiselect__trigger-text--placeholder" }, this.placeholder || 'Select...'));
        }
        const variant = this.disabled ? 'text' : 'tag';
        const visibleItems = this.selectedItems.slice(0, Math.max(1, this.maxInlinePills));
        const overflowCount = this.selectedItems.length - visibleItems.length;
        return (h("div", { class: "pds-multiselect__pill-list pds-multiselect__pill-list--inline", "aria-label": "Selected items" }, visibleItems.map(item => (h("pds-chip", { key: String(item.id), "component-id": `${this.componentId}-pill-${item.id}`, variant: variant, size: "sm", sentiment: "neutral", onPdsTagCloseClick: this.handlePillRemove(item) }, item.text))), overflowCount > 0 && (h("span", { class: "pds-multiselect__pill-overflow" }, "+", overflowCount))));
    }
    renderBelowPills() {
        if (this.selectedItems.length === 0)
            return null;
        const variant = this.disabled ? 'text' : 'tag';
        return (h("div", { class: "pds-multiselect__pill-list pds-multiselect__pill-list--below", "aria-label": "Selected items" }, this.selectedItems.map(item => (h("pds-chip", { key: String(item.id), "component-id": `${this.componentId}-pill-${item.id}`, variant: variant, size: "md", sentiment: "neutral", onPdsTagCloseClick: this.handlePillRemove(item) }, item.text)))));
    }
    getTriggerText() {
        const count = this.selectedItems.length;
        if (count === 0 || (this.selectedDisplay === 'pill' && this.pillPosition === 'below')) {
            return this.placeholder || 'Select...';
        }
        return `${count} item${count === 1 ? '' : 's'}`;
    }
    render() {
        const hasSelections = this.selectedItems.length > 0;
        return (h(Host, { key: '086b6d2d199b35110e64ba1da3c12f1a81360581', "aria-disabled": this.disabled ? 'true' : null }, h("div", { key: 'c2faf217a97de056a9111a78e7077025e4ab5ee1', class: "pds-multiselect", onFocusout: this.handleContainerFocusOut }, this.label && (h("label", { key: '0aff86a3a0054aef256bac5c0204dd914a9bd3ae', id: `${this.componentId}-label`, htmlFor: this.componentId, class: {
                'pds-multiselect__label': true,
                'visually-hidden': this.hideLabel,
            } }, this.label)), h("div", { key: '3b92119871dc32bb3c9a559be816bdece2fc30a6', class: "pds-multiselect__wrapper", ref: el => (this.containerEl = el), style: { width: this.triggerWidth } }, this.selectedDisplay === 'pill' && this.pillPosition === 'inline' ? (h("div", { ref: el => (this.pillInlineTriggerEl = el || undefined), part: "trigger", class: {
                'pds-multiselect__trigger': true,
                'pds-multiselect__trigger--open': this.isOpen,
                'pds-multiselect__trigger--disabled': this.disabled,
                'pds-multiselect__trigger--invalid': this.invalid || !!this.errorMessage,
                'pds-multiselect__trigger--has-value': hasSelections,
                'pds-multiselect__trigger--pill-inline': true,
            }, onClick: this.handleTriggerClick }, this.renderInlinePills(), h("button", { ref: el => (this.triggerEl = el), type: "button", id: this.componentId, part: "pill-toggle", class: "pds-multiselect__pill-toggle", disabled: this.disabled, "aria-expanded": this.isOpen ? 'true' : 'false', "aria-haspopup": "listbox", "aria-labelledby": this.label ? `${this.componentId}-label` : undefined, "aria-required": this.required ? 'true' : undefined, "aria-describedby": assignDescription(this.componentId, this.invalid || !!this.errorMessage, this.errorMessage || this.helperMessage), "aria-invalid": this.invalid || !!this.errorMessage ? 'true' : undefined, onKeyDown: this.handleTriggerKeyDown }, h("pds-icon", { class: "pds-multiselect__icon", icon: enlarge })))) : (h("button", { ref: el => (this.triggerEl = el), type: "button", part: "trigger", class: {
                'pds-multiselect__trigger': true,
                'pds-multiselect__trigger--open': this.isOpen,
                'pds-multiselect__trigger--disabled': this.disabled,
                'pds-multiselect__trigger--invalid': this.invalid || !!this.errorMessage,
                'pds-multiselect__trigger--has-value': hasSelections,
            }, id: this.componentId, disabled: this.disabled, "aria-required": this.required ? 'true' : undefined, "aria-expanded": this.isOpen ? 'true' : 'false', "aria-haspopup": "listbox", "aria-describedby": assignDescription(this.componentId, this.invalid || !!this.errorMessage, this.errorMessage || this.helperMessage), "aria-invalid": this.invalid || !!this.errorMessage ? 'true' : undefined, onClick: this.handleTriggerClick, onKeyDown: this.handleTriggerKeyDown }, h("span", { class: {
                'pds-multiselect__trigger-text': true,
                'pds-multiselect__trigger-text--placeholder': !hasSelections || (this.selectedDisplay === 'pill' && this.pillPosition === 'below'),
            } }, this.getTriggerText()), h("pds-icon", { class: "pds-multiselect__icon", icon: enlarge }))), this.renderDropdown()), this.selectedDisplay === 'pill' && this.pillPosition === 'below' && this.renderBelowPills(), h("span", { key: '07d6a6bf9ccc2fe8bfcf9502a1cd4b7abf5c5e6f', class: "visually-hidden", "aria-live": "polite", "aria-atomic": "true" }, this.removalAnnouncement), this.helperMessage && !(this.errorMessage && this.errorMessage.length > 0) && (h("p", { key: 'a94bb6f31805d31ec3cd5b5f2ca8b1984f4d41d1', class: "pds-multiselect__helper", id: messageId(this.componentId, 'helper') }, this.helperMessage)), this.errorMessage && (h("p", { key: '15afc6b9313e1fe22bd56b00eee1ebcf9e085c2e', class: "pds-multiselect__error", id: messageId(this.componentId, 'error') }, h("pds-icon", { key: '452814577b14ca8f891b101903bd1a7a7e5d8bb1', icon: danger, size: "small" }), this.errorMessage)), h("div", { key: 'd8ce787886e666303759cd2654c78b44269a40ff', style: { display: 'none' } }, h("slot", { key: '8295376fb03b23d44c5baeca9bc967514e433e0f' })))));
    }
    static get is() { return "pds-multiselect"; }
    static get encapsulation() { return "shadow"; }
    static get formAssociated() { return true; }
    static get originalStyleUrls() {
        return {
            "$": ["pds-multiselect.scss"]
        };
    }
    static get styleUrls() {
        return {
            "$": ["pds-multiselect.css"]
        };
    }
    static get properties() {
        return {
            "componentId": {
                "type": "string",
                "mutable": false,
                "complexType": {
                    "original": "string",
                    "resolved": "string",
                    "references": {}
                },
                "required": true,
                "optional": false,
                "docs": {
                    "tags": [],
                    "text": "A unique identifier used for the underlying component `id` attribute."
                },
                "getter": false,
                "setter": false,
                "reflect": false,
                "attribute": "component-id"
            },
            "label": {
                "type": "string",
                "mutable": false,
                "complexType": {
                    "original": "string",
                    "resolved": "string",
                    "references": {}
                },
                "required": false,
                "optional": true,
                "docs": {
                    "tags": [],
                    "text": "Text to be displayed as the multiselect label."
                },
                "getter": false,
                "setter": false,
                "reflect": false,
                "attribute": "label"
            },
            "placeholder": {
                "type": "string",
                "mutable": false,
                "complexType": {
                    "original": "string",
                    "resolved": "string",
                    "references": {}
                },
                "required": false,
                "optional": true,
                "docs": {
                    "tags": [],
                    "text": "Placeholder text for the input field."
                },
                "getter": false,
                "setter": false,
                "reflect": false,
                "attribute": "placeholder",
                "defaultValue": "'Select...'"
            },
            "searchPlaceholder": {
                "type": "string",
                "mutable": false,
                "complexType": {
                    "original": "string",
                    "resolved": "string",
                    "references": {}
                },
                "required": false,
                "optional": false,
                "docs": {
                    "tags": [{
                            "name": "default",
                            "text": "'Find...'"
                        }],
                    "text": "Placeholder text for the search input inside the dropdown panel."
                },
                "getter": false,
                "setter": false,
                "reflect": false,
                "attribute": "search-placeholder",
                "defaultValue": "'Find...'"
            },
            "closePanelOnSelect": {
                "type": "boolean",
                "mutable": false,
                "complexType": {
                    "original": "boolean",
                    "resolved": "boolean",
                    "references": {}
                },
                "required": false,
                "optional": false,
                "docs": {
                    "tags": [],
                    "text": "Whether to close the panel after an option is selected.\nDefaults to `false` (panel stays open for multi-select)."
                },
                "getter": false,
                "setter": false,
                "reflect": false,
                "attribute": "close-panel-on-select",
                "defaultValue": "false"
            },
            "name": {
                "type": "string",
                "mutable": false,
                "complexType": {
                    "original": "string",
                    "resolved": "string",
                    "references": {}
                },
                "required": false,
                "optional": true,
                "docs": {
                    "tags": [],
                    "text": "Specifies the name. Submitted with the form as part of a name/value pair."
                },
                "getter": false,
                "setter": false,
                "reflect": false,
                "attribute": "name"
            },
            "value": {
                "type": "unknown",
                "mutable": true,
                "complexType": {
                    "original": "string[]",
                    "resolved": "string[]",
                    "references": {}
                },
                "required": false,
                "optional": false,
                "docs": {
                    "tags": [],
                    "text": "Array of selected option values."
                },
                "getter": false,
                "setter": false,
                "defaultValue": "[]"
            },
            "disabled": {
                "type": "boolean",
                "mutable": false,
                "complexType": {
                    "original": "boolean",
                    "resolved": "boolean",
                    "references": {}
                },
                "required": false,
                "optional": false,
                "docs": {
                    "tags": [],
                    "text": "Determines whether or not the multiselect is disabled."
                },
                "getter": false,
                "setter": false,
                "reflect": false,
                "attribute": "disabled",
                "defaultValue": "false"
            },
            "asyncUrl": {
                "type": "string",
                "mutable": false,
                "complexType": {
                    "original": "string",
                    "resolved": "string",
                    "references": {}
                },
                "required": false,
                "optional": true,
                "docs": {
                    "tags": [],
                    "text": "URL endpoint for async data fetching."
                },
                "getter": false,
                "setter": false,
                "reflect": false,
                "attribute": "async-url"
            },
            "asyncMethod": {
                "type": "string",
                "mutable": false,
                "complexType": {
                    "original": "'GET' | 'POST'",
                    "resolved": "\"GET\" | \"POST\"",
                    "references": {}
                },
                "required": false,
                "optional": false,
                "docs": {
                    "tags": [],
                    "text": "HTTP method for async requests."
                },
                "getter": false,
                "setter": false,
                "reflect": false,
                "attribute": "async-method",
                "defaultValue": "'GET'"
            },
            "debounce": {
                "type": "number",
                "mutable": false,
                "complexType": {
                    "original": "number",
                    "resolved": "number",
                    "references": {}
                },
                "required": false,
                "optional": false,
                "docs": {
                    "tags": [],
                    "text": "Debounce delay in milliseconds for search/fetch."
                },
                "getter": false,
                "setter": false,
                "reflect": false,
                "attribute": "debounce",
                "defaultValue": "300"
            },
            "fetchTimeout": {
                "type": "number",
                "mutable": false,
                "complexType": {
                    "original": "number",
                    "resolved": "number",
                    "references": {}
                },
                "required": false,
                "optional": false,
                "docs": {
                    "tags": [],
                    "text": "Timeout in milliseconds for async fetch requests."
                },
                "getter": false,
                "setter": false,
                "reflect": false,
                "attribute": "fetch-timeout",
                "defaultValue": "30000"
            },
            "maxSelections": {
                "type": "number",
                "mutable": false,
                "complexType": {
                    "original": "number",
                    "resolved": "number",
                    "references": {}
                },
                "required": false,
                "optional": true,
                "docs": {
                    "tags": [],
                    "text": "Maximum number of selections allowed."
                },
                "getter": false,
                "setter": false,
                "reflect": false,
                "attribute": "max-selections"
            },
            "maxHeight": {
                "type": "string",
                "mutable": false,
                "complexType": {
                    "original": "string",
                    "resolved": "string",
                    "references": {}
                },
                "required": false,
                "optional": false,
                "docs": {
                    "tags": [],
                    "text": "Maximum height of the dropdown before scrolling."
                },
                "getter": false,
                "setter": false,
                "reflect": false,
                "attribute": "max-height",
                "defaultValue": "'300px'"
            },
            "triggerWidth": {
                "type": "string",
                "mutable": false,
                "complexType": {
                    "original": "string",
                    "resolved": "string",
                    "references": {}
                },
                "required": false,
                "optional": false,
                "docs": {
                    "tags": [],
                    "text": "Width of the trigger button (and reference for dropdown positioning)."
                },
                "getter": false,
                "setter": false,
                "reflect": false,
                "attribute": "trigger-width",
                "defaultValue": "'100%'"
            },
            "minWidth": {
                "type": "string",
                "mutable": false,
                "complexType": {
                    "original": "string",
                    "resolved": "string",
                    "references": {}
                },
                "required": false,
                "optional": false,
                "docs": {
                    "tags": [],
                    "text": "Minimum width of the dropdown panel."
                },
                "getter": false,
                "setter": false,
                "reflect": false,
                "attribute": "min-width",
                "defaultValue": "'250px'"
            },
            "panelWidth": {
                "type": "string",
                "mutable": false,
                "complexType": {
                    "original": "string",
                    "resolved": "string",
                    "references": {}
                },
                "required": false,
                "optional": true,
                "docs": {
                    "tags": [],
                    "text": "Width of the dropdown panel. Defaults to the trigger width."
                },
                "getter": false,
                "setter": false,
                "reflect": false,
                "attribute": "panel-width"
            },
            "hideLabel": {
                "type": "boolean",
                "mutable": false,
                "complexType": {
                    "original": "boolean",
                    "resolved": "boolean",
                    "references": {}
                },
                "required": false,
                "optional": false,
                "docs": {
                    "tags": [],
                    "text": "Visually hides the label but keeps it accessible."
                },
                "getter": false,
                "setter": false,
                "reflect": false,
                "attribute": "hide-label",
                "defaultValue": "false"
            },
            "hideSelectedItems": {
                "type": "boolean",
                "mutable": false,
                "complexType": {
                    "original": "boolean",
                    "resolved": "boolean",
                    "references": {}
                },
                "required": false,
                "optional": false,
                "docs": {
                    "tags": [],
                    "text": "Hides the selected items summary section in the dropdown panel."
                },
                "getter": false,
                "setter": false,
                "reflect": false,
                "attribute": "hide-selected-items",
                "defaultValue": "false"
            },
            "selectedDisplay": {
                "type": "string",
                "mutable": false,
                "complexType": {
                    "original": "'count' | 'pill'",
                    "resolved": "\"count\" | \"pill\"",
                    "references": {}
                },
                "required": false,
                "optional": false,
                "docs": {
                    "tags": [{
                            "name": "default",
                            "text": "'count'"
                        }],
                    "text": "Display mode for selected items outside the dropdown panel.\n`'count'` shows \"N item(s)\" text in the trigger (default). `'pill'` renders\nselected items as dismissible pds-chip tags."
                },
                "getter": false,
                "setter": false,
                "reflect": false,
                "attribute": "selected-display",
                "defaultValue": "'count'"
            },
            "pillPosition": {
                "type": "string",
                "mutable": false,
                "complexType": {
                    "original": "'inline' | 'below'",
                    "resolved": "\"below\" | \"inline\"",
                    "references": {}
                },
                "required": false,
                "optional": false,
                "docs": {
                    "tags": [{
                            "name": "default",
                            "text": "'inline'"
                        }],
                    "text": "Position of pill chips when `selectedDisplay` is `'pill'`.\n`'inline'` places chips inside the trigger; `'below'` places chips in a\nflex-wrap row directly below the trigger."
                },
                "getter": false,
                "setter": false,
                "reflect": false,
                "attribute": "pill-position",
                "defaultValue": "'inline'"
            },
            "maxInlinePills": {
                "type": "number",
                "mutable": false,
                "complexType": {
                    "original": "number",
                    "resolved": "number",
                    "references": {}
                },
                "required": false,
                "optional": false,
                "docs": {
                    "tags": [{
                            "name": "default",
                            "text": "3"
                        }],
                    "text": "Maximum chips shown inline before collapsing to a \"+N more\" badge.\nOnly applies when `selectedDisplay='pill'` and `pillPosition='inline'`."
                },
                "getter": false,
                "setter": false,
                "reflect": false,
                "attribute": "max-inline-pills",
                "defaultValue": "3"
            },
            "errorMessage": {
                "type": "string",
                "mutable": false,
                "complexType": {
                    "original": "string",
                    "resolved": "string",
                    "references": {}
                },
                "required": false,
                "optional": true,
                "docs": {
                    "tags": [],
                    "text": "Error message to display."
                },
                "getter": false,
                "setter": false,
                "reflect": false,
                "attribute": "error-message"
            },
            "helperMessage": {
                "type": "string",
                "mutable": false,
                "complexType": {
                    "original": "string",
                    "resolved": "string",
                    "references": {}
                },
                "required": false,
                "optional": true,
                "docs": {
                    "tags": [],
                    "text": "Helper message to display below the input."
                },
                "getter": false,
                "setter": false,
                "reflect": false,
                "attribute": "helper-message"
            },
            "invalid": {
                "type": "boolean",
                "mutable": false,
                "complexType": {
                    "original": "boolean",
                    "resolved": "boolean",
                    "references": {}
                },
                "required": false,
                "optional": true,
                "docs": {
                    "tags": [],
                    "text": "If true, the multiselect is in an invalid state."
                },
                "getter": false,
                "setter": false,
                "reflect": false,
                "attribute": "invalid"
            },
            "required": {
                "type": "boolean",
                "mutable": false,
                "complexType": {
                    "original": "boolean",
                    "resolved": "boolean",
                    "references": {}
                },
                "required": false,
                "optional": false,
                "docs": {
                    "tags": [],
                    "text": "If true, the multiselect is required."
                },
                "getter": false,
                "setter": false,
                "reflect": false,
                "attribute": "required",
                "defaultValue": "false"
            },
            "loading": {
                "type": "boolean",
                "mutable": true,
                "complexType": {
                    "original": "boolean",
                    "resolved": "boolean",
                    "references": {}
                },
                "required": false,
                "optional": false,
                "docs": {
                    "tags": [],
                    "text": "Whether the component is currently loading async options."
                },
                "getter": false,
                "setter": false,
                "reflect": false,
                "attribute": "loading",
                "defaultValue": "false"
            },
            "options": {
                "type": "unknown",
                "mutable": false,
                "complexType": {
                    "original": "MultiselectOption[]",
                    "resolved": "MultiselectOption[]",
                    "references": {
                        "MultiselectOption": {
                            "location": "import",
                            "path": "./multiselect-interface",
                            "id": "src/components/pds-multiselect/multiselect-interface.ts::MultiselectOption"
                        }
                    }
                },
                "required": false,
                "optional": true,
                "docs": {
                    "tags": [],
                    "text": "Options provided externally (for consumer-managed async).\nWhen using `group` on options, keep each group block contiguous in the array.\nThe same `group` label appearing again after other items produces a separate header (same as native `<optgroup>`)."
                },
                "getter": false,
                "setter": false
            },
            "formatResult": {
                "type": "unknown",
                "mutable": false,
                "complexType": {
                    "original": "(item: unknown) => MultiselectOption",
                    "resolved": "(item: unknown) => MultiselectOption",
                    "references": {
                        "MultiselectOption": {
                            "location": "import",
                            "path": "./multiselect-interface",
                            "id": "src/components/pds-multiselect/multiselect-interface.ts::MultiselectOption"
                        }
                    }
                },
                "required": false,
                "optional": true,
                "docs": {
                    "tags": [],
                    "text": "Function to format async results. Receives raw API response item."
                },
                "getter": false,
                "setter": false
            },
            "createUrl": {
                "type": "string",
                "mutable": false,
                "complexType": {
                    "original": "string",
                    "resolved": "string",
                    "references": {}
                },
                "required": false,
                "optional": true,
                "docs": {
                    "tags": [],
                    "text": "URL endpoint for creating new options. When set, shows \"Add\" option when no matches found."
                },
                "getter": false,
                "setter": false,
                "reflect": false,
                "attribute": "create-url"
            },
            "csrfToken": {
                "type": "string",
                "mutable": false,
                "complexType": {
                    "original": "string",
                    "resolved": "string",
                    "references": {}
                },
                "required": false,
                "optional": true,
                "docs": {
                    "tags": [],
                    "text": "CSRF token for authenticated requests. If not provided, attempts to read from meta tag."
                },
                "getter": false,
                "setter": false,
                "reflect": false,
                "attribute": "csrf-token"
            },
            "csrfHeaderName": {
                "type": "string",
                "mutable": false,
                "complexType": {
                    "original": "string",
                    "resolved": "string",
                    "references": {}
                },
                "required": false,
                "optional": true,
                "docs": {
                    "tags": [],
                    "text": "CSRF header name for authenticated requests. Defaults to `X-CSRF-Token`."
                },
                "getter": false,
                "setter": false,
                "reflect": false,
                "attribute": "csrf-header-name",
                "defaultValue": "'X-CSRF-Token'"
            }
        };
    }
    static get states() {
        return {
            "isOpen": {},
            "searchQuery": {},
            "highlightedIndex": {},
            "internalOptions": {},
            "selectedItems": {},
            "currentPage": {},
            "hasMore": {},
            "creating": {},
            "removalAnnouncement": {}
        };
    }
    static get events() {
        return [{
                "method": "pdsMultiselectChange",
                "name": "pdsMultiselectChange",
                "bubbles": true,
                "cancelable": true,
                "composed": true,
                "docs": {
                    "tags": [],
                    "text": "Emitted when selection changes."
                },
                "complexType": {
                    "original": "MultiselectChangeEventDetail",
                    "resolved": "MultiselectChangeEventDetail",
                    "references": {
                        "MultiselectChangeEventDetail": {
                            "location": "import",
                            "path": "./multiselect-interface",
                            "id": "src/components/pds-multiselect/multiselect-interface.ts::MultiselectChangeEventDetail"
                        }
                    }
                }
            }, {
                "method": "pdsMultiselectSearch",
                "name": "pdsMultiselectSearch",
                "bubbles": true,
                "cancelable": true,
                "composed": true,
                "docs": {
                    "tags": [],
                    "text": "Emitted on search input (for consumer-managed async)."
                },
                "complexType": {
                    "original": "MultiselectSearchEventDetail",
                    "resolved": "MultiselectSearchEventDetail",
                    "references": {
                        "MultiselectSearchEventDetail": {
                            "location": "import",
                            "path": "./multiselect-interface",
                            "id": "src/components/pds-multiselect/multiselect-interface.ts::MultiselectSearchEventDetail"
                        }
                    }
                }
            }, {
                "method": "pdsMultiselectLoadOptions",
                "name": "pdsMultiselectLoadOptions",
                "bubbles": true,
                "cancelable": true,
                "composed": true,
                "docs": {
                    "tags": [],
                    "text": "Emitted to request more options (pagination)."
                },
                "complexType": {
                    "original": "MultiselectLoadOptionsEventDetail",
                    "resolved": "MultiselectLoadOptionsEventDetail",
                    "references": {
                        "MultiselectLoadOptionsEventDetail": {
                            "location": "import",
                            "path": "./multiselect-interface",
                            "id": "src/components/pds-multiselect/multiselect-interface.ts::MultiselectLoadOptionsEventDetail"
                        }
                    }
                }
            }, {
                "method": "pdsMultiselectCreate",
                "name": "pdsMultiselectCreate",
                "bubbles": true,
                "cancelable": true,
                "composed": true,
                "docs": {
                    "tags": [],
                    "text": "Emitted when a new option is created."
                },
                "complexType": {
                    "original": "MultiselectCreateEventDetail",
                    "resolved": "MultiselectCreateEventDetail",
                    "references": {
                        "MultiselectCreateEventDetail": {
                            "location": "import",
                            "path": "./multiselect-interface",
                            "id": "src/components/pds-multiselect/multiselect-interface.ts::MultiselectCreateEventDetail"
                        }
                    }
                }
            }, {
                "method": "pdsMultiselectDismiss",
                "name": "pdsMultiselectDismiss",
                "bubbles": true,
                "cancelable": true,
                "composed": true,
                "docs": {
                    "tags": [],
                    "text": "Emitted when the dropdown is dismissed via Escape key or click outside.\n\nThis event fires only when the user explicitly dismisses the panel without making a selection:\n- \u2705 Fires: Pressing Escape key while dropdown is open\n- \u2705 Fires: Clicking outside the component while dropdown is open\n- \u274C Does NOT fire: When panel closes due to selection (including when `closePanelOnSelect` is true)\n- \u274C Does NOT fire: When panel closes programmatically via `closeDropdown()`\n\nEquivalent to Sage's `onEscapeHook`. Use this to restore parent UI state or run cleanup when the user cancels their interaction."
                },
                "complexType": {
                    "original": "void",
                    "resolved": "void",
                    "references": {}
                }
            }];
    }
    static get methods() {
        return {
            "setFocus": {
                "complexType": {
                    "signature": "() => Promise<void>",
                    "parameters": [],
                    "references": {
                        "Promise": {
                            "location": "global",
                            "id": "global::Promise"
                        }
                    },
                    "return": "Promise<void>"
                },
                "docs": {
                    "text": "Sets focus on the trigger button.",
                    "tags": []
                }
            },
            "clear": {
                "complexType": {
                    "signature": "() => Promise<void>",
                    "parameters": [],
                    "references": {
                        "Promise": {
                            "location": "global",
                            "id": "global::Promise"
                        }
                    },
                    "return": "Promise<void>"
                },
                "docs": {
                    "text": "Clears all selected values and resets the component.",
                    "tags": []
                }
            }
        };
    }
    static get elementRef() { return "el"; }
    static get watchers() {
        return [{
                "propName": "debounce",
                "methodName": "setupDebounce"
            }, {
                "propName": "value",
                "methodName": "valueChanged"
            }, {
                "propName": "options",
                "methodName": "optionsChanged"
            }, {
                "propName": "internalOptions",
                "methodName": "internalOptionsChanged"
            }];
    }
    static get listeners() {
        return [{
                "name": "keydown",
                "method": "handleWindowKeyDown",
                "target": "window",
                "capture": false,
                "passive": false
            }];
    }
}
//# sourceMappingURL=pds-multiselect.js.map
