forked from geolba/tethys.frontend
- 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:
parent
8e0bc7c18d
commit
156bf0ae26
30 changed files with 4937 additions and 2044 deletions
|
@ -1,126 +0,0 @@
|
|||
<template>
|
||||
<div class="hello">
|
||||
<h1>{{ msg }}</h1>
|
||||
<p>
|
||||
For a guide and recipes on how to configure / customize this project,<br />
|
||||
check out the
|
||||
<a href="https://cli.vuejs.org" target="_blank" rel="noopener"
|
||||
>vue-cli documentation</a
|
||||
>.
|
||||
</p>
|
||||
<h3>Installed CLI Plugins</h3>
|
||||
<ul>
|
||||
<li>
|
||||
<a
|
||||
href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-babel"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
>babel</a
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-typescript"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
>typescript</a
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-eslint"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
>eslint</a
|
||||
>
|
||||
</li>
|
||||
</ul>
|
||||
<h3>Essential Links</h3>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="https://vuejs.org" target="_blank" rel="noopener">Core Docs</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://forum.vuejs.org" target="_blank" rel="noopener"
|
||||
>Forum</a
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://chat.vuejs.org" target="_blank" rel="noopener"
|
||||
>Community Chat</a
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://twitter.com/vuejs" target="_blank" rel="noopener"
|
||||
>Twitter</a
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://news.vuejs.org" target="_blank" rel="noopener">News</a>
|
||||
</li>
|
||||
</ul>
|
||||
<h3>Ecosystem</h3>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="https://router.vuejs.org" target="_blank" rel="noopener"
|
||||
>vue-router</a
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://vuex.vuejs.org" target="_blank" rel="noopener">vuex</a>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
href="https://github.com/vuejs/vue-devtools#vue-devtools"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
>vue-devtools</a
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://vue-loader.vuejs.org" target="_blank" rel="noopener"
|
||||
>vue-loader</a
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
href="https://github.com/vuejs/awesome-vue"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
>awesome-vue</a
|
||||
>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { Options, Vue } from "vue-class-component";
|
||||
|
||||
@Options({
|
||||
props: {
|
||||
msg: String,
|
||||
},
|
||||
})
|
||||
export default class HelloWorld extends Vue {
|
||||
msg!: string;
|
||||
}
|
||||
</script>
|
||||
|
||||
<!-- Add "scoped" attribute to limit CSS to this component only -->
|
||||
<style scoped lang="scss">
|
||||
h3 {
|
||||
margin: 40px 0 0;
|
||||
}
|
||||
ul {
|
||||
list-style-type: none;
|
||||
padding: 0;
|
||||
}
|
||||
li {
|
||||
display: inline-block;
|
||||
margin: 0 10px;
|
||||
}
|
||||
a {
|
||||
color: #42b983;
|
||||
}
|
||||
</style>
|
14
src/components/HelloWorld/HelloWorld.scss
Normal file
14
src/components/HelloWorld/HelloWorld.scss
Normal file
|
@ -0,0 +1,14 @@
|
|||
h3 {
|
||||
margin: 40px 0 0;
|
||||
}
|
||||
ul {
|
||||
list-style-type: none;
|
||||
padding: 0;
|
||||
}
|
||||
li {
|
||||
display: inline-block;
|
||||
margin: 0 10px;
|
||||
}
|
||||
a {
|
||||
color: #42b983;
|
||||
}
|
10
src/components/HelloWorld/HelloWorld.ts
Normal file
10
src/components/HelloWorld/HelloWorld.ts
Normal file
|
@ -0,0 +1,10 @@
|
|||
import { Options, Vue } from "vue-class-component";
|
||||
import { Prop } from "vue-property-decorator";
|
||||
|
||||
@Options({
|
||||
name: "HelloWorld",
|
||||
})
|
||||
export default class HelloWorld extends Vue {
|
||||
@Prop({ type: String })
|
||||
private msg!: string;
|
||||
}
|
32
src/components/HelloWorld/HelloWorld.vue
Normal file
32
src/components/HelloWorld/HelloWorld.vue
Normal file
|
@ -0,0 +1,32 @@
|
|||
<template>
|
||||
<div class="hello">
|
||||
<h1>{{ msg }}</h1>
|
||||
<p>
|
||||
For a guide and recipes on how to configure / customize this project,<br />
|
||||
check out the
|
||||
<a href="https://cli.vuejs.org" target="_blank" rel="noopener">vue-cli documentation</a>.
|
||||
</p>
|
||||
<h3>Installed CLI Plugins</h3>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-babel" target="_blank" rel="noopener">babel</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-typescript" target="_blank" rel="noopener">typescript</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-eslint" target="_blank" rel="noopener">eslint</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import HelloWorld from "./HelloWorld";
|
||||
export default HelloWorld;
|
||||
</script>
|
||||
|
||||
<!-- Add "scoped" attribute to limit CSS to this component only -->
|
||||
<style scoped lang="scss">
|
||||
@import "./HelloWorld.scss"; // $text-primary would be defined in that file
|
||||
</style>
|
60
src/components/face-category/FacetCategory.ts
Normal file
60
src/components/face-category/FacetCategory.ts
Normal file
|
@ -0,0 +1,60 @@
|
|||
import { FacetItem } from "@/models/headers";
|
||||
import { FilterItem } from "@/models/solr";
|
||||
import { Options, Vue } from "vue-class-component";
|
||||
import { Prop, Emit } from "vue-property-decorator";
|
||||
// import FilterItem from '../models/filter-item';
|
||||
|
||||
@Options({
|
||||
name: "FacetCategory",
|
||||
})
|
||||
export default class FacetCategory extends Vue {
|
||||
ITEMS_PER_FILTER = 2;
|
||||
bar = "";
|
||||
collapsed = true;
|
||||
|
||||
@Prop()
|
||||
facetItems!: Array<FacetItem>;
|
||||
|
||||
@Prop([String])
|
||||
filterName!: string;
|
||||
|
||||
get alias(): string {
|
||||
return this.filterName == "datatype" ? "doctype" : this.filterName;
|
||||
}
|
||||
|
||||
// get filterItems(): Array<FilterItem> {
|
||||
// return this.data;
|
||||
// }
|
||||
|
||||
get overflowing(): boolean {
|
||||
//ko.observable(self.filterItems().length - self.activeFilterItems().length > ITEMS_PER_FILTER);
|
||||
return this.facetItems.length > this.ITEMS_PER_FILTER;
|
||||
}
|
||||
|
||||
get uncollapseLabelText(): string {
|
||||
if (this.collapsed == true) {
|
||||
// return myLabels.viewer.sidePanel.more; //"More results";
|
||||
return "More results";
|
||||
} else {
|
||||
// return myLabels.viewer.sidePanel.collapse; //"Collapse";
|
||||
return "Collapse";
|
||||
}
|
||||
}
|
||||
|
||||
toggle(): void {
|
||||
if (this.collapsed == true) {
|
||||
this.collapsed = false;
|
||||
} else if (this.collapsed == false) {
|
||||
this.collapsed = true;
|
||||
//list.children("li:gt(4)").hide();
|
||||
}
|
||||
}
|
||||
|
||||
@Emit("filter")
|
||||
activateItem(filterItem: FacetItem): FacetItem {
|
||||
filterItem.category = this.alias;
|
||||
filterItem.active = true;
|
||||
// this.$emit("filter", filterItem);
|
||||
return filterItem;
|
||||
}
|
||||
}
|
62
src/components/face-category/facet-category.vue
Normal file
62
src/components/face-category/facet-category.vue
Normal file
|
@ -0,0 +1,62 @@
|
|||
<template>
|
||||
<div class="panel panel-default">
|
||||
<!-- <h3 class="panel-title filterViewModelName">{{ filterName }}</h3> -->
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title titlecase filterViewModelName">{{ filterName }}</h3>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<!-- e.g.language -->
|
||||
<ul class="filter-items list-unstyled" v-bind:class="{ limited: facetItems.length > 1 && collapsed }">
|
||||
<li v-for="(item, index) in facetItems" :key="index" class="list-group-item titlecase">
|
||||
<!-- <span :class="item.Active ? 'disabled' : ''" @click.prevent="activateItem(item)">{{ item.val }} ({{ item.count }}) </span> -->
|
||||
<span @click.prevent="activateItem(item)">{{ item.val }} ({{ item.count }}) </span>
|
||||
</li>
|
||||
</ul>
|
||||
<ul class="overflowing" v-if="overflowing == true">
|
||||
<li>
|
||||
<span @click="toggle()">{{ uncollapseLabelText }}</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import FacetCategory from "./FacetCategory";
|
||||
export default FacetCategory;
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* local styles */
|
||||
.disabled {
|
||||
/* color: #EBEBE4; */
|
||||
color: lightgray;
|
||||
pointer-events: none;
|
||||
text-decoration: line-through;
|
||||
}
|
||||
|
||||
.overflowing {
|
||||
color: #444444;
|
||||
list-style: none;
|
||||
z-index: 999;
|
||||
}
|
||||
|
||||
.overflowing span {
|
||||
color: #444444;
|
||||
font-style: italic;
|
||||
text-decoration: underline;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.filter-items.limited li:nth-of-type(1n + 3) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
ul.filter-items li span {
|
||||
cursor: pointer;
|
||||
/* color:#ffffff;
|
||||
text-decoration: none;
|
||||
font-weight: bold;
|
||||
font-size: 16px; */
|
||||
}
|
||||
</style>
|
250
src/components/vs-input/vs-input.ts
Normal file
250
src/components/vs-input/vs-input.ts
Normal 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");
|
||||
}
|
||||
}
|
98
src/components/vs-input/vs-input.vue
Normal file
98
src/components/vs-input/vs-input.vue
Normal 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>
|
51
src/components/vs-result/vs-result.ts
Normal file
51
src/components/vs-result/vs-result.ts
Normal file
|
@ -0,0 +1,51 @@
|
|||
import { Dataset } from "@/models/dataset";
|
||||
import { Options, Vue } from "vue-class-component";
|
||||
import { Prop } from "vue-property-decorator";
|
||||
|
||||
@Options({
|
||||
name: "VsResult",
|
||||
})
|
||||
export default class VsResult extends Vue {
|
||||
public openAccessLicences: Array<string> = ["CC-BY-4.0", "CC-BY-SA-4.0"];
|
||||
|
||||
@Prop()
|
||||
private datasets!: Array<Dataset>;
|
||||
|
||||
public get results(): Array<Dataset> {
|
||||
return this.datasets;
|
||||
}
|
||||
|
||||
public getDomainWithoutSubdomain() {
|
||||
const urlParts = new URL(window.location.href).hostname.split(".");
|
||||
|
||||
return urlParts
|
||||
.slice(0)
|
||||
.slice(-(urlParts.length === 4 ? 3 : 2))
|
||||
.join(".");
|
||||
}
|
||||
|
||||
private convert(unixtimestamp: number) {
|
||||
// Unixtimestamp
|
||||
// var unixtimestamp = document.getElementById('timestamp').value;
|
||||
// Months array
|
||||
const months_arr = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
|
||||
// Convert timestamp to milliseconds
|
||||
const date = new Date(unixtimestamp * 1000);
|
||||
// Year
|
||||
const year = date.getFullYear();
|
||||
// Month
|
||||
const month = months_arr[date.getMonth()];
|
||||
// Day
|
||||
const day = date.getDate();
|
||||
// Hours
|
||||
const hours = date.getHours();
|
||||
// Minutes
|
||||
const minutes = "0" + date.getMinutes();
|
||||
// Seconds
|
||||
const seconds = "0" + date.getSeconds();
|
||||
// Display date time in MM-dd-yyyy h:m:s format
|
||||
const convdataTime = month + "-" + day + "-" + year + " " + hours + ":" + minutes.substr(-2) + ":" + seconds.substr(-2);
|
||||
// document.getElementById('datetime').innerHTML = convdataTime;
|
||||
return convdataTime;
|
||||
}
|
||||
}
|
165
src/components/vs-result/vs-result.vue
Normal file
165
src/components/vs-result/vs-result.vue
Normal file
|
@ -0,0 +1,165 @@
|
|||
<template>
|
||||
<div class="column" v-for="document in results" :key="document.id">
|
||||
<div class="card result-list-container">
|
||||
<div class="card-content row record-elem">
|
||||
<span class="label label-info" data-container="div" data-title="Publication date">
|
||||
{{ convert(document.server_date_published) }}
|
||||
</span>
|
||||
<span class="label label-default ng-binding">{{ document.doctype }}</span>
|
||||
<span v-if="openAccessLicences.includes(document.licence)" class="label label-success titlecase">Open Access</span>
|
||||
|
||||
<p v-if="document.identifier && document.identifier.length > 0">
|
||||
<!-- <span>Author: {{ document.identifier.join(', ') }}</span> -->
|
||||
<!-- <span v-for="(author,index) in document.author" :key="index">{{ author }}; </span> -->
|
||||
<!-- <span>'https://doi.org/' + {{ document.identifier[0] }}</span> -->
|
||||
<a target="_blank" v-bind:href="'https://doi.org/' + document.identifier[0]">
|
||||
{{ "https://doi.org/" + document.identifier[0] }}
|
||||
</a>
|
||||
</p>
|
||||
|
||||
<h4>
|
||||
<a
|
||||
v-if="document.identifier && document.identifier.length > 0"
|
||||
target="_self"
|
||||
v-bind:href="'https://doi.' + getDomainWithoutSubdomain() + '/' + document.identifier[0]"
|
||||
class="ng-binding"
|
||||
>
|
||||
{{ document.title_output }}
|
||||
</a>
|
||||
<a v-else target="_self" v-bind:href="'dataset/' + document.id" class="ng-binding">
|
||||
{{ document.title_output }}
|
||||
</a>
|
||||
</h4>
|
||||
|
||||
<p v-if="document.author && document.author.length > 0">
|
||||
<!-- <span>Author: {{ document.author.join(', ') }}</span> -->
|
||||
<span v-for="(author, index) in document.author" :key="index">{{ author }}; </span>
|
||||
</p>
|
||||
|
||||
<p class="clamped clamped-2">
|
||||
<span class="text">
|
||||
Abstract: {{ document.abstract_output }}
|
||||
<span class="ellipsis">...</span>
|
||||
<span class="fill"></span>
|
||||
</span>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<span>Licence: {{ document.licence }}</span>
|
||||
</p>
|
||||
|
||||
<span class="label label-keyword titlecase" v-for="(item, index) in document.subject" :key="index"> #{{ item }} </span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import VsResults from "./vs-result";
|
||||
export default VsResults;
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* overflow for abstracts */
|
||||
.clamped {
|
||||
line-height: 1.5;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
}
|
||||
.clamped-2 {
|
||||
/* Clamp to 2 lines, ie line-height x 2: */
|
||||
max-height: 4.5em;
|
||||
}
|
||||
.ellipsis {
|
||||
background: #fff;
|
||||
bottom: 0;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
}
|
||||
.fill {
|
||||
background: #fff;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* zenodo */
|
||||
.label-success {
|
||||
background-color: #5cb85c;
|
||||
}
|
||||
.label-default {
|
||||
background-color: #777;
|
||||
}
|
||||
.label-info {
|
||||
background-color: #6aa3d5;
|
||||
}
|
||||
.label-keyword {
|
||||
background-color: #7e7e7e;
|
||||
}
|
||||
.label {
|
||||
display: inline-block;
|
||||
padding: 0.2em 0.12em 0.3em;
|
||||
font-size: 75%;
|
||||
color: #fff;
|
||||
border-radius: 0.25em;
|
||||
margin-right: 1.25em;
|
||||
/* margin-left: 10px; */
|
||||
}
|
||||
/* .record-elem .h4, record-elem h4 {
|
||||
font-size: 18px;
|
||||
} */
|
||||
.record-elem p a {
|
||||
color: #3ad29f;
|
||||
font-size: 14px;
|
||||
}
|
||||
/* .record-elem h4 a {
|
||||
color: #000;
|
||||
} */
|
||||
|
||||
.record-elem h4 a {
|
||||
cursor: pointer;
|
||||
color: #000;
|
||||
text-decoration: none;
|
||||
font-weight: bold;
|
||||
/* margin-left: 10px; */
|
||||
font-size: 16px;
|
||||
}
|
||||
.record-elem h4 a:hover {
|
||||
color: #777;
|
||||
text-decoration: none;
|
||||
}
|
||||
/* .search-detail a:hover {
|
||||
color: rgb(0, 128, 0);
|
||||
} */
|
||||
.record-elem .h4,
|
||||
.record-elem h4 {
|
||||
font-size: 16px;
|
||||
letter-spacing: 0.05em;
|
||||
margin-top: 8px;
|
||||
}
|
||||
.record-detail h1,
|
||||
.record-detail p,
|
||||
.record-elem h4,
|
||||
.record-elem p,
|
||||
.well {
|
||||
word-wrap: break-word;
|
||||
}
|
||||
.record-elem p span {
|
||||
color: #000;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.post {
|
||||
position: relative;
|
||||
min-height: 1px;
|
||||
padding-left: 15px;
|
||||
padding-right: 15px;
|
||||
|
||||
box-sizing: border-box;
|
||||
margin-bottom: 10px;
|
||||
margin-top: 15px;
|
||||
}
|
||||
/* .record-elem {
|
||||
border-top: 1px solid rgb(238, 238, 238);
|
||||
} */
|
||||
</style>
|
Loading…
Add table
editor.link_modal.header
Reference in a new issue