Create endless components for Visual Editor

You can extend Visual Editor to create more components as per your requirement. To create new component you need to create a VisualEditorComponent plugin in your custom module.

To add new component, create a php class file(e.g. HelloWorldComponent.php) in /<path-to-your-module>/src/Plugin/VisualEditorComponent/

File: /modules/custom/example/src/Plugin/VisualEditorComponent/HelloWorldComponent.php

<?php

/**
 * @file
 * Contains \Drupal\restaurant\Plugin\VisualEditorComponent\HelloWorldComponent.
 */

namespace Drupal\restaurant\Plugin\VisualEditorComponent;

use Drupal\visual_editor\VisualEditorBase;
use Drupal\Core\Form\FormStateInterface;

/**
 * Provides a 'hello_world' visual editor compoent.
 *
 * @VisualEditorComponent(
 *   id = "hello_world",
 *   type = "widget",
 *   name = @Translation("Hello world"),
 *   description = @Translation("Description about component."),
 *   iconClass = "fa fa-square",
 * )
 */
class HelloWorldComponent extends VisualEditorBase {

  public function buildForm(array &$form, FormStateInterface $form_state, array $settings) {
    $content = $settings['content'];
    $form['content']['heading'] = [
      '#type' => 'textfield',
      '#required' => true,
      '#title' => t('Heading'),
      '#default_value' => isset($content['heading']) ? $content['heading'] : '',
    ];
    $form['content']['heading_type'] = [
      '#type' => 'select',
      '#required' => true,
      '#title' => t('Heading type'),
      '#options' => [
        'h1' => 'Heading 1',
        'h2' => 'Heading 2',
        'h3' => 'Heading 3',
        'h4' => 'Heading 4',
        'h5' => 'Heading 5',
        'h6' => 'Heading 6',
      ],
      '#default_value' => isset($content['heading_type']) ? $content['heading_type'] : '',
    ];
  }

  public function render($settings) {
    $build = parent::getBuild($settings);
    if(!empty($settings['content']['heading'])){
      $build['#type'] = 'html_tag';
      $build['#tag'] = $settings['content']['heading_type'];
      $build['#value'] = $settings['content']['heading'];
    }
    return $build;
  }

}

Define component

To create new component, you need to add annotation as shown below:

/**
 * Provides a '<replace_with_component_name>' visual editor compoent.
 *
 * @VisualEditorComponent(
 *   id = "<replace_with_component_name>",
 *   type = "<replace_with_component_type:row,column,widget>",
 *   name = @Translation("<replace_with_component_label>"),
 *   description = @Translation("<replace_with_component_description>"),
 *   iconClass = "<replace_with_css_class_name_for_icon>",
 * )
 */

There are three component types available as listed below:

  1. Row(row): Creates column wrappers
  2. Column(column): Creates column
  3. Widget(widget): Create components like heading, image, rich text etc.

Implement Form Inputs

Provide settings for component using buildForm() function.

public function buildForm(array &$form, FormStateInterface $form_state, array $settings) {
    $content = $settings['content'];
    $form['content']['heading'] = [
      '#type' => 'textfield',
      '#required' => true,
      '#title' => t('Heading'),
      '#default_value' => isset($content['heading']) ? $content['heading'] : '',
    ];
    $form['content']['heading_type'] = [
      '#type' => 'select',
      '#required' => true,
      '#title' => t('Heading type'),
      '#options' => [
        'h1' => 'Heading 1',
        'h2' => 'Heading 2',
        'h3' => 'Heading 3',
        'h4' => 'Heading 4',
        'h5' => 'Heading 5',
        'h6' => 'Heading 6',
      ],
      '#default_value' => isset($content['heading_type']) ? $content['heading_type'] : '',
    ];
  }

Here you can get component settings using component variable. Update the $form as you do for normal forms.

Render component

Build a renderable array using render() function. 

public function render($settings) {
    $build = parent::getBuild($settings);
    if(!empty($settings['content']['heading'])){
      $build['#type'] = 'html_tag';
      $build['#tag'] = $settings['content']['heading_type'];
      $build['#value'] = $settings['content']['heading'];
    }
    return $build;
  }