diff --git a/.gitignore b/.gitignore index f6c3bf6..581f2f0 100755 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ release vendor phpunit.xml .idea +.vscode # Project Files dist diff --git a/README.md b/README.md index bc2aef0..bff7c14 100755 --- a/README.md +++ b/README.md @@ -86,6 +86,16 @@ _* If your host machine's local version of PHP is <7.2, composer may produce the ``` _To suppress this error, add the flag `--ignore-platform-reqs` (ie. `composer install --ignore-platform-reqs`)._ +## Custom Blocks +The Theme Scaffolding supports custom blocks and leverages the Block API as of WordPress 5.5. Set up a custom block using the following steps: + +1. Define an entry point for the block in `/config/webpack.settings.js`. The entry point should in the format `{name}-block` i.e `hero-block` +2. Add a directory for the block in `/includes/blocks/{name}-block`. i.e `/includes/blocks/hero-block` +3. The block directory requires a `block.json` file. +4. Refer to the `./includes/blocks/example-block` directory to see how the block files should be set up. + +To set up a custom block As such, each custom block must have an entry point defined in `/config/webpack.settings.js` and it's assets stored in `/includes/blocks/{block}` + ## Automated Style Guide The Theme Scaffolding ships with a default style guide you can find in `/templates/page-styleguide.php`. This file contains all the basic HTML elements you would find at the very top of the cascade (headings, typography, tables, forms, etc.) These base elements will be styled and displayed as you naturally build out your CSS. The style guide also automatically pulls in the color variables used in the project. Any hex codes added into `/assets/css/frontend/global/variables.css` will be automatically displayed in the style guide. To set up your style guide, you just need to create a new page in WordPress and assign it the "Style Guide" template. diff --git a/assets/css/blocks/example-block-1.css b/assets/css/blocks/example-block.css similarity index 100% rename from assets/css/blocks/example-block-1.css rename to assets/css/blocks/example-block.css diff --git a/assets/css/blocks/index.css b/assets/css/blocks/index.css index 5cf729f..8e8eba8 100644 --- a/assets/css/blocks/index.css +++ b/assets/css/blocks/index.css @@ -9,7 +9,7 @@ * create new partials here as needed */ -/* @import url("example-block-1.css"); */ +/* @import url("example-block.css"); */ /* @import url("example-block-2.css"); */ diff --git a/assets/css/frontend/editor-style.css b/assets/css/frontend/editor-style.css index 3fd45fa..3e5f22e 100755 --- a/assets/css/frontend/editor-style.css +++ b/assets/css/frontend/editor-style.css @@ -8,4 +8,10 @@ /* Gutenberg blocks */ +/* +For Admin specific overrides please see use +the index.css in the block directory. +*/ + + /* @import url("../blocks/index.css"); */ diff --git a/assets/js/blocks/blocks.js b/assets/js/blocks/blocks.js deleted file mode 100644 index 83ea3c3..0000000 --- a/assets/js/blocks/blocks.js +++ /dev/null @@ -1,6 +0,0 @@ -/** - * Gutenberg block-specific JavaScript: - * used on front-end and/or in editor - */ - -// import example-block-1 from './example-block-1'; diff --git a/assets/js/blocks/example-block-1.js b/assets/js/blocks/example-block-1.js deleted file mode 100644 index 798dead..0000000 --- a/assets/js/blocks/example-block-1.js +++ /dev/null @@ -1,8 +0,0 @@ -/** - * Gutenberg block-specific JavaScript: - * used on front-end and/or in editor - */ - -/** - * Block 1 JavaScript - */ diff --git a/config/webpack.config.common.js b/config/webpack.config.common.js index 7138800..a9e3ab2 100644 --- a/config/webpack.config.common.js +++ b/config/webpack.config.common.js @@ -6,6 +6,7 @@ const MiniCssExtractPlugin = require('mini-css-extract-plugin'); const StyleLintPlugin = require('stylelint-webpack-plugin'); const WebpackBar = require('webpackbar'); const ImageminPlugin = require('imagemin-webpack-plugin').default; +const DependencyExtractionWebpackPlugin = require('@wordpress/dependency-extraction-webpack-plugin'); const isProduction = process.env.NODE_ENV === 'production'; @@ -31,7 +32,12 @@ module.exports = { entry: configureEntries(), output: { path: path.resolve(process.cwd(), settings.paths.dist.base), - filename: settings.filename.js, + filename: (pathData) => { + return pathData.chunk.name.includes('block') + ? settings.filename.block + : settings.filename.js; + }, + /** * If multiple webpack runtimes (from different compilations) are used on the same webpage, * there is a risk of conflicts of on-demand chunks in the global namespace. @@ -87,7 +93,10 @@ module.exports = { // Styles. { test: /\.css$/, - include: path.resolve(process.cwd(), settings.paths.src.css), + include: [ + path.resolve(process.cwd(), settings.paths.src.base), + path.resolve(process.cwd(), settings.paths.src.blocks), + ], use: [ { loader: MiniCssExtractPlugin.loader, @@ -126,6 +135,8 @@ module.exports = { // Extract CSS into individual files. new MiniCssExtractPlugin({ filename: settings.filename.css, + moduleFilename: ({ name }) => + name.includes('block') ? settings.filename.blockCSS : settings.filename.css, chunkFilename: '[id].css', }), @@ -153,5 +164,8 @@ module.exports = { // Fancy WebpackBar. new WebpackBar(), + + // Extract dependencies + new DependencyExtractionWebpackPlugin({ injectPolyfill: true }), ], }; diff --git a/config/webpack.settings.js b/config/webpack.settings.js index bc1d4ef..c8efb46 100644 --- a/config/webpack.settings.js +++ b/config/webpack.settings.js @@ -3,11 +3,9 @@ module.exports = { entries: { // JS files. admin: './assets/js/admin/admin.js', - blocks: './assets/js/blocks/blocks.js', frontend: './assets/js/frontend/frontend.js', shared: './assets/js/shared/shared.js', styleguide: './assets/js/styleguide/styleguide.js', - 'blocks-editor': './includes/blocks/blocks-editor.js', // CSS files. 'admin-style': './assets/css/admin/admin-style.css', @@ -15,20 +13,27 @@ module.exports = { 'shared-style': './assets/css/shared/shared-style.css', style: './assets/css/frontend/style.css', 'styleguide-style': './assets/css/styleguide/styleguide.css', + + // Blocks + // Uncomment to build the example block. + // 'example-block': './includes/blocks/example-block/', }, filename: { js: 'js/[name].js', css: 'css/[name].css', + block: 'blocks/[name]/editor.js', + blockCSS: 'blocks/[name]/editor.css', }, paths: { src: { base: './assets/', + blocks: './includes/blocks/', css: './assets/css/', js: './assets/js/', }, dist: { base: './dist/', - clean: ['./images', './css', './js'], + clean: ['./images', './css', './js', './blocks'], }, }, stats: { diff --git a/functions.php b/functions.php index 6d83ca1..12802fa 100755 --- a/functions.php +++ b/functions.php @@ -10,6 +10,7 @@ define( 'TENUP_SCAFFOLD_TEMPLATE_URL', get_template_directory_uri() ); define( 'TENUP_SCAFFOLD_PATH', get_template_directory() . '/' ); define( 'TENUP_SCAFFOLD_INC', TENUP_SCAFFOLD_PATH . 'includes/' ); +define( 'TENUP_SCAFFOLD_BLOCK_DIR', TENUP_SCAFFOLD_INC . 'blocks/' ); require_once TENUP_SCAFFOLD_INC . 'core.php'; require_once TENUP_SCAFFOLD_INC . 'overrides.php'; diff --git a/includes/blocks.php b/includes/blocks.php index 3d18dc4..88e4347 100644 --- a/includes/blocks.php +++ b/includes/blocks.php @@ -7,6 +7,9 @@ namespace TenUpScaffold\Blocks; +use TenUpScaffold\Blocks\Example; + + /** * Set up blocks * @@ -17,10 +20,56 @@ function setup() { return __NAMESPACE__ . "\\$function"; }; - add_action( 'enqueue_block_assets', $n( 'blocks_scripts' ) ); - add_action( 'enqueue_block_editor_assets', $n( 'blocks_editor_scripts' ) ); + add_action( 'enqueue_block_editor_assets', $n( 'blocks_editor_styles' ) ); add_filter( 'block_categories', $n( 'blocks_categories' ), 10, 2 ); + + /* + // Uncomment to register custom blocks via the Block Library plugin. + + add_filter( 'tenup_available_blocks', function ( $blocks ) { + $blocks['example-block'] = [ + 'dir' => TENUP_SCAFFOLD_BLOCK_DIR, + ]; + return $blocks; + } ); + */ + + /* + // Uncomment to register custom blocks via the theme. + + add_action( + 'init', + function() { + // Filter the plugins URL to allow us to have blocks in themes with linked assets. i.e editorScripts + //add_filter( 'plugins_url', __NAMESPACE__ . '\filter_plugins_url', 10, 2 ); + + + // Require custom blocks. + require_once TENUP_SCAFFOLD_BLOCK_DIR . '/example-block/register.php'; + + // Call block register functions for each block. + Example\register(); + + // Remove the filter after we register the blocks + //remove_filter( 'plugins_url', __NAMESPACE__ . '\filter_plugins_url', 10, 2 ); + } + ); + */ + +} + +/** + * Filter the plugins_url to allow us to use assets from theme. + * + * @param string $url The plugins url + * @param string $path The path to the asset. + * + * @return string The overridden url to the block asset. + */ +function filter_plugins_url( $url, $path ) { + $file = preg_replace( '/\.\.\//', '', $path ); + return trailingslashit( get_stylesheet_directory_uri() ) . $file; } /** @@ -45,16 +94,7 @@ function blocks_scripts() { * * @return void */ -function blocks_editor_scripts() { - - wp_enqueue_script( - 'blocks-editor', - TENUP_SCAFFOLD_TEMPLATE_URL . '/dist/js/blocks-editor.js', - [ 'wp-i18n', 'wp-element', 'wp-blocks', 'wp-components' ], - TENUP_SCAFFOLD_VERSION, - false - ); - +function blocks_editor_styles() { wp_enqueue_style( 'editor-style', TENUP_SCAFFOLD_TEMPLATE_URL . '/dist/css/editor-style.css', diff --git a/includes/blocks/blocks-editor.js b/includes/blocks/blocks-editor.js deleted file mode 100644 index 0c0e738..0000000 --- a/includes/blocks/blocks-editor.js +++ /dev/null @@ -1,6 +0,0 @@ -/** - * Gutenberg block-specific JavaScript: - * used in editor only - */ - -// import './example-block-1'; diff --git a/includes/blocks/example-block-1/index.js b/includes/blocks/example-block-1/index.js deleted file mode 100644 index 05fb462..0000000 --- a/includes/blocks/example-block-1/index.js +++ /dev/null @@ -1,75 +0,0 @@ -/** - * Example-block-1 - * Custom title block -- feel free to delete - */ - -const { __ } = wp.i18n; -const { registerBlockType } = wp.blocks; -const { TextControl } = wp.components; - -/** - * Register block - */ -export default registerBlockType( - 'tenup/example-block', - { - title: __( 'My first block', 'tenup-scaffold' ), - description: __( 'My first block description', 'tenup-scaffold' ), - icon: 'smiley', - category: 'tenup-scaffold-blocks', - keywords: [ - __( 'example', 'tenup-scaffold' ), - ], - attributes: { - customTitle: { - type: 'string' - }, - }, - /** - * See https://wordpress.org/gutenberg/handbook/designers-developers/developers/block-api/block-edit-save/#edit - */ - edit: props => { - const { - attributes: { - customTitle - }, - className, - setAttributes, - isSelected - } = props; - - if ( isSelected ) { - return ( -
- setAttributes( { customTitle } ) } - /> -
- ); - } else { - return ( -

- { customTitle } -

- ); - } - }, - /** - * See https://wordpress.org/gutenberg/handbook/designers-developers/developers/block-api/block-edit-save/#save - */ - save: props => { - const { - customTitle - } = props.attributes; - - return ( -

- { customTitle } -

- ); - }, - }, -); diff --git a/includes/blocks/example-block/block.json b/includes/blocks/example-block/block.json new file mode 100644 index 0000000..032ace4 --- /dev/null +++ b/includes/blocks/example-block/block.json @@ -0,0 +1,19 @@ +{ + "title": "Example Block", + "description": "An Example Block", + "text-domain": "tenup-scaffold", + "name": "tenup/example", + "icon": "feedback", + "attributes":{ + "customTitle": { + "type" : "string" + } + }, + "example": { + "attributes":{ + "customTitle": "Example Block" + } + }, + "editorScript": "file:../../../dist/blocks/example-block/editor.js", + "editorStyle": "file:../../../dist/blocks/example-block/editor.css" +} diff --git a/includes/blocks/example-block/edit.js b/includes/blocks/example-block/edit.js new file mode 100644 index 0000000..bdcac13 --- /dev/null +++ b/includes/blocks/example-block/edit.js @@ -0,0 +1,38 @@ +/** + * WordPress dependencies + */ +import { __ } from '@wordpress/i18n'; +import { RichText } from '@wordpress/block-editor'; + +/** + * Internal dependencies + */ +import { editPropsShape } from './props-shape'; + +/** + * Edit component. + * See https://wordpress.org/gutenberg/handbook/designers-developers/developers/block-api/block-edit-save/#edit + * + * @param {Object} props The block props + * @return {Function} Render the edit screen + */ +const ExampleBockEdit = ({ + attributes: { customTitle: currentTitle }, + className, + setAttributes, +}) => { + return ( +
+ setAttributes({ customTitle })} + /> +
+ ); +}; +// Set the propTypes +ExampleBockEdit.propTypes = editPropsShape; +export default ExampleBockEdit; diff --git a/includes/blocks/example-block/index.css b/includes/blocks/example-block/index.css new file mode 100644 index 0000000..2a77f35 --- /dev/null +++ b/includes/blocks/example-block/index.css @@ -0,0 +1,9 @@ +/** + * Example Block + * This file is for CSS admin overrides only. + * Use theme files for standard CSS work. + */ +.wp-block-example-block__title { + border: 3px dashed #ccc; + padding: 1em; +} diff --git a/includes/blocks/example-block/index.js b/includes/blocks/example-block/index.js new file mode 100644 index 0000000..0a91c7d --- /dev/null +++ b/includes/blocks/example-block/index.js @@ -0,0 +1,30 @@ +/** + * Example-block + * Custom title block -- feel free to delete + */ + +/** + * WordPress dependencies + */ +import { __ } from '@wordpress/i18n'; +import { registerBlockType } from '@wordpress/blocks'; + +/** + * Internal dependencies + */ +import edit from './edit'; +import save from './save'; +import { name } from './block.json'; + +/* Uncomment for CSS overrides in the admin */ +// import './index.css'; + +/** + * Register block + */ +registerBlockType(name, { + title: __('Example Block'), + description: __('An Example Block'), + edit, + save, +}); diff --git a/includes/blocks/example-block/markup.php b/includes/blocks/example-block/markup.php new file mode 100644 index 0000000..f308c84 --- /dev/null +++ b/includes/blocks/example-block/markup.php @@ -0,0 +1,32 @@ + [ + 'customTitle' => __( 'Custom title default', 'tenup' ), + ], + 'class_name' => 'wp-block-tenup-example', + ] +); + +?> +
+

