File

src/app/modules/content-search/components/search-filter/search-filter.component.ts

Implements

OnInit OnDestroy

Metadata

Index

Properties
Methods
Inputs
Outputs
Accessors

Constructor

constructor(resourceService: ResourceService, router: Router, contentSearchService: ContentSearchService, activatedRoute: ActivatedRoute, cdr: ChangeDetectorRef, layoutService: LayoutService, formService: FormService, cacheService: CacheService, utilService: UtilService)
Parameters :
Name Type Optional
resourceService ResourceService No
router Router No
contentSearchService ContentSearchService No
activatedRoute ActivatedRoute No
cdr ChangeDetectorRef No
layoutService LayoutService No
formService FormService No
cacheService CacheService No
utilService UtilService No

Inputs

defaultFilters
Type : {}
Default value : {}
defaultTab
Type : {}
Default value : {}
facets$
Type : any
Default value : new BehaviorSubject({})
filterResponseData
Type : any
isOpen
Type : any
layoutConfiguration
Type : any
pageData
Type : any
pageId
Type : any
Default value : _.get(this.activatedRoute, 'snapshot.data.telemetry.pageid')
userSelectedPreference
Type : any

Outputs

filterChange
Type : EventEmitter<literal type>

Methods

Private boardChangeHandler
boardChangeHandler()
Returns : any
Private checkForWindowSize
checkForWindowSize()
Returns : void
Private emitFilterChangeEvent
emitFilterChangeEvent(skipUrlUpdate)
Parameters :
Name Optional Default value
skipUrlUpdate No false
Returns : void
Private fetchAndFormatQueryParams
fetchAndFormatQueryParams()
Returns : any
Private fetchFilters
fetchFilters()
Returns : any
Private fetchSelectedFilterOptions
fetchSelectedFilterOptions()
Returns : any
Private getAudienceTypeFormConfig
getAudienceTypeFormConfig()
Returns : any
Public getChannelId
getChannelId(index)
Parameters :
Name Optional
index No
Returns : any
Private getFacets
getFacets()
Returns : any
Private getFilterForm$
getFilterForm$()
Returns : any
Private getFramework
getFramework(undefined)
Parameters :
Name Optional
No
Returns : any
Private getIndicesFromDefaultFilters
getIndicesFromDefaultFilters(undefined)
Parameters :
Name Optional
No
Returns : {}
Public getInteractEdata
getInteractEdata()
Returns : { id: string; type: string; pageid: any; extra: { filters: any; }; }
Private getSelectedFilter
getSelectedFilter()
Returns : any
Private handleFilterChange
handleFilterChange()
Returns : any
Private hardRefreshFilter
hardRefreshFilter()
Returns : void
isLayoutAvailable
isLayoutAvailable()
Returns : any
ngOnDestroy
ngOnDestroy()
Returns : void
ngOnInit
ngOnInit()
Returns : void
onSearchFrameworkFilterReset
onSearchFrameworkFilterReset()
Returns : void
Private popFilter
popFilter(undefined)
Parameters :
Name Optional
No
Returns : void
Private pushNewFilter
pushNewFilter(undefined)
Parameters :
Name Optional
No
Returns : void
Public resetFilters
resetFilters()
Returns : void
selectedGroupOption
selectedGroupOption(data)
Parameters :
Name Optional Description
data No

event object that gets passed when selected value is changed in old layout

Returns : void
Private sortFilters
sortFilters(undefined)
Parameters :
Name Optional
No
Returns : any
Private updateBoardList
updateBoardList()
Returns : void
Private updateFiltersList
updateFiltersList(undefined: literal type)
Parameters :
Name Type Optional
literal type No
Returns : void
Private updateRoute
updateRoute(resetFilters?: boolean)
Parameters :
Name Type Optional
resetFilters boolean Yes
Returns : void

Properties

