I'm working within a Block Library plugin and have a simple block plugin (standard build with blocks.registerBlockType
, editor.BlockEdit
, blocks.getSaveContent.extraProps
, and editor.BlockListBlock
) to handle DeviceVisibility
that adds a panel to every block that can populate a few attributes (e.g. hide on mobile, hide on desktop) on the wrapperProps
. This works perfectly.
However, I've run into a few situations where it would be useful to allow several of the blocks I've created to add controls unique to the block to the DeviceVisibility plugin's Panel, for example when a block should "Stack children on mobile" in addition to the default controls added by the plugin.
SlotFill seems like the obvious answer.
The problem is that when I create a SlotFill (using createSlotFill
) instead of controls for the current block being added to the Slot, any controls added to a <DeviceVisibilityControls/>
component in any block are all added to every block. Here's the code of the SlotFill:
// DeviceVisibilityControls/index.js
import {createSlotFill} from '@wordpress/components';
const {Fill, Slot} = createSlotFill('DeviceVisibilityControls');
const DeviceVisibilityControls = ({children, clientId}) => {
return (
<Fill>
{children}
</Fill>
);
};
DeviceVisibilityControls.Slot = Slot;
export default DeviceVisibilityControls;
and the implementation in the plugin's inspector:
// plugin/inspector.js
<InspectorControls>
<PanelBody>
// other controls
<DeviceVisibilityControls.Slot/>
</PanelBody>
<InspectorControls>
This feels like one of those situations where I'm missing something simple -- some method of forwarding context or isolating the controls sent for the active control by clientId. Something. Been searching Gutenberg's source, but the solutions there are spread out enough that it's hard to connect the dots (or maybe I've been looking at this too long).
Could use an assist. In reward, I will name my next child after anyone who helps with a solution.
I'm working within a Block Library plugin and have a simple block plugin (standard build with blocks.registerBlockType
, editor.BlockEdit
, blocks.getSaveContent.extraProps
, and editor.BlockListBlock
) to handle DeviceVisibility
that adds a panel to every block that can populate a few attributes (e.g. hide on mobile, hide on desktop) on the wrapperProps
. This works perfectly.
However, I've run into a few situations where it would be useful to allow several of the blocks I've created to add controls unique to the block to the DeviceVisibility plugin's Panel, for example when a block should "Stack children on mobile" in addition to the default controls added by the plugin.
SlotFill seems like the obvious answer.
The problem is that when I create a SlotFill (using createSlotFill
) instead of controls for the current block being added to the Slot, any controls added to a <DeviceVisibilityControls/>
component in any block are all added to every block. Here's the code of the SlotFill:
// DeviceVisibilityControls/index.js
import {createSlotFill} from '@wordpress/components';
const {Fill, Slot} = createSlotFill('DeviceVisibilityControls');
const DeviceVisibilityControls = ({children, clientId}) => {
return (
<Fill>
{children}
</Fill>
);
};
DeviceVisibilityControls.Slot = Slot;
export default DeviceVisibilityControls;
and the implementation in the plugin's inspector:
// plugin/inspector.js
<InspectorControls>
<PanelBody>
// other controls
<DeviceVisibilityControls.Slot/>
</PanelBody>
<InspectorControls>
This feels like one of those situations where I'm missing something simple -- some method of forwarding context or isolating the controls sent for the active control by clientId. Something. Been searching Gutenberg's source, but the solutions there are spread out enough that it's hard to connect the dots (or maybe I've been looking at this too long).
Could use an assist. In reward, I will name my next child after anyone who helps with a solution.
Share Improve this question edited Apr 7 at 8:05 fuxia♦ 107k39 gold badges255 silver badges459 bronze badges asked Apr 4 at 14:15 JerJer 5114 silver badges6 bronze badges 2 |1 Answer
Reset to default 3I cannot believe I missed this...
A solution (not necessarily the solution) is to retrieve the current block context using useBlockEditContext
and compare the ?.clientId
to the currently selected block (found with getSelectedBlockClientId
) within the Fill
.
useBlockEditContext
returns information about the block where the hook is being used -- so where the custom Fill
wrapper is instantiated.
Here's a working example:
import { useSelect } from '@wordpress/data';
import { store as blockEditorStore, useBlockEditContext } from '@wordpress/block-editor';
import {createSlotFill} from '@wordpress/components';
const {Fill, Slot} = createSlotFill('DeviceVisibilityControls');
const DeviceVisibilityControls = ({children}) => {
const context = useBlockEditContext();
const selectedClientId = useSelect(
( select ) => select( blockEditorStore ).getSelectedBlockClientId(),
[]
);
if( selectedClientId !== context?.clientId){
return null;
}
return (
<Fill>
{children}
</Fill>
);
};
DeviceVisibilityControls.Slot = Slot;
export default DeviceVisibilityControls;
I'm still not sure this is the best solution, but again, it does appear to be a working solution.
<head>
etc. This is how inspector controls works because the editor sidebar isn't inside the blocks HTML markup, and while you in theory could slot into that, that's not how that API tends to be used – Tom J Nowell ♦ Commented Apr 4 at 16:29InspectorControls
is in a block plugin useseditor.BlockEdit
to add a panel to every block (that opts in viasupports
). A few of these blocks have additional controls that logically should appear here. However, because theInspectorControls
is in the plugin, the block cannot add children. ThinkInspectorAdvancedControls
-- I'm trying to replicate that pattern. – Jer Commented Apr 5 at 23:24