|
/** |
|
* Retrieves the translation of text. |
|
* |
|
* @see https://developer.wordpress.org/block-editor/packages/packages-i18n/ |
|
*/ |
|
import { __ } from '@wordpress/i18n'; |
|
|
|
/** |
|
* 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/packages/packages-block-editor/#useBlockProps |
|
*/ |
|
import { |
|
InnerBlocks, |
|
useBlockProps, |
|
InspectorControls, |
|
} from '@wordpress/block-editor'; |
|
|
|
/** |
|
* Other things needed |
|
* |
|
*/ |
|
|
|
import { SelectControl, TextControl } from '@wordpress/components'; |
|
import { Component } from '@wordpress/element'; |
|
import { useDispatch, useSelect } from '@wordpress/data'; |
|
|
|
/** |
|
* 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'; |
|
|
|
function mySelectPosts({clientId, attributes, setAttributes }) { |
|
|
|
// Used to reset inner blocks if we've changed the selected exercise |
|
const { replaceInnerBlocks } = useDispatch("core/block-editor"); |
|
const { inner_blocks } = useSelect(select => ({ |
|
inner_blocks: select("core/block-editor").getBlocks(clientId) |
|
})); |
|
|
|
/** |
|
* Called when the inspector controls select box is changed |
|
* Saves the value of the selected post to the selectedPost attribute |
|
* and resets the innerblock |
|
**/ |
|
|
|
const onSelectPost = ( post ) => { |
|
|
|
// reset the inner |
|
let inner_blocks_new = []; |
|
|
|
replaceInnerBlocks(clientId, inner_blocks_new, false); |
|
|
|
setAttributes( { |
|
selectedPost: parseInt(post), |
|
} ); |
|
}; |
|
|
|
const posts = useSelect( 'core' ).getEntityRecords( 'postType', 'exercise', { //1 |
|
// *no _embed here |
|
|
|
// This should always be set to avoid duplicates in the post selection dropdown. |
|
// But of course, you can change the value, e.g. to 20 or, the max value - 100. |
|
per_page: 10, |
|
} ); |
|
|
|
const selectedPost = useSelect( select => { //2 |
|
const { getEntityRecord } = select( 'core' ); |
|
|
|
const post_id = attributes.selectedPost; |
|
|
|
// Fetch the post from the REST API, if we have a valid post ID. |
|
return post_id && getEntityRecord( 'postType', 'exercise', post_id, { |
|
// Request featured media along with the standard post data. |
|
_embed: 'wp:featuredmedia', |
|
|
|
// *no per_page here |
|
} ); |
|
// This useSelect callback has one dependency - the selected post ID. |
|
}, [ attributes.selectedPost ] ); |
|
|
|
/* Set of functions for saving out attributes |
|
* reps, sets, notes |
|
*/ |
|
|
|
const onChangeReps = ( reps ) => { |
|
setAttributes( { |
|
reps: Number( reps ), //3 |
|
} ); |
|
} |
|
|
|
const onChangeSets = ( sets ) => { |
|
setAttributes( { |
|
sets: Number( sets ), //4 |
|
} ); |
|
} |
|
|
|
const onChangeNotes = ( notes ) => { |
|
setAttributes( { |
|
notes: notes, |
|
} ); |
|
} |
|
|
|
// will contain the text output for the edit. |
|
let output = ""; |
|
|
|
const blockProps = useBlockProps( { |
|
className: "sandcexercise", |
|
} ); |
|
|
|
// we've selected a post, so grab the bits from that post to put into the html |
|
if (selectedPost) { //5 |
|
|
|
// grab the things we are putting in the innerblock from the post |
|
let mediaID = selectedPost?.featured_media || 0; //6 |
|
let mediaURL = selectedPost?._embedded?.['wp:featuredmedia'][0]?.source_url || ''; //7 |
|
let exerciseLink = selectedPost.link; |
|
|
|
// remove any html content from the excerpt as it explodes the inner block :/ |
|
let strippedContent = selectedPost.excerpt.rendered.replace(/(<([^>]+)>)/gi, ""); |
|
|
|
// create a linked heading |
|
let heading = '<a href="' + exerciseLink + '">' + selectedPost.title.rendered + '</a>'; |
|
|
|
// build the innerblocks template |
|
const MY_TEMPLATE = [ |
|
[ 'core/heading', { content: heading } ], |
|
[ 'core/image', { id: mediaID, url: mediaURL, href: exerciseLink, align: "right", sizeSlug: "medium", caption: "Click for instructions", width: 300 } ], //* |
|
[ 'core/paragraph', { content: strippedContent } ] |
|
]; |
|
output = <InnerBlocks |
|
template={ MY_TEMPLATE } |
|
templateLock="" |
|
/> |
|
|
|
} else { |
|
output = <p>Select an exercise from the right</p> |
|
} |
|
|
|
//8 I changed the SelectControl's options - a "loading" message is shown if "posts" |
|
// is yet filled. |
|
|
|
// return the edit html. |
|
return [ |
|
<div { ...blockProps } key="sandcexercise"> |
|
<InspectorControls> |
|
<div id="sandcexercise-controls"> |
|
<SelectControl |
|
onChange={ onSelectPost } |
|
value={ attributes.selectedPost } |
|
label={ __( 'Select a Post' ) } |
|
options={posts ? [ |
|
...[ { value: 0, label: __( 'Select an exercise' ) } ], |
|
...posts.map( post => ( { |
|
value: post.id, |
|
label: post.title.rendered, |
|
})), |
|
] : [ |
|
{ value: 0, label: __( 'Loading the posts list..' ) }, |
|
]} |
|
/> |
|
</div> |
|
</InspectorControls> |
|
{output} |
|
<TextControl |
|
{ ...blockProps } |
|
label="Reps:" |
|
type="number" |
|
className="thereps" |
|
onChange={ onChangeReps } |
|
value={ attributes.reps } |
|
/> |
|
<TextControl |
|
{ ...blockProps } |
|
label="Sets:" |
|
type="number" |
|
className="thesets" |
|
onChange={ onChangeSets } |
|
value={ attributes.sets } |
|
/> |
|
<TextControl |
|
{ ...blockProps } |
|
label="Notes:" |
|
type="text" |
|
className="thenotes" |
|
onChange={ onChangeNotes } |
|
value={ attributes.notes } |
|
/> |
|
</div> |
|
] |
|
|
|
} |
|
|
|
export default mySelectPosts; |
I've approached it as a Gutenberg block because
On reflection, copying the data is important because the plan is a point in time thing. I.e. you don't want the plan to change because the exercise does and it will have customisations done by the author of the plan as it's created, so it changing dynamically might make that stop making sense, or be confusing when you review later on.
Am I right in thinking that if I want the author of the plan to be able to tweak the heading that's been outputting, that will need to come put as a heading block, and therefore innerblocks is the right way to do it? Or is this a fundamental misunderstanding I have?