Private _filterConfig$
Type : any
allValues
Type : object
Default value : {}
Private audienceList
Private boardChange$
Default value : new Subject<void>()
Public boards
Type : any[]
Default value : []
Public emptyBoard
Default value : false
filterChangeEvent
Default value : new Subject()
filterFormTemplateConfig
Type : IFrameworkCategoryFilterFieldTemplateConfig[]
Public filterLayout
Default value : LibraryFiltersLayout
Private filters
Public layoutService
Type : LayoutService
Public optionData
Type : any[]
Default value : []
Public optionLabel
Type : object
Default value : { Publisher: _.get(this.resourceService, 'frmelmnts.lbl.publisher'), Board: _.get(this.resourceService, 'frmelmnts.lbl.boards') }
Private queryFilters
Type : any
Default value : {}
Public refresh
Default value : true
Public refreshSearchFilterComponent
Default value : true
Public resourceService
Type : ResourceService
searchFrameworkFilterComponent
Type : any
Decorators :
@ViewChild('sbSearchFrameworkFilterComponent')
Public selectedBoard
Type : literal type
selectedFilters
Type : object
Default value : {}
selectedNgModels
Type : object
Default value : {}
Public selectedOption
Type : literal type
Private unsubscribe$
Default value : new Subject<void>()

Accessors

filterData
getfilterData()
filterConfig$
getfilterConfig$()
import { Component, Output, EventEmitter, Input, OnInit, OnDestroy, ChangeDetectorRef, ViewChild } from '@angular/core';
import * as _ from 'lodash-es';
import { LibraryFiltersLayout } from '@project-sunbird/common-consumption';
import { ResourceService, LayoutService, UtilService } from '@sunbird/shared';
import { Router, ActivatedRoute } from '@angular/router';
import { Subject, merge, of, zip, BehaviorSubject, defer } from 'rxjs';
import { debounceTime, map, tap, switchMap, takeUntil, retry, catchError } from 'rxjs/operators';
import { ContentSearchService } from '../../services';
import { FormService } from '@sunbird/core';
import { IFrameworkCategoryFilterFieldTemplateConfig } from '@project-sunbird/common-form-elements-full';
import { CacheService } from '../../../shared/services/cache-service/cache.service';

@Component({
  selector: 'app-search-filter',
  templateUrl: './search-filter.component.html',
  styleUrls: ['./search-filter.component.scss']
})
export class SearchFilterComponent implements OnInit, OnDestroy {
  public filterLayout = LibraryFiltersLayout;
  public refresh = true;
  private unsubscribe$ = new Subject<void>();
  private boardChange$ = new Subject<void>();
  public emptyBoard = false;
  private filters;
  private queryFilters: any = {};
  public optionData: any[] = [];
  public selectedBoard: { label: string, value: string, selectedOption: string };
  public selectedOption: { label: string, value: string, selectedOption: string };
  public optionLabel = {
    Publisher: _.get(this.resourceService, 'frmelmnts.lbl.publisher'), Board: _.get(this.resourceService, 'frmelmnts.lbl.boards')
  };
  public boards: any[] = [];
  filterChangeEvent = new Subject();
  @Input() isOpen;
  @Input() defaultFilters = {};
  @Input() pageId = _.get(this.activatedRoute, 'snapshot.data.telemetry.pageid');
  @Output() filterChange: EventEmitter<{ status: string, filters?: any }> = new EventEmitter();
  @Input() layoutConfiguration;
  @Input() pageData;
  @Input() facets$ = new BehaviorSubject({});
  @Input() defaultTab = {};
  @Input() filterResponseData;
  @Input() userSelectedPreference;
  selectedFilters = {};
  allValues = {};
  selectedNgModels = {};
  private audienceList;
  public refreshSearchFilterComponent = true;

  @ViewChild('sbSearchFrameworkFilterComponent') searchFrameworkFilterComponent: any;
  filterFormTemplateConfig: IFrameworkCategoryFilterFieldTemplateConfig[];
  private _filterConfig$: any;
  constructor(public resourceService: ResourceService, private router: Router,
    private contentSearchService: ContentSearchService,
    private activatedRoute: ActivatedRoute, private cdr: ChangeDetectorRef,
    public layoutService: LayoutService, private formService: FormService,
    private cacheService: CacheService, private utilService: UtilService) { }

  get filterData() {
    return _.get(this.pageData, 'metaData.filters') || ['medium', 'gradeLevel', 'board', 'channel', 'subject', 'audience', 'publisher', 'se_subjects', 'se_boards', 'se_gradeLevels', 'se_mediums'];
  }

  public getChannelId(index) {
    const { publisher: publishers = [] } = this.filters || {};
    return _.get(publishers[index], 'value');
  }

