src/app/modules/content-search/components/search-filter/search-filter.component.ts
OnInit
OnDestroy
selector | app-search-filter |
styleUrls | ./search-filter.component.scss |
templateUrl | ./search-filter.component.html |
constructor(resourceService: ResourceService, router: Router, contentSearchService: ContentSearchService, activatedRoute: ActivatedRoute, cdr: ChangeDetectorRef, layoutService: LayoutService, formService: FormService, cacheService: CacheService, utilService: UtilService)
|
||||||||||||||||||||||||||||||
Parameters :
|
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
|
|
filterChange | |
Type : EventEmitter<literal type>
|
|
Private boardChangeHandler |
boardChangeHandler()
|
Returns :
any
|
Private checkForWindowSize |
checkForWindowSize()
|
Returns :
void
|
Private emitFilterChangeEvent | ||||||
emitFilterChangeEvent(skipUrlUpdate)
|
||||||
Parameters :
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 :
Returns :
any
|
Private getFacets |
getFacets()
|
Returns :
any
|
Private getFilterForm$ |
getFilterForm$()
|
Returns :
any
|
Private getFramework | |||
getFramework(undefined)
|
|||
Parameters :
Returns :
any
|
Private getIndicesFromDefaultFilters | |||
getIndicesFromDefaultFilters(undefined)
|
|||
Parameters :
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 :
Returns :
void
|
Private pushNewFilter | |||
pushNewFilter(undefined)
|
|||
Parameters :
Returns :
void
|
Public resetFilters |
resetFilters()
|
Returns :
void
|
selectedGroupOption | ||||||
selectedGroupOption(data)
|
||||||
Parameters :
Returns :
void
|
Private sortFilters | |||
sortFilters(undefined)
|
|||
Parameters :
Returns :
any
|
Private updateBoardList |
updateBoardList()
|
Returns :
void
|
Private updateFiltersList | |||||
updateFiltersList(undefined: literal type)
|
|||||
Parameters :
Returns :
void
|
Private updateRoute | ||||||
updateRoute(resetFilters?: boolean)
|
||||||
Parameters :
Returns :
void
|
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>()
|
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;
}