search results sorting #804, better transitions between succesive searches

This commit is contained in:
2020-07-26 00:18:07 +02:00
parent 57e869bfd5
commit 3ed132147c
5 changed files with 200 additions and 45 deletions
+24 -3
View File
@@ -529,13 +529,13 @@ section[role="main-content"]{
}
footer[role="tools"]{
$list-item-h: 5em;
$list-item-h: 7em;
@mixin resultItem{
box-sizing: border-box;
// we are only on 10 colls as 2 are occupied by sides
flex-basis: percentage(2/($default_sum - 2));
height: $list-item-h;
max-height: $list-item-h;
overflow: hidden;
padding-bottom: 1em;
padding-right: $default_gap;
@@ -602,7 +602,7 @@ footer[role="tools"]{
z-index: 9;
background-color: $gris;
padding:1.2em $side-padding;
max-height: $list-item-h * 5;
max-height: $list-item-h * 3;
@include accordeon-transition($list-item-h * 3);
>header{
.search-keys{
@@ -636,6 +636,17 @@ footer[role="tools"]{
// TODO: how to center the loading
}
}
>header, section.results-list{
transition: opacity 0.2s ease-in-out;
}
&.loading {
>header, section.results-list{
transition: opacity 0.5s ease-in-out;
opacity:0.5;
pointer-events: none;
}
}
}
#footer-bottom{
z-index: 10;
@@ -740,6 +751,16 @@ footer[role="tools"]{
padding-top:0.2em;
}
}
form{
transition: opacity 0.2s ease-in-out;
}
&.loading{
form{
opacity:0.5;
transition: opacity 0.5s ease-in-out;
pointer-events: none;
}
}
}
}
h2{
+11 -11
View File
@@ -35,9 +35,9 @@ export default {
required: true
}
},
data: () => ({
preview: ''
}),
// data: () => ({
// preview: ''
// }),
computed: {
...mapState({
editionsbyuuid: state => state.Corpus.editionsbyuuid
@@ -46,14 +46,14 @@ export default {
return this.editionsbyuuid[this.result.textId].title
}
},
created () {
if (this.result.extract) {
const subString = this.result.extract.substr(0, 80)
this.preview = subString.substr(0, subString.lastIndexOf(' ')) + ' …'
} else {
console.warn(`No extract for ${this.result.textId}/${this.result.uuid}`)
}
},
// created () {
// if (this.result.extract) {
// const subString = this.result.extract.substr(0, 80)
// this.preview = subString.substr(0, subString.lastIndexOf(' ')) + ' …'
// } else {
// console.warn(`No extract for ${this.result.textId}/${this.result.uuid}`)
// }
// },
methods: {
...mapActions({
addHistoryItem: 'History/addItem'
+73 -4
View File
@@ -4,11 +4,22 @@
v-if="opened"
id="results"
class="row"
:class="{ loading: isloading }"
>
<header class="col-1">
<h2>Resultats</h2>
<span class="search-keys">{{ keys }}</span><br>
<span v-if="resultsQuantity" class="results-count">{{ resultsCount }}</span>
<v-select
id="sorting"
type="select"
placeholder="trier par"
append-to-body
:calculate-position="dropDownMenuPos"
:options="sortOptions"
:value="sorting"
@input="onSortingSelected"
/>
</header>
<section class="col-10 results-list">
<div class="wrapper">
@@ -19,6 +30,7 @@
<infinite-loading
v-if="offset < resultsQuantity.quantity"
@infinite="nextResultsBatch"
:identifier="isloading"
/>
</ul>
</div>
@@ -37,24 +49,31 @@
<script>
import { createPopper } from '@popperjs/core'
import ResultItem from '../Content/ResultItem'
import { mapState, mapActions } from 'vuex'
import { mapState, mapActions, mapMutations } from 'vuex'
export default {
name: 'Results',
components: {
ResultItem
},
// data: () => ({
// }),
computed: {
opened: {
get () { return this.$store.state.Search.opened },
set (value) { this.$store.commit('Search/setOpened', value) }
},
...mapState({
isloading: state => state.Search.isloading,
keys: state => state.Search.keys,
results: state => state.Search.results,
resultsQuantity: state => state.Search.resultsQuantity,
offset: state => state.Search.offset
offset: state => state.Search.offset,
sortOptions: state => state.Search.sortOptions,
sorting: state => state.Search.sorting
}),
resultsCount () {
return `${this.$store.state.Search.resultsQuantity.quantity} ${this.$store.state.Search.resultsQuantity.unit}`
@@ -65,9 +84,59 @@ export default {
console.log('clicked on close results')
this.opened = false
},
...mapMutations({
setSorting: 'Search/setSorting'
}),
...mapActions({
nextResultsBatch: 'Search/nextResultsBatch'
})
nextResultsBatch: 'Search/nextResultsBatch',
updateSearch: 'Search/updateSearch'
}),
dropDownMenuPos (dropdownList, component, { width }) {
/**
* We need to explicitly define the dropdown width since
* it is usually inherited from the parent with CSS.
*/
dropdownList.style.width = width
/**
* Here we position the dropdownList relative to the $refs.toggle Element.
*
* The 'offset' modifier aligns the dropdown so that the $refs.toggle and
* the dropdownList overlap by 1 pixel.
*
* The 'toggleClass' modifier adds a 'drop-up' class to the Vue Select
* wrapper so that we can set some styles for when the dropdown is placed
* above.
*/
const popper = createPopper(component.$refs.toggle, dropdownList, {
placement: 'top',
modifiers: [
{
name: 'offset',
options: {
offset: [0, -1]
}
},
{
name: 'toggleClass',
enabled: true,
phase: 'write',
fn ({ state }) {
component.$el.classList.toggle('drop-up', state.placement === 'top')
}
}]
})
/**
* To prevent memory leaks Popper needs to be destroyed.
* If you return function, it will be called just before dropdown is removed from DOM.
*/
return () => popper.destroy()
},
onSortingSelected (o) {
this.setSorting(o)
this.updateSearch()
}
}
}
</script>
+13 -7
View File
@@ -1,6 +1,6 @@
<template>
<div id="search" class="col-11">
<form v-if="corpusLoaded" class="search-form row">
<div id="search" class="col-11" :class="{ loading: isloading }">
<form class="search-form row">
<fieldset class="search">
<div>
<label for="keys">Search</label>
@@ -84,7 +84,7 @@
import { createPopper } from '@popperjs/core'
import { mapActions, mapState } from 'vuex'
import { mapActions, mapMutations, mapState } from 'vuex'
export default {
name: 'Search',
@@ -114,10 +114,13 @@ export default {
}
},
methods: {
...mapMutations({
setActiveFilters: 'Search/setActiveFilters'
}),
...mapActions({
newSearch: 'Search/newSearch',
setSearchTypeValue: 'Search/setSearchTypeValue',
setSearchActiveFilters: 'Search/setSearchActiveFilters'
updateSearch: 'Search/updateSearch'
}),
submit () {
console.log('submited', this.keys)
@@ -171,15 +174,18 @@ export default {
},
onFiltersNominumSelected (e) {
console.log('onFiltersNominumSelected', e)
this.setSearchActiveFilters({ index: 'persons', value: e })
this.setActiveFilters({ index: 'persons', value: e })
this.updateSearch()
},
onFiltersLocorumSelected (e) {
console.log('onFiltersLocorumSelected', e)
this.setSearchActiveFilters({ index: 'places', value: e })
this.setActiveFilters({ index: 'places', value: e })
this.updateSearch()
},
onFiltersOperumSelected (e) {
console.log('onFiltersOperumSelected', e)
this.setSearchActiveFilters({ index: 'objects', value: e })
this.setActiveFilters({ index: 'objects', value: e })
this.updateSearch()
}
}
}
+79 -20
View File
@@ -1,8 +1,15 @@
// import axios from 'axios'
import { REST } from 'api/rest-axios'
import qs from 'querystring'
// const _CancelToken = axios.CancelToken
// const _cancelTokenSource = _CancelToken.source()
// let _cancel
export default {
namespaced: true,
testconst: 'Hello',
// initial state
state: {
@@ -16,9 +23,18 @@ export default {
searchTypeValue: { 'code': 'text', 'label': 'Dans les textes' },
filters: { persons: [], places: [], objects: [] },
activeFilters: { persons: [], places: [], objects: [] },
sortOptions: [
{ code: 'date', label: 'date' },
{ code: 'score', label: 'pertinence' },
{ code: 'size', label: 'nombre de mots' }
],
sorting: null,
results: [],
resultsQuantity: null,
isloading: false,
// infiniteLoadingIsLoading: false,
// infiniteLoadingCancelToken: null,
// infiniteLoadingCancelTokenSource: null,
limit: 10,
offset: 0,
opened: false
@@ -93,6 +109,10 @@ export default {
for (var index of ['persons', 'places', 'objects']) {
state.activeFilters[index] = []
}
},
setSorting (state, sort) {
console.log('setSorting', sort)
state.sorting = sort
}
},
@@ -101,7 +121,13 @@ export default {
getResults ({ dispatch, commit, state }, $infiniteLoadingState = null) {
console.log('getResults', state.keys, $infiniteLoadingState)
// reset results on new search
commit('setIsloading', true)
if (!$infiniteLoadingState) {
commit('setIsloading', true)
}
// else {
// state.infiniteLoadingIsLoading = true
// }
let params = {
search: `${state.keys}`,
start: state.offset,
@@ -121,42 +147,75 @@ export default {
}
}
// params.filterPersons = ['nomLouisXIII', 'nomChampagnePhilippeDe']
if (state.sorting) {
params.sort = state.sorting.code
}
// console.log('Search getResults params', params);
let q = qs.stringify(params)
return REST.get(`${window.apipath}/search?` + q)
let ops = {}
// if ($infiniteLoadingState) {
// ops.cancelToken = new _CancelToken(function executor (c) {
// _cancel = c
// })
// }
return REST.get(`${window.apipath}/search?` + q, ops)
.then(({ data }) => {
console.log('search REST: data', data.meta.quantity.quantity, state.offset + state.limit, data)
commit('setIsloading', false)
commit('setOpened', true)
commit('setResults', data.content)
commit('setResultsCount', data.meta.quantity)
commit('setFilters', data.meta.filters)
if ($infiniteLoadingState) {
if (state.offset + state.limit > data.meta.quantity.quantity) {
console.log('Search infinite completed')
// tell to vue-infinite-loading plugin that there si no new page
if (state.isLoading) {
// we are in a new search or an update so we dont apply the infinite loading received results
$infiniteLoadingState.complete()
} else {
console.log('Search infinite loaded')
// tell to vue-infinite-loading plugin that newpage is loaded
$infiniteLoadingState.loaded()
commit('setResults', data.content)
if (state.offset + state.limit > data.meta.quantity.quantity) {
console.log('Search infinite completed')
// tell to vue-infinite-loading plugin that there si no new page
$infiniteLoadingState.complete()
} else {
console.log('Search infinite loaded')
// tell to vue-infinite-loading plugin that newpage is loaded
$infiniteLoadingState.loaded()
}
// state.infiniteLoadingIsLoading = false
}
} else {
commit('resetResults')
commit('setIsloading', false)
commit('setOpened', true)
commit('setResults', data.content)
}
})
.catch((error) => {
console.warn('Issue with search', error)
commit('setIsloading', false)
$infiniteLoadingState.error()
// if (axios.isCancel(error)) {
// console.log('Request canceled', error.message)
// if ($infiniteLoadingState) {
// $infiniteLoadingState.complete()
// }
// } else {
Promise.reject(error)
if ($infiniteLoadingState) {
$infiniteLoadingState.error()
}
// }
})
},
newSearch ({ dispatch, commit, state }) {
commit('resetResults')
// commit('resetResults')
commit('resetActiveFilters')
// if (_cancel) {
// _cancel('new search fired')
// }
dispatch('getResults')
},
filteredSearch ({ dispatch, commit, state }) {
commit('resetResults')
updateSearch ({ dispatch, commit, state }) {
// TODO: wait for new results came to reset results list
// TODO: indicate loading state
// commit('resetResults')
dispatch('getResults')
},
nextResultsBatch ({ dispatch, commit, state }, $infiniteLoadingState) {
@@ -170,11 +229,11 @@ export default {
},
setSearchTypeValue ({ dispatch, commit, state }, value) {
commit('setSearchTypeValue', value)
},
setSearchActiveFilters ({ dispatch, commit, state }, filters) {
// console.log('setSearchFiltersValue', filters)
commit('setActiveFilters', filters)
dispatch('filteredSearch')
}
// setSearchActiveFilters ({ dispatch, commit, state }, filters) {
// // console.log('setSearchFiltersValue', filters)
// commit('setActiveFilters', filters)
// dispatch('updateSearch')
// }
}
}