I’m trying to make a Gutenberg block that will let me select/upload multiple images from the WordPress Media Manager. I’m totally new to React and building Gutenberg blocks.
Ideally what I’d like to have is a button in the Inspector Controls that opens the Media Manager, and then save the IDs of the selected media to an object/array in block $attributes.
I’ve tried over a dozen different combinations to try to get the to work, but it breaks the block every time. The console log has a very generic “not defined” error.
Where’s what I have so far for my edit.js
/**
* Retrieves the translation of text.
*
* @see https://developer.wordpress.org/block-editor/reference-guides/packages/packages-i18n/
*/
import { __ } from '@wordpress/i18n';
import {DynamicGallery, StaticGallery,MyMediaUploader} from './components/gallery';
import metadata from './block.json';
import "./editor.scss"
/**
* React hook that is used to mark the block wrapper element.
* It provides all the necessary props like the class name.
*
* @see https://developer.wordpress.org/block-editor/reference-guides/packages/packages-block-editor/#useblockprops
*/
import { useBlockProps, InspectorControls } from '@wordpress/block-editor';
import { useState } from 'react';
import {
PanelBody,
MediaUploadCheck,
MediaUpload,
SelectControl,
__experimentalNumberControl as NumberControl,
Button,
ToggleControl,
DropZone,
TextControl
} from '@wordpress/components';
/**
* Lets webpack process CSS, SASS or SCSS files referenced in JavaScript files.
* Those files can contain any CSS code that gets applied to the editor.
*
* @see https://www.npmjs.com/package/@wordpress/scripts#using-css
*/
import './editor.scss';
/**
* The edit function describes the structure of your block in the context of the
* editor. This represents what the editor will render when the block is used.
*
* @see https://developer.wordpress.org/block-editor/reference-guides/block-api/block-edit-save/#edit
*
* @return {Element} Element to render.
*/
export default function Edit(props) {
console.log(props);
const{className, ...blockProps} = useBlockProps();
return (
<>
<InspectorControls>
<PanelBody title={__('Source Settings')}>
<SelectControl
label={__('Images Source', metadata.textdomain)}
value={props.attributes.source}
onChange={(newValue) => props.setAttributes({ source: newValue })}
options={[
{ value: 'manual', label: __('Manually Select', metadata.textdomain) },
{ value: 'meta_field', label: __('Meta field of current post', metadata.textdomain) },
{ value: 'linked_table', label: __('Linked Table', metadata.textdomain) }
]}
/>
{props.attributes.source == "meta_field" && (
<div style={{ display: 'flex', alignItems: 'center' }}>
<TextControl
label="Meta Field Name"
value={props.attributes.sourceMetaField}
onChange={(newValue) => props.setAttributes({ sourceMetaField: newValue })}
/>
</div>
)}
{props.attributes.source == "linked_table" && (
<div style={{ display: 'flex', alignItems: 'center' }}>
<TextControl
label="Table Name"
value={props.attributes.sourceTable}
onChange={(newValue) => props.setAttributes({ sourceTable: newValue })}
/>
</div>
)}
{props.attributes.source == "linked_table" && (
<div style={{ display: 'flex', alignItems: 'center' }}>
<TextControl
label="Column Name"
value={props.attributes.sourceColumn}
onChange={(newValue) => props.setAttributes({ sourceColumn: newValue })}
/>
</div>
)}
<div style={{ display: 'flex', alignItems: 'center' }}>
<ToggleControl onChange={(isChecked) => {
props.setAttributes({
includePostThumbnail: isChecked
})
}
} checked={props.attributes.includePostThumbnail}
label={__('Include Post Thumbnail (Featured Image)', metadata.textdomain)}
/>
</div>
<div style={{ display: 'block' }}>
<Button isPrimary>{__('Select Media')}</Button>
<DropZone />
</div>
</PanelBody>
<PanelBody title={__('Display Settings')}>
<SelectControl
label={__('Select an Option', metadata.textdomain)}
value={props.attributes.initialLayout}
onChange={(newValue) => props.setAttributes({ initialLayout: newValue })}
options={[
{ value: 'inline', label: __('Inline', metadata.textdomain) },
{ value: 'grid', label: __('Grid', metadata.textdomain) },
{ value: 'justified', label: __('Justified', metadata.textdomain) },
{ value: 'masonry', label: __('Masonry', metadata.textdomain) }
]}
/>
{props.attributes.initialLayout == "grid" && (
<div style={{ display: 'block' }}>
<span>{__('Grid Columns', metadata.textdomain)}</span>
<NumberControl
onChange={(newValue) => {
props.setAttributes({
gridColumns: parseInt(newValue)
});
}}
value={props.attributes.gridColumns}
isShiftStepEnabled={true}
shiftStep={10}
/>
</div>
)}
<div style={{ display: 'block', alignItems: 'center' }}>
<ToggleControl
onChange={(isChecked) => {
props.setAttributes({
dynamic: isChecked
})
}
} checked={props.attributes.dynamic}
label={__('Load Dynamically', metadata.textdomain)}
/>
</div>
{props.attributes.dynamic && (
<div style={{ display: 'block', alignItems: 'center' }}>
<span>{__('Max to load', metadata.textdomain)}</span>
<NumberControl
onChange={(newValue) => {
props.setAttributes({
maxInitialImages: parseInt(newValue)
});
}}
value={props.attributes.maxInitialImages}
isShiftStepEnabled={true}
shiftStep={10}
/>
</div>
)}
</PanelBody>
<PanelBody title={__('Gallery Options')}>
<div style={{ display: 'flex', alignItems: 'center' }}>
<ToggleControl
onChange={(isChecked) => {
props.setAttributes({
loop: isChecked
})
}
} checked={props.attributes.loop}
label={__('Enable Loop', metadata.textdomain)}
/>
</div>
<div style={{ display: 'flex', alignItems: 'center' }}>
<ToggleControl
onChange={(isChecked) => {
props.setAttributes({
download: isChecked
})
}
} checked={props.attributes.download}
label={__('Allow Download', metadata.textdomain)}
/>
</div>
{props.attributes.download && (
<SelectControl
label={__('Download Image Size', metadata.textdomain)}
value={props.attributes.downloadMaxSize}
onChange={(newValue) => props.setAttributes({ downloadMaxSize: newValue })}
options={[
{ value: 'full', label: __('Full (Original Size)', metadata.textdomain) },
{ value: 'medium', label: __('Medium (Up to 300x300px - Not Cropped)', metadata.textdomain) },
{ value: 'large', label: __('Large (Up to 1024 x 1024px - Not Cropped)', metadata.textdomain) },
{ value: 'thumbnail', label: __('Thumbnail (150 x 150px - Cropped', metadata.textdomain) }
]}
/>
)}
<div style={{ display: 'flex', alignItems: 'center' }}>
<ToggleControl
onChange={(isChecked) => {
props.setAttributes({
allowZoom: isChecked
})
}
} checked={props.attributes.allowZoom}
label={__('Allow Zoom In/Out', metadata.textdomain)}
/>
</div>
<div style={{ display: 'flex', alignItems: 'center' }}>
<ToggleControl
onChange={(isChecked) => {
props.setAttributes({
closeOnTap: isChecked
})
}
} checked={props.attributes.closeOnTap}
label={__('Close on tap', metadata.textdomain)}
/>
</div>
<div style={{ display: 'flex', alignItems: 'center' }}>
<ToggleControl
onChange={(isChecked) => {
props.setAttributes({
showCounter: isChecked
})
}
} checked={props.attributes.showCounter}
label={__('Show counter', metadata.textdomain)}
/>
</div>
<div style={{ display: 'flex', alignItems: 'center' }}>
<ToggleControl
onChange={(isChecked) => {
props.setAttributes({
showControls: isChecked
})
}
} checked={props.attributes.showControls}
label={__('Show Previous/Next Buttons', metadata.textdomain)}
/>
</div>
</PanelBody>
</InspectorControls>
</>
);
}
/**
* Retrieves the translation of text.
*
* @see https://developer.wordpress.org/block-editor/reference-guides/packages/packages-i18n/
*/
import { __ } from '@wordpress/i18n';
import {DynamicGallery, StaticGallery,MyMediaUploader} from './components/gallery';
import metadata from './block.json';
import "./editor.scss"
/**
* React hook that is used to mark the block wrapper element.
* It provides all the necessary props like the class name.
*
* @see https://developer.wordpress.org/block-editor/reference-guides/packages/packages-block-editor/#useblockprops
*/
import { useBlockProps, InspectorControls } from '@wordpress/block-editor';
import { useState } from 'react';
import {
PanelBody,
MediaUploadCheck,
MediaUpload,
SelectControl,
__experimentalNumberControl as NumberControl,
Button,
ToggleControl,
DropZone,
TextControl
} from '@wordpress/components';
/**
* Lets webpack process CSS, SASS or SCSS files referenced in JavaScript files.
* Those files can contain any CSS code that gets applied to the editor.
*
* @see https://www.npmjs.com/package/@wordpress/scripts#using-css
*/
import './editor.scss';
/**
* The edit function describes the structure of your block in the context of the
* editor. This represents what the editor will render when the block is used.
*
* @see https://developer.wordpress.org/block-editor/reference-guides/block-api/block-edit-save/#edit
*
* @return {Element} Element to render.
*/
export default function Edit(props) {
console.log(props);
const{className, ...blockProps} = useBlockProps();
return (
<>
<InspectorControls>
<PanelBody title={__('Source Settings')}>
<SelectControl
label={__('Images Source', metadata.textdomain)}
value={props.attributes.source}
onChange={(newValue) => props.setAttributes({ source: newValue })}
options={[
{ value: 'manual', label: __('Manually Select', metadata.textdomain) },
{ value: 'meta_field', label: __('Meta field of current post', metadata.textdomain) },
{ value: 'linked_table', label: __('Linked Table', metadata.textdomain) }
]}
/>
{props.attributes.source == "meta_field" && (
<div style={{ display: 'flex', alignItems: 'center' }}>
<TextControl
label="Meta Field Name"
value={props.attributes.sourceMetaField}
onChange={(newValue) => props.setAttributes({ sourceMetaField: newValue })}
/>
</div>
)}
{props.attributes.source == "linked_table" && (
<div style={{ display: 'flex', alignItems: 'center' }}>
<TextControl
label="Table Name"
value={props.attributes.sourceTable}
onChange={(newValue) => props.setAttributes({ sourceTable: newValue })}
/>
</div>
)}
{props.attributes.source == "linked_table" && (
<div style={{ display: 'flex', alignItems: 'center' }}>
<TextControl
label="Column Name"
value={props.attributes.sourceColumn}
onChange={(newValue) => props.setAttributes({ sourceColumn: newValue })}
/>
</div>
)}
<div style={{ display: 'flex', alignItems: 'center' }}>
<ToggleControl onChange={(isChecked) => {
props.setAttributes({
includePostThumbnail: isChecked
})
}
} checked={props.attributes.includePostThumbnail}
label={__('Include Post Thumbnail (Featured Image)', metadata.textdomain)}
/>
</div>
<div style={{ display: 'block' }}>
<Button isPrimary>{__('Select Media')}</Button>
<DropZone />
</div>
</PanelBody>
<PanelBody title={__('Display Settings')}>
<SelectControl
label={__('Select an Option', metadata.textdomain)}
value={props.attributes.initialLayout}
onChange={(newValue) => props.setAttributes({ initialLayout: newValue })}
options={[
{ value: 'inline', label: __('Inline', metadata.textdomain) },
{ value: 'grid', label: __('Grid', metadata.textdomain) },
{ value: 'justified', label: __('Justified', metadata.textdomain) },
{ value: 'masonry', label: __('Masonry', metadata.textdomain) }
]}
/>
{props.attributes.initialLayout == "grid" && (
<div style={{ display: 'block' }}>
<span>{__('Grid Columns', metadata.textdomain)}</span>
<NumberControl
onChange={(newValue) => {
props.setAttributes({
gridColumns: parseInt(newValue)
});
}}
value={props.attributes.gridColumns}
isShiftStepEnabled={true}
shiftStep={10}
/>
</div>
)}
<div style={{ display: 'block', alignItems: 'center' }}>
<ToggleControl
onChange={(isChecked) => {
props.setAttributes({
dynamic: isChecked
})
}
} checked={props.attributes.dynamic}
label={__('Load Dynamically', metadata.textdomain)}
/>
</div>
{props.attributes.dynamic && (
<div style={{ display: 'block', alignItems: 'center' }}>
<span>{__('Max to load', metadata.textdomain)}</span>
<NumberControl
onChange={(newValue) => {
props.setAttributes({
maxInitialImages: parseInt(newValue)
});
}}
value={props.attributes.maxInitialImages}
isShiftStepEnabled={true}
shiftStep={10}
/>
</div>
)}
</PanelBody>
<PanelBody title={__('Gallery Options')}>
<div style={{ display: 'flex', alignItems: 'center' }}>
<ToggleControl
onChange={(isChecked) => {
props.setAttributes({
loop: isChecked
})
}
} checked={props.attributes.loop}
label={__('Enable Loop', metadata.textdomain)}
/>
</div>
<div style={{ display: 'flex', alignItems: 'center' }}>
<ToggleControl
onChange={(isChecked) => {
props.setAttributes({
download: isChecked
})
}
} checked={props.attributes.download}
label={__('Allow Download', metadata.textdomain)}
/>
</div>
{props.attributes.download && (
<SelectControl
label={__('Download Image Size', metadata.textdomain)}
value={props.attributes.downloadMaxSize}
onChange={(newValue) => props.setAttributes({ downloadMaxSize: newValue })}
options={[
{ value: 'full', label: __('Full (Original Size)', metadata.textdomain) },
{ value: 'medium', label: __('Medium (Up to 300x300px - Not Cropped)', metadata.textdomain) },
{ value: 'large', label: __('Large (Up to 1024 x 1024px - Not Cropped)', metadata.textdomain) },
{ value: 'thumbnail', label: __('Thumbnail (150 x 150px - Cropped', metadata.textdomain) }
]}
/>
)}
<div style={{ display: 'flex', alignItems: 'center' }}>
<ToggleControl
onChange={(isChecked) => {
props.setAttributes({
allowZoom: isChecked
})
}
} checked={props.attributes.allowZoom}
label={__('Allow Zoom In/Out', metadata.textdomain)}
/>
</div>
<div style={{ display: 'flex', alignItems: 'center' }}>
<ToggleControl
onChange={(isChecked) => {
props.setAttributes({
closeOnTap: isChecked
})
}
} checked={props.attributes.closeOnTap}
label={__('Close on tap', metadata.textdomain)}
/>
</div>
<div style={{ display: 'flex', alignItems: 'center' }}>
<ToggleControl
onChange={(isChecked) => {
props.setAttributes({
showCounter: isChecked
})
}
} checked={props.attributes.showCounter}
label={__('Show counter', metadata.textdomain)}
/>
</div>
<div style={{ display: 'flex', alignItems: 'center' }}>
<ToggleControl
onChange={(isChecked) => {
props.setAttributes({
showControls: isChecked
})
}
} checked={props.attributes.showControls}
label={__('Show Previous/Next Buttons', metadata.textdomain)}
/>
</div>
</PanelBody>
</InspectorControls>
</>
);
}