- add search components: vs-result.vue, vs-input.vue, facet-category.vue

- add search models
- add opensans font
- add bulma css framework
- add axios, rxjs, vue-property-decorator
- npm updates
This commit is contained in:
Arno Kaimbacher 2021-11-12 10:13:22 +01:00
parent 8e0bc7c18d
commit 156bf0ae26
30 changed files with 4937 additions and 2044 deletions

View file

@ -0,0 +1,250 @@
// import Vue from "vue";
// import { Component, Prop } from 'vue-property-decorator';
// import debounce from 'lodash/debounce';
import { DatasetService } from "../../services/dataset.service";
import { SolrSettings } from "@/models/solr";
// import { ref } from "vue";
import { Options, Vue } from "vue-class-component";
import { Prop, Emit } from "vue-property-decorator";
import { Dataset, Suggestion, SearchType } from "@/models/dataset";
@Options({
name: "VsInput",
})
export default class VsInput extends Vue {
@Prop()
private title!: string;
@Prop({ default: "Search" })
private placeholder!: string;
private display = "";
private value!: Suggestion;
private error = "";
private results: Array<Dataset> = [];
private loading = false;
private selectedIndex = 0;
private selectedDisplay = "";
private solr: SolrSettings = {
core: "rdr_data", // SOLR.core;
host: "tethys.at",
};
private rdrAPI!: DatasetService;
itemRefs!: Array<any>;
setItemRef(el: any) {
this.itemRefs.push(el);
}
beforeUpdate() {
this.itemRefs = [];
}
mounted() {
this.rdrAPI = new DatasetService();
}
get showResults(): boolean {
return this.results.length > 0;
}
get noResults(): boolean {
return Array.isArray(this.results) && this.results.length === 0;
}
get isLoading(): boolean {
return this.loading === true;
}
get hasError(): boolean {
return this.error !== null;
}
get suggestions(): Suggestion[] {
// const suggestion = {
// titles: new Array<string>(),
// authors: new Array<string>(),
// subjects: new Array<string>(),
// };
const suggestions = new Array<Suggestion>();
this.results.forEach((dataset) => {
const del = dataset.title_output?.toLowerCase();
if (dataset.title_output.toLowerCase().includes(this.display.toLowerCase())) {
const title = dataset.title_output;
// if (!suggestion["titles"].find((value) => value === title)) {
// suggestion.titles.push(title);
// }
const hasTitleSuggestion = suggestions.some((suggestion) => suggestion.value === title && suggestion.type == SearchType.Title);
if (!hasTitleSuggestion) {
const suggestion = new Suggestion(title, SearchType.Title);
suggestions.push(suggestion);
}
}
if (this.find(dataset.author, this.display.toLowerCase()) !== "") {
const author = this.find(dataset.author, this.display.toLowerCase());
const hasAuthorSuggestion = suggestions.some((suggestion) => suggestion.value === author && suggestion.type == SearchType.Author);
if (!hasAuthorSuggestion) {
const suggestion = new Suggestion(author, SearchType.Author);
suggestions.push(suggestion);
}
}
if (this.find(dataset.subject, this.display.toLowerCase()) != "") {
const subject = this.find(dataset.subject, this.display.toLowerCase());
const hasSubjectSuggestion = suggestions.some((suggestion) => suggestion.value === subject && suggestion.type == SearchType.Subject);
if (!hasSubjectSuggestion) {
const suggestion = new Suggestion(subject, SearchType.Subject);
suggestions.push(suggestion);
}
}
});
return suggestions;
}
/**
* Clear all values, results and errors
**/
clear(): void {
this.display = "";
// this.value = null;
this.results = [];
this.error = "";
this.$emit("clear");
}
@Emit("search-change")
search(): string {
this.results = [];
// this.$emit("search", this.display)
return this.display;
}
searchChanged(): void {
this.selectedIndex = 0;
// Let's warn the parent that a change was made
// this.$emit("input", this.display);
if (this.display.length >= 2) {
this.loading = true;
this.resourceSearch();
} else {
this.results = [];
}
}
private resourceSearch() {
if (!this.display) {
this.results = [];
return;
}
this.loading = true;
// this.setEventListener();
this.request();
}
private request(): void {
this.rdrAPI.searchTerm(this.display, this.solr.core, this.solr.host).subscribe(
(res: Dataset[]) => this.dataHandler(res),
(error: any) => this.errorHandler(error),
() => (this.loading = false),
);
}
private dataHandler(datasets: Dataset[]): void {
this.results = datasets;
// this.$emit("search", this.display);
// this.loading = false;
}
private errorHandler(err: any): void {
this.error = err;
// this.loading = false;
}
/**
* Is this item selected?
* @param {Object}
* @return {Boolean}
*/
isSelected(key: number): boolean {
return key === this.selectedIndex;
}
onArrowDown(ev: any): void {
ev.preventDefault();
if (this.selectedIndex === null) {
this.selectedIndex = 0;
return;
}
this.selectedIndex = this.selectedIndex === this.suggestions.length - 1 ? 0 : this.selectedIndex + 1;
this.fixScrolling();
}
private fixScrolling() {
const currentElement = this.itemRefs[this.selectedIndex];
currentElement.scrollIntoView({
behavior: "smooth",
block: "nearest",
inline: "start",
});
}
onArrowUp(ev: any): void {
ev.preventDefault();
if (this.selectedIndex === null) {
this.selectedIndex = this.suggestions.length - 1;
return;
}
this.selectedIndex = this.selectedIndex === 0 ? this.suggestions.length - 1 : this.selectedIndex - 1;
this.fixScrolling();
}
onEnter(): void {
if (this.selectedIndex === null) {
this.$emit("nothingSelected", this.display);
return;
}
this.select(this.suggestions[this.selectedIndex]);
// this.$emit("enter", this.display);
}
@Emit("search-change")
select(obj: Suggestion): Suggestion {
// if (!obj) {
// return;
// }
this.value = obj; //(obj["title_output"]) ? obj["title_output"] : obj.id
// this.display = obj; // this.formatDisplay(obj)
this.selectedDisplay = this.display;
// this.$emit("search", this.value);
this.close();
return this.value;
}
private find(myarray: Array<string>, searchterm: string): string {
for (let i = 0, len = myarray.length; i < len; i += 1) {
if (typeof myarray[i] === "string" && myarray[i].toLowerCase().indexOf(searchterm) !== -1) {
// print or whatever
return myarray[i];
}
}
return "";
}
/**
* Close the results list. If nothing was selected clear the search
*/
close(): void {
if (!this.value || !this.selectedDisplay) {
this.clear();
}
if (this.selectedDisplay !== this.display && this.value) {
this.display = this.selectedDisplay;
}
this.results = [];
this.error = "";
//this.removeEventListener()
this.$emit("close");
}
}

