/// /// Copyright 2015-2026 Micro Focus or one of its affiliates. /// /// Licensed under the Apache License, Version 2.0 (the "License"); /// you may not use this file except in compliance with the License. /// You may obtain a copy of the License at /// /// http://www.apache.org/licenses/LICENSE-2.0 /// /// Unless required by applicable law or agreed to in writing, software /// distributed under the License is distributed on an "AS IS" BASIS, /// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. /// See the License for the specific language governing permissions and /// limitations under the License. /// import { FocusKeyManager } from '@angular/cdk/a11y'; import { AfterViewInit, Component, EventEmitter, HostBinding, inject, Input, OnDestroy, Output, QueryList, ViewChildren, } from '@angular/core'; import { Subject } from 'rxjs'; import { takeUntil } from 'rxjs/operators'; import { FacetHeaderComponent } from '../base/facet-header/facet-header.component'; import { FacetDeselect, FacetDeselectAll, FacetEvent, FacetSelect } from '../facet-events'; import { FacetService } from '../facet.service'; import { Facet } from '../models/facet'; import { FacetCheckListItemComponent } from './check-list-item/facet-check-list-item.component'; let uniqueId = 0; @Component({ selector: 'ux-facet-check-list', templateUrl: './facet-check-list.component.html', imports: [FacetHeaderComponent, FacetCheckListItemComponent], }) export class FacetCheckListComponent implements AfterViewInit, OnDestroy { readonly facetService = inject(FacetService); @Input() @HostBinding() id: string = `ux-facet-check-list-${uniqueId++}`; /** This will allow you to define an initial set of selected facets. */ @Input() set selected(selection: Facet[]) { if (Array.isArray(selection)) { selection.forEach(facet => this.facetService.select(facet)); } } /** Defines the complete list of facets that can be selected. */ @Input() facets: Facet[] = []; /** Defines the text displayed at the top of the Facet Check List. */ @Input() header: string; /** If `false` the list will grow to display all possible facets. If `true` a scrollbar will appear to prevent the list from growing too large. */ @Input() scrollbar: boolean = true; /** Defines whether or not the checkboxes will appear in simplified form. */ @Input() simplified: boolean = false; /** Defines whether or not the Facet Check List should be initially expanded or not. */ @Input() expanded: boolean = true; /** * This will be triggered when a facet is selected, deselected or all facets are deselected. * The event will be an instance of either `FacetSelect`, `FacetDeselect` or `FacetDeselectAll` and * will contain the facet being selected or deselected in a `facet` property (deselect all will not contain affected facets). */ @Output() events: Subject = new Subject(); /** If two-way binding is used this array will get updated any time the selected facets change. */ @Output() selectedChange: EventEmitter = new EventEmitter(); @ViewChildren(FacetCheckListItemComponent) options: QueryList; isFocused: boolean = false; activeIndex: number = 0; private readonly _onDestroy = new Subject(); private _focusKeyManager: FocusKeyManager; constructor() { this.facetService.events$.pipe(takeUntil(this._onDestroy)).subscribe(event => { // deselect all events should always be emitted if (event instanceof FacetDeselectAll) { this.events.next(event); this.selectedChange.next([]); } // selection and deselection events should only be emitted when the facet belongs to this component if ( (event instanceof FacetSelect || event instanceof FacetDeselect) && this.isOwnFacet(event.facet) ) { this.events.next(event); this.selectedChange.next(this.getSelectedFacets()); } }); } ngAfterViewInit(): void { this._focusKeyManager = new FocusKeyManager(this.options).withVerticalOrientation(); this._focusKeyManager.change .pipe(takeUntil(this._onDestroy)) .subscribe(index => (this.activeIndex = index)); } ngOnDestroy(): void { this._onDestroy.next(); this._onDestroy.complete(); } onFocus(index: number): void { if (this._focusKeyManager.activeItemIndex === -1) { this._focusKeyManager.setActiveItem(index); } } onKeydown(event: KeyboardEvent): void { this._focusKeyManager.onKeydown(event); } toggleFacet(index: number, facet: Facet): void { this.facetService.toggle(facet); this._focusKeyManager.setActiveItem(index); } private getSelectedFacets(): Facet[] { return this.facetService.facets$.value.filter(facet => this.isOwnFacet(facet)); } private isOwnFacet(facet: Facet): boolean { return this.facets.indexOf(facet) !== -1; } }