Monolithic to Standard Blocks development through Plugins — Block not appearing in Gutenberg Editor

admin2025-05-31  6

woracious-suite/
├── blocks/
│   └── wo-custom-sidebar-menu/
│       ├── block.json
│       ├── edit.js
│       ├── editor.scss
│       ├── index.js
│       ├── render.php
│       ├── style.scss
│       └── view.js
├── build/
│   ├── index.asset.php
│   ├── index.js
│   ├── style-index-rtl.css
│   └── style-index.css
├── example-static.php
├── package-lock.json
├── package.json
└── readme.txt

Above is the file structure. Prveiously I had working block in Monolithoc style that I wanted to convert into what is prescribed by WordPress.

block.json —

{
    "$schema": ".json",
    "apiVersion": 2,
    "name": "woracioussuite/cust-sidebar-menu",
    "version": "1.0.0",
    "title": "Woracious Custom Sidebar Menu",
    "category": "theme",
    "icon": "menu",
    "description": "A custom sidebar menu block that can display custom menu items or categories.",
    "supports": {
        "html": false
    },
    "attributes": {
        "heading": {
            "type": "string",
            "default": "Header"
        },
        "menuItems": {
            "type": "array",
            "default": []
        },
        "useCategories": {
            "type": "boolean",
            "default": false
        },
        "categoryOrderBy": {
            "type": "string",
            "default": "name"
        },
        "categoryOrder": {
            "type": "string",
            "default": "ASC"
        },
        "hideEmpty": {
            "type": "boolean",
            "default": true
        },
        "selectedCategories": {
            "type": "array",
            "default": []
        },
        "useSelectedCategoriesOnly": {
            "type": "boolean",
            "default": false
        }
    },
    "textdomain": "woracious-suite",
    "editorScript": "file:./index.js",
    "render": "file:./render.php"
}

package.json —

{
    "name": "woracious-suite",
    "version": "0.1.0",
    "description": "Example block scaffolded with Create Block tool.",
    "author": "James Bond 007",
    "license": "GPL-2.0-or-later",
    "main": "build/index.js",
    "scripts": {
        "start": "wp-scripts start blocks/wo-custom-sidebar-menu/index.js",
        "build": "wp-scripts build --blocks-manifest",
        "format": "wp-scripts format",
        "lint:css": "wp-scripts lint-style",
        "lint:js": "wp-scripts lint-js",
        "packages-update": "wp-scripts packages-update",
        "plugin-zip": "wp-scripts plugin-zip"
    },
    "devDependencies": {
        "@wordpress/scripts": "^30.15.0"
    }
}

index.js—

/**
 * Registers a new block provided a unique name and an object defining its behavior.
 *
 * @see /
 */
import { registerBlockType } from "@wordpress/blocks";

/**
 * Lets webpack process CSS, SASS or SCSS files referenced in JavaScript files.
 * All files containing `style` keyword are bundled together. The code used
 * gets applied both to the front of your site and to the editor.
 *
 * @see /@wordpress/scripts#using-css
 */
import "./style.scss";

/**
 * Internal dependencies
 */
import Edit from "./edit";
import metadata from "./block.json";

/**
 * Every block starts by registering a new block type definition.
 *
 * @see /
 */
registerBlockType(metadata.name, {
    ...metadata,
    edit: Edit,
    // No save function as we're using server-side rendering
    save: () => null,
});

edit.js:

/**
 * WordPress dependencies
 */
import { __ } from "@wordpress/i18n";
import { InspectorControls, useBlockProps } from "@wordpress/block-editor";
import {
    Button,
    CheckboxControl,
    PanelBody,
    SelectControl,
    TextControl,
    ToggleControl,
} from "@wordpress/components";
import { useEffect, useState } from "@wordpress/element";
import apiFetch from "@wordpress/api-fetch";

/**
 * Edit component for the custom sidebar menu block
 *
 * @param {Object} props
 * @returns {JSX.Element} The edit component
 */
