- search paginate

- typescript
- tsconfig.json and typings.d.ts
This commit is contained in:
Arno Kaimbacher 2019-10-18 17:05:56 +02:00
parent 78a88081c2
commit b7abdd83e2
31 changed files with 710 additions and 139 deletions

View file

@ -1,8 +1,9 @@
<template>
<template v-if="loaded">
<div class="search-container row">
<div class="four columns left-bar">
<div id="left-bar" class="sidebar left-bar">
<h2 class="indexheader">DataXplore</h2>
<div class="sidebar-image"></div>
<!-- <h2 class="indexheader">DataXplore</h2> -->
<!-- <div class="card" v-for="item in facets.language" :key="item.id">
<span>{{ item }}</span>
@ -10,7 +11,7 @@
<!-- <facet-list v-bind:data="facets"></facet-list> -->
<!-- <div class="card" v-for="(item, index) in facets" :key="index"> -->
<div class="card" v-for="(values, key, index) in facets" :key="index">
<div class="card" v-for="(values, key, index) in facets" :key="index">
<facet-list :data="values" :filterName="key" @filter="onFilter"></facet-list>
</div>
</div>
@ -18,7 +19,6 @@
<div class="eight columns right-bar">
<div id="right-bar" class="sidebar right-bar">
<!-- Search input section -->
<div class="row">
<div class="twelve columns">
@ -26,17 +26,31 @@
</div>
</div>
<div class="row">
<div v-if="results.length > 0" class="result-list-info">
<div class="resultheader">
Your search yielded
<strong>{{ numFound }}</strong> results:
</div>
</div>
<div class="row">
<div class="active-filter-items twelve columns">
<a class="filter-link" v-for="(value, key, index) in activeFilterItems" :key="index">
<span>{{ key + ": " }}</span>
<span v-if="value && value.length > 0">{{ value.join(', ') }}</span>
<span v-if="value && value.length > 0">{{ value.join(', ') }}</span>
</a>
</div>
</div>
<!-- Results section -->
<vs-results v-bind:data="results"></vs-results>
<!-- pagination -->
<div class="row">
<div class="twelve columns">
<vs-pagination v-bind:data="pagination" @paginate="onPaginate"></vs-pagination>
</div>
</div>
</div>
</div>
</div>
@ -56,9 +70,9 @@
</div>-->
</template>
<script lang="js">
import App from './app-class.js';
export default App;
<script lang="ts">
import App from "./app-class";
export default App;
</script>
<style lang="scss">

View file

