DIY EZ Web Components

This is a simple WordPress plugin template for you to easily add your web components to WordPress.

Github Repository

<?php
//index.php
/*
Plugin Name: DIY EZ Web Components
Plugin URI:  https://undead.institute/diy-ez-web-components/
Description: Easily Add Web Components to Your WordPress Site
Version:     1.0.0
Author:      John Rhea
Author URI:  https://johnrhea.com
License:     GPL2
License URI: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
*/
/** How to use this template **/
/*
1. Set the $webcompname variable to your web component's name e.g. zombie-profile for <zombie-profile></zombie-profile>
2. Set the path to your JavaScript file.
3. Set the path to your CSS file. If not using one, set to false.
4. Paste your HTML Template content into diy_ezwebcomp_footer().
5. Edit diy-ezwebcomp-block.js with the fields you need 
*/
$webcompname = 'zombie-profile';
$path2js = get_template_directory_uri() . '/js/ezwebcomp.js';
$path2style = get_template_directory_uri() . '/ezwebcomp.css'; //Set $path2style to false if not using this
function diy_ezwebcomp_footer()
{
?>
  <!-- Replace me with your HTML template code -->
<?php
}
add_action('wp_footer', 'diy_ezwebcomp_footer');
wp_enqueue_script($webcompname . '_js', $path2js, '', '1.0', true);
if ($path2style) {
  wp_enqueue_style($webcompname . '_style', $path2style, '', '1.0', 'screen');
}
function add_diy_ezwebcomp_to_kses_allowed($the_allowed_tags)
{
  global $webcompname;
  $the_allowed_tags[$webcompname] = array();
  foreach ($the_allowed_tags as &$tag) {
    $tag['slot'] = true;
    $tag['part'] = true;
  }
  return $the_allowed_tags;
}
add_filter('wp_kses_allowed_html', 'add_diy_ezwebcomp_to_kses_allowed');

//Custom block PHP
function diy_ezwebcomp_register_block()
{
  global $webcompname;
  //Enqueues the JavaScript needed to build the custom block
  wp_register_script(
    'diy-ezwebcomp',
    plugins_url('diy-ezwebcomp-block.js', __FILE__),
    array('wp-blocks', 'wp-element', 'wp-editor'),
    filemtime(plugin_dir_path(__FILE__) . 'diy-ezwebcomp-block.js')
  );
  //Enqueues the CSS file for anything you need in the editor
  wp_register_style(
    'diy-ezwebcomp',
    plugins_url('diy-ezwebcomp-style.css', __FILE__),
    array(),
    filemtime(plugin_dir_path(__FILE__) . 'diy-ezwebcomp-style.css')
  );
  //Registers the actual block
  register_block_type('diy-ezwebcomp/' . $webcompname, array(
    'editor_style' => 'diy-ezwebcomp',
    'editor_script' => 'diy-ezwebcomp',
  ));
}
add_action('init', 'diy_ezwebcomp_register_block');
//diy-ezwebcomp-block.js
//set the variable below to the same as $webcompname from index.php
var webcompname = 'zombie-profile',
	userFriendlyTitle = 'Zombie Profile';
(function (blocks, editor, element, components) {
	var el = element.createElement;
	var RichText = editor.RichText;
	var MediaUpload = editor.MediaUpload;
	blocks.registerBlockType('diy-ezwebcomp/' + webcompname, {
		title: userFriendlyTitle,
		icon: 'id-alt',
		category: 'layout',
		//The attributes are all the different fields to use.
		attributes: {
			textinput: {
				type: 'string', //the type of content
				source: 'text', //where to pull the info from e.g. the text of an element, from an attribute, from child elements etc.
				selector: '.textinput', //selectors are how Gutenberg selects and grabs the content. These should be unique within an instance of a block. If you only have one img or one ul, you can use element selectors.
			},
			mediaID: {
				type: 'number',
			},
			mediaURL: {
				type: 'string',
				source: 'attribute',
				selector: 'img',
				attribute: 'src',
			},
			list: {
				type: 'array',
				source: 'children',
				selector: 'ul',
			},
		},
		//the edit function deals with how the information will be displayed in the editor.
		edit: function (props) {
			var attributes = props.attributes;
			var onSelectImage = function (media) {
				return props.setAttributes({
					mediaURL: media.url,
					mediaID: media.id,
				});
			};
			return el(
				'div', {
					className: props.className
				},
				el('h3', {}, 'Text Input'),
				el(RichText, {
					tagName: 'p',
					className: 'textinput',
					placeholder: 'Some Text Input…',
					value: attributes.textinput,
					onChange: function (value) {
						props.setAttributes({
							textinput: value
						});
					},
				}),
				el( //an image
					'div', {
						className: 'pic'
					},
					el(MediaUpload, {
						onSelect: onSelectImage,
						allowedTypes: 'image',
						value: attributes.mediaID,
						render: function (obj) {
							return el(
								components.Button, {
									className: attributes.mediaID ?
										'image-button' : 'button button-large',
									onClick: obj.open,
								},
								!attributes.mediaID ?
								'Upload Image' :
								el('img', {
									src: attributes.mediaURL
								})
							);
						},
					})
				),
				el('h3', {}, 'List'), //heading
				el(RichText, { //where you add list items one at a time
					tagName: 'ul',
					multiline: 'li', //creates a new li when you hit enter
					placeholder: 'Write a list…',
					value: attributes.list,
					onChange: function (value) {
						props.setAttributes({
							list: value
						});
					},
					className: 'list',
				}),
			);
		},
		save: function (props) {
			var attributes = props.attributes;
			return el(
				webcompname, //the component name
				{}, //the web component does not need any HTML attributes so this can remain empty
				attributes.mediaURL && //ensure a URL exists before you print it
				el('img', { //print the image
					src: attributes.mediaURL,
					slot: 'profile-image'
				}),
				attributes.textinput &&
				el(RichText.Content, { //print the name
					tagName: 'span',
					slot: 'text-input',
					className: 'textinput',
					value: attributes.textinput,
				}),
				attributes.list[0] && //Since list's type is array we need to verify there is something in the first element.
				el(RichText.Content, {
					tagName: 'ul',
					slot: 'list',
					value: attributes.list,
				}), );
		},
	});
})(
	//import the dependencies
	window.wp.blocks,
	window.wp.blockEditor,
	window.wp.element,
	window.wp.components
);
/* diy-ez-webcomp-style.css */
#editor .pic img {
	max-width: 300px;
	height: 300px;
}
#editor .pic button.components-button {
	overflow: visible;
	height: auto;
}