|
/** |
|
* 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'm not following what you are suggesting here. because what you're saying I should do, I think I'm actually doing!
We might have 30 different exercises defined, which will have lots of content on them, videos, progressions, tips, comments etc. They exist as a generic library which will grow. This block is a way to pull out a small subset of that information into another post type. In the typical use case (but could be others), it will be when a coach wants to create a specific Strength and Conditioning Plan for a person. They will add this block 3 or 4 times, select the required exercises and add some notes, sets and reps. The user can then view their own plan, see what they have to do, and if they want to know more, click the title of the exercise and see all the additional generic information about that exercise.
It might be better to pull this information dynamically when it's viewed, rather than "copying" it I guess, but how would that work? Can you do that within a published Gutenberg block?