  private fetchAndFormatQueryParams() {
    return this.activatedRoute.queryParams
      .pipe(
        map(
          queryParams => {
            const queryFilters: Record<string, string[]> = {};
            _.forIn(queryParams, (value, key) => {
              if (this.filterData.includes(key)) {
                queryFilters[key] = _.isArray(value) ? value : [value];
              }
            });
            this.queryFilters = _.cloneDeep(queryFilters);
            return queryFilters;
          }
        )
      );
  }

  private checkForWindowSize() {
    if (window.innerWidth <= 992) {
      this.isOpen = false;
    }
  }

  private getFramework({ boardName = null }) {
    return this.contentSearchService.fetchFilter(boardName);
  }

  private sortFilters({ filters, filterBy = 'name', omitKeys = ['gradeLevel'] }) {
    const sortedFilters = _.cloneDeep(filters);
    _.forEach(sortedFilters, (values, key) => {
      sortedFilters[key] = _.includes(omitKeys, key) ? values : _.sortBy(values, [filterBy]);
    });
    return sortedFilters;
  }

  private fetchFilters() {
    return this.fetchAndFormatQueryParams()
      .pipe(
        switchMap(queryParams => {
          this.filterChange.emit({ status: 'FETCHING' });
          let boardName = _.get(queryParams, 'board[0]') || _.get(this.boards, '[0]');
          return zip(this.getFramework({ boardName }), this.getAudienceTypeFormConfig())
            .pipe(map(([filters, audienceTypeFilter]: [object, object]) => ({ ...filters, audience: audienceTypeFilter })));
        })
      );
  }

  ngOnInit() {
    this.getFilterForm$();
    this.checkForWindowSize();
    merge(this.boardChangeHandler(), this.fetchSelectedFilterOptions(), this.handleFilterChange(), this.getFacets(), this.filterConfig$)
      .pipe(takeUntil(this.unsubscribe$))
      .subscribe(null, error => {
        console.error('Error while fetching filters', error);
      });

    // Checking the fix for #ED-2455
    // if (!_.get(this.activatedRoute, 'snapshot.queryParams["board"]')) {
    //   const queryParams = { ...this.defaultFilters, selectedTab: _.get(this.activatedRoute, 'snapshot.queryParams.selectedTab') || _.get(this.defaultTab, 'contentType') || 'textbook' };
    //   this.router.navigate([], { queryParams, relativeTo: this.activatedRoute });
    // }
  }

  /**
  * @description - Method for old layout, triggered when the selected board changed in the old layout
  */
  private boardChangeHandler() {
    return this.boardChange$.pipe(
      switchMap(boardName => {
        return this.getFramework({ boardName })
          .pipe(
            tap(filters => {
              this.filters = { ...this.filters, ...this.sortFilters({ filters }) };
              this.updateRoute();
            })
          );
      })
    );
  }

  private fetchSelectedFilterOptions() {
    return this.fetchFilters()
      .pipe(
        tap(filters => {
          filters = _.pick(filters || {}, this.filterData);
          this.filters = filters = this.sortFilters({ filters });
          this.updateBoardList();
          this.updateFiltersList({ filters: _.omit(filters, 'board') });
          this.emitFilterChangeEvent(true);
          this.hardRefreshFilter();
        })
      );
  }

  private handleFilterChange() {
    return this.filterChangeEvent
      .pipe(
        debounceTime(1000),
        tap(({ type, event }) => {
          if (_.has(event, 'data.index')) {
            const index = _.get(event, 'data.index');
            const selectedIndices = _.get(this.selectedFilters, type) || [];
            if (_.includes(selectedIndices, index)) {
              if (_.get(selectedIndices, 'length') > 1) {
                this.popFilter({ type, index });
              }
            } else {
              this.pushNewFilter({ type, index });
            }
          } else {
            if (type === 'subject') {
              this.selectedNgModels['selected_subjects'] = event;
            }
            this.pushNewFilter({
              type, updatedValues: _.map(event || [],
                selectedValue => _.findIndex(this.allValues[type], val => val === selectedValue))
            });
          }
          this.emitFilterChangeEvent();
        }));
  }

