import * as i4 from '@angular/cdk/overlay';
import { OverlayModule } from '@angular/cdk/overlay';
import * as i2 from '@angular/cdk/scrolling';
import { ScrollingModule } from '@angular/cdk/scrolling';
import * as i0 from '@angular/core';
import { input, model, output, Component, signal, computed, viewChild, inject, ElementRef, Renderer2, effect, forwardRef } from '@angular/core';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import * as i1 from '@angular/forms';
import { FormGroup, FormControl, NG_VALUE_ACCESSOR, ReactiveFormsModule } from '@angular/forms';
import * as i3 from '@ngx-translate/core';
import { TranslateService, TranslateModule } from '@ngx-translate/core';
import { CheckboxComponent } from '@seniorsistemas/angular-components/checkbox';
import { debounceTime, distinctUntilChanged } from 'rxjs/operators';
import { trigger, state, style, transition, animate } from '@angular/animations';

class SelectOptionComponent {
    id = input.required();
    label = input.required();
    multiple = input.required();
    checkmark = input.required();
    isGrouper = input.required();
    isFocused = input.required();
    isSelected = model.required();
    selected = output();
    onClick() {
        if (this.isGrouper()) {
            return;
        }
        this.selected.emit();
    }
    static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: SelectOptionComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
    static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: SelectOptionComponent, isStandalone: true, selector: "s-select-option", inputs: { id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: true, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: true, transformFunction: null }, multiple: { classPropertyName: "multiple", publicName: "multiple", isSignal: true, isRequired: true, transformFunction: null }, checkmark: { classPropertyName: "checkmark", publicName: "checkmark", isSignal: true, isRequired: true, transformFunction: null }, isGrouper: { classPropertyName: "isGrouper", publicName: "isGrouper", isSignal: true, isRequired: true, transformFunction: null }, isFocused: { classPropertyName: "isFocused", publicName: "isFocused", isSignal: true, isRequired: true, transformFunction: null }, isSelected: { classPropertyName: "isSelected", publicName: "isSelected", isSignal: true, isRequired: true, transformFunction: null } }, outputs: { isSelected: "isSelectedChange", selected: "selected" }, ngImport: i0, template: "<div\n    [attr.id]=\"id()\"\n    [attr.role]=\"isGrouper() ? 'presentation' : 'option'\"\n    [attr.aria-selected]=\"isGrouper() ? null : isSelected()\"\n    class=\"flex select-none items-center gap-3 px-3 py-2\"\n    [class.hover:bg-grayscale-10]=\"!isGrouper()\"\n    [class.bg-grayscale-10]=\"!isGrouper() && isFocused()\"\n    (click)=\"onClick()\"\n    (keydown.enter)=\"onClick()\"\n    (keydown.space)=\"onClick(); $event.preventDefault()\"\n>\n    @if (!isGrouper() && checkmark() && !multiple() && isSelected()) {\n        <i\n            class=\"far fa-check text-grayscale-60\"\n            aria-hidden=\"true\"\n        ></i>\n    }\n\n    @if (!isGrouper() && multiple()) {\n        <s-checkbox [checked]=\"isSelected()\"></s-checkbox>\n    }\n    <span\n        [class]=\"isSelected() ? 'text-[#428BCA]' : 'text-grayscale-90'\"\n        [class.font-bold]=\"isGrouper()\"\n    >\n        {{ label() }}\n    </span>\n</div>\n\n", dependencies: [{ kind: "component", type: CheckboxComponent, selector: "s-checkbox", inputs: ["disabled", "checked", "indeterminate", "label", "ariaLabel", "ariaLabelledBy"], outputs: ["disabledChange", "checkedChange"] }] });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: SelectOptionComponent, decorators: [{
            type: Component,
            args: [{ selector: 's-select-option', standalone: true, imports: [CheckboxComponent], template: "<div\n    [attr.id]=\"id()\"\n    [attr.role]=\"isGrouper() ? 'presentation' : 'option'\"\n    [attr.aria-selected]=\"isGrouper() ? null : isSelected()\"\n    class=\"flex select-none items-center gap-3 px-3 py-2\"\n    [class.hover:bg-grayscale-10]=\"!isGrouper()\"\n    [class.bg-grayscale-10]=\"!isGrouper() && isFocused()\"\n    (click)=\"onClick()\"\n    (keydown.enter)=\"onClick()\"\n    (keydown.space)=\"onClick(); $event.preventDefault()\"\n>\n    @if (!isGrouper() && checkmark() && !multiple() && isSelected()) {\n        <i\n            class=\"far fa-check text-grayscale-60\"\n            aria-hidden=\"true\"\n        ></i>\n    }\n\n    @if (!isGrouper() && multiple()) {\n        <s-checkbox [checked]=\"isSelected()\"></s-checkbox>\n    }\n    <span\n        [class]=\"isSelected() ? 'text-[#428BCA]' : 'text-grayscale-90'\"\n        [class.font-bold]=\"isGrouper()\"\n    >\n        {{ label() }}\n    </span>\n</div>\n\n" }]
        }] });

/**
 * @description Componente de seleção dropdown genérico com suporte a seleção simples e múltipla,
 * filtro, agrupamento, virtual scroll e acessibilidade (ARIA combobox). Implementa
 * {@link ControlValueAccessor} para integração com Reactive Forms e Template-driven Forms.
 *
 * @example
 * ```html
 * <s-select
 *   formControlName="categoria"
 *   placeholder="Selecione uma categoria"
 *   [options]="categorias"
 *   optionLabel="nome"
 *   optionValue="id"
 *   [filter]="true"
 *   [showClear]="true" />
 * ```
 *
 * @category Inputs
 */
