import { __decorate } from "tslib";
import { attr, observable } from '@ni/fast-element';
import { DesignSystem, FoundationElement } from '@ni/fast-foundation';
import { uniqueId } from '@ni/fast-web-utilities';
import { chipTag } from '@ni/nimble-components/dist/esm/chip';
import { diacriticInsensitiveStringNormalizer } from '@ni/nimble-components/dist/esm/utilities/models/string-normalizers';
import { styles } from './styles';
import { template } from './template';
/**
 * A chip picker with inline text entry, removable chips, and a dropdown option list.
 */
export class FvChipSelector extends FoundationElement {
    constructor() {
        super(...arguments);
        this.disabled = false;
        this.open = false;
        this.label = '';
        this.selectedValues = '';
        this.options = '';
        this.placeholder = 'Select values';
        this.allowCustomValues = false;
        /** @internal */
        this.filterText = '';
        /** @internal */
        this.activeOptionIndex = -1;
        /** @internal */
        this.activeOptionId = null;
        /** @internal */
        this.inputId = uniqueId('ok-fv-chip-selector-input');
        /** @internal */
        this.labelId = uniqueId('ok-fv-chip-selector-label');
        /** @internal */
        this.menuId = uniqueId('ok-fv-chip-selector-menu');
        this.inputElement = null;
        this.menuButtonElement = null;
        this.documentClickHandler = (event) => {
            if (this.open && !event.composedPath().includes(this)) {
                this.setOpen(false);
            }
        };
    }
    /** @internal */
    disabledChanged() {
        if (this.disabled) {
            this.filterText = '';
            this.open = false;
        }
    }
    /** @internal */
    openChanged() {
        this.syncMenuButtonState();
        if (!this.open) {
            this.activeOptionIndex = -1;
        }
    }
    /** @internal */
    activeOptionIndexChanged() {
        this.syncActiveOption();
    }
    /** @internal */
    filterTextChanged() {
        this.activeOptionIndex = -1;
    }
    /** @internal */
    optionsChanged() { }
    /** @internal */
    allowCustomValuesChanged() { }
    /** @internal */
    selectedValuesChanged() { }
    /** @internal */
    connectedCallback() {
        super.connectedCallback();
        document.addEventListener('click', this.documentClickHandler);
    }
    /** @internal */
    disconnectedCallback() {
        document.removeEventListener('click', this.documentClickHandler);
        super.disconnectedCallback();
    }
    /** @internal */
    captureInputRef(input) {
        this.inputElement = input;
    }
    /** @internal */
    captureMenuButtonRef(menuButton) {
        this.menuButtonElement = menuButton;
        this.syncMenuButtonState();
    }
    /** @internal */
    regionChanged(_prev, _next) {
        if (this.region && this.field) {
            this.region.anchorElement = this.field;
        }
    }
    /** @internal */
    fieldChanged(_prev, _next) {
        if (this.region) {
            this.region.anchorElement = this.field ?? null;
        }
    }
    /** @internal */
    get selectedValueList() {
        return Array.from(new Set(this.selectedValues
            .split(',')
            .map(value => value.trim())
            .filter(value => value.length > 0)));
    }
    /** @internal */
    get optionList() {
        return Array.from(new Set(this.options
            .split(',')
            .map(option => option.trim())
            .filter(option => option.length > 0)));
    }
    /** @internal */
    get visibleOptionList() {
        const selectedValues = new Set(this.selectedValueList);
        const normalizedFilter = diacriticInsensitiveStringNormalizer(this.filterText.trim());
        return this.optionList.filter(option => {
            if (selectedValues.has(option)) {
                return false;
            }
            return normalizedFilter.length === 0
                || diacriticInsensitiveStringNormalizer(option).includes(normalizedFilter);
        });
    }
    /** @internal */
    get customValueCandidate() {
        const trimmedFilterText = this.filterText.trim();
        if (!this.allowCustomValues || trimmedFilterText.length === 0 || trimmedFilterText.includes(',')) {
            return '';
        }
        if (this.optionList.includes(trimmedFilterText) || this.selectedValueList.includes(trimmedFilterText)) {
            return '';
        }
        return trimmedFilterText;
    }
    /** @internal */
    get createOptionLabel() {
        return `Add '${this.customValueCandidate}'`;
    }
    /** @internal */
    get showEmptyState() {
        return this.visibleOptionList.length === 0 && this.customValueCandidate.length === 0;
    }
    /** @internal */
    handleFieldClick(event) {
        if (this.disabled) {
            return;
        }
        const target = event.target;
        if (target?.closest(chipTag) || target?.closest('.chip-selector-menu-button')) {
            return;
        }
        this.setOpen(true);
        this.inputElement?.focus();
    }
    /** @internal */
    handleMenuButtonClick(event) {
        event.stopPropagation();
        event.preventDefault();
        if (this.disabled) {
            return;
        }
        this.setOpen(!this.open);
        this.inputElement?.focus();
    }
    /** @internal */
    handleMenuButtonChange(event) {
        // Prevent internal toggle-button change events from surfacing as chip-selector selection changes.
        event.stopPropagation();
        this.syncMenuButtonState();
    }
    /** @internal */
    handleInput(event) {
        const input = event.target;
        this.filterText = input.value;
        this.setOpen(true);
    }
    /** @internal */
    handleInputFocus() {
        if (!this.disabled) {
            this.setOpen(true);
        }
    }
    /** @internal */
    handleInputKeydown(event) {
        if (this.disabled) {
            return true;
        }
        if (event.key === 'ArrowDown' || event.key === 'ArrowUp') {
            event.preventDefault();
            if (!this.open) {
                this.setOpen(true);
            }
            const maxIndex = this.visibleOptionList.length + (this.customValueCandidate ? 1 : 0) - 1;
            if (maxIndex < 0) {
                return false;
            }
            if (event.key === 'ArrowDown') {
                this.activeOptionIndex = Math.min(this.activeOptionIndex + 1, maxIndex);
            }
            else {
                this.activeOptionIndex = Math.max(this.activeOptionIndex - 1, 0);
            }
            return false;
        }
        if (event.key === 'Enter') {
            if (this.open && this.activeOptionIndex >= 0) {
                event.preventDefault();
                this.commitActiveOption();
                return false;
            }
            return true;
        }
        if (event.key === 'Escape') {
            event.preventDefault();
            this.filterText = '';
            this.setOpen(false);
            return false;
        }
        if (event.key === 'Backspace' && this.filterText.length === 0) {
            const selectedValues = this.selectedValueList;
            const lastValue = selectedValues[selectedValues.length - 1];
            if (lastValue) {
                event.preventDefault();
                this.removeValue(lastValue);
            }
            return false;
        }
        return true;
    }
    /** @internal */
    handleChipRemove(event) {
        const chip = event.composedPath().find(element => element instanceof HTMLElement && element.tagName.toLowerCase() === chipTag);
        const value = chip?.getAttribute('data-chip-value')?.trim();
        if (value) {
            this.removeValue(value);
        }
    }
    /** @internal */
    handleMenuClick(event) {
        const optionEl = event.composedPath().find(el => el instanceof HTMLElement && el.hasAttribute('data-option-value'));
        const value = optionEl?.getAttribute('data-option-value')?.trim();
        if (value) {
            this.addValue(value);
        }
    }
    /** @internal */
    handleMenuItemChange(event) {
        event.stopPropagation();
        const menuItem = event.composedPath().find(pathItem => pathItem instanceof HTMLElement && pathItem.hasAttribute('data-option-value'));
        const value = menuItem?.getAttribute('data-option-value')?.trim();
        if (value) {
            this.addValue(value);
        }
    }
    syncActiveOption() {
        const options = Array.from(this.shadowRoot?.querySelectorAll('.chip-selector-option') ?? []);
        options.forEach((option, index) => {
            option.setAttribute('aria-selected', String(index === this.activeOptionIndex));
        });
        if (this.activeOptionIndex < 0) {
            this.activeOptionId = null;
        }
        else if (this.customValueCandidate.length > 0
            && this.activeOptionIndex === this.visibleOptionList.length) {
            this.activeOptionId = `${this.menuId}-option-create`;
        }
        else {
            this.activeOptionId = `${this.menuId}-option-${this.activeOptionIndex}`;
        }
    }
    commitActiveOption() {
        const allOptions = [
            ...this.visibleOptionList,
            ...(this.customValueCandidate ? [this.customValueCandidate] : [])
        ];
        const value = allOptions[this.activeOptionIndex];
        if (value !== undefined) {
            this.addValue(value);
        }
    }
    addValue(value) {
        if (this.disabled) {
            return;
        }
        const nextValues = this.selectedValueList;
        if (nextValues.includes(value)) {
            return;
        }
        nextValues.push(value);
        this.commitSelection(nextValues);
        this.filterText = '';
        this.setOpen(true);
        this.inputElement?.focus();
    }
    removeValue(value) {
        if (this.disabled) {
            return;
        }
        const nextValues = this.selectedValueList.filter(selectedValue => selectedValue !== value);
        this.commitSelection(nextValues);
        this.inputElement?.focus();
    }
    commitSelection(nextValues) {
        this.selectedValues = nextValues.join(',');
        this.dispatchEvent(new CustomEvent('change', {
            bubbles: true,
            composed: true,
            detail: { selectedValues: nextValues }
        }));
    }
    setOpen(nextOpen) {
        this.open = this.disabled ? false : nextOpen;
        this.syncMenuButtonState();
    }
    syncMenuButtonState() {
        if (this.menuButtonElement) {
            this.menuButtonElement.checked = this.open;
        }
    }
}
__decorate([
    attr({ mode: 'boolean' })
], FvChipSelector.prototype, "disabled", void 0);
__decorate([
    attr({ mode: 'boolean' })
], FvChipSelector.prototype, "open", void 0);
__decorate([
    attr
], FvChipSelector.prototype, "label", void 0);
__decorate([
    attr({ attribute: 'selected-values' })
], FvChipSelector.prototype, "selectedValues", void 0);
__decorate([
    attr
], FvChipSelector.prototype, "options", void 0);
__decorate([
    attr
], FvChipSelector.prototype, "placeholder", void 0);
__decorate([
    attr({ attribute: 'allow-custom-values', mode: 'boolean' })
], FvChipSelector.prototype, "allowCustomValues", void 0);
__decorate([
    observable
], FvChipSelector.prototype, "filterText", void 0);
__decorate([
    observable
], FvChipSelector.prototype, "activeOptionIndex", void 0);
__decorate([
    observable
], FvChipSelector.prototype, "activeOptionId", void 0);
__decorate([
    observable
], FvChipSelector.prototype, "region", void 0);
__decorate([
    observable
], FvChipSelector.prototype, "field", void 0);
const okFvChipSelector = FvChipSelector.compose({
    baseName: 'fv-chip-selector',
    template,
    styles
});
DesignSystem.getOrCreate().withPrefix('ok').register(okFvChipSelector());
export const fvChipSelectorTag = 'ok-fv-chip-selector';
//# sourceMappingURL=index.js.map