Skip to content
Open
Prev Previous commit
Next Next commit
Filter block tools in fill wrapper
  • Loading branch information
jeryj committed Sep 5, 2025
commit 2c48aee8261d55b13fca2986a2cec174cab17fa0
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
/**
* Internal dependencies
*/
import { useBlockEditingMode } from '../block-editing-mode';

/**
* A wrapper component that conditionally renders its children based on the block editing mode.
*
* In content-only mode, only components with `category="content"` will be rendered.
* In other modes, all components are rendered.
*
* @param {Object} props The props object.
* @param {any} props.children The children to render.
* @return {any} The filtered children.
*/
export function ContentOnlyFilter( { children } ) {
const blockEditingMode = useBlockEditingMode();

// In content-only mode, filter children to only show those with category="content"
if ( blockEditingMode === 'contentOnly' ) {
return filterChildrenByCategory( children );
}

// In other modes, render all components
return children;
}

// Helper function to recursively filter children
function filterChildrenByCategory( children ) {
if ( ! children ) {
return null;
}

// Handle arrays
if ( Array.isArray( children ) ) {
const filtered = children
.map( filterChildrenByCategory )
.filter( Boolean );
return filtered.length > 0 ? filtered : null;
}

// Handle React elements
if ( children.type && children.props ) {
// If this component has category="content", keep it
if ( children.props.category === 'content' ) {
return children;
}

// If this component doesn't have category="content", filter its children
if ( children.props.children ) {
const filteredChildren = filterChildrenByCategory(
children.props.children
);
if ( filteredChildren ) {
return {
...children,
props: {
...children.props,
children: filteredChildren,
},
};
}
}

// No category="content" found in this branch
return null;
}

// Handle other cases (fragments, etc.)
if ( children.props ) {
if ( children.props.category === 'content' ) {
return children;
}
if ( children.props.children ) {
return filterChildrenByCategory( children.props.children );
}
return null;
}

return null;
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
* Internal dependencies
*/
import useBlockControlsFill from './hook';
import { ContentOnlyFilter } from './content-only-filter';

export default function BlockControlsFill( {
group = 'default',
Expand Down Expand Up @@ -43,7 +44,7 @@ export default function BlockControlsFill( {
( inner, [ Provider, props ] ) => (
<Provider { ...props }>{ inner }</Provider>
),
innerMarkup
<ContentOnlyFilter>{ innerMarkup }</ContentOnlyFilter>
);
} }
</Fill>
Expand Down
88 changes: 1 addition & 87 deletions packages/block-editor/src/components/block-controls/slot.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,12 @@ import warning from '@wordpress/warning';
*/
import groups from './groups';
import { unlock } from '../../lock-unlock';
import { useBlockEditingMode } from '../block-editing-mode';

const { ComponentsContext } = unlock( privateApis );

export default function BlockControlsSlot( { group = 'default', ...props } ) {
const toolbarState = useContext( ToolbarContext );
const contextState = useContext( ComponentsContext );
const blockEditingMode = useBlockEditingMode();

const fillProps = useMemo(
() => ( {
Expand All @@ -34,88 +32,6 @@ export default function BlockControlsSlot( { group = 'default', ...props } ) {
[ toolbarState, contextState ]
);

// Create filter function for content-only mode
const filter = useMemo( () => {
if ( blockEditingMode !== 'contentOnly' ) {
return undefined; // No filtering in default mode
}

return ( fill ) => {
// Check if the fill has category="content" prop directly
if ( fill?.props?.category === 'content' ) {
return true;
}

// Fallback: check children if needed
if ( fill?.children && typeof fill.children === 'function' ) {
// For function children, we need to check the rendered result
const renderedChildren = fill.children( fillProps );
return hasCategoryContent( renderedChildren );
} else if ( fill?.children ) {
// For React element children, check directly
return hasCategoryContent( fill.children );
}
return false;
};
}, [ blockEditingMode, fillProps ] );

// Helper function to recursively check if any component has category="content"
const hasCategoryContent = ( children ) => {
if ( ! children ) {
return false;
}

// Handle React elements (most common case)
if ( children.type && children.props ) {
// Check this element's category prop
if ( children.props.category === 'content' ) {
return true;
}

// Recursively check children
if ( children.props.children ) {
const hasCategory = hasCategoryContent(
children.props.children
);
if ( hasCategory ) {
return true;
}
}

return false;
}

// Handle arrays of React elements
if ( Array.isArray( children ) ) {
const hasCategory = children.some( ( child ) => {
return hasCategoryContent( child );
} );
return hasCategory;
}

// Handle other cases (fragments, etc.)
if ( children.props ) {
// Check this element's category prop
if ( children.props.category === 'content' ) {
return true;
}

// Recursively check children
if ( children.props.children ) {
const hasCategory = hasCategoryContent(
children.props.children
);
if ( hasCategory ) {
return true;
}
}

return false;
}

return false;
};

const slotFill = groups[ group ];
const fills = useSlotFills( slotFill?.name );

Expand All @@ -131,9 +47,7 @@ export default function BlockControlsSlot( { group = 'default', ...props } ) {
const { Slot } = slotFill;

// Use the new filter prop instead of children function
const slot = (
<Slot { ...props } fillProps={ fillProps } filter={ filter } />
);
const slot = <Slot { ...props } fillProps={ fillProps } />;

if ( group === 'default' ) {
return slot;
Expand Down