282 lines
5.7 KiB
Vue
282 lines
5.7 KiB
Vue
<template>
|
|
<div>
|
|
<div class="search" :class="{ active: focusingInput }">
|
|
<IconSearch class="search-icon" tabindex="-1" />
|
|
|
|
<input
|
|
ref="input"
|
|
type="text"
|
|
placeholder="Search for movie or show"
|
|
aria-label="Search input for finding a movie or show"
|
|
autocorrect="off"
|
|
autocapitalize="off"
|
|
tabindex="0"
|
|
v-model="query"
|
|
@input="handleInput"
|
|
@click="focusingInput = true"
|
|
@keydown.escape="handleEscape"
|
|
@keyup.enter="handleSubmit"
|
|
@keydown.up="navigateUp"
|
|
@keydown.down="navigateDown"
|
|
@focus="focusingInput = true"
|
|
@blur="focusingInput = false"
|
|
/>
|
|
|
|
<IconClose
|
|
tabindex="0"
|
|
aria-label="button"
|
|
v-if="query && query.length"
|
|
@click="resetQuery"
|
|
@keydown.enter.stop="resetQuery"
|
|
class="close-icon"
|
|
/>
|
|
</div>
|
|
|
|
<AutocompleteDropdown
|
|
v-if="showAutocompleteResults"
|
|
:query="query"
|
|
:index="dropdownIndex"
|
|
:results.sync="dropdownResults"
|
|
/>
|
|
</div>
|
|
</template>
|
|
|
|
<script>
|
|
import { mapActions, mapGetters } from "vuex";
|
|
import SeasonedButton from "@/components/ui/SeasonedButton";
|
|
import AutocompleteDropdown from "@/components/AutocompleteDropdown";
|
|
|
|
import IconSearch from "src/icons/IconSearch";
|
|
import IconClose from "src/icons/IconClose";
|
|
import config from "@/config.json";
|
|
|
|
export default {
|
|
name: "SearchInput",
|
|
components: {
|
|
SeasonedButton,
|
|
AutocompleteDropdown,
|
|
IconClose,
|
|
IconSearch
|
|
},
|
|
data() {
|
|
return {
|
|
query: null,
|
|
disabled: false,
|
|
dropdownIndex: -1,
|
|
dropdownResults: [],
|
|
focusingInput: false,
|
|
showAutocomplete: false
|
|
};
|
|
},
|
|
computed: {
|
|
...mapGetters("popup", ["isOpen"]),
|
|
showAutocompleteResults() {
|
|
return (
|
|
!this.disabled &&
|
|
this.focusingInput &&
|
|
this.query &&
|
|
this.query.length > 0
|
|
);
|
|
}
|
|
},
|
|
created() {
|
|
const params = new URLSearchParams(window.location.search);
|
|
if (params && params.has("query")) {
|
|
this.query = decodeURIComponent(params.get("query"));
|
|
}
|
|
|
|
const elasticUrl = config.ELASTIC_URL;
|
|
if (elasticUrl === undefined || elasticUrl === false || elasticUrl === "") {
|
|
this.disabled = true;
|
|
}
|
|
},
|
|
methods: {
|
|
...mapActions("popup", ["open"]),
|
|
navigateDown() {
|
|
if (this.dropdownIndex < this.dropdownResults.length - 1) {
|
|
this.dropdownIndex++;
|
|
}
|
|
},
|
|
navigateUp() {
|
|
if (this.dropdownIndex > -1) this.dropdownIndex--;
|
|
|
|
const input = this.$refs.input;
|
|
const textLength = input.value.length;
|
|
|
|
setTimeout(() => {
|
|
input.focus();
|
|
input.setSelectionRange(textLength, textLength + 1);
|
|
}, 1);
|
|
},
|
|
search() {
|
|
const encodedQuery = encodeURI(this.query.replace('/ /g, "+"'));
|
|
|
|
this.$router.push({
|
|
name: "search",
|
|
query: {
|
|
...this.$route.query,
|
|
query: encodedQuery
|
|
}
|
|
});
|
|
},
|
|
resetQuery(event) {
|
|
this.query = "";
|
|
this.$refs.input.focus();
|
|
},
|
|
handleInput(e) {
|
|
this.$emit("input", this.query);
|
|
this.dropdownIndex = -1;
|
|
},
|
|
handleSubmit() {
|
|
if (!this.query || this.query.length == 0) return;
|
|
|
|
if (this.dropdownIndex >= 0) {
|
|
const resultItem = this.dropdownResults[this.dropdownIndex];
|
|
|
|
console.log("resultItem:", resultItem);
|
|
this.open({
|
|
id: resultItem.id,
|
|
type: resultItem.type
|
|
});
|
|
return;
|
|
}
|
|
|
|
this.search();
|
|
this.$refs.input.blur();
|
|
this.dropdownIndex = -1;
|
|
},
|
|
handleEscape() {
|
|
if (!this.isOpen) {
|
|
this.$refs.input.blur();
|
|
this.dropdownIndex = -1;
|
|
}
|
|
}
|
|
}
|
|
};
|
|
</script>
|
|
|
|
<style lang="scss" scoped>
|
|
@import "src/scss/variables";
|
|
@import "src/scss/media-queries";
|
|
@import "src/scss/main";
|
|
|
|
.close-icon {
|
|
position: absolute;
|
|
top: calc(50% - 12px);
|
|
right: 0;
|
|
cursor: pointer;
|
|
fill: var(--text-color);
|
|
height: 24px;
|
|
width: 24px;
|
|
|
|
@include tablet-min {
|
|
right: 6px;
|
|
}
|
|
}
|
|
|
|
.filter {
|
|
width: 100%;
|
|
display: flex;
|
|
flex-direction: column;
|
|
margin: 1rem 2rem;
|
|
|
|
h2 {
|
|
margin-top: 0.5rem;
|
|
margin-bottom: 0.5rem;
|
|
font-weight: 400;
|
|
}
|
|
|
|
&-items {
|
|
display: flex;
|
|
flex-direction: row;
|
|
align-items: center;
|
|
|
|
> :not(:first-child) {
|
|
margin-left: 1rem;
|
|
}
|
|
}
|
|
}
|
|
|
|
hr {
|
|
display: block;
|
|
height: 1px;
|
|
border: 0;
|
|
border-bottom: 1px solid $text-color-50;
|
|
margin-top: 10px;
|
|
margin-bottom: 10px;
|
|
width: 90%;
|
|
}
|
|
|
|
.search.active {
|
|
input {
|
|
border-color: var(--color-green);
|
|
}
|
|
|
|
.search-icon {
|
|
fill: var(--color-green);
|
|
}
|
|
}
|
|
|
|
.search {
|
|
height: $header-size;
|
|
display: flex;
|
|
position: fixed;
|
|
flex-wrap: wrap;
|
|
z-index: 5;
|
|
border: 0;
|
|
background-color: $background-color-secondary;
|
|
|
|
// TODO check if this is for mobile
|
|
width: calc(100% - 110px);
|
|
top: 0;
|
|
right: 55px;
|
|
|
|
@include tablet-min {
|
|
position: relative;
|
|
width: 100%;
|
|
right: 0px;
|
|
}
|
|
|
|
input {
|
|
display: block;
|
|
width: 100%;
|
|
padding: 13px 28px 13px 45px;
|
|
outline: none;
|
|
margin: 0;
|
|
border: 0;
|
|
background-color: $background-color-secondary;
|
|
font-weight: 300;
|
|
font-size: 18px;
|
|
color: $text-color;
|
|
border-bottom: 1px solid transparent;
|
|
|
|
&:focus {
|
|
// border-bottom: 1px solid var(--color-green);
|
|
border-color: var(--color-green);
|
|
}
|
|
|
|
@include tablet-min {
|
|
font-size: 24px;
|
|
padding: 13px 40px 13px 60px;
|
|
}
|
|
}
|
|
|
|
&-icon {
|
|
width: 20px;
|
|
height: 20px;
|
|
fill: var(--text-color-50);
|
|
pointer-events: none;
|
|
position: absolute;
|
|
left: 15px;
|
|
top: calc(50% - 10px);
|
|
|
|
@include tablet-min {
|
|
width: 24px;
|
|
height: 24px;
|
|
top: calc(50% - 12px);
|
|
left: 22px;
|
|
}
|
|
}
|
|
}
|
|
</style>
|