-
Notifications
You must be signed in to change notification settings - Fork 4.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Block bindings: UI for connecting bindings #62880
Changes from 76 commits
ae6afbc
5efa047
0a1b636
1f9900e
8128c0f
4eac7dc
db906bd
e357882
1a18fb6
39b2efa
8484002
88759d3
aa626fe
be20d24
5343678
58054eb
8e8a122
683abfb
d3ba547
3b703f7
f0cd95a
3bc6072
da6845e
28ceffe
a90d9da
2df3ef3
6be1849
0380ee8
fe88757
41691df
4f097d0
6e04036
51904df
28ba254
25051fc
981367a
b1aaa7a
3d9182e
ff7c393
f2469c9
26b5988
8798953
27639ba
f0a0680
42693a4
b7efec2
11b2a4b
58eb28c
d17b593
dfeba8b
9e67785
b19d01e
fb4a51b
6439308
9f277b3
0beff34
8e6ad14
c9ea7c7
436c97f
9896dbf
752d7e0
feb890d
8bb9136
22e1020
cabd3d8
b814725
591bd72
8ffc7f4
13cd09e
3a086ab
12c9088
bc05788
a9b347f
839b37a
01fcf7d
d14d50f
2dff252
50f6f1e
3bf117c
6fb7d89
d091c34
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -2,42 +2,199 @@ | |||||||||||||||||||||||||||||||||||||||||||||||||||||||
* WordPress dependencies | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
*/ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
import { __ } from '@wordpress/i18n'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
import { store as blocksStore } from '@wordpress/blocks'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
import { privateApis as blocksPrivateApis } from '@wordpress/blocks'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
import { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
BaseControl, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
PanelBody, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
__experimentalHStack as HStack, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
__experimentalItemGroup as ItemGroup, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
__experimentalItem as Item, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
__experimentalText as Text, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
__experimentalToolsPanel as ToolsPanel, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
__experimentalToolsPanelItem as ToolsPanelItem, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
__experimentalTruncate as Truncate, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
__experimentalVStack as VStack, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
privateApis as componentsPrivateApis, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} from '@wordpress/components'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
import { useSelect } from '@wordpress/data'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
import { useSelect, useDispatch, useRegistry } from '@wordpress/data'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
import { useContext, Fragment } from '@wordpress/element'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
import { useViewportMatch } from '@wordpress/compose'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
/** | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
* Internal dependencies | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
*/ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
import { canBindAttribute } from '../hooks/use-bindings-attributes'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
import { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
canBindAttribute, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
getBindableAttributes, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} from '../hooks/use-bindings-attributes'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
import { store as blockEditorStore } from '../store'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
import { unlock } from '../lock-unlock'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
import InspectorControls from '../components/inspector-controls'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
import BlockContext from '../components/block-context'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
DropdownMenuV2: DropdownMenu, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
DropdownMenuGroupV2: DropdownMenuGroup, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
DropdownMenuRadioItemV2: DropdownMenuRadioItem, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
DropdownMenuItemLabelV2: DropdownMenuItemLabel, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
DropdownMenuItemHelpTextV2: DropdownMenuItemHelpText, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
DropdownMenuSeparatorV2: DropdownMenuSeparator, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} = unlock( componentsPrivateApis ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const useToolsPanelDropdownMenuProps = () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const isMobile = useViewportMatch( 'medium', '<' ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
return ! isMobile | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
? { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
popoverProps: { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
placement: 'left-start', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
// For non-mobile, inner sidebar width (248px) - button width (24px) - border (1px) + padding (16px) + spacing (20px) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
offset: 259, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
: {}; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
function BlockBindingsPanelDropdown( { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
fieldsList, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
addConnection, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
attribute, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
binding, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} ) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const currentKey = binding?.args?.key; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
return ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
{ Object.entries( fieldsList ).map( ( [ label, fields ], i ) => ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<Fragment key={ label }> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<DropdownMenuGroup> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
{ Object.keys( fieldsList ).length > 1 && ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<Text | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
className="block-editor-bindings__source-label" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
upperCase | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
variant="muted" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
aria-hidden | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
{ label } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
</Text> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
) } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
{ Object.entries( fields ).map( ( [ key, value ] ) => ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<DropdownMenuRadioItem | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
key={ key } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
onChange={ () => | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
addConnection( key, attribute ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
name={ attribute + '-binding' } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
value={ key } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
checked={ key === currentKey } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<DropdownMenuItemLabel> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
{ key } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
</DropdownMenuItemLabel> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<DropdownMenuItemHelpText> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
{ value } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
</DropdownMenuItemHelpText> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
</DropdownMenuRadioItem> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
) ) } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
</DropdownMenuGroup> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
{ i !== Object.keys( fieldsList ).length - 1 && ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This separator always was problematic as it rarely should be rendered after the last element. I dream of the future when there is a prop on the menu to automatically injects the separator when there are groups defined 😄 |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<DropdownMenuSeparator /> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
) } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
</Fragment> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
) ) } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
</> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
function BlockBindingsAttribute( { attribute, binding } ) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const { source: sourceName, args } = binding || {}; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const sourceProps = | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
unlock( blocksPrivateApis ).getBlockBindingsSource( sourceName ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
return ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<VStack> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<Truncate>{ attribute }</Truncate> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
{ !! binding && ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<Text | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
variant="muted" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
className="block-editor-bindings__item-explanation" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<Truncate> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
{ args?.key || sourceProps?.label || sourceName } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
</Truncate> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Comment on lines
+116
to
+118
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. When we extract an API to add different sources, we should maybe consider how to let sources intentionally define a value here other than There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, we need to explore how other sources will hook into this. I don't know yet if using a key should be enforced or not and how much flexibility we should provide in this specific UI. They can always create their own UI for their use case if it defers from the Custom Fields one. We can discuss that in a follow-up before opening the APIs. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In my opinion, gutenberg/packages/block-library/src/heading/index.js Lines 30 to 56 in 3dfdfef
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
</Text> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
) } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
</VStack> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
function ReadOnlyBlockBindingsPanelItems( { bindings } ) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
return ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
{ Object.entries( bindings ).map( ( [ attribute, binding ] ) => ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<Item key={ attribute }> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<BlockBindingsAttribute | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
attribute={ attribute } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
binding={ binding } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
/> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
</Item> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
) ) } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
</> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Comment on lines
+125
to
+138
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should we add a visual indicator to let users know when the panel is read only? Otherwise the panel may just appear broken in those cases. I imagine this would be especially useful when a user doesn't have sufficient permissions to modify the bindings. We could just add a gray background to the box. What do you think? This PR is already big, so we could also handle that in a separate PR. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I agree the UI for read only needs to be revisited, and I also agree it can be done in a follow-up. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Agreed. Also, since this part of the UI is using Please ping @WordPress/gutenberg-components when you start working on it! |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
function EditableBlockBindingsPanelItems( { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
attributes, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
bindings, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
fieldsList, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
addConnection, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
removeConnection, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} ) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const isMobile = useViewportMatch( 'medium', '<' ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
return ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
{ attributes.map( ( attribute ) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const binding = bindings[ attribute ]; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
return ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<ToolsPanelItem | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
key={ attribute } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
hasValue={ () => !! binding } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
label={ attribute } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
onDeselect={ () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
removeConnection( attribute ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<DropdownMenu | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
placement={ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
isMobile ? 'bottom-start' : 'left-start' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
gutter={ isMobile ? 8 : 36 } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
className="block-editor-bindings__popover" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
trigger={ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<Item> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<BlockBindingsAttribute | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
attribute={ attribute } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
binding={ binding } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
/> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
</Item> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<BlockBindingsPanelDropdown | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
fieldsList={ fieldsList } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
addConnection={ addConnection } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
attribute={ attribute } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
binding={ binding } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
/> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
</DropdownMenu> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
</ToolsPanelItem> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} ) } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
</> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
export const BlockBindingsPanel = ( { name, metadata } ) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const registry = useRegistry(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const blockContext = useContext( BlockContext ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const { bindings } = metadata || {}; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const { sources } = useSelect( ( select ) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const _sources = unlock( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
select( blocksStore ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
).getAllBlockBindingsSources(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
return { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
sources: _sources, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}, [] ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if ( ! bindings ) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
return null; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const bindableAttributes = getBindableAttributes( name ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const dropdownMenuProps = useToolsPanelDropdownMenuProps(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
// Don't show not allowed attributes. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
// Don't show the bindings connected to pattern overrides in the inspectors panel. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
// TODO: Explore if this should be abstracted to let other sources decide. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const filteredBindings = { ...bindings }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Object.keys( filteredBindings ).forEach( ( key ) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
@@ -48,43 +205,129 @@ export const BlockBindingsPanel = ( { name, metadata } ) => { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if ( Object.keys( filteredBindings ).length === 0 ) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const { updateBlockAttributes } = useDispatch( blockEditorStore ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const { _id } = useSelect( ( select ) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const { getSelectedBlockClientId } = select( blockEditorStore ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
return { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
_id: getSelectedBlockClientId(), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}, [] ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if ( ! bindableAttributes || bindableAttributes.length === 0 ) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
return null; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const removeAllConnections = () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const newMetadata = { ...metadata }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
delete newMetadata.bindings; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
updateBlockAttributes( _id, { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
metadata: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Object.keys( newMetadata ).length === 0 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
? undefined | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
: newMetadata, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const addConnection = ( value, attribute ) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
// Assuming the block expects a flat structure for its metadata attribute | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const newMetadata = { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
...metadata, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
// Adjust this according to the actual structure expected by your block | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
bindings: { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
...metadata?.bindings, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
[ attribute ]: { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
source: 'core/post-meta', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
args: { key: value }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
// Update the block's attributes with the new metadata | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
updateBlockAttributes( _id, { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
metadata: newMetadata, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const removeConnection = ( key ) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const newMetadata = { ...metadata }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if ( ! newMetadata.bindings ) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
return; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
delete newMetadata.bindings[ key ]; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if ( Object.keys( newMetadata.bindings ).length === 0 ) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
delete newMetadata.bindings; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
updateBlockAttributes( _id, { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
metadata: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Object.keys( newMetadata ).length === 0 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
? undefined | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
: newMetadata, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const fieldsList = {}; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const { getBlockBindingsSources } = unlock( blocksPrivateApis ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const registeredSources = getBlockBindingsSources(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Object.values( registeredSources ).forEach( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
( { getFieldsList, label, usesContext } ) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if ( getFieldsList ) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
// Populate context. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const context = {}; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if ( usesContext?.length ) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
for ( const key of usesContext ) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
context[ key ] = blockContext[ key ]; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const sourceList = getFieldsList( { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
registry, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
context, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
// Only add source if the list is not empty. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if ( sourceList ) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
fieldsList[ label ] = { ...sourceList }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
// At this moment, the UI can be locked when there are no fields to connect to. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const readOnly = ! Object.keys( fieldsList ).length; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if ( readOnly && Object.keys( filteredBindings ).length === 0 ) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
return null; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
return ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<InspectorControls> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<PanelBody | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
title={ __( 'Attributes' ) } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
className="components-panel__block-bindings-panel" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<ToolsPanel | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
label={ __( 'Attributes' ) } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
resetAll={ () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
removeAllConnections(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
dropdownMenuProps={ dropdownMenuProps } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
className="block-editor-bindings__panel" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<BaseControl | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
help={ __( 'Attributes connected to various sources.' ) } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<ItemGroup isBordered isSeparated size="large"> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
{ Object.keys( filteredBindings ).map( ( key ) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
return ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<Item key={ key }> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<HStack> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<span>{ key }</span> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<span className="components-item__block-bindings-source"> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
{ sources[ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
filteredBindings[ key ].source | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
? sources[ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
filteredBindings[ key ] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
.source | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
].label | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
: filteredBindings[ key ] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
.source } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
</span> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
</HStack> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
</Item> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} ) } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
</ItemGroup> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
</BaseControl> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
</PanelBody> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<ItemGroup isBordered isSeparated> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
{ readOnly ? ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<ReadOnlyBlockBindingsPanelItems | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
bindings={ filteredBindings } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
/> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
) : ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<EditableBlockBindingsPanelItems | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
attributes={ bindableAttributes } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
bindings={ filteredBindings } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
fieldsList={ fieldsList } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
addConnection={ addConnection } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
removeConnection={ removeConnection } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
/> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
) } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
</ItemGroup> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<Text variant="muted"> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
{ __( 'Attributes connected to various sources.' ) } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
</Text> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
</ToolsPanel> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
</InspectorControls> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,14 @@ | ||
.components-panel__block-bindings-panel .components-item__block-bindings-source { | ||
color: $gray-700; | ||
div.block-editor-bindings__panel { | ||
grid-template-columns: auto; | ||
button:hover .block-editor-bindings__item-explanation { | ||
Comment on lines
+1
to
+2
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For this use case, it seems we need to use just 1 column in the Apart from that, I had to add more specificity to override those styles. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We were not as involved in the My guess is that My advice is to follow the recommendations of the docs, and at most open a separate issue where we can discuss if and how to make There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
color: inherit; | ||
} | ||
} | ||
|
||
.block-editor-bindings__popover { | ||
// This won't be needed if `DropdownMenuGroup` component handles the label. | ||
.block-editor-bindings__source-label { | ||
grid-column: 2; | ||
margin: $grid-unit-10 0; | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can see the
useToolsPanelDropdownMenuProps
is already defined the same way in a couple of places: Global Styles utils and Block Library utils.After taking a quick look, it seems
dropdownMenuProps
is initialized using this util. I'm wondering if this should be part of the component, maybe the default or an option. Or have one unique util that can be used everywhere.It doesn't make too much sense to me to replicate this code in all the places.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree with that, but, at the same time, we would need to allow this option to be extensible and editable for external developers.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pinging @WordPress/gutenberg-components because they will probably have more context if this makes sense or feasible.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for the ping!
Those popover settings (placement and offset) are related to the layout of the block editor and the block inspector sidebar. The
@wordpress/components
package needs to provide UI components to more consumers than just the block editor, and therefore it should not be aware of those details otherwise it would become tightly coupled with the editor.I agree though that it would be best to store those settings in a common place.
Since those settings are related to the sidebar, I would suggest storing them in a package that is a common dependency of the block editor, but not a generic package (like components, data, compose, gets).
Maybe in the
@wordpress/interface
package (where complementary area, the component used for the sidebar, is defined), or even in the@wordpress/editor
package itself?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay, thanks for the context! We can figure out the best place for this in a follow-up pull request I believe.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To my knowledge, there is intent to sunset
@wordpress/interface
in favor of@wordpress/editor
.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have created this follow-up pull request to use only one util and keep discussing it.