  private updateBoardList() {
    if (_.get(this.filters, 'board') || !_.get(this.filters, 'board.length')) {
      this.emptyBoard = true;
    }
    this.boards = this.allValues['board'] = this.filters.board || [];
    this.boards = _.map(this.boards, node => ({
      name: node.name,
      value: node.name,
    }));
    this.optionData.push({
      label: this.optionLabel.Board,
      value: 'board',
      option: this.boards
    });
    this.optionData = _.uniqBy(this.optionData, 'label');
    if (this.boards.length) {
      const selectedOption = _.find(this.boards, { name: _.get(this.queryFilters, 'board[0]') }) ||
        _.find(this.boards, { name: _.get(this.defaultFilters, 'board[0]') }) || this.boards[0];
      this.selectedBoard = { label: this.optionLabel.Board, value: 'board', selectedOption: _.get(selectedOption, 'name') };
      this.selectedOption = this.selectedBoard;
    }
  }

  private popFilter({ type, index }) {
    const selectedIndices = _.get(this.selectedFilters, type) || [];
    _.remove(selectedIndices, (currentIndex) => {
      return currentIndex === index;
    });
  }

  private pushNewFilter({ type, index = null, updatedValues = [] }) {
    if (index != null) {
      this.selectedFilters[type] = [index, ...(this.selectedFilters.hasOwnProperty(type) ? this.selectedFilters[type] : [])];
    } else {
      this.selectedFilters[type] = updatedValues;
    }
  }
  private getIndicesFromDefaultFilters({ type }) {
    const defaultValues = _.get(this.defaultFilters, type) || [];
    let indices = [];
    if (_.get(defaultValues, 'length')) {
      indices = _.filter(_.map(defaultValues, defaultValue => _.findIndex(this.allValues[type] || [],
        val => val === defaultValue)), index => index !== -1);
    }
    if (['audience', 'publisher', 'subject'].includes(type) && !indices.length) {
      return [];
    }
    return indices.length ? indices : [];
  }

  /**
   *@description - This method is used to update the filters list  
   * @param  {{filters:Record<string,any[]>} {filter}- The filter list that need to be updated 
   */
  private updateFiltersList({ filters }: { filters: Record<string, any[]> }) {
    this.selectedFilters = {};
    this.selectedNgModels = {};
    this.allValues = {};
    _.forEach(filters, (filterValues: { name: any }[], filterKey: string) => {
     const values = this.allValues[filterKey] = _.map(filterValues, 'name');
      if (_.get(values, 'length')) {
        let selectedIndices;
        const filterValuesFromQueryParams = this.queryFilters[filterKey] || [];
        if (_.get(filterValuesFromQueryParams, 'length')) {
          const indices = _.filter(_.map(filterValuesFromQueryParams, queryParamValue => values.findIndex((val) =>
            _.toLower(val) === _.toLower(queryParamValue))), index => index !== -1);
          selectedIndices = _.get(indices, 'length') ? indices : this.getIndicesFromDefaultFilters({ type: filterKey });
        } else {
          selectedIndices = this.getIndicesFromDefaultFilters({ type: filterKey });
        }
        this.selectedFilters[filterKey] = selectedIndices;
        this.selectedNgModels[filterKey] = _.map(selectedIndices, index => this.allValues[filterKey][index]);
        if (filterKey === 'subject') {
          this.selectedNgModels['selected_subjects'] = filterValuesFromQueryParams;
        }
      }
    });
  }
  private updateRoute(resetFilters?: boolean) {
    const selectedTab = _.get(this.activatedRoute, 'snapshot.queryParams.selectedTab') || _.get(this.defaultTab, 'contentType') || 'textbook';
    this.router.navigate([], {
      queryParams: resetFilters ? { ...this.defaultFilters, selectedTab } : _.omit(this.getSelectedFilter() || {}, ['audienceSearchFilterValue']),
      relativeTo: this.activatedRoute.parent
    });
  }

