2019-05-21 11:13:59 +01:00

199 lines
4.7 KiB
Vue

<template>
<div id="search" @click="open" v-bind:class="{ active , ongoing }">
<div id="input">
<button
v-if="active"
class="action"
@click="close"
:aria-label="$t('buttons.close')"
:title="$t('buttons.close')"
>
<i class="material-icons">arrow_back</i>
</button>
<i v-else class="material-icons">search</i>
<input
type="text"
@keyup.exact="keyup"
@keyup.enter="submit"
ref="input"
:autofocus="active"
v-model.trim="value"
:aria-label="$t('search.search')"
:placeholder="$t('search.search')"
>
</div>
<div id="result" ref="result">
<div>
<template v-if="isEmpty">
<p>{{ text }}</p>
<template v-if="value.length === 0">
<div class="boxes">
<h3>{{ $t('search.types') }}</h3>
<div>
<div
tabindex="0"
v-for="(v,k) in boxes"
:key="k"
role="button"
@click="init('type:'+k)"
:aria-label="$t('search.'+v.label)"
>
<i class="material-icons">{{v.icon}}</i>
<p>{{ $t('search.'+v.label) }}</p>
</div>
</div>
</div>
</template>
</template>
<ul v-show="results.length > 0">
<li v-for="(s,k) in filteredResults" :key="k">
<router-link @click.native="close" :to="'./' + s.path">
<i v-if="s.dir" class="material-icons">folder</i>
<i v-else class="material-icons">insert_drive_file</i>
<span>./{{ s.path }}</span>
</router-link>
</li>
</ul>
</div>
<p id="renew">
<i class="material-icons spin">autorenew</i>
</p>
</div>
</div>
</template>
<script>
import { mapState, mapGetters, mapMutations } from "vuex"
import url from "@/utils/url"
import { search } from "@/api"
var boxes = {
image: { label: "images", icon: "insert_photo" },
audio: { label: "music", icon: "volume_up" },
video: { label: "video", icon: "movie" },
pdf: { label: "pdf", icon: "picture_as_pdf" }
}
export default {
name: "search",
data: function() {
return {
value: "",
active: false,
ongoing: false,
results: [],
reload: false,
resultsCount: 50,
scrollable: null
}
},
watch: {
show (val, old) {
this.active = val === "search"
if (old === "search" && !this.active) {
if (this.reload) {
this.setReload(true)
}
document.body.style.overflow = "auto"
this.reset()
this.value = ''
this.active = false
this.$refs.input.blur()
} else if (this.active) {
this.reload = false
this.$refs.input.focus()
document.body.style.overflow = "hidden"
}
},
value () {
if (this.results.length) {
this.reset()
}
}
},
computed: {
...mapState(["user", "show"]),
...mapGetters(["isListing"]),
boxes() {
return boxes
},
isEmpty() {
return this.results.length === 0
},
text() {
if (this.ongoing) {
return ""
}
return this.value === '' ? this.$t("search.typeToSearch") : this.$t("search.pressToSearch")
},
filteredResults () {
return this.results.slice(0, this.resultsCount)
}
},
mounted() {
window.addEventListener("keydown", event => {
if (event.keyCode === 27) {
this.closeHovers()
}
})
this.$refs.result.addEventListener('scroll', event => {
if (event.target.offsetHeight + event.target.scrollTop >= event.target.scrollHeight - 100) {
this.resultsCount += 50
}
})
},
methods: {
...mapMutations(["showHover", "closeHovers", "setReload"]),
open() {
this.showHover("search")
},
close(event) {
event.stopPropagation()
event.preventDefault()
this.closeHovers()
},
keyup(event) {
if (event.keyCode === 27) {
this.close(event)
return
}
this.results.length = 0
},
init (string) {
this.value = `${string} `
this.$refs.input.focus()
},
reset () {
this.ongoing = false
this.resultsCount = 50
this.results = []
},
async submit(event) {
event.preventDefault()
if (this.value === '') {
return
}
let path = this.$route.path
if (!this.isListing) {
path = url.removeLastDir(path) + "/"
}
this.ongoing = true
this.results = await search(path, this.value)
this.ongoing = false
}
}
}
</script>