export default function Edit({ attributes, setAttributes }) {
    const {
        heading,
        menuItems,
        useCategories,
        categoryOrderBy,
        categoryOrder,
        hideEmpty,
        selectedCategories,
        useSelectedCategoriesOnly,
    } = attributes;

    // State for storing fetched categories
    const [categories, setCategories] = useState([]);
    const [isLoading, setIsLoading] = useState(false);

    // Fetch categories when component mounts or when ordering options change
    useEffect(() => {
        if (useCategories) {
            setIsLoading(true);

            const queryParams =
                `?per_page=100&orderby=${categoryOrderBy}&order=${categoryOrder}` +
                (hideEmpty ? "&hide_empty=true" : "");

            apiFetch({ path: "/wp/v2/categories" + queryParams })
                .then((fetchedCategories) => {
                    setCategories(fetchedCategories);
                    setIsLoading(false);
                })
                .catch((error) => {
                    console.error("Error fetching categories:", error);
                    setIsLoading(false);
                });
        }
    }, [useCategories, categoryOrderBy, categoryOrder, hideEmpty]);

    // Function to add a new menu item
    function addMenuItem() {
        const newItems = [...menuItems];
        newItems.push({ text: "", url: "" });
        setAttributes({ menuItems: newItems });
    }

    // Function to update a menu item
    function updateMenuItem(index, field, value) {
        const newItems = [...menuItems];
        newItems[index][field] = value;
        setAttributes({ menuItems: newItems });
    }

    // Function to remove a menu item
    function removeMenuItem(index) {
        const newItems = [...menuItems];
        newItems.splice(index, 1);
        setAttributes({ menuItems: newItems });
    }

    // Function to toggle category selection
    function toggleCategorySelection(categoryId) {
        const newSelection = [...selectedCategories];
        const index = newSelection.indexOf(categoryId);

        if (index === -1) {
            newSelection.push(categoryId);
        } else {
            newSelection.splice(index, 1);
        }

        setAttributes({ selectedCategories: newSelection });
    }

    // Prepare preview items
    let previewItems = [];

    if (useCategories) {
        if (isLoading) {
            previewItems.push(<li key="loading">Loading categories...</li>);
        } else if (categories.length === 0) {
            previewItems.push(<li key="none">No categories found</li>);
        } else {
            let displayCategories = categories;

            if (useSelectedCategoriesOnly && selectedCategories.length > 0) {
                displayCategories = categories.filter((category) =>
                    selectedCategories.includes(category.id),
                );
            }

            previewItems = displayCategories.slice(0, 5).map((category) => (
                <li key={category.id}>
                    <a href="#">
                        {category.name} ({category.count})
                    </a>
                </li>
            ));

            if (displayCategories.length > 5) {
                previewItems.push(<li key="more">...</li>);
            }
        }
    } else {
        if (menuItems.length === 0) {
            previewItems.push(<li key="add">Add menu items in the sidebar</li>);
        } else {
            previewItems = menuItems.map((item, index) => (
                <li key={index}>
                    <a href={item.url || "#"}>{item.text || "Menu Item"}</a>
                </li>
            ));
        }
    }

    return (
        <>
            <InspectorControls>
                <PanelBody
                    title={__("Menu Settings", "neatminimum2025")}
                    initialOpen={true}
                >
                    <TextControl
                        label={__("Heading", "neatminimum2025")}
                        value={heading}
                        onChange={(value) => setAttributes({ heading: value })}
                    />

                    <ToggleControl
                        label={__("Use Categories", "neatminimum2025")}
                        checked={useCategories}
                        onChange={(value) => setAttributes({ useCategories: value })}
                    />

                    {!useCategories && (
                        <div className="menu-items-controls">
                            <h3>{__("Menu Items", "neatminimum2025")}</h3>
                            {menuItems.map((item, index) => (
                                <div className="menu-item" key={index}>
                                    <TextControl
                                        label={__("Text", "neatminimum2025")}
                                        value={item.text}
                                        onChange={(value) => updateMenuItem(index, "text", value)}
                                    />
                                    <TextControl
                                        label={__("URL", "neatminimum2025")}
                                        value={item.url}
                                        onChange={(value) => updateMenuItem(index, "url", value)}
                                    />
                                    <Button
                                        isDestructive={true}
                                        onClick={() => removeMenuItem(index)}
                                    >
                                        {__("Remove", "neatminimum2025")}
                                    </Button>
                                </div>
                            ))}
                            <Button isPrimary={true} onClick={addMenuItem}>
                                {__("Add Menu Item", "neatminimum2025")}
                            </Button>
                        </div>
                    )}

                    {useCategories && (
                        <PanelBody
                            title={__("Category Settings", "neatminimum2025")}
                            initialOpen={true}
                        >
                            <SelectControl
                                label={__("Order By", "neatminimum2025")}
                                value={categoryOrderBy}
                                options={[
                                    { label: __("Name", "neatminimum2025"), value: "name" },
                                    { label: __("Count", "neatminimum2025"), value: "count" },
                                    { label: __("ID", "neatminimum2025"), value: "id" },
                                ]}
                                onChange={(value) => setAttributes({ categoryOrderBy: value })}
                            />

                            <SelectControl
                                label={__("Order", "neatminimum2025")}
                                value={categoryOrder}
                                options={[
                                    { label: __("Ascending", "neatminimum2025"), value: "asc" },
                                    { label: __("Descending", "neatminimum2025"), value: "desc" },
                                ]}
                                onChange={(value) => setAttributes({ categoryOrder: value })}
                            />

                            <ToggleControl
                                label={__("Hide Empty Categories", "neatminimum2025")}
                                checked={hideEmpty}
                                onChange={(value) => setAttributes({ hideEmpty: value })}
                            />

                            <ToggleControl
                                label={__("Show Only Selected Categories", "neatminimum2025")}
                                checked={useSelectedCategoriesOnly}
                                onChange={(value) =>
                                    setAttributes({ useSelectedCategoriesOnly: value })
                                }
                            />

                            {useSelectedCategoriesOnly && categories.length > 0 && (
                                <div className="category-selection">
                                    <h4>{__("Select Categories", "neatminimum2025")}</h4>
                                    {categories.map((category) => (
                                        <CheckboxControl
                                            key={category.id}
                                            label={`${category.name} (${category.count})`}
                                            checked={selectedCategories.includes(category.id)}
                                            onChange={() => toggleCategorySelection(category.id)}
                                        />
                                    ))}
                                </div>
                            )}

                            {isLoading && (
                                <p>{__("Loading categories...", "neatminimum2025")}</p>
                            )}
                        </PanelBody>
                    )}
                </PanelBody>
            </InspectorControls>

            <div {...useBlockProps({ className: "menu" })}>
                <h6>{heading}</h6>
                <ul>{previewItems}</ul>
            </div>
        </>
    );
}

example-static.php —

<?php
/**
 * Plugin Name:       Woracious Block Suite
 * Description:       Example block scaffolded with Create Block tool.
 * Version:           0.1.0
 * Requires at least: 6.7
 * Requires PHP:      7.4
 * Author:            The WordPress Contributors
 * License:           GPL-2.0-or-later
 * License URI:       .0.html
 * Text Domain:       woracious-suite
 *
 * @package Woracioussuite
 */

if ( ! defined( 'ABSPATH' ) ) {
    exit; 
}

/**
 * Register custom blocks
 */
function woracious_suite_register_custom_blocks() { 
  register_block_type( __DIR__ . '/blocks/wo-custom-sidebar-menu' );
} 
add_action( 'init', 'woracious_suite_register_custom_blocks' ); 
转载请注明原文地址:http://conceptsofalgorithm.com/Algorithm/1748641796a313721.html

最新回复(0)