  /**
   * @description - Method which gets triggered when selected value changes in old layout
   * @param  {} data event object that gets passed when selected value is changed in old layout
   */
  selectedGroupOption(data) {
    this.selectedOption = data;
    this.selectedBoard = data;
    this.filterChange.emit({ status: 'FETCHING' });
    this.boardChange$.next(_.get(data, 'selectedOption'));
  }
  private getSelectedFilter() {
    const filters = _.mapValues(this.selectedFilters, (value, key) => {
      return _.compact(_.map(value, index => _.has(this.allValues, [key, index]) ? this.allValues[key][index] : null));
    });
    if (_.has(this.selectedFilters, 'publisher')) {
      filters['channel'] = _.compact(_.map(this.selectedFilters['publisher'], publisher => this.getChannelId(publisher)));
    }
    if (_.has(this.selectedNgModels, 'selected_subjects')) {
      filters['subject'] = this.selectedNgModels['selected_subjects'] || [];
    }
    if (_.has(this.selectedFilters, 'audience')) {
      filters['audienceSearchFilterValue'] = _.flatten(_.compact(_.map(filters['audience'] || {}, audienceType => {
        const audience = _.find(this.audienceList || {}, { 'name': audienceType });
        return audience ? _.get(audience, 'searchFilter') : null;
      })));
    }
    filters['board'] = _.get(this.selectedBoard, 'selectedOption') ? [this.selectedBoard.selectedOption] : [];
    filters['selectedTab'] = _.get(this.activatedRoute, 'snapshot.queryParams.selectedTab') || _.get(this.defaultTab, 'contentType') || 'textbook';
    return filters;
  }
  private emitFilterChangeEvent(skipUrlUpdate = false) {
    const filters = this.getSelectedFilter();
    this.filterChange.emit({ status: 'FETCHED', filters });
    if (!skipUrlUpdate) {
      this.updateRoute();
    }
  }

  /**
   *@description -  Method that gets called when any user interaction happens on the page. Used for telemetry purpose
   */
  public getInteractEdata() {
    return {
      id: 'reset-filter',
      type: 'click',
      pageid: _.get(this.activatedRoute, 'snapshot.data.telemetry.pageid'),
      extra: {
        filters: this.getSelectedFilter() || {}
      }
    };
  }
  ngOnDestroy() {
    this.unsubscribe$.next();
    this.unsubscribe$.complete();
  }
  private hardRefreshFilter() {
    this.refresh = false;
    this.cdr.detectChanges();
    this.refresh = true;
  }
  isLayoutAvailable() {
    return this.layoutService.isLayoutAvailable(this.layoutConfiguration);
  }
  public resetFilters() {
    this.updateRoute(true);
  }

  onSearchFrameworkFilterReset() {
    if (this.cacheService.exists('searchFilters')) {
      this.cacheService.remove('searchFilters');
    }
    if (this.searchFrameworkFilterComponent) {
      const selectedTab = _.get(this.activatedRoute, 'snapshot.queryParams.selectedTab') || _.get(this.defaultTab, 'contentType') || 'textbook';
      this.router.navigate([], {
        queryParams: { ...this.userSelectedPreference, selectedTab },
        relativeTo: this.activatedRoute.parent
      });
    }
  }

  private getAudienceTypeFormConfig() {
    const formServiceInputParams = { formType: 'config', formAction: 'get', contentType: 'userType', component: 'portal' };
    return this.formService.getFormConfig(formServiceInputParams).pipe(
      map(response => _.map(_.filter(response, 'visibility'), value => {
        const { name, searchFilter } = value;
        return { name: name, searchFilter };
      })),
      tap(mapping => { this.audienceList = mapping; }),
      retry(5),
      catchError(err => of([]))
    );
  }

  /**
   * @description - Method to fetch facets passed from parent component. Subjects from facets are overridden here
   */
  private getFacets() {
    return this.facets$.pipe(tap(filters => {
      filters = this.filters = { ...this.filters, ...this.sortFilters({ filters }) };
      const categoryMapping = Object.entries(this.contentSearchService.getCategoriesMapping);
      filters = _.mapKeys(filters, (value, filterKey) => {
        const [key = null] = categoryMapping.find(([category, mappedValue]) => mappedValue === filterKey) || [];
        return key || filterKey;
      });
      this.updateFiltersList({ filters });
      this.hardRefreshFilter();
    }));
  }

