diff --git a/includes/manager/class-content-model-loader.php b/includes/manager/class-content-model-loader.php index 14e133a..e0f0c0a 100644 --- a/includes/manager/class-content-model-loader.php +++ b/includes/manager/class-content-model-loader.php @@ -122,6 +122,22 @@ private function register_post_type() { ) ); + register_post_meta( + Content_Model_Manager::POST_TYPE_NAME, + 'blocks', + array( + 'type' => 'string', + 'single' => true, + 'sanitize_callback' => '', + 'default' => '[]', + 'show_in_rest' => array( + 'schema ' => array( + 'type' => 'string', + ), + ), + ) + ); + $cpt_fields = array( 'plural_label' => array( 'type' => 'string', diff --git a/includes/manager/src/components/attribute-binder-panel.js b/includes/manager/src/components/attribute-binder-panel.js index f52203d..15099e9 100644 --- a/includes/manager/src/components/attribute-binder-panel.js +++ b/includes/manager/src/components/attribute-binder-panel.js @@ -35,13 +35,12 @@ export const AttributeBinderPanel = ( { attributes, setAttributes, name } ) => { 'meta' ); - const fields = useMemo( () => { - // Saving the fields as serialized JSON because I was tired of fighting the REST API. - return meta?.fields ? JSON.parse( meta.fields ) : []; - }, [ meta.fields ] ); + const blocks = useMemo( () => { + return meta?.blocks ? JSON.parse( meta.blocks ) : []; + }, [ meta.blocks ] ); - const boundField = fields.find( - ( field ) => field.slug === attributes.metadata?.slug + const boundField = blocks.find( + ( block ) => block.slug === attributes.metadata?.slug ); const removeBindings = useCallback( () => { @@ -55,25 +54,25 @@ export const AttributeBinderPanel = ( { attributes, setAttributes, name } ) => { delete newAttributes.metadata[ BLOCK_VARIATION_NAME_ATTR ]; delete newAttributes.metadata.slug; - const newFields = fields.filter( - ( field ) => field.slug !== attributes.metadata.slug + const newBlocks = blocks.filter( + ( block ) => block.slug !== attributes.metadata.slug ); setMeta( { - fields: JSON.stringify( newFields ), + blocks: JSON.stringify( newBlocks ), } ); setAttributes( newAttributes ); - }, [ attributes.metadata, setAttributes, fields, setMeta ] ); + }, [ attributes.metadata, setAttributes, blocks, setMeta ] ); const setBinding = useCallback( - ( field ) => { + ( block ) => { const newBindings = supportedAttributes.reduce( ( acc, attribute ) => { acc[ attribute ] = - 'post_content' === field.slug - ? field.slug - : `${ field.slug }__${ attribute }`; + 'post_content' === block.slug + ? block.slug + : `${ block.slug }__${ attribute }`; return acc; }, @@ -83,8 +82,8 @@ export const AttributeBinderPanel = ( { attributes, setAttributes, name } ) => { const newAttributes = { metadata: { ...( attributes.metadata ?? {} ), - [ BLOCK_VARIATION_NAME_ATTR ]: field.label, - slug: field.slug, + [ BLOCK_VARIATION_NAME_ATTR ]: block.label, + slug: block.slug, [ BINDINGS_KEY ]: newBindings, }, }; diff --git a/includes/manager/src/components/edit-block.js b/includes/manager/src/components/edit-block.js new file mode 100644 index 0000000..0bc47f8 --- /dev/null +++ b/includes/manager/src/components/edit-block.js @@ -0,0 +1,134 @@ +import { + Button, + ButtonGroup, + TextControl, + __experimentalGrid as Grid, + CardBody, + Card, + __experimentalItemGroup as ItemGroup, + __experimentalItem as Item, + Flex, + FlexBlock, + FlexItem, +} from '@wordpress/components'; +import { __ } from '@wordpress/i18n'; +import { useState, useEffect } from '@wordpress/element'; +import { + blockDefault, + paragraph, + image, + heading, + group, + button, +} from '@wordpress/icons'; + +import { SUPPORTED_BLOCK_ATTRIBUTES } from '../constants'; + +const EditBlockForm = ( { + block = { + label: '', + slug: '', + description: '', + type: 'text', + visible: false, + }, + onChange = () => {}, +} ) => { + const [ formData, setFormData ] = useState( block ); + + useEffect( () => { + onChange( formData ); + }, [ formData ] ); + + return ( + <> + + + + + { __( 'Block Binding' ) } + + + + + + + + setFormData( { + ...formData, + description: value, + } ) + } + /> + + + + + + > + ); +}; + +const BlockAttributes = ( { slug, type } ) => { + const supportedAttributes = SUPPORTED_BLOCK_ATTRIBUTES[ type ]; + return ( + + { supportedAttributes.map( ( attribute ) => ( + + + { attribute } + + + { 'post_content' === slug ? ( + { slug } + ) : ( + { `${ slug }__${ attribute }` } + ) } + + + + + ) ) } + + ); +}; + +const blockIcon = ( type ) => { + switch ( type ) { + case 'core/paragraph': + return paragraph; + case 'core/image': + return image; + case 'core/heading': + return heading; + case 'core/group': + return group; + case 'core/button': + return button; + default: + return blockDefault; + } +}; + +export default EditBlockForm; diff --git a/includes/manager/src/components/edit-field.js b/includes/manager/src/components/edit-field.js index 7e8b5fa..0a8c1bc 100644 --- a/includes/manager/src/components/edit-field.js +++ b/includes/manager/src/components/edit-field.js @@ -3,7 +3,6 @@ import { ButtonGroup, TextControl, SelectControl, - ToggleControl, __experimentalGrid as Grid, CardBody, Card, @@ -15,15 +14,7 @@ import { } from '@wordpress/components'; import { __ } from '@wordpress/i18n'; import { useState, useEffect } from '@wordpress/element'; -import { - trash, - chevronUp, - chevronDown, - blockDefault, - post, -} from '@wordpress/icons'; - -import { SUPPORTED_BLOCK_ATTRIBUTES } from '../constants'; +import { trash, chevronUp, chevronDown, post } from '@wordpress/icons'; const EditFieldForm = ( { field = { @@ -55,18 +46,10 @@ const EditFieldForm = ( { marginBottom: '1rem', } } > - { formData.type.indexOf( 'core/' ) === -1 ? ( - - { __( 'Custom Field' ) } - - ) : ( - - { __( 'Block Binding' ) } - - ) } + + { __( 'Custom Field' ) } + + { index > 0 && ( ) } - { formData.type.indexOf( 'core/' ) === -1 ?? ( - { - // eslint-disable-next-line no-alert - const userWantsToDelete = confirm( - __( - 'Are you sure you want to delete this field?' - ) - ); - if ( userWantsToDelete ) { - onDelete( formData ); - } - } } - /> - ) } + { + // eslint-disable-next-line no-alert + const userWantsToDelete = confirm( + __( + 'Are you sure you want to delete this field?' + ) + ); + + if ( userWantsToDelete ) { + onDelete( formData ); + } + } } + /> - + - - { formData.type.indexOf( 'core/' ) === -1 && ( - - - setFormData( { ...formData, type: value } ) - } - /> - - setFormData( { - ...formData, - visible: value, - } ) - } - /> - - ) } - { formData.type.indexOf( 'core/' ) === 0 ? ( - + setFormData( { ...formData, type: value } ) + } /> - ) : ( - - - - { formData.type } - - - { formData.slug } - - - - - - ) } + + + + + { formData.type } + + + { formData.slug } + + + + + > ); }; -const BlockAttributes = ( { slug, type } ) => { - const supportedAttributes = SUPPORTED_BLOCK_ATTRIBUTES[ type ]; - return ( - - { supportedAttributes.map( ( attribute ) => ( - - - { attribute } - - - { 'post_content' === slug ? ( - { slug } - ) : ( - { `${ slug }__${ attribute }` } - ) } - - - - - ) ) } - - ); -}; - export default EditFieldForm; diff --git a/includes/manager/src/components/fields-ui.js b/includes/manager/src/components/fields-ui.js index 406de5b..ee4ffbf 100644 --- a/includes/manager/src/components/fields-ui.js +++ b/includes/manager/src/components/fields-ui.js @@ -2,6 +2,7 @@ import { PluginDocumentSettingPanel } from '@wordpress/editor'; import { Button, PanelRow, + TabPanel, Modal, __experimentalVStack as VStack, __experimentalItemGroup as ItemGroup, @@ -14,9 +15,18 @@ import { import { __ } from '@wordpress/i18n'; import { useEntityProp } from '@wordpress/core-data'; import { useState } from '@wordpress/element'; -import { seen, unseen, blockDefault } from '@wordpress/icons'; +import { + post, + blockDefault, + paragraph, + heading, + button, + group, + image, +} from '@wordpress/icons'; import EditFieldForm from './edit-field'; +import EditBlockForm from './edit-block'; import { POST_TYPE_NAME } from '../constants'; export const FieldsUI = function () { @@ -24,15 +34,8 @@ export const FieldsUI = function () { const [ meta ] = useEntityProp( 'postType', POST_TYPE_NAME, 'meta' ); - // Saving the fields as serialized JSON because I was tired of fighting the REST API. const fields = meta?.fields ? JSON.parse( meta.fields ) : []; - - // Add UUID to fields - fields.forEach( ( field ) => { - if ( ! field.uuid ) { - field.uuid = crypto.randomUUID(); - } - } ); + const blocks = meta?.blocks ? JSON.parse( meta.blocks ) : []; return ( <> @@ -41,45 +44,51 @@ export const FieldsUI = function () { title={ __( 'Post Meta' ) } className="create-content-model-field-settings" > - { fields.length > 0 && ( - - { fields.map( ( field ) => ( - - - { field.label } - - { field.slug } - - - { field.visible && ( + + { blocks.length > 0 && ( + <> + { blocks.map( ( block ) => ( + + - + - ) } - { ! field.visible && - field.type.indexOf( 'core' ) > -1 && ( - - - - ) } - { ! field.visible && - field.type.indexOf( 'core' ) < 0 && ( - - - - ) } - - - ) ) } - - ) } + { block.label } + + { block.slug } + + + + ) ) } + > + ) } + { fields.length > 0 && ( + <> + { fields.map( ( field ) => ( + + + + + + { field.label } + + { field.slug } + + + + ) ) } + > + ) } + setFieldsOpen( true ) } > - { __( 'Manage Fields' ) } + { __( 'Manage Post Meta' ) } @@ -89,7 +98,22 @@ export const FieldsUI = function () { size="large" onRequestClose={ () => setFieldsOpen( false ) } > - + , + }, + { + name: 'blocks', + title: __( 'Block Bindings' ), + content: , + }, + ] } + > + { ( tab ) => <>{ tab.content }> } + ) } @@ -104,7 +128,6 @@ const FieldsList = () => { 'meta' ); - // Saving the fields as serialized JSON because I was tired of fighting the REST API. const fields = meta?.fields ? JSON.parse( meta.fields ) : []; // Save the fields back to the meta. @@ -126,6 +149,7 @@ const FieldsList = () => { return ( <> + { __( 'Custom fields show up in the post sidebar.' ) } { fields.map( ( field ) => ( { setFields( [ ...fields, @@ -180,3 +205,35 @@ const FieldsList = () => { > ); }; + +const BlocksList = () => { + const [ meta ] = useEntityProp( 'postType', POST_TYPE_NAME, 'meta' ); + + const blocks = meta?.blocks ? JSON.parse( meta.blocks ) : []; + + return ( + <> + + { blocks.map( ( block ) => ( + + ) ) } + + > + ); +}; +const blockIcon = ( type ) => { + switch ( type ) { + case 'core/paragraph': + return paragraph; + case 'core/image': + return image; + case 'core/heading': + return heading; + case 'core/group': + return group; + case 'core/button': + return button; + default: + return blockDefault; + } +}; diff --git a/includes/manager/src/components/manage-bindings.js b/includes/manager/src/components/manage-bindings.js index d24900f..13a7dd6 100644 --- a/includes/manager/src/components/manage-bindings.js +++ b/includes/manager/src/components/manage-bindings.js @@ -10,7 +10,7 @@ const ManageBindings = ( { slug: '', description: '', type: 'text', - visible: true, + visible: false, uuid: crypto.randomUUID(), }, onSave = () => {}, @@ -24,26 +24,26 @@ const ManageBindings = ( { 'meta' ); - const fields = meta?.fields ? JSON.parse( meta.fields ) : []; + const blocks = meta?.blocks ? JSON.parse( meta.blocks ) : []; const saveForm = ( e ) => { e.preventDefault(); - let newFields = fields; + let newBlocks = blocks; - if ( fields.find( ( field ) => field.uuid === formData.uuid ) ) { - // If the slug is the same and it exists, update the field. - newFields = newFields.map( ( field ) => { - if ( field.uuid === formData.uuid ) { - field = formData; + if ( blocks.find( ( block ) => block.uuid === formData.uuid ) ) { + // If the slug is the same and it exists, update the block. + newBlocks = newBlocks.map( ( block ) => { + if ( block.uuid === formData.uuid ) { + block = formData; } - return field; + return block; } ); setMeta( { - fields: JSON.stringify( newFields ), + blocks: JSON.stringify( newBlocks ), } ); } else { setMeta( { - fields: JSON.stringify( [ ...newFields, formData ] ), + blocks: JSON.stringify( [ ...newBlocks, formData ] ), } ); } onSave( formData );
{ slug }
{ `${ slug }__${ attribute }` }
{ formData.slug }
{ field.slug }
{ block.slug }
{ __( 'Custom fields show up in the post sidebar.' ) }