Add ViewBase component to deal with skeletons and initial views queries, update ViewSearch accordingly

This commit is contained in:
Axolotle 2020-12-16 12:15:19 +01:00
parent 694bdb6ea1
commit 0486865f56
2 changed files with 124 additions and 30 deletions

View file

@ -0,0 +1,83 @@
<template>
<div>
<top-bar v-if="hasTopBar">
<slot name="top-bar-group-left" slot="group-left" />
<slot name="top-bar-group-right" slot="group-right" />
</top-bar>
<slot v-else name="top-bar" />
<slot name="top" v-bind="{ loading: isLoading }" />
<b-skeleton-wrapper :loading="isLoading">
<template #loading>
<slot name="skeleton">
<component :is="skeleton" />
</slot>
</template>
<!-- Empty div to be able to receive multiple components -->
<div>
<slot name="default" v-bind="{ loading: isLoading }" />
</div>
</b-skeleton-wrapper>
<slot name="bot" v-bind="{ loading: isLoading }" />
</div>
</template>
<script>
import api from '@/api'
export default {
name: 'ViewBase',
props: {
queries: { type: Array, default: null },
skeleton: { type: [String, Array], default: null },
// Optional prop to take control of the loading value
loading: { type: Boolean, default: null }
},
data () {
return {
fallback_loading: this.loading === null && this.fetch !== null ? true : null
}
},
computed: {
isLoading () {
if (this.loading !== null) return this.loading
return this.fallback_loading
},
hasTopBar () {
return ['top-bar-group-left', 'top-bar-group-right'].some(slotName => (slotName in this.$slots))
}
},
methods: {
fetchQueries (triggerLoading = false) {
if (triggerLoading) {
this.fallback_loading = true
}
const [apiQueries, storeQueries] = this.queries.reduce((types, query) => {
types[typeof query === 'string' ? 0 : 1].push(query)
return types
}, [[], []])
Promise.all([
api.getAll(apiQueries),
this.$store.dispatch('FETCH_ALL', storeQueries)
]).then(([apiResponses, storeResponses]) => {
this.$emit('queries-response', ...apiResponses, ...storeResponses)
this.fallback_loading = false
})
}
},
created () {
if (this.queries) this.fetchQueries()
}
}
</script>

View file

@ -1,46 +1,57 @@
<template>
<div>
<view-top-bar>
<template #group-left>
<b-input-group id="search-group" class="w-100">
<b-input-group-prepend is-text>
<icon iname="search" />
</b-input-group-prepend>
<view-base v-bind="$attrs" v-on="$listeners" :skeleton="skeleton">
<slot v-if="hasCustomTopBar" name="top-bar" slot="top-bar" />
<template v-if="!hasCustomTopBar" #top-bar-group-left>
<b-input-group class="w-100">
<b-input-group-prepend is-text>
<icon iname="search" />
</b-input-group-prepend>
<b-form-input
id="top-bar-search"
:value="search" @input="$emit('update:search', $event)"
:placeholder="$t('search.for', { items: $tc('items.' + itemsName, 2) })"
:disabled="!items"
/>
</b-input-group>
</template>
<b-form-input
id="top-bar-search"
:value="search" @input="$emit('update:search', $event)"
:placeholder="$t('search.for', { items: $tc('items.' + itemsName, 2) })"
:disabled="!items"
/>
</b-input-group>
</template>
<slot v-if="!hasCustomTopBar" name="top-bar-buttons" slot="top-bar-group-right" />
<slot name="top-bar-buttons" slot="group-right" />
</view-top-bar>
<slot name="top" slot="top" />
<b-alert v-if="items === null || filteredItems === null" variant="warning" show>
<slot name="alert-message">
<icon iname="exclamation-triangle" />
{{ $t(items === null ? 'items_verbose_count' : 'search.not_found', { items: $tc('items.' + itemsName, 0) }) }}
</slot>
</b-alert>
<template #default>
<b-alert v-if="items === null || filteredItems === null" variant="warning">
<slot name="alert-message">
<icon iname="exclamation-triangle" />
{{ $t(items === null ? 'items_verbose_count' : 'search.not_found', { items: $tc('items.' + itemsName, 0) }) }}
</slot>
</b-alert>
<slot v-else name="default" />
<slot v-else name="default" />
</template>
<slot name="extra"/>
</div>
<slot name="bot" slot="bot" />
<slot name="skeleton" slot="skeleton" />
</view-base>
</template>
<script>
export default {
name: 'SearchView',
name: 'ViewSearch',
props: {
search: { type: String, required: true },
itemsName: { type: String, required: true },
items: { type: null, required: true },
filteredItems: { type: null, required: true }
itemsName: { type: String, required: true },
filteredItems: { type: null, required: true },
search: { type: String, default: null },
skeleton: { type: String, default: 'list-group-skeleton' }
},
computed: {
hasCustomTopBar () {
return 'top-bar' in this.$slots
}
}
}
</script>