diff --git a/packages/block-editor/package.json b/packages/block-editor/package.json
index 008e02355ac703..e373fdd193efaf 100644
--- a/packages/block-editor/package.json
+++ b/packages/block-editor/package.json
@@ -46,6 +46,7 @@
"@wordpress/components": "*",
"@wordpress/compose": "*",
"@wordpress/data": "*",
+ "@wordpress/dataviews": "*",
"@wordpress/date": "*",
"@wordpress/deprecated": "*",
"@wordpress/dom": "*",
diff --git a/packages/block-editor/src/hooks/block-bindings.js b/packages/block-editor/src/hooks/block-bindings.js
index e10696cc1257d7..a2fe3918d5350d 100644
--- a/packages/block-editor/src/hooks/block-bindings.js
+++ b/packages/block-editor/src/hooks/block-bindings.js
@@ -5,7 +5,6 @@ import { __ } from '@wordpress/i18n';
import {
getBlockBindingsSource,
getBlockBindingsSources,
- getBlockType,
} from '@wordpress/blocks';
import {
__experimentalItemGroup as ItemGroup,
@@ -14,11 +13,14 @@ import {
__experimentalToolsPanel as ToolsPanel,
__experimentalToolsPanelItem as ToolsPanelItem,
__experimentalVStack as VStack,
+ Modal,
privateApis as componentsPrivateApis,
} from '@wordpress/components';
import { useSelect } from '@wordpress/data';
-import { useContext, Fragment } from '@wordpress/element';
+import { DataViews, filterSortAndPaginate } from '@wordpress/dataviews';
+import { useContext, Fragment, useState } from '@wordpress/element';
import { useViewportMatch } from '@wordpress/compose';
+import { connection } from '@wordpress/icons';
/**
* Internal dependencies
@@ -27,15 +29,13 @@ import {
canBindAttribute,
getBindableAttributes,
} from '../hooks/use-bindings-attributes';
-import { unlock } from '../lock-unlock';
import InspectorControls from '../components/inspector-controls';
import BlockContext from '../components/block-context';
-import { useBlockEditContext } from '../components/block-edit';
import { useBlockBindingsUtils } from '../utils/block-bindings';
import { store as blockEditorStore } from '../store';
+import { unlock } from '../lock-unlock';
const { Menu } = unlock( componentsPrivateApis );
-
const EMPTY_OBJECT = {};
const useToolsPanelDropdownMenuProps = () => {
@@ -51,74 +51,16 @@ const useToolsPanelDropdownMenuProps = () => {
: {};
};
-function BlockBindingsPanelDropdown( { fieldsList, attribute, binding } ) {
- const { clientId } = useBlockEditContext();
- const registeredSources = getBlockBindingsSources();
- const { updateBlockBindings } = useBlockBindingsUtils();
- const currentKey = binding?.args?.key;
- const attributeType = useSelect(
- ( select ) => {
- const { name: blockName } =
- select( blockEditorStore ).getBlock( clientId );
- const _attributeType =
- getBlockType( blockName ).attributes?.[ attribute ]?.type;
- return _attributeType === 'rich-text' ? 'string' : _attributeType;
- },
- [ clientId, attribute ]
- );
- return (
- <>
- { Object.entries( fieldsList ).map( ( [ name, fields ], i ) => (
-
-
- { Object.keys( fieldsList ).length > 1 && (
-
- { registeredSources[ name ].label }
-
- ) }
- { Object.entries( fields )
- .filter(
- ( [ , args ] ) => args?.type === attributeType
- )
- .map( ( [ key, args ] ) => (
-
- updateBlockBindings( {
- [ attribute ]: {
- source: name,
- args: { key },
- },
- } )
- }
- name={ attribute + '-binding' }
- value={ key }
- checked={ key === currentKey }
- >
-
- { args?.label }
-
-
- { args?.value }
-
-
- ) ) }
-
- { i !== Object.keys( fieldsList ).length - 1 && (
-
- ) }
-
- ) ) }
- >
- );
-}
-
-function BlockBindingsAttribute( { attribute, binding, fieldsList } ) {
+function BlockBindingsAttribute( { attribute, binding, fieldsList, onClick } ) {
const { source: sourceName, args } = binding || {};
const sourceProps = getBlockBindingsSource( sourceName );
const isSourceInvalid = ! sourceProps;
return (
-
+
{ attribute }
{ !! binding && (
{ Object.entries( bindings ).map( ( [ attribute, binding ] ) => (
- -
+
-
+ { Object.entries( fieldsList ).map( ( [ name ] ) => (
+
+
+ { Object.keys( fieldsList ).length > 1 && (
+ onClick( name ) }>
+ { registeredSources[ name ].label }
+
+ ) }
+
+
+ ) ) }
+ >
+ );
+}
+
function EditableBlockBindingsPanelItems( {
attributes,
bindings,
fieldsList,
} ) {
const { updateBlockBindings } = useBlockBindingsUtils();
+ const [ isOuterModalOpen, setOuterModalOpen ] = useState( false );
+ const defaultLayouts = {
+ table: {
+ layout: {
+ primaryField: 'label',
+ styles: {
+ label: {
+ minWidth: 320,
+ },
+ value: {
+ width: '50%',
+ minWidth: 320,
+ },
+ },
+ },
+ },
+ };
+ const [ view, setView ] = useState( {
+ type: 'table',
+ search: '',
+ filters: [],
+ page: 1,
+ perPage: 10,
+ sort: {},
+ fields: [ 'label', 'value' ],
+ layout: defaultLayouts.table.layout,
+ } );
+
+ const [ data, setData ] = useState( null );
+ const [ paginationInfo, setPaginationInfo ] = useState( null );
+
+ const actions = [
+ {
+ id: 'select',
+ label: __( 'Connect attribute' ),
+ isPrimary: true,
+ icon: connection,
+ callback: ( field ) => {
+ updateBlockBindings( {
+ content: {
+ source: 'core/post-meta',
+ args: { key: field[ 0 ]?.id },
+ },
+ } );
+ setOuterModalOpen( false );
+ },
+ },
+ ];
+ const fields = [
+ {
+ id: 'label',
+ label: __( 'Label' ),
+ type: 'text',
+ enableGlobalSearch: true,
+ },
+ {
+ id: 'value',
+ label: __( 'Value' ),
+ type: 'text',
+ enableGlobalSearch: true,
+ },
+ ];
+ const onChangeView = ( newView ) => {
+ const { data: newData, paginationInfo: newPaginationInfo } =
+ filterSortAndPaginate( data, newView, fields );
+ setView( newView );
+ setData( newData );
+ setPaginationInfo( newPaginationInfo );
+ };
const isMobile = useViewportMatch( 'medium', '<' );
return (
<>
@@ -192,13 +222,68 @@ function EditableBlockBindingsPanelItems( {
>
{
+ if (
+ Object.keys( fieldsList[ sourceName ] )
+ .length !== 0
+ ) {
+ const {
+ data: newData,
+ paginationInfo: newPaginationInfo,
+ } = filterSortAndPaginate(
+ Object.entries(
+ fieldsList[ sourceName ]
+ ).map( ( [ key, field ] ) => {
+ const value =
+ field.value === undefined
+ ? `${ field.label } value`
+ : field.value;
+
+ return {
+ id: key,
+ label: field.label || key,
+ value:
+ value !== ''
+ ? value
+ : `Add a new ${ field.label }`,
+ };
+ } ),
+ view,
+ fields
+ );
+ setData( newData );
+ setPaginationInfo( newPaginationInfo );
+ setOuterModalOpen( true );
+ } else {
+ updateBlockBindings( {
+ [ attribute ]: {
+ source: sourceName,
+ },
+ } );
+ }
+ } }
/>
);
} ) }
+ { isOuterModalOpen && (
+ setOuterModalOpen( false ) }
+ __experimentalHideHeader
+ className="block-editor-bindings__modal"
+ >
+
+
+ ) }
>
);
}
@@ -221,6 +306,9 @@ export const BlockBindingsPanel = ( { name: blockName, metadata } ) => {
const registeredSources = getBlockBindingsSources();
Object.entries( registeredSources ).forEach(
( [ sourceName, { getFieldsList, usesContext } ] ) => {
+ if ( sourceName === 'core/pattern-overrides' ) {
+ return;
+ }
if ( getFieldsList ) {
// Populate context.
const context = {};
@@ -233,10 +321,9 @@ export const BlockBindingsPanel = ( { name: blockName, metadata } ) => {
select,
context,
} );
- // Only add source if the list is not empty.
- if ( Object.keys( sourceList || {} ).length ) {
- _fieldsList[ sourceName ] = { ...sourceList };
- }
+ _fieldsList[ sourceName ] = { ...sourceList };
+ } else {
+ _fieldsList[ sourceName ] = {};
}
}
);
diff --git a/packages/block-editor/src/hooks/block-bindings.scss b/packages/block-editor/src/hooks/block-bindings.scss
index e8f05895922f0f..c4190e322954b0 100644
--- a/packages/block-editor/src/hooks/block-bindings.scss
+++ b/packages/block-editor/src/hooks/block-bindings.scss
@@ -3,4 +3,32 @@ div.block-editor-bindings__panel {
button:hover .block-editor-bindings__item span {
color: inherit;
}
+
+}
+
+.block-editor-bindings__modal {
+ width: 100%;
+ height: 100%;
+ .dataviews-view-table {
+ margin-top: $grid-unit-20;
+ border: 1px solid #f0f0f0;
+ width: 100%;
+ thead {
+ th {
+ text-align: left;
+ padding-left: 12px;
+ button {
+ text-transform: uppercase;
+ color: #1e1e1e;
+ }
+ }
+ }
+ tbody {
+ td {
+ border-top: 1px solid #f0f0f0;
+ padding-left: 24px;
+ }
+ }
+ }
+
}