  /**
   * @description - Method to get the formconfig for filters. Language translation for labels also takes place here
   */
  get filterConfig$() {
    return this.resourceService.languageSelected$.pipe(
      switchMap(_ => this._filterConfig$),
      tap((config: IFrameworkCategoryFilterFieldTemplateConfig[]) => {
        this.filterFormTemplateConfig = config;
        this.resourceService.languageSelected$.pipe(takeUntil(this.unsubscribe$)).subscribe((languageData) => {
          this.filterFormTemplateConfig?.forEach((facet) => {
            facet['labelText'] = this.utilService.transposeTerms(facet['labelText'], facet['labelText'], this.resourceService.selectedLang);
            facet['placeholderText'] = this.utilService.transposeTerms(facet['placeholderText'], facet['placeholderText'], this.resourceService.selectedLang);
          });
        });
        this.refreshSearchFilterComponent = false;
        this.cdr.detectChanges();
        this.refreshSearchFilterComponent = true;
      }))
  }

  /**
   * @description - Method to transform the input formconfig to pass it as config to the search filter plugin
   */
  private getFilterForm$() {
    if (this.filterResponseData) {
      this._filterConfig$ = defer(() => of(
        this.filterResponseData.map((value) => {
          return {
            category: _.get(value, 'category'),
            type: _.get(value, 'type'),
            labelText: _.get(this.resourceService, value.labelText) ? _.get(this.resourceService, value.labelText) : _.get(value, 'defaultLabelText'),
            placeholderText: _.get(this.resourceService, value.placeholderText) ? _.get(this.resourceService, value.placeholderText) : _.get(value, 'defaultPlaceholderText'),
            multiple: _.get(value, 'multiple'),
          }
        }
        )));
    }
    return this._filterConfig$;
  }
}
<ng-container *ngIf="!isLayoutAvailable()" tabindex="-1">
  <div *ngIf="(boards.length || emptyBoard) && refresh" class="sb-header-filter-bar zindex-2">
    <div class="d-flex state-medium-container sb-bg-color-white">
      <div class="ui container d-flex flex-ai-center">
        <div *ngIf="boards?.length" class="state-medium-container__dropdown">
          <div class="sb-field mb-0"><label>
              <app-select-option-group *ngIf="optionData" [optionData]="optionData" [selectedOption]="selectedOption"
                (selectedValue)="selectedGroupOption($event);">
              </app-select-option-group>
            </label>
          </div>
        </div>
        <div *ngIf="boards?.length && allValues?.medium?.length" class="state-medium-container__separator">
        </div>
        <div class="state-medium-container__medium" *ngIf="allValues?.medium?.length">
          <sb-library-filters [list]="allValues['medium']" [layout]="filterLayout.SQUARE"
            [selectedItems]="selectedFilters['medium']"
            (selectedFilter)="filterChangeEvent.next({ event: $event, type: 'medium'})">
          </sb-library-filters>
        </div>
      </div>
    </div>
    <div class="sb-class-bar sb-bg-color-gray-0" *ngIf="allValues?.gradeLevel?.length">
      <div class="ui container">
        <sb-library-filters [list]="allValues['gradeLevel']" [layout]="filterLayout.ROUND"
          [selectedItems]="selectedFilters['gradeLevel']"
          (selectedFilter)="filterChangeEvent.next({ event: $event, type: 'gradeLevel'})">
        </sb-library-filters>
      </div>
    </div>
    <div class="sb-bg-color-white sb-global-filter-section">
      <div class="ui container">
        <div class="d-flex flex-ai-center flex-w-wrap">
          <div *ngIf="allValues?.subject?.length" class="state-medium-container__dropdown">
            <div class="sb-field mb-0"><label>
                <sui-multi-select [hasLabels]="false" [(ngModel)]="selectedNgModels['subject']"
                  (selectedOptionsChange)="filterChangeEvent.next({ event: $event, type: 'subject'})"
                  defaultSelectionText={{resourceService?.frmelmnts?.lbl?.subject}}
                  zeroSelectionText={{resourceService.frmelmnts.lbl.Select}}
                  class="selection sbt-dropdown-tick text-cencapitalize"
                  [ngClass]="!layoutConfiguration ? 'state-selection' : 'sbt-dropdown sbt-dropdown-bold sbt-dropdown--sm sbt-purple--lbg'">
                  <div [ngClass]="!layoutConfiguration ? 'state-selection__section' : 'sbt-dropdown-section'">
                    <sui-select-option *ngFor="let dropdownValue of allValues['subject']" [value]="dropdownValue">
                    </sui-select-option>
                  </div>
                </sui-multi-select>
              </label>
            </div>
          </div>
          <div *ngIf="allValues?.publisher?.length" class="state-medium-container__separator"> </div>
          <div *ngIf="allValues?.publisher?.length" class="state-medium-container__dropdown">
            <div class="sb-field mb-0"><label>
                <sui-multi-select [hasLabels]="false" [(ngModel)]="selectedNgModels['publisher']"
                  (selectedOptionsChange)="filterChangeEvent.next({ event: $event, type: 'publisher'})"
                  defaultSelectionText={{resourceService?.frmelmnts?.lbl?.publishedBy}}
                  zeroSelectionText={{resourceService?.frmelmnts?.lbl?.Select}}
                  class="selection sbt-dropdown-tick text-cencapitalize"
                  [ngClass]="!layoutConfiguration ? 'state-selection' : 'sbt-dropdown sbt-dropdown-bold sbt-dropdown--sm sbt-purple--lbg'">
                  <div [ngClass]="!layoutConfiguration ? 'state-selection__section' : 'sbt-dropdown-section'">
                    <sui-select-option *ngFor="let dropdownValue of allValues['publisher']" [value]="dropdownValue">
                    </sui-select-option>
                  </div>
                </sui-multi-select>
              </label>
            </div>
          </div>
          <div *ngIf="allValues?.audience?.length" class="state-medium-container__separator"> </div>
          <div *ngIf="allValues?.audience?.length" class="state-medium-container__dropdown">
            <div class="sb-field mb-0"><label>
                <sui-multi-select [hasLabels]="false" [(ngModel)]="selectedNgModels['audience']"
                  (selectedOptionsChange)="filterChangeEvent.next({ event: $event, type: 'audience'})"
                  defaultSelectionText={{resourceService?.frmelmnts?.lbl?.userType}}
                  zeroSelectionText={{resourceService.frmelmnts.lbl.Select}} tabindex="0"
                  class="selection sbt-dropdown-tick text-cencapitalize"
                  [ngClass]="!layoutConfiguration ? 'state-selection' : 'sbt-dropdown sbt-dropdown-bold sbt-dropdown--sm sbt-purple--lbg'">
                  <div [ngClass]="!layoutConfiguration ? 'state-selection__section' : 'sbt-dropdown-section'">
                    <sui-select-option *ngFor="let dropdownValue of allValues['audience']" [value]="dropdownValue">
                    </sui-select-option>
                  </div>
                </sui-multi-select>
              </label>
            </div>
          </div>
          <button (click)="resetFilters()" appTelemetryInteract [telemetryInteractEdata]="getInteractEdata()"
            tabindex="0" class="sb-btn sb-btn-normal sb-btn-outline-primary ml-auto">{{resourceService.frmelmnts?.btn?.reset
            | translate}}
          </button>
        </div>
      </div>
    </div>
  </div>