@ -1,7 +1,8 @@
import { Component, Vue, Prop, Provide } from 'vue-property-decorator';
import VsInput from './text-search/vs-input.vue';
import VsResults from './search-results/vs-results.vue';
import FacetList from './search-results/facet-list.vue'
import FacetList from './search-results/facet-list.vue';
import VsPagination from './search-results/vs-pagination.vue';
import rdrApi from './search-results/dataservice';
import FilterItem from './models/filter-item';
@ -9,30 +10,52 @@ import FilterItem from './models/filter-item';
components: {
VsInput,
VsResults,
FacetList
FacetList,
VsPagination
}
})
export default class App extends Vue {
results = [];
facets = {};
searchTerm = '';
activeFilterItems = {};
results: Array<any> = [];
facets: Object = {};
searchTerm: string = '';
activeFilterItems: Object = {};
pagination: Object = {
total: 0,
per_page: 2,
current_page: 0,
// last_page: 0,
data: []
};
loaded = false;
numFound: number;
async onFilter(filter) {
async onPaginate(start: number): Promise<void> {
console.log(start);
var res = await rdrApi.search(this.searchTerm, this.activeFilterItems, start.toString());
this.results = res.response.docs;
}
async onFilter(filter): Promise<void> {
// console.log(filter.value);
// if (!this.activeFilterItems.some(e => e.value === filter.value)) {
// this.activeFilterItems.push(filter);
if (!this.activeFilterItems.hasOwnProperty(filter.Category)) {
this.activeFilterItems[filter.Category] = [];
// this.activeFilterItems.push(filter);
if (!this.activeFilterItems.hasOwnProperty(filter.Category)) {
this.activeFilterItems[filter.Category] = [];
}
if (!this.activeFilterItems[filter.Category].some(e => e === filter.value)) {
this.activeFilterItems[filter.Category].push(filter.value);
var res = await rdrApi.search(this.searchTerm, this.activeFilterItems);
this.results = res.response.docs;
// this.facets = res.facet_counts.facet_fields;
// this.facets = [];
this.numFound = res.response.numFound;
// pagination
this.pagination['total'] = res.response.numFound;
this.pagination['per_page'] = res.responseHeader.params.rows;
this.pagination['current_page'] = 1;
this.pagination['data'] = res.response.docs;
var facet_fields = res.facet_counts.facet_fields;
for (var prop in facet_fields) {
var facetValues = facet_fields[prop].map((facetValue, i) => {
@ -41,12 +64,12 @@ export default class App extends Vue {
var rObj;
if (filter.value == facetValue) {
rObj = filter;
} else if( this.facets[prop].some(e => e.value === facetValue)) {
} else if (this.facets[prop].some(e => e.value === facetValue)) {
console.log(facetValue + " is included")
var indexOfFacetValue = this.facets[prop].findIndex(i => i.value === facetValue);
var indexOfFacetValue = this.facets[prop].findIndex(i => i.value === facetValue);
console.log(indexOfFacetValue);
rObj = this.facets[prop][indexOfFacetValue];
rObj.count = facet_fields[prop][i + 1];
rObj.count = facet_fields[prop][i + 1];
} else {
rObj = new FilterItem(facetValue, facet_fields[prop][i + 1]);
}
@ -62,11 +85,13 @@ export default class App extends Vue {
}
}
async onSearch(term) {
// console.log(term);
// while (this.activeFilterItems.length > 0) {
// this.activeFilterItems.pop();
// }
async onSearch(term): Promise<void> {
if (term){
term = term.trim();
} else {
term = "*%3A*";
}
this.activeFilterItems = {};
// while (this.facets.length > 0) {
// this.facets.pop();
@ -75,6 +100,15 @@ export default class App extends Vue {
this.searchTerm = term;
var res = await rdrApi.search(this.searchTerm, this.activeFilterItems);
this.results = res.response.docs;
this.numFound = res.response.numFound;
// pagination
this.pagination['total'] = res.response.numFound;
this.pagination['per_page'] = res.responseHeader.params.rows;
this.pagination['current_page'] = 1;
this.pagination['data'] = res.response.docs;
// facets
var facet_fields = res.facet_counts.facet_fields;
for (var prop in facet_fields) {
var facetValues = facet_fields[prop].map((facet, i) => {
@ -92,9 +126,22 @@ export default class App extends Vue {
// console.log(this.facets.toString());
}
// When the window loads, read query parameters and perform search
async mounted() {
var query = this.getParameterByName("q");
if (query) query = query.trim();
await this.onSearch("*%3A*");
this.loaded = true;
}
mounted() {
// console.log('Component mounted.')
getParameterByName(name: string, url?: string) {
if (!url) url = window.location.href;
name = name.replace(/[\[\]]/g, "\\$&");
var regex = new RegExp("[?&]" + name + "(=([^&#]*)|&|#|$)"),
results = regex.exec(url);
if (!results) return null;
if (!results[2]) return "";
return decodeURIComponent(results[2].replace(/\+/g, " "));
}
}

View file

@ -1,28 +0,0 @@
export default class FilterItem {
category;
value;
count;
active;
constructor(value, count) {
// this.category = category;
this.value = value;
this.count = count;
this.active = false;
this.category = "";
}
get Category() {
return this.category;
}
set Category(theCategory) {
this.category = theCategory;
}
get Active() {
return this.active;
}
set Active(isActive) {
this.active = isActive;
}
}

View file

@ -0,0 +1,31 @@
export default class FilterItem {
private category: string;
value: string;
count: number;
private active: boolean;
constructor(value: string, count: number) {
this.value = value;
this.count = count;
this.active = false;
this.category = "";
}
//#region properties
get Category(): string {
return this.category;
}
set Category(theCategory: string) {
this.category = theCategory;
}
get Active(): boolean {
return this.active;
}
set Active(isActive: boolean) {
this.active = isActive;
}
//#endregion
}

View file

@ -1,35 +0,0 @@
import axios from "axios";
export default {
async search(term, filterItems) {
// solr endpoint
// const host = 'http://voyagerdemo.com/';
const host = 'https://repository.geologie.ac.at/';
const path = 'solr/rdr_data/select';
const fields = 'id,server_date_published,abstract_output,title_output,title_additional,author,subject'; // fields we want returned
const dismaxFields = "title^3 abstract^2 subject^1";
const facetFields = "facet.field=language&facet.field={!key=datatype}doctype&facet.field=subject";//&fq=year:(2019)";//&facet.query=year:2018";
var filterFields = "";
// filterItems.forEach(function (item) {
// console.log(item.value + " " + item.category);
// filterFields += "&fq=" + item.category +":("+ item.value + ")";
// });
Object.entries(filterItems).forEach(([key, valueArray]) => {
// console.log(`${key} ${valueArray}`);
valueArray.forEach(function (value) {
filterFields += "&fq=" + key +":("+ value + ")";
});
});
// $dismax->setQueryFields('title^3 abstract^2 subject^1');
const api = `${host}${path}?defType=dismax&q=${term}&fl=${fields}&qf=${dismaxFields}&facet=on&${facetFields}&${filterFields}&wt=json&rows=20&indent=on`;
const res = await axios.get(api);
return res.data;//.response;//.docs;
}
}

View file

@ -0,0 +1,68 @@
import axios from "axios";
var SOLR_CONFIG = {
"server": "https://arcticdata.io/metacat/d1/mn/v2/query/solr?", // Solr server
"filter": "knb-lter-bnz", // Filter results for an organization or user
"limit": 4, // Max number of results to retrieve per page
"resultsElementId": "searchResults", // Element to contain results
"urlElementId": "searchUrl", // Element to display search URL
"countElementId": "resultCount", // Element showing number of results
"pagesElementId": "pagination", // Element to display result page links
"showPages": 5 // MUST BE ODD NUMBER! Max number of page links to show
};
export default {
async search(term: string, filterItems: Object, start?: string): Promise<any> {
// solr endpoint
// const host = 'http://voyagerdemo.com/';
const host = 'https://repository.geologie.ac.at/';
const path = 'solr/rdr_data/select?';
var base = host + path;
//const fields = 'id,server_date_published,abstract_output,title_output,title_additional,author,subject'; // fields we want returned
var fields = ["id",
"server_date_published",
"abstract_output",
"title_output",
"title_additional",
"author",
"subject"].toString();
var limit = "&rows=" + SOLR_CONFIG["limit"];
// var limit = solrConfig.limit;
var params = "fl=" + fields + "&defType=edismax&wt=json&indent=on";
if (start === undefined) start = "0";
start = "&start=" + start;
//const dismaxFields = "title^3 abstract^2 subject^1";
const facetFields = "&facet=on&facet.field=language&facet.field={!key=datatype}doctype&facet.field=subject";//&fq=year:(2019)";//&facet.query=year:2018";
var filterFields = "";
// filterItems.forEach(function (item) {
// console.log(item.value + " " + item.category);
// filterFields += "&fq=" + item.category +":("+ item.value + ")";
// });
Object.entries(filterItems).forEach(([key, valueArray]) => {
// console.log(`${key} ${valueArray}`);
valueArray.forEach(function (value) {
filterFields += "&fq=" + key + ":(" + value + ")";
});
});
var query = "&q=" + term;
// $dismax->setQueryFields('title^3 abstract^2 subject^1');
//const api = `${host}${path}?defType=dismax&q=${term}&fl=${fields}&qf=${dismaxFields}&facet=on&${facetFields}&${filterFields}&wt=json&rows=20&indent=on`;
//const api = `${host}${path}?q=${term}&fl=${fields}&facet=on&${facetFields}&${filterFields}&wt=json&rows=20&indent=on`;
var api = base + params + limit + start + query + filterFields + facetFields;
let res = await axios.get(api);
// let { data } = res.data;
return res.data;//.response;//.docs;
}
}

View file

@ -1,4 +1,5 @@
import { Component, Vue, Prop, Provide } from 'vue-property-decorator';
import FilterItem from './../models/filter-item';
@Component
export default class FacetList extends Vue {
@ -12,10 +13,10 @@ export default class FacetList extends Vue {
data;
@Prop([String])
filterName;
@Prop([String])
// @Prop([String])
// alias;
get alias() {
get alias(): string {
return this.filterName == 'datatype' ? 'doctype' : this.filterName
}
// get filterItems() {
@ -38,16 +39,16 @@ export default class FacetList extends Vue {
// // }, this);
// return facetValues;
// }
get filterItems() {
get filterItems(): Array<FilterItem> {
return this.data;
}
get overflowing() {
get overflowing(): boolean {
//ko.observable(self.filterItems().length - self.activeFilterItems().length > ITEMS_PER_FILTER);
return (this.filterItems.length) > this.ITEMS_PER_FILTER;
}
get uncollapseLabelText() {
get uncollapseLabelText() : string {
if (this.collapsed == true) {
// return myLabels.viewer.sidePanel.more; //"More results";
return "More results";
@ -58,7 +59,7 @@ export default class FacetList extends Vue {
}
}
toggle = function () {
toggle = function (): void {
if (this.collapsed == true) {
this.collapsed = false;
}
@ -68,7 +69,7 @@ export default class FacetList extends Vue {
}
}
activateItem = function (filterItem) {
activateItem = function (filterItem: FilterItem): void {
filterItem.Category = this.alias;
filterItem.Active = true;
this.$emit("filter", filterItem);

View file

@ -36,9 +36,8 @@ export default FacetList;
<style scoped>
/* local styles */
.disabled {
/* background: #dddddd; */
/* color: #EBEBE4; */
color:#ffffff;
color:lightgray;
pointer-events: none;
text-decoration:line-through;
}

View file

@ -0,0 +1,49 @@
import { Vue, Component, Prop } from 'vue-property-decorator';
// import Vue from "vue";
@Component
export default class VsPagination extends Vue {
pageNumber: number = 0; // default to page 0
@Prop()
data;
@Prop({ default: 4 }) readonly offset: number;
changePage(page) {
if (page == this.data.current_page) {
return;
}
this.data.current_page = page;
let from = (page * this.data.per_page) - this.data.per_page;
this.$emit('paginate', from);
}
get numberOfPages() {
return Math.ceil(this.data.total / this.data.per_page);
}
get pages() {
let numberOfPages = Math.ceil(this.data.total / this.data.per_page);
// if (!this.data.to) {
// return [];
// }
let from = this.data.current_page - this.data.per_page;
if (from < 1) {
from = 1;
}
let to = from + (this.data.per_page * 2);
if (to >= numberOfPages) {
to = numberOfPages;
}
let pagesArray = [];
for (let page = from; page <= to; page++) {
pagesArray.push(page);
}
return pagesArray;
}
mounted() {
}
}

View file

@ -0,0 +1,278 @@
<template>
<nav v-if="pages.length > 1" class="pagination is-rounded is-medium" role="navigation" aria-label="pagination">
<ul class="pagination-list">
<!-- Previous Page Link -->
<li v-if="data.current_page > 1">
<a class="pagination-previous" href="#" rel="prev" v-on:click.prevent="changePage(data.current_page - 1)">&laquo;</a>
</li>
<li v-else>
<a class="pagination-previous disabled" disabled href="#" rel="prev">&laquo;</a>
</li>
<li v-for="(page, index) in pages" :key="index" >
<a href="#" v-on:click.prevent="changePage(page)" v-bind:class="['pagination-link', page == data.current_page ? 'is-current' : '']">{{ page }}</a>
</li>
<!-- Previous Page Link -->
<li v-if="data.current_page < numberOfPages">
<a class="pagination-next" href="#" v-on:click.prevent="changePage(data.current_page + 1)" rel="next">&raquo;</a>
</li>
<li v-else>
<a class="pagination-next disabled" disabled href="#" rel="next">&raquo;</a>
</li>
</ul>
</nav>
</template>
<script lang="ts">
import VsPagination from "./vs-pagination-class";
export default VsPagination;
</script>
<style scoped>
/* local styles */
/* vom cms Pagination */
.pagination {
text-align: center;
margin: 80px 0 0 0;
clear: both;
}
.pagination a {
/*font-size:1.1em;*/
padding-left: 1em;
padding-right: 1em;
}
/* selbst Pagination */
.pagination-previous,
.pagination-next,
.pagination-link,
.pagination-dots {
-webkit-touch-callout: none;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
-moz-appearance: none;
-webkit-appearance: none;
align-items: center;
border: 1px solid transparent;
border-radius: 4px;
box-shadow: none;
display: inline-flex;
/*display: inline-block;*/
font-size: 1rem;
height: 2.25em;
justify-content: flex-start;
line-height: 1.5;
padding: calc(0.375em - 1px) calc(0.625em - 1px) calc(0.375em - 1px)
calc(0.625em - 1px);
/*padding-top: calc(0.375em - 1px);
padding-right: calc(0.625em - 1px);
padding-bottom: calc(0.375em - 1px);
padding-left: calc(0.625em - 1px);*/
position: relative;
vertical-align: top;
}
.pagination-previous:focus,
.pagination-next:focus,
.pagination-link:focus,
.pagination-dots:focus,
.is-focused.pagination-next,
.is-focused.pagination-link,
.is-focused.pagination-dots,
.pagination-previous:active,
.pagination-next:active,
.pagination-link:active,
.pagination-dots:active,
.is-active.pagination-previous,
.is-active.pagination-next,
.is-active.pagination-link,
.is-active.pagination-dots {
outline: none;
}
.pagination-previous.disabled,
.pagination-next.disabled,
.pagination-link.disabled,
.pagination-dots.disabled {
cursor: not-allowed;
}
.pagination {
/*background-color:azure;*/
font-size: 1rem;
margin: -0.25rem;
}
.pagination.is-small {
font-size: 0.75rem;
}
.pagination.is-medium {
font-size: 1.25rem;
}
.pagination.is-large {
font-size: 1.5rem;
}
.pagination.is-rounded .pagination-previous,
.pagination.is-rounded .pagination-next {
padding-left: 1em;
padding-right: 1em;
border-radius: 290486px;
}
.pagination.is-rounded .pagination-link {
border-radius: 290486px;
}
/*.pagination,*/
.pagination-list {
align-items: center;
display: flex;
justify-content: center;
text-align: center;
list-style: none;
}
.pagination-previous,
.pagination-next,
.pagination-link,
.pagination-dots {
font-size: 1em;
padding-left: 0.5em;
padding-right: 0.5em;
justify-content: center;
margin: 0.25rem;
text-align: center;
}
.pagination-previous,
.pagination-next,
.pagination-link {
border-color: #dbdbdb;
color: #363636;
min-width: 2.25em;
}
.pagination-previous:hover,
.pagination-next:hover,
.pagination-link:hover {
border-color: #b5b5b5;
color: #363636;
}
.pagination-previous:focus,
.pagination-next:focus,
.pagination-link:focus {
border-color: #3273dc;
}
.pagination-previous:active,
.pagination-next:active,
.pagination-link:active {
box-shadow: inset 0 1px 2px rgba(10, 10, 10, 0.2);
}
.pagination-previous.disabled,
.pagination-next.disabled,
.pagination-link.disabled {
background-color: #dbdbdb;
border-color: #dbdbdb;
box-shadow: none;
color: #7a7a7a;
opacity: 0.5;
}
.pagination-previous,
.pagination-next {
padding-left: 0.75em;
padding-right: 0.75em;
white-space: nowrap;
}
.pagination-link.is-current {
background-color: #3abac4; /*#3273dc;*/
border-color: #3abac4;
color: #fff;
}
.pagination-dots {
color: #b5b5b5;
pointer-events: none;
}
.pagination-list {
flex-wrap: wrap;
}
@media screen and (max-width: 767px) {
.pagination {
flex-wrap: wrap;
}
.pagination-previous,
.pagination-next {
flex-grow: 1;
flex-shrink: 1;
}
.pagination-list li {
flex-grow: 1;
flex-shrink: 1;
}
}
@media screen and (min-width: 768px), print {
.pagination-list {
flex-grow: 1;
flex-shrink: 1;
justify-content: center;
order: 1;
}
.pagination-previous {
order: 2;
}
.pagination-next {
order: 3;
}
.pagination {
justify-content: space-between;
}
.pagination.is-centered .pagination-previous {
order: 1;
}
.pagination.is-centered .pagination-list {
justify-content: center;
order: 2;
}
.pagination.is-centered .pagination-next {
order: 3;
}
.pagination.is-right .pagination-previous {
order: 1;
}
.pagination.is-right .pagination-next {
order: 2;
}
.pagination.is-right .pagination-list {
justify-content: flex-end;
order: 3;
}
}
</style>

View file

@ -21,7 +21,7 @@ export default class VsResults extends Vue {
this.$forceUpdate();
};
convert(unixtimestamp) {
convert(unixtimestamp: number) {
// Unixtimestamp
// var unixtimestamp = document.getElementById('timestamp').value;

View file

@ -10,12 +10,12 @@
</div>-->
<div class="results">
<div class="result-list-info">
<!-- <div class="result-list-info">
<div class="resultheader">
Your search yielded
<strong>{{ results.length }}</strong> results:
</div>
</div>
</div> -->
<section class="result-list-container">
<div class="row">

4
resources/assets/js/typings.d.ts vendored Normal file
View file

@ -0,0 +1,4 @@
declare module '*.vue' {
import Vue from 'vue'
export default Vue
}

View file

@ -109,7 +109,7 @@
<!-- Help -->
<section data-sr id="help" class="help u-full-width featured-bg-image">
<h4 class="centered">
<button class="button inverted">We're here to help!</button>
<a class="button inverted" href="{{ URL::route('frontend.home.help') }}">We're here to help!</a>
</h4>
</section>

View file

@ -161,5 +161,6 @@
@endsection
@section('after-scripts')
{{-- <script type="text/javascript" src="{{ asset('js/search/main.js') }}"></script> --}}
<script type="text/javascript" src="{{ asset('js/search/main.js') }}"></script>
@stop