From a6b1904d020a0ce8d50b1c8a522ec575f4ae97e2 Mon Sep 17 00:00:00 2001 From: mateuswetah Date: Tue, 22 Feb 2022 09:14:06 -0300 Subject: [PATCH] Begins implementation of item gallery block. #674. --- .../blocks/item-gallery/block.json | 39 ++ .../blocks/item-gallery/deprecated.js | 1 + .../blocks/item-gallery/edit.js | 130 +++++++ .../blocks/item-gallery/icon.js | 13 + .../blocks/item-gallery/index.js | 15 + .../item-gallery/related-items-modal.js | 349 ++++++++++++++++++ .../blocks/item-gallery/save.js | 3 + .../class-tainacan-gutenberg-block.php | 22 +- .../js/tainacan-blocks-compat-register.js | 34 +- .../tainacan-blocks-common-scripts.js | 3 +- webpack.common.js | 3 +- 11 files changed, 589 insertions(+), 23 deletions(-) create mode 100644 src/views/gutenberg-blocks/blocks/item-gallery/block.json create mode 100644 src/views/gutenberg-blocks/blocks/item-gallery/deprecated.js create mode 100644 src/views/gutenberg-blocks/blocks/item-gallery/edit.js create mode 100644 src/views/gutenberg-blocks/blocks/item-gallery/icon.js create mode 100644 src/views/gutenberg-blocks/blocks/item-gallery/index.js create mode 100644 src/views/gutenberg-blocks/blocks/item-gallery/related-items-modal.js create mode 100644 src/views/gutenberg-blocks/blocks/item-gallery/save.js diff --git a/src/views/gutenberg-blocks/blocks/item-gallery/block.json b/src/views/gutenberg-blocks/blocks/item-gallery/block.json new file mode 100644 index 000000000..c737545d8 --- /dev/null +++ b/src/views/gutenberg-blocks/blocks/item-gallery/block.json @@ -0,0 +1,39 @@ +{ + "$schema": "https://schemas.wp.org/trunk/block.json", + "name": "tainacan/item-gallery", + "title": "Tainacan Item Gallery", + "category": "tainacan-blocks", + "keywords": [ "item", "document", "carousel", "attachments", "zoom" ], + "description": "The media gallery of the item, displaying document and attachments.", + "textdomain": "tainacan", + "example": { + "attributes": { + "content": "preview" + } + }, + "attributes": { + "content": { + "type": "array", + "source": "query", + "selector": "div" + }, + "collectionId": { + "type": "string", + "default": "" + }, + "itemId": { + "type": "string", + "default": "" + }, + "isModalOpen": { + "type": "boolean", + "default": false + } + }, + "supports": { + "align": ["full", "wide"], + "multiple": true + }, + "editorScript": "item-gallery", + "editorStyle": "item-gallery" +} \ No newline at end of file diff --git a/src/views/gutenberg-blocks/blocks/item-gallery/deprecated.js b/src/views/gutenberg-blocks/blocks/item-gallery/deprecated.js new file mode 100644 index 000000000..109fa8b38 --- /dev/null +++ b/src/views/gutenberg-blocks/blocks/item-gallery/deprecated.js @@ -0,0 +1 @@ +export default []; \ No newline at end of file diff --git a/src/views/gutenberg-blocks/blocks/item-gallery/edit.js b/src/views/gutenberg-blocks/blocks/item-gallery/edit.js new file mode 100644 index 000000000..56c36381a --- /dev/null +++ b/src/views/gutenberg-blocks/blocks/item-gallery/edit.js @@ -0,0 +1,130 @@ +const { __ } = wp.i18n; + +const { Spinner, Button, Placeholder } = wp.components; + +const { InnerBlocks } = (tainacan_blocks.wp_version < '5.2' ? wp.editor : wp.blockEditor ); + +import RelatedItemsModal from './related-items-modal.js'; +import tainacan from '../../js/axios.js'; +import axios from 'axios'; + +export default function ({ attributes, setAttributes, className, isSelected }) { + + let { + content, + collectionId, + itemId, + isModalOpen, + isLoading, + } = attributes; + + function setContent(){ + } + + function openRelatedItemsModal() { + isModalOpen = true; + setAttributes( { + isModalOpen: isModalOpen + } ); + } + + // Executed only on the first load of page + if(content && content.length && content[0].type) + setContent(); + + return content == 'preview' ? +
+ +
+ : ( +
+ + { isSelected ? + ( +
+ { isModalOpen ? + { + // if (collectionId != selectedCollectionId) + // relatedItems = []; + + collectionId = selectedCollectionId; + setAttributes({ + collectionId: collectionId, + // relatedItems: relatedItems + }); + }} + onApplyRelatedItem={ (selectedItemId) => { + // if (itemId != selectedItemId) { + // relatedItems = []; + // relatedItemsTemplate = []; + // } + + itemId = selectedItemId; + setAttributes({ + itemId: itemId, + // relatedItems: relatedItems, + // relatedItemsTemplate: relatedItemsTemplate, + isModalOpen: false + }); + setContent(); + }} + onCancelSelection={ () => setAttributes({ isModalOpen: false }) }/> + : null + } + +
+ ) : null + } + + { !itemId && !isLoading ? ( + + )}> +

+ + + + {__('Select an item to create a set of lists with items related to it via relationship metadata.', 'tainacan')} +

+ +
+ ) : null + } + + { isLoading ? +
+ +
: +
+ { itemId ? ( + +
+ +
+ ) : null + } +
+ } + +
+ ); +}; \ No newline at end of file diff --git a/src/views/gutenberg-blocks/blocks/item-gallery/icon.js b/src/views/gutenberg-blocks/blocks/item-gallery/icon.js new file mode 100644 index 000000000..da9503476 --- /dev/null +++ b/src/views/gutenberg-blocks/blocks/item-gallery/icon.js @@ -0,0 +1,13 @@ + const { SVG, Path } = wp.components; + + export default ( + + + + ); + \ No newline at end of file diff --git a/src/views/gutenberg-blocks/blocks/item-gallery/index.js b/src/views/gutenberg-blocks/blocks/item-gallery/index.js new file mode 100644 index 000000000..956c07742 --- /dev/null +++ b/src/views/gutenberg-blocks/blocks/item-gallery/index.js @@ -0,0 +1,15 @@ +import tainacanRegisterBlockType from '../../js/tainacan-blocks-compat-register.js'; + +import metadata from './block.json'; +import icon from './icon.js'; +import edit from './edit.js'; +import save from './save.js'; +import deprecated from './deprecated.js'; + +tainacanRegisterBlockType({ + metadata, + icon, + edit, + save, + deprecated +}); diff --git a/src/views/gutenberg-blocks/blocks/item-gallery/related-items-modal.js b/src/views/gutenberg-blocks/blocks/item-gallery/related-items-modal.js new file mode 100644 index 000000000..81118188d --- /dev/null +++ b/src/views/gutenberg-blocks/blocks/item-gallery/related-items-modal.js @@ -0,0 +1,349 @@ +import tainacan from '../../js/axios.js'; +import axios from 'axios'; + +const { __ } = wp.i18n; + +const { TextControl, Button, Modal, RadioControl, SelectControl, Spinner } = wp.components; +const currentWPVersion = (typeof tainacan_blocks != 'undefined') ? tainacan_blocks.wp_version : tainacan_plugin.wp_version; + +export default class RelatedItemsModal extends React.Component { + constructor(props) { + super(props); + + // Initialize state + this.state = { + collectionsPerPage: 24, + collectionId: undefined, + itemId: undefined, + collectionName: '', + isLoadingCollections: false, + modalCollections: [], + totalModalCollections: 0, + collectionOrderBy: 'date-desc', + collectionPage: 1, + temporaryCollectionId: '', + temporaryItemId: '', + searchCollectionName: '', + collections: [], + collectionsRequestSource: undefined, + searchURL: '', + itemsPerPage: 12 + }; + + // 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.applyRelatedItem = this.applyRelatedItem.bind(this); + } + + componentWillMount() { + + this.setState({ + collectionId: this.props.existingCollectionId, + itemId: this.props.existingItemId + }); + + if (this.props.existingCollectionId) { + this.fetchCollection(this.props.existingCollectionId); + this.setState({ + searchURL: tainacan_blocks.admin_url + 'admin.php?itemsSingleSelectionMode=true&page=tainacan_admin#/collections/'+ this.props.existingCollectionId + '/items/?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/?perpage=' + this.state.collectionsPerPage + '&paged=' + this.state.collectionPage; + + if (this.state.collectionOrderBy == 'date') + endpoint += '&orderby=date&order=asc'; + else if (this.state.collectionOrderBy == 'date-desc') + endpoint += '&orderby=date&order=desc'; + else if (this.state.collectionOrderBy == 'title') + endpoint += '&orderby=title&order=asc'; + else if (this.state.collectionOrderBy == 'title-desc') + endpoint += '&orderby=title&order=desc'; + + 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?itemsSingleSelectionMode=true&page=tainacan_admin#/collections/' + selectedCollectionId + '/items/?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/?perpage=' + this.state.collectionsPerPage; + if (name != undefined && name != '') + endpoint += '&search=' + name; + + if (this.state.collectionOrderBy == 'date') + endpoint += '&orderby=date&order=asc'; + else if (this.state.collectionOrderBy == 'date-desc') + endpoint += '&orderby=date&order=desc'; + else if (this.state.collectionOrderBy == 'title') + endpoint += '&orderby=title&order=asc'; + else if (this.state.collectionOrderBy == 'title-desc') + endpoint += '&orderby=title&order=desc'; + + 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); + }); + } + + applyRelatedItem() { + let iframe = document.getElementById("itemsFrame"); + if (iframe) { + let params = new URLSearchParams(iframe.contentWindow.location.search); + let selectedItems = params.getAll('selecteditems'); + params.delete('selecteditems') + this.props.onApplyRelatedItem(selectedItems[0]); + } + } + + resetCollections() { + + this.setState({ + collectionId: null, + collectionPage: 1, + modalCollections: [] + }); + this.fetchModalCollections(); + } + + cancelSelection() { + + this.setState({ + modalCollections: [] + }); + + this.props.onCancelSelection(); + } + + render() { + return this.state.collectionId ? ( + // Items modal + this.cancelSelection() } + shouldCloseOnClickOutside={ false } + contentLabel={ __('Select one item that has relations', 'tainacan') }> +