</ng-container>
<div *ngIf="isLayoutAvailable()" class="sbt-filter" tabindex="-1">
  <mat-accordion class="sb-mat-accordion sbt-filter-accordion">
    <mat-expansion-panel [expanded]="isOpen">
      <mat-expansion-panel-header>
        <div class="sbt-filter-switcher-container cursor-pointer mobile only" (click)="isOpen = !isOpen" tabindex="0">
          <div class="sbt-filter-switcher"><i class="sliders horizontal icon"></i></div>
          <div class="sbt-filter-text">{{resourceService.frmelmnts?.lbl?.filters | translate}}</div>
        </div>
        <div class="sbt-filter-switcher-container cursor-pointer computer only">
          <div class="sbt-filter-switcher"><i class="sliders horizontal icon"></i></div>
          <div class="sbt-filter-text">{{resourceService.frmelmnts?.lbl?.filters | translate}}</div>
        </div>
      </mat-expansion-panel-header>
      <ng-container>
        <div [hidden]="!((boards.length || emptyBoard) && refresh)" class="sbt-filter-bar mr-16 pl-24">
          <div class="sbt-reset-bar d-flex flex-ai-center flex-ai-jc-center" tabindex="0">
            <button (click)="onSearchFrameworkFilterReset()" tabindex="0" appTelemetryInteract
              [telemetryInteractEdata]="getInteractEdata()"
              class="sb-btn sb-btn-xs sb-btn-link-primary pull-right sbt-btn-reset cursor-pointer">{{resourceService.frmelmnts?.btn?.reset
              | translate}}
              <i class="icon undo"></i></button>
            <span class="sbt-filter-close"><i class="icon-svg icon-svg--xxs icon-close cursor-pointer"
                (click)="isOpen = !isOpen" tabindex="0" attr.aria-label="{{resourceService.frmelmnts?.btn?.close}}">
                <svg class="icon icon-svg--red">
                  <use xlink:href="./assets/images/sprite.svg#close"></use>
                </svg>
              </i>
            </span>
          </div>
          <div class="fsmall my-8 text-left filter-pref-text" tabindex="0">
            {{resourceService.frmelmnts?.lbl?.basedOnPref | translate}}</div>
          <div class="sbt-filter-scrollable pr-24">
            <ng-container *ngIf="filterFormTemplateConfig && refreshSearchFilterComponent">
              <sb-search-framework-filter [frameworkAssociations]="allValues"
                [frameworkCategoryFilterFieldTemplateConfig]="filterFormTemplateConfig"
                [supportedFilterAttributes]="pageData?.metaData?.filters" [baseSearchFilter]="defaultFilters"
                #sbSearchFrameworkFilterComponent>
              </sb-search-framework-filter>
            </ng-container>
          </div>
        </div>
      </ng-container>
    </mat-expansion-panel>
  </mat-accordion>
