Skip to content

Commit

Permalink
wip: Refactor collections based components #917
Browse files Browse the repository at this point in the history
  • Loading branch information
cnouguier committed Aug 13, 2024
1 parent 77a27b9 commit 077dd48
Showing 1 changed file with 161 additions and 54 deletions.
215 changes: 161 additions & 54 deletions core/client/components/collection/KGrid.vue
Original file line number Diff line number Diff line change
@@ -1,55 +1,117 @@
<template>
<div v-if="items.length > 0" class="q-pa-sm row">
<template v-for="item in items" :key="item._id">
<div :class="itemClass">
<component
:id="item._id"
:service="service"
:item="item"
:contextId="contextId"
:is="rendererComponent"
v-bind="renderer"
@item-selected="onItemSelected" />
</div>
</template>
<div v-if="nbPages > 1" class="col-12">
<q-pagination
class="justify-center q-ma-md"
v-model="currentPage"
:max="nbPages"
:input="true"
@update:model-value="refreshCollection"
/>
<div class="column no-wrap">
<!--
Header
-->
<div class="q-pr-xs q-pb-xs">
<slot name="header">
<KPanel
:content="header"
:class="headerClass"
/>
</slot>
</div>
</div>
<div v-else>
<slot name="empty-section">
<div class="row justify-center">
<KStamp icon="las la-exclamation-circle" icon-size="1.6rem" :text="$t('KGrid.EMPTY_GRID')" direction="horizontal" />
<!--
Content
-->
<div v-if="items.length > 0" class="col">
<!-- Infinite mode -->
<div v-if="appendItems" class="fit scroll">
<q-infinite-scroll
@load="onLoad"
:initial-index="1"
:offset="200"
class="fit"
>
<div class="row">
<template v-for="(item, index) in items" :key="item._id">
<div :class="rendererClass">
<component
:id="item._id"
:service="service"
:item="item"
:contextId="contextId"
:is="itemRenderer"
v-bind="renderer"
@item-selected="onItemSelected"
/>
</div>
</template>
</div>
<template v-slot:loading>
<div class="text-center q-my-md">
<q-spinner-dots
color="primary"
size="40px"
/>
</div>
</template>
</q-infinite-scroll>
</div>
</slot>
<!-- Paginated mode -->
<div v-else class="fit row">
<template v-for="item in items" :key="item._id">
<div :class="rendererClass">
<component
:id="item._id"
:service="service"
:item="item"
:contextId="contextId"
:is="itemRenderer"
v-bind="renderer"
@item-selected="onItemSelected" />
</div>
</template>
<div v-if="nbPages > 1" class="col-12">
<q-pagination
class="row justify-center q-ma-md"
v-model="currentPage"
:max="nbPages"
:input="true"
@update:model-value="refreshCollection"
/>
</div>
</div>
</div>
<!-- Empty slot -->
<div v-else>
<slot name="empty">
<div class="row justify-center">
<KStamp
icon="las la-exclamation-circle"
icon-size="1.6rem"
:text="$t('KCollection.EMPTY_LABEL')"
direction="horizontal"
class="q-pa-md"
/>
</div>
</slot>
</div>
<!--
Footer
-->
<div>
<slot name="footer">
<q-separator inset v-if="footer"/>
<KPanel
:content="footer"
:class="footerClass"
/>
</slot>
</div>
</div>
</template>

<script setup>
import { computed, watch, toRefs, onBeforeMount, onBeforeUnmount } from 'vue'
import KStamp from '../KStamp.vue'
import { Events } from '../../events.js'
import { useCollection } from '../../composables'
import { Events } from '../../events.js'
import { loadComponent } from '../../utils'
const emit = defineEmits(['selection-changed', 'collection-refreshed'])
import KStamp from '../KStamp.vue'
import KPanel from '../KPanel.vue'
// Props
const props = defineProps({
renderer: {
type: Object,
default: () => {
return {
component: 'collection/KCard'
}
}
},
contextId: {
type: String,
default: undefined
Expand All @@ -66,45 +128,90 @@ const props = defineProps({
type: Object,
default: () => {}
},
listStrategy: {
type: String,
default: 'smart'
renderer: {
type: Object,
default: () => {
return {
component: 'collection/KCard'
}
}
},
processor: {
type: Function,
default: undefined
},
appendItems: {
type: Boolean,
default: false
},
nbItemsPerPage: {
type: Number,
default: 12
},
processor: {
type: Function,
listStrategy: {
type: String,
default: 'smart'
},
header: {
type: [Array, Object],
default: () => null
},
headerClass: {
type: String,
default: undefined
},
footer: {
type: [Array, Object],
default: () => null
},
footerClass: {
type: String,
default: undefined
}
})
// Emits
const emit = defineEmits(['collection-refreshed', 'selection-changed'])
// Data
const { items, nbTotalItems, nbPages, currentPage, refreshCollection, resetCollection } = useCollection(toRefs(props))
let doneFunction = null
// Computed
const rendererComponent = computed(() => loadComponent(props.renderer.component))
const itemClass = computed(() => props.renderer.class || 'q-pa-sm col-12 col-sm-6 col-md-4 col-lg-3 col-xl-2')
const itemRenderer = computed(() => {
return loadComponent(props.renderer.component)
})
const rendererClass = computed(() => {
return props.renderer.class || 'q-pa-sm col-12 col-sm-6 col-md-4 col-lg-3'
})
// Watch
watch(items, onCollectionRefreshed)
// Functions
function onLoad (index, done) {
currentPage.value = index
refreshCollection()
doneFunction = done
}
function onItemSelected (item, section) {
emit('selection-changed', item, section)
}
function onCollectionRefreshed () {
emit('collection-refreshed', items.value)
// call done callback if needed
if (doneFunction) {
doneFunction(items.value.length === nbTotalItems.value)
doneFunction = null
}
}
const { items, nbTotalItems, nbPages, currentPage, refreshCollection, resetCollection } = useCollection(toRefs(props))
// Lifecycle hooks
// Emit events so that embbeding components can be aware of it
watch(items, onCollectionRefreshed)
// Hooks
onBeforeMount(() => {
refreshCollection()
// Whenever the user abilities are updated, update collection as well
Events.on('user-abilities-changed', refreshCollection)
})
onBeforeUnmount(() => {
Events.off('user-abilities-changed', refreshCollection)
})
Expand Down

0 comments on commit 077dd48

Please sign in to comment.