diff --git a/build/blocks-manifest.php b/build/blocks-manifest.php new file mode 100644 index 0000000..8d4d0e4 --- /dev/null +++ b/build/blocks-manifest.php @@ -0,0 +1,596 @@ + array( + '$schema' => 'https://schemas.wp.org/trunk/block.json', + 'apiVersion' => 3, + 'name' => 'yard/collapse', + 'version' => '0.1.0', + 'title' => 'Uitklap', + 'category' => 'yard', + 'description' => 'Voeg uitklapbare blokken toe om inhoud in en uit te kunnen vouwen.', + 'attributes' => array( + 'hasStructuredData' => array( + 'type' => 'boolean', + 'default' => false + ), + 'headingLevel' => array( + 'type' => 'string', + 'default' => 'h3' + ), + 'showMultiple' => array( + 'type' => 'boolean', + 'default' => true + ) + ), + 'supports' => array( + 'align' => array( + 'wide', + 'full' + ), + 'contentRole' => true, + 'listView' => true, + 'html' => false + ), + 'textdomain' => 'yard', + 'editorScript' => 'file:./index.js', + 'editorStyle' => 'file:./index.css', + 'viewScript' => 'file:./frontend.js' + ), + 'collapse-item' => array( + '$schema' => 'https://schemas.wp.org/trunk/block.json', + 'apiVersion' => 3, + 'name' => 'yard/collapse-item', + 'version' => '0.2.0', + 'title' => 'Uitklap item', + 'category' => 'yard', + 'description' => 'Uitklapbaar component.', + 'attributes' => array( + 'hasStructuredData' => array( + 'type' => 'boolean', + 'default' => false + ), + 'headingLevel' => array( + 'type' => 'string', + 'default' => 'h3' + ), + 'headingText' => array( + 'type' => 'string', + 'default' => '', + 'role' => 'content' + ), + 'icon' => array( + 'type' => 'string', + 'default' => '' + ), + 'iconAltText' => array( + 'type' => 'string', + 'default' => '' + ), + 'isOpen' => array( + 'type' => 'boolean', + 'default' => false + ), + 'anchor' => array( + 'type' => 'string', + 'default' => '' + ), + 'hasSubtitle' => array( + 'type' => 'boolean', + 'default' => false + ), + 'subtitleText' => array( + 'type' => 'string', + 'default' => '', + 'role' => 'content' + ) + ), + 'parent' => array( + 'yard/collapse' + ), + 'supports' => array( + 'anchor' => true, + 'html' => false, + 'reusable' => false + ), + 'textdomain' => 'yard', + 'editorScript' => 'file:./index.js', + 'editorStyle' => 'file:./index.css', + 'style' => 'file:./style-index.css' + ), + 'counting-number' => array( + '$schema' => 'https://schemas.wp.org/trunk/block.json', + 'apiVersion' => 3, + 'name' => 'yard/counting-number', + 'version' => '0.0.2', + 'title' => 'Oplopende teller', + 'category' => 'yard', + 'description' => 'Toont een oplopende cijfer.', + 'attributes' => array( + 'number' => array( + 'type' => 'string', + 'default' => '100' + ), + 'numberPrefix' => array( + 'type' => 'string' + ), + 'numberSuffix' => array( + 'type' => 'string' + ), + 'hasThousandsSeparator' => array( + 'type' => 'boolean', + 'default' => false + ), + 'animationDuration' => array( + 'type' => 'number', + 'default' => 2.5 + ) + ), + 'supports' => array( + 'align' => true, + 'color' => array( + 'background' => true, + 'color' => true + ), + 'contentRole' => true, + 'spacing' => array( + 'margin' => true, + 'padding' => true + ), + 'typography' => array( + 'fontSize' => true + ) + ), + 'textdomain' => 'yard', + 'editorScript' => 'file:./index.js', + 'editorStyle' => 'file:./index.css', + 'viewScript' => 'file:./frontend.js' + ), + 'facetwp' => array( + '$schema' => 'https://schemas.wp.org/trunk/block.json', + 'apiVersion' => 3, + 'name' => 'yard/facetwp', + 'version' => '0.1.0', + 'title' => 'FacetWP', + 'category' => 'yard', + 'description' => 'Plaats filtering op de pagina.', + 'supports' => array( + 'html' => false, + 'align' => array( + 'wide', + 'full' + ) + ), + 'attributes' => array( + 'selectedFacets' => array( + 'type' => 'array', + 'default' => array( + + ) + ), + 'selectedTemplate' => array( + 'type' => 'object', + 'default' => array( + + ) + ), + 'align' => array( + 'type' => 'string', + 'default' => 'wide' + ) + ), + 'textdomain' => 'yard', + 'editorScript' => 'file:./index.js', + 'editorStyle' => 'file:./index.css' + ), + 'icon' => array( + '$schema' => 'https://schemas.wp.org/trunk/block.json', + 'apiVersion' => 3, + 'name' => 'yard/icon', + 'version' => '0.1.0', + 'title' => 'Icoon', + 'category' => 'yard', + 'description' => 'Voeg een Font Awesome icoon toe.', + 'supports' => array( + 'align' => true, + 'ariaLabel' => true, + 'color' => array( + 'background' => true, + 'color' => true + ), + 'contentRole' => true, + 'spacing' => array( + 'margin' => true, + 'padding' => true + ), + 'typography' => array( + 'fontSize' => true + ) + ), + 'attributes' => array( + 'icon' => array( + 'type' => 'string', + 'default' => 'fa-classic fa-light fa-envelope' + ), + 'iconAltText' => array( + 'type' => 'string', + 'default' => '' + ) + ), + 'textdomain' => 'yard', + 'editorScript' => 'file:./index.js', + 'editorStyle' => 'file:./index.css' + ), + 'iconlist' => array( + '$schema' => 'https://schemas.wp.org/trunk/block.json', + 'apiVersion' => 3, + 'name' => 'yard/iconlist', + 'version' => '0.1.1', + 'title' => 'Iconenlijst', + 'category' => 'yard', + 'description' => 'Voeg een lijst met Font Awesome iconen toe. Verander de tekst en iconen afzonderlijk van elkaar.', + 'attributes' => array( + 'useLinkComponent' => array( + 'type' => 'boolean', + 'default' => false + ) + ), + 'supports' => array( + 'align' => array( + 'wide', + 'full' + ), + 'contentRole' => true, + 'html' => false, + 'layout' => array( + 'default' => array( + 'type' => 'flex', + 'orientation' => 'vertical' + ), + 'allowVerticalAlignment' => false + ), + 'listView' => true + ), + 'textdomain' => 'yard', + 'editorScript' => 'file:./index.js', + 'editorStyle' => 'file:./index.css', + 'style' => 'file:./style-index.css' + ), + 'iconlist-item' => array( + '$schema' => 'https://schemas.wp.org/trunk/block.json', + 'apiVersion' => 3, + 'name' => 'yard/iconlist-item', + 'version' => '0.1.1', + 'title' => 'Iconenlijst item', + 'category' => 'yard', + 'description' => 'Een enkele iconenlijst item.', + 'attributes' => array( + 'icon' => array( + 'type' => 'string', + 'default' => 'fa-classic fa-light fa-envelope', + 'role' => 'content' + ), + 'iconAltText' => array( + 'type' => 'string', + 'default' => '' + ), + 'listText' => array( + 'type' => 'string', + 'default' => '', + 'role' => 'content' + ), + 'linkUrl' => array( + 'type' => 'string', + 'default' => '', + 'role' => 'content' + ), + 'opensInNewTab' => array( + 'type' => 'boolean', + 'default' => false, + 'role' => 'content' + ) + ), + 'parent' => array( + 'yard/iconlist' + ), + 'supports' => array( + 'align' => false, + 'color' => array( + 'background' => true, + 'text' => true + ), + 'reusable' => false, + 'typography' => array( + 'fontSize' => true + ) + ), + 'textdomain' => 'yard', + 'editorScript' => 'file:./index.js', + 'editorStyle' => 'file:./index.css', + 'style' => 'file:./style-index.css' + ), + 'slide' => array( + '$schema' => 'https://schemas.wp.org/trunk/block.json', + 'apiVersion' => 3, + 'name' => 'yard/slide', + 'version' => '0.1.0', + 'title' => 'Slide', + 'category' => 'yard', + 'description' => 'Een enkele slide binnen een slider blok.', + 'attributes' => array( + + ), + 'parent' => array( + 'yard/slider' + ), + 'supports' => array( + 'contentRole' => true, + 'html' => false, + 'reusable' => false + ), + 'textdomain' => 'yard', + 'editorScript' => 'file:./index.js', + 'editorStyle' => 'file:./index.css' + ), + 'slider' => array( + '$schema' => 'https://schemas.wp.org/trunk/block.json', + 'apiVersion' => 3, + 'name' => 'yard/slider', + 'version' => '0.1.0', + 'title' => 'Slider', + 'category' => 'yard', + 'description' => 'Voeg slider toe.', + 'attributes' => array( + 'align' => array( + 'type' => 'string', + 'default' => 'wide' + ), + 'activeSlide' => array( + 'type' => 'string', + 'default' => null + ) + ), + 'supports' => array( + 'align' => array( + 'wide', + 'full' + ), + 'contentRole' => true, + 'html' => false, + 'listView' => true, + 'spacing' => array( + 'margin' => true, + 'padding' => true + ) + ), + 'textdomain' => 'yard', + 'editorScript' => 'file:./index.js', + 'editorStyle' => 'file:./index.css' + ), + 'table-of-contents' => array( + '$schema' => 'https://schemas.wp.org/trunk/block.json', + 'apiVersion' => 3, + 'name' => 'yard/table-of-contents', + 'version' => '0.1.0', + 'title' => 'Inhoudsopgave', + 'category' => 'yard', + 'description' => 'Toont een inhoudsopgave op basis van de koppen op de pagina.', + 'supports' => array( + 'html' => false, + 'multiple' => false, + 'reusable' => false + ), + 'attributes' => array( + 'includeSubheading' => array( + 'type' => 'boolean', + 'default' => true + ), + 'contentSelector' => array( + 'type' => 'string', + 'default' => '' + ), + 'headingSelector' => array( + 'type' => 'string', + 'default' => '' + ) + ), + 'textdomain' => 'yard', + 'editorScript' => 'file:./index.js', + 'editorStyle' => 'file:./index.css', + 'viewScript' => 'file:./view.js' + ), + 'tabs' => array( + '$schema' => 'https://schemas.wp.org/trunk/block.json', + 'apiVersion' => 3, + 'name' => 'yard/tabs', + 'version' => '0.1.1', + 'title' => 'Tabbladen', + 'category' => 'yard', + 'description' => 'Groepeer gemakkelijk content in verschillende tabbladen.', + 'attributes' => array( + 'align' => array( + 'type' => 'string', + 'default' => 'wide' + ), + 'currentTab' => array( + 'type' => 'string' + ), + 'defaultTab' => array( + 'type' => 'string' + ), + 'defaultTabEnabled' => array( + 'type' => 'boolean', + 'default' => false + ), + 'headingLevel' => array( + 'type' => 'string', + 'default' => 'h3' + ) + ), + 'supports' => array( + 'html' => false, + 'align' => array( + 'wide', + 'full' + ), + 'contentRole' => true, + 'listView' => true + ), + 'textdomain' => 'yard', + 'editorScript' => 'file:./index.js', + 'editorStyle' => 'file:./index.css', + 'viewScript' => 'file:./frontend.js', + 'style' => 'file:./style-index.css' + ), + 'tabs-item' => array( + '$schema' => 'https://schemas.wp.org/trunk/block.json', + 'apiVersion' => 3, + 'name' => 'yard/tabs-item', + 'version' => '0.1.1', + 'title' => 'Tabblad', + 'category' => 'yard', + 'description' => 'Tabblad item.', + 'attributes' => array( + 'headingLevel' => array( + 'type' => 'string', + 'default' => 'h3' + ), + 'headingText' => array( + 'type' => 'string', + 'default' => '', + 'role' => 'content' + ), + 'icon' => array( + 'type' => 'string', + 'default' => '' + ), + 'iconAltText' => array( + 'type' => 'string', + 'default' => '' + ), + 'id' => array( + 'type' => 'string' + ) + ), + 'parent' => array( + 'yard/tabs' + ), + 'supports' => array( + 'anchor' => true, + 'contentRole' => true, + 'html' => false, + 'reusable' => false + ), + 'textdomain' => 'yard', + 'editorScript' => 'file:./index.js', + 'editorStyle' => 'file:./index.css', + 'style' => 'file:./style-index.css' + ), + 'timeline' => array( + '$schema' => 'https://schemas.wp.org/trunk/block.json', + 'apiVersion' => 3, + 'name' => 'yard/timeline', + 'version' => '0.2.0', + 'title' => 'Tijdlijn', + 'category' => 'yard', + 'description' => 'Creeƫrt een tijdlijn wrapper waarbij elk blokje een nieuwe stap is.', + 'attributes' => array( + 'isOrderedList' => array( + 'type' => 'boolean', + 'default' => false + ), + 'headingLevel' => array( + 'type' => 'string', + 'default' => 'h3' + ) + ), + 'supports' => array( + 'align' => array( + 'wide' + ), + 'color' => array( + 'background' => true, + 'text' => true + ), + 'contentRole' => true, + 'html' => false, + 'listView' => true + ), + 'textdomain' => 'yard', + 'editorScript' => 'file:./index.js', + 'editorStyle' => 'file:./index.css', + 'style' => 'file:./style-index.css' + ), + 'timeline-item' => array( + '$schema' => 'https://schemas.wp.org/trunk/block.json', + 'apiVersion' => 3, + 'name' => 'yard/timeline-item', + 'version' => '0.1.0', + 'title' => 'Tijdlijn item', + 'category' => 'yard', + 'description' => 'Een nieuwe stap van de tijdlijn.', + 'parent' => array( + 'yard/timeline' + ), + 'attributes' => array( + + ), + 'supports' => array( + 'color' => array( + 'background' => true, + 'text' => true + ), + 'contentRole' => true, + 'html' => false + ), + 'textdomain' => 'yard', + 'editorScript' => 'file:./index.js' + ), + 'timeline-item-collapse' => array( + '$schema' => 'https://schemas.wp.org/trunk/block.json', + 'apiVersion' => 3, + 'name' => 'yard/timeline-item-collapse', + 'version' => '0.1.0', + 'title' => 'Tijdlijn item uitklapbaar', + 'category' => 'yard', + 'description' => 'Een nieuwe uitklapbare stap van de tijdlijn.', + 'parent' => array( + 'yard/timeline' + ), + 'attributes' => array( + 'title' => array( + 'type' => 'string', + 'source' => 'html', + 'selector' => '.wp-block-yard-timeline-item-collapse__title', + 'role' => 'content' + ), + 'headingLevel' => array( + 'type' => 'string', + 'default' => 'h3' + ), + 'subtitle' => array( + 'type' => 'string', + 'source' => 'html', + 'selector' => '.wp-block-yard-timeline-item-collapse__subtitle', + 'role' => 'content' + ), + 'isOpen' => array( + 'type' => 'boolean', + 'default' => false + ), + 'hasInnerBlocks' => array( + 'type' => 'boolean', + 'default' => false + ) + ), + 'supports' => array( + 'color' => array( + 'background' => true, + 'text' => true + ), + 'html' => false + ), + 'textdomain' => 'yard', + 'editorScript' => 'file:./index.js' + ) +); diff --git a/package.json b/package.json index e8f3d44..2aa3c02 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,7 @@ "license": "MIT License", "main": "build/index.js", "scripts": { - "build": "wp-scripts build", + "build": "wp-scripts build --blocks-manifest", "lint:css": "yard-toolkit lint css -m custom './src/**/*.css'", "lint:js": "yard-toolkit lint js -m custom './src/**/*.js'", "lint:scss": "yard-toolkit lint scss -m custom './src/**/*.scss'", @@ -15,7 +15,7 @@ "format:scss": "yard-toolkit format scss -m custom './src/**/*.scss'", "packages-update": "wp-scripts packages-update", "plugin-zip": "wp-scripts plugin-zip", - "start": "wp-scripts start" + "start": "wp-scripts start --blocks-manifest" }, "devDependencies": { "@wordpress/scripts": "^31.5.0", diff --git a/src/PluginServiceProvider.php b/src/PluginServiceProvider.php index 7307aff..0c7fab4 100644 --- a/src/PluginServiceProvider.php +++ b/src/PluginServiceProvider.php @@ -68,65 +68,62 @@ public function addBlockCategory(array $categories) * Behind the scenes, it registers also all assets so they can be enqueued * through the block editor in the corresponding context. * + * If a blocks-manifest.php is available (generated by wp-scripts' + * `--blocks-manifest` flag as part of `npm run build`), its metadata + * collection is registered first so `register_block_type()` reads every + * block's data from that one already-loaded array instead of reading + * and JSON-decoding each block.json from disk on every request. Older + * builds without a manifest fall back to reading block.json from disk. + * * @see https://developer.wordpress.org/reference/functions/register_block_type/ + * @see https://developer.wordpress.org/block-editor/reference-guides/packages/packages-scripts/#blocks-manifest */ public function registerBlocks() { $blocksPath = dirname(__DIR__, 1) . '/build/Blocks/'; - $folders = array_filter(glob($blocksPath . '*', GLOB_ONLYDIR)); + $manifestPath = dirname($blocksPath) . '/blocks-manifest.php'; + + if (function_exists('wp_register_block_metadata_collection') && file_exists($manifestPath)) { + wp_register_block_metadata_collection($blocksPath, $manifestPath); + } + + $blockNames = array_map('basename', array_filter(glob($blocksPath . '*', GLOB_ONLYDIR) ?: [])); if (has_filter('yard::gutenberg/allowed-blocks')) { $allowedBlocks = apply_filters('yard::gutenberg/allowed-blocks', []); - $folders = array_filter( - $folders, - function (string $folder) use ($allowedBlocks) { - return in_array(basename($folder), $allowedBlocks); + $blockNames = array_filter( + $blockNames, + function (string $blockName) use ($allowedBlocks) { + return in_array($blockName, $allowedBlocks); } ); } - foreach ($folders as $folder) { - $blockName = basename($folder); - + foreach ($blockNames as $blockName) { $blockPath = $blocksPath . $blockName; + $renderCallback = $this->getRenderCallback($blockName); - if ($this->isDynamicBlock($blockName)) { - \register_block_type($blockPath, [ - 'render_callback' => $this->getRenderCallback($blockName), - ]); - } else { - \register_block_type($blockPath); - } + \register_block_type($blockPath, $renderCallback ? ['render_callback' => $renderCallback] : []); } } - /** - * Check if the block is dynamic. + * Get the render callback for a dynamic block, if a matching render class exists. * - * @param string $blockName The name of the block. - */ - public function isDynamicBlock($blockName): bool - { - $files = glob(__DIR__ . '/*/*/' . ucfirst($blockName) . '.php') ?? []; - - if (count($files)) { - return true; - } - - return false; - } - - /** - * Get the render callback for a dynamic block if it exists. + * Block names containing a hyphen (e.g. `collapse-item`) can never map to a + * valid PHP class name under our namespacing convention, so they're skipped + * before the `class_exists()` autoload lookup. * * @param string $blockName The name of the block. * - * @return callable|null The render callback or null if not found. + * @return callable|null The render callback or null if the block isn't dynamic. */ - public function getRenderCallback(string $blockName) + public function getRenderCallback(string $blockName): ?callable { + if (false !== strpos($blockName, '-')) { + return null; + } $nameSpacedClass = 'Yard\\Gutenberg\\Blocks\\' . $blockName . '\\' . ucfirst($blockName); if (class_exists($nameSpacedClass)) {