Adds selection mode to dynamic items block #592.

This commit is contained in:
mateuswetah 2021-08-25 16:07:11 -03:00
parent 65e510bd36
commit 8b37b75254
9 changed files with 529 additions and 410 deletions

View File

@ -191,7 +191,8 @@
box-shadow: none; } box-shadow: none; }
.wp-block-tainacan-dynamic-items-list ul.items-list-edit li.item-list-item { .wp-block-tainacan-dynamic-items-list ul.items-list-edit li.item-list-item {
display: flex; display: flex;
align-items: flex-start; } align-items: flex-start;
position: relative; }
.wp-block-tainacan-dynamic-items-list ul.items-list-edit li.item-list-item button { .wp-block-tainacan-dynamic-items-list ul.items-list-edit li.item-list-item button {
position: absolute !important; position: absolute !important;
background-color: rgba(255, 255, 255, 0.75); background-color: rgba(255, 255, 255, 0.75);

File diff suppressed because one or more lines are too long

View File

@ -47,7 +47,8 @@ export default class CarouselItemsModal extends React.Component {
if (this.props.existingCollectionId) { if (this.props.existingCollectionId) {
this.fetchCollection(this.props.existingCollectionId); this.fetchCollection(this.props.existingCollectionId);
this.setState({ this.setState({
searchURL: this.props.existingSearchURL ? this.props.existingSearchURL : tainacan_blocks.admin_url + 'admin.php?page=tainacan_admin#/collections/'+ this.props.existingCollectionId + (this.props.loadStrategy == 'search' ? '/items/?iframemode=true&readmode=true&status=publish' : '/items/?iframemode=true&status=publish') }); searchURL: this.props.existingSearchURL ? this.props.existingSearchURL : tainacan_blocks.admin_url + 'admin.php?page=tainacan_admin#/collections/'+ this.props.existingCollectionId + (this.props.loadStrategy == 'search' ? '/items/?iframemode=true&readmode=true&status=publish' : '/items/?iframemode=true&status=publish')
});
} else { } else {
this.setState({ collectionPage: 1 }); this.setState({ collectionPage: 1 });
this.fetchModalCollections(); this.fetchModalCollections();

View File

@ -4,7 +4,7 @@ const { RangeControl, Spinner, Button, ToggleControl, SelectControl, Placeholder
const { InspectorControls, BlockControls, useBlockProps } = (tainacan_blocks.wp_version < '5.2' ? wp.editor : wp.blockEditor ); const { InspectorControls, BlockControls, useBlockProps } = (tainacan_blocks.wp_version < '5.2' ? wp.editor : wp.blockEditor );
import CarouselItemsModal from './carousel-items-modal.js'; import CarouselItemsModal from './dynamic-and-carousel-items-modal.js';
import tainacan from '../../js/axios.js'; import tainacan from '../../js/axios.js';
import axios from 'axios'; import axios from 'axios';
import qs from 'qs'; import qs from 'qs';

View File

@ -85,6 +85,14 @@
"type": "String", "type": "String",
"default": "" "default": ""
}, },
"selectedItems": {
"type": "Array",
"default": []
},
"loadStrategy": {
"type": "String",
"value": "search"
},
"order": { "order": {
"type": "String", "type": "String",
"default": "" "default": ""

View File

@ -1,4 +1,206 @@
const { useBlockProps } = (tainacan_blocks.wp_version < '5.2' ? wp.editor : wp.blockEditor );
export default [ export default [
/* Deprecated when new selection strategy was added */
{
"attributes": {
"content": {
"type": "Array",
"source": "children",
"selector": "div"
},
"collectionId": {
"type": "String",
"default": ""
},
"items": {
"type": "Array",
"default": []
},
"showImage": {
"type": "Boolean",
"default": true
},
"showName": {
"type": "Boolean",
"default": true
},
"layout": {
"type": "String",
"default": "grid"
},
"isModalOpen": {
"type": "Boolean",
"default": false
},
"gridMargin": {
"type": "Number",
"default": 0
},
"searchURL": {
"type": "String",
"default": ""
},
"itemsRequestSource": {
"type": "String",
"default": ""
},
"maxItemsNumber": {
"type": "Number",
"value": 12
},
"isLoading": {
"type": "Boolean",
"value": false
},
"isLoadingCollection": {
"type": "Boolean",
"value": false
},
"showSearchBar": {
"type": "Boolean",
"value": false
},
"showCollectionHeader": {
"type": "Boolean",
"value": false
},
"showCollectionLabel": {
"type": "Boolean",
"value": false
},
"collection": {
"type": "Object",
"value": {}
},
"searchString": {
"type": "String",
"default": ""
},
"order": {
"type": "String",
"default": ""
},
"blockId": {
"type": "String",
"default": ""
},
"collectionBackgroundColor": {
"type": "String",
"default": "#454647"
},
"collectionTextColor": {
"type": "String",
"default": "#ffffff"
},
"mosaicHeight": {
"type": "Number",
"value": 280
},
"mosaicGridColumns": {
"type": "Number",
"value": 3
},
"mosaicGridRows": {
"type": "Number",
"value": 3
},
"sampleBackgroundImage": {
"type": "String",
"default": ""
},
"mosaicItemFocalPoint": {
"type": "Object",
"default": {
"x": 0.5,
"y": 0.5
}
},
"mosaicDensity": {
"type": "Number",
"default": 5
},
"maxColumnsCount": {
"type": "Number",
"default": 4
},
"cropImagesToSquare": {
"type": "Boolean",
"value": true
}
},
"supports": {
"align": ["full", "wide"],
"html": false,
"typography": {
"fontSize": true
},
"color": {
"text": true,
"background": false,
"gradients": false,
"link": true
}
},
save: function({ attributes, className }) {
const {
content,
blockId,
collectionId,
showImage,
showName,
layout,
gridMargin,
searchURL,
maxItemsNumber,
order,
showSearchBar,
showCollectionHeader,
showCollectionLabel,
collectionBackgroundColor,
collectionTextColor,
mosaicHeight,
mosaicGridRows,
mosaicGridColumns,
mosaicItemFocalPoint,
mosaicDensity,
maxColumnsCount,
cropImagesToSquare
} = attributes;
// Gets attributes such as style, that are automatically added by the editor hook
const blockProps = tainacan_blocks.wp_version < '5.6' ? { className: className } : useBlockProps.save();
return <div
{ ...blockProps }
data-module="dynamic-items-list"
search-url={ searchURL }
collection-id={ collectionId }
show-image={ '' + showImage }
show-name={ '' + showName }
show-search-bar={ '' + showSearchBar }
show-collection-header={ '' + showCollectionHeader }
show-collection-label={ '' + showCollectionLabel }
crop-images-to-square={ '' + cropImagesToSquare }
layout={ layout }
mosaic-height={ mosaicHeight }
mosaic-density={ mosaicDensity }
mosaic-grid-rows={ mosaicGridRows }
mosaic-grid-columns={ mosaicGridColumns }
mosaic-item-focal-point-x={ (mosaicItemFocalPoint && mosaicItemFocalPoint.x ? mosaicItemFocalPoint.x : 0.5) }
mosaic-item-focal-point-y={ (mosaicItemFocalPoint && mosaicItemFocalPoint.y ? mosaicItemFocalPoint.y : 0.5) }
max-columns-count={ maxColumnsCount }
collection-background-color={ collectionBackgroundColor }
collection-text-color={ collectionTextColor }
grid-margin={ gridMargin }
max-items-number={ maxItemsNumber }
order={ order }
tainacan-api-root={ tainacan_blocks.root }
tainacan-base-url={ tainacan_blocks.base_url }
id={ 'wp-block-tainacan-dynamic-items-list_' + blockId }>
{ content }
</div>
}
},
/* Deprecated on Tainacan 0.18.4, due to the new block.json strategy */ /* Deprecated on Tainacan 0.18.4, due to the new block.json strategy */
{ {
attributes: { attributes: {

View File

@ -1,293 +0,0 @@
import tainacan from '../../js/axios.js';
import axios from 'axios';
const { __ } = wp.i18n;
const { TextControl, Button, Modal, RadioControl, Spinner } = wp.components;
export default class DynamicItemsModal extends React.Component {
constructor(props) {
super(props);
// Initialize state
this.state = {
collectionsPerPage: 24,
collectionId: undefined,
collectionName: '',
isLoadingCollections: false,
modalCollections: [],
totalModalCollections: 0,
collectionPage: 1,
temporaryCollectionId: '',
searchCollectionName: '',
collections: [],
collectionsRequestSource: undefined,
searchURL: '',
};
// Bind events
this.resetCollections = this.resetCollections.bind(this);
this.selectCollection = this.selectCollection.bind(this);
this.fetchCollections = this.fetchCollections.bind(this);
this.fetchModalCollections = this.fetchModalCollections.bind(this);
this.fetchCollection = this.fetchCollection.bind(this);
this.applySelectedSearchURL = this.applySelectedSearchURL.bind(this);
}
componentWillMount() {
this.setState({
collectionId: this.props.existingCollectionId
});
if (this.props.existingCollectionId) {
this.fetchCollection(this.props.existingCollectionId);
this.setState({ searchURL: this.props.existingSearchURL ? this.props.existingSearchURL : tainacan_blocks.admin_url + 'admin.php?page=tainacan_admin#/collections/'+ this.props.existingCollectionId + '/items/?readmode=true&iframemode=true&status=publish' });
} else {
this.setState({ collectionPage: 1 });
this.fetchModalCollections();
}
}
// COLLECTIONS RELATED --------------------------------------------------
fetchModalCollections() {
let someModalCollections = this.state.modalCollections;
if (this.state.collectionPage <= 1)
someModalCollections = [];
let endpoint = '/collections/?orderby=title&order=asc&perpage=' + this.state.collectionsPerPage + '&paged=' + this.state.collectionPage;
this.setState({
isLoadingCollections: true,
collectionPage: this.state.collectionPage + 1,
modalCollections: someModalCollections
});
tainacan.get(endpoint)
.then(response => {
let otherModalCollections = this.state.modalCollections;
for (let collection of response.data) {
otherModalCollections.push({
name: collection.name,
id: collection.id
});
}
this.setState({
isLoadingCollections: false,
modalCollections: otherModalCollections,
totalModalCollections: response.headers['x-wp-total']
});
return otherModalCollections;
})
.catch(error => {
console.log('Error trying to fetch collections: ' + error);
});
}
fetchCollection(collectionId) {
tainacan.get('/collections/' + collectionId)
.then((response) => {
this.setState({ collectionName: response.data.name });
}).catch(error => {
console.log('Error trying to fetch collection: ' + error);
});
}
selectCollection(selectedCollectionId) {
this.setState({
collectionId: selectedCollectionId,
searchURL: tainacan_blocks.admin_url + 'admin.php?page=tainacan_admin#/collections/' + selectedCollectionId + '/items/?readmode=true&iframemode=true&status=publish'
});
this.props.onSelectCollection(selectedCollectionId);
this.fetchCollection(selectedCollectionId);
}
fetchCollections(name) {
if (this.state.collectionsRequestSource != undefined)
this.state.collectionsRequestSource.cancel('Previous collections search canceled.');
let aCollectionRequestSource = axios.CancelToken.source();
this.setState({
collectionsRequestSource: aCollectionRequestSource,
isLoadingCollections: true,
collections: [],
items: []
});
let endpoint = '/collections/?orderby=title&order=asc&perpage=' + this.state.collectionsPerPage;
if (name != undefined && name != '')
endpoint += '&search=' + name;
tainacan.get(endpoint, { cancelToken: aCollectionRequestSource.token })
.then(response => {
let someCollections = response.data.map((collection) => ({ name: collection.name, id: collection.id + '' }));
this.setState({
isLoadingCollections: false,
collections: someCollections
});
return someCollections;
})
.catch(error => {
console.log('Error trying to fetch collections: ' + error);
});
}
applySelectedSearchURL() {
this.props.onApplySearchURL(document.getElementById("itemsFrame").contentWindow.location.href);
}
resetCollections() {
this.setState({
collectionId: null,
collectionPage: 1,
modalCollections: []
});
this.fetchModalCollections();
}
cancelSelection() {
this.setState({
modalCollections: []
});
this.props.onCancelSelection();
}
render() {
return this.state.collectionId ? (
// Items modal
<Modal
className="wp-block-tainacan-modal dynamic-modal"
title={__('Configure the items search to be used on block', 'tainacan')}
onRequestClose={ () => this.cancelSelection() }
shouldCloseOnClickOutside={ false }
contentLabel={__('Configure your items search to be shown on block', 'tainacan')}>
<iframe
id="itemsFrame"
src={ this.state.searchURL } />
<div className="modal-footer-area">
<Button
isSecondary
onClick={ () => { this.resetCollections() }}>
{__('Switch collection', 'tainacan')}
</Button>
<Button
isPrimary
onClick={ () => this.applySelectedSearchURL() }>
{__('Use this search', 'tainacan')}
</Button>
</div>
</Modal>
) : (
// Collections modal
<Modal
className="wp-block-tainacan-modal"
title={__('Select a collection to fetch items from', 'tainacan')}
onRequestClose={ () => this.cancelSelection() }
shouldCloseOnClickOutside={ false }
contentLabel={__('Select items', 'tainacan')}>
<div>
<div className="modal-search-area">
<TextControl
label={__('Search for a collection', 'tainacan')}
value={ this.state.searchCollectionName }
onChange={(value) => {
this.setState({
searchCollectionName: value
});
_.debounce(this.fetchCollections(value), 300);
}}/>
</div>
{(
this.state.searchCollectionName != '' ? (
this.state.collections.length > 0 ?
(
<div>
<div className="modal-radio-list">
{
<RadioControl
selected={ this.state.temporaryCollectionId }
options={
this.state.collections.map((collection) => {
return { label: collection.name, value: '' + collection.id }
})
}
onChange={ ( aCollectionId ) => {
this.setState({ temporaryCollectionId: aCollectionId });
} } />
}
</div>
</div>
) :
this.state.isLoadingCollections ? (
<Spinner />
) :
<div className="modal-loadmore-section">
<p>{ __('Sorry, no collection found.', 'tainacan') }</p>
</div>
):
this.state.modalCollections.length > 0 ?
(
<div>
<div className="modal-radio-list">
{
<RadioControl
selected={ this.state.temporaryCollectionId }
options={
this.state.modalCollections.map((collection) => {
return { label: collection.name, value: '' + collection.id }
})
}
onChange={ ( aCollectionId ) => {
this.setState({ temporaryCollectionId: aCollectionId });
} } />
}
</div>
<div className="modal-loadmore-section">
<p>{ __('Showing', 'tainacan') + " " + this.state.modalCollections.length + " " + __('of', 'tainacan') + " " + this.state.totalModalCollections + " " + __('collections', 'tainacan') + "."}</p>
{
this.state.modalCollections.length < this.state.totalModalCollections ? (
<Button
isSecondary
isSmall
onClick={ () => this.fetchModalCollections() }>
{__('Load more', 'tainacan')}
</Button>
) : null
}
</div>
</div>
) : this.state.isLoadingCollections ? <Spinner/> :
<div className="modal-loadmore-section">
<p>{ __('Sorry, no collection found.', 'tainacan') }</p>
</div>
)}
<div className="modal-footer-area">
<Button
isSecondary
onClick={ () => { this.cancelSelection() }}>
{__('Cancel', 'tainacan')}
</Button>
<Button
isPrimary
disabled={ this.state.temporaryCollectionId == undefined || this.state.temporaryCollectionId == null || this.state.temporaryCollectionId == ''}
onClick={ () => { this.selectCollection(this.state.temporaryCollectionId); } }>
{__('Configure search', 'tainacan')}
</Button>
</div>
</div>
</Modal>
);
}
}

View File

@ -4,7 +4,7 @@ const { ResizableBox, FocalPointPicker, SelectControl, RangeControl, Spinner, Bu
const { InspectorControls, BlockControls, useBlockProps } = (tainacan_blocks.wp_version < '5.2' ? wp.editor : wp.blockEditor ); const { InspectorControls, BlockControls, useBlockProps } = (tainacan_blocks.wp_version < '5.2' ? wp.editor : wp.blockEditor );
import DynamicItemsModal from './dynamic-items-modal.js'; import DynamicItemsModal from '../carousel-items-list/dynamic-and-carousel-items-modal.js';
import tainacan from '../../js/axios.js'; import tainacan from '../../js/axios.js';
import axios from 'axios'; import axios from 'axios';
import qs from 'qs'; import qs from 'qs';
@ -27,7 +27,9 @@ export default function({ attributes, setAttributes, className, isSelected, clie
maxItemsNumber, maxItemsNumber,
order, order,
searchString, searchString,
selectedItems,
isLoading, isLoading,
loadStrategy,
showSearchBar, showSearchBar,
showCollectionHeader, showCollectionHeader,
showCollectionLabel, showCollectionLabel,
@ -49,6 +51,7 @@ export default function({ attributes, setAttributes, className, isSelected, clie
// Obtains block's client id to render it on save function // Obtains block's client id to render it on save function
setAttributes({ blockId: clientId }); setAttributes({ blockId: clientId });
const thumbHelper = ThumbnailHelperFunctions();
// Sets some defaults that were not working // Sets some defaults that were not working
if (maxColumnsCount === undefined) { if (maxColumnsCount === undefined) {
@ -59,8 +62,10 @@ export default function({ attributes, setAttributes, className, isSelected, clie
cropImagesToSquare = true; cropImagesToSquare = true;
setAttributes({ cropImagesToSquare: cropImagesToSquare }); setAttributes({ cropImagesToSquare: cropImagesToSquare });
} }
if (loadStrategy === undefined) {
const thumbHelper = ThumbnailHelperFunctions(); loadStrategy = 'search';
setAttributes({ loadStrategy: loadStrategy });
}
function prepareItem(item) { function prepareItem(item) {
return ( return (
@ -72,20 +77,31 @@ export default function({ attributes, setAttributes, className, isSelected, clie
backgroundPosition: layout == 'mosaic' ? `${ (mosaicItemFocalPoint && mosaicItemFocalPoint.x ? mosaicItemFocalPoint.x : 0.5) * 100 }% ${ (mosaicItemFocalPoint && mosaicItemFocalPoint.y ? mosaicItemFocalPoint.y : 0.5) * 100 }%` : 'none' backgroundPosition: layout == 'mosaic' ? `${ (mosaicItemFocalPoint && mosaicItemFocalPoint.x ? mosaicItemFocalPoint.x : 0.5) * 100 }% ${ (mosaicItemFocalPoint && mosaicItemFocalPoint.y ? mosaicItemFocalPoint.y : 0.5) * 100 }%` : 'none'
}} }}
> >
{ loadStrategy == 'selection' ?
( tainacan_blocks.wp_version < '5.4' ?
<IconButton
onClick={ () => removeItemOfId(item.id) }
icon="no-alt"
label={__('Remove', 'tainacan')}/>
:
<Button
onClick={ () => removeItemOfId(item.id) }
icon="no-alt"
label={__('Remove', 'tainacan')}/>
)
:null
}
<a <a
id={ isNaN(item.id) ? item.id : 'item-id-' + item.id } id={ isNaN(item.id) ? item.id : 'item-id-' + item.id }
href={ item.url } href={ item.url }
onClick={ (event) => event.preventDefault() } onClick={ (event) => event.preventDefault() }
target="_blank" target="_blank"
style={ {
} }
className={ (!showName ? 'item-without-title' : '') + ' ' + (!showImage ? 'item-without-image' : '') }> className={ (!showName ? 'item-without-title' : '') + ' ' + (!showImage ? 'item-without-image' : '') }>
<img <img
src={ thumbHelper.getSrc(item['thumbnail'], ( (layout == 'list' || cropImagesToSquare) ? 'tainacan-medium' : 'tainacan-medium-full'), item['document_mimetype']) } src={ thumbHelper.getSrc(item['thumbnail'], ( (layout == 'list' || cropImagesToSquare) ? 'tainacan-medium' : 'tainacan-medium-full'), item['document_mimetype']) }
srcSet={ thumbHelper.getSrcSet(item['thumbnail'], ( (layout == 'list' || cropImagesToSquare) ? 'tainacan-medium' : 'tainacan-medium-full'), item['document_mimetype']) } srcSet={ thumbHelper.getSrcSet(item['thumbnail'], ( (layout == 'list' || cropImagesToSquare) ? 'tainacan-medium' : 'tainacan-medium-full'), item['document_mimetype']) }
alt={ item.thumbnail_alt ? item.thumbnail_alt : (item && item.title ? item.title : __( 'Thumbnail', 'tainacan' )) }/> alt={ item.thumbnail_alt ? item.thumbnail_alt : (item && item.title ? item.title : __( 'Thumbnail', 'tainacan' )) }/>
<span>{ item.title ? item.title : '' }</span> <span>{ item.id ? item.id : '' }</span>
</a> </a>
</li> </li>
); );
@ -110,63 +126,63 @@ export default function({ attributes, setAttributes, className, isSelected, clie
) )
} }
function setContent(){ function setContent() {
isLoading = true;
if (searchURL) { setAttributes({
isLoading: isLoading
});
items = []; items = [];
isLoading = true;
if (loadStrategy == 'parent') {
if (layout !== 'mosaic') {
for (let item of selectedItems)
items.push(prepareItem(item));
setAttributes({
content: <div></div>,
items: items,
isLoading: false
});
} else {
// Initializes some variables
mosaicDensity = mosaicDensity ? mosaicDensity : 5;
mosaicGridRows = mosaicGridRows ? mosaicGridRows : 3;
mosaicGridColumns = mosaicGridColumns ? mosaicGridColumns : 3;
mosaicHeight = mosaicHeight ? mosaicHeight : 280;
mosaicItemFocalPoint = mosaicItemFocalPoint ? mosaicItemFocalPoint : { x: 0.5, y: 0.5 };
sampleBackgroundImage = response.data.items && response.data.items[0] && response.data.items[0] ? getItemThumbnail(response.data.items[0], 'tainacan-medium') : '';
const mosaicGroups = mosaicPartition(response.data.items);
for (let mosaicGroup of mosaicGroups)
items.push(prepareMosaicItem(mosaicGroup, mosaicGroups.length));
setAttributes({
content: <div></div>,
items: items,
isLoading: false,
itemsRequestSource: itemsRequestSource,
mosaicDensity: mosaicDensity,
mosaicHeight: mosaicHeight,
mosaicGridRows: mosaicGridRows,
mosaicGridColumns: mosaicGridColumns,
mosaicItemFocalPoint: mosaicItemFocalPoint,
sampleBackgroundImage: sampleBackgroundImage
});
}
} else if (loadStrategy == 'selection') {
if (itemsRequestSource != undefined && typeof itemsRequestSource == 'function') if (itemsRequestSource != undefined && typeof itemsRequestSource == 'function')
itemsRequestSource.cancel('Previous items search canceled.'); itemsRequestSource.cancel('Previous items search canceled.');
itemsRequestSource = axios.CancelToken.source(); itemsRequestSource = axios.CancelToken.source();
setAttributes({ let endpoint = '/collection/' + collectionId + '/items?'+ qs.stringify({ postin: selectedItems, perpage: selectedItems.length }) + '&fetch_only=title,url,thumbnail';
isLoading: isLoading
});
let endpoint = '/collection' + searchURL.split('#')[1].split('/collections')[1];
let query = endpoint.split('?')[1];
let queryObject = qs.parse(query);
// Set up max items to be shown
if (maxItemsNumber != undefined && maxItemsNumber > 0)
queryObject.perpage = maxItemsNumber;
else if (queryObject.perpage != undefined && queryObject.perpage > 0)
setAttributes({ maxItemsNumber: queryObject.perpage });
else {
queryObject.perpage = 12;
setAttributes({ maxItemsNumber: 12 });
}
// Set up sorting order
if (order != '' && showSearchBar)
queryObject.order = order;
else if (queryObject.order != '')
setAttributes({ order: queryObject.order });
else {
queryObject.order = 'asc';
setAttributes({ order: 'asc' });
}
// Set up sorting order
if (searchString != undefined)
queryObject.search = searchString;
else if (queryObject.search != undefined)
setAttributes({ searchString: queryObject.search });
else {
delete queryObject.search;
setAttributes({ searchString: undefined });
}
// Remove unecessary queries
delete queryObject.readmode;
delete queryObject.iframemode;
delete queryObject.admin_view_mode;
delete queryObject.fetch_only_meta;
endpoint = endpoint.split('?')[0] + '?' + qs.stringify(queryObject) + '&fetch_only=title,url,thumbnail';
tainacan.get(endpoint, { cancelToken: itemsRequestSource.token }) tainacan.get(endpoint, { cancelToken: itemsRequestSource.token })
.then(response => { .then(response => {
@ -209,7 +225,102 @@ export default function({ attributes, setAttributes, className, isSelected, clie
sampleBackgroundImage: sampleBackgroundImage sampleBackgroundImage: sampleBackgroundImage
}); });
} }
}); });
} else {
if (searchURL) {
if (itemsRequestSource != undefined && typeof itemsRequestSource == 'function')
itemsRequestSource.cancel('Previous items search canceled.');
itemsRequestSource = axios.CancelToken.source();
let endpoint = '/collection' + searchURL.split('#')[1].split('/collections')[1];
let query = endpoint.split('?')[1];
let queryObject = qs.parse(query);
// Set up max items to be shown
if (maxItemsNumber != undefined && maxItemsNumber > 0)
queryObject.perpage = maxItemsNumber;
else if (queryObject.perpage != undefined && queryObject.perpage > 0)
setAttributes({ maxItemsNumber: queryObject.perpage });
else {
queryObject.perpage = 12;
setAttributes({ maxItemsNumber: 12 });
}
// Set up sorting order
if (order != '' && showSearchBar)
queryObject.order = order;
else if (queryObject.order != '')
setAttributes({ order: queryObject.order });
else {
queryObject.order = 'asc';
setAttributes({ order: 'asc' });
}
// Set up sorting order
if (searchString != undefined)
queryObject.search = searchString;
else if (queryObject.search != undefined)
setAttributes({ searchString: queryObject.search });
else {
delete queryObject.search;
setAttributes({ searchString: undefined });
}
// Remove unecessary queries
delete queryObject.readmode;
delete queryObject.iframemode;
delete queryObject.admin_view_mode;
delete queryObject.fetch_only_meta;
endpoint = endpoint.split('?')[0] + '?' + qs.stringify(queryObject) + '&fetch_only=title,url,thumbnail';
tainacan.get(endpoint, { cancelToken: itemsRequestSource.token })
.then(response => {
if (layout !== 'mosaic') {
for (let item of response.data.items)
items.push(prepareItem(item));
setAttributes({
content: <div></div>,
items: items,
isLoading: false,
itemsRequestSource: itemsRequestSource
});
} else {
// Initializes some variables
mosaicDensity = mosaicDensity ? mosaicDensity : 5;
mosaicGridRows = mosaicGridRows ? mosaicGridRows : 3;
mosaicGridColumns = mosaicGridColumns ? mosaicGridColumns : 3;
mosaicHeight = mosaicHeight ? mosaicHeight : 280;
mosaicItemFocalPoint = mosaicItemFocalPoint ? mosaicItemFocalPoint : { x: 0.5, y: 0.5 };
sampleBackgroundImage = response.data.items && response.data.items[0] && response.data.items[0] ? getItemThumbnail(response.data.items[0], 'tainacan-medium') : '';
const mosaicGroups = mosaicPartition(response.data.items);
for (let mosaicGroup of mosaicGroups)
items.push(prepareMosaicItem(mosaicGroup, mosaicGroups.length));
setAttributes({
content: <div></div>,
items: items,
isLoading: false,
itemsRequestSource: itemsRequestSource,
mosaicDensity: mosaicDensity,
mosaicHeight: mosaicHeight,
mosaicGridRows: mosaicGridRows,
mosaicGridColumns: mosaicGridColumns,
mosaicItemFocalPoint: mosaicItemFocalPoint,
sampleBackgroundImage: sampleBackgroundImage
});
}
});
}
} }
} }
@ -261,13 +372,46 @@ export default function({ attributes, setAttributes, className, isSelected, clie
) )
} }
function openDynamicItemsModal() { function openDynamicItemsModal(aLoadStrategy) {
loadStrategy = aLoadStrategy;
isModalOpen = true; isModalOpen = true;
setAttributes( { setAttributes( {
isModalOpen: isModalOpen isModalOpen: isModalOpen,
loadStrategy: loadStrategy
} ); } );
} }
function removeItemOfId(itemId) {
let existingItemIndex = -1;
let existingSelectedItemIndex = selectedItems.findIndex((existingSelectedItem) => existingSelectedItem == itemId);
if (existingSelectedItemIndex >= 0)
selectedItems.splice(existingSelectedItemIndex, 1);
if (layout == 'mosaic') {
existingItemIndex = items.findIndex((existingItem) => existingItem.key == itemId);
setAttributes({
selectedItems: selectedItems,
content: <div></div>
});
// In the case of the mosaic layout, we need to re-render as the items array is organized in groups.
setContent();
} else {
existingItemIndex = items.findIndex((existingItem) => existingItem.key == itemId);
if (existingItemIndex >= 0)
items.splice(existingItemIndex, 1);
setAttributes({
selectedItems: selectedItems,
items: items,
content: <div></div>
});
}
}
function updateLayout(newLayout) { function updateLayout(newLayout) {
layout = newLayout; layout = newLayout;
@ -355,24 +499,41 @@ export default function({ attributes, setAttributes, className, isSelected, clie
: ( : (
<div { ...blockProps }> <div { ...blockProps }>
<div> { items.length ?
<BlockControls> <BlockControls>
{ TainacanBlocksCompatToolbar({ controls: layoutControls }) } { TainacanBlocksCompatToolbar({ controls: layoutControls }) }
{ items.length ? { loadStrategy != 'parent' ?
TainacanBlocksCompatToolbar({ (
label: __('Configure search', 'tainacan'), loadStrategy == 'selection' ?
icon: <svg TainacanBlocksCompatToolbar({
xmlns="http://www.w3.org/2000/svg" label: __('Add more items', 'tainacan'),
viewBox="0 -2 24 24" icon: <svg
height="24px" xmlns="http://www.w3.org/2000/svg"
width="24px"> viewBox="0 -2 24 24"
<path d="M14,2V4H7v7.24A5.33,5.33,0,0,0,5.5,11a4.07,4.07,0,0,0-.5,0V4A2,2,0,0,1,7,2Zm7,10v8a2,2,0,0,1-2,2H12l1-1-2.41-2.41A5.56,5.56,0,0,0,11,16.53a5.48,5.48,0,0,0-2-4.24V8a2,2,0,0,1,2-2h4Zm-2.52,0L14,7.5V12ZM11,21l-1,1L8.86,20.89,8,20H8l-.57-.57A3.42,3.42,0,0,1,5.5,20a3.5,3.5,0,0,1-.5-7,2.74,2.74,0,0,1,.5,0,3.41,3.41,0,0,1,1.5.34,3.5,3.5,0,0,1,2,3.16,3.42,3.42,0,0,1-.58,1.92L9,19H9l.85.85Zm-4-4.5A1.5,1.5,0,0,0,5.5,15a1.39,1.39,0,0,0-.5.09A1.5,1.5,0,0,0,5.5,18a1.48,1.48,0,0,0,1.42-1A1.5,1.5,0,0,0,7,16.53Z"/> height="24px"
</svg>, width="24px">
onClick: openDynamicItemsModal <path d="M14,2V4H7v7.24A5.33,5.33,0,0,0,5.5,11a4.07,4.07,0,0,0-.5,0V4A2,2,0,0,1,7,2Zm7,10v8a2,2,0,0,1-2,2H12l1-1-2.41-2.41A5.56,5.56,0,0,0,11,16.53a5.48,5.48,0,0,0-2-4.24V8a2,2,0,0,1,2-2h4Zm-2.52,0L14,7.5V12ZM11,21l-1,1L8.86,20.89,8,20H8l-.57-.57A3.42,3.42,0,0,1,5.5,20a3.5,3.5,0,0,1-.5-7,2.74,2.74,0,0,1,.5,0,3.41,3.41,0,0,1,1.5.34,3.5,3.5,0,0,1,2,3.16,3.42,3.42,0,0,1-.58,1.92L9,19H9l.85.85Zm-4-4.5A1.5,1.5,0,0,0,5.5,15a1.39,1.39,0,0,0-.5.09A1.5,1.5,0,0,0,5.5,18a1.48,1.48,0,0,0,1.42-1A1.5,1.5,0,0,0,7,16.53Z"/>
}) </svg>,
: null } onClick: openDynamicItemsModal,
onClickParams: 'selection'
})
:
TainacanBlocksCompatToolbar({
label: __('Configure a search', 'tainacan'),
icon: <svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 -2 24 24"
height="24px"
width="24px">
<path d="M14,2V4H7v7.24A5.33,5.33,0,0,0,5.5,11a4.07,4.07,0,0,0-.5,0V4A2,2,0,0,1,7,2Zm7,10v8a2,2,0,0,1-2,2H12l1-1-2.41-2.41A5.56,5.56,0,0,0,11,16.53a5.48,5.48,0,0,0-2-4.24V8a2,2,0,0,1,2-2h4Zm-2.52,0L14,7.5V12ZM11,21l-1,1L8.86,20.89,8,20H8l-.57-.57A3.42,3.42,0,0,1,5.5,20a3.5,3.5,0,0,1-.5-7,2.74,2.74,0,0,1,.5,0,3.41,3.41,0,0,1,1.5.34,3.5,3.5,0,0,1,2,3.16,3.42,3.42,0,0,1-.58,1.92L9,19H9l.85.85Zm-4-4.5A1.5,1.5,0,0,0,5.5,15a1.39,1.39,0,0,0-.5.09A1.5,1.5,0,0,0,5.5,18a1.48,1.48,0,0,0,1.42-1A1.5,1.5,0,0,0,7,16.53Z"/>
</svg>,
onClick: openDynamicItemsModal,
onClickParams: 'search'
})
) : null
}
</BlockControls> </BlockControls>
</div> : null }
<div> <div>
<InspectorControls> <InspectorControls>
@ -435,39 +596,43 @@ export default function({ attributes, setAttributes, className, isSelected, clie
: null : null
} }
</PanelBody> </PanelBody>
<PanelBody { loadStrategy == 'search' ?
title={__('Search bar', 'tainacan')} <PanelBody
initialOpen={ true } title={__('Search bar', 'tainacan')}
> initialOpen={ true }
<ToggleControl >
label={__('Display bar', 'tainacan')} <ToggleControl
help={ showSearchBar ? __('Toggle to show search bar on block', 'tainacan') : __('Do not show search bar', 'tainacan')} label={__('Display bar', 'tainacan')}
checked={ showSearchBar } help={ showSearchBar ? __('Toggle to show search bar on block', 'tainacan') : __('Do not show search bar', 'tainacan')}
onChange={ ( isChecked ) => { checked={ showSearchBar }
showSearchBar = isChecked; onChange={ ( isChecked ) => {
setAttributes({ showSearchBar: showSearchBar }); showSearchBar = isChecked;
setAttributes({ showSearchBar: showSearchBar });
}
} }
} />
/> </PanelBody>
</PanelBody> : null }
<PanelBody <PanelBody
title={__('Items', 'tainacan')} title={__('Items', 'tainacan')}
initialOpen={ true } initialOpen={ true }
> >
<div> { loadStrategy == 'search' ?
<RangeControl <div>
label={__('Maximum number of items', 'tainacan')} <RangeControl
value={ maxItemsNumber ? maxItemsNumber : 12 } label={__('Maximum number of items', 'tainacan')}
onChange={ ( aMaxItemsNumber ) => { value={ maxItemsNumber ? maxItemsNumber : 12 }
maxItemsNumber = aMaxItemsNumber; onChange={ ( aMaxItemsNumber ) => {
setAttributes( { maxItemsNumber: aMaxItemsNumber } ) maxItemsNumber = aMaxItemsNumber;
setContent(); setAttributes( { maxItemsNumber: aMaxItemsNumber } )
}} setContent();
min={ 1 } }}
max={ 96 } min={ 1 }
/> max={ 96 }
/>
<hr></hr>
</div> </div>
<hr></hr> : null }
<div> <div>
{ layout == 'list' ? { layout == 'list' ?
<div style={{ marginTop: '16px'}}> <div style={{ marginTop: '16px'}}>
@ -594,6 +759,7 @@ export default function({ attributes, setAttributes, className, isSelected, clie
{ label: '4 x 3', value: '4x3' }, { label: '4 x 3', value: '4x3' },
{ label: '4 x 5', value: '4x5' }, { label: '4 x 5', value: '4x5' },
{ label: '5 x 4', value: '5x4' }, { label: '5 x 4', value: '5x4' },
{ label: '6 x 5', value: '6x5' },
] } ] }
onChange={ ( aGrid ) => { onChange={ ( aGrid ) => {
mosaicGridRows = aGrid.split('x')[0]; mosaicGridRows = aGrid.split('x')[0];
@ -628,21 +794,41 @@ export default function({ attributes, setAttributes, className, isSelected, clie
<div> <div>
{ isModalOpen ? { isModalOpen ?
<DynamicItemsModal <DynamicItemsModal
loadStrategy={ loadStrategy }
existingCollectionId={ collectionId } existingCollectionId={ collectionId }
existingSearchURL={ searchURL } existingSearchURL={ searchURL }
onSelectCollection={ (selectedCollectionId) => { onSelectCollection={ (selectedCollectionId) => {
if (collectionId != selectedCollectionId) {
items = [];
selectedItems = [];
}
collectionId = selectedCollectionId; collectionId = selectedCollectionId;
setAttributes({ collectionId: collectionId }); setAttributes({
collectionId: collectionId,
items: items,
selectedItems: selectedItems
});
fetchCollectionForHeader(); fetchCollectionForHeader();
}} }}
onApplySearchURL={ (aSearchURL) =>{ onApplySearchURL={ (aSearchURL) =>{
searchURL = aSearchURL searchURL = aSearchURL;
loadStrategy = 'search';
setAttributes({ setAttributes({
searchURL: searchURL, searchURL: searchURL,
isModalOpen: false isModalOpen: false
}); });
setContent(); setContent();
}} }}
onApplySelectedItems={ (aSelectionOfItems) => {
selectedItems = selectedItems.concat(aSelectionOfItems);
loadStrategy = 'selection';
setAttributes({
selectedItems: selectedItems,
loadStrategy: loadStrategy,
isModalOpen: false
});
setContent();
}}
onCancelSelection={ () => setAttributes({ isModalOpen: false }) }/> onCancelSelection={ () => setAttributes({ isModalOpen: false }) }/>
: null : null
} }
@ -809,12 +995,25 @@ export default function({ attributes, setAttributes, className, isSelected, clie
</svg> </svg>
{__('Dynamically list items from a Tainacan items search', 'tainacan')} {__('Dynamically list items from a Tainacan items search', 'tainacan')}
</p> </p>
<Button {
isPrimary loadStrategy != 'parent' ?
type="button" <div>
onClick={ () => openDynamicItemsModal() }> <Button
{__('Configure search', 'tainacan')} isPrimary
</Button> type="button"
onClick={ () => openDynamicItemsModal('selection') }>
{__('Select Items', 'tainacan')}
</Button>
<p style={{ margin: '0 12px' }}>{__('or', 'tainacan')}</p>
<Button
isPrimary
type="button"
onClick={ () => openDynamicItemsModal('search') }>
{__('Configure a search', 'tainacan')}
</Button>
</div>
: null
}
</Placeholder> </Placeholder>
) : null ) : null
} }

View File

@ -237,6 +237,7 @@
ul.items-list-edit li.item-list-item { ul.items-list-edit li.item-list-item {
display: flex; display: flex;
align-items: flex-start; align-items: flex-start;
position: relative;
button { button {
position: absolute !important; position: absolute !important;