View file

@ -0,0 +1,98 @@
<template>
<div class="content column is-half is-offset-one-quarter" style="margin-top: 30px; padding-bottom: 0; margin-bottom: 0px">
<div class="field has-addons">
<div class="control is-expanded">
<input
id="search_query"
class="input is-medium"
type="text"
name="q"
autocomplete="off"
v-model="display"
v-on:input="searchChanged"
v-bind:title="title"
v-bind:placeholder="placeholder"
v-on:keydown.down="onArrowDown"
v-on:keydown.up="onArrowUp"
v-on:keydown.enter="onEnter"
@keydown.tab="close"
v-on:focus="focus"
/>
<!-- <p>Message is: {{ display }}</p> -->
</div>
<div class="control">
<button class="button input is-medium search-button-icon" @click="search()">
<img src="../../assets/fa/search.svg" style="height: 22px; width: 22px" />
</button>
</div>
</div>
</div>
<div class="autocomplete-column is-half is-offset-one-quarter">
<ul class="autocomplete-results pure-u-23-24" v-show="showResults">
<li class="loading" v-if="isLoading">Loading results...</li>
<li
v-else
v-for="(result, i) in suggestions"
:key="i"
class="autocomplete-result-item"
:class="{ 'is-active': isSelected(i) }"
@click.prevent="select(result)"
:ref="setItemRef"
>
<div class="small-label">
<label>{{ result.value }} ({{ result.type }})</label>
</div>
</li>
</ul>
</div>
</template>
<script lang="ts">
import VsInput from "./vs-input";
export default VsInput;
</script>
<style>
.autocomplete-column {
display: block;
flex-basis: 0;
flex-grow: 1;
flex-shrink: 1;
padding: 0 0.75rem 0.75rem 0.75rem;
position: relative;
}
.autocomplete-results {
padding: 0;
margin: 0;
margin-left: 0em;
border: 1px solid #eeeeee;
list-style-type: none;
z-index: 1000;
position: absolute;
max-height: 200px;
overflow-y: auto;
overflow: hidden;
background: white;
width: 100%;
border: 1px solid #ccc;
border-top: 0;
color: black;
}
.autocomplete-result-item {
list-style: none;
text-align: left;
/* padding: 7px 10px; */
cursor: pointer;
}
.autocomplete-result-item.is-active {
background: rgba(0, 180, 255, 0.15);
}
.autocomplete-result-item:hover {
background: rgba(0, 180, 255, 0.075);
}
</style>