Skip to content

Commit

Permalink
Block Bindings: Open the stable editor APIs (WordPress#65713)
Browse files Browse the repository at this point in the history
* Try use select and dispatch instead of registry

* Fix `setValues`

* Open `useBlockBindingsUtils`

* Open registration APIs

* Restrict `getFieldsList` API

* Update `useBlockBindingsUtils` docs

* Update register/unregister bindings docs

* Simplify API functions examples

Co-authored-by: Greg Ziółkowski <grzegorz@gziolo.pl>

* Update docs

* Update comments

---------

Co-authored-by: Carlos Bravo <37012961+cbravobernal@users.noreply.github.com>
Co-authored-by: Greg Ziółkowski <grzegorz@gziolo.pl>

Co-authored-by: cbravobernal <cbravobernal@git.wordpress.org>
Co-authored-by: SantosGuillamot <santosguillamot@git.wordpress.org>
Co-authored-by: gziolo <gziolo@git.wordpress.org>
  • Loading branch information
4 people authored Oct 1, 2024
1 parent 5128f04 commit 1c081c8
Show file tree
Hide file tree
Showing 18 changed files with 199 additions and 63 deletions.
42 changes: 42 additions & 0 deletions packages/block-editor/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -839,6 +839,48 @@ _Related_

- <https://github.com/WordPress/gutenberg/blob/HEAD/packages/block-editor/src/components/url-popover/README.md>

### useBlockBindingsUtils

Retrieves the existing utils needed to update the block `bindings` metadata. They can be used to create, modify, or remove connections from the existing block attributes.

It contains the following utils:

- `updateBlockBindings`: Updates the value of the bindings connected to block attributes. It can be used to remove a specific binding by setting the value to `undefined`.
- `removeAllBlockBindings`: Removes the bindings property of the `metadata` attribute.

_Usage_

```js
import { useBlockBindingsUtils } from '@wordpress/block-editor';
const { updateBlockBindings, removeAllBlockBindings } = useBlockBindingsUtils();

// Update url and alt attributes.
updateBlockBindings( {
url: {
source: 'core/post-meta',
args: {
key: 'url_custom_field',
},
},
alt: {
source: 'core/post-meta',
args: {
key: 'text_custom_field',
},
},
} );

// Remove binding from url attribute.
updateBlockBindings( { url: undefined } );

// Remove bindings from all attributes.
removeAllBlockBindings();
```

_Returns_

- `?WPBlockBindingsUtils`: Object containing the block bindings utils.

### useBlockCommands

Undocumented declaration.
Expand Down
4 changes: 1 addition & 3 deletions packages/block-editor/src/components/rich-text/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import {
removeFormat,
} from '@wordpress/rich-text';
import { Popover } from '@wordpress/components';
import { store as blocksStore } from '@wordpress/blocks';
import { getBlockBindingsSource } from '@wordpress/blocks';
import deprecated from '@wordpress/deprecated';
import { __, sprintf } from '@wordpress/i18n';

Expand All @@ -39,7 +39,6 @@ import FormatEdit from './format-edit';
import { getAllowedFormats } from './utils';
import { Content, valueToHTMLString } from './content';
import { withDeprecations } from './with-deprecations';
import { unlock } from '../../lock-unlock';
import { canBindBlock } from '../../hooks/use-bindings-attributes';
import BlockContext from '../block-context';

Expand Down Expand Up @@ -175,7 +174,6 @@ export function RichTextWrapper(
}

const relatedBinding = blockBindings[ identifier ];
const { getBlockBindingsSource } = unlock( select( blocksStore ) );
const blockBindingsSource = getBlockBindingsSource(
relatedBinding.source
);
Expand Down
10 changes: 5 additions & 5 deletions packages/block-editor/src/hooks/block-bindings.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@
* WordPress dependencies
*/
import { __ } from '@wordpress/i18n';
import { privateApis as blocksPrivateApis } from '@wordpress/blocks';
import {
getBlockBindingsSource,
getBlockBindingsSources,
} from '@wordpress/blocks';
import {
__experimentalItemGroup as ItemGroup,
__experimentalItem as Item,
Expand Down Expand Up @@ -47,7 +50,6 @@ const useToolsPanelDropdownMenuProps = () => {
};

function BlockBindingsPanelDropdown( { fieldsList, attribute, binding } ) {
const { getBlockBindingsSources } = unlock( blocksPrivateApis );
const registeredSources = getBlockBindingsSources();
const { updateBlockBindings } = useBlockBindingsUtils();
const currentKey = binding?.args?.key;
Expand Down Expand Up @@ -96,8 +98,7 @@ function BlockBindingsPanelDropdown( { fieldsList, attribute, binding } ) {

function BlockBindingsAttribute( { attribute, binding, fieldsList } ) {
const { source: sourceName, args } = binding || {};
const sourceProps =
unlock( blocksPrivateApis ).getBlockBindingsSource( sourceName );
const sourceProps = getBlockBindingsSource( sourceName );
const isSourceInvalid = ! sourceProps;
return (
<VStack className="block-editor-bindings__item" spacing={ 0 }>
Expand Down Expand Up @@ -200,7 +201,6 @@ export const BlockBindingsPanel = ( { name: blockName, metadata } ) => {
if ( ! bindableAttributes || bindableAttributes.length === 0 ) {
return EMPTY_OBJECT;
}
const { getBlockBindingsSources } = unlock( blocksPrivateApis );
const registeredSources = getBlockBindingsSources();
Object.entries( registeredSources ).forEach(
( [ sourceName, { getFieldsList, usesContext } ] ) => {
Expand Down
2 changes: 0 additions & 2 deletions packages/block-editor/src/private-apis.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,6 @@ import { PrivatePublishDateTimePicker } from './components/publish-date-time-pic
import useSpacingSizes from './components/spacing-sizes-control/hooks/use-spacing-sizes';
import useBlockDisplayTitle from './components/block-title/use-block-display-title';
import TabbedSidebar from './components/tabbed-sidebar';
import { useBlockBindingsUtils } from './utils/block-bindings';

/**
* Private @wordpress/block-editor APIs.
Expand Down Expand Up @@ -92,6 +91,5 @@ lock( privateApis, {
useBlockDisplayTitle,
__unstableBlockStyleVariationOverridesWithConfig,
setBackgroundStyleDefaults,
useBlockBindingsUtils,
sectionRootClientIdKey,
} );
47 changes: 47 additions & 0 deletions packages/block-editor/src/utils/block-bindings.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,53 @@ function isObjectEmpty( object ) {
return ! object || Object.keys( object ).length === 0;
}

/**
* Contains utils to update the block `bindings` metadata.
*
* @typedef {Object} WPBlockBindingsUtils
*
* @property {Function} updateBlockBindings Updates the value of the bindings connected to block attributes.
* @property {Function} removeAllBlockBindings Removes the bindings property of the `metadata` attribute.
*/

/**
* Retrieves the existing utils needed to update the block `bindings` metadata.
* They can be used to create, modify, or remove connections from the existing block attributes.
*
* It contains the following utils:
* - `updateBlockBindings`: Updates the value of the bindings connected to block attributes. It can be used to remove a specific binding by setting the value to `undefined`.
* - `removeAllBlockBindings`: Removes the bindings property of the `metadata` attribute.
*
* @return {?WPBlockBindingsUtils} Object containing the block bindings utils.
*
* @example
* ```js
* import { useBlockBindingsUtils } from '@wordpress/block-editor'
* const { updateBlockBindings, removeAllBlockBindings } = useBlockBindingsUtils();
*
* // Update url and alt attributes.
* updateBlockBindings( {
* url: {
* source: 'core/post-meta',
* args: {
* key: 'url_custom_field',
* },
* },
* alt: {
* source: 'core/post-meta',
* args: {
* key: 'text_custom_field',
* },
* },
* } );
*
* // Remove binding from url attribute.
* updateBlockBindings( { url: undefined } );
*
* // Remove bindings from all attributes.
* removeAllBlockBindings();
* ```
*/
export function useBlockBindingsUtils() {
const { clientId } = useBlockEditContext();
const { updateBlockAttributes } = useDispatch( blockEditorStore );
Expand Down
1 change: 1 addition & 0 deletions packages/block-editor/src/utils/index.js
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export { default as transformStyles } from './transform-styles';
export { default as getPxFromCssUnit } from './get-px-from-css-unit';
export { useBlockBindingsUtils } from './block-bindings';
3 changes: 1 addition & 2 deletions packages/block-library/src/block/edit.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ import {
InnerBlocks,
} from '@wordpress/block-editor';
import { privateApis as patternsPrivateApis } from '@wordpress/patterns';
import { store as blocksStore } from '@wordpress/blocks';
import { getBlockBindingsSource } from '@wordpress/blocks';

/**
* Internal dependencies
Expand Down Expand Up @@ -196,7 +196,6 @@ function ReusableBlockEdit( {
( select ) => {
const { getBlocks, getSettings, getBlockEditingMode } =
select( blockEditorStore );
const { getBlockBindingsSource } = unlock( select( blocksStore ) );
// For editing link to the site editor if the theme and user permissions support it.
return {
innerBlocks: getBlocks( patternClientId ),
Expand Down
9 changes: 4 additions & 5 deletions packages/block-library/src/button/edit.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import clsx from 'clsx';
import { NEW_TAB_TARGET, NOFOLLOW_REL } from './constants';
import { getUpdatedLinkAttributes } from './get-updated-link-attributes';
import removeAnchorTag from '../utils/remove-anchor-tag';
import { unlock } from '../lock-unlock';

/**
* WordPress dependencies
Expand Down Expand Up @@ -45,7 +44,7 @@ import {
createBlock,
cloneBlock,
getDefaultBlockName,
store as blocksStore,
getBlockBindingsSource,
} from '@wordpress/blocks';
import { useMergeRefs, useRefEffect } from '@wordpress/compose';
import { useSelect, useDispatch } from '@wordpress/data';
Expand Down Expand Up @@ -240,9 +239,9 @@ function ButtonEdit( props ) {
return {};
}

const blockBindingsSource = unlock(
select( blocksStore )
).getBlockBindingsSource( metadata?.bindings?.url?.source );
const blockBindingsSource = getBlockBindingsSource(
metadata?.bindings?.url?.source
);

return {
lockUrlControls:
Expand Down
9 changes: 4 additions & 5 deletions packages/block-library/src/image/edit.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import clsx from 'clsx';
* WordPress dependencies
*/
import { isBlobURL, createBlobURL } from '@wordpress/blob';
import { store as blocksStore, createBlock } from '@wordpress/blocks';
import { createBlock, getBlockBindingsSource } from '@wordpress/blocks';
import { Placeholder } from '@wordpress/components';
import { useDispatch, useSelect } from '@wordpress/data';
import {
Expand All @@ -28,7 +28,6 @@ import { useResizeObserver } from '@wordpress/compose';
/**
* Internal dependencies
*/
import { unlock } from '../lock-unlock';
import { useUploadMediaFromBlobURL } from '../utils/hooks';
import Image from './image';
import { isValidFileType } from './utils';
Expand Down Expand Up @@ -372,9 +371,9 @@ export function ImageEdit( {
return {};
}

const blockBindingsSource = unlock(
select( blocksStore )
).getBlockBindingsSource( metadata?.bindings?.url?.source );
const blockBindingsSource = getBlockBindingsSource(
metadata?.bindings?.url?.source
);

return {
lockUrlControls:
Expand Down
3 changes: 1 addition & 2 deletions packages/block-library/src/image/image.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ import { useEffect, useMemo, useState, useRef } from '@wordpress/element';
import { __, _x, sprintf, isRTL } from '@wordpress/i18n';
import { DOWN } from '@wordpress/keycodes';
import { getFilename } from '@wordpress/url';
import { switchToBlockType, store as blocksStore } from '@wordpress/blocks';
import { getBlockBindingsSource, switchToBlockType } from '@wordpress/blocks';
import { crop, overlayText, upload } from '@wordpress/icons';
import { store as noticesStore } from '@wordpress/notices';
import { store as coreStore } from '@wordpress/core-data';
Expand Down Expand Up @@ -476,7 +476,6 @@ export default function Image( {
if ( ! isSingleSelected ) {
return {};
}
const { getBlockBindingsSource } = unlock( select( blocksStore ) );
const {
url: urlBinding,
alt: altBinding,
Expand Down
66 changes: 66 additions & 0 deletions packages/blocks/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,26 @@ _Returns_

- `string[]`: The attribute names that have the provided role.

### getBlockBindingsSource

Returns a registered block bindings source by its name.

_Parameters_

- _name_ `string`: Block bindings source name.

_Returns_

- `?Object`: Block bindings source.

### getBlockBindingsSources

Returns all registered block bindings sources.

_Returns_

- `Array`: Block bindings sources.

### getBlockContent

Given a block object, returns the Block's Inner HTML markup.
Expand Down Expand Up @@ -492,6 +512,36 @@ _Returns_

- `Array`: A list of blocks.

### registerBlockBindingsSource

Registers a new block bindings source with an object defining its behavior. Once registered, the source is available to be connected to the supported block attributes.

_Usage_

```js
import { _x } from '@wordpress/i18n';
import { registerBlockBindingsSource } from '@wordpress/blocks';

registerBlockBindingsSource( {
name: 'plugin/my-custom-source',
label: _x( 'My Custom Source', 'block bindings source' ),
usesContext: [ 'postType' ],
getValues: getSourceValues,
setValues: updateMyCustomValuesInBatch,
canUserEditValue: () => true,
} );
```

_Parameters_

- _source_ `Object`: Properties of the source to be registered.
- _source.name_ `string`: The unique and machine-readable name.
- _source.label_ `[string]`: Human-readable label. Optional when it is defined in the server.
- _source.usesContext_ `[Array]`: Optional array of context needed by the source only in the editor.
- _source.getValues_ `[Function]`: Optional function to get the values from the source.
- _source.setValues_ `[Function]`: Optional function to update multiple values connected to the source.
- _source.canUserEditValue_ `[Function]`: Optional function to determine if the user can edit the value.

### registerBlockCollection

Registers a new block collection to group blocks in the same namespace in the inserter.
Expand Down Expand Up @@ -793,6 +843,22 @@ _Returns_

- `Array`: Updated Block list.

### unregisterBlockBindingsSource

Unregisters a block bindings source by providing its name.

_Usage_

```js
import { unregisterBlockBindingsSource } from '@wordpress/blocks';
unregisterBlockBindingsSource( 'plugin/my-custom-source' );
```

_Parameters_

- _name_ `string`: The name of the block bindings source to unregister.

### unregisterBlockStyle

Unregisters a block style for the given block.
Expand Down
Loading

0 comments on commit 1c081c8

Please sign in to comment.