diff --git a/resources/i18n/en.json b/resources/i18n/en.json index 832fbb95aa..f835699087 100644 --- a/resources/i18n/en.json +++ b/resources/i18n/en.json @@ -94,9 +94,9 @@ "filters": { "head": "Filters", "reset": "Reset filters", - "facet-filter": { + "filter": { "show-more": "Show more", - "show-less": "Show less", + "show-less": "Collapse", "author": { "placeholder": "Author name", "head": "Author" @@ -108,6 +108,10 @@ "subject": { "placeholder": "Subject", "head": "Subject" + }, + "date": { + "placeholder": "Date", + "head": "Date" } } } diff --git a/src/app/+search-page/search-filters/search-filter/search-facet-filter/search-facet-filter.component.html b/src/app/+search-page/search-filters/search-filter/search-facet-filter/search-facet-filter.component.html index e253e65048..436f497bf5 100644 --- a/src/app/+search-page/search-filters/search-filter/search-facet-filter/search-facet-filter.component.html +++ b/src/app/+search-page/search-filters/search-filter/search-facet-filter/search-facet-filter.component.html @@ -1,15 +1,32 @@ - - - - {{value.value}} - ({{value.count}}) - - -{{"search.filters.facet-filter.show-more" - | translate}} -{{"search.filters.facet-filter.show-less" | - translate}} - - \ No newline at end of file +
+
+ + + {{value}} + + + + + {{value.value}} + ({{value.count}}) + + + +
+
+ + +
+
\ No newline at end of file diff --git a/src/app/+search-page/search-filters/search-filter/search-facet-filter/search-facet-filter.component.scss b/src/app/+search-page/search-filters/search-filter/search-facet-filter/search-facet-filter.component.scss index 9ad844019b..595b2aefb8 100644 --- a/src/app/+search-page/search-filters/search-filter/search-facet-filter/search-facet-filter.component.scss +++ b/src/app/+search-page/search-filters/search-filter/search-facet-filter/search-facet-filter.component.scss @@ -1,2 +1,18 @@ @import '../../../../../styles/variables.scss'; -@import '../../../../../styles/mixins.scss'; \ No newline at end of file +@import '../../../../../styles/mixins.scss'; + +.filters { + margin-top: $spacer/2; + margin-bottom: $spacer/2; + a { + color: $body-color; + &:hover { + text-decoration: none; + } + } + .toggle-more-filters a { + color: $link-color; + text-decoration: underline; + cursor: pointer; + } +} \ No newline at end of file diff --git a/src/app/+search-page/search-filters/search-filter/search-facet-filter/search-facet-filter.component.ts b/src/app/+search-page/search-filters/search-filter/search-facet-filter/search-facet-filter.component.ts index 0c9eb06baa..3b3b7312e8 100644 --- a/src/app/+search-page/search-filters/search-filter/search-facet-filter/search-facet-filter.component.ts +++ b/src/app/+search-page/search-filters/search-filter/search-facet-filter/search-facet-filter.component.ts @@ -1,10 +1,10 @@ import { Component, Input, OnInit } from '@angular/core'; import { FacetValue } from '../../../search-service/facet-value.model'; import { SearchFilterConfig } from '../../../search-service/search-filter-config.model'; -import { SearchService } from '../../../search-service/search.service'; -import { Params } from '@angular/router'; +import { Params, Router } from '@angular/router'; import { Observable } from 'rxjs/Observable'; import { SearchFilterService } from '../search-filter.service'; +import { isNotEmpty } from '../../../../shared/empty.util'; /** * This component renders a simple item page. @@ -21,9 +21,11 @@ import { SearchFilterService } from '../search-filter.service'; export class SidebarFacetFilterComponent implements OnInit { @Input() filterValues: FacetValue[]; @Input() filterConfig: SearchFilterConfig; + @Input() selectedValues: string[]; currentPage: Observable; + filter: string; - constructor(private filterService: SearchFilterService) { + constructor(private filterService: SearchFilterService, private router: Router) { } ngOnInit(): void { @@ -38,8 +40,12 @@ export class SidebarFacetFilterComponent implements OnInit { return this.filterService.searchLink; } - getQueryParams(value: FacetValue): Observable { - return this.filterService.getFilterValueURL(this.filterConfig, value.value); + getQueryParamsWith(value: string): Observable { + return this.filterService.getQueryParamsWith(this.filterConfig, value); + } + + getQueryParamsWithout(value: string): Observable { + return this.filterService.getQueryParamsWithout(this.filterConfig, value); } get facetCount(): Observable { @@ -60,6 +66,16 @@ export class SidebarFacetFilterComponent implements OnInit { getCurrentPage(): Observable { return this.filterService.getPage(this.filterConfig.name); + } + onSubmit(data: any) { + if (isNotEmpty(data.filter)) { + this.getQueryParamsWith(data.filter).first().subscribe((a) => { + this.router.navigate([this.getSearchLink()], { queryParams: a } + ); + } + ); + this.filter = ''; + } } } diff --git a/src/app/+search-page/search-filters/search-filter/search-filter.component.html b/src/app/+search-page/search-filters/search-filter/search-filter.component.html index c78ea9f676..f5acb42b6d 100644 --- a/src/app/+search-page/search-filters/search-filter/search-filter.component.html +++ b/src/app/+search-page/search-filters/search-filter/search-filter.component.html @@ -1,8 +1,8 @@
-
{{filter.name}}
{{'search.filters.filter.' + filter.name + '.head'| translate}}
+ [filterValues]="(filterValues | async)?.payload" [selectedValues]="getSelectedValues() | async">
\ No newline at end of file diff --git a/src/app/+search-page/search-filters/search-filter/search-filter.component.scss b/src/app/+search-page/search-filters/search-filter/search-filter.component.scss index 93be5d1c05..f694e9e167 100644 --- a/src/app/+search-page/search-filters/search-filter/search-filter.component.scss +++ b/src/app/+search-page/search-filters/search-filter/search-filter.component.scss @@ -1,6 +1,12 @@ @import '../../../../styles/variables.scss'; @import '../../../../styles/mixins.scss'; -.search-filter-wrapper { - overflow: hidden; +:host { + border: 1px solid map-get($theme-colors, light); + .search-filter-wrapper { + overflow: hidden; + } + .filter-toggle { + line-height: $line-height-base; + } } \ No newline at end of file diff --git a/src/app/+search-page/search-filters/search-filter/search-filter.component.ts b/src/app/+search-page/search-filters/search-filter/search-filter.component.ts index 211315e397..e3f86c124c 100644 --- a/src/app/+search-page/search-filters/search-filter/search-filter.component.ts +++ b/src/app/+search-page/search-filters/search-filter/search-filter.component.ts @@ -6,6 +6,7 @@ import { FacetValue } from '../../search-service/facet-value.model'; import { SearchFilterService } from './search-filter.service'; import { Observable } from 'rxjs/Observable'; import { slide } from '../../../shared/animations/slide'; +import { RouteService } from '../../../shared/route.service'; /** * This component renders a simple item page. @@ -17,7 +18,7 @@ import { slide } from '../../../shared/animations/slide'; selector: 'ds-search-filter', styleUrls: ['./search-filter.component.scss'], templateUrl: './search-filter.component.html', - animations: [slide] + animations: [slide], }) export class SidebarFilterComponent implements OnInit { @@ -51,4 +52,8 @@ export class SidebarFilterComponent implements OnInit { initialExpand() { this.filterService.initialExpand(this.filter.name); } + + getSelectedValues(): Observable { + return this.filterService.getSelectedValuesForFilter(this.filter); + } } diff --git a/src/app/+search-page/search-filters/search-filter/search-filter.service.ts b/src/app/+search-page/search-filters/search-filter/search-filter.service.ts index 492eabdcf3..1a154e2808 100644 --- a/src/app/+search-page/search-filters/search-filter/search-filter.service.ts +++ b/src/app/+search-page/search-filters/search-filter/search-filter.service.ts @@ -11,7 +11,6 @@ import { SearchFilterToggleAction } from './search-filter.actions'; import { hasValue, } from '../../../shared/empty.util'; -import { Params } from '@angular/router'; import { SearchFilterConfig } from '../../search-service/search-filter-config.model'; import { SearchService } from '../../search-service/search.service'; import { RouteService } from '../../../shared/route.service'; @@ -30,14 +29,16 @@ export class SearchFilterService { return this.routeService.hasQueryParamWithValue(paramName, filterValue); } - getFilterValueURL(filterConfig: SearchFilterConfig, value: string): Observable { - return this.isFilterActive(filterConfig.paramName, value).flatMap((isActive) => { - if (isActive) { - return this.routeService.removeQueryParameterValue(filterConfig.paramName, value); - } else { - return this.routeService.addQueryParameterValue(filterConfig.paramName, value); - } - }) + getQueryParamsWithout(filterConfig: SearchFilterConfig, value: string) { + return this.routeService.removeQueryParameterValue(filterConfig.paramName, value); + } + + getQueryParamsWith(filterConfig: SearchFilterConfig, value: string) { + return this.routeService.addQueryParameterValue(filterConfig.paramName, value); + } + + getSelectedValuesForFilter(filterConfig: SearchFilterConfig): Observable { + return this.routeService.getQueryParameterValues(filterConfig.paramName); } get searchLink() { diff --git a/src/app/+search-page/search-filters/search-filters.component.html b/src/app/+search-page/search-filters/search-filters.component.html index 9d08545923..5bd42928a1 100644 --- a/src/app/+search-page/search-filters/search-filters.component.html +++ b/src/app/+search-page/search-filters/search-filters.component.html @@ -1,7 +1,7 @@

{{"search.filters.head" | translate}}

- +
{{"search.filters.reset" | translate}} \ No newline at end of file diff --git a/src/app/+search-page/search-filters/search-filters.component.ts b/src/app/+search-page/search-filters/search-filters.component.ts index 8380df7e05..c06c35b372 100644 --- a/src/app/+search-page/search-filters/search-filters.component.ts +++ b/src/app/+search-page/search-filters/search-filters.component.ts @@ -1,4 +1,4 @@ -import { Component, Input } from '@angular/core'; +import { Component } from '@angular/core'; import { SearchService } from '../search-service/search.service'; import { RemoteData } from '../../core/data/remote-data'; import { SearchFilterConfig } from '../search-service/search-filter-config.model'; diff --git a/src/app/+search-page/search-page.component.html b/src/app/+search-page/search-page.component.html index fe08336579..e8f2fe5487 100644 --- a/src/app/+search-page/search-page.component.html +++ b/src/app/+search-page/search-page.component.html @@ -2,7 +2,7 @@
+ [resultCount]="(resultsRDObs | async)?.pageInfo?.totalElements">
@@ -37,3 +37,4 @@
+ diff --git a/src/app/+search-page/search-page.component.ts b/src/app/+search-page/search-page.component.ts index 4a7d6322b9..1d3b80e4e7 100644 --- a/src/app/+search-page/search-page.component.ts +++ b/src/app/+search-page/search-page.component.ts @@ -90,7 +90,6 @@ export class SearchPageComponent implements OnInit, OnDestroy { } private updateSearchResults(searchOptions) { - // Resolve search results this.resultsRDObs = this.service.search(this.query, this.scope, searchOptions); } diff --git a/src/app/+search-page/search-service/search-filter-config.model.ts b/src/app/+search-page/search-service/search-filter-config.model.ts index 476dd8438e..fd3223c7bc 100644 --- a/src/app/+search-page/search-service/search-filter-config.model.ts +++ b/src/app/+search-page/search-service/search-filter-config.model.ts @@ -5,7 +5,7 @@ export class SearchFilterConfig { name: string; type: FilterType; hasFacets: boolean; - pageSize = 3; + pageSize = 2; isOpenByDefault: boolean; /** * Name of this configuration that can be used in a url diff --git a/src/app/+search-page/search-service/search.service.ts b/src/app/+search-page/search-service/search.service.ts index 2d34b3a98f..20b64b2528 100644 --- a/src/app/+search-page/search-service/search.service.ts +++ b/src/app/+search-page/search-service/search.service.ts @@ -15,6 +15,7 @@ import { FilterType } from './filter-type.model'; import { FacetValue } from './facet-value.model'; import { ViewMode } from '../../+search-page/search-options.model'; import { Router, NavigationExtras, ActivatedRoute, Params } from '@angular/router'; +import { RouteService } from '../../shared/route.service'; function shuffle(array: any[]) { let i = 0; @@ -81,6 +82,7 @@ export class SearchService implements OnDestroy { ]; constructor(private itemDataService: ItemDataService, + private routeService: RouteService, private route: ActivatedRoute, private router: Router) { @@ -126,31 +128,31 @@ export class SearchService implements OnDestroy { .filter((rd: RemoteData) => rd.hasSucceeded) .map((rd: RemoteData) => { - const totalElements = rd.pageInfo.totalElements > 20 ? 20 : rd.pageInfo.totalElements; - const pageInfo = Object.assign({}, rd.pageInfo, { totalElements: totalElements }); + const totalElements = rd.pageInfo.totalElements > 20 ? 20 : rd.pageInfo.totalElements; + const pageInfo = Object.assign({}, rd.pageInfo, { totalElements: totalElements }); - const payload = shuffle(rd.payload) - .map((item: Item, index: number) => { - const mockResult: SearchResult = new ItemSearchResult(); - mockResult.dspaceObject = item; - const highlight = new Metadatum(); - highlight.key = 'dc.description.abstract'; - highlight.value = this.mockedHighlights[index % this.mockedHighlights.length]; - mockResult.hitHighlights = new Array(highlight); - return mockResult; - }); + const payload = shuffle(rd.payload) + .map((item: Item, index: number) => { + const mockResult: SearchResult = new ItemSearchResult(); + mockResult.dspaceObject = item; + const highlight = new Metadatum(); + highlight.key = 'dc.description.abstract'; + highlight.value = this.mockedHighlights[index % this.mockedHighlights.length]; + mockResult.hitHighlights = new Array(highlight); + return mockResult; + }); - return new RemoteData( - self, - rd.isRequestPending, - rd.isResponsePending, - rd.hasSucceeded, - errorMessage, - statusCode, - pageInfo, - payload - ) - }).startWith(new RemoteData( + return new RemoteData( + self, + rd.isRequestPending, + rd.isResponsePending, + rd.hasSucceeded, + errorMessage, + statusCode, + pageInfo, + payload + ) + }).startWith(new RemoteData( '', true, false, @@ -182,39 +184,43 @@ export class SearchService implements OnDestroy { } getFacetValuesFor(searchFilterConfigName: string): Observable> { - const filterConfig = this.config.find((config: SearchFilterConfig) => config.name === searchFilterConfigName); - const values: FacetValue[] = []; - for (let i = 0; i < 5; i++) { - const value = searchFilterConfigName + ' ' + (i + 1); - values.push({ - value: value, - count: Math.floor(Math.random() * 20) + 20 * (5 - i), // make sure first results have the highest (random) count - search: decodeURI(this.router.url) + (this.router.url.includes('?') ? '&' : '?') + filterConfig.paramName + '=' + value - }); - } - const requestPending = false; - const responsePending = false; - const isSuccessful = true; - const errorMessage = undefined; - const statusCode = '200'; - const returningPageInfo = new PageInfo(); - return Observable.of(new RemoteData( - 'https://dspace7.4science.it/dspace-spring-rest/api/search', - requestPending, - responsePending, - isSuccessful, - errorMessage, - statusCode, - returningPageInfo, - values - )); + return this.routeService.getQueryParameterValues(filterConfig.paramName).map((selectedValues: string[]) => { + const values: FacetValue[] = []; + for (let i = 0; i < 5; i++) { + const value = searchFilterConfigName + ' ' + (i + 1); + if (!selectedValues.includes(value)) { + values.push({ + value: value, + count: Math.floor(Math.random() * 20) + 20 * (5 - i), // make sure first results have the highest (random) count + search: decodeURI(this.router.url) + (this.router.url.includes('?') ? '&' : '?') + filterConfig.paramName + '=' + value + }); + } + } + const requestPending = false; + const responsePending = false; + const isSuccessful = true; + const errorMessage = undefined; + const statusCode = '200'; + const returningPageInfo = new PageInfo(); + return new RemoteData( + 'https://dspace7.4science.it/dspace-spring-rest/api/search', + requestPending, + responsePending, + isSuccessful, + errorMessage, + statusCode, + returningPageInfo, + values + ) + } + ) } getViewMode(): Observable { - return this.route.queryParams.map((params) => { - if (isNotEmpty(params.view) && hasValue(params.view)) { - return params.view; + return this.routeService.getQueryParameterValue('view').map((value) => { + if (hasValue(value)) { + return value as ViewMode; } else { return ViewMode.List; } diff --git a/src/app/+search-page/search-sidebar/search-sidebar.component.scss b/src/app/+search-page/search-sidebar/search-sidebar.component.scss index c8c0a60225..2b318454ed 100644 --- a/src/app/+search-page/search-sidebar/search-sidebar.component.scss +++ b/src/app/+search-page/search-sidebar/search-sidebar.component.scss @@ -5,4 +5,7 @@ .results { line-height: $button-height; } + ds-view-mode-switch { + margin-bottom: $spacer; + } } diff --git a/src/app/core/core.module.ts b/src/app/core/core.module.ts index bfd5cebcb6..39570f9aed 100644 --- a/src/app/core/core.module.ts +++ b/src/app/core/core.module.ts @@ -32,6 +32,7 @@ import { ServerResponseService } from '../shared/server-response.service'; import { NativeWindowFactory, NativeWindowService } from '../shared/window.service'; import { BrowseService } from './browse/browse.service'; import { BrowseResponseParsingService } from './data/browse-response-parsing.service'; +import { RouteService } from '../shared/route.service'; const IMPORTS = [ CommonModule, @@ -65,6 +66,7 @@ const PROVIDERS = [ ServerResponseService, BrowseResponseParsingService, BrowseService, + RouteService, { provide: NativeWindowService, useFactory: NativeWindowFactory } ]; diff --git a/src/app/shared/animations/slide.ts b/src/app/shared/animations/slide.ts index 381f592168..fa4a451863 100644 --- a/src/app/shared/animations/slide.ts +++ b/src/app/shared/animations/slide.ts @@ -1,4 +1,4 @@ -import { animate, state, transition, trigger, style } from '@angular/animations'; +import { animate, state, transition, trigger, style, stagger, query } from '@angular/animations'; export const slide = trigger('slide', [ @@ -6,5 +6,5 @@ export const slide = trigger('slide', [ state('collapsed', style({ height: 0 })), - transition('expanded <=> collapsed', animate(250)), + transition('expanded <=> collapsed', animate(250)) ]); diff --git a/src/app/shared/route.service.ts b/src/app/shared/route.service.ts index 5a34e40779..b11da41211 100644 --- a/src/app/shared/route.service.ts +++ b/src/app/shared/route.service.ts @@ -1,4 +1,4 @@ -import { Injectable, OnDestroy } from '@angular/core'; +import { Injectable } from '@angular/core'; import { Observable } from 'rxjs/Observable'; import { ActivatedRoute, convertToParamMap, Params, } from '@angular/router'; import { isNotEmpty } from './empty.util'; @@ -9,6 +9,14 @@ export class RouteService { constructor(private route: ActivatedRoute) { } + getQueryParameterValues(paramName: string): Observable { + return this.route.queryParamMap.map((map) => map.getAll(paramName)); + } + + getQueryParameterValue(paramName: string): Observable { + return this.route.queryParamMap.map((map) => map.get(paramName)); + } + hasQueryParam(paramName: string): Observable { return this.route.queryParamMap.map((map) => {return map.has(paramName);}); }