Begins implementation of item gallery block. #674.

This commit is contained in:
mateuswetah 2022-02-22 09:14:06 -03:00
parent f8541311bf
commit a6b1904d02
11 changed files with 589 additions and 23 deletions

View File

@ -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"
}

View File

@ -0,0 +1 @@
export default [];

View File

@ -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' ?
<div className={className}>
<img
width="100%"
src={ `${tainacan_blocks.base_url}/assets/images/related-carousel-items.png` } />
</div>
: (
<div className={className}>
{ isSelected ?
(
<div>
{ isModalOpen ?
<RelatedItemsModal
existingCollectionId={ collectionId }
existingItemId={ itemId }
onSelectCollection={ (selectedCollectionId) => {
// 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
}
</div>
) : null
}
{ !itemId && !isLoading ? (
<Placeholder
className="tainacan-block-placeholder"
icon={(
<img
width={148}
src={ `${tainacan_blocks.base_url}/assets/images/tainacan_logo_header.svg` }
alt="Tainacan Logo"/>
)}>
<p>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
height="24px"
width="24px">
<path d="M16,6H12a2,2,0,0,0-2,2v6.52A6,6,0,0,1,12,19a6,6,0,0,1-.73,2.88A1.92,1.92,0,0,0,12,22h8a2,2,0,0,0,2-2V12Zm-1,6V7.5L19.51,12ZM15,2V4H8v9.33A5.8,5.8,0,0,0,6,13V4A2,2,0,0,1,8,2ZM10.09,19.05,7,22.11V16.05L8,17l2,2ZM5,16.05v6.06L2,19.11Z"/>
</svg>
{__('Select an item to create a set of lists with items related to it via relationship metadata.', 'tainacan')}
</p>
<Button
isPrimary
type="button"
onClick={ () => openRelatedItemsModal() }>
{__('Select Item', 'tainacan')}
</Button>
</Placeholder>
) : null
}
{ isLoading ?
<div class="spinner-container">
<Spinner />
</div> :
<div>
{ itemId ? (
<div className={ 'item-gallery-edit-container' }>
</div>
) : null
}
</div>
}
</div>
);
};

View File

@ -0,0 +1,13 @@
const { SVG, Path } = wp.components;
export default (
<SVG
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
height="24px"
width="24px">
<Path
d="M16,6H12a2,2,0,0,0-2,2v6.52A6,6,0,0,1,12,19a6,6,0,0,1-.73,2.88A1.92,1.92,0,0,0,12,22h8a2,2,0,0,0,2-2V12Zm-1,6V7.5L19.51,12ZM15,2V4H8v9.33A5.8,5.8,0,0,0,6,13V4A2,2,0,0,1,8,2ZM10.09,19.05,7,22.11V16.05L8,17l2,2ZM5,16.05v6.06L2,19.11Z"/>
</SVG>
);

View File

@ -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
});

View File

@ -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
<Modal
className={ 'wp-block-tainacan-modal dynamic-modal ' + (currentWPVersion < 5.9 ? 'wp-version-smaller-than-5-9' : '') }
title={ __('Select one item that has relations', 'tainacan') }
onRequestClose={ () => this.cancelSelection() }
shouldCloseOnClickOutside={ false }
contentLabel={ __('Select one item that has relations', 'tainacan') }>
<iframe
id="itemsFrame"
src={ this.state.searchURL } />
<div className="modal-footer-area">
<Button
isSecondary
onClick={ () => { this.resetCollections() }}>
{__('Switch collection', 'tainacan')}
</Button>
<Button
style={{ marginLeft: 'auto' }}
isPrimary
onClick={ () => this.applyRelatedItem() }>
{__('Get relations of this item', '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 item', 'tainacan')}>
<div>
<div className="modal-search-area">
<TextControl
label={__('Search for a collection', 'tainacan')}
placeholder={ __('Search by collection\'s name', 'tainacan') }
value={ this.state.searchCollectionName }
onChange={(value) => {
this.setState({
searchCollectionName: value
});
_.debounce(this.fetchCollections(value), 300);
}}/>
<SelectControl
label={__('Order by', 'tainacan')}
value={ this.state.collectionOrderBy }
options={ [
{ label: __('Latest', 'tainacan'), value: 'date-desc' },
{ label: __('Oldest', 'tainacan'), value: 'date' },
{ label: __('Name (A-Z)', 'tainacan'), value: 'title' },
{ label: __('Name (Z-A)', 'tainacan'), value: 'title-desc' }
] }
onChange={ ( aCollectionOrderBy ) => {
this.state.collectionOrderBy = aCollectionOrderBy;
this.state.collectionPage = 1;
this.setState({
collectionOrderBy: this.state.collectionOrderBy,
collectionPage: this.state.collectionPage
});
if (this.state.searchCollectionName && this.state.searchCollectionName != '') {
this.fetchCollections(this.state.searchCollectionName);
} else {
this.fetchModalCollections();
}
}}/>
</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); } }>
{ __('Select item', 'tainacan') }
</Button>
</div>
</div>
</Modal>
);
}
}

