Created
August 25, 2023 08:10
-
-
Save tresorama/bf512a84b6a32faafadd1600664118d1 to your computer and use it in GitHub Desktop.
wordpress-gutenberg--POC--dynamic-data
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { createHigherOrderComponent } from '@wordpress/compose'; | |
import * as wpHooks from '@wordpress/hooks'; | |
import { BlockControls, InspectorControls } from '@wordpress/block-editor'; | |
import { PanelBody, PanelRow } from '@wordpress/components'; | |
import { Icon, backup as backupIcon } from '@wordpress/icons'; | |
import { Post, useEntityRecord } from '@wordpress/core-data'; | |
import { store as coreStore } from '@wordpress/core-data'; | |
import { useSelect, useDispatch } from '@wordpress/data'; | |
import { BlockEditProps } from '@wordpress/blocks'; | |
const TEXT_ALLOWED_BLOCKS = ["core/heading", "core/paragraph"]; | |
// add attributes to registration of block | |
// that is when block.json is parsed and registered | |
// | |
wpHooks.addFilter( | |
'blocks.registerBlockType', | |
'tccb/add-dynamic-data--text', | |
(props, name: string) => { | |
if (!TEXT_ALLOWED_BLOCKS.includes(name)) return props; | |
return { | |
...props, | |
"usesContext": ["postType", "postId", "queryId"], | |
}; | |
}, | |
); | |
const useDynamicDataSources = (props: BlockEditProps<any>) => { | |
const { attributes, context } = props; | |
// extract dynamic data fields "names" and return as list | |
// fetch data | |
const post = useEntityRecord<Post>('postType', context.postType, context.postId); | |
const acfFields = post.record.acf || {}; | |
// const meta = post.record.meta || {}; | |
return [ | |
{ section_name: 'Post', fields: ['post_title'].map(_ => "{" + _ + "}") }, | |
{ section_name: 'ACF', fields: Object.keys(acfFields).map(_ => "{acf:" + _ + "}") }, | |
] satisfies { section_name: string, fields: Array<string>; }[]; | |
}; | |
const useContentWithDynamicData = (props: BlockEditProps<any>) => { | |
const { attributes, context } = props; | |
// given a "string" that rapresetn text content of a block | |
// replace predefined placeholders with dynamic data | |
// that come from wordpress/data | |
// fetch data | |
const post = useEntityRecord('postType', context.postType, context.postId); | |
const acfFields = post.record.acf || {}; | |
// const meta = post.record.meta || {}; | |
// do templating | |
let subsitutedContent: string = attributes.content; | |
// replace core wp data | |
subsitutedContent = subsitutedContent.replaceAll("{post_title}", post.record.title.raw); | |
// replace acf fields | |
// ("stream_name" field slug must be written as {acf:stream_name}) | |
{ | |
//const text = "Hello {acf:text1}, this is a sample text. {acf:text2} can be used multiple times. Anche {acf:ciao_mamma}"; | |
//const dynamicPlaceholder = "{acf:any_string_with_underscore}"; | |
const fixedPart = "acf:"; | |
const escapedFixedPart = fixedPart.replace(/[-/\\^$*+?.()|[\]{}]/g, "\\$&"); | |
const regex = new RegExp(escapedFixedPart + "([^}]+?)}", "g"); | |
let extractedValues: string[] = []; | |
subsitutedContent.replace(regex, (match, value) => { | |
extractedValues.push(value); | |
return match; | |
}); | |
//console.log(extractedValues); // expected ["text1", "text2", "ciao_mamma"] | |
extractedValues.forEach(suffix => { | |
const fieldValue = acfFields[suffix]; | |
if (!fieldValue) return; | |
const placeholder = "{acf:" + suffix + "}"; | |
subsitutedContent = subsitutedContent.replaceAll(placeholder, fieldValue); | |
}); | |
} | |
return { post, subsitutedContent }; | |
}; | |
// handle the "edit" component | |
wpHooks.addFilter( | |
'editor.BlockEdit', | |
'tccb/add-dynamic-data-selector--text', | |
createHigherOrderComponent(BlockEdit => (props: BlockEditProps<any>) => { | |
// abort if not desired block | |
if (!TEXT_ALLOWED_BLOCKS.includes(props.name)) return <BlockEdit {...props} />; | |
// | |
const { attributes, isSelected, setAttributes } = props; | |
const { content = "" } = attributes; | |
// get all dynamic data sources available in this block. | |
// dyanmic data source depends on block context | |
// here we have only "field" names of dynamic data | |
const dynamicDataSources = useDynamicDataSources(props); | |
// when user select a field from the tollbar select | |
// we paste the field name refernce insied the content | |
const handleSelectDynamicData = (fieldName: string) => { | |
setAttributes({ content: content + fieldName }); | |
}; | |
// replace placeholders of fields with real value from the Data layer | |
const typedContent = content; | |
const { subsitutedContent } = useContentWithDynamicData(props); | |
return ( | |
<> | |
{/* Toolbar */} | |
<BlockControls controls={{}}> | |
<select onChange={e => handleSelectDynamicData(e.currentTarget.value)} value="this-select-value-never-change--is-only-a-list-from-whichto-copy-string-into-content"> | |
<option value="this-select-value-never-change--is-only-a-list-from-whichto-copy-string-into-content">Insert Dynamic</option> | |
{dynamicDataSources.map(({ section_name, fields }) => fields.map(fieldName => <option value={fieldName}>{fieldName} ({section_name})</option>))} | |
</select> | |
</BlockControls> | |
{/* Canvas */} | |
<BlockEdit | |
{...props} | |
attributes={{ | |
...props.attributes, | |
content: isSelected ? typedContent : subsitutedContent | |
}} | |
/> | |
</> | |
); | |
}, 'tccb/add-dynamic-data-selector--text'), | |
); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Iteration #2
WIP - Test of an implementation that can have third party dynami cdata source providers