+ +

+
diff --git a/includes/blocks/example-block/props-shape.js b/includes/blocks/example-block/props-shape.js new file mode 100644 index 0000000..4411149 --- /dev/null +++ b/includes/blocks/example-block/props-shape.js @@ -0,0 +1,15 @@ +import PropTypes from 'prop-types'; + +export const propsShape = { + attributes: PropTypes.shape({ + customTitle: PropTypes.string, + }).isRequired, + className: PropTypes.string, +}; + +export const editPropsShape = { + ...propsShape, + clientId: PropTypes.string, + isSelected: PropTypes.bool, + setAttributes: PropTypes.func.isRequired, +}; diff --git a/includes/blocks/example-block/register.php b/includes/blocks/example-block/register.php new file mode 100644 index 0000000..8419c33 --- /dev/null +++ b/includes/blocks/example-block/register.php @@ -0,0 +1,49 @@ + $n( 'render_block_callback' ), + ] + ); +} + +/** + * Render callback method for the block + * + * @param array $attributes The blocks attributes + * @param string $content Data returned from InnerBlocks.Content + * @param array $block Block information such as context. + * + * @return string The rendered block markup. + */ +function render_block_callback( $attributes, $content, $block ) { + ob_start(); + get_template_part( + 'includes/blocks/example-block/markup', + null, + [ + 'class_name' => 'wp-block-tenup-example', + 'attributes' => $attributes, + 'content' => $content, + 'block' => $block, + ] + ); + + return ob_get_clean(); +} diff --git a/includes/blocks/example-block/save.js b/includes/blocks/example-block/save.js new file mode 100644 index 0000000..739dc6d --- /dev/null +++ b/includes/blocks/example-block/save.js @@ -0,0 +1,8 @@ +/** + * See https://wordpress.org/gutenberg/handbook/designers-developers/developers/block-api/block-edit-save/#save + * + * @return {null} Dynamic blocks do not save the HTML. + */ +const ExampleBlockSave = () => null; + +export default ExampleBlockSave; diff --git a/package-lock.json b/package-lock.json index 8c352af..c8ca9b2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2673,6 +2673,17 @@ "integrity": "sha512-vRgzGoxhcNVChBP30XZlyK4w6r/9ZpO+Fi1dzmButp31lUEb1pT5WBxTIQl3HE0JZ9YTEJ00WWGO5sjGi5MHZA==", "dev": true }, + "@wordpress/dependency-extraction-webpack-plugin": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@wordpress/dependency-extraction-webpack-plugin/-/dependency-extraction-webpack-plugin-2.8.0.tgz", + "integrity": "sha512-fEOsSl1kYY8gkiAe7OM9IopmSOtaAug37OQwKVeda5fK6xLsnpqprP5iwHHOApNWMEzgmVGS6/iW5IZoi7qv/A==", + "dev": true, + "requires": { + "json2php": "^0.0.4", + "webpack": "^4.8.3", + "webpack-sources": "^1.3.0" + } + }, "@wordpress/eslint-plugin": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/@wordpress/eslint-plugin/-/eslint-plugin-5.0.1.tgz", @@ -10469,8 +10480,7 @@ "js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" }, "js-yaml": { "version": "3.13.1", @@ -10536,6 +10546,12 @@ "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", "dev": true }, + "json2php": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/json2php/-/json2php-0.0.4.tgz", + "integrity": "sha1-a9haHdpqXdfpECK7JEA8wbfC7jQ=", + "dev": true + }, "json5": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.1.tgz", @@ -11501,7 +11517,6 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", - "dev": true, "requires": { "js-tokens": "^3.0.0 || ^4.0.0" } @@ -12437,8 +12452,7 @@ "object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", - "dev": true + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" }, "object-component": { "version": "0.0.3", @@ -14926,7 +14940,6 @@ "version": "15.7.2", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz", "integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==", - "dev": true, "requires": { "loose-envify": "^1.4.0", "object-assign": "^4.1.1", @@ -15160,10 +15173,9 @@ } }, "react-is": { - "version": "16.12.0", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.12.0.tgz", - "integrity": "sha512-rPCkf/mWBtKc97aLL9/txD8DZdemK0vkA3JMLShjlJB3Pj3s+lpf1KaBzMfQrAmhMQB0n1cU/SUGgKKBCe837Q==", - "dev": true + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" }, "read-cache": { "version": "1.0.0", diff --git a/package.json b/package.json index bcb26bc..d30a2c9 100755 --- a/package.json +++ b/package.json @@ -50,6 +50,7 @@ "@10up/eslint-config": "^2.0.0", "@10up/stylelint-config": "^1.0.9", "@babel/core": "^7.9.0", + "@wordpress/dependency-extraction-webpack-plugin": "^2.8.0", "@wordpress/eslint-plugin": "^5.0.1", "babel-eslint": "^10.0.3", "babel-loader": "^8.0.6", @@ -111,6 +112,7 @@ "node": ">=12.0.0" }, "dependencies": { - "normalize.css": "^8.0.1" + "normalize.css": "^8.0.1", + "prop-types": "^15.7.2" } }