diff --git a/includes/manager/class-content-model-loader.php b/includes/manager/class-content-model-loader.php index 0ef6268..14e133a 100644 --- a/includes/manager/class-content-model-loader.php +++ b/includes/manager/class-content-model-loader.php @@ -38,7 +38,7 @@ public static function get_instance() { * Initializes the Content_Model_Loader class. * * Checks if the current user has the capability to manage options. - * If they do, it registers the post type and enqueues the attribute binder. + * If they do, it registers the post type and enqueues the manager scripts. * * @return void */ @@ -48,11 +48,8 @@ private function __construct() { } $this->register_post_type(); - $this->maybe_enqueue_the_attribute_binder(); - $this->maybe_enqueue_the_cpt_settings_panel(); - $this->maybe_enqueue_the_fields_ui(); - $this->maybe_enqueue_content_model_length_restrictor(); - $this->maybe_enqueue_the_defaut_value_placeholder(); + + add_action( 'enqueue_block_editor_assets', array( $this, 'maybe_enqueue_scripts' ) ); add_action( 'save_post', array( $this, 'map_template_to_bindings_api_signature' ), 99, 2 ); @@ -149,178 +146,35 @@ private function register_post_type() { } /** - * Conditionally enqueues the attribute binder script for the block editor. - * - * Checks if the current post is of the correct type before enqueueing the script. + * Enqueue the helper scripts if opening the content model manager. * * @return void */ - private function maybe_enqueue_the_attribute_binder() { - add_action( - 'enqueue_block_editor_assets', - function () { - global $post; - - if ( ! $post || Content_Model_Manager::POST_TYPE_NAME !== $post->post_type ) { - return; - } - - $register_attribute_binder_js = include CONTENT_MODEL_PLUGIN_PATH . '/includes/manager/dist/register-attribute-binder.asset.php'; - - wp_enqueue_script( - 'content-model/attribute-binder', - CONTENT_MODEL_PLUGIN_URL . '/includes/manager/dist/register-attribute-binder.js', - $register_attribute_binder_js['dependencies'], - $register_attribute_binder_js['version'], - true - ); - - wp_add_inline_script( - 'content-model/attribute-binder', - 'window.BINDINGS_KEY = "' . Content_Model_Loader::BINDINGS_KEY . '";', - 'before' - ); - - wp_add_inline_script( - 'content-model/attribute-binder', - 'window.BLOCK_VARIATION_NAME_ATTR = "' . Content_Model_Block::BLOCK_VARIATION_NAME_ATTR . '";', - 'before' - ); - } - ); - } + public function maybe_enqueue_scripts() { + global $post; - /** - * Conditionally enqueues the CPT settings script for the content model editor. - * - * Checks if the current post is of the correct type before enqueueing the script. - * - * @return void - */ - private function maybe_enqueue_the_cpt_settings_panel() { - add_action( - 'enqueue_block_editor_assets', - function () { - global $post; - - if ( ! $post || Content_Model_Manager::POST_TYPE_NAME !== $post->post_type ) { - return; - } - - $asset_file = include CONTENT_MODEL_PLUGIN_PATH . 'includes/manager/dist/cpt-settings-panel.asset.php'; - - wp_register_script( - 'data-types/cpt-settings-panel', - CONTENT_MODEL_PLUGIN_URL . '/includes/manager/dist/cpt-settings-panel.js', - $asset_file['dependencies'], - $asset_file['version'], - true - ); - - wp_localize_script( - 'data-types/cpt-settings-panel', - 'contentModelFields', - array( - 'postType' => Content_Model_Manager::POST_TYPE_NAME, - ) - ); - - wp_enqueue_script( 'data-types/cpt-settings-panel' ); - } - ); - } + if ( ! $post || Content_Model_Manager::POST_TYPE_NAME !== $post->post_type ) { + return; + } + $asset_file = include CONTENT_MODEL_PLUGIN_PATH . '/includes/manager/dist/manager.asset.php'; - /** - * Conditionally enqueues the fields UI script for the block editor. - * - * Checks if the current post is of the correct type before enqueueing the script. - * - * @return void - */ - private function maybe_enqueue_the_fields_ui() { - add_action( - 'enqueue_block_editor_assets', - function () { - global $post; - - if ( ! $post || Content_Model_Manager::POST_TYPE_NAME !== $post->post_type ) { - return; - } - - $asset_file = include CONTENT_MODEL_PLUGIN_PATH . 'includes/manager/dist/fields-ui.asset.php'; - - wp_register_script( - 'data-types/fields-ui', - CONTENT_MODEL_PLUGIN_URL . '/includes/manager/dist/fields-ui.js', - $asset_file['dependencies'], - $asset_file['version'], - true - ); - - wp_localize_script( - 'data-types/fields-ui', - 'contentModelFields', - array( - 'postType' => Content_Model_Manager::POST_TYPE_NAME, - ) - ); - - wp_enqueue_script( 'data-types/fields-ui' ); - } - ); - } - - /** - * Conditionally enqueues the content model length restrictor script for the block editor. - * - * Checks if the current post is of the correct type before enqueueing the script. - * - * @return void - */ - private function maybe_enqueue_content_model_length_restrictor() { - add_action( - 'enqueue_block_editor_assets', - function () { - global $post; - - if ( ! $post || Content_Model_Manager::POST_TYPE_NAME !== $post->post_type ) { - return; - } - - $content_model_length_restrictor_js = include CONTENT_MODEL_PLUGIN_PATH . '/includes/manager/dist/content-model-title-length-restrictor.asset.php'; - - wp_enqueue_script( - 'content-model/length-restrictor', - CONTENT_MODEL_PLUGIN_URL . '/includes/manager/dist/content-model-title-length-restrictor.js', - $content_model_length_restrictor_js['dependencies'], - $content_model_length_restrictor_js['version'], - true - ); - } + wp_enqueue_script( + 'content-model/manager', + CONTENT_MODEL_PLUGIN_URL . '/includes/manager/dist/manager.js', + $asset_file['dependencies'], + $asset_file['version'], + true ); - } - private function maybe_enqueue_the_defaut_value_placeholder() { - add_action( - 'enqueue_block_editor_assets', - function () { - global $post; - - if ( ! $post || Content_Model_Manager::POST_TYPE_NAME !== $post->post_type ) { - return; - } - - $content_model_length_restrictor_js = include CONTENT_MODEL_PLUGIN_PATH . '/includes/manager/dist/default-value-placeholder-changer.asset.php'; - - wp_enqueue_script( - 'content-model/default-value-placeholder-changer', - CONTENT_MODEL_PLUGIN_URL . '/includes/manager/dist/default-value-placeholder-changer.js', - $content_model_length_restrictor_js['dependencies'], - $content_model_length_restrictor_js['version'], - true - ); - } + wp_localize_script( + 'content-model/manager', + 'contentModelData', + array( + 'BINDINGS_KEY' => self::BINDINGS_KEY, + 'BLOCK_VARIATION_NAME_ATTR' => Content_Model_Block::BLOCK_VARIATION_NAME_ATTR, + 'POST_TYPE_NAME' => Content_Model_Manager::POST_TYPE_NAME, + ) ); } diff --git a/includes/manager/manager.js b/includes/manager/manager.js new file mode 100644 index 0000000..417ed68 --- /dev/null +++ b/includes/manager/manager.js @@ -0,0 +1,11 @@ +import { registerAttributeBinder } from './src/register-attribute-binder'; +import { registerContentModelLengthRestrictor } from './src/register-content-model-title-length-restrictor'; +import { registerCPTSettingsPanel } from './src/register-cpt-settings-panel'; +import { registerDefaultValuePlaceholderChanger } from './src/register-default-value-placeholder-changer'; +import { registerFieldsUI } from './src/register-fields-ui'; + +registerAttributeBinder(); +registerCPTSettingsPanel(); +registerFieldsUI(); +registerContentModelLengthRestrictor(); +registerDefaultValuePlaceholderChanger(); diff --git a/includes/manager/register-attribute-binder.js b/includes/manager/register-attribute-binder.js deleted file mode 100644 index 4bef79c..0000000 --- a/includes/manager/register-attribute-binder.js +++ /dev/null @@ -1,222 +0,0 @@ -import { addFilter } from '@wordpress/hooks'; -import { useCallback, useMemo, useState } from '@wordpress/element'; -import { __ } from '@wordpress/i18n'; -import { createHigherOrderComponent } from '@wordpress/compose'; -import { InspectorControls } from '@wordpress/block-editor'; -import { - PanelBody, - PanelRow, - Button, - ButtonGroup, - __experimentalItemGroup as ItemGroup, - __experimentalItem as Item, - Flex, - FlexBlock, - FlexItem, -} from '@wordpress/components'; -import { useSelect } from '@wordpress/data'; -import { store as blocksStore } from '@wordpress/blocks'; -import { useEntityProp } from '@wordpress/core-data'; - -import ManageBindings from './_manage-bindings'; - -import SUPPORTED_BLOCK_ATTRIBUTES from './_supported-attributes'; - -const withAttributeBinder = createHigherOrderComponent( ( BlockEdit ) => { - return ( props ) => { - const { getBlockType } = useSelect( blocksStore ); - const { getBlockParentsByBlockName, getBlocksByClientId } = - useSelect( 'core/block-editor' ); - const [ editingBoundAttribute, setEditingBoundAttribute ] = - useState( null ); - - const [ meta, setMeta ] = useEntityProp( - 'postType', - window.contentModelFields.postType, - '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 { attributes, setAttributes, name } = props; - - const boundField = fields.find( - ( field ) => field.slug === attributes.metadata?.slug - ); - - const removeBindings = useCallback( () => { - const newAttributes = { - metadata: { - ...( attributes.metadata ?? {} ), - }, - }; - - delete newAttributes.metadata[ window.BINDINGS_KEY ]; - delete newAttributes.metadata[ window.BLOCK_VARIATION_NAME_ATTR ]; - delete newAttributes.metadata.slug; - - const newFields = fields.filter( - ( field ) => field.slug !== attributes.metadata.slug - ); - - setMeta( { - fields: JSON.stringify( newFields ), - } ); - - setAttributes( newAttributes ); - }, [ attributes.metadata, setAttributes, fields, setMeta ] ); - - const selectedBlockType = getBlockType( name ); - - const blockParentsByBlockName = getBlockParentsByBlockName( - props.clientId, - [ 'core/group' ] - ); - - // Check if any parent blocks have bindings. - const parentHasBindings = useMemo( () => { - return ( - getBlocksByClientId( blockParentsByBlockName ).filter( - ( block ) => - Object.keys( - block?.attributes?.metadata?.[ - window.BINDINGS_KEY - ] || {} - ).length > 0 - ).length > 0 - ); - }, [ blockParentsByBlockName, getBlocksByClientId ] ); - - const supportedAttributes = - SUPPORTED_BLOCK_ATTRIBUTES[ selectedBlockType?.name ]; - - const setBinding = useCallback( - ( field ) => { - const bindings = supportedAttributes.reduce( - ( acc, attribute ) => { - acc[ attribute ] = - 'post_content' === field.slug - ? field.slug - : `${ field.slug }__${ attribute }`; - - return acc; - }, - {} - ); - - const newAttributes = { - metadata: { - ...( attributes.metadata ?? {} ), - [ window.BLOCK_VARIATION_NAME_ATTR ]: field.label, - slug: field.slug, - [ window.BINDINGS_KEY ]: bindings, - }, - }; - - setAttributes( newAttributes ); - }, - [ attributes.metadata, setAttributes, supportedAttributes ] - ); - - if ( ! supportedAttributes || parentHasBindings ) { - return ; - } - - const bindings = attributes?.metadata?.[ window.BINDINGS_KEY ]; - - return ( - <> - - - { ! editingBoundAttribute && bindings && ( - - { supportedAttributes.map( ( attribute ) => { - return ( - - - - { attribute } - - { bindings[ attribute ] && ( - - - - { - bindings[ - attribute - ] - } - - - - ) } - - - ); - } ) } - - ) } - { ! editingBoundAttribute && ( - - - - { bindings && ( - - ) } - - - ) } - { editingBoundAttribute && ( - - { - setBinding( formData ); - setEditingBoundAttribute( null ); - } } - defaultFormData={ { - label: - attributes?.metadata?.[ - window.BLOCK_VARIATION_NAME_ATTR - ] ?? '', - slug: attributes?.metadata?.slug ?? '', - uuid: - boundField?.uuid ?? - window.crypto.randomUUID(), - description: '', - type: selectedBlockType?.name, - visible: false, - } } - typeIsDisabled={ true } - /> - - ) } - - - - - ); - }; -}, 'withAttributeBinder' ); - -addFilter( - 'editor.BlockEdit', - 'content-model/attribute-binder', - withAttributeBinder -); diff --git a/includes/manager/src/components/attribute-binder-panel.js b/includes/manager/src/components/attribute-binder-panel.js new file mode 100644 index 0000000..f52203d --- /dev/null +++ b/includes/manager/src/components/attribute-binder-panel.js @@ -0,0 +1,175 @@ +import { useCallback, useMemo, useState } from '@wordpress/element'; +import { __ } from '@wordpress/i18n'; +import { InspectorControls } from '@wordpress/block-editor'; +import { + PanelBody, + PanelRow, + Button, + ButtonGroup, + __experimentalItemGroup as ItemGroup, + __experimentalItem as Item, + Flex, + FlexBlock, + FlexItem, +} from '@wordpress/components'; +import { useEntityProp } from '@wordpress/core-data'; + +import ManageBindings from './manage-bindings'; +import { + SUPPORTED_BLOCK_ATTRIBUTES, + BINDINGS_KEY, + POST_TYPE_NAME, + BLOCK_VARIATION_NAME_ATTR, +} from '../constants'; + +export const AttributeBinderPanel = ( { attributes, setAttributes, name } ) => { + const supportedAttributes = SUPPORTED_BLOCK_ATTRIBUTES[ name ]; + const bindings = attributes?.metadata?.[ BINDINGS_KEY ]; + + const [ editingBoundAttribute, setEditingBoundAttribute ] = + useState( null ); + + const [ meta, setMeta ] = useEntityProp( + 'postType', + POST_TYPE_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 boundField = fields.find( + ( field ) => field.slug === attributes.metadata?.slug + ); + + const removeBindings = useCallback( () => { + const newAttributes = { + metadata: { + ...( attributes.metadata ?? {} ), + }, + }; + + delete newAttributes.metadata[ BINDINGS_KEY ]; + delete newAttributes.metadata[ BLOCK_VARIATION_NAME_ATTR ]; + delete newAttributes.metadata.slug; + + const newFields = fields.filter( + ( field ) => field.slug !== attributes.metadata.slug + ); + + setMeta( { + fields: JSON.stringify( newFields ), + } ); + + setAttributes( newAttributes ); + }, [ attributes.metadata, setAttributes, fields, setMeta ] ); + + const setBinding = useCallback( + ( field ) => { + const newBindings = supportedAttributes.reduce( + ( acc, attribute ) => { + acc[ attribute ] = + 'post_content' === field.slug + ? field.slug + : `${ field.slug }__${ attribute }`; + + return acc; + }, + {} + ); + + const newAttributes = { + metadata: { + ...( attributes.metadata ?? {} ), + [ BLOCK_VARIATION_NAME_ATTR ]: field.label, + slug: field.slug, + [ BINDINGS_KEY ]: newBindings, + }, + }; + + setAttributes( newAttributes ); + }, + [ attributes.metadata, setAttributes, supportedAttributes ] + ); + + return ( + + + { ! editingBoundAttribute && bindings && ( + + { supportedAttributes.map( ( attribute ) => { + return ( + + + { attribute } + { bindings[ attribute ] && ( + + + + { + bindings[ + attribute + ] + } + + + + ) } + + + ); + } ) } + + ) } + { ! editingBoundAttribute && ( + + + + { bindings && ( + + ) } + + + ) } + { editingBoundAttribute && ( + + { + setBinding( formData ); + setEditingBoundAttribute( null ); + } } + defaultFormData={ { + label: + attributes?.metadata?.[ + BLOCK_VARIATION_NAME_ATTR + ] ?? '', + slug: attributes?.metadata?.slug ?? '', + uuid: boundField?.uuid ?? crypto.randomUUID(), + description: '', + type: name, + visible: false, + } } + typeIsDisabled={ true } + /> + + ) } + + + ); +}; diff --git a/includes/manager/cpt-settings-panel.js b/includes/manager/src/components/cpt-settings-panel.js similarity index 87% rename from includes/manager/cpt-settings-panel.js rename to includes/manager/src/components/cpt-settings-panel.js index 3eeea38..3841deb 100644 --- a/includes/manager/cpt-settings-panel.js +++ b/includes/manager/src/components/cpt-settings-panel.js @@ -1,4 +1,3 @@ -import { registerPlugin } from '@wordpress/plugins'; import { PluginDocumentSettingPanel } from '@wordpress/editor'; import { TextControl, Dashicon } from '@wordpress/components'; import { __ } from '@wordpress/i18n'; @@ -8,17 +7,18 @@ import { createInterpolateElement, } from '@wordpress/element'; import { useEntityProp } from '@wordpress/core-data'; +import { POST_TYPE_NAME } from '../constants'; -const CreateContentModelCptSettings = function () { +export const CPTSettingsPanel = function () { const [ meta, setMeta ] = useEntityProp( 'postType', - window.contentModelFields.postType, + POST_TYPE_NAME, 'meta' ); const [ title, setTitle ] = useEntityProp( 'postType', - window.contentModelFields.postType, + POST_TYPE_NAME, 'title' ); @@ -94,8 +94,3 @@ const CreateContentModelCptSettings = function () { ); }; - -// Register the plugin. -registerPlugin( 'create-content-model-cpt-settings', { - render: CreateContentModelCptSettings, -} ); diff --git a/includes/manager/_edit-field.js b/includes/manager/src/components/edit-field.js similarity index 91% rename from includes/manager/_edit-field.js rename to includes/manager/src/components/edit-field.js index b1c2fe2..7e8b5fa 100644 --- a/includes/manager/_edit-field.js +++ b/includes/manager/src/components/edit-field.js @@ -14,7 +14,7 @@ import { FlexItem, } from '@wordpress/components'; import { __ } from '@wordpress/i18n'; -import { useState } from '@wordpress/element'; +import { useState, useEffect } from '@wordpress/element'; import { trash, chevronUp, @@ -22,17 +22,9 @@ import { blockDefault, post, } from '@wordpress/icons'; -import { useEffect } from '@wordpress/element'; -import SUPPORTED_BLOCK_ATTRIBUTES from './_supported-attributes'; +import { SUPPORTED_BLOCK_ATTRIBUTES } from '../constants'; -/** - * Display a form to edit a field. - * @param {Object} props - * @param {Function} props.onSave - * @param {Object} props.defaultFormData (to be updated with the field data for editing) - * @returns EditFieldForm - */ const EditFieldForm = ( { field = { label: '', @@ -98,11 +90,16 @@ const EditFieldForm = ( { icon={ trash } title={ __( 'Delete Field' ) } onClick={ () => { - confirm( + // eslint-disable-next-line no-alert + const userWantsToDelete = confirm( __( 'Are you sure you want to delete this field?' ) - ) && onDelete( formData ); + ); + + if ( userWantsToDelete ) { + onDelete( formData ); + } } } /> ) } diff --git a/includes/manager/fields-ui.js b/includes/manager/src/components/fields-ui.js similarity index 89% rename from includes/manager/fields-ui.js rename to includes/manager/src/components/fields-ui.js index c2c6c50..406de5b 100644 --- a/includes/manager/fields-ui.js +++ b/includes/manager/src/components/fields-ui.js @@ -1,4 +1,3 @@ -import { registerPlugin } from '@wordpress/plugins'; import { PluginDocumentSettingPanel } from '@wordpress/editor'; import { Button, @@ -17,16 +16,13 @@ import { useEntityProp } from '@wordpress/core-data'; import { useState } from '@wordpress/element'; import { seen, unseen, blockDefault } from '@wordpress/icons'; -import EditFieldForm from './_edit-field'; +import EditFieldForm from './edit-field'; +import { POST_TYPE_NAME } from '../constants'; -const CreateContentModelPageSettings = function () { +export const FieldsUI = function () { const [ isFieldsOpen, setFieldsOpen ] = useState( false ); - const [ meta ] = useEntityProp( - 'postType', - window.contentModelFields.postType, - 'meta' - ); + 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 ) : []; @@ -34,7 +30,7 @@ const CreateContentModelPageSettings = function () { // Add UUID to fields fields.forEach( ( field ) => { if ( ! field.uuid ) { - field.uuid = window.crypto.randomUUID(); + field.uuid = crypto.randomUUID(); } } ); @@ -104,7 +100,7 @@ const CreateContentModelPageSettings = function () { const FieldsList = () => { const [ meta, setMeta ] = useEntityProp( 'postType', - window.contentModelFields.postType, + POST_TYPE_NAME, 'meta' ); @@ -168,7 +164,7 @@ const FieldsList = () => { setFields( [ ...fields, { - uuid: window.crypto.randomUUID(), + uuid: crypto.randomUUID(), label: '', slug: '', description: '', @@ -184,8 +180,3 @@ const FieldsList = () => { ); }; - -// Register the plugin. -registerPlugin( 'create-content-model-page-settings', { - render: CreateContentModelPageSettings, -} ); diff --git a/includes/manager/_manage-bindings.js b/includes/manager/src/components/manage-bindings.js similarity index 86% rename from includes/manager/_manage-bindings.js rename to includes/manager/src/components/manage-bindings.js index 66ea56b..d24900f 100644 --- a/includes/manager/_manage-bindings.js +++ b/includes/manager/src/components/manage-bindings.js @@ -2,14 +2,8 @@ import { Button, TextControl } from '@wordpress/components'; import { __ } from '@wordpress/i18n'; import { useEntityProp } from '@wordpress/core-data'; import { useState, useEffect } from '@wordpress/element'; +import { POST_TYPE_NAME } from '../constants'; -/** - * Display a form to edit a field. - * @param {Object} props - * @param {Function} props.onSave - * @param {Object} props.defaultFormData (to be updated with the field data for editing) - * @returns EditFieldForm - */ const ManageBindings = ( { defaultFormData = { label: '', @@ -17,7 +11,7 @@ const ManageBindings = ( { description: '', type: 'text', visible: true, - uuid: window.crypto.randomUUID(), + uuid: crypto.randomUUID(), }, onSave = () => {}, } ) => { @@ -26,7 +20,7 @@ const ManageBindings = ( { const [ meta, setMeta ] = useEntityProp( 'postType', - contentModelFields.postType, + POST_TYPE_NAME, 'meta' ); diff --git a/includes/manager/src/constants.js b/includes/manager/src/constants.js new file mode 100644 index 0000000..655c793 --- /dev/null +++ b/includes/manager/src/constants.js @@ -0,0 +1,11 @@ +// https://github.com/WordPress/WordPress/blob/master/wp-includes/class-wp-block.php#L246-L251 +export const SUPPORTED_BLOCK_ATTRIBUTES = { + 'core/group': [ 'content' ], + 'core/paragraph': [ 'content' ], + 'core/heading': [ 'content' ], + 'core/image': [ 'id', 'url', 'title', 'alt' ], + 'core/button': [ 'url', 'text', 'linkTarget', 'rel' ], +}; + +export const { BINDINGS_KEY, BLOCK_VARIATION_NAME_ATTR, POST_TYPE_NAME } = + window.contentModelData; diff --git a/includes/manager/content-model-title-length-restrictor.js b/includes/manager/src/hooks/use-content-model-name-length-restrictor.js similarity index 79% rename from includes/manager/content-model-title-length-restrictor.js rename to includes/manager/src/hooks/use-content-model-name-length-restrictor.js index a9b8b0a..940db3b 100644 --- a/includes/manager/content-model-title-length-restrictor.js +++ b/includes/manager/src/hooks/use-content-model-name-length-restrictor.js @@ -3,9 +3,8 @@ import { useEffect } from '@wordpress/element'; import { useSelect, useDispatch } from '@wordpress/data'; import { store as editorStore } from '@wordpress/editor'; import { store as noticesStore } from '@wordpress/notices'; -import { registerPlugin } from '@wordpress/plugins'; -const ContentModelLengthRestrictor = () => { +export const useContentModelNameLengthRestrictor = () => { const { editPost } = useDispatch( editorStore ); const { createNotice } = useDispatch( noticesStore ); @@ -27,7 +26,3 @@ const ContentModelLengthRestrictor = () => { } }, [ title, editPost, createNotice ] ); }; - -registerPlugin( 'content-model-title-length-restrictor', { - render: ContentModelLengthRestrictor, -} ); diff --git a/includes/manager/default-value-placeholder-changer.js b/includes/manager/src/hooks/use-default-value-placeholder-changer.js similarity index 83% rename from includes/manager/default-value-placeholder-changer.js rename to includes/manager/src/hooks/use-default-value-placeholder-changer.js index 018b2b1..d775509 100644 --- a/includes/manager/default-value-placeholder-changer.js +++ b/includes/manager/src/hooks/use-default-value-placeholder-changer.js @@ -2,9 +2,8 @@ import { __, sprintf } from '@wordpress/i18n'; import { useEffect } from '@wordpress/element'; import { useSelect, useDispatch } from '@wordpress/data'; import { store as blockEditorStore } from '@wordpress/block-editor'; -import { registerPlugin } from '@wordpress/plugins'; -const ContentModelDefaultValuePlaceholderChanger = () => { +export const useDefaultValuePlaceholderChanger = () => { const boundBlocks = useSelect( ( select ) => { const blocks = select( blockEditorStore ).getBlocks(); const map = {}; @@ -41,7 +40,3 @@ const ContentModelDefaultValuePlaceholderChanger = () => { } ); }, [ boundBlocks, updateBlockAttributes ] ); }; - -registerPlugin( 'content-model-default-value-placeholder-changer', { - render: ContentModelDefaultValuePlaceholderChanger, -} ); diff --git a/includes/manager/src/register-attribute-binder.js b/includes/manager/src/register-attribute-binder.js new file mode 100644 index 0000000..6146040 --- /dev/null +++ b/includes/manager/src/register-attribute-binder.js @@ -0,0 +1,51 @@ +import { addFilter } from '@wordpress/hooks'; +import { useMemo } from '@wordpress/element'; +import { createHigherOrderComponent } from '@wordpress/compose'; +import { useSelect } from '@wordpress/data'; +import { store as blockEditorStore } from '@wordpress/block-editor'; + +import { SUPPORTED_BLOCK_ATTRIBUTES, BINDINGS_KEY } from './constants'; +import { AttributeBinderPanel } from './components/attribute-binder-panel'; + +const withAttributeBinder = createHigherOrderComponent( ( BlockEdit ) => { + return ( props ) => { + const { getBlockParentsByBlockName, getBlocksByClientId } = + useSelect( blockEditorStore ); + + const blockParentsByBlockName = getBlockParentsByBlockName( + props.clientId, + [ 'core/group' ] + ); + + const parentHasBindings = useMemo( () => { + return ( + getBlocksByClientId( blockParentsByBlockName ).filter( + ( block ) => + Object.keys( + block?.attributes?.metadata?.[ BINDINGS_KEY ] || {} + ).length > 0 + ).length > 0 + ); + }, [ blockParentsByBlockName, getBlocksByClientId ] ); + + const shouldDisplayAttributeBinderPanel = + SUPPORTED_BLOCK_ATTRIBUTES[ props.name ] && ! parentHasBindings; + + return ( + <> + + { shouldDisplayAttributeBinderPanel && ( + + ) } + + ); + }; +}, 'withAttributeBinder' ); + +export const registerAttributeBinder = () => { + addFilter( + 'editor.BlockEdit', + 'content-model/attribute-binder', + withAttributeBinder + ); +}; diff --git a/includes/manager/src/register-content-model-title-length-restrictor.js b/includes/manager/src/register-content-model-title-length-restrictor.js new file mode 100644 index 0000000..95ccda4 --- /dev/null +++ b/includes/manager/src/register-content-model-title-length-restrictor.js @@ -0,0 +1,11 @@ +import { registerPlugin } from '@wordpress/plugins'; +import { useContentModelNameLengthRestrictor } from './hooks/use-content-model-name-length-restrictor'; + +export const registerContentModelLengthRestrictor = () => { + registerPlugin( 'content-model-title-length-restrictor', { + render: () => { + // eslint-disable-next-line react-hooks/rules-of-hooks + useContentModelNameLengthRestrictor(); + }, + } ); +}; diff --git a/includes/manager/src/register-cpt-settings-panel.js b/includes/manager/src/register-cpt-settings-panel.js new file mode 100644 index 0000000..b3033b5 --- /dev/null +++ b/includes/manager/src/register-cpt-settings-panel.js @@ -0,0 +1,8 @@ +import { registerPlugin } from '@wordpress/plugins'; +import { CPTSettingsPanel } from './components/cpt-settings-panel'; + +export const registerCPTSettingsPanel = () => { + registerPlugin( 'create-content-model-cpt-settings-pannel', { + render: CPTSettingsPanel, + } ); +}; diff --git a/includes/manager/src/register-default-value-placeholder-changer.js b/includes/manager/src/register-default-value-placeholder-changer.js new file mode 100644 index 0000000..16ebd14 --- /dev/null +++ b/includes/manager/src/register-default-value-placeholder-changer.js @@ -0,0 +1,11 @@ +import { registerPlugin } from '@wordpress/plugins'; +import { useDefaultValuePlaceholderChanger } from './hooks/use-default-value-placeholder-changer'; + +export const registerDefaultValuePlaceholderChanger = () => { + registerPlugin( 'content-model-default-value-placeholder-changer', { + render: () => { + // eslint-disable-next-line react-hooks/rules-of-hooks + useDefaultValuePlaceholderChanger(); + }, + } ); +}; diff --git a/includes/manager/src/register-fields-ui.js b/includes/manager/src/register-fields-ui.js new file mode 100644 index 0000000..dc5cf89 --- /dev/null +++ b/includes/manager/src/register-fields-ui.js @@ -0,0 +1,8 @@ +import { registerPlugin } from '@wordpress/plugins'; +import { FieldsUI } from './components/fields-ui'; + +export const registerFieldsUI = () => { + registerPlugin( 'create-content-model-fields-ui', { + render: FieldsUI, + } ); +}; diff --git a/includes/runtime/class-content-model.php b/includes/runtime/class-content-model.php index 97b2669..f9e6fc6 100644 --- a/includes/runtime/class-content-model.php +++ b/includes/runtime/class-content-model.php @@ -81,10 +81,8 @@ public function __construct( WP_Post $content_model_post ) { $this->blocks = $this->inflate_template_blocks( $this->template ); $this->fields = json_decode( get_post_meta( $content_model_post->ID, 'fields', true ), true ); $this->register_meta_fields(); - $this->maybe_enqueue_the_fields_ui(); - $this->maybe_enqueue_bound_group_extractor(); - $this->maybe_enqueue_content_locking(); - $this->maybe_enqueue_fallback_value_clearer(); + + add_action( 'enqueue_block_editor_assets', array( $this, 'maybe_enqueue_scripts' ) ); add_filter( 'block_categories_all', array( $this, 'register_block_category' ) ); @@ -462,167 +460,35 @@ public function swap_post_content_with_hydrated_template( $post_content ) { } /** - * When you use the Bindings API, the Editor automatically extracts bound attributes as post meta. - * But because we're binding to the inner blocks of Groups (and not an attribute), - * we need to manually extract it. - * - * @return void - */ - private function maybe_enqueue_bound_group_extractor() { - add_action( - 'enqueue_block_editor_assets', - function () { - global $post; - - if ( ! $post || $this->slug !== $post->post_type ) { - return; - } - - $asset_file = include CONTENT_MODEL_PLUGIN_PATH . 'includes/runtime/dist/bound-group-extractor.asset.php'; - - wp_register_script( - 'data-types/bound-group-extractor', - CONTENT_MODEL_PLUGIN_URL . '/includes/runtime/dist/bound-group-extractor.js', - $asset_file['dependencies'], - $asset_file['version'], - true - ); - - wp_localize_script( - 'data-types/bound-group-extractor', - 'contentModelFields', - array( - 'postType' => $this->slug, - 'fields' => $this->fields, - ) - ); - - wp_enqueue_script( 'data-types/bound-group-extractor' ); - } - ); - } - - /** - * Conditionally enqueues the fields UI script for the block editor. - * - * Checks if the current post is of the correct type before enqueueing the script. - * - * @return void - */ - private function maybe_enqueue_the_fields_ui() { - add_action( - 'enqueue_block_editor_assets', - function () { - global $post; - - if ( ! $post || $this->slug !== $post->post_type ) { - return; - } - - $asset_file = include CONTENT_MODEL_PLUGIN_PATH . 'includes/runtime/dist/fields-ui.asset.php'; - - wp_register_script( - 'data-types/fields-ui', - CONTENT_MODEL_PLUGIN_URL . '/includes/runtime/dist/fields-ui.js', - $asset_file['dependencies'], - $asset_file['version'], - true - ); - - wp_localize_script( - 'data-types/fields-ui', - 'contentModelFields', - array( - 'postType' => $this->slug, - 'fields' => $this->fields, - ) - ); - - wp_enqueue_script( 'data-types/fields-ui' ); - } - ); - } - - - /** - * Conditionally enqueues the fields UI script for the block editor. - * - * Checks if the current post is of the correct type before enqueueing the script. + * Enqueue the helper scripts if entering data to a content model. * * @return void */ - private function maybe_enqueue_content_locking() { - add_action( - 'enqueue_block_editor_assets', - function () { - global $post; - - if ( ! $post || $this->slug !== $post->post_type ) { - return; - } - - $asset_file = include CONTENT_MODEL_PLUGIN_PATH . 'includes/runtime/dist/content-locking.asset.php'; + public function maybe_enqueue_scripts() { + global $post; - wp_register_script( - 'data-types/content-locking', - CONTENT_MODEL_PLUGIN_URL . '/includes/runtime/dist/content-locking.js', - $asset_file['dependencies'], - $asset_file['version'], - true - ); + if ( ! $post || $this->slug !== $post->post_type ) { + return; + } - wp_localize_script( - 'data-types/content-locking', - 'contentModelFields', - array( - 'postType' => $this->slug, - 'fields' => $this->fields, - ) - ); + $asset_file = include CONTENT_MODEL_PLUGIN_PATH . 'includes/runtime/dist/runtime.asset.php'; - wp_enqueue_script( 'data-types/content-locking' ); - } + wp_enqueue_script( + 'content-model/runtime', + CONTENT_MODEL_PLUGIN_URL . '/includes/runtime/dist/runtime.js', + $asset_file['dependencies'], + $asset_file['version'], + true ); - } - /** - * Conditionally enqueues the fallback value clearer, allowing the block to become editable. - * - * Checks if the current post is of the correct type before enqueueing the script. - * - * @return void - */ - private function maybe_enqueue_fallback_value_clearer() { - add_action( - 'enqueue_block_editor_assets', - function () { - global $post; - - if ( ! $post || $this->slug !== $post->post_type ) { - return; - } - - $asset_file = include CONTENT_MODEL_PLUGIN_PATH . 'includes/runtime/dist/fallback-value-clearer.asset.php'; - - wp_register_script( - 'data-types/fallback-value-clearer', - CONTENT_MODEL_PLUGIN_URL . '/includes/runtime/dist/fallback-value-clearer.js', - $asset_file['dependencies'], - $asset_file['version'], - true - ); - - wp_localize_script( - 'data-types/fallback-value-clearer', - 'contentModelFields', - array( - 'postType' => $this->slug, - 'FALLBACK_VALUE_PLACEHOLDER' => self::FALLBACK_VALUE_PLACEHOLDER, - ) - ); - - wp_enqueue_script( 'data-types/fallback-value-clearer' ); - } + wp_localize_script( + 'content-model/runtime', + 'contentModelData', + array( + 'POST_TYPE' => $this->slug, + 'FIELDS' => $this->fields, + 'FALLBACK_VALUE_PLACEHOLDER' => self::FALLBACK_VALUE_PLACEHOLDER, + ) ); } } diff --git a/includes/runtime/fields-ui.js b/includes/runtime/fields-ui.js deleted file mode 100644 index 989692f..0000000 --- a/includes/runtime/fields-ui.js +++ /dev/null @@ -1,201 +0,0 @@ -import { registerPlugin } from '@wordpress/plugins'; -import { PluginDocumentSettingPanel } from '@wordpress/editor'; -import { - Button, - Modal, - TextControl, - TextareaControl, - __experimentalVStack as VStack, - Card, - CardBody, - CardFooter, -} from '@wordpress/components'; -import { MediaPlaceholder } from '@wordpress/block-editor'; -import { __ } from '@wordpress/i18n'; -import { useEntityProp } from '@wordpress/core-data'; -import { useState } from '@wordpress/element'; - -/** - * Our base plugin component. - * @returns CreateContentModelFieldsUI - */ -const CreateContentModelFieldsUI = function () { - const [ isFieldsOpen, setFieldsOpen ] = useState( false ); - - const fields = contentModelFields.fields; - - if ( - ! fields || - fields.filter( ( field ) => field.visible ).length === 0 - ) { - return null; - } - - return ( - - - - - - - { isFieldsOpen && ( - setFieldsOpen( false ) } - > - - - ) } - - ); -}; - -/** - * Display the list of fields inside the modal. - * @returns FieldsList - */ -const FieldsList = () => { - const fields = contentModelFields.fields; - - return ( - <> - - { fields - .filter( ( field ) => field.visible ) - .map( ( field ) => ( - - ) ) } - - - ); -}; - -/** - * Display a row for a field. - * @param {Object} field - * @returns FieldRow - */ -const FieldRow = ( { field } ) => { - const [ meta, setMeta ] = useEntityProp( - 'postType', - contentModelFields.postType, - 'meta' - ); - - const value = meta[ field.slug ] ?? ''; - - return ( - <> -
- { - setMeta( { - [ slug ]: value, - } ); - } } - /> - - { field.description } - -
- - ); -}; - -/** - * Display the input for a field. - * @param {Object} field - * @param {boolean} isDisabled - * @returns FieldInput - */ -const FieldInput = ( { field, isDisabled = false, value, saveChanges } ) => { - switch ( field.type ) { - case 'image': - return ( - <> - - { value && ( - - - { - - - - - - ) } - { ! value && ( - - saveChanges( field.slug, value.url ) - } - /> - ) } - - ); - break; - - case 'textarea': - return ( - saveChanges( field.slug, value ) } - /> - ); - break; - - default: - return ( - saveChanges( field.slug, value ) } - /> - ); - break; - } -}; - -// Register the plugin. -registerPlugin( 'create-content-model-fields-ui', { - render: CreateContentModelFieldsUI, -} ); diff --git a/includes/runtime/runtime.js b/includes/runtime/runtime.js new file mode 100644 index 0000000..6767b6b --- /dev/null +++ b/includes/runtime/runtime.js @@ -0,0 +1,9 @@ +import { registerBoundGroupExtractor } from './src/register-bound-group-extractor'; +import { registerContentLocking } from './src/register-content-locking'; +import { registerFallbackValueClearer } from './src/register-fallback-value-clearer'; +import { registerFieldsUI } from './src/register-fields-ui'; + +registerBoundGroupExtractor(); +registerFieldsUI(); +registerContentLocking(); +registerFallbackValueClearer(); diff --git a/includes/runtime/src/components/fields-ui.js b/includes/runtime/src/components/fields-ui.js new file mode 100644 index 0000000..ae5d073 --- /dev/null +++ b/includes/runtime/src/components/fields-ui.js @@ -0,0 +1,156 @@ +import { PluginDocumentSettingPanel } from '@wordpress/editor'; +import { + Button, + Modal, + TextControl, + TextareaControl, + __experimentalVStack as VStack, + Card, + CardBody, + CardFooter, +} from '@wordpress/components'; +import { MediaPlaceholder } from '@wordpress/block-editor'; +import { __ } from '@wordpress/i18n'; +import { useEntityProp } from '@wordpress/core-data'; +import { useState } from '@wordpress/element'; +import { FIELDS, POST_TYPE } from '../constants'; + +export const FieldsUI = function () { + const [ isFieldsOpen, setFieldsOpen ] = useState( false ); + + if ( FIELDS.filter( ( field ) => field.visible ).length === 0 ) { + return null; + } + + return ( + + + + + + + { isFieldsOpen && ( + setFieldsOpen( false ) } + > + + + ) } + + ); +}; + +const FieldsList = ( { fields } ) => { + return ( + + { fields + .filter( ( field ) => field.visible ) + .map( ( field ) => ( + + ) ) } + + ); +}; + +const FieldRow = ( { field } ) => { + const [ meta, setMeta ] = useEntityProp( 'postType', POST_TYPE, 'meta' ); + + const value = meta[ field.slug ] ?? ''; + + return ( +
+ { + setMeta( { + [ slug ]: newValue, + } ); + } } + /> + + { field.description } + +
+ ); +}; + +const FieldInput = ( { field, isDisabled = false, value, saveChanges } ) => { + if ( 'image' === field.type ) { + return ( + <> + + { field.label } + + { ! value && ( + + saveChanges( field.slug, newValue.url ) + } + /> + ) } + { value && ( + + + { + + + + + + ) } + + ); + } + + if ( 'textarea' === field.type ) { + return ( + saveChanges( field.slug, newValue ) } + /> + ); + } + + return ( + saveChanges( field.slug, newValue ) } + /> + ); +}; diff --git a/includes/manager/_supported-attributes.js b/includes/runtime/src/constants.js similarity index 68% rename from includes/manager/_supported-attributes.js rename to includes/runtime/src/constants.js index 370fae7..934af4f 100644 --- a/includes/manager/_supported-attributes.js +++ b/includes/runtime/src/constants.js @@ -1,5 +1,5 @@ // https://github.com/WordPress/WordPress/blob/master/wp-includes/class-wp-block.php#L246-L251 -const SUPPORTED_BLOCK_ATTRIBUTES = { +export const SUPPORTED_BLOCK_ATTRIBUTES = { 'core/group': [ 'content' ], 'core/paragraph': [ 'content' ], 'core/heading': [ 'content' ], @@ -7,4 +7,5 @@ const SUPPORTED_BLOCK_ATTRIBUTES = { 'core/button': [ 'url', 'text', 'linkTarget', 'rel' ], }; -export default SUPPORTED_BLOCK_ATTRIBUTES; +export const { POST_TYPE, FIELDS, FALLBACK_VALUE_PLACEHOLDER } = + window.contentModelData; diff --git a/includes/runtime/bound-group-extractor.js b/includes/runtime/src/hooks/use-bound-group-extractor.js similarity index 73% rename from includes/runtime/bound-group-extractor.js rename to includes/runtime/src/hooks/use-bound-group-extractor.js index 182ca7f..0b31842 100644 --- a/includes/runtime/bound-group-extractor.js +++ b/includes/runtime/src/hooks/use-bound-group-extractor.js @@ -1,9 +1,23 @@ import { useEffect } from '@wordpress/element'; -import { registerPlugin } from '@wordpress/plugins'; import { useEntityProp } from '@wordpress/core-data'; import { useSelect } from '@wordpress/data'; import { store as editorStore } from '@wordpress/block-editor'; import { serialize } from '@wordpress/blocks'; +import { POST_TYPE } from '../constants'; + +export const useBoundGroupExtractor = () => { + const blocks = useSelect( ( select ) => select( editorStore ).getBlocks() ); + + const [ , setMeta ] = useEntityProp( 'postType', POST_TYPE, 'meta' ); + + useEffect( () => { + const boundBlocks = findBoundBlocks( blocks ); + + setMeta( boundBlocks ); + }, [ blocks, setMeta ] ); + + return null; +}; const findBoundBlocks = ( blocks, acc = {} ) => { for ( const block of blocks ) { @@ -24,25 +38,3 @@ const findBoundBlocks = ( blocks, acc = {} ) => { return acc; }; - -const CreateContentModelBoundGroupExtractor = () => { - const blocks = useSelect( ( select ) => select( editorStore ).getBlocks() ); - - const [ , setMeta ] = useEntityProp( - 'postType', - window.contentModelFields.postType, - 'meta' - ); - - useEffect( () => { - const boundBlocks = findBoundBlocks( blocks ); - - setMeta( boundBlocks ); - }, [ blocks, setMeta ] ); - - return null; -}; - -registerPlugin( 'create-content-model-bound-group-extractor', { - render: CreateContentModelBoundGroupExtractor, -} ); diff --git a/includes/runtime/content-locking.js b/includes/runtime/src/hooks/use-content-locking.js similarity index 77% rename from includes/runtime/content-locking.js rename to includes/runtime/src/hooks/use-content-locking.js index 67d1689..80a229f 100644 --- a/includes/runtime/content-locking.js +++ b/includes/runtime/src/hooks/use-content-locking.js @@ -1,16 +1,11 @@ -import { registerPlugin } from '@wordpress/plugins'; import { useEffect } from '@wordpress/element'; -import { dispatch } from '@wordpress/data'; -import { useDispatch } from '@wordpress/data'; +import { dispatch, useDispatch } from '@wordpress/data'; import { store as blockEditorStore } from '@wordpress/block-editor'; +import { SUPPORTED_BLOCK_ATTRIBUTES } from '../constants'; -/** - * Our base plugin component. - * @returns CreateContentModelPageSettings - */ -const CreateContentModelContentLocking = function () { - const fields = contentModelFields.fields; +const SUPPORTED_BLOCKS = Object.keys( SUPPORTED_BLOCK_ATTRIBUTES ); +export const useContentLocking = function () { const blocks = wp.data.select( 'core/block-editor' ).getBlocks(); const currentBlock = wp.data @@ -19,25 +14,12 @@ const CreateContentModelContentLocking = function () { const { setBlockEditingMode } = useDispatch( blockEditorStore ); - if ( ! fields ) { - return null; - } - useEffect( () => { if ( blocks.length > 0 ) { parseBlocks( blocks, setBlockEditingMode ); } }, [ blocks, setBlockEditingMode, currentBlock ] ); - - return; }; -const SUPPORTED_BLOCKS = [ - 'core/group', - 'core/paragraph', - 'core/heading', - 'core/image', - 'core/button', -]; const parseBlocks = ( blocks, setEditMode, forceEnabled = false ) => { blocks.forEach( ( block ) => { @@ -100,8 +82,3 @@ const findBoundGroup = ( blocks ) => { } return null; }; - -// Register the plugin. -registerPlugin( 'create-content-model-content-locking', { - render: CreateContentModelContentLocking, -} ); diff --git a/includes/runtime/fallback-value-clearer.js b/includes/runtime/src/hooks/use-fallback-value-clearer.js similarity index 81% rename from includes/runtime/fallback-value-clearer.js rename to includes/runtime/src/hooks/use-fallback-value-clearer.js index 0529350..54e2e48 100644 --- a/includes/runtime/fallback-value-clearer.js +++ b/includes/runtime/src/hooks/use-fallback-value-clearer.js @@ -1,8 +1,8 @@ import { useLayoutEffect } from '@wordpress/element'; -import { registerPlugin } from '@wordpress/plugins'; import { useEntityProp } from '@wordpress/core-data'; import { store as blockEditorStore } from '@wordpress/block-editor'; import { useSelect, useDispatch } from '@wordpress/data'; +import { FALLBACK_VALUE_PLACEHOLDER, POST_TYPE } from '../constants'; /** * This allows the user to edit values that are bound to an attribute. @@ -13,12 +13,8 @@ import { useSelect, useDispatch } from '@wordpress/data'; * * See https://github.com/Automattic/create-content-model/issues/63 for the problem. */ -const CreateContentModelFallbackValueClearer = () => { - const [ meta, setMeta ] = useEntityProp( - 'postType', - window.contentModelFields.postType, - 'meta' - ); +export const useFallbackValueClearer = () => { + const [ meta, setMeta ] = useEntityProp( 'postType', POST_TYPE, 'meta' ); const { updateBlockAttributes } = useDispatch( blockEditorStore ); @@ -56,10 +52,8 @@ const CreateContentModelFallbackValueClearer = () => { ( [ blockId, metaInfos ] ) => { metaInfos.forEach( ( { metaKey, blockName } ) => { const value = meta[ metaKey ]; - if ( - value === - window.contentModelFields.FALLBACK_VALUE_PLACEHOLDER - ) { + + if ( value === FALLBACK_VALUE_PLACEHOLDER ) { setMeta( { [ metaKey ]: '' } ); updateBlockAttributes( blockId, { @@ -73,7 +67,3 @@ const CreateContentModelFallbackValueClearer = () => { return null; }; - -registerPlugin( 'create-content-model-fallback-value-clearer', { - render: CreateContentModelFallbackValueClearer, -} ); diff --git a/includes/runtime/src/register-bound-group-extractor.js b/includes/runtime/src/register-bound-group-extractor.js new file mode 100644 index 0000000..5b6f816 --- /dev/null +++ b/includes/runtime/src/register-bound-group-extractor.js @@ -0,0 +1,11 @@ +import { registerPlugin } from '@wordpress/plugins'; +import { useBoundGroupExtractor } from './hooks/use-bound-group-extractor'; + +export const registerBoundGroupExtractor = () => { + registerPlugin( 'create-content-model-bound-group-extractor', { + render: () => { + // eslint-disable-next-line react-hooks/rules-of-hooks + useBoundGroupExtractor(); + }, + } ); +}; diff --git a/includes/runtime/src/register-content-locking.js b/includes/runtime/src/register-content-locking.js new file mode 100644 index 0000000..8c8baf5 --- /dev/null +++ b/includes/runtime/src/register-content-locking.js @@ -0,0 +1,11 @@ +import { registerPlugin } from '@wordpress/plugins'; +import { useContentLocking } from './hooks/use-content-locking'; + +export const registerContentLocking = () => { + registerPlugin( 'create-content-model-content-locking', { + render: () => { + // eslint-disable-next-line react-hooks/rules-of-hooks + useContentLocking(); + }, + } ); +}; diff --git a/includes/runtime/src/register-fallback-value-clearer.js b/includes/runtime/src/register-fallback-value-clearer.js new file mode 100644 index 0000000..305e0c1 --- /dev/null +++ b/includes/runtime/src/register-fallback-value-clearer.js @@ -0,0 +1,11 @@ +import { registerPlugin } from '@wordpress/plugins'; +import { useFallbackValueClearer } from './hooks/use-fallback-value-clearer'; + +export const registerFallbackValueClearer = () => { + registerPlugin( 'create-content-model-fallback-value-clearer', { + render: () => { + // eslint-disable-next-line react-hooks/rules-of-hooks + useFallbackValueClearer(); + }, + } ); +}; diff --git a/includes/runtime/src/register-fields-ui.js b/includes/runtime/src/register-fields-ui.js new file mode 100644 index 0000000..220bb32 --- /dev/null +++ b/includes/runtime/src/register-fields-ui.js @@ -0,0 +1,9 @@ +import { registerPlugin } from '@wordpress/plugins'; +import { FieldsUI } from './components/fields-ui'; + +export const registerFieldsUI = () => { + // Register the plugin. + registerPlugin( 'create-content-model-fields-ui', { + render: FieldsUI, + } ); +}; diff --git a/webpack.config.js b/webpack.config.js index 851b3f4..fcaa68a 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -8,9 +8,6 @@ const includesDir = path.resolve( process.cwd(), 'includes' ); const entries = glob .sync( '*/*.js', { cwd: includesDir } ) - .filter( ( entry ) => { - return ! entry.split( '/' )[ 1 ].startsWith( '_' ); - } ) .reduce( ( acc, entry ) => { const [ folder, name ] = entry.split( '/' );