Layout Builder: Beyond The Basics - 2019.drupalcorn

Transcription

Layout Builder:Beyond the BasicsDrupalCorn 2019 November 7, 2019Presented by Christopher BurgeDigital Experience Group, University of Nebraska-Lincoln

Today’s Presentation Create your own layoutAdd settings to a layoutMake settings available to block/field templatesCreate a Layout Builder Restrictions pluginAlter Layout Builder block formsGotchas and current issues

Create a your own layoutLayout API provides 2 way to register layouts: *.layouts.yml PHP class with annotationsSee o-register-layoutsfor complete documentation.

Create a your own layoutCreate a Layout Plugin (annotation method)custom m layout.info.ymlcustom layouts.libraries.yml

Create a your own layoutTwoColumnLayout.php ?phpnamespace Drupal\custom layouts\Plugin\Layout;use Drupal\Core\Layout\LayoutDefault;/*** A custom, two-column layout.** @Layout(*id "custom two column",*label @Translation("Custom: Two-column"),*category @Translation("Custom Layouts"),*template "templates/layout--two-column",*library "custom layouts/two-column",*regions {*"first" {*"label" @Translation("First"),*},*"second" {*"label" @Translation("Second"),*}*},*icon map {*{*"first",*"second"*}*}* )*/class TwoColumnLayout extends LayoutDefault {

Create a your own layoutTwoColumnLayout.php*}* )*/class TwoColumnLayout extends LayoutDefault {// Override any methods you'd like to customize here!}

Create a your own layoutlayout--two-column.html.twig{%set classes ['layout','layout--custom-two-column',]%}{% if content %} div{{ attributes.addClass(classes) }} {% if content.first %} div {{ region attributes.first.addClass('layout region', 'layout region--first') }} {{ content.first }} /div {% endif %}{% if content.second %} div {{ region attributes.second.addClass('layout region', 'layout region--second')}} {{ content.second }} /div {% endif %} /div {% endif %}

Create a your own layouttwo-column.css.layout--custom-two-column {display: grid;grid-template-columns: 50% 50%;grid-gap: 1em;}.layout--custom-two-column .layout region--first,.layout--custom-two-column .layout region--second {padding: 1em;}

Create a your own layoutcustom layouts.libraries.ymltwo-column:version: VERSIONcss:theme:css/two-column.css: {}

Create a your own layout

Create a your own layout

Create a your own layout

Add settings to a layoutLayoutDefault Allows for Layout Settings: dateConfigurationForm()submitConfigurationForm()

Add settings to a layoutAdd the following settings: Column widths (25-75, 50-50, 75-25) Layout custom classes

Add settings to a layoutTwoColumnLayout.php ?phpnamespace Drupal\custom layouts\Plugin\Layout;use Drupal\Core\Form\FormStateInterface;use Drupal\Core\Layout\LayoutDefault;use Drupal\Core\Plugin\PluginFormInterface;/*** A custom, two-column layout.-------------------------------*}* )*/class TwoColumnLayout extends LayoutDefault implements PluginFormInterface {// Override any methods you'd like to customize here!}

Add settings to a layoutbuildConfigurationForm()/*** {@inheritdoc}*/public function buildConfigurationForm(array form, FormStateInterface form state) { configuration this- getConfiguration(); form['column widths'] ['#type' 'select','#title' this- t('Column widths'),'#default value' configuration['column widths'],'#options' ['25-75' this- t('25%/75%'),'50-50' this- t('50%/50%'),'75-25' this- t('75%/25%'),],]; form['extra classes'] ['#type' 'textfield','#title' this- t('Custom classes'),'#default value' configuration['custom classes'],'#description' this- t('Separate classes with a space'),];return form;}

Add settings to a layoutdefaultConfiguration()/*** {@inheritdoc}*/public function defaultConfiguration() {return parent::defaultConfiguration() ['column widths' '50-50','custom classes' '',];}

Add settings to a layoutvalidateConfigurationForm()/*** {@inheritdoc}*/public function validateConfigurationForm(array & form, FormStateInterface form state) { custom classes explode(' ', form state- getValue('custom classes'));foreach ( custom classes as class) { class cleaned Html::cleanCssIdentifier( class);if ( class ! class cleaned) { form state- setError( form['custom classes'], this- t("'@class' is an invalid class value.", ['@class' class]));break;}}}Use patch #20 from 77;otherwise, form errors will not display.

Add settings to a layoutsubmitConfigurationForm()/*** {@inheritdoc}*/public function submitConfigurationForm(array & form, FormStateInterface form state) { this- configuration['column widths'] form state- getValue('column widths'); this- configuration['custom classes'] form state- getValue('custom classes');}

Add settings to a layoutlayout--two-column.html.twig{%set classes olumns-' settings.column widths,settings.custom classes,]%}{% if content %} div{{ attributes.addClass(classes) }} {% if content.first %} div {{ region attributes.first.addClass('layout region', 'layout region--first') }} {{ content.first }} /div {% endif %}{% if content.second %} div {{ region attributes.second.addClass('layout region', 'layout region--second') }} {{ content.second }} /div {% endif %} /div {% endif %}

Add settings to a layouttwo-column.css.layout--custom-two-column {display: grid;grid-template-columns: 50% 50%;grid-gap: -75 {grid-template-columns: 25% -50 {grid-template-columns: 50% -25 {grid-template-columns: 75% 25%;}.layout--custom-two-column .layout region--first,.layout--custom-two-column .layout region--second {padding: 1em;}

Add settings to a layout

Add settings to a layout

Add settings to a layout

Add settings to a layout

Make settings available to block/field templatesMake settings available toblock/field templates

Make settings available to block/field templatescustom layouts.module/*** Implements template preprocess layout().*/function custom layouts preprocess layout(& variables) { custom classes (!empty( variables['settings']['custom classes']))? explode(' ', variables['settings']['custom classes']) : '';// Make custom classes array available to each block in the layout.if (isset( custom classes) && !empty( custom classes)) {// Loop through each region.foreach ( variables['content'] as region id region) {if (substr( region id, 0, 1) ! '#') {// Loop through each block.foreach ( region as block id block) {if (substr( block id, 0, 1) ! '#') {if (isset( variables['content'][ region id][ block id]['content']['#block content'])) { variables['content'][ region id][ block id]['content']['#block content']- set('#custom classes', custom classes);}}}}}}}

Make settings available to block/field templatescustom layouts.module/*** Implements template preprocess block().*/function custom layouts preprocess block(& variables) {if (isset( variables['content']['#block content'])) { custom classes variables['content']['#block content']- get('#custom classes');if ( custom classes) { variables['data']['custom classes'] custom classes;}}}

Make settings available to block/field templatescustom layouts.module/*** Implements template preprocess field().*/function custom layouts preprocess field(& variables) { custom classes variables['element']['#object']- get('#custom classes');if ( custom classes) {foreach ( variables['items'] as key value) { variables['items'][ key]['data']['custom classes'] custom classes;}}}

Make settings available to block/field templatesblock.html.twig{%set classes ['block','block-' configuration.provider clean class,'block-' plugin id clean class,]%}{% if 'test-class' in data.custom classes %}{% set classes classes merge(['inverse']) %}{% endif %} div{{ attributes.addClass(classes) }} {{ title prefix }}{% if label %} h2{{ title attributes }} {{ label }} /h2 {% endif %}{{ title suffix }}{% block content %} div{{ content attributes.addClass('content') }} {{ content }} /div {% endblock %} /div For block type templates, use the Block Type Templates module:https://www.drupal.org/project/block type templates

Make settings available to block/field templatesfield.html.twig{%set classes ['field','field--name-' field name clean class,'field--type-' field type clean class,'field--label-' label display,]%}{% if 'test-class' in items.0.data.custom classes %}{% set classes classes merge(['inverse']) %}{% endif %}{%set title classes ['field label',label display 'visually hidden' ? 'visually-hidden',]%}{% if label hidden %}{% if multiple %} div{{ attributes.addClass(classes, 'field items') }}

Make settings available to block/field templateslayout--two-column.html.twig{%set classes umns-' settings.column widths,settings.custom classes,]%}

Make settings available to block/field templatesblock.html.twig{% if 'test-class' in data.custom classes %}{% set classes classes merge(['inverse']) %}{% endif %}

Make settings available to block/field templatesfield.html.twig{% if 'test-class' in items.0.data.custom classes %}{% set classes classes merge(['inverse']) %}{% endif %}

Create a Layout Builder Restrictions pluginLayout Builder Restrictions Provides a UI for restricting layouts/blocks Built with plugins (so you can writeyour own)

Create a Layout Builder Restrictions pluginWithout Layout Builder Restrictions

Create a Layout Builder Restrictions pluginLayout Builder Restrictions Any view mode with Layout Builderenabled can be restricted

Create a Layout Builder Restrictions plugin

Create a Layout Builder Restrictions plugin

Create a Layout Builder Restrictions pluginWhat does it take to be a plugin? At bare minimum, a plugin class Probably – configuration and aconfiguration UIIf you don’t need a UI, consider using hook plugin filter TYPE CONSUMER alter()directly instead creating a Layout Builder Restrictions plugin

Create a Layout Builder Restrictions pluginStep 1: Create a custom module with aplugin classcustom lb Restriction.phpcustom lb restriction.info.yml

Create a Layout Builder Restrictions pluginCustomRestriction.php ?phpnamespace Drupal\custom lb useuseDrupal\layout builder\SectionStorageInterface;Drupal\layout builder rupal\layout builder onent\DependencyInjection\ContainerInterface;/*** CustomRestriction Plugin.** @LayoutBuilderRestriction(*id "custom restriction",*title @Translation("Custom Layout Builder restriction")* )**/class CustomRestriction extends LayoutBuilderRestrictionBase {use PluginHelperTrait;

Create a Layout Builder Restrictions pluginCustomRestriction.php/*** Constructs a Drupal\Component\Plugin\PluginBase object.** @param array configuration*A configuration array containing information about the plugin instance.* @param string plugin id*The plugin id for the plugin instance.* @param mixed plugin definition*The plugin implementation definition.*/public function construct(array configuration, plugin id, plugin definition) { this- configuration configuration; this- pluginId plugin id; this- pluginDefinition plugin definition;}/*** {@inheritdoc}*/public static function create(ContainerInterface container, array configuration) {return new static( configuration, plugin id, plugin definition);}

Create a Layout Builder Restrictions pluginCustomRestriction.php/*** {@inheritdoc}*/public function alterBlockDefinitions(array definitions, array context) {return definitions;}/*** {@inheritdoc}*/public function alterSectionDefinitions(array definitions, array context) {return definitions;}/*** {@inheritdoc}*/public function blockAllowedinContext(SectionStorageInterface section storage, delta from, delta to, region to, block uuid, preceding block uuid NULL) {return TRUE;}

Create a Layout Builder Restrictions pluginStep 2: Add config and UIcustom lb restrictionconfigschemacustom lb outBuilderRestrictionCustomRestriction.phpcustom lb restriction.info.ymlcustom lb restriction.links.menu.ymlcustom lb restriction.routing.yml

Create a your own layoutSettings.php ?phpnamespace Drupal\custom lb restriction\Form;use Drupal\Core\Form\ConfigFormBase;use Drupal\Core\Form\FormStateInterface;/*** Configure example settings for this site.*/class Settings extends ConfigFormBase {/*** Config settings.** @var string*/const SETTINGS 'custom lb restriction.settings';/*** {@inheritdoc}*/public function getFormId() {return 'custom lb restriction settings';}/*** {@inheritdoc}*/protected function getEditableConfigNames() {return [static::SETTINGS,];}

Create a Layout Builder Restrictions pluginSettings.php/*** {@inheritdoc}*/public function buildForm(array form, FormStateInterface form state) { config this- config(static::SETTINGS); form['disallow views blocks'] ['#type' 'checkbox','#title' this- t('Disallow Views blocks on a global basis'),'#default value' config- get('disallow views blocks'),'#description' this- t('If a block view is needed, a custom block plugin should be used.’),]; form['layouts restrict to custom'] ['#type' 'checkbox','#title' this- t('Only allow Custom layouts'),'#default value' config- get('layouts restrict to custom'),'#description' this- t('Only Custom layouts will be allowed on a global basis.’),];return parent::buildForm( form, form state);}

Create a Layout Builder Restrictions pluginSettings.php/*** {@inheritdoc}*/public function submitForm(array & form, FormStateInterface form state) {// Retrieve the configuration. config this- configFactory- getEditable(static::SETTINGS);// Set configuration value. config- set('disallow views blocks',(bool) form state- getValue('disallow views blocks')); config- set('layouts restrict to custom',(bool) form state- getValue('layouts restrict to custom'));// Save configuration. config- save();parent::submitForm( form, form state);}

Create a Layout Builder Restrictions plugincustom lb restriction.routing.ymlcustom lb restriction.settings:path: ngs'defaults:form: '\Drupal\custom lb restriction\Form\Settings'title: 'Custom Layout Builder Restriction Settings'requirements:permission: 'configure layout builder restrictions'options:admin route: TRUE

Create a Layout Builder Restrictions plugincustom lb restriction.links.menu.ymlcustom lb restriction.settings:title: 'Custom Layout Builder Restriction'route name: custom lb restriction.settingsparent: system.admin config contentdescription: 'Configure the Custom Layout Builder Restriction module.'weight: 20

Create a Layout Builder Restrictions plugincustom lb restriction.schema.ymlcustom lb restriction.settings:type: config objectlabel: 'Custom Layout Builder Restriction settings'mapping:disallow views blocks:type: booleanlabel: 'Disallow Views blocks on a global basis'layouts restrict to custom:type: booleanlabel: 'Only allow Custom layouts'

Create a Layout Builder Restrictions pluginCustomRestriction.php/*** {@inheritdoc}*/public function alterBlockDefinitions(array definitions, array context) {// Retrieve config. config \Drupal::service('config.factory')- get('custom lb restriction.settings'); disallow views block config- get('disallow views blocks');if ( disallow views block) {foreach ( definitions as id definition) {if (substr( id, 0, 11) "views block") {unset( definitions[ id]);}}}return definitions;}

Create a Layout Builder Restrictions pluginCustomRestriction.php/*** {@inheritdoc}*/public function alterSectionDefinitions(array definitions, array context) {// Retrieve config. config \Drupal::service('config.factory')- get('custom lb restriction.settings'); layouts restrict to custom config- get('layouts restrict to custom');if ( layouts restrict to custom) {foreach ( definitions as id definition) {if (substr( id, 0, 7) ! "custom ") {unset( definitions[ id]);}}}return definitions;}

Create a Layout Builder Restrictions pluginCustomRestriction.php/*** {@inheritdoc}*/public function blockAllowedinContext(SectionStorageInterface section storage, delta from, delta to, region to, block uuid, preceding block uuid NULL) {return TRUE;}

Create a Layout Builder Restrictions pluginEnable the tions

Alter Layout Builder Block FormsAlter Layout Builder Block Forms It’s just hook form alter(), right? Nope

Alter Layout Builder Block FormsAlter Layout Builder Block Forms Target two form IDs: layout builder add block layout builder update block Use a ‘process’ function

Alter Layout Builder Block FormsAlter Layout Builder Block Forms BUT – this only alter block forms insideLayout Builder Custom block forms are unaffected(block content)

Alter Layout Builder Block FormsAlter Layout Builder Block Forms Target ‘block content BUNDLE edit form’and ‘block content BUNDLE form’ forms

Alter Layout Builder Block FormsBlock Form Alter hook block plugin form alter() hook block type form alter()https://www.drupal.org/project/block form alter

Alter Layout Builder Block Formshook block plugin form alter()/*** Alter block forms per block plugin.** Block forms for the 'block content' and 'inline content' plugins must use* hook block type form alter().** @param array form*Nested array of form elements that comprise the form.* @param \Drupal\Core\Form\FormStateInterface form state*The form state.* @param string plugin*The machine name of the plugin implementing the block.*/function hook block plugin form alter(array & form, FormStateInterface & form state, string plugin){if ( plugin 'webform block') { form['settings']['redirect']['#default value'] TRUE; form['settings']['redirect']['#disabled'] TRUE;}}

Alter Layout Builder Block Formshook block type form alter()/*** Alter custom block forms rendered by Block Content and Layout Builder.** E.g. Alter block forms for 'block content' and 'inline block' plugins.** @param array form*Nested array of form elements that comprise the form.* @param \Drupal\Core\Form\FormStateInterface form state*The form state.* @param string block type*The machine name of the custom block bundle.*/function hook block type form alter(array & form, FormStateInterface & form state, string block type){if ( block type 'accordion') { form['example field']['widget'][0]['value']['#default value'] 'A better default value';}}

GotchasForm element name attribute differsCustom Block:Inline Block (Layout Builder):

GotchasForm element name attribute differs/*** Implements hook block type form alter().*/function my block type form alter(array & form, FormStateInterface & form state, string block id) {if ( block id 'basic') { form['field my field']['#states'] ['required' [':input[name "field require my field[value]"]' ['checked' TRUE],':input[name "settings[block form][field require my field][value]"]' ['checked' TRUE],],];}}

Relevant Issues on Drupal.orgAdd visibility control conditions to blocks within Layout s/2916876Reorder Layout Builder es/3080606[PP-1] Reverting entity revisions that contain custom blocks erroneouslytriggers ect/drupal/issues/3053881

Relevant Issues on Drupal.orgMultiple select element is inappropriately targeted with CSS in settings 080698It's very difficult to alter forms of inline (content blocks) placed viaLayout s/3028391 form['#attributes']['data-drupal-selector'] is set to a random value so can'tbe used for its intended s/2897377

That’s the End!Questions?

Today's Presentation Create your own layout Add settings to a layout Make settings available to block/field templates Create a Layout Builder Restrictions plugin Alter Layout Builder block forms Gotchas and current issues