</div>

./search-filter.component.scss

@use "@project-sunbird/sb-styles/assets/mixins/mixins" as *;
@use "pages/sbt-filter" as *;
@use "components/sb-forms/facet-filters" as *;

.sb-header-filter-bar {
  box-shadow: 0 calculateRem(2px) calculateRem(8px) 0 rgba(var(--rc-rgba-black), 0.2);
  position: relative;
}

.state-medium-container {
  position: relative;
  z-index: 999;
  height: calculateRem(64px);

  &__dropdown {
    padding: calculateRem(16px) 0;
    z-index: 1;
    .sb-field label{
      margin-bottom: 0;
    }
  }

  &__separator {
    height: calculateRem(24px);
    width: calculateRem(1px);
    background-color: var(--gray-200);
    margin: 0px calculateRem(16px) 0px calculateRem(16px);

    @include respond-below(sm) {
      margin: 0px calculateRem(8px) 0px calculateRem(8px);
    }
  }

  &__medium {
    overflow-x: auto;
    overflow-y: hidden;
    height: calculateRem(64px);

    @include respond-below(xs) {
      flex: 0 0 50%;
    }
  }
}
.sb-global-filter-section {
  @include respond-below(sm) {
    padding-bottom: calculateRem(16px);
    .state-medium-container__dropdown {
      width: 100%;
      .sb-field {
        width: 100%;
        label{
          width: 100%;
        }
      }
    }
    .state-medium-container__separator {
      display: none;
    }
  }
}

// state dropdown css
.ui.selection.dropdown .menu>.item {
  font-size: calculateRem(12px);
  padding: 1em 1em !important;
}

.ui.selection.dropdown.state-medium {
  color: var(--primary-400);
  display: flex;
  align-items: center;
  border: none;
  background: none;
  min-width: calculateRem(100px);
  margin: 0;

  .medium-selection {
    min-width: calculateRem(120px);
  }

  &:hover {
    background: var(--rc-E0F1FD);
  }
}
.state-selection__section
 .item:hover, .state-selection__section .item.active {
  background-color: var(--primary-100);
  width: 100% !important;
  padding-right: calculateRem(24px);
}
.state-selection__section .item {
  padding: 0.25rem 0.5rem;
  display: block;
  cursor: pointer;
  width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
}

.ui.selection.dropdown .menu {
  max-height: 20em;
}

.ui.default.dropdown:not(.button)>.text,
.ui.dropdown:not(.button)>.default.text {
  color: var(--red-400);
}



:host ::ng-deep .dropdown:not(.button)>.default.text {
  color: var(--primary-400) !important;
}

:host ::ng-deep .sb-slider-pills-container .sb-pills-container {
  padding: 0.5rem 0 !important;
}





Legend
Html element
Component
Html element with directive

results matching ""

    No results matching ""