class SelectComponent {
    static nextId = 0;
    /** @description Texto exibido quando nenhuma opção está selecionada. @default '' */
    placeholder = input('');
    /** @description Habilita a seleção de múltiplas opções simultâneas. @default false */
    multiple = input(false);
    /** @description Lista de opções disponíveis para seleção. Campo obrigatório. */
    options = input.required();
    /** @description Chave do objeto de opção usada como label de exibição. */
    optionLabel = input();
    /** @description Chave do objeto de opção usada como valor a ser emitido ao formulário. */
    optionValue = input();
    /** @description Exibe um botão para limpar a seleção atual. @default false */
    showClear = input(false);
    /** @description Habilita um campo de filtro textual para pesquisa nas opções. @default false */
    filter = input(false);
    /** @description Exibe um ícone de checkmark ao lado da opção selecionada. @default false */
    checkmark = input(false);
    /** @description Chave(s) do objeto usada(s) para filtragem textual. Quando não informado, usa `optionLabel`. */
    filterBy = input();
    /** @description Habilita agrupamento de opções. As opções devem conter itens filhos conforme a estrutura de grupo suportada. @default false */
    group = input(false);
    /** @description Habilita virtualização da lista para melhor performance com grandes volumes de opções. @default false */
    virtualScroll = input(false);
    /** @description Altura fixa de cada item da lista em pixels, necessária para o cálculo do virtual scroll. @default 37 */
    virtualScrollItemSize = input(37);
    /** @description Mensagem exibida quando nenhuma opção corresponde ao filtro aplicado. @default null */
    emptyMessage = input(null);
    /** @description Controla o estado desabilitado do componente via two-way binding. @default false */
    disabled = model(false);
    /** @description Controla a visibilidade do painel de opções via two-way binding. @default false */
    showOptions = model(false);
    /** @description Label de acessibilidade do componente. @default '' */
    ariaLabel = input('');
    /** @description ID do elemento externo que descreve o componente (aria-labelledby). @default '' */
    ariaLabelledBy = input('');
    /**
     * ID do label resolvido para `aria-labelledby` do combobox interno.
     * Prioriza o valor explícito de `ariaLabelledBy`; caso contrário, usa o id de um
     * `<label for="...">` externo associado ao host (detectado em runtime).
     */
    resolvedAriaLabelledBy = signal('');
    value = signal(null);
    values = signal([]);
    filterValue = signal('');
    focusedIndex = signal(-1);
    _pendingValue = null;
    isOpen = false;
    componentId = `select${SelectComponent.nextId++}`;
    /**
     * Computed que indica se todas as opções filtradas estão selecionadas.
     * Retorna `true` se todas as opções disponíveis (excluindo agrupadores) estiverem presentes
     * na lista de valores selecionados, e `false` caso contrário.
     * @returns {boolean} `true` se todas as opções filtradas estão selecionadas, `false` caso contrário.
     */
    allSelected = computed(() => {
        const options = this.filteredOptions().filter((option) => !option.grouper);
        const selectedIds = this.values().map((option) => option.id);
        const ret = options.length > 0 && options.every((option) => selectedIds.includes(option.id));
        return ret;
    });
    /**
     * Retorna o item atualmente focado na lista de opções filtradas.
     * @returns O dado do item focado ou `null` caso nenhum item esteja focado.
     */
    focusedItem = computed(() => {
        const index = this.focusedIndex();
        if (index < 0)
            return null;
        return this.filteredOptions()[index]?.data ?? null;
    });
    /**
     * Retorna o item atualmente focado na lista de opções filtradas.
     * @returns O dado do item focado ou `null` caso nenhum item esteja focado.
     */
    focusedOptionId = computed(() => {
        const index = this.focusedIndex();
        if (index < 0)
            return null;
        return this.filteredOptions()[index]?.id ?? null;
    });
    /**
     * Computa e retorna a lista interna de opções do dropdown.
     * - Se as opções estiverem agrupadas (`group()` retorna verdadeiro),
     *   utiliza `_grouperToFlat` para transformar as opções agrupadas em uma lista plana.
     * - Caso contrário, retorna as opções com IDs adicionados.
     * @returns Um array de `InternalDropdownOption<T>` representando as opções disponíveis para o componente select.
     */
    internalOptions = computed(() => {
        const opts = this.options();
        const optionsWithId = this._addIdToOptions(opts);
        return this.group() ? this._grouperToFlat(optionsWithId) : optionsWithId;
    });
    /**
     * Retorna uma string representando a seleção atual do componente.
     * - Se o modo múltiplo estiver ativado e mais de um item estiver selecionado,
     *   retorna uma mensagem traduzida indicando o número total de registros selecionados.
     * - Se apenas um item estiver selecionado, retorna o rótulo desse item.
     * - Se o modo múltiplo não estiver ativado, retorna o rótulo do item selecionado ou uma string vazia caso nenhum item esteja selecionado.
     * @returns {string} Texto representando a seleção atual.
     */
    print = computed(() => {
        if (this.multiple()) {
            const selected = this.values();
            if (selected.length > 1) {
                return this._translateService.instant(`platform.angular_components.total_records_selected`, {
                    count: selected.length,
                });
            }
            else if (selected.length === 1) {
                return this._getLabelFromOption(selected[0].data);
            }
        }
        else {
            const selected = this.value();
            return selected ? this._getLabelFromOption(selected.data) : '';
        }
        return '';
    });
    /**
     * Indica se o seletor está "limpo", ou seja, sem nenhum valor selecionado.
     * - No modo múltiplo (`multiple`), retorna `true` se não houver nenhum valor selecionado (`values` está vazio).
     * - No modo simples, retorna `true` se não houver valor selecionado (`value` é falsy).
     * @returns `true` se não houver seleção, `false` caso contrário.
     */
    isClean = computed(() => {
        return this.multiple() ? !this.values().length : !this.value();
    });
    /**
     * Computed que indica se o estado de seleção "Selecionar Todos" está indeterminado.
     * Retorna `true` quando nem todos os itens filtrados estão selecionados e nem todos estão desmarcados,
     * ou seja, quando há uma seleção parcial dos itens disponíveis.
     * @returns {boolean} `true` se a seleção for parcial, `false` caso contrário.
     */
    selectAllIsIndeterminate = computed(() => {
        const selected = this.internalOptions().filter((option) => {
            return this.values().some((o) => o.id === option.id);
        });
        const options = this.filteredOptions().filter((option) => !option.grouper);
        const all = options.length > 0 && options.every((option) => selected.includes(option));
        const none = options.every((option) => !selected.includes(option));
        return !all && !none;
    });
    /**
     * Retorna uma lista de opções filtradas com base no valor de busca (`filterValue`) e nos critérios definidos.
     * - Se o filtro estiver desabilitado ou o valor de busca estiver vazio, retorna todas as opções.
     * - Caso contrário, filtra as opções utilizando o(s) campo(s) especificado(s) em `filterBy`.
     * - Ignora opções que possuem a propriedade `grouper` definida.
     * @returns As opções filtradas conforme o valor de busca e critérios de filtro.
     */
    filteredOptions = computed(() => {
        const allOptions = this.internalOptions();
        const search = this.filterValue().toLowerCase().trim();
        if (!this.filter() || !search) {
            return allOptions;
        }
        const filterBy = this.filterBy();
        return allOptions.filter((option) => {
            if (option.grouper) {
                return false;
            }
            let label = '';
            const optionLabel = this.optionLabel();
            if (!filterBy) {
                label = optionLabel ? String(option.data[optionLabel]) : String(option.data);
            }
            else if (Array.isArray(filterBy)) {
                label = filterBy.map((by) => String(option.data[by] ?? '')).join(' ');
            }
            else {
                label = String(option.data[filterBy] ?? '');
            }
            return label.toLowerCase().includes(search);
        });
    });
    filterForm = new FormGroup({
        filter: new FormControl('', { nonNullable: true }),
    });
    _containerDiv = viewChild('containerDiv');
    _filterInput = viewChild('filterInput');
    _selectAllCheckbox = viewChild('selectAllCheckbox');
    _onChange = () => { };
    _onTouched = () => { };
    _clickableLabel = null;
    _translateService = inject(TranslateService);
    _hostRef = inject(ElementRef);
    _renderer = inject(Renderer2);
    constructor() {
        this.filterForm
            .get('filter')
            .valueChanges.pipe(debounceTime(300), distinctUntilChanged(), takeUntilDestroyed())
            .subscribe((value) => {
            this.filterValue.set(value);
        });
        // Efeito para tentar aplicar valor pendente quando options mudar
        effect(() => {
            const opts = this.options();
            if (opts && opts.length && this._pendingValue !== null) {
                this._applyPendingValue();
            }
        });
        // Mantém o aria-labelledby resolvido em sincronia com o input explícito.
        effect(() => {
            const explicitLabelledBy = this.ariaLabelledBy();
            if (explicitLabelledBy) {
                this.resolvedAriaLabelledBy.set(explicitLabelledBy);
            }
        });
    }
    ngOnInit() {
        this._validateInputs();
    }
    ngAfterViewInit() {
        this._associateExternalLabel();
    }
    ngOnDestroy() {
        this._clickableLabel?.removeEventListener('click', this._onLabelClick);
    }
    /**
     * Define o valor selecionado do componente select.
     * @param value O valor a ser definido. Pode ser um objeto do tipo `T`, um array de `T` (quando múltipla seleção está habilitada), ou `null`.
     * @throws Se o modo múltiplo estiver ativado e o valor não for um array, lança um erro.
     * @throws Se o modo múltiplo estiver desativado e o valor for um array, lança um erro.
     * Este método mapeia o(s) valor(es) fornecido(s) para as opções internas do componente,
     * garantindo que apenas opções válidas sejam selecionadas conforme o modo de seleção (único ou múltiplo).
     */
    writeValue(value) {
        // Armazena valor pendente e tenta aplicar
        this._pendingValue = value;
        this._applyPendingValue();
    }
    /**
     * Tenta aplicar o valor pendente (_pendingValue) ao estado do componente,
     * mapeando para InternalDropdownOption<T> conforme options disponíveis.
     */
    _applyPendingValue() {
        const value = this._pendingValue;
        const opts = this.internalOptions();
        const optionValue = this.optionValue();
        if (value && this.multiple()) {
            if (Array.isArray(value)) {
                const mapped = value
                    .map((v) => {
                    if (optionValue) {
                        return opts.find((opt) => opt.data[optionValue] === v) || null;
                    }
                    else {
                        return opts.find((opt) => JSON.stringify(opt.data) === JSON.stringify(v)) || null;
                    }
                })
                    .filter((opt) => !!opt);
                this.values.set(mapped);
            }
            else {
                this.values.set([]);
            }
        }
        else {
            let mapped = null;
            if (Array.isArray(value)) {
                mapped = null;
            }
            else if (optionValue && value != null) {
                const compareValue = typeof value === 'object' && value !== null && optionValue in value ? value[optionValue] : value;
                mapped = opts.find((opt) => opt.data[optionValue] === compareValue) || null;
            }
            else if (value != null) {
                mapped = opts.find((opt) => JSON.stringify(opt.data) === JSON.stringify(value)) || null;
            }
            this.value.set(mapped ?? null);
        }
        this._pendingValue = null;
    }
    /**
     * Registra uma função de callback que será chamada sempre que o valor do componente mudar.
     * @param onChange Função de callback que recebe o novo valor selecionado ou null.
     */
    registerOnChange(onChange) {
        this._onChange = onChange;
    }
    /**
     * Registra uma função de callback que será chamada quando o componente for tocado (perder o foco).
     * @param onTouched Função de callback a ser executada quando o componente for marcado como "tocado".
     */
    registerOnTouched(onTouched) {
        this._onTouched = onTouched;
    }
    /**
     * Define o estado de desabilitado do componente.
     * @param disabled Indica se o componente deve ser desabilitado (`true`) ou habilitado (`false`).
     */
    setDisabledState(disabled) {
        this.disabled.set(disabled);
    }
    /**
     * Alterna a exibição das opções do componente select.
     * Se o componente estiver desabilitado, não realiza nenhuma ação.
     * Caso contrário, inverte o estado de exibição das opções.
     * Ao abrir, move o foco para o item atualmente selecionado.
     */
    toggle() {
        if (this.disabled()) {
            return;
        }
        const shouldOpen = !this.showOptions();
        this.isOpen = shouldOpen;
        this.showOptions.set(shouldOpen);
        if (shouldOpen) {
            if (this.filter()) {
                // Quando há filtro com múltipla seleção, foco vai para o checkbox primeiro;
                // caso contrário, vai direto para o input de filtro.
                // O foco é movido após a renderização do overlay.
                setTimeout(() => {
                    if (this.multiple()) {
                        const checkboxHost = this._selectAllCheckbox()?.nativeElement;
                        const focusable = checkboxHost?.querySelector('[tabindex]');
                        focusable?.focus();
                    }
                    else {
                        this._filterInput()?.nativeElement.focus();
                    }
                });
            }
            else {
                this._focusSelectedOption();
            }
        }
    }
    /**
     * Seleciona um item no componente de seleção.
     * @param item O item a ser selecionado ou desmarcado.
     * Se o componente estiver desabilitado, a função retorna imediatamente.
     * No modo múltiplo, adiciona ou remove o item da lista de valores selecionados.
     * No modo simples, define o item como valor selecionado e fecha o dropdown.
     * Sempre notifica as mudanças e marca o componente como tocado.
     */
    selectItem(item) {
        if (this.disabled()) {
            return;
        }
        const optionValue = this.optionValue();
        if (this.multiple()) {
            const store = [...this.values()];
            const index = store.findIndex((selectedItem) => {
                return selectedItem === item;
            });
            if (index !== -1) {
                store.splice(index, 1);
            }
            else {
                store.push(item);
            }
            this.values.set(store);
            const result = optionValue ? store.map((item) => item.data[optionValue]) : store;
            this._onChange(result);
        }
        else {
            this.value.set(item);
            const result = optionValue ? item.data[optionValue] : item.data;
            this._onChange(result);
            this._closeDropdown();
            this._containerDiv()?.nativeElement.focus();
        }
        this._onTouched();
    }
    /**
     * Limpa o valor selecionado no componente select.
     * Se o componente estiver desabilitado, não executa nenhuma ação.
     * Para seleção múltipla, remove todos os valores selecionados e notifica a alteração.
     * Para seleção única, define o valor como nulo e notifica a alteração.
     * Fecha o dropdown e marca o componente como "tocado".
     * Impede a propagação do evento do mouse.
     * @param event Evento do mouse que acionou a limpeza da seleção.
     */
    clear(event) {
        if (this.disabled()) {
            return;
        }
        if (this.multiple()) {
            this.values.set([]);
            this._onChange(this.values());
        }
        else {
            this.value.set(null);
            this._onChange(this.value());
        }
        this._closeDropdown();
        this._onTouched();
        event.stopPropagation();
    }
    /**
     * Limpa o filtro do formulário, resetando o campo 'filter' do formulário de filtro.
     * Este método redefine o valor do campo 'filter' para seu estado inicial,
     * removendo qualquer texto ou valor previamente inserido pelo usuário.
     */
    clearFilter() {
        this.filterForm.get('filter')?.reset();
    }
    /**
     * Manipula o evento de redimensionamento da janela.
     * Fecha o dropdown de opções caso ele esteja visível ao redimensionar a janela.
     * @param _ Evento de redimensionamento da janela (não utilizado).
     */
    onWindowResize(_) {
        if (this.showOptions()) {
            this._closeDropdown();
        }
    }
    /**
     * Manipula o evento de clique no elemento container da seleção.
     * Este método alterna o estado de exibição do componente select e,
     * em seguida, define o foco no elemento container correspondente.
     */
    onContainerDivClick() {
        this.toggle();
        this._containerDiv()?.nativeElement.focus();
    }
    /**
     * Manipula eventos de teclado no input de filtro do overlay.
     * - 'ArrowDown': Move o foco para a primeira opção da lista.
     * - 'ArrowUp': Move o foco para a última opção da lista.
     * - 'Escape': Fecha o dropdown e retorna o foco ao combobox.
     * - 'Enter': Seleciona a opção focada, se houver.
     * @param event O evento de teclado disparado no input de filtro.
     */
    onFilterKeyDown(event) {
        const options = this.filteredOptions();
        switch (event.key) {
            case 'ArrowDown':
                event.preventDefault();
                this._focusNextOption(options);
                break;
            case 'ArrowUp':
                event.preventDefault();
                if (options.length > 0) {
                    if (this.focusedIndex() < 0) {
                        // Nenhum item focado: foca o último disponível
                        let lastIndex = options.length - 1;
                        while (lastIndex >= 0 && options[lastIndex].grouper) {
                            lastIndex--;
                        }
                        if (lastIndex >= 0) {
                            this.focusedIndex.set(lastIndex);
                            this._scrollToFocusedOption();
                        }
                    }
                    else {
                        this._focusPreviousOption(options);
                    }
                }
                break;
            case 'Escape':
                event.preventDefault();
                event.stopPropagation();
                this._closeDropdown();
                this._containerDiv()?.nativeElement.focus();
                break;
            case 'Enter':
                if (this.focusedIndex() >= 0 && this.focusedIndex() < options.length) {
                    event.preventDefault();
                    this.selectItem(options[this.focusedIndex()]);
                }
                break;
        }
    }
    /**
     * Manipula eventos de teclado para navegação e seleção de opções no componente select.
     * - 'ArrowDown': Move o foco para a próxima opção disponível.
     * - 'ArrowUp': Move o foco para a opção anterior.
     * - 'Tab': Seleciona a opção atualmente focada e fecha o dropdown.
     * - 'Enter': Seleciona a opção focada se o dropdown estiver aberto, ou alterna a abertura do dropdown.
     * - 'Escape': Fecha o dropdown.
     * - ' ': (barra de espaço) Seleciona a opção focada se o dropdown estiver aberto, ou alterna a abertura do dropdown.
     * Previne o comportamento padrão do navegador quando necessário para garantir a navegação adequada pelo teclado.
     * @param event O evento de teclado disparado pelo usuário.
     */
    onKeyDown(event) {
        const options = this.filteredOptions();
        if (!options.length && event.key !== 'Escape' && event.key !== 'Enter' && event.key !== ' ') {
            return;
        }
        const _selectOption = () => {
            if (this.focusedIndex() >= 0 && this.focusedIndex() < options.length) {
                event.preventDefault();
                this.selectItem(options[this.focusedIndex()]);
            }
        };
        switch (event.key) {
            case 'ArrowDown':
                event.preventDefault();
                if (!this.showOptions()) {
                    this.toggle();
                }
                else {
                    this._focusNextOption(options);
                }
                break;
            case 'ArrowUp':
                event.preventDefault();
                if (this.showOptions()) {
                    this._focusPreviousOption(options);
                }
                break;
            case 'Tab':
                // Quando há filtro aberto, Tab deve navegar para dentro do overlay (não fechar)
                if (this.showOptions() && this.filter()) {
                    return;
                }
                _selectOption();
                this._closeDropdown();
                break;
            case 'Enter':
                if (this.showOptions()) {
                    _selectOption();
                }
                else {
                    this.toggle();
                }
                break;
            case 'Escape':
                if (this.showOptions()) {
                    event.preventDefault();
                    event.stopPropagation();
                }
                this._closeDropdown();
                break;
            case ' ':
                event.preventDefault();
                if (this.showOptions()) {
                    _selectOption();
                }
                else {
                    this.toggle();
                }
                break;
        }
    }
    /**
     * Retorna o rótulo (label) associado a uma opção fornecida.
     * @param option - A opção do tipo T para a qual o rótulo deve ser obtido.
     * @returns O rótulo da opção como uma string.
     */
    getOptionLabel(option) {
        return this._getLabelFromOption(option);
    }
    /**
     * Função de trackBy utilizada em blocos @for para otimizar a renderização de listas.
     * Retorna o identificador único (`id`) de cada item do tipo `InternalDropdownOption<T>`.
     * @param _ - Índice do item na lista (não utilizado).
     * @param item - O item atual da lista do tipo `InternalDropdownOption<T>`.
     * @returns O identificador único do item como uma string.
     */
    trackById(_, item) {
        return item.id;
    }
    /**
     * Verifica se a opção fornecida está selecionada.
     * @param option - A opção interna do dropdown a ser verificada.
     * @returns `true` se a opção estiver selecionada, caso contrário `false`.
     */
    isOptionSelected(option) {
        return (this.values().find((o) => {
            return o === option;
        }) !== undefined);
    }
    /**
     * Alterna a seleção de todos os itens disponíveis.
     * Se o modo múltiplo estiver ativado, seleciona todos os itens filtrados que não são agrupadores
     * caso nem todos estejam selecionados, ou limpa a seleção caso todos já estejam selecionados.
     * Após a alteração, notifica a mudança de valor.
     */
    toggleSelectAll() {
        if (!this.multiple()) {
            return;
        }
        const options = this.filteredOptions().filter((option) => !option.grouper);
        if (this.allSelected()) {
            this.values.set([]);
        }
        else {
            this.values.set(options);
        }
        this._onChange(this.values());
    }
    /**
     * Retorna o rótulo (label) de uma opção fornecida.
     * Este método utiliza a propriedade definida por `optionLabel` para extrair o valor do rótulo da opção.
     * Caso `optionLabel` não esteja definido, retorna a representação em string da própria opção.
     * @private
     * @param option A opção da qual o rótulo será extraído.
     * @returns O rótulo da opção como uma string.
     */
    _getLabelFromOption(option) {
        const optionLabel = this.optionLabel();
        return optionLabel ? String(option[optionLabel]) : String(option);
    }
    /**
     * Valida as entradas do componente de seleção.
     * Este método verifica se as opções fornecidas são válidas. Caso as opções sejam objetos,
     * garante que a propriedade `optionLabel` esteja definida, lançando um erro caso contrário.
     * Se não houver opções, a validação é ignorada.
     * @private
     * @throws {Error} Se as opções forem objetos e `optionLabel` não estiver definido.
     */
    _validateInputs() {
        const options = this.options();
        if (!options.length) {
            return;
        }
        const hasObjectOptions = options.some((option) => typeof option === 'object' && option !== null);
        if (hasObjectOptions) {
            if (!this.optionLabel()) {
                throw new Error('The "optionLabel" input is required when options are objects.');
            }
        }
    }
    /**
     * Associa um `<label for="...">` externo (que aponta para o `id` do host) ao
     * combobox interno, resolvendo o problema de "orphaned label".
     *
     * Como o host `s-select` não é um elemento rotulável (labelable), o `for` não
     * cria vínculo programático. Este método:
     * - localiza o label que referencia o `id` do host;
     * - garante que esse label tenha um `id`;
     * - aponta o `aria-labelledby` do combobox para esse label;
     * - adiciona um clique no label para focar o combobox (equivalente ao `for`).
     *
     * Não sobrescreve um `ariaLabelledBy` explícito informado pelo consumidor.
     * @private
     */
    _associateExternalLabel() {
        if (this.ariaLabelledBy()) {
            return;
        }
        const hostId = this._hostRef.nativeElement.id;
        if (!hostId) {
            return;
        }
        const label = this._findLabelFor(hostId);
        if (!label) {
            return;
        }
        if (!label.id) {
            this._renderer.setAttribute(label, 'id', `${this.componentId}-label`);
        }
        this.resolvedAriaLabelledBy.set(label.id);
        // O atributo `for` não funciona em host não-rotulável, então replicamos
        // o comportamento de "clicar no label foca o controle" manualmente.
        this._clickableLabel = label;
        label.addEventListener('click', this._onLabelClick);
    }
    /**
     * Localiza o `<label>` cujo atributo `for` corresponde ao id informado.
     * @private
     * @param hostId Id do host referenciado pelo label.
     * @returns O elemento label correspondente ou `null`.
     */
    _findLabelFor(hostId) {
        const labels = document.querySelectorAll('label[for]');
        for (const label of Array.from(labels)) {
            if (label.getAttribute('for') === hostId) {
                return label;
            }
        }
        return null;
    }
    /**
     * Move o foco para o combobox interno ao clicar no label externo associado.
     * @private
     */
    _onLabelClick = () => {
        if (this.disabled()) {
            return;
        }
        this._containerDiv()?.nativeElement.focus();
    };
    /**
     * Fecha o dropdown de opções do componente select.
     * Este método oculta as opções disponíveis e redefine o índice do item focado para -1,
     * indicando que nenhum item está atualmente focado.
     * @private
     */
    _closeDropdown() {
        this.showOptions.set(false);
        this.focusedIndex.set(-1);
    }
    /**
     * Adiciona um identificador único a cada opção fornecida.
     * Para cada item no array de opções, gera um objeto `InternalDropdownOption` contendo o dado original
     * e um campo `id` único baseado no timestamp atual e no índice do item. Caso a opção seja um grupo
     * (detectado pela presença de um array `items`), adiciona também a propriedade `grouper: true`.
     * @private
     * @param options Array de opções do tipo `T` a serem processadas.
     * @returns Um array de objetos `InternalDropdownOption<T>` com identificadores únicos.
     */
    _addIdToOptions(options) {
        const timestamp = Date.now();
        return options.map((option, index) => {
            if (this.group() && Array.isArray(option.items)) {
                return {
                    data: option,
                    id: `id_${timestamp}_${index++}`,
                    grouper: true,
                };
            }
            return {
                data: option,
                id: `id_${timestamp}_${index++}`,
            };
        });
    }
    /**
     * Converte uma lista de opções possivelmente agrupadas em uma lista plana de opções.
     * Para cada opção que possui um agrupador (`grouper`) e um array de itens em `data.items`,
     * adiciona a opção do agrupador e, em seguida, adiciona cada item do grupo como uma nova opção
     * individual, atribuindo um `id` único para cada item do grupo.
     * Caso a opção não seja um agrupador, ela é adicionada diretamente ao resultado.
     * @private
     * @param options Lista de opções internas do dropdown, podendo conter agrupadores.
     * @returns Lista plana de opções, incluindo agrupadores e seus itens expandidos.
     */
    _grouperToFlat(options) {
        const result = [];
        options.forEach((option) => {
            if (option.grouper && Array.isArray(option.data.items)) {
                result.push(option);
                const groupItems = option.data.items.map((item, index) => ({
                    data: item,
                    id: `${option.id}_item_${index}`,
                }));
                result.push(...groupItems);
            }
            else {
                result.push(option);
            }
        });
        return result;
    }
    /**
     * Move o foco para a próxima opção disponível na lista, ignorando opções que sejam agrupadores.
     * @private
     * @param options Lista de opções internas do dropdown.
     */
    _focusNextOption(options) {
        let nextIndex = this.focusedIndex() + 1;
        while (nextIndex < options.length && options[nextIndex].grouper) {
            nextIndex++;
        }
        if (nextIndex < options.length) {
            this.focusedIndex.set(nextIndex);
            this._scrollToFocusedOption();
        }
    }
    /**
     * Move o foco para a opção anterior na lista, ignorando opções do tipo "grouper".
     * @private
     * @param options Lista de opções internas do dropdown.
     */
    _focusPreviousOption(options) {
        let prevIndex = this.focusedIndex() - 1;
        while (prevIndex >= 0 && options[prevIndex].grouper) {
            prevIndex--;
        }
        if (prevIndex >= 0) {
            this.focusedIndex.set(prevIndex);
            this._scrollToFocusedOption();
        }
    }
    /**
     * Rola a lista de opções para garantir que a opção atualmente focada esteja visível.
     * Este método obtém o ID da opção atualmente focada a partir da lista de opções filtradas.
     * Se um ID válido for encontrado, busca o elemento correspondente no DOM e utiliza
     * `scrollIntoView` para rolar até a opção, alinhando-a ao bloco mais próximo.
     * @private
     */
    _scrollToFocusedOption() {
        const id = this.filteredOptions()[this.focusedIndex()]?.id;
        if (!id) {
            return;
        }
        const el = document.getElementById(id);
        el?.scrollIntoView({ block: 'nearest' });
    }
    /**
     * Define o foco no item atualmente selecionado ao abrir o dropdown.
     * No modo simples, foca o item selecionado. No modo múltiplo, foca o primeiro item selecionado.
     * Após renderização do overlay, rola a lista até o item focado.
     * @private
     */
    _focusSelectedOption() {
        const options = this.filteredOptions();
        let selectedIndex = -1;
        if (this.multiple()) {
            const firstSelected = this.values()[0];
            if (firstSelected) {
                selectedIndex = options.findIndex((o) => o.id === firstSelected.id);
            }
        }
        else {
            const selected = this.value();
            if (selected) {
                selectedIndex = options.findIndex((o) => o.id === selected.id);
            }
        }
        this.focusedIndex.set(selectedIndex);
        if (selectedIndex >= 0) {
            setTimeout(() => this._scrollToFocusedOption());
        }
    }
    static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: SelectComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
    static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: SelectComponent, isStandalone: true, selector: "s-select", inputs: { placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, multiple: { classPropertyName: "multiple", publicName: "multiple", isSignal: true, isRequired: false, transformFunction: null }, options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: true, transformFunction: null }, optionLabel: { classPropertyName: "optionLabel", publicName: "optionLabel", isSignal: true, isRequired: false, transformFunction: null }, optionValue: { classPropertyName: "optionValue", publicName: "optionValue", isSignal: true, isRequired: false, transformFunction: null }, showClear: { classPropertyName: "showClear", publicName: "showClear", isSignal: true, isRequired: false, transformFunction: null }, filter: { classPropertyName: "filter", publicName: "filter", isSignal: true, isRequired: false, transformFunction: null }, checkmark: { classPropertyName: "checkmark", publicName: "checkmark", isSignal: true, isRequired: false, transformFunction: null }, filterBy: { classPropertyName: "filterBy", publicName: "filterBy", isSignal: true, isRequired: false, transformFunction: null }, group: { classPropertyName: "group", publicName: "group", isSignal: true, isRequired: false, transformFunction: null }, virtualScroll: { classPropertyName: "virtualScroll", publicName: "virtualScroll", isSignal: true, isRequired: false, transformFunction: null }, virtualScrollItemSize: { classPropertyName: "virtualScrollItemSize", publicName: "virtualScrollItemSize", isSignal: true, isRequired: false, transformFunction: null }, emptyMessage: { classPropertyName: "emptyMessage", publicName: "emptyMessage", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, showOptions: { classPropertyName: "showOptions", publicName: "showOptions", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, ariaLabelledBy: { classPropertyName: "ariaLabelledBy", publicName: "ariaLabelledBy", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { disabled: "disabledChange", showOptions: "showOptionsChange" }, host: { listeners: { "window:resize": "onWindowResize($event)" } }, providers: [
            {
                provide: NG_VALUE_ACCESSOR,
                useExisting: forwardRef(() => SelectComponent),
                multi: true,
            },
        ], viewQueries: [{ propertyName: "_containerDiv", first: true, predicate: ["containerDiv"], descendants: true, isSignal: true }, { propertyName: "_filterInput", first: true, predicate: ["filterInput"], descendants: true, isSignal: true }, { propertyName: "_selectAllCheckbox", first: true, predicate: ["selectAllCheckbox"], descendants: true, isSignal: true }], ngImport: i0, template: "<div\n    #containerDiv\n    cdkOverlayOrigin\n    #trigger=\"cdkOverlayOrigin\"\n    class=\"select-container group flex h-[35px] w-full overflow-hidden rounded-[3px] border outline-1 outline-primary focus:outline\"\n    [class.pointer-events-none]=\"disabled()\"\n    [class.border-grayscale-20]=\"disabled()\"\n    [class.bg-grayscale-5]=\"disabled()\"\n    [class.pointer-events-auto]=\"!disabled()\"\n    [class.border-grayscale-30]=\"!disabled()\"\n    [class.bg-grayscale-0]=\"!disabled()\"\n    [attr.aria-disabled]=\"disabled()\"\n    [attr.aria-expanded]=\"showOptions()\"\n    [attr.aria-haspopup]=\"'listbox'\"\n    [attr.aria-controls]=\"componentId + '-listbox'\"\n    [attr.aria-owns]=\"showOptions() ? componentId + '-listbox' : null\"\n    [attr.aria-activedescendant]=\"showOptions() ? focusedOptionId() : null\"\n    role=\"combobox\"\n    [id]=\"componentId + '-combobox'\"\n    (click)=\"onContainerDivClick()\"\n    (keydown)=\"onKeyDown($event)\"\n    tabindex=\"0\"\n    [attr.aria-label]=\"ariaLabel() || null\"\n    [attr.aria-labelledby]=\"resolvedAriaLabelledBy() || null\"\n>\n    <span\n        class=\"flex flex-grow select-none px-3 py-[7px]\"\n        [class.text-grayscale-90]=\"!disabled() && !isClean()\"\n        [class.text-grayscale-60]=\"!disabled() && isClean()\"\n        [class.text-grayscale-30]=\"disabled()\"\n    >\n        {{ print() || placeholder() }}\n    </span>\n\n    @if (!disabled() && showClear() && !isClean()) {\n        <button\n            class=\"mx-3\"\n            (click)=\"clear($event)\"\n            [attr.aria-label]=\"'platform.angular_components.aria_clear_selection' | translate\"\n        >\n            <i\n                class=\"fas fa-times flex items-center\"\n                aria-hidden=\"true\"\n            ></i>\n        </button>\n    }\n\n    <div\n        class=\"dropdown-container flex items-center border-l px-3 group-hover:bg-grayscale-10 group-focus:border-primary\"\n        [class.border-grayscale-20]=\"disabled()\"\n        [class.border-grayscale-30]=\"!disabled()\"\n    >\n        <i\n            class=\"fas\"\n            aria-hidden=\"true\"\n            [class.text-grayscale-30]=\"disabled()\"\n            [class.text-grayscale-90]=\"!disabled()\"\n            [class.fa-caret-down]=\"!showOptions()\"\n            [class.fa-caret-up]=\"showOptions()\"\n        ></i>\n    </div>\n</div>\n\n<ng-template\n    #dropdownTemplate\n    cdkConnectedOverlay\n    [cdkConnectedOverlayOrigin]=\"trigger\"\n    [cdkConnectedOverlayOpen]=\"showOptions()\"\n    [cdkConnectedOverlayHasBackdrop]=\"true\"\n    (backdropClick)=\"showOptions.set(false)\"\n    cdkConnectedOverlayBackdropClass=\"cdk-overlay-transparent-backdrop\"\n    (detach)=\"showOptions.set(false)\"\n>\n    <div\n        [id]=\"componentId + '-listbox'\"\n        class=\"dropdown-body-class z-[1000] rounded-[3px] bg-grayscale-0 py-1 shadow-md\"\n        (click)=\"$event.stopPropagation()\"\n        [style.width.px]=\"containerDiv.offsetWidth\"\n        [attr.aria-multiselectable]=\"multiple() ? true : null\"\n        [@visibilityChanged]\n        role=\"listbox\"\n    >\n        @if (filter()) {\n            <div class=\"flex w-full items-center gap-3 px-3 py-1.5\">\n                @if (multiple()) {\n                    <s-checkbox\n                        #selectAllCheckbox\n                        [checked]=\"allSelected()\"\n                        [indeterminate]=\"selectAllIsIndeterminate()\"\n                        (checkedChange)=\"toggleSelectAll()\"\n                    ></s-checkbox>\n                }\n\n                <div class=\"relative flex h-[35px] grow\">\n                    <form\n                        class=\"flex w-full grow\"\n                        [formGroup]=\"filterForm\"\n                    >\n                        <input\n                            #filterInput\n                            class=\"w-full grow rounded-[3px] border border-grayscale-30 pl-2.5 pr-7 outline-1 outline-primary\"\n                            type=\"text\"\n                            formControlName=\"filter\"\n                            (click)=\"$event.stopPropagation()\"\n                            (keydown)=\"onFilterKeyDown($event)\"\n                            [attr.aria-label]=\"'platform.angular_components.filter_options' | translate\"\n                            [attr.aria-controls]=\"componentId + '-listbox'\"\n                            [attr.aria-activedescendant]=\"focusedOptionId()\"\n                            autocomplete=\"off\"\n                        />\n                    </form>\n                    <i\n                        class=\"fas fa-search absolute right-2.5 top-2.5 text-grayscale-90\"\n                        aria-hidden=\"true\"\n                    ></i>\n                </div>\n                <button\n                    (click)=\"clearFilter()\"\n                    [attr.aria-label]=\"'platform.angular_components.clear_filter' | translate\"\n                >\n                    <i\n                        class=\"fas fa-times flex items-center\"\n                        aria-hidden=\"true\"\n                    ></i>\n                </button>\n            </div>\n        }\n\n        <!-- Virtual scroll -->\n        @if (virtualScroll() && filteredOptions().length > 10) {\n            <cdk-virtual-scroll-viewport\n                [itemSize]=\"virtualScrollItemSize()\"\n                class=\"h-52 overflow-auto\"\n            >\n                <ng-container *cdkVirtualFor=\"let option of filteredOptions(); trackBy: trackById\">\n                    <s-select-option\n                        [id]=\"option.id\"\n                        [label]=\"getOptionLabel(option.data)\"\n                        [multiple]=\"multiple()\"\n                        [checkmark]=\"checkmark()\"\n                        [isSelected]=\"isOptionSelected(option)\"\n                        (selected)=\"selectItem(option)\"\n                        [isGrouper]=\"option.grouper ?? false\"\n                        [isFocused]=\"option.data === focusedItem()\"\n                    ></s-select-option>\n                </ng-container>\n            </cdk-virtual-scroll-viewport>\n        }\n\n        <!-- Normal list -->\n        @if (!virtualScroll() || filteredOptions().length <= 10) {\n            <ul class=\"max-h-52 overflow-auto\">\n                @for (option of filteredOptions(); track option.id) {\n                    <s-select-option\n                        [id]=\"option.id\"\n                        [label]=\"getOptionLabel(option.data)\"\n                        [multiple]=\"multiple()\"\n                        [checkmark]=\"checkmark()\"\n                        [isSelected]=\"isOptionSelected(option)\"\n                        (selected)=\"selectItem(option)\"\n                        [isGrouper]=\"option.grouper ?? false\"\n                        [isFocused]=\"option.data === focusedItem()\"\n                    ></s-select-option>\n                }\n            </ul>\n        }\n\n        @if (filteredOptions().length === 0) {\n            <span class=\"m-3 text-grayscale-60\">\n                {{ emptyMessage() ?? 'platform.angular_components.no_records_found' | translate }}\n            </span>\n        }\n    </div>\n</ng-template>\n\n", styles: [":host.ng-dirty.ng-invalid{.dropdown-container,.select-container{border-color:#c13018;outline:none}}\n"], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "ngmodule", type: ScrollingModule }, { kind: "directive", type: i2.CdkFixedSizeVirtualScroll, selector: "cdk-virtual-scroll-viewport[itemSize]", inputs: ["itemSize", "minBufferPx", "maxBufferPx"] }, { kind: "directive", type: i2.CdkVirtualForOf, selector: "[cdkVirtualFor][cdkVirtualForOf]", inputs: ["cdkVirtualForOf", "cdkVirtualForTrackBy", "cdkVirtualForTemplate", "cdkVirtualForTemplateCacheSize"] }, { kind: "component", type: i2.CdkVirtualScrollViewport, selector: "cdk-virtual-scroll-viewport", inputs: ["orientation", "appendOnly"], outputs: ["scrolledIndexChange"] }, { kind: "component", type: SelectOptionComponent, selector: "s-select-option", inputs: ["id", "label", "multiple", "checkmark", "isGrouper", "isFocused", "isSelected"], outputs: ["isSelectedChange", "selected"] }, { kind: "component", type: CheckboxComponent, selector: "s-checkbox", inputs: ["disabled", "checked", "indeterminate", "label", "ariaLabel", "ariaLabelledBy"], outputs: ["disabledChange", "checkedChange"] }, { kind: "ngmodule", type: TranslateModule }, { kind: "pipe", type: i3.TranslatePipe, name: "translate" }, { kind: "ngmodule", type: OverlayModule }, { kind: "directive", type: i4.CdkConnectedOverlay, selector: "[cdk-connected-overlay], [connected-overlay], [cdkConnectedOverlay]", inputs: ["cdkConnectedOverlayOrigin", "cdkConnectedOverlayPositions", "cdkConnectedOverlayPositionStrategy", "cdkConnectedOverlayOffsetX", "cdkConnectedOverlayOffsetY", "cdkConnectedOverlayWidth", "cdkConnectedOverlayHeight", "cdkConnectedOverlayMinWidth", "cdkConnectedOverlayMinHeight", "cdkConnectedOverlayBackdropClass", "cdkConnectedOverlayPanelClass", "cdkConnectedOverlayViewportMargin", "cdkConnectedOverlayScrollStrategy", "cdkConnectedOverlayOpen", "cdkConnectedOverlayDisableClose", "cdkConnectedOverlayTransformOriginOn", "cdkConnectedOverlayHasBackdrop", "cdkConnectedOverlayLockPosition", "cdkConnectedOverlayFlexibleDimensions", "cdkConnectedOverlayGrowAfterOpen", "cdkConnectedOverlayPush", "cdkConnectedOverlayDisposeOnNavigation"], outputs: ["backdropClick", "positionChange", "attach", "detach", "overlayKeydown", "overlayOutsideClick"], exportAs: ["cdkConnectedOverlay"] }, { kind: "directive", type: i4.CdkOverlayOrigin, selector: "[cdk-overlay-origin], [overlay-origin], [cdkOverlayOrigin]", exportAs: ["cdkOverlayOrigin"] }], animations: [
            trigger('visibilityChanged', [
                state('void', style({ opacity: 0, transform: 'scale(0.95)' })),
                transition(':enter', [
                    animate('500ms cubic-bezier(0, 0, 0.2, 1)', style({ opacity: 1, transform: 'scale(1)' })),
                ]),
                transition(':leave', [
                    animate('500ms cubic-bezier(0.4, 0, 0.2, 1)', style({ opacity: 0, transform: 'scale(0.95)' })),
                ]),
            ]),
        ] });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: SelectComponent, decorators: [{
            type: Component,
            args: [{ selector: 's-select', standalone: true, imports: [
                        ReactiveFormsModule,
                        ScrollingModule,
                        SelectOptionComponent,
                        CheckboxComponent,
                        TranslateModule,
                        OverlayModule,
                    ], host: {
                        '(window:resize)': 'onWindowResize($event)',
                    }, providers: [
                        {
                            provide: NG_VALUE_ACCESSOR,
                            useExisting: forwardRef(() => SelectComponent),
                            multi: true,
                        },
                    ], animations: [
                        trigger('visibilityChanged', [
                            state('void', style({ opacity: 0, transform: 'scale(0.95)' })),
                            transition(':enter', [
                                animate('500ms cubic-bezier(0, 0, 0.2, 1)', style({ opacity: 1, transform: 'scale(1)' })),
                            ]),
                            transition(':leave', [
                                animate('500ms cubic-bezier(0.4, 0, 0.2, 1)', style({ opacity: 0, transform: 'scale(0.95)' })),
                            ]),
                        ]),
                    ], template: "<div\n    #containerDiv\n    cdkOverlayOrigin\n    #trigger=\"cdkOverlayOrigin\"\n    class=\"select-container group flex h-[35px] w-full overflow-hidden rounded-[3px] border outline-1 outline-primary focus:outline\"\n    [class.pointer-events-none]=\"disabled()\"\n    [class.border-grayscale-20]=\"disabled()\"\n    [class.bg-grayscale-5]=\"disabled()\"\n    [class.pointer-events-auto]=\"!disabled()\"\n    [class.border-grayscale-30]=\"!disabled()\"\n    [class.bg-grayscale-0]=\"!disabled()\"\n    [attr.aria-disabled]=\"disabled()\"\n    [attr.aria-expanded]=\"showOptions()\"\n    [attr.aria-haspopup]=\"'listbox'\"\n    [attr.aria-controls]=\"componentId + '-listbox'\"\n    [attr.aria-owns]=\"showOptions() ? componentId + '-listbox' : null\"\n    [attr.aria-activedescendant]=\"showOptions() ? focusedOptionId() : null\"\n    role=\"combobox\"\n    [id]=\"componentId + '-combobox'\"\n    (click)=\"onContainerDivClick()\"\n    (keydown)=\"onKeyDown($event)\"\n    tabindex=\"0\"\n    [attr.aria-label]=\"ariaLabel() || null\"\n    [attr.aria-labelledby]=\"resolvedAriaLabelledBy() || null\"\n>\n    <span\n        class=\"flex flex-grow select-none px-3 py-[7px]\"\n        [class.text-grayscale-90]=\"!disabled() && !isClean()\"\n        [class.text-grayscale-60]=\"!disabled() && isClean()\"\n        [class.text-grayscale-30]=\"disabled()\"\n    >\n        {{ print() || placeholder() }}\n    </span>\n\n    @if (!disabled() && showClear() && !isClean()) {\n        <button\n            class=\"mx-3\"\n            (click)=\"clear($event)\"\n            [attr.aria-label]=\"'platform.angular_components.aria_clear_selection' | translate\"\n        >\n            <i\n                class=\"fas fa-times flex items-center\"\n                aria-hidden=\"true\"\n            ></i>\n        </button>\n    }\n\n    <div\n        class=\"dropdown-container flex items-center border-l px-3 group-hover:bg-grayscale-10 group-focus:border-primary\"\n        [class.border-grayscale-20]=\"disabled()\"\n        [class.border-grayscale-30]=\"!disabled()\"\n    >\n        <i\n            class=\"fas\"\n            aria-hidden=\"true\"\n            [class.text-grayscale-30]=\"disabled()\"\n            [class.text-grayscale-90]=\"!disabled()\"\n            [class.fa-caret-down]=\"!showOptions()\"\n            [class.fa-caret-up]=\"showOptions()\"\n        ></i>\n    </div>\n</div>\n\n<ng-template\n    #dropdownTemplate\n    cdkConnectedOverlay\n    [cdkConnectedOverlayOrigin]=\"trigger\"\n    [cdkConnectedOverlayOpen]=\"showOptions()\"\n    [cdkConnectedOverlayHasBackdrop]=\"true\"\n    (backdropClick)=\"showOptions.set(false)\"\n    cdkConnectedOverlayBackdropClass=\"cdk-overlay-transparent-backdrop\"\n    (detach)=\"showOptions.set(false)\"\n>\n    <div\n        [id]=\"componentId + '-listbox'\"\n        class=\"dropdown-body-class z-[1000] rounded-[3px] bg-grayscale-0 py-1 shadow-md\"\n        (click)=\"$event.stopPropagation()\"\n        [style.width.px]=\"containerDiv.offsetWidth\"\n        [attr.aria-multiselectable]=\"multiple() ? true : null\"\n        [@visibilityChanged]\n        role=\"listbox\"\n    >\n        @if (filter()) {\n            <div class=\"flex w-full items-center gap-3 px-3 py-1.5\">\n                @if (multiple()) {\n                    <s-checkbox\n                        #selectAllCheckbox\n                        [checked]=\"allSelected()\"\n                        [indeterminate]=\"selectAllIsIndeterminate()\"\n                        (checkedChange)=\"toggleSelectAll()\"\n                    ></s-checkbox>\n                }\n\n                <div class=\"relative flex h-[35px] grow\">\n                    <form\n                        class=\"flex w-full grow\"\n                        [formGroup]=\"filterForm\"\n                    >\n                        <input\n                            #filterInput\n                            class=\"w-full grow rounded-[3px] border border-grayscale-30 pl-2.5 pr-7 outline-1 outline-primary\"\n                            type=\"text\"\n                            formControlName=\"filter\"\n                            (click)=\"$event.stopPropagation()\"\n                            (keydown)=\"onFilterKeyDown($event)\"\n                            [attr.aria-label]=\"'platform.angular_components.filter_options' | translate\"\n                            [attr.aria-controls]=\"componentId + '-listbox'\"\n                            [attr.aria-activedescendant]=\"focusedOptionId()\"\n                            autocomplete=\"off\"\n                        />\n                    </form>\n                    <i\n                        class=\"fas fa-search absolute right-2.5 top-2.5 text-grayscale-90\"\n                        aria-hidden=\"true\"\n                    ></i>\n                </div>\n                <button\n                    (click)=\"clearFilter()\"\n                    [attr.aria-label]=\"'platform.angular_components.clear_filter' | translate\"\n                >\n                    <i\n                        class=\"fas fa-times flex items-center\"\n                        aria-hidden=\"true\"\n                    ></i>\n                </button>\n            </div>\n        }\n\n        <!-- Virtual scroll -->\n        @if (virtualScroll() && filteredOptions().length > 10) {\n            <cdk-virtual-scroll-viewport\n                [itemSize]=\"virtualScrollItemSize()\"\n                class=\"h-52 overflow-auto\"\n            >\n                <ng-container *cdkVirtualFor=\"let option of filteredOptions(); trackBy: trackById\">\n                    <s-select-option\n                        [id]=\"option.id\"\n                        [label]=\"getOptionLabel(option.data)\"\n                        [multiple]=\"multiple()\"\n                        [checkmark]=\"checkmark()\"\n                        [isSelected]=\"isOptionSelected(option)\"\n                        (selected)=\"selectItem(option)\"\n                        [isGrouper]=\"option.grouper ?? false\"\n                        [isFocused]=\"option.data === focusedItem()\"\n                    ></s-select-option>\n                </ng-container>\n            </cdk-virtual-scroll-viewport>\n        }\n\n        <!-- Normal list -->\n        @if (!virtualScroll() || filteredOptions().length <= 10) {\n            <ul class=\"max-h-52 overflow-auto\">\n                @for (option of filteredOptions(); track option.id) {\n                    <s-select-option\n                        [id]=\"option.id\"\n                        [label]=\"getOptionLabel(option.data)\"\n                        [multiple]=\"multiple()\"\n                        [checkmark]=\"checkmark()\"\n                        [isSelected]=\"isOptionSelected(option)\"\n                        (selected)=\"selectItem(option)\"\n                        [isGrouper]=\"option.grouper ?? false\"\n                        [isFocused]=\"option.data === focusedItem()\"\n                    ></s-select-option>\n                }\n            </ul>\n        }\n\n        @if (filteredOptions().length === 0) {\n            <span class=\"m-3 text-grayscale-60\">\n                {{ emptyMessage() ?? 'platform.angular_components.no_records_found' | translate }}\n            </span>\n        }\n    </div>\n</ng-template>\n\n", styles: [":host.ng-dirty.ng-invalid{.dropdown-container,.select-container{border-color:#c13018;outline:none}}\n"] }]
        }], ctorParameters: () => [] });

/**
 * Generated bundle index. Do not edit.
 */

export { SelectComponent };
//# sourceMappingURL=seniorsistemas-angular-components-select.mjs.map
