Here’s a question for both WP and React developers. I want to develop a custom Gutemberg block using the official create-block library. For UX reasons, I would like this block to have a drag-and-drop sorting system, like this gif. But I haven’t found a solution. There’s this library https://github.com/lucprincen/gutenberg-sortable, but it’s obsolete and won’t compile.
Any ideas? Here’s a simplified code that you can improve.
import { registerBlockType } from "@wordpress/blocks";
import "./style.scss";
import metadata from "./block.json";
import { useBlockProps } from "@wordpress/block-editor";
registerBlockType(metadata.name, {
attributes: {
items: {
type: "array",
default: ["carrot", "cabbage", "zucchini"],
},
},
edit: (props) => {
const blockProps = useBlockProps();
const {
attributes: { items },
setAttributes,
} = props;
const sortItems = (sortedItems) => {
setAttributes({ items: sortedItems });
};
return (
<div {...blockProps}>
{items.map((item) => (
<div className="item">{item}</div>
))}
</div>
);
},
save: (props) => {
const blockProps = useBlockProps.save();
const {
attributes: { items },
} = props;
return (
<div {...blockProps}>
{items.map((item) => (
<div className="item">{item}</div>
))}
</div>
);
},
});
I've been thinking so hard and i find a solution :). The best thing to do is to use Gutenberg's native drag and drop. I've developed two custom blocks. A parent block and a child block. The parent block has an innerBlock which can only receive child blocks. Here's the simplified code.
parent block.json
{
"$schema": "https://schemas.wp.org/trunk/block.json",
"apiVersion": 2,
"name": "domain/vegetables",
"version": "0.1.0",
"title": "Vegetables",
"category": "text",
"icon": "carrot",
"description": "A sortable list of vegetables",
"supports": {
"html": false
},
"editorScript": "file:./index.js",
"editorStyle": "file:./index.css",
"style": "file:./style-index.css"
}
parent index.js
import { registerBlockType } from "@wordpress/blocks";
import metadata from "./block.json";
import { useBlockProps, InnerBlocks } from "@wordpress/block-editor";
registerBlockType(metadata, {
edit: () => {
const blockProps = useBlockProps();
return (
<div {...blockProps}>
<InnerBlocks
allowedBlocks={["domain/vegetable"]}
template={[["domain/vegetable"]]}
/>
</div>
);
},
save: (props) => {
const blockProps = useBlockProps.save();
return (
<div {...blockProps}>
<InnerBlocks.Content />
</div>
);
},
});
child block.json
{
"$schema": "https://schemas.wp.org/trunk/block.json",
"apiVersion": 2,
"name": "domain/vegetable",
"version": "0.1.0",
"title": "Vegetable",
"category": "text",
"icon": "carrot",
"parent": ["domain/vegetables"],
"description": "A vegetable",
"supports": {
"html": false
},
"attributes": {
"name": {
"type": "string"
}
},
"editorScript": "file:./index.js",
"editorStyle": "file:./index.css",
"style": "file:./style-index.css"
}
child index.js
import { registerBlockType } from "@wordpress/blocks";
import metadata from "./block.json";
import { useBlockProps, RichText } from "@wordpress/block-editor";
registerBlockType(metadata, {
edit: (props) => {
const blockProps = useBlockProps();
const {
attributes: { name },
setAttributes,
} = props;
return (
<div {...blockProps}>
<RichText
{...blockProps}
tagName="p"
value={name}
allowedFormats={[]}
onChange={(name) => setAttributes({ name })}
placeholder={"A vegetable..."}
/>
</div>
);
},
save: (props) => {
const blockProps = useBlockProps.save();
const {
attributes: { name },
} = props;
return (
<div {...blockProps}>
<p>{name}</p>
</div>
);
},
});