View File

@ -0,0 +1,3 @@
export default function() {
return null
};

View File

@ -15,7 +15,8 @@ const TAINACAN_BLOCKS = [
'related-items-list' => [],
'terms-list' => [],
'faceted-search' => [],
'item-submission-form' => []
'item-submission-form' => [],
'item-gallery' => ['render_callback' => 'gutenberg_examples_dynamic_render_callback']
];
// Lets do this!
@ -75,6 +76,17 @@ function tainacan_blocks_register_and_enqueue_all_blocks() {
}
}
/**
* Plugin Name: Gutenberg examples dynamic
*/
function gutenberg_examples_dynamic_render_callback( $block_attributes, $content ) {
error_log($content);
error_log(json_encode($block_attributes));
$content = '<p>TESTE</p>';
return $content;
}
/**
* Registers a 'generic' Tainacan Block, according to the TAINACAN_BLOCKs array
*
@ -125,10 +137,16 @@ function tainacan_blocks_register_block($block_slug, $options = []) {
);
$register_params['style'] = $block_slug;
// If there is a server side render callback...
if ( isset($options['render_callback']) ) {
$register_params['render_callback'] = $options['render_callback'];
$register_params['skip_inner_blocks'] = true;
}
// Registers the new block
if (function_exists('register_block_type')) {
if ( version_compare( $wp_version, '5.8-RC', '>=') )
register_block_type( __DIR__ . '/blocks/' . $block_slug );
register_block_type( __DIR__ . '/blocks/' . $block_slug, $register_params );
else
register_block_type( 'tainacan/' . $block_slug, $register_params );
}

View File

@ -24,33 +24,29 @@ function tainacanBlocksLocalizeMetadata(metadata) {
export default function({ metadata, icon, edit, save, deprecated, transforms }) {
const currentWPVersion = (typeof tainacan_blocks != 'undefined') ? tainacan_blocks.wp_version : tainacan_plugin.wp_version;
let attributes = {
icon: {
src: icon,
foreground: '#298596',
},
edit,
deprecated,
transforms
}
if (save)
attributes['save'] = save;
if (currentWPVersion >= '5.8-RC') {
// Registers block type using new strategy from WP 5.8
registerBlockType( metadata, {
icon: {
src: icon,
foreground: '#298596',
},
edit,
save,
deprecated,
transforms
});
registerBlockType( metadata, attributes);
} else {
// Converts this array to a valid array previous to WP 5.8
registerBlockType( metadata.name, {
...tainacanBlocksLocalizeMetadata(metadata),
icon: {
src: icon,
foreground: '#298596',
},
edit,
save,
deprecated,
transforms
...attributes
});
}
};

View File

@ -23,7 +23,8 @@ const addDataModuleToOldBlocks = () => {
'carousel-items-list',
'carousel-terms-list',
'related-items-list',
'carousel-collections-list'
'carousel-collections-list',
'item-gallery'
];
// Looks for Tainacan Blocks based on their classes.

View File

@ -19,7 +19,8 @@ module.exports = {
block_facets_list: './src/views/gutenberg-blocks/blocks/facets-list/index.js',
block_item_submission_form: './src/views/gutenberg-blocks/blocks/item-submission-form/index.js',
block_faceted_search: './src/views/gutenberg-blocks/blocks/faceted-search/index.js',
block_carousel_terms_list: './src/views/gutenberg-blocks/blocks/carousel-terms-list/index.js'
block_carousel_terms_list: './src/views/gutenberg-blocks/blocks/carousel-terms-list/index.js',
block_item_gallery: './src/views/gutenberg-blocks/blocks/item-gallery/index.js'
},
output: {
path: path.resolve(__dirname, './src/assets/js/'),