mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-07 10:04:11 +00:00
Implement sidebar filter and dropdown components
This commit is contained in:
@@ -1,7 +1,18 @@
|
||||
<div class="facet-filter d-block mb-3 p-3" *ngIf="active$ | async">
|
||||
<div (click)="toggle()" class="filter-name"><h5 class="d-inline-block mb-0">{{'search.filters.filter.' + filter.name + '.head'| translate}}</h5> <span class="filter-toggle fas float-right"
|
||||
[ngClass]="(collapsed$ | async) ? 'fa-plus' : 'fa-minus'"></span></div>
|
||||
<div [@slide]="(collapsed$ | async) ? 'collapsed' : 'expanded'" (@slide.start)="startSlide($event)" (@slide.done)="finishSlide($event)" class="search-filter-wrapper" [ngClass]="{'closed' : closed}">
|
||||
<ds-search-facet-filter-wrapper [filterConfig]="filter" [inPlaceSearch]="inPlaceSearch"></ds-search-facet-filter-wrapper>
|
||||
<div (click)="toggle()" class="filter-name">
|
||||
<h5 class="d-inline-block mb-0">
|
||||
{{'search.filters.filter.' + filter.name + '.head'| translate}}
|
||||
</h5>
|
||||
<span class="filter-toggle fas float-right"
|
||||
[ngClass]="(collapsed$ | async) ? 'fa-plus' : 'fa-minus'">
|
||||
</span>
|
||||
</div>
|
||||
<div [@slide]="(collapsed$ | async) ? 'collapsed' : 'expanded'"
|
||||
(@slide.start)="startSlide($event)" (@slide.done)="finishSlide($event)"
|
||||
class="search-filter-wrapper" [ngClass]="{'closed' : closed}">
|
||||
<ds-search-facet-filter-wrapper
|
||||
[filterConfig]="filter"
|
||||
[inPlaceSearch]="inPlaceSearch">
|
||||
</ds-search-facet-filter-wrapper>
|
||||
</div>
|
||||
</div>
|
||||
|
@@ -34,6 +34,7 @@ import { SearchLabelComponent } from './search-labels/search-label/search-label.
|
||||
import { ConfigurationSearchPageComponent } from './configuration-search-page.component';
|
||||
import { ConfigurationSearchPageGuard } from './configuration-search-page.guard';
|
||||
import { FilteredSearchPageComponent } from './filtered-search-page.component';
|
||||
import { SidebarFilterService } from "../shared/sidebar/filter/sidebar-filter.service";
|
||||
|
||||
const effects = [
|
||||
SidebarEffects
|
||||
@@ -78,6 +79,7 @@ const components = [
|
||||
declarations: components,
|
||||
providers: [
|
||||
SidebarService,
|
||||
SidebarFilterService,
|
||||
SearchFilterService,
|
||||
SearchFixedFilterService,
|
||||
ConfigurationSearchPageGuard,
|
||||
|
@@ -1,24 +1,32 @@
|
||||
<ng-container *ngVar="(searchOptions$ | async) as config">
|
||||
<h3>{{ 'search.sidebar.settings.title' | translate}}</h3>
|
||||
<div *ngIf="config?.sort" class="setting-option result-order-settings mb-3 p-3">
|
||||
<h5>{{ 'search.sidebar.settings.sort-by' | translate}}</h5>
|
||||
<select class="form-control" (change)="reloadOrder($event)">
|
||||
|
||||
<div class="result-order-settings">
|
||||
<ds-sidebar-dropdown
|
||||
*ngIf="config?.sort"
|
||||
[id]="'search-sidebar-sort'"
|
||||
[label]="'search.sidebar.settings.sort-by'"
|
||||
(change)="reloadOrder($event)"
|
||||
>
|
||||
<option *ngFor="let sortOption of searchOptionPossibilities"
|
||||
[value]="sortOption.field + ',' + sortOption.direction.toString()"
|
||||
[selected]="sortOption.field === config?.sort.field && sortOption.direction === (config?.sort.direction)? 'selected': null">
|
||||
{{'sorting.' + sortOption.field + '.' + sortOption.direction | translate}}
|
||||
</option>
|
||||
</select>
|
||||
</ds-sidebar-dropdown>
|
||||
</div>
|
||||
|
||||
<div class="setting-option page-size-settings mb-3 p-3">
|
||||
<h5>{{ 'search.sidebar.settings.rpp' | translate}}</h5>
|
||||
<select class="form-control" (change)="reloadRPP($event)">
|
||||
<div class="page-size-settings">
|
||||
<ds-sidebar-dropdown
|
||||
[id]="'search-sidebar-rpp'"
|
||||
[label]="'search.sidebar.settings.rpp'"
|
||||
(change)="reloadRPP($event)"
|
||||
>
|
||||
<option *ngFor="let pageSizeOption of config?.pagination.pageSizeOptions"
|
||||
[value]="pageSizeOption"
|
||||
[selected]="pageSizeOption === +config?.pagination.pageSize ? 'selected': null">
|
||||
{{pageSizeOption}}
|
||||
</option>
|
||||
</select>
|
||||
</ds-sidebar-dropdown>
|
||||
</div>
|
||||
</ng-container>
|
@@ -1,4 +1,4 @@
|
||||
import { ActionReducerMap, createSelector, MemoizedSelector } from '@ngrx/store';
|
||||
import { ActionReducerMap, createSelector, MemoizedSelector, State } from '@ngrx/store';
|
||||
import * as fromRouter from '@ngrx/router-store';
|
||||
import { hostWindowReducer, HostWindowState } from './shared/host-window.reducer';
|
||||
import { formReducer, FormState } from './shared/form/form.reducer';
|
||||
@@ -6,6 +6,10 @@ import {
|
||||
SidebarState,
|
||||
sidebarReducer
|
||||
} from './shared/sidebar/sidebar.reducer';
|
||||
import {
|
||||
SidebarFilterState,
|
||||
sidebarFilterReducer, SidebarFiltersState
|
||||
} from './shared/sidebar/filter/sidebar-filter.reducer';
|
||||
import {
|
||||
filterReducer,
|
||||
SearchFiltersState
|
||||
@@ -37,7 +41,8 @@ export interface AppState {
|
||||
metadataRegistry: MetadataRegistryState;
|
||||
bitstreamFormats: BitstreamFormatRegistryState;
|
||||
notifications: NotificationsState;
|
||||
searchSidebar: SidebarState;
|
||||
sidebar: SidebarState;
|
||||
sidebarFilter: SidebarFiltersState;
|
||||
searchFilter: SearchFiltersState;
|
||||
truncatable: TruncatablesState;
|
||||
cssVariables: CSSVariablesState;
|
||||
@@ -53,7 +58,8 @@ export const appReducers: ActionReducerMap<AppState> = {
|
||||
metadataRegistry: metadataRegistryReducer,
|
||||
bitstreamFormats: bitstreamFormatReducer,
|
||||
notifications: notificationsReducer,
|
||||
searchSidebar: sidebarReducer,
|
||||
sidebar: sidebarReducer,
|
||||
sidebarFilter: sidebarFilterReducer,
|
||||
searchFilter: filterReducer,
|
||||
truncatable: truncatableReducer,
|
||||
cssVariables: cssVariablesReducer,
|
||||
|
@@ -148,6 +148,9 @@ import { PublicationGridElementComponent } from './object-grid/item-grid-element
|
||||
import { ItemTypeBadgeComponent } from './object-list/item-type-badge/item-type-badge.component';
|
||||
import { ItemMetadataRepresentationListElementComponent } from './object-list/metadata-representation-list-element/item/item-metadata-representation-list-element.component';
|
||||
import { PageWithSidebarComponent } from './sidebar/page-with-sidebar.component';
|
||||
import { SidebarDropdownComponent } from './sidebar/sidebar-dropdown.component';
|
||||
import { SidebarFilterComponent } from './sidebar/filter/sidebar-filter.component';
|
||||
import { SidebarFilterSelectedOptionComponent } from './sidebar/filter/sidebar-filter-selected-option.component';
|
||||
|
||||
const MODULES = [
|
||||
// Do NOT include UniversalModule, HttpModule, or JsonpModule here
|
||||
@@ -235,6 +238,9 @@ const COMPONENTS = [
|
||||
PaginationComponent,
|
||||
SearchFormComponent,
|
||||
PageWithSidebarComponent,
|
||||
SidebarDropdownComponent,
|
||||
SidebarFilterComponent,
|
||||
SidebarFilterSelectedOptionComponent,
|
||||
ThumbnailComponent,
|
||||
GridThumbnailComponent,
|
||||
UploaderComponent,
|
||||
|
@@ -0,0 +1,6 @@
|
||||
<a class="d-flex flex-row" (click)="click.emit($event)">
|
||||
<label>
|
||||
<input type="checkbox" [checked]="true" class="my-1 align-self-stretch"/>
|
||||
<span class="filter-value pl-1 text-capitalize">{{label}}</span>
|
||||
</label>
|
||||
</a>
|
@@ -0,0 +1,11 @@
|
||||
a {
|
||||
color: $body-color;
|
||||
|
||||
&:hover, &focus {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
span.badge {
|
||||
vertical-align: text-top;
|
||||
}
|
||||
}
|
@@ -0,0 +1,15 @@
|
||||
import { Component, EventEmitter, Input, Output } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'ds-sidebar-filter-selected-option',
|
||||
styleUrls: ['./sidebar-filter-selected-option.component.scss'],
|
||||
templateUrl: './sidebar-filter-selected-option.component.html',
|
||||
})
|
||||
|
||||
/**
|
||||
* Represents a single selected option in a sidebar filter
|
||||
*/
|
||||
export class SidebarFilterSelectedOptionComponent {
|
||||
@Input() label:string;
|
||||
@Output() click:EventEmitter<any> = new EventEmitter<any>();
|
||||
}
|
74
src/app/shared/sidebar/filter/sidebar-filter.actions.ts
Normal file
74
src/app/shared/sidebar/filter/sidebar-filter.actions.ts
Normal file
@@ -0,0 +1,74 @@
|
||||
import { Action } from '@ngrx/store';
|
||||
|
||||
import { type } from '../../ngrx/type';
|
||||
|
||||
/**
|
||||
* For each action type in an action group, make a simple
|
||||
* enum object for all of this group's action types.
|
||||
*
|
||||
* The 'type' utility function coerces strings into string
|
||||
* literal types and runs a simple check to guarantee all
|
||||
* action types in the application are unique.
|
||||
*/
|
||||
export const SidebarFilterActionTypes = {
|
||||
INITIALIZE: type('dspace/sidebar-filter/INITIALIZE'),
|
||||
COLLAPSE: type('dspace/sidebar-filter/COLLAPSE'),
|
||||
EXPAND: type('dspace/sidebar-filter/EXPAND'),
|
||||
TOGGLE: type('dspace/sidebar-filter/TOGGLE'),
|
||||
};
|
||||
|
||||
export class SidebarFilterAction implements Action {
|
||||
/**
|
||||
* Name of the filter the action is performed on, used to identify the filter
|
||||
*/
|
||||
filterName: string;
|
||||
|
||||
/**
|
||||
* Type of action that will be performed
|
||||
*/
|
||||
type;
|
||||
|
||||
/**
|
||||
* Initialize with the filter's name
|
||||
* @param {string} name of the filter
|
||||
*/
|
||||
constructor(name: string) {
|
||||
this.filterName = name;
|
||||
}
|
||||
}
|
||||
|
||||
/* tslint:disable:max-classes-per-file */
|
||||
/**
|
||||
* Used to initialize a filter
|
||||
*/
|
||||
export class FilterInitializeAction extends SidebarFilterAction {
|
||||
type = SidebarFilterActionTypes.INITIALIZE;
|
||||
initiallyExpanded;
|
||||
|
||||
constructor(name:string, initiallyExpanded:boolean) {
|
||||
super(name);
|
||||
this.initiallyExpanded = initiallyExpanded;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to collapse a filter
|
||||
*/
|
||||
export class FilterCollapseAction extends SidebarFilterAction {
|
||||
type = SidebarFilterActionTypes.COLLAPSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to expand a filter
|
||||
*/
|
||||
export class FilterExpandAction extends SidebarFilterAction {
|
||||
type = SidebarFilterActionTypes.EXPAND;
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to collapse a filter when it's expanded and expand it when it's collapsed
|
||||
*/
|
||||
export class FilterToggleAction extends SidebarFilterAction {
|
||||
type = SidebarFilterActionTypes.TOGGLE;
|
||||
}
|
||||
/* tslint:enable:max-classes-per-file */
|
24
src/app/shared/sidebar/filter/sidebar-filter.component.html
Normal file
24
src/app/shared/sidebar/filter/sidebar-filter.component.html
Normal file
@@ -0,0 +1,24 @@
|
||||
<div class="facet-filter d-block mb-3 p-3">
|
||||
<div (click)="toggle()" class="filter-name">
|
||||
<h5 class="d-inline-block mb-0">
|
||||
{{ label | translate }}
|
||||
</h5>
|
||||
<span class="filter-toggle fas float-right"
|
||||
[ngClass]="(collapsed$ | async) ? 'fa-plus' : 'fa-minus'">
|
||||
</span>
|
||||
</div>
|
||||
<div [@slide]="(collapsed$ | async) ? 'collapsed' : 'expanded'"
|
||||
(@slide.start)="startSlide($event)" (@slide.done)="finishSlide($event)"
|
||||
class="sidebar-filter-wrapper" [ngClass]="{'closed' : closed}">
|
||||
<div>
|
||||
<div class="filters py-2">
|
||||
<ds-sidebar-filter-selected-option
|
||||
*ngFor="let value of (selectedValues | async)"
|
||||
[label]="value"
|
||||
(click)="removeValue.emit(value)">
|
||||
</ds-sidebar-filter-selected-option>
|
||||
</div>
|
||||
<ng-content></ng-content>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
12
src/app/shared/sidebar/filter/sidebar-filter.component.scss
Normal file
12
src/app/shared/sidebar/filter/sidebar-filter.component.scss
Normal file
@@ -0,0 +1,12 @@
|
||||
:host .facet-filter {
|
||||
border: 1px solid map-get($theme-colors, light);
|
||||
cursor: pointer;
|
||||
|
||||
.sidebar-filter-wrapper.closed {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.filter-toggle {
|
||||
line-height: $line-height-base;
|
||||
}
|
||||
}
|
84
src/app/shared/sidebar/filter/sidebar-filter.component.ts
Normal file
84
src/app/shared/sidebar/filter/sidebar-filter.component.ts
Normal file
@@ -0,0 +1,84 @@
|
||||
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
|
||||
import { Observable } from 'rxjs';
|
||||
import { SidebarFilterService } from './sidebar-filter.service';
|
||||
import { slide } from '../../animations/slide';
|
||||
|
||||
@Component({
|
||||
selector: 'ds-sidebar-filter',
|
||||
styleUrls: ['./sidebar-filter.component.scss'],
|
||||
templateUrl: './sidebar-filter.component.html',
|
||||
animations: [slide],
|
||||
})
|
||||
export class SidebarFilterComponent implements OnInit {
|
||||
|
||||
@Input() name:string;
|
||||
@Input() type:string;
|
||||
@Input() label:string;
|
||||
@Input() expanded = true;
|
||||
@Input() selectedValues:Observable<string[]>;
|
||||
@Output() submitValue:EventEmitter<any> = new EventEmitter<any>();
|
||||
@Output() removeValue:EventEmitter<any> = new EventEmitter<any>();
|
||||
|
||||
/**
|
||||
* True when the filter is 100% collapsed in the UI
|
||||
*/
|
||||
closed = true;
|
||||
|
||||
/**
|
||||
* Emits true when the filter is currently collapsed in the store
|
||||
*/
|
||||
collapsed$:Observable<boolean>;
|
||||
|
||||
constructor(
|
||||
protected filterService:SidebarFilterService
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes the state for this filter to collapsed when it's expanded and to expanded it when it's collapsed
|
||||
*/
|
||||
toggle() {
|
||||
this.filterService.toggle(this.name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to change this.collapsed to false when the slide animation ends and is sliding open
|
||||
* @param event The animation event
|
||||
*/
|
||||
finishSlide(event:any):void {
|
||||
if (event.fromState === 'collapsed') {
|
||||
this.closed = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to change this.collapsed to true when the slide animation starts and is sliding closed
|
||||
* @param event The animation event
|
||||
*/
|
||||
startSlide(event:any):void {
|
||||
if (event.toState === 'collapsed') {
|
||||
this.closed = true;
|
||||
}
|
||||
}
|
||||
|
||||
ngOnInit():void {
|
||||
this.initializeFilter();
|
||||
this.collapsed$ = this.isCollapsed();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the initial state of the filter
|
||||
*/
|
||||
initializeFilter() {
|
||||
this.filterService.initializeFilter(this.name, this.expanded);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the filter is currently collapsed
|
||||
* @returns {Observable<boolean>} Emits true when the current state of the filter is collapsed, false when it's expanded
|
||||
*/
|
||||
private isCollapsed():Observable<boolean> {
|
||||
return this.filterService.isCollapsed(this.name);
|
||||
}
|
||||
|
||||
}
|
70
src/app/shared/sidebar/filter/sidebar-filter.reducer.ts
Normal file
70
src/app/shared/sidebar/filter/sidebar-filter.reducer.ts
Normal file
@@ -0,0 +1,70 @@
|
||||
import {
|
||||
FilterInitializeAction,
|
||||
SidebarFilterAction,
|
||||
SidebarFilterActionTypes
|
||||
} from './sidebar-filter.actions';
|
||||
|
||||
/**
|
||||
* Interface that represents the state for a single filters
|
||||
*/
|
||||
export interface SidebarFilterState {
|
||||
filterCollapsed:boolean,
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface that represents the state for all available filters
|
||||
*/
|
||||
export interface SidebarFiltersState {
|
||||
[name:string]:SidebarFilterState
|
||||
}
|
||||
|
||||
const initialState:SidebarFiltersState = Object.create(null);
|
||||
|
||||
/**
|
||||
* Performs a filter action on the current state
|
||||
* @param {SidebarFiltersState} state The state before the action is performed
|
||||
* @param {SidebarFilterAction} action The action that should be performed
|
||||
* @returns {SidebarFiltersState} The state after the action is performed
|
||||
*/
|
||||
export function sidebarFilterReducer(state = initialState, action:SidebarFilterAction):SidebarFiltersState {
|
||||
|
||||
switch (action.type) {
|
||||
|
||||
case SidebarFilterActionTypes.INITIALIZE: {
|
||||
const initAction = (action as FilterInitializeAction);
|
||||
return Object.assign({}, state, {
|
||||
[action.filterName]: {
|
||||
filterCollapsed: !initAction.initiallyExpanded,
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
case SidebarFilterActionTypes.COLLAPSE: {
|
||||
return Object.assign({}, state, {
|
||||
[action.filterName]: {
|
||||
filterCollapsed: true,
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
case SidebarFilterActionTypes.EXPAND: {
|
||||
return Object.assign({}, state, {
|
||||
[action.filterName]: {
|
||||
filterCollapsed: false,
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
case SidebarFilterActionTypes.TOGGLE: {
|
||||
return Object.assign({}, state, {
|
||||
[action.filterName]: {
|
||||
filterCollapsed: !state[action.filterName].filterCollapsed,
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
default: {
|
||||
return state;
|
||||
}
|
||||
}
|
||||
}
|
87
src/app/shared/sidebar/filter/sidebar-filter.service.ts
Normal file
87
src/app/shared/sidebar/filter/sidebar-filter.service.ts
Normal file
@@ -0,0 +1,87 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import {
|
||||
FilterCollapseAction,
|
||||
FilterExpandAction, FilterInitializeAction,
|
||||
FilterToggleAction
|
||||
} from './sidebar-filter.actions';
|
||||
import { createSelector, MemoizedSelector, select, Store } from '@ngrx/store';
|
||||
import { SidebarFiltersState, SidebarFilterState } from './sidebar-filter.reducer';
|
||||
import { Observable } from 'rxjs';
|
||||
import { distinctUntilChanged, map } from 'rxjs/operators';
|
||||
import { hasValue } from '../../empty.util';
|
||||
|
||||
@Injectable()
|
||||
export class SidebarFilterService {
|
||||
|
||||
constructor(private store:Store<SidebarFilterState>) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatches an initialize action to the store for a given filter
|
||||
* @param {string} filter The filter for which the action is dispatched
|
||||
* @param {boolean} expanded If the filter should be open from the start
|
||||
*/
|
||||
public initializeFilter(filter:string, expanded:boolean):void {
|
||||
this.store.dispatch(new FilterInitializeAction(filter, expanded));
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatches a collapse action to the store for a given filter
|
||||
* @param {string} filterName The filter for which the action is dispatched
|
||||
*/
|
||||
public collapse(filterName:string):void {
|
||||
this.store.dispatch(new FilterCollapseAction(filterName));
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatches an expand action to the store for a given filter
|
||||
* @param {string} filterName The filter for which the action is dispatched
|
||||
*/
|
||||
public expand(filterName:string):void {
|
||||
this.store.dispatch(new FilterExpandAction(filterName));
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatches a toggle action to the store for a given filter
|
||||
* @param {string} filterName The filter for which the action is dispatched
|
||||
*/
|
||||
public toggle(filterName:string):void {
|
||||
this.store.dispatch(new FilterToggleAction(filterName));
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the state of a given filter is currently collapsed or not
|
||||
* @param {string} filterName The filtername for which the collapsed state is checked
|
||||
* @returns {Observable<boolean>} Emits the current collapsed state of the given filter, if it's unavailable, return false
|
||||
*/
|
||||
isCollapsed(filterName:string):Observable<boolean> {
|
||||
return this.store.pipe(
|
||||
select(filterByNameSelector(filterName)),
|
||||
map((object:SidebarFilterState) => {
|
||||
if (object) {
|
||||
return object.filterCollapsed;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}),
|
||||
distinctUntilChanged()
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const filterStateSelector = (state:SidebarFiltersState) => state.sidebarFilter;
|
||||
|
||||
function filterByNameSelector(name:string):MemoizedSelector<SidebarFiltersState, SidebarFilterState> {
|
||||
return keySelector<SidebarFilterState>(name);
|
||||
}
|
||||
|
||||
export function keySelector<T>(key:string):MemoizedSelector<SidebarFiltersState, T> {
|
||||
return createSelector(filterStateSelector, (state:SidebarFilterState) => {
|
||||
if (hasValue(state)) {
|
||||
return state[key];
|
||||
} else {
|
||||
return undefined;
|
||||
}
|
||||
});
|
||||
}
|
@@ -2,8 +2,8 @@ import { By } from '@angular/platform-browser';
|
||||
import { of as observableOf } from 'rxjs';
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { PageWithSidebarComponent } from './page-with-sidebar.component';
|
||||
import { SidebarService } from './sidebar/sidebar.service';
|
||||
import { HostWindowService } from '../shared/host-window.service';
|
||||
import { SidebarService } from './sidebar.service';
|
||||
import { HostWindowService } from '../host-window.service';
|
||||
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
|
||||
|
||||
describe('PageWithSidebarComponent', () => {
|
||||
|
6
src/app/shared/sidebar/sidebar-dropdown.component.html
Normal file
6
src/app/shared/sidebar/sidebar-dropdown.component.html
Normal file
@@ -0,0 +1,6 @@
|
||||
<div class="setting-option mb-3 p-3">
|
||||
<h5><label for="{{id}}">{{label | translate}}</label></h5>
|
||||
<select id="{{id}}" class="form-control" (change)="change.emit($event)">
|
||||
<ng-content></ng-content>
|
||||
</select>
|
||||
</div>
|
3
src/app/shared/sidebar/sidebar-dropdown.component.scss
Normal file
3
src/app/shared/sidebar/sidebar-dropdown.component.scss
Normal file
@@ -0,0 +1,3 @@
|
||||
.setting-option {
|
||||
border: 1px solid map-get($theme-colors, light);
|
||||
}
|
12
src/app/shared/sidebar/sidebar-dropdown.component.ts
Normal file
12
src/app/shared/sidebar/sidebar-dropdown.component.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import { Component, EventEmitter, Input, Output } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'ds-sidebar-dropdown',
|
||||
styleUrls: ['./sidebar-dropdown.component.scss'],
|
||||
templateUrl: './sidebar-dropdown.component.html',
|
||||
})
|
||||
export class SidebarDropdownComponent {
|
||||
@Input() id:string;
|
||||
@Input() label:string;
|
||||
@Output() change:EventEmitter<any> = new EventEmitter<number>();
|
||||
}
|
@@ -12,7 +12,7 @@ const initialState: SidebarState = {
|
||||
};
|
||||
|
||||
/**
|
||||
* Performs a search sidebar action on the current state
|
||||
* Performs a sidebar action on the current state
|
||||
* @param {SidebarState} state The state before the action is performed
|
||||
* @param {SidebarAction} action The action that should be performed
|
||||
* @returns {SidebarState} The state after the action is performed
|
||||
|
@@ -7,7 +7,7 @@ import { AppState } from '../../app.reducer';
|
||||
import { HostWindowService } from '../host-window.service';
|
||||
import { map } from 'rxjs/operators';
|
||||
|
||||
const sidebarStateSelector = (state: AppState) => state.searchSidebar;
|
||||
const sidebarStateSelector = (state: AppState) => state.sidebar;
|
||||
const sidebarCollapsedSelector = createSelector(sidebarStateSelector, (sidebar: SidebarState) => sidebar.sidebarCollapsed);
|
||||
|
||||
/**
|
||||
|
Reference in New Issue
Block a user