mirror of
https://github.com/gotson/komga.git
synced 2025-12-20 23:45:11 +01:00
fix(webui): replace searchbox for authors in filter panel
This commit is contained in:
parent
61ca80dc72
commit
14e6718252
2 changed files with 122 additions and 60 deletions
|
|
@ -16,20 +16,30 @@
|
|||
{{ f.name }}
|
||||
</v-expansion-panel-header>
|
||||
<v-expansion-panel-content class="no-padding">
|
||||
<v-autocomplete
|
||||
<search-box-base
|
||||
v-if="f.search"
|
||||
:search-function="f.search"
|
||||
@selected="click(key, $event)"
|
||||
>
|
||||
<template v-slot:item="data">
|
||||
<v-list-item-content class="text-body-2">{{ data.item }}</v-list-item-content>
|
||||
</template>
|
||||
</search-box-base>
|
||||
|
||||
<v-list
|
||||
v-if="f.search"
|
||||
v-model="model[key]"
|
||||
:items="items[key]"
|
||||
:search-input.sync="search[key]"
|
||||
:loading="loading[key]"
|
||||
:hide-no-data="!search[key] || loading[key]"
|
||||
@keydown.esc="search[key] = null"
|
||||
multiple
|
||||
deletable-chips
|
||||
small-chips
|
||||
dense
|
||||
solo
|
||||
/>
|
||||
>
|
||||
<v-list-item v-for="(v, i) in filtersActive[key]"
|
||||
:key="i"
|
||||
@click.stop="click(key, v)"
|
||||
>
|
||||
<v-list-item-icon>
|
||||
<v-icon color="secondary">mdi-checkbox-marked</v-icon>
|
||||
</v-list-item-icon>
|
||||
<v-list-item-title>{{ v }}</v-list-item-title>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
|
||||
<v-list
|
||||
v-if="f.values"
|
||||
|
|
@ -57,17 +67,11 @@
|
|||
|
||||
<script lang="ts">
|
||||
import Vue, {PropType} from 'vue'
|
||||
import SearchBoxBase from "@/components/SearchBoxBase.vue";
|
||||
|
||||
export default Vue.extend({
|
||||
name: 'FilterPanels',
|
||||
data: () => {
|
||||
return {
|
||||
search: {} as any,
|
||||
model: {} as any,
|
||||
items: {} as any,
|
||||
loading: {} as any,
|
||||
}
|
||||
},
|
||||
components: {SearchBoxBase},
|
||||
props: {
|
||||
filtersOptions: {
|
||||
type: Object as PropType<FiltersOptions>,
|
||||
|
|
@ -78,46 +82,6 @@ export default Vue.extend({
|
|||
required: true,
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
search: {
|
||||
deep: true,
|
||||
async handler(val: any) {
|
||||
for (const prop in val) {
|
||||
if (val[prop] !== null) {
|
||||
this.loading[prop] = true
|
||||
this.$set(this.items, prop, await (this.filtersOptions[prop] as any).search(val[prop]))
|
||||
this.loading[prop] = false
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
model: {
|
||||
deep: true,
|
||||
async handler(val: any) {
|
||||
for (const prop in val) {
|
||||
if (val[prop] !== null && val[prop] !== this.filtersActive[prop]) {
|
||||
let r = this.$_.cloneDeep(this.filtersActive)
|
||||
r[prop] = this.$_.clone(val[prop])
|
||||
|
||||
this.$emit('update:filtersActive', r)
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
filtersActive: {
|
||||
deep: true,
|
||||
immediate: true,
|
||||
handler(val: any) {
|
||||
for (const prop in val) {
|
||||
if (val[prop].length > 0) {
|
||||
// we need to add existing values to items also, else v-autocomplete won't show it
|
||||
this.$set(this.items, prop, this.$_.union(this.items[prop], val[prop]))
|
||||
this.$set(this.model, prop, val[prop])
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
clear(key: string) {
|
||||
let r = this.$_.cloneDeep(this.filtersActive)
|
||||
|
|
|
|||
98
komga-webui/src/components/SearchBoxBase.vue
Normal file
98
komga-webui/src/components/SearchBoxBase.vue
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
<template>
|
||||
<div>
|
||||
<v-autocomplete
|
||||
v-model="selectedItem"
|
||||
:placeholder="$t('search.search')"
|
||||
:no-data-text="$t('searchbox.no_results')"
|
||||
:loading="loading"
|
||||
:items="results"
|
||||
:hide-no-data="!showResults"
|
||||
clearable
|
||||
solo
|
||||
hide-details
|
||||
no-filter
|
||||
dense
|
||||
append-icon=""
|
||||
auto-select-first
|
||||
:search-input.sync="search"
|
||||
@keydown.esc="clear"
|
||||
ref="searchbox"
|
||||
>
|
||||
<template v-slot:selection>
|
||||
</template>
|
||||
|
||||
<template v-slot:item="data">
|
||||
<slot name="item" v-bind:item="data.item"></slot>
|
||||
</template>
|
||||
</v-autocomplete>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import {debounce} from 'lodash'
|
||||
import Vue, {PropType} from 'vue'
|
||||
import {BookDto} from '@/types/komga-books'
|
||||
import {SeriesDto} from "@/types/komga-series";
|
||||
|
||||
export default Vue.extend({
|
||||
name: 'SearchBoxBase',
|
||||
data: function () {
|
||||
return {
|
||||
selectedItem: null as unknown as any,
|
||||
search: null,
|
||||
showResults: false,
|
||||
loading: false,
|
||||
results: [],
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
selectedItem(val, old) {
|
||||
if (val) {
|
||||
this.$nextTick(() => {
|
||||
this.selectedItem = undefined
|
||||
this.clear()
|
||||
})
|
||||
|
||||
this.$emit('selected', val)
|
||||
|
||||
//@ts-ignore
|
||||
this.$refs.searchbox.blur()
|
||||
}
|
||||
},
|
||||
search(val) {
|
||||
this.searchItems(val)
|
||||
},
|
||||
showResults(val) {
|
||||
!val && this.clear()
|
||||
},
|
||||
},
|
||||
props: {
|
||||
searchFunction: {
|
||||
type: Function as PropType<Function>,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
getLibraryName(item: BookDto | SeriesDto): string {
|
||||
return this.$store.getters.getLibraryById(item.libraryId).name;
|
||||
},
|
||||
searchItems: debounce(async function (this: any, query: string) {
|
||||
if (query) {
|
||||
this.loading = true
|
||||
|
||||
this.results = await this.searchFunction.apply(this, [query])
|
||||
|
||||
this.showResults = true
|
||||
this.loading = false
|
||||
} else {
|
||||
this.clear()
|
||||
}
|
||||
}, 500),
|
||||
clear() {
|
||||
this.search = null
|
||||
this.results = []
|
||||
this.showResults = false
|
||||
},
|
||||
},
|
||||
})
|
||||
</script>
|
||||
Loading…
Reference in a new issue