From 56a89e8512ffbb77c55268ededa1c5131a495b66 Mon Sep 17 00:00:00 2001 From: cnouguier Date: Wed, 30 Oct 2024 16:34:48 +0100 Subject: [PATCH] wip: Refactor collections based components #917 --- core/client/components/collection/KGrid.vue | 17 +- core/client/components/collection/KTable.vue | 64 ++++--- .../components/collection/KTimeLine.vue | 18 +- core/client/composables/collection.js | 4 +- core/client/mixins/index.js | 1 - core/client/mixins/mixin.base-collection.js | 162 ------------------ 6 files changed, 76 insertions(+), 190 deletions(-) delete mode 100644 core/client/mixins/mixin.base-collection.js diff --git a/core/client/components/collection/KGrid.vue b/core/client/components/collection/KGrid.vue index 133cab447..ac8ae5661 100644 --- a/core/client/components/collection/KGrid.vue +++ b/core/client/components/collection/KGrid.vue @@ -11,7 +11,7 @@ -
-
+
+ +
+ +
+ +
+
+
diff --git a/core/client/components/collection/KTable.vue b/core/client/components/collection/KTable.vue index 59e3caba2..169c23b9f 100644 --- a/core/client/components/collection/KTable.vue +++ b/core/client/components/collection/KTable.vue @@ -1,5 +1,6 @@ @@ -158,6 +179,20 @@ const { schema, compile } = useSchema() const options = Object.assign({ filterQuery }, _.omit(toRefs(props), ['filterQuery'])) const { items, nbTotalItems, nbPages, currentPage, refreshCollection, resetCollection } = useCollection(options) +// Watch +watch(items, onCollectionRefreshed) +watch(schema, () => { + processSchema() + resetCollection() +}) +watch(() => props.schema, async (value) => { + await compile(props.schema || `${props.service}.get`) +}) +watch(nbTotalItems, () => { + // Update pagination for table + pagination.value.rowsNumber = nbTotalItems.value +}) + // Functions function processSchema () { _.forOwn(schema.value.properties, (value, key) => { @@ -227,29 +262,14 @@ function onCollectionRefreshed () { emit('collection-refreshed', items.value) } -// Lifecycle hooks - -// Emit events so that embbeding components can be aware of it -watch(items, onCollectionRefreshed) -watch(schema, () => { - processSchema() - resetCollection() -}) -watch(() => props.schema, async (value) => { - await compile(props.schema || `${props.service}.get`) -}) -watch(nbTotalItems, () => { - // Update pagination for table - pagination.value.rowsNumber = nbTotalItems.value -}) - +// Hooks onBeforeMount(async () => { // This will launch collection refresh await compile(props.schema || `${props.service}.get`) + refreshCollection() // Whenever the user abilities are updated, update collection as well Events.on('user-abilities-changed', refreshCollection) }) - onBeforeUnmount(() => { Events.off('user-abilities-changed', refreshCollection) }) diff --git a/core/client/components/collection/KTimeLine.vue b/core/client/components/collection/KTimeLine.vue index a93f0901b..66a9c27db 100644 --- a/core/client/components/collection/KTimeLine.vue +++ b/core/client/components/collection/KTimeLine.vue @@ -14,7 +14,7 @@ -
-
+
+ +
+ +
+ +
+
+
diff --git a/core/client/composables/collection.js b/core/client/composables/collection.js index bd2b11213..cde108111 100644 --- a/core/client/composables/collection.js +++ b/core/client/composables/collection.js @@ -20,7 +20,7 @@ export function useCollection (options) { }) // Data - const items = ref([]) + const items = ref(null) const nbTotalItems = ref(0) const currentPage = ref(1) let itemListener = null @@ -118,6 +118,8 @@ export function useCollection (options) { }, options.refreshThrottle.value, { leading: false }) function resetCollection () { + // Do not reset the collection since it is initializing + if (_.isNil(items.value)) return // Reset pagination and start again refreshing the collection if (options.appendItems.value) setCollectionItems([]) currentPage.value = 1 diff --git a/core/client/mixins/index.js b/core/client/mixins/index.js index 7b528793e..e01ee6a28 100644 --- a/core/client/mixins/index.js +++ b/core/client/mixins/index.js @@ -1,6 +1,5 @@ export * from './mixin.base-context.js' export * from './mixin.base-activity.js' -export * from './mixin.base-collection.js' export * from './mixin.base-editor.js' export * from './mixin.base-item.js' export * from './mixin.base-field.js' diff --git a/core/client/mixins/mixin.base-collection.js b/core/client/mixins/mixin.base-collection.js deleted file mode 100644 index 6f9f05e8d..000000000 --- a/core/client/mixins/mixin.base-collection.js +++ /dev/null @@ -1,162 +0,0 @@ -import _ from 'lodash' -import logger from 'loglevel' -import { getLocale } from '../utils/utils.locale.js' - -export const baseCollection = { - emits: [ - 'collection-refreshed', - 'selection-changed', - 'item-toggled' - ], - props: { - // This value can be overriden in activities if they want to manage pagination by themselves - // nbItemsPerPage = 0 means that the client does not handle pagination and server defaults will be used - nbItemsPerPage: { - type: Number, - default: 12 - }, - // This value indicate if items of each page replace or are appended to previous ones - appendItems: { - type: Boolean, - default: false - }, - // Only invoke refresh at most once per every refreshThrottle milliseconds - refreshThrottle: { - type: Number, - default: 500 - } - }, - computed: { - nbPages () { - return (this.nbItemsPerPage > 0 ? Math.ceil(this.nbTotalItems / this.nbItemsPerPage) : 1) - } - }, - data () { - return { - items: [], - nbTotalItems: 0, - currentPage: 1 - } - }, - methods: { - subscribe (query) { - // Remove previous listener if any - this.unsubscribe() - this.itemListener = this.getService().watch({ listStrategy: this.listStrategy || 'smart' }) - .find({ query }) - .subscribe(response => { - // Manage GeoJson features collection as well - if (response.type === 'FeatureCollection') { - this.items = response.features - } else if (this.appendItems) { - // Append the response ensuring there is no duplicates - this.items = _.unionBy(response.data, this.items, '_id') - // We keep order from the updated list as depending on the sorting criteria a new item might have to be pushed on top of current items - const sortQuery = _.get(this.getCollectionBaseQuery(), '$sort') - if (sortQuery) { - // By default orderBy is case sensitive while using collation we want to perform case insensitive sort - this.items = _.orderBy(this.items, - // Sort function for each sort property - _.map(_.keys(sortQuery), property => { - return item => { - const value = _.get(item, property) - return (typeof value === 'string' ? value.toLowerCase() : value) - } - }), - // Sort order for each sort property - _.map(_.values(sortQuery), value => { return value > 0 ? 'asc' : 'desc' })) - } - } else { - this.items = response.data - } - this.nbTotalItems = response.total - this.onCollectionRefreshed() - }, error => { - logger.error(error) - }) - }, - unsubscribe () { - if (this.itemListener) { - this.itemListener.unsubscribe() - this.itemListener = null - } - }, - getCollectionBaseQuery () { - // This method should be overriden in collections - return {} - }, - getCollectionFilterQuery () { - // This method should be overriden in collections - return {} - }, - getCollectionPaginationQuery () { - // This method can be overriden in collections - if (this.nbItemsPerPage > 0) { - return { - $limit: this.nbItemsPerPage, - $skip: (this.currentPage - 1) * this.nbItemsPerPage - } - } else return {} - }, - resetCollection () { - // Reset pagination and start again refreshing the collection - this.items = [] - this.currentPage = 1 - this.refreshCollection() - }, - onPageChanged () { - this.refreshCollection() - }, - onItemToggled (item, toggled) { - this.$emit('item-toggled', item, toggled) - }, - onItemSelected (item, section) { - this.$emit('selection-changed', item, section) - }, - onItemsSelected (items) { - this.$emit('selection-changed', items) - }, - onCollectionRefreshed () { - this.$emit('collection-refreshed', this.items) - }, - onItemsUpdated (items) { - // When we append items some items of the previous pages might have been updated. - // In this case we need to reset the full collection as Rx only tracks changes on the current page - let updatedItems = (Array.isArray(items) ? items : [items]) - // We keep order from the updated list as depending on the sorting criteria a new item might have to be pushed on top of current items - updatedItems = _.intersectionWith(this.items, updatedItems, (item1, item2) => (item1._id.toString() === item2._id.toString())) - if (updatedItems.length > 0) this.resetCollection() - } - }, - created () { - // Avoid initiating too much request as the same time, this might be the case - // when async UI components update simultaneously the base/filter query - // see https://github.com/kalisio/kdk/issues/432 - const refreshCollection = () => { - // Add locale to perform sorting (i.e. collation) correctly w.r.t. user's language - const fullQuery = Object.assign({ $locale: getLocale() }, - this.getCollectionBaseQuery(), - this.getCollectionFilterQuery(), - this.getCollectionPaginationQuery()) - // Find the desired items - this.subscribe(fullQuery) - } - this.refreshCollection = _.throttle(refreshCollection, this.refreshThrottle, { leading: false }) - - if (this.appendItems) { - const service = this.getService() - service.on('patched', this.onItemsUpdated) - service.on('updated', this.onItemsUpdated) - service.on('removed', this.onItemsUpdated) - } - }, - beforeUnmount () { - this.unsubscribe() - if (this.appendItems) { - const service = this.getService() - service.off('patched', this.onItemsUpdated) - service.off('updated', this.onItemsUpdated) - service.off('removed', this.onItemsUpdated) - } - } -}