auto-registering blocks in php and javascript with dynamic enqueuing of associated styles and scripts with support for scss and blade

This commit is contained in:
2025-12-13 14:22:42 +00:00
parent f8d5bb5aa2
commit ac5351c8c7
25 changed files with 219 additions and 67 deletions

View File

@@ -36,64 +36,75 @@ add_action('init', function () {
$blocks = glob(get_theme_file_path('resources/views/blocks/*/block.json')); $blocks = glob(get_theme_file_path('resources/views/blocks/*/block.json'));
foreach ($blocks as $block_json) { foreach ($blocks as $block_json) {
$json = json_decode($block_json); $json = json_decode(file_get_contents($block_json));
$slug = basename(dirname($block_json)); $slug = basename(dirname($block_json));
$blockPath = "resources/views/blocks/{$slug}";
// Editor JS $viewScript = "{$blockPath}/view.js";
$editor_js_path = "resources/views/blocks/{$slug}/index.jsx"; $script = "{$blockPath}/script.js";
if (file_exists(get_theme_file_path($editor_js_path))) { $editorCSS = "{$blockPath}/editor.scss";
$style = "{$blockPath}/style.scss";
// editorStyle
if (file_exists(get_theme_file_path($editorCSS))) {
wp_register_style(
"{$slug}-editor-style",
\Vite::asset($editorCSS),
[],
null
);
}
// script
if(file_exists(get_theme_file_path($script))) {
wp_register_script( wp_register_script(
"{$slug}-editor-script", "{$slug}-script",
\Vite::asset($editor_js_path), \Vite::asset($script),
['wp-blocks', 'wp-element', 'wp-editor'], [],
null, null,
true true
); );
} }
// Editor SCSS (compiled to CSS) // style
$editor_css_path = "resources/views/blocks/{$slug}/editor.scss"; if (file_exists(get_theme_file_path($style))) {
if (file_exists(get_theme_file_path($editor_css_path))) {
wp_register_style( wp_register_style(
"{$slug}-editor-style", "{$slug}-style",
\Vite::asset($editor_css_path), \Vite::asset($style),
[], [],
null null
); );
} }
// Frontend SCSS (compiled to CSS) // viewScript
$style_css_path = "resources/views/blocks/{$slug}/style.scss"; if(file_exists(get_theme_file_path($viewScript))) {
if (file_exists(get_theme_file_path($style_css_path))) { wp_register_script(
wp_register_style( "{$slug}-view-script",
"{$slug}-style", \Vite::asset($viewScript),
\Vite::asset($style_css_path),
[], [],
null null,
true
); );
} }
$props = [ $props = [
'editor_script' => "{$slug}-editor-script", 'editor_style' => "{$slug}-editor-style",
'editor_style' => "{$slug}-editor-style", 'style' => "{$slug}-style",
'style' => "{$slug}-style", 'script' => "{$slug}-script",
'render_callback' => function ($attributes, $content, $block) { 'view_script' => "{$slug}-view-script",
$slug = basename($block->name);
$view = "blocks.{$slug}.render";
if (\Roots\view()->exists($view)) {
return \Roots\view($view, [
'attributes' => $attributes,
'content' => $content,
'block' => $block,
]);
}
return $content;
}
]; ];
if(!@$json['render_callback']) unset($props['render_callback']); if(!property_exists($json, 'acf') && \Roots\view()->exists("blocks.{$slug}.render")) {
$props['render_callback'] = function ($attributes, $content, $block) {
$slug = basename($block->name);
return \Roots\view("blocks.{$slug}.render", [
'attributes' => $attributes,
'content' => $content,
'block' => $block,
]);
};
}
register_block_type($block_json, $props); register_block_type($block_json, $props);
} }
@@ -136,3 +147,36 @@ function list_allowed()
apply_filters('badegg_block_types_allow', $add_allowed), apply_filters('badegg_block_types_allow', $add_allowed),
); );
} }
function render_acf($block, $content = '', $is_preview = false, $post_id = 0, $wp_block = false, $context = false) {
$slug = basename($block['name']);
$block['slug'] = $slug;
$blade = \Roots\view("blocks.{$slug}.render", [
'block' => $block,
'content' => $content,
'is_preview' => $is_preview,
'post_id' => $post_id,
'wp_block' => $wp_block,
'context' => $context,
]);
if($blade) {
echo $blade;
} else {
ob_start(); ?>
<section class="section bg-error knockout">
<div class="container container-small align-centre wysiwyg">
<h2>Missing Blade Template</h2>
<p>(resources/views/blocks/<?= $slug ?>/render.blade.php)</p>
</div>
</section>
<?php echo ob_get_clean();
}
}
add_action('wp_footer', function(){
echo '<pre>',print_r(list_allowed()),'</pre>';
});

