This is fairly easy to achieve with two custom blocks: (1) for the wrapper <ul>...</ul>
element and (2) another for the <li>...</li>
items. Each would use InnerBlocks
to allow you to place any blocks within the <li>
items.
Basic example to get you started:
import { __ } from '@wordpress/i18n';
import { registerBlockType } from '@wordpress/blocks';
import {
useBlockProps,
InnerBlocks,
__experimentalUseInnerBlocksProps as useInnerBlocksProps,
} from '@wordpress/block-editor';
// List block
registerBlockType('pb/list', {
apiVersion: 2,
title: __('List', 'pb'),
category: 'text',
edit: (props) => {
const blockProps = useBlockProps();
const innerBlocksProps = useInnerBlocksProps(blockProps, {
allowedBlocks: ['pb/list-item'],
});
return (
<ul { ...innerBlocksProps } />
);
},
save: () => {
const blockProps = useBlockProps.save();
return (
<ul { ...blockProps }>
<InnerBlocks.Content />
</ul>
);
},
});
// List item block
registerBlockType('pb/list-item', {
apiVersion: 2,
title: __('List Item', 'pb'),
parent: ['pb/list'],
category: 'text',
edit: (props) => {
const blockProps = useBlockProps();
const innerBlocksProps = useInnerBlocksProps(blockProps);
return (
<li { ...innerBlocksProps } />
);
},
save: () => {
const blockProps = useBlockProps.save();
return (
<li { ...blockProps }>
<InnerBlocks.Content />
</li>
);
},
});