Begins implementation of dynamic items list block with tainacan view modes. #872.

This commit is contained in:
mateuswetah 2024-04-30 10:06:56 -03:00
parent 9289b36898
commit 40eba14fc6
8 changed files with 196 additions and 31 deletions

View File

@ -993,6 +993,19 @@
.wp-block-tainacan-dynamic-items-list ul.items-list.items-layout-mosaic .mosaic-container,
.wp-block-tainacan-dynamic-items-list ul.items-list-edit.items-layout-mosaic .mosaic-container {
min-width: 240px; } }
.wp-block-tainacan-dynamic-items-list .items-list.items-layout-tainacan-view-modes {
--tainacan-container-padding: 0.875em; }
.wp-block-tainacan-dynamic-items-list .items-list.items-layout-tainacan-view-modes h1,
.wp-block-tainacan-dynamic-items-list .items-list.items-layout-tainacan-view-modes h2,
.wp-block-tainacan-dynamic-items-list .items-list.items-layout-tainacan-view-modes h3,
.wp-block-tainacan-dynamic-items-list .items-list.items-layout-tainacan-view-modes h4,
.wp-block-tainacan-dynamic-items-list .items-list.items-layout-tainacan-view-modes h5,
.wp-block-tainacan-dynamic-items-list .items-list.items-layout-tainacan-view-modes h6 {
margin: 0; }
.wp-block-tainacan-dynamic-items-list .items-list.items-layout-tainacan-view-modes ul {
list-style: none; }
.wp-block-tainacan-dynamic-items-list .items-list.items-layout-tainacan-view-modes .loading-overlay {
min-height: auto !important; }
.block-editor-block-list__block:not(.has-text-color) > .wp-block-tainacan-dynamic-items-list li.item-list-item a > span,
.block-editor-block-list__block:not(.has-text-color) > .wp-block-tainacan-dynamic-items-list li.item-list-item a:hover > span {

File diff suppressed because one or more lines are too long

View File

@ -1188,7 +1188,7 @@ class Theme_Helper {
* @param array $args {
* Optional. Array of arguments.
* @type string $item_id The Item ID
* @type string $items_list_layout The type of list to be rendered. Accepts 'grid', 'list', 'mosaic' and 'carousel'.
* @type string $items_list_layout The type of list to be rendered. Accepts 'grid', 'list', 'mosaic', 'carousel' and 'tainacan-view-mode.
* @type string $order Sorting direction to the related items query. Either 'desc' or 'asc'.
* @type string $orderby Sortby metadata. By now we're accepting only 'title' and 'date'.
* @type string $class_name Extra class to add to the wrapper, besides the default wp-block-tainacan-carousel-related-items
@ -1265,15 +1265,44 @@ class Theme_Helper {
? $block_args['image_size']
: ($no_crop_images_to_square ? 'tainacan-medium-full' : 'tainacan-medium');
// Remove attribute description to avoid poluting HTML
// No need to pass the complete item to avoid poluting HTML
$related_group['items'] = array_map(
function($el) use ($image_size) {
function($el) use ($args) {
// In Tainacan View Modes, we fetch items from api so we only need ID
if ( $args['items_list_layout'] === 'tainacan-view-modes' )
return $el['id'];
// For other layouts, we simply remove attribute description
unset($el['description']);
return $el;
}, $related_group['items']
);
if ( isset($args['items_list_layout']) && $args['items_list_layout'] !== 'carousel' ) {
if ( isset($args['items_list_layout']) && $args['items_list_layout'] === 'carousel' ) {
$items_list_args = wp_parse_args([
'collection_id' => $related_group['collection_id'],
'load_strategy' => 'parent',
'selected_items' => json_encode($related_group['items']),
'image_size' => $image_size
], $block_args);
$items_list_div = $this->get_tainacan_items_carousel($items_list_args);
} else if ( isset($args['items_list_layout']) && $args['items_list_layout'] === 'tainacan-view-modes' ) {
$items_list_args = wp_parse_args([
'collection_id' => $related_group['collection_id'],
'load_strategy' => 'selection', // Tainacan View Modes fetch item from api to get item metadata as well
'selected_items' => json_encode($related_group['items']),
'layout' => $args['items_list_layout'],
'displayed_metadata' => json_encode(isset( $block_args['displayed_metadata'] ) ? $block_args['displayed_metadata'] : []),
'selected_items' => json_encode($related_group['items']),
'tainacan_view_mode' => $block_args['tainacan_view_mode']
], $block_args);
$items_list_div = $this->get_tainacan_dynamic_items_list($items_list_args);
} else {
$items_list_args = wp_parse_args([
'collection_id' => $related_group['collection_id'],
'load_strategy' => 'parent',
@ -1283,15 +1312,7 @@ class Theme_Helper {
], $block_args);
$items_list_div = $this->get_tainacan_dynamic_items_list($items_list_args);
} else {
$items_list_args = wp_parse_args([
'collection_id' => $related_group['collection_id'],
'load_strategy' => 'parent',
'selected_items' => json_encode($related_group['items']),
'image_size' => $image_size
], $block_args);
$items_list_div = $this->get_tainacan_items_carousel($items_list_args);
}
}

View File

@ -1143,6 +1143,29 @@
}
}
}
// Rules for Tainacan View Modes are similar to those applied
// for .theme-items-list in /blocks/faceted-search/theme.vue
.items-list.items-layout-tainacan-view-modes {
// Not really used in EVERY container, but a rather frequent value
--tainacan-container-padding: 0.875em;
h1,
h2,
h3,
h4,
h5,
h6 {
margin: 0;
}
ul {
list-style: none;
}
.loading-overlay {
min-height: auto !important;
}
}
}
.block-editor-block-list__block:not(.has-text-color)>.wp-block-tainacan-dynamic-items-list {
li.item-list-item {

View File

@ -1,8 +1,11 @@
import { createApp, h } from 'vue';
import { createApp, h, defineAsyncComponent } from 'vue';
import DynamicItemsListTheme from './theme.vue';
import { ThumbnailHelperPlugin } from '../../../admin/js/utilities.js';
import { I18NPlugin } from '../../../admin/js/admin-utilities';
import VueBlurHash from 'another-vue3-blurhash';
import VTooltip from 'floating-vue';
import getDataAttribute from '../../js/compatibility/tainacan-blocks-compat-data-attributes.js';
export default (element) => {
@ -21,8 +24,31 @@ export default (element) => {
// Creates a new Vue Instance to manage each block isolatelly
blocks.forEach((block) => {
// View Modes Logic
let registeredViewModes =
( tainacan_blocks && tainacan_blocks.registered_view_modes && tainacan_blocks.registered_view_modes.length ) ?
tainacan_blocks.registered_view_modes :
[ 'table', 'cards', 'records', 'masonry', 'list', 'map' ];
// At first, we consider that all registered view modes are included.
let possibleViewModes = registeredViewModes.filter((aViewMode) => aViewMode === 'slideshow');
if ( getDataAttribute(block, 'enabled-view-modes') != undefined )
possibleViewModes = getDataAttribute(block, 'enabled-view-modes').split(',');
// View Mode settings
let possibleDefaultViewMode = 'masonry';
if ( getDataAttribute(block, 'tainacan-view-mode') != undefined )
possibleDefaultViewMode = getDataAttribute(block, 'tainacan-view-mode');
if ( possibleViewModes.indexOf(possibleDefaultViewMode) < 0 )
possibleViewModes.push(possibleDefaultViewMode);
// Configure Vue logic before passing it to constructor:
const VueDynamicItemsList = createApp( {
mounted() {
block.classList.add('has-mounted');
},
render() {
return h(DynamicItemsListTheme, {
searchURL: getDataAttribute(block, 'search-url'),
@ -50,14 +76,31 @@ export default (element) => {
showCollectionLabel: getDataAttribute(block, 'show-collection-label', 'false') == 'true',
collectionBackgroundColor: getDataAttribute(block, 'collection-background-color'),
collectionTextColor: getDataAttribute(block, 'collection-text-color'),
tainacanApiRoot: getDataAttribute(block, 'tainacan-api-root')
tainacanApiRoot: getDataAttribute(block, 'tainacan-api-root'),
tainacanViewMode: possibleDefaultViewMode,
enabledViewModes: possibleViewModes,
displayedMetadata: JSON.parse(getDataAttribute(block, 'displayed-metadata', '[]')),
});
},
mounted() {
block.classList.add('has-mounted');
}
});
// Logic for dynamic importing Tainacan oficial view modes only if they are necessary
possibleViewModes.forEach(viewModeSlug => {
if ( registeredViewModes.indexOf(viewModeSlug) >= 0 )
VueDynamicItemsList.component('view-mode-' + viewModeSlug, defineAsyncComponent(() => import('../faceted-search/theme-search/components/view-mode-' + viewModeSlug + '.vue')));
});
VueDynamicItemsList.use(VTooltip, {
popperTriggers: ['hover'],
themes: {
'taianacan-tooltip': {
'$extend': 'tooltip',
triggers: ['hover', 'focus', 'touch'],
autoHide: true,
html: true,
}
}
});
VueDynamicItemsList.use(I18NPlugin);
VueDynamicItemsList.use(ThumbnailHelperPlugin);
VueDynamicItemsList.use(VueBlurHash);

View File

@ -145,7 +145,7 @@
</span>
</button>
</div>
<template v-if="isLoading">
<template v-if="isLoading && layout !== 'tainacan-view-modes'">
<ul
v-if="layout !== 'mosaic'"
:style="{
@ -186,7 +186,7 @@
</template>
<div v-else>
<ul
v-if="items.length > 0 && layout !== 'mosaic'"
v-if="items.length > 0 && layout !== 'mosaic' && layout !== 'tainacan-view-modes'"
:style="{
gridGap: layout == 'grid' ? ((showName ? gridMargin + 24 : gridMargin) + 'px') : 'inherit',
marginTop: (showSearchBar || showCollectionHeader) ? ((showName ? gridMargin + 24 : gridMargin) + 'px') : '0px'
@ -261,6 +261,25 @@
</li>
</div>
</ul>
<div
v-if="layout === 'tainacan-view-modes'"
class="items-list"
:class="'items-layout-' + layout + (!showName ? ' items-list-without-margin' : '')">
<div
v-if="(!isLoading && !isLoadingMetadata ) && registeredViewModes !== undefined && registeredViewModes[tainacanViewMode] != undefined && registeredViewModes[tainacanViewMode].type == 'template'"
v-html="itemsListTemplate" />
<component
:is="registeredViewModes[tainacanViewMode] != undefined ? registeredViewModes[tainacanViewMode].component : ''"
v-if="registeredViewModes !== undefined && registeredViewModes[tainacanViewMode] != undefined && registeredViewModes[tainacanViewMode].type == 'component'"
:collection-id="collectionId"
:displayed-metadata="displayedMetadataObjects"
:should-hide-items-thumbnail="false"
:items="items"
:is-filters-menu-compressed="true"
:total-items="items.length"
:is-loading="isLoadingMetadata || isLoading"
:enabled-view-modes="enabledViewModes" />
</div>
<div
v-else-if="!isLoading && items.length <= 0"
class="spinner-container">
@ -302,7 +321,10 @@ export default {
showCollectionLabel: Boolean,
collectionBackgroundColor: String,
collectionTextColor: String,
tainacanApiRoot: String
tainacanApiRoot: String,
enabledViewModes: Array,
tainacanViewMode: String,
displayedMetadata: Array
},
data() {
return {
@ -318,7 +340,11 @@ export default {
paged: undefined,
totalItems: 0,
apiRoot: '',
errorMessage: 'No items found.'
errorMessage: 'No items found.',
displayedMetadataObjects: [],
isLoadingMetadata: false,
itemsListTemplate: '',
registeredViewModes: tainacan_blocks.registered_view_modes,
}
},
created() {
@ -335,7 +361,10 @@ export default {
if (this.showCollectionHeader)
this.fetchCollectionForHeader();
this.fetchItems();
if (this.layout == 'tainacan-view-modes' && this.registeredViewModes !== undefined && this.registeredViewModes[this.tainacanViewMode] != undefined && this.registeredViewModes[this.tainacanViewMode]['dynamic_metadata'] == true)
this.fetchMetadata();
else
this.fetchItems();
},
methods: {
wpI18n(string, context) {
@ -374,7 +403,21 @@ export default {
this.localMaxItemsNumber = this.selectedItems.length;
let endpoint = '/collection/' + this.collectionId + '/items?' + qs.stringify({ postin: this.selectedItems, perpage: this.localMaxItemsNumber }) + '&orderby=post__in&fetch_only=title,url,thumbnail';
let endpoint = '/collection/' + this.collectionId + '/items?' + qs.stringify({ postin: this.selectedItems, perpage: this.localMaxItemsNumber }) + '&orderby=post__in';
if (
this.layout == 'tainacan-view-modes' &&
this.registeredViewModes !== undefined &&
this.registeredViewModes[this.tainacanViewMode] != undefined &&
this.registeredViewModes[this.tainacanViewMode]['dynamic_metadata'] == true
) {
if ( this.displayedMetadataObjects.length > 0 )
endpoint += '&fetch_only=title,url,thumbnail&fetch_only_meta=' + this.displayedMetadataObjects.map((aMetadatum) => aMetadatum.id).join(',');
else
endpoint += '&fetch_only=title,description,url,thumbnail';
} else {
endpoint += '&fetch_only=title,url,thumbnail';
}
this.tainacanAxios.get(endpoint, { cancelToken: this.itemsRequestSource.token })
.then(response => {
@ -382,6 +425,9 @@ export default {
for (let item of response.data.items)
this.items.push(item);
if ( response.data.template )
this.itemsListTemplate = response.data.template;
this.isLoading = false;
this.totalItems = response.headers['x-wp-total'];
@ -475,6 +521,22 @@ export default {
});
}
},
fetchMetadata() {
let metadataEndpoint = '/collection/' + this.collectionId + '/metadata/?nopaging=1';
if ( this.displayedMetadata != undefined && this.displayedMetadata.length > 0 )
metadataEndpoint += '&' + qs.stringify({ postin: this.displayedMetadata });
else
metadataEndpoint += '&metakey=display&metavalue=yes';
this.isLoadingMetadata = true;
this.tainacanAxios.get(metadataEndpoint)
.then(response => {
this.displayedMetadataObjects = response.data;
this.fetchItems();
this.isLoadingMetadata = false;
});
},
mosaicPartition(items) {
const partition = _.groupBy(items, (item, i) => {
if (i % 2 == 0)

View File

@ -23,7 +23,7 @@ export const viewModesMixin = {
},
computed: {
queries() {
let currentQueries = JSON.parse(JSON.stringify(this.$route.query));
let currentQueries = this.$route && this.$route.query ? JSON.parse(JSON.stringify(this.$route.query)) : {};
if (currentQueries) {
delete currentQueries['view_mode'];
delete currentQueries['fetch_only'];
@ -81,7 +81,8 @@ export const viewModesMixin = {
// Inserts information necessary for item by item navigation on single pages
this.queries['pos'] = ((this.queries['paged'] - 1) * this.queries['perpage']) + index;
this.queries['source_list'] = this.termId ? 'term' : (!this.collectionId || this.collectionId == 'default' ? 'repository' : 'collection');
this.queries['ref'] = this.$route.path;
if ( this.$route && this.$route.path )
this.queries['ref'] = this.$route.path;
return itemUrl + '?' + qs.stringify(this.queries);
}
return itemUrl;
@ -122,10 +123,11 @@ export const viewModesMixin = {
return metadata.value_as_html;
},
starSlideshowFromHere(index) {
this.$router.replace({ query: {...this.$route.query, ...{'slideshow-from': index } }}).catch((error) => this.$console.log(error));
if (this.$router && this.$route && this.$route.query)
this.$router.replace({ query: {...this.$route.query, ...{'slideshow-from': index } }}).catch((error) => this.$console.log(error));
},
getPosInSet(index) {
if (Number(this.queries.paged) !== NaN && Number(this.queries.perpage) !== NaN)
if ( !isNaN(Number(this.queries.paged)) && !isNaN(Number(this.queries.perpage)) )
return ((Number(this.queries.paged) - 1) * Number(this.queries.perpage)) + index + 1;
}
}

View File

@ -221,7 +221,8 @@ function tainacan_blocks_get_plugin_js_settings(){
'admin_url' => admin_url(),
'site_url' => site_url(),
'theme_items_list_url' => esc_url_raw( get_site_url() ) . '/' . \Tainacan\Theme_Helper::get_instance()->get_items_list_slug(),
'collections_post_types' => $cpts
'collections_post_types' => $cpts,
'registered_view_modes' => \Tainacan\Theme_Helper::get_instance()->get_registered_view_modes(),
];
return $settings;