View File

@@ -3,8 +3,11 @@ import.meta.glob([
'../fonts/**', '../fonts/**',
]); ]);
// import.meta.glob('../views/blocks/**/{style.scss,script.js,view.js}', { eager: true })
import Header from '../views/sections/header/header.js'; import Header from '../views/sections/header/header.js';
import LazyLoad from './lib/Lazy.js'; import LazyLoad from './lib/Lazy.js';
LazyLoad(); LazyLoad();
Header(); Header();

View File

@@ -1,5 +1,6 @@
import domReady from '@wordpress/dom-ready'; import domReady from '@wordpress/dom-ready';
import blockWhitelist from '../json/core-block-whitelist.json'; import blockWhitelist from '../json/core-block-whitelist.json';
import.meta.glob('../views/blocks/**/{index.jsx,index.js}', { eager: true })
domReady(() => { domReady(() => {
const TEXT_EDITOR_BLOCKS = [ const TEXT_EDITOR_BLOCKS = [

View File

@@ -66,7 +66,7 @@ class Editor
$data = array_merge($data, $block); $data = array_merge($data, $block);
$data['section_classes'] = $CssClasses->section($data); $data['section_classes'] = $CssClasses->section($data);
$data['allowed_blocks'] = \App\block_whitelist(); $data['allowed_blocks'] = \App\Blocks\list_inner();
$data['template'] = $this->default_template(); $data['template'] = $this->default_template();
$data['block'] = $block; $data['block'] = $block;

View File

@@ -0,0 +1,33 @@
{
"name": "badegg/acfdemo",
"title": "ACF Demo",
"category": "badegg",
"icon": "media-document",
"description": "An example block powered by ACF",
"keywords": ["acf", "demo"],
"editorScript": "acfdemo-editor-script",
"editorStyle": "acfdemo-editor-style",
"script": "acfdemo-script",
"style": "acfdemo-style",
"viewScript": "acfdemo-view-script",
"acf": {
"mode": "preview",
"validate": "false",
"renderCallback": "\\App\\Blocks\\render_acf"
},
"supports": {
"anchor": true,
"align": false,
"jsx": true
},
"example": {
"attributes": {
"mode": "preview",
"data": {
"inserter": true
}
}
}
}

View File

@@ -0,0 +1,4 @@
// block.json's editorStyle, applied in block editor and front end
.wp-block-acf-demo.block-editor-block-list__block {
display: block;
}

View File

@@ -0,0 +1,2 @@
// block.json's editorScript, loaded only in the block editor
console.log('loaded: resources/views/blocks/acfdemo/index.js')

View File

@@ -0,0 +1,3 @@
<section class="wp-block-{{ $block['slug'] }}">
<h2>ACF Example</h2>
</section>

View File

@@ -0,0 +1,2 @@
// block.json's script, loaded in block editor and front end
console.log('loaded: resources/views/blocks/acf-demo/script.js')

View File

@@ -0,0 +1,4 @@
// block.json's style, applied in block editor and front end
.wp-block-acf-demo {
display: block;
}

View File

@@ -0,0 +1,2 @@
// block.json's viewScript, applied on front end only
console.log('loaded: resources/views/blocks/acf-demo/view.js')

View File

@@ -3,8 +3,17 @@
"name": "badegg/article", "name": "badegg/article",
"title": "Article Builder", "title": "Article Builder",
"category": "badegg", "category": "badegg",
"icon": {
"src": "format-aside",
"foreground": "#f58762"
},
"description": "A wrapper to contain core blocks", "description": "A wrapper to contain core blocks",
"editorScript": "article-editor-script", "supports": {
"editorStyle": "article-editor-style", "html": true,
"style": "article-style" "align": ["wide", "full"],
"color": {
"background": true,
"text": false
}
}
} }

View File

@@ -1,3 +1,4 @@
.block-badegg-example-editor { // block.json's editorStyle, applied in block editor and front end
.wp-block-badegg-article.block-editor-block-list__block {
display: block; display: block;
} }

View File

@@ -1,13 +1,11 @@
// block.json's editorScript, loaded only in the block editor
import { registerBlockType } from '@wordpress/blocks'; import { registerBlockType } from '@wordpress/blocks';
import { InnerBlocks, useBlockProps } from '@wordpress/block-editor'; import { InnerBlocks, useBlockProps } from '@wordpress/block-editor';
import metadata from './block.json'; import metadata from './block.json';
import allowedBlocks from '../../../json/core-block-whitelist.json'; import allowedBlocks from '../../../json/core-block-whitelist.json';
registerBlockType(metadata.name, { registerBlockType(metadata.name, {
icon: {
src: 'format-aside',
foreground: '#f58762',
},
edit({ attributes, setAttributes }) { edit({ attributes, setAttributes }) {
const blockProps = useBlockProps(); const blockProps = useBlockProps();

View File

@@ -1,3 +0,0 @@
<section class="block-badegg-article">
<h2>Bad Egg Article Block (Blade)</h2>
</section>

View File

@@ -0,0 +1,2 @@
// block.json's script, loaded in block editor and front end
console.log('loaded: resources/views/blocks/article/script.js')

View File

@@ -1,3 +1,4 @@
.block-badegg-hero { // block.json's style, applied in block editor and front end
.wp-block-badegg-article {
display: block; display: block;
} }

View File

@@ -0,0 +1,2 @@
// block.json's viewScript, applied on front end only
console.log('loaded: resources/views/blocks/article/view.js')

View File

@@ -4,9 +4,12 @@
"title": "Example", "title": "Example",
"category": "badegg", "category": "badegg",
"icon": "cover-image", "icon": "cover-image",
"description": "This is an example of a custom Wordpress Block created using the offical method.", "description": "This is an example of a custom native block",
"editorScript": "example-editor-script", "editorScript": "example-editor-script",
"editorStyle": "example-editor-style", "editorStyle": "example-editor-style",
"style": "example-style", "style": "example-style",
"render": "resources/blocks/example/render.blade.php" "script": "example-script",
"supports": {
"html": false
}
} }

View File

@@ -1,3 +1,5 @@
.block-badegg-example-editor { // block.json's editorStyle, applied in block editor and front end
.wp-block-badegg-example.block-editor-block-list__block {
display: block; display: block;
border: 2px solid red;
} }

View File

@@ -1,11 +1,17 @@
import { registerBlockType } from '@wordpress/blocks'; // block.json's editorScript, loaded only in the block editor
registerBlockType('badegg/example', { import { registerBlockType } from '@wordpress/blocks';
import { useBlockProps } from '@wordpress/block-editor';
import metadata from './block.json';
registerBlockType(metadata.name, {
edit() { edit() {
const blockProps = useBlockProps();
return ( return (
<section className="block-badegg-example"> <section { ...blockProps }>
<h2>Bad Egg Block Example</h2> <h2>Bad Egg Block Example</h2>
</section> </section>
); );
}, }
}); });

View File

@@ -0,0 +1,2 @@
// block.json's script, loaded in block editor and front end
console.log('loaded: resources/views/blocks/example/script.js')

View File

@@ -1,3 +1,4 @@
.block-badegg-hero { // block.json's style, applied in block editor and front end
.wp-block-badegg-example {
display: block; display: block;
} }

View File

@@ -0,0 +1,2 @@
// block.json's viewScript, applied on front end only
console.log('loaded: resources/views/blocks/example/view.js')

View File

@@ -1,21 +1,49 @@
import { defineConfig } from 'vite' import { defineConfig } from 'vite'
import laravel from 'laravel-vite-plugin' import laravel from 'laravel-vite-plugin'
import { wordpressPlugin, wordpressThemeJson } from '@roots/vite-plugin'; import { wordpressPlugin, wordpressThemeJson } from '@roots/vite-plugin';
import fg from 'fast-glob' import fg from 'fast-glob';
import path from 'path';
const blockEntries = fg.sync('resources/views/blocks/**/{index.jsx,style.scss,editor.js,editor.scss}') function blockAsset(file)
{
const files = fg.sync('resources/views/blocks/**/' + file);
let list = {};
files.forEach(file => {
const parts = file.split(path.sep);
const fileName = parts[parts.length - 1];
const extension = fileName.split('.').pop();
const blockName = parts[parts.length - 2];
list[`blocks/${blockName}/${fileName.replace('.' + extension, '')}`] = `resources/views/blocks/${blockName}/${fileName}`;
});
return list;
}
const editorStyle = blockAsset('editor.scss');
const script = blockAsset('script.js');
const viewScript = blockAsset('view.js');
const style = blockAsset('style.scss');
export default defineConfig({ export default defineConfig({
base: '/app/themes/badegg/public/build/', base: '/app/themes/badegg/public/build/',
plugins: [ plugins: [
laravel({ laravel({
input: [ input: {
'resources/css/app.scss', // 'resources/css/app.scss',
'resources/js/app.js', // 'resources/js/app.js',
'resources/css/editor.scss', // 'resources/css/editor.scss',
'resources/js/editor.js', // 'resources/js/editor.js',
...blockEntries, 'css/app': 'resources/css/app.scss',
], 'js/app': 'resources/js/app.js',
'css/editor': 'resources/css/editor.scss',
'js/editor': 'resources/js/editor.js',
...editorStyle,
...viewScript,
...script,
...style,
},
refresh: true, refresh: true,
url: process.env.APP_URL, url: process.env.APP_URL,
}), }),