').click(moveDown))
+ .prependTo(item);
+ });
+ var drake = dragula($('.erl-layout-wrapper, .erl-layout-region, .erl-disabled-wrapper', item).get(), {
+ moves: function(el, container, handle) {
+ return handle.className.toString().indexOf('layout-handle') >= 0;
+ },
+
+ accepts: function(el, target, source, sibling) {
+ var $el = $(el);
+
+ // Regions always have to have a sibling,
+ // forcing layout controls to be last element in container.
+ if (!$el.is('.erl-layout') && !sibling) {
+ //console.log('no sibling');
+ return false;
+ }
+
+ // Layouts can never go inside another layout.
+ if ($el.is('.erl-layout')) {
+ if ($(target).parents('.erl-layout').length) {
+ //console.log('no nested layouts');
+ return false;
+ }
+ }
+
+ // Layouts can not be dropped into disabled (only individual items).
+ if ($el.is('.erl-layout')) {
+ if ($(target).is('.erl-disabled-wrapper')) {
+ //console.log('no disabled layouts');
+ return false;
+ }
+ }
+ // Require non-layout items to be dropped in a layout.
+ else {
+ if($(target).parents('.erl-layout').length == 0 && !$(target).is('.erl-disabled-wrapper')) {
+ //console.log('items must go in layouts');
+ return false;
+ }
+ }
+
+ return true;
+ }
+ });
+
+ drake.on('drop', function(el, target, source, sibling){
+ updateFields($(el).closest('.erl-field'));
+ updateDisabled($(el).closest('.erl-field'));
+ });
+ }
+
+ });
+
+ // Update hidden fields.
+ $('.erl-field', context).once('erl-update-fields').each(function(index, item){
+ updateFields($(item));
+ updateDisabled($(item));
+ });
+ }
+ };
+
+})(jQuery, Drupal, drupalSettings);
diff --git a/src/Plugin/Field/FieldType/EntityReferenceLayoutRevisioned.php b/src/Plugin/Field/FieldType/EntityReferenceLayoutRevisioned.php
index 928f419..933879b 100644
--- a/src/Plugin/Field/FieldType/EntityReferenceLayoutRevisioned.php
+++ b/src/Plugin/Field/FieldType/EntityReferenceLayoutRevisioned.php
@@ -149,6 +149,7 @@ class EntityReferenceLayoutRevisioned extends EntityReferenceRevisionsItem {
'handler_settings',
'target_bundles',
]);
+
if (!empty($input_target_bundles)) {
$target_bundles = $input_target_bundles;
}
@@ -166,11 +167,20 @@ class EntityReferenceLayoutRevisioned extends EntityReferenceRevisionsItem {
}
}
+ $default_layout_bundle = '';
+ if (isset($handler_settings['layout_bundles'])) {
+ $default_layout_bundle = reset($handler_settings['layout_bundles']);
+ }
+
+ if (empty($layout_bundle_options[$default_layout_bundle])) {
+ $default_layout_bundle = [];
+ }
+
$form['handler']['handler_settings']['layout_bundles'] = [
'#type' => 'radios',
'#options' => $layout_bundle_options,
'#title' => $this->t('Layout @target_type type', ['@target_type' => $target_type]),
- '#default_value' => isset($handler_settings['layout_bundles']) ? reset($handler_settings['layout_bundles']) : [],
+ '#default_value' => $default_layout_bundle,
'#multiple' => TRUE,
'#description' => $this->t('Which @target_type type should be used for layout.', ['@target_type' => $target_type]),
'#required' => TRUE,
diff --git a/src/Plugin/Field/FieldWidget/EntityReferenceLayoutWidget.php b/src/Plugin/Field/FieldWidget/EntityReferenceLayoutWidget.php
index b0dc8fb..3fda284 100644
--- a/src/Plugin/Field/FieldWidget/EntityReferenceLayoutWidget.php
+++ b/src/Plugin/Field/FieldWidget/EntityReferenceLayoutWidget.php
@@ -2,29 +2,27 @@
namespace Drupal\entity_reference_layout\Plugin\Field\FieldWidget;
-use Drupal\Core\Language\LanguageInterface;
-use Drupal\Core\Language\LanguageManager;
-use Drupal\file\Entity\File;
+use Drupal\Core\Entity\Entity\EntityFormDisplay;
+use Drupal\Core\Field\WidgetBase;
+use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\Core\Field\FieldItemListInterface;
-use Drupal\inline_entity_form\Plugin\Field\FieldWidget\InlineEntityFormComplex;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Render\Element;
use Drupal\Core\Layout\LayoutPluginManager;
use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\Core\Entity\EntityTypeBundleInfoInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
-use Drupal\Core\Entity\EntityDisplayRepositoryInterface;
-use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Render\Renderer;
-use Drupal\Core\Session\AccountProxy;
-use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Drupal\Core\Plugin\PluginFormInterface;
use Drupal\Core\Plugin\PluginWithFormsInterface;
use Drupal\Core\Layout\LayoutInterface;
use Drupal\Core\Form\SubformState;
use Drupal\Component\Utility\NestedArray;
-use Drupal\entity_reference_layout\Event\ErlPropertiesFormEvent;
+use Drupal\Component\Utility\Html;
+use Drupal\Core\Language\LanguageInterface;
+use Drupal\Core\Language\LanguageManager;
+use Drupal\Core\Field\FieldFilteredMarkup;
/**
* Entity Reference with Layout field widget.
@@ -36,17 +34,23 @@ use Drupal\entity_reference_layout\Event\ErlPropertiesFormEvent;
* field_types = {
* "entity_reference_layout_revisioned"
* },
- * multiple_values = true
* )
*/
-class EntityReferenceLayoutWidget extends InlineEntityFormComplex {
+class EntityReferenceLayoutWidget extends WidgetBase implements ContainerFactoryPluginInterface {
/**
- * The List of allowed Layouts.
+ * The Renderer service property.
*
- * @var array
+ * @var \Drupal\Core\Render\RendererInterface
*/
- protected $allowedLayouts;
+ protected $renderer;
+
+ /**
+ * The Entity Type Manager service property.
+ *
+ * @var \Drupal\Core\Entity\EntityTypeManagerInterface
+ */
+ protected $entityTypeManager;
/**
* The Layouts Manager.
@@ -56,35 +60,49 @@ class EntityReferenceLayoutWidget extends InlineEntityFormComplex {
protected $layoutPluginManager;
/**
- * The Event Dispatcher.
+ * Layouts available to this widget.
*
- * @var \Symfony\Component\EventDispatcher\EventDispatcherInterface
+ * @var array
*/
- protected $eventDispatcher;
+ protected $availableLayouts;
/**
- * The Renderer service property.
+ * The entity that contains this field.
*
- * @var \Drupal\Core\Render\RendererInterface
+ * @var \Drupal\Core\Entity\Entity
*/
- protected $renderer;
+ protected $host;
/**
- * The Current User.
+ * The name of the field.
*
- * @var \Drupal\Core\Session\AccountInterface
+ * @var string
*/
- protected $currentUser;
+ protected $fieldName;
/**
- * The language manager
+ * The Html Id of the wrapper element.
+ *
+ * @var string
+ */
+ protected $wrapperId;
+
+ /**
+ * The Html Id of the item form wrapper element.
+ *
+ * @var string
+ */
+ protected $itemFormWrapperId;
+
+ /**
+ * The language manager.
*
* @var \Drupal\Core\Language\LanguageManager
*/
protected $languageManager;
/**
- * Constructs an EntityReferenceLayoutWidget object.
+ * Constructs a WidgetBase object.
*
* @param string $plugin_id
* The plugin_id for the widget.
@@ -96,22 +114,16 @@ class EntityReferenceLayoutWidget extends InlineEntityFormComplex {
* The widget settings.
* @param array $third_party_settings
* Any third party settings.
+ * @param Drupal\Core\Render\Renderer $renderer
+ * Core renderer service.
+ * @param Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
+ * Core entity type manager service.
* @param \Drupal\Core\Entity\EntityTypeBundleInfoInterface $entity_type_bundle_info
* The entity type bundle info.
- * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
- * The entity type manager.
- * @param \Drupal\Core\Entity\EntityDisplayRepositoryInterface $entity_display_repository
- * The entity display repository.
- * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
- * Module handler service.
- * @param \Drupal\Core\Layout\LayoutPluginManager $layoutPluginManager
- * Layout plugin manager service.
- * @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $eventDispatcher
- * Event dispatcher service.
- * @param \Drupal\Core\Render\Renderer $renderer
- * Renderer service.
- * @param \Drupal\Core\Session\AccountProxy $current_user
- * Current logged in user.
+ * @param \Drupal\Core\Layout\LayoutPluginManager $layout_plugin_manager
+ * Core layout plugin manager service.
+ * @param \Drupal\Core\Language\LanguageManager $language_manager
+ * Core language manager service.
*/
public function __construct(
$plugin_id,
@@ -119,21 +131,20 @@ class EntityReferenceLayoutWidget extends InlineEntityFormComplex {
FieldDefinitionInterface $field_definition,
array $settings,
array $third_party_settings,
- EntityTypeBundleInfoInterface $entity_type_bundle_info,
- EntityTypeManagerInterface $entity_type_manager,
- EntityDisplayRepositoryInterface $entity_display_repository,
- ModuleHandlerInterface $module_handler,
- LayoutPluginManager $layoutPluginManager,
- EventDispatcherInterface $eventDispatcher,
Renderer $renderer,
- AccountProxy $current_user,
- LanguageManager $languageManager) {
- parent::__construct($plugin_id, $plugin_definition, $field_definition, $settings, $third_party_settings, $entity_type_bundle_info, $entity_type_manager, $entity_display_repository, $module_handler);
- $this->layoutPluginManager = $layoutPluginManager;
- $this->eventDispatcher = $eventDispatcher;
+ EntityTypeManagerInterface $entity_type_manager,
+ EntityTypeBundleInfoInterface $entity_type_bundle_info,
+ LayoutPluginManager $layout_plugin_manager,
+ LanguageManager $language_manager) {
+
+ parent::__construct($plugin_id, $plugin_definition, $field_definition, $settings, $third_party_settings);
+
$this->renderer = $renderer;
- $this->currentUser = $current_user;
- $this->languageManager = $languageManager;
+ $this->entityTypeManager = $entity_type_manager;
+ $this->entityTypeBundleInfo = $entity_type_bundle_info;
+ $this->layoutPluginManager = $layout_plugin_manager;
+ $this->fieldName = $this->fieldDefinition->getName();
+ $this->languageManager = $language_manager;
}
/**
@@ -146,722 +157,702 @@ class EntityReferenceLayoutWidget extends InlineEntityFormComplex {
$configuration['field_definition'],
$configuration['settings'],
$configuration['third_party_settings'],
- $container->get('entity_type.bundle.info'),
+ $container->get('renderer'),
$container->get('entity_type.manager'),
- $container->get('entity_display.repository'),
- $container->get('module_handler'),
+ $container->get('entity_type.bundle.info'),
$container->get('plugin.manager.core.layout'),
- $container->get('event_dispatcher'),
- $container->get('renderer'),
- $container->get('current_user'),
$container->get('language_manager')
);
}
/**
- * Prepare the form state.
- *
- * First calls parent method, then loads
- * layout info into $form_state from items.
+ * Builds the main widget form array container/wrapper.
*
- * @param \Drupal\Core\Form\FormStateInterface $form_state
- * The form state.
- * @param \Drupal\Core\Field\FieldItemListInterface $items
- * The field values.
- * @param bool $translating
- * Whether there's a translation in progress.
+ * Form elements for individual items are built by formElement().
*/
- protected function prepareFormState(FormStateInterface $form_state, FieldItemListInterface $items, $translating = FALSE) {
+ protected function formMultipleElements(FieldItemListInterface $items, array &$form, FormStateInterface $form_state) {
- parent::prepareFormState($form_state, $items, $translating);
- $entities = $form_state->get([
- 'inline_entity_form',
- $this->getIefId(),
- 'entities',
- ]);
+ $parents = $form['#parents'];
+ $widget_state = static::getWidgetState($parents, $this->fieldName, $form_state);
+
+ $this->wrapperId = Html::getId(implode('-', $parents) . $this->fieldName . '-wrapper');
+ $this->itemFormWrapperId = Html::getId(implode('-', $parents) . $this->fieldName . '-form');
- // Only duplicate the paragraph items when the field is defined
- // as translatable.
+ $handler_settings = $items->getSetting('handler_settings');
+ $layout_bundles = $handler_settings['layout_bundles'] ?? [];
+ $target_bundles = $handler_settings['target_bundles'] ?? [];
+ $title = $this->fieldDefinition->getLabel();
+ $description = FieldFilteredMarkup::create(\Drupal::token()->replace($this->fieldDefinition->getDescription()));
+
+ // Save items to widget state when the form first loads.
+ if (empty($widget_state['items'])) {
+ $widget_state['items'] = [];
+ foreach ($items as $delta => $item) {
+ $widget_state['items'][$delta] = [
+ 'entity' => $item->entity,
+ 'layout' => $item->layout,
+ 'config' => $item->config,
+ 'options' => $item->options,
+ 'new_region' => NULL,
+ 'parent_weight' => NULL,
+ ];
+ }
+ }
+ // Handle asymmetric translation if field is translatable
+ // by duplicating items for enabled languages.
if ($items->getFieldDefinition()->isTranslatable()) {
$langcode = $this->languageManager
->getCurrentLanguage(LanguageInterface::TYPE_CONTENT)
->getId();
- foreach ($entities as $delta => $entity) {
- if ($entity['entity']->get('langcode')->value == $langcode) {
+ foreach ($widget_state['items'] as $delta => $item) {
+ if (empty($item['entity']) || $item['entity']->get('langcode')->value == $langcode) {
continue;
}
-
- $duplicate = $entity['entity']->createDuplicate();
+ $duplicate = $item['entity']->createDuplicate();
$duplicate->set('langcode', $langcode);
- $entities[$delta]['entity'] = $duplicate;
- }
- }
-
- foreach ($items as $delta => $item) {
- if (isset($entities[$delta]) && !isset($entities[$delta]['layout_properties'])) {
- $layout_properties = [
- 'options' => [],
- 'options' => isset($item->options) ? $item->options : [],
- 'config' => !empty($item->config) ? $item->config : [],
- 'region' => isset($item->region) ? $item->region : '',
- 'layout' => isset($item->layout) ? $item->layout : '',
- 'entity_bundle' => $item->entity->bundle(),
- 'parent' => -1,
- ];
- $form_state->set([
- 'inline_entity_form',
- $this->getIefId(),
- 'entities',
- $delta,
- 'layout_properties',
- ], $layout_properties);
+ $widget_state['items'][$delta]['entity'] = $duplicate;
}
}
+ static::setWidgetState($parents, $this->fieldName, $form_state, $widget_state);
- }
+ $elements = parent::formMultipleElements($items, $form, $form_state);
+ $elements += [
+ '#title' => $title,
+ '#description' => $description,
+ ];
+ $elements['#theme'] = 'entity_reference_layout_widget';
+ $elements['#id'] = $this->wrapperId;
- /**
- * Stores new or edited entity in $form_state.
- *
- * Stored temporarily in $form_state, saved to db
- * when container entity is saved down the line.
- *
- * Except for the lines that save layout info with entity,
- * this is copied directly from the InlineEntityFormComplex class.
- */
- public static function submitSaveEntity($entity_form, FormStateInterface $form_state) {
-
- parent::submitSaveEntity($entity_form, $form_state);
-
- $ief_id = $entity_form['#ief_id'];
- $layout_name = $form_state->getValue(array_merge($entity_form['#parents'], ['layout_selection', 'layout']));
- $layout_properties = $form_state->getValue(array_merge($entity_form['#parents'], ['layout_properties']));
- $layout_options = $form_state->getValue(array_merge($entity_form['#parents'], ['layout_options']));
-
- // Pass layout config to plugin submit handler for processing.
- $layout_config = [];
- if ($layout_name && !empty($entity_form['layout_config']['config'])) {
- $layout_instance = \Drupal::service('plugin.manager.core.layout')->createInstance($layout_name);
- if ($layout_instance instanceof PluginFormInterface) {
- $subform_state = SubformState::createForSubform($entity_form['layout_config']['config'], $form_state->getCompleteForm(), $form_state);
- $layout_instance->submitConfigurationForm($entity_form['layout_config']['config'], $subform_state);
- $layout_config = $layout_instance->getConfiguration();
- }
+ // Button to add new section and other paragraphs.
+ $elements['add_more'] = [
+ '#attributes' => ['class' => ['js-hide']],
+ '#type' => 'container',
+ ];
+ $bundle_info = $this->entityTypeBundleInfo->getBundleInfo('paragraph');
+ foreach ($layout_bundles as $bundle_id) {
+ $elements['add_more']['section'] = [
+ '#type' => 'submit',
+ '#bundle_id' => $bundle_id,
+ '#host' => $items->getEntity(),
+ '#value' => $this->t('Add @label', ['@label' => $bundle_info[$bundle_id]['label']]),
+ '#modal_label' => $this->t('Add new @label', ['@label' => $bundle_info[$bundle_id]['label']]),
+ '#name' => implode('_', $parents) . '_add_' . $bundle_id,
+ '#submit' => [
+ [$this, 'newItemSubmit'],
+ ],
+ '#attributes' => ['class' => ['erl-add-section']],
+ '#limit_validation_errors' => [array_merge($parents, [$this->fieldName])],
+ '#ajax' => [
+ 'callback' => [$this, 'elementAjax'],
+ 'wrapper' => $this->wrapperId,
+ ],
+ '#element_parents' => $parents,
+ ];
}
- if (in_array($entity_form['#op'], ['add', 'duplicate'])) {
- // Saving a new entity so set delta to last item.
- $entities = $form_state->get(['inline_entity_form', $ief_id, 'entities']);
- $delta = count($entities) - 1;
+ // Add other paragraph types.
+ $options = [];
+ $bundle_ids = array_diff($target_bundles, $layout_bundles);
+ foreach ($bundle_ids as $bundle_id) {
+ $options[$bundle_id] = $bundle_info[$bundle_id]['label'];
}
- else {
- $delta = $entity_form['#ief_row_delta'];
- }
-
- $layout_properties = [
- 'layout' => $layout_name,
- 'region' => $layout_properties['region'],
- 'parent' => $layout_properties['parent'],
- 'config' => $layout_config,
- 'options' => $layout_options,
- 'entity_bundle' => $entity_form['#entity']->bundle(),
+ $elements['add_more']['type'] = [
+ '#title' => $this->t('Choose type'),
+ '#type' => 'select',
+ '#options' => $options,
+ '#attributes' => ['class' => ['erl-item-type']],
];
- $form_state->set([
- 'inline_entity_form',
- $ief_id,
- 'entities',
- $delta,
- 'layout_properties',
- ], $layout_properties);
+ $elements['add_more']['item'] = [
+ '#type' => 'submit',
+ '#host' => $items->getEntity(),
+ '#value' => $this->t('Create New'),
+ '#submit' => [[$this, 'newItemSubmit']],
+ '#limit_validation_errors' => [array_merge($parents, [$this->fieldName])],
+ '#attributes' => ['class' => ['erl-add-item']],
+ '#ajax' => [
+ 'callback' => [$this, 'elementAjax'],
+ 'wrapper' => $this->wrapperId,
+ ],
+ '#name' => implode('_', $parents) . '_add_item',
+ '#element_parents' => $parents,
+ ];
+ // Add region and parent_delta hidden items only in this is a new entity.
+ // Prefix with underscore to prevent namespace collisions.
+ $elements['add_more']['_region'] = [
+ '#type' => 'hidden',
+ '#attributes' => ['class' => ['erl-new-item-region']],
+ ];
+ $elements['add_more']['_parent_weight'] = [
+ '#type' => 'hidden',
+ '#attributes' => ['class' => ['erl-new-item-parent']],
+ ];
+ return $elements;
}
/**
- * Build the form widget.
+ * Builds the widget form array for an individual item.
*/
public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state) {
- $element = parent::formElement($items, $delta, $element, $form, $form_state);
- if (empty($element['entities']['#attached']['library'])) {
- $element['entities']['#attached']['library'] = [];
- }
-
- $element['entities']['#theme'] = 'entity_reference_layout_widget';
- $entities = $form_state->get([
- 'inline_entity_form',
- $this->getIefId(),
- 'entities',
- ]);
- $element['entities']['#widget'] = 'entity_reference_layout_widget';
-
+ $parents = $form['#parents'];
+ $widget_state = static::getWidgetState($parents, $this->fieldName, $form_state);
$handler_settings = $items->getSetting('handler_settings');
- $layout_bundles = $element['entities']['#layout_bundles'] = $handler_settings['layout_bundles'];
- $this->allowedLayouts = isset($handler_settings['allowed_layouts']) ? $handler_settings['allowed_layouts'] : [];
-
- // Attach libraries from all available layouts.
- foreach ($this->allowedLayouts as $layouts) {
- foreach (array_keys($layouts) as $layout_id) {
- $layout_instance = $this->layoutPluginManager->createInstance($layout_id);
- if ($plugin_definition = $layout_instance->getPluginDefinition()) {
- $library = $plugin_definition->getLibrary();
- if (!empty($library)) {
- $element['entities']['#attached']['library'][] = $library;
- }
- }
- }
- }
-
- // Load paragraph type icons and libraries.
- $target_type = $items->getSetting('target_type');
- $bundles = $handler_settings['target_bundles'];
- $entity_type = $this->entityTypeManager->getDefinition($target_type);
- $storage = $this->entityTypeManager->getStorage($target_type);
- foreach ($bundles as $bundle) {
-
- $values = [];
- if ($bundle_key = $entity_type->getKey('bundle')) {
- $values[$bundle_key] = $bundle;
- }
- $entity = $storage->create($values);
+ $layout_bundles = $handler_settings['layout_bundles'] ?? [];
- // Render an empty entity to get the attached libraries.
- // This ensure CSS/JS is loaded when entities are created via AJAX.
- $view_builder = $this->entityTypeManager->getViewBuilder($entity->getEntityTypeId());
- $preview = $view_builder->view($entity);
- $this->renderer->render($preview);
- $element['entities']['#attached']['library'] += $preview['#attached']['library'];
-
- // Get the icon and pass to Javascript.
- if (method_exists($entity, 'getParagraphType')) {
- if ($icon = $entity->getParagraphType()->getIconFile()) {
- $path = $icon->url();
- $element['entities']['#attached']['drupalSettings']['erlIcons']['icon_' . $bundle] = $path;
- }
- }
+ if (empty($widget_state['items'][$delta]['entity'])) {
+ return [];
}
- $field_name = $this->fieldDefinition->getName();
- $element['#field_name'] = $field_name;
-
- if (isset($element['actions'])) {
-
- $element['actions']['#attributes']['class'][] = 'erl-field-actions';
- $element['actions']['bundle']['#attributes']['class'][] = 'hidden';
- $element['actions']['ief_add']['#attributes']['style'][] = 'display:none;';
-
- if (!count($entities)) {
- $element['actions']['#attributes']['class'][] = 'empty';
+ // Flatten layouts array for use with radio buttons.
+ $available_layouts = [];
+ foreach ($handler_settings['allowed_layouts'] as $group) {
+ foreach ($group as $layout_id => $layout_name) {
+ $available_layouts[$layout_id] = $layout_name;
}
- $nested_bundles = ['Layout' => [], 'Content' => []];
- foreach ($element['actions']['bundle']['#options'] as $key => $val) {
- $bundle_type = in_array($key, $layout_bundles) ? 'Layout' : 'Content';
- $nested_bundles[$bundle_type][$key] = $val;
- }
- $element['actions']['bundle']['#options'] = $nested_bundles;
- $element['actions']['region'] = [
- '#type' => 'hidden',
- '#attributes' => [
- 'class' => ['erl-new-item-region', 'hidden'],
- ],
- ];
- $element['actions']['delta'] = [
- '#type' => 'hidden',
- '#attributes' => [
- 'class' => ['erl-new-item-delta', 'hidden'],
- ],
- ];
}
- if ($this->getSetting('full_screen_editing')) {
- $element['entities']['#attached']['library'][] = 'entity_reference_layout/full_screen_editing';
+ $entity = !empty($widget_state['items'][$delta]) ? $widget_state['items'][$delta]['entity'] : NULL;
+ $options = !empty($widget_state['items'][$delta]['options']) ? $widget_state['items'][$delta]['options'] : [];
+ $config = !empty($widget_state['items'][$delta]['config']) ? $widget_state['items'][$delta]['config'] : [];
+ $layout_path = array_merge($parents, [
+ $this->fieldName,
+ $delta,
+ 'entity_form',
+ 'layout_selection',
+ 'layout',
+ ]);
+ if (!$layout = $form_state->getValue($layout_path)) {
+ $layout = !empty($widget_state['items'][$delta]['layout']) ? $widget_state['items'][$delta]['layout'] : NULL;
}
- foreach ($entities as $delta => $value) {
+ $element = [
+ '#type' => 'container',
+ '#delta' => $delta,
+ '#entity' => $entity,
+ '#layout' => !empty($items[$delta]->layout) ? $items[$delta]->layout : '',
+ '#region' => !empty($items[$delta]->region) ? $items[$delta]->region : '',
+ '#attributes' => [
+ 'class' => [
+ 'erl-item',
+ ],
+ 'id' => [
+ $this->fieldName . '--item-' . $delta,
+ ],
+ ],
+ 'region' => [
+ '#type' => 'hidden',
+ '#attributes' => ['class' => ['erl-region']],
+ '#default_value' => !empty($items[$delta]->region) ? $items[$delta]->region : '',
+ ],
- $layout_properties = $value['layout_properties'];
+ // Edit and remove button.
+ 'actions' => [
+ '#type' => 'container',
+ '#weight' => -1000,
+ '#attributes' => ['class' => ['erl-actions']],
+ 'edit' => [
+ '#type' => 'submit',
+ '#name' => 'edit_' . $delta,
+ '#value' => $this->t('Edit'),
+ '#attributes' => ['class' => ['erl-edit']],
+ '#limit_validation_errors' => [array_merge($parents, [$this->fieldName])],
+ '#submit' => [[$this, 'editItemSubmit']],
+ '#delta' => $delta,
+ '#ajax' => [
+ 'callback' => [$this, 'elementAjax'],
+ 'wrapper' => $this->wrapperId,
+ ],
+ '#element_parents' => $parents,
+ ],
+ 'remove' => [
+ '#type' => 'submit',
+ '#name' => 'remove_' . $delta,
+ '#value' => $this->t('Remove'),
+ '#attributes' => ['class' => ['erl-remove']],
+ '#limit_validation_errors' => [array_merge($parents, [$this->fieldName])],
+ '#submit' => [[$this, 'removeItemSubmit']],
+ '#delta' => $delta,
+ '#ajax' => [
+ 'callback' => [$this, 'elementAjax'],
+ 'wrapper' => $this->wrapperId,
+ ],
+ '#element_parents' => $parents,
+ ],
+ ],
- // We set #layout_parent to instruct the theme function where to position
- // new entities that have not been saved to the db yet but need to be
- // represented on the screen, in the right place.
- if (isset($layout_properties['parent']) && is_numeric($layout_properties['parent'])) {
- $element['entities'][$delta]['#layout_parent'] = $layout_properties['parent'];
- }
+ // These properties aren't modified by the main form,
+ // but are modified when a user edits a specific item.
+ 'entity' => [
+ '#type' => 'value',
+ '#value' => $entity,
+ ],
+ 'config' => [
+ '#type' => 'value',
+ '#value' => $config,
+ ],
+ 'options' => [
+ '#type' => 'value',
+ '#value' => $options,
+ ],
+ 'layout' => [
+ '#type' => 'value',
+ '#value' => $layout,
+ ],
+ '#process' => [],
+ ];
- /* @var \Drupal\core\Entity\EntityInterface $entity */
- $entity = $value['entity'];
- $form_item =& $element['entities'][$delta];
- $form_item['#layout_properties'] = $layout_properties;
+ // If this is a new entity, pass the region and parent
+ // item's weight to the theme.
+ if (!empty($widget_state['items'][$delta]['is_new'])) {
+ $element['#is_new'] = TRUE;
+ $element['#new_region'] = $widget_state['items'][$delta]['new_region'];
+ $element['#parent_weight'] = $widget_state['items'][$delta]['parent_weight'];
+ $element['#attributes']['class'][] = 'js-hide';
+ }
- // Render the paragraph on the edit form.
- // @todo Allow admins to configure which view mode should be used.
+ // Build the preview and render it in the form.
+ $preview = [];
+ if (isset($entity)) {
$view_builder = $this->entityTypeManager->getViewBuilder($entity->getEntityTypeId());
$preview = $view_builder->view($entity);
- $rendered_preview = $this->renderer->render($preview);
- $form_item['prefix'] = [
- '#markup' => $rendered_preview,
- '#weight' => -100,
+ $preview['#cache']['max-age'] = 0;
+ }
+ $element += [
+ 'preview' => $preview,
+ ];
+
+ // Add remove confirmation form if we're removing.
+ if (isset($widget_state['remove_item']) && $widget_state['remove_item'] === $delta) {
+ $element['remove_form'] = [
+ '#prefix' => '
',
+ '#suffix' => '
',
+ '#type' => 'container',
+ '#attributes' => ['data-dialog-title' => [$this->t('Confirm removal')]],
+ 'message' => [
+ '#type' => 'markup',
+ '#markup' => $this->t('Are you sure you want to permanently remove this
@type?This action cannot be undone.', ['@type' => $entity->type->entity->label()]),
+ ],
+ 'actions' => [
+ '#type' => 'container',
+ 'confirm' => [
+ '#type' => 'submit',
+ '#value' => $this->t('Remove'),
+ '#delta' => $delta,
+ '#submit' => [[$this, 'removeItemConfirmSubmit']],
+ '#ajax' => [
+ 'callback' => [$this, 'elementAjax'],
+ 'wrapper' => $this->wrapperId,
+ ],
+ '#element_parents' => $parents,
+ ],
+ 'cancel' => [
+ '#type' => 'submit',
+ '#value' => $this->t('Cancel'),
+ '#delta' => $delta,
+ '#submit' => [[$this, 'removeItemCancelSubmit']],
+ '#attributes' => [
+ 'class' => ['erl-cancel'],
+ ],
+ '#ajax' => [
+ 'callback' => [$this, 'elementAjax'],
+ 'wrapper' => $this->wrapperId,
+ ],
+ '#element_parents' => $parents,
+ ],
+ ],
+ '#weight' => 1000,
+ '#delta' => $delta,
];
+ }
- if (!empty($form_item['form'])) {
- if ($value['form'] == 'remove') {
- try {
- $form_item['form']['#attributes']['class'][] = 'erl-remove-form';
- /* @var \Drupal\core\Entity\EntityInterface $form_item_entity */
- $form_item_entity = $form_item['#entity'];
- $bundle_type_id = $form_item_entity->getEntityType()
- ->getBundleEntityType();
- $bundle_label = $this->entityTypeManager->getStorage($bundle_type_id)
- ->load($form_item_entity->bundle())
- ->label();
- if ($bundle_label) {
- $form_title = $this->t('Remove @bundle_label', ['@bundle_label' => $bundle_label]);
- $form_item['form']['ief_title'] = [
- '#markup' => '
' . $form_title . '
',
- '#weight' => -1000,
- ];
- $form_item['form']['message']['#markup'] = $this->t('Are you sure you want to remove this %bundle?', ['%bundle' => $bundle_label]);
- $form_item['form']['message']['#attributes']['class'] = ['erl-remove-message'];
- }
- }
- catch (\Exception $e) {
- watchdog_exception('entity_reference_layout', $e);
+ // Add edit form if open.
+ if (isset($widget_state['open_form']) && $widget_state['open_form'] === $delta) {
+ $display = EntityFormDisplay::collectRenderDisplay($entity, 'default');
+ $bundle_label = $entity->type->entity->label();
+ $element['entity_form'] = [
+ '#prefix' => '
',
+ '#suffix' => '
',
+ '#type' => 'container',
+ '#parents' => array_merge($parents, [
+ $this->fieldName,
+ $delta,
+ 'entity_form',
+ ]),
+ '#weight' => 1000,
+ '#delta' => $delta,
+ '#display' => $display,
+ '#attributes' => [
+ 'data-dialog-title' => [
+ $entity->id() ? $this->t('Edit @type', ['@type' => $bundle_label]) : $this->t('Create new @type', ['@type' => $bundle_label]),
+ ],
+ ],
+ ];
+ $display->buildForm($entity, $element['entity_form'], $form_state);
+
+ // Add the layout plugin form if applicable.
+ if (in_array($entity->bundle(), $layout_bundles)) {
+ $element['entity_form']['layout_selection'] = [
+ '#type' => 'container',
+ 'layout' => [
+ '#type' => 'radios',
+ '#title' => $this->t('Select a layout:'),
+ '#options' => $available_layouts,
+ '#default_value' => $layout,
+ '#attributes' => [
+ 'class' => ['erl-layout-select'],
+ ],
+ '#required' => TRUE,
+ '#after_build' => [[get_class($this), 'processLayoutOptions']],
+ '#ajax' => [
+ 'event' => 'change',
+ 'callback' => [$this, 'buildLayoutConfigurationFormAjax'],
+ 'trigger_as' => ['name' => 'update_layout'],
+ 'wrapper' => 'layout-config',
+ ],
+ ],
+ 'update' => [
+ '#type' => 'submit',
+ '#value' => $this->t('Update'),
+ '#name' => 'update_layout',
+ '#delta' => $element['#delta'],
+ '#limit_validation_errors' => [
+ array_merge($parents, [
+ $this->fieldName,
+ $delta,
+ 'entity_form',
+ 'layout_selection',
+ ]),
+ ],
+ '#submit' => [
+ [$this, 'editItemSubmit'],
+ ],
+ '#ajax' => [
+ 'callback' => [$this, 'buildLayoutConfigurationFormAjax'],
+ 'wrapper' => 'layout-config',
+ ],
+ '#attributes' => [
+ 'class' => ['js-hide'],
+ ],
+ '#element_parents' => $parents,
+ ],
+ ];
+ $element['entity_form']['layout_plugin_form'] = [
+ '#prefix' => '
',
+ '#suffix' => '
',
+ ];
+ // Add the layout configuration form if applicable.
+ if (!empty($layout)) {
+ $layout_instance = $this->layoutPluginManager->createInstance($layout, $config);
+ if ($layout_plugin = $this->getLayoutPluginForm($layout_instance)) {
+ $element['entity_form']['layout_plugin_form'] += [
+ '#type' => 'details',
+ '#title' => $this->t('Layout Configuration'),
+ ];
+ $element['entity_form']['layout_plugin_form'] += $layout_plugin->buildConfigurationForm([], $form_state);
}
}
- }
-
- // Edit form is open for this item.
- if (!empty($form_item['form']['inline_entity_form'])) {
- // Allow module to alter forms.
- $form_item['form']['#process'][] = 'entity_reference_layout_alter_form';
- $form_item['form']['#theme'] = 'entity_reference_layout_ief';
- $form_item['form']['inline_entity_form']['#process'][] = [$this, 'iefFormTitle'];
- $form_item['form']['inline_entity_form']['#process'][] = [get_class($this), 'alterEntityFormActions'];
-
- // This is a layout item so add the layout options form.
- if (in_array($layout_properties['entity_bundle'], $layout_bundles)) {
- $form_item['form']['inline_entity_form']['#layout_properties'] = $form_state->get([
- 'inline_entity_form',
- $this->getIefId(),
- 'entities',
- $delta,
- 'layout_properties',
- ]);
- $form_item['form']['inline_entity_form']['#process'][] = [$this, 'layoutOptionsForm'];
+ // Add the additional options form if applicable.
+ // This is deprecated and included only for backwards compatibility.
+ if ($this->getSetting('always_show_options_form')) {
+ // Other layout options.
+ $element['entity_form']['options'] = [
+ '#type' => 'details',
+ '#title' => $this->t('Basic Layout Options'),
+ '#description' => $this->t('Classes will be applied to the container for this field item.'),
+ '#open' => FALSE,
+ ];
+ $element['entity_form']['options']['container_classes'] = [
+ '#type' => 'textfield',
+ '#title' => $this->t('Custom Classes for Layout Container'),
+ '#description' => $this->t('Classes will be applied to the container for this field item.'),
+ '#size' => 50,
+ '#default_value' => !empty($options['container_classes']) ? $options['container_classes'] : '',
+ '#placeholder' => $this->t('CSS Classes'),
+ ];
+ $element['entity_form']['options']['bg_color'] = [
+ '#type' => 'textfield',
+ '#title' => $this->t('Background Color for Layout Container'),
+ '#description' => $this->t('Background will be applied to the layout container.'),
+ '#size' => 10,
+ '#default_value' => !empty($options['bg_color']) ? $options['bg_color'] : '',
+ '#placeholder' => $this->t('Hex Code'),
+ ];
}
- $form_item['form']['inline_entity_form']['#erl_form'] = TRUE;
- }
-
- // Adds layout property fields (region, delta, etc.).
- $form_item['#process'][] = [$this, 'layoutPropertiesForm'];
-
- // Hide delta without relying on javascript.
- $form_item['delta']['#attributes']['class'][] = 'hidden';
-
- // Set layout info for rendering in theme function.
- if (in_array($entity->bundle(), $layout_bundles) && !empty($layout_properties['layout'])) {
- $form_item['#layout'] = $layout_properties['layout'];
- }
- elseif (!empty($layout_properties['region'])) {
- $form_item['#region'] = $layout_properties['region'];
- }
-
- // Set layout options for use in rendering form.
- if (isset($value['layout_properties']['options'])) {
- $form_item['#layout_options'] = $value['layout_properties']['options'];
- }
-
- // Set layout plugin config for use in rendering form.
- if (isset($value['layout_properties']['config'])) {
- $form_item['#layout_config'] = !empty($value['layout_properties']['config']) ? $value['layout_properties']['config'] : [];
- }
- else {
- $form_item['#layout_config'] = [];
}
-
- }
-
- // Add layout options for inline entity form:
- if (isset($element['form']['inline_entity_form'])) {
-
- // Allow module to alter form.
- $element['form']['#theme'] = 'entity_reference_layout_ief';
- $element['form']['#process'][] = 'entity_reference_layout_alter_form';
- $element['form']['#attributes']['class'][] = 'erl-add-entity-form';
- $element['form']['inline_entity_form']['#erl_form'] = TRUE;
- $element['form']['inline_entity_form']['#process'][] = [$this, 'iefFormTitle'];
- $element['form']['inline_entity_form']['#process'][] = [get_class($this), 'alterEntityFormActions'];
-
- // Using (and sanitizing) raw input because $form_state values
- // are not dependable at this point.
- $input = $form_state->getUserInput();
- $default_properties = [
- 'parent' => 10000,
- 'entity_bundle' => $element['form']['inline_entity_form']['#bundle'],
- 'is_new' => TRUE,
- 'reset' => TRUE,
+ // Add save, cancel, etc.
+ $element['entity_form'] += [
+ 'actions' => [
+ '#weight' => 1000,
+ '#type' => 'container',
+ '#attributes' => ['class' => ['erl-item-form-actions']],
+ 'save_item' => [
+ '#type' => 'submit',
+ '#name' => 'save',
+ '#value' => $this->t('Save'),
+ '#delta' => $element['#delta'],
+ '#limit_validation_errors' => [array_merge($parents, [$this->fieldName])],
+ '#submit' => [
+ [$this, 'saveItemSubmit'],
+ ],
+ '#ajax' => [
+ 'callback' => [$this, 'elementAjax'],
+ 'wrapper' => $this->wrapperId,
+ ],
+ '#element_parents' => $parents,
+ ],
+ 'cancel' => [
+ '#type' => 'submit',
+ '#name' => 'cancel',
+ '#value' => $this->t('Cancel'),
+ '#limit_validation_errors' => [],
+ '#delta' => $element['#delta'],
+ '#submit' => [
+ [$this, 'cancelItemSubmit'],
+ ],
+ '#attributes' => [
+ 'class' => ['erl-cancel'],
+ ],
+ '#ajax' => [
+ 'callback' => [$this, 'elementAjax'],
+ 'wrapper' => $this->wrapperId,
+ ],
+ '#element_parents' => $parents,
+ ],
+ ],
];
-
- // Use form input for layout name if the user switched layouts.
- $parents = array_merge($element['form']['inline_entity_form']['#parents'], ['layout_selection', 'layout']);
- $layout_name = preg_replace('/[^A-Za-z0-9\-\_]/', '', NestedArray::getValue($input, $parents));
- if ($layout_name) {
- $default_properties['layout'] = $layout_name;
- }
-
- $default_properties['is_layout_bundle'] = in_array($default_properties['entity_bundle'], $layout_bundles);
- if (isset($input[$field_name]['actions']['region'])) {
- $default_properties['region'] = preg_replace('/[^A-Za-z0-9\-\_]/', '', $input[$field_name]['actions']['region']);
- }
- if (isset($input[$field_name]['actions']['delta'])) {
- $default_properties['parent'] = intval($input[$field_name]['actions']['delta']);
- }
-
- $element['form']['inline_entity_form']['#layout_properties'] = $default_properties;
- if (in_array($default_properties['entity_bundle'], $layout_bundles)) {
- $element['form']['inline_entity_form']['#process'][] = [$this, 'layoutOptionsForm'];
- }
-
- // Adds layout property fields (region, delta, etc.).
- $element['form']['inline_entity_form']['#process'][] = [$this, 'layoutPropertiesForm'];
}
-
- $element['#attributes'] = [
- 'class' => ['erl-field'],
- ];
-
- $element['#attached']['library'][] = 'core/drupal.dialog';
-
return $element;
}
/**
- * Manipulate form actions form element.
+ * Add theme wrappers to layout selection radios.
+ *
+ * Theme function injects layout icons into radio buttons.
*/
- public static function alterEntityFormActions($element) {
- $element['actions']['#weight'] = 10000;
- $element['actions']['#attributes']['class'][] = 'erl-form-actions';
-
- if (!empty($element['#bundle'])) {
- try {
- /* @var \Drupal\core\Entity\EntityInterface $element_entity */
- $element_entity = $element['#entity'];
- $bundle_type_id = $element_entity->getEntityType()
- ->getBundleEntityType();
- $bundle_label = \Drupal::entityTypeManager()
- ->getStorage($bundle_type_id)
- ->load($element['#bundle'])
- ->label();
-
- if ($element['#op'] == 'add') {
- $save_label = t('Create @type_singular', ['@type_singular' => $bundle_label]);
- }
- elseif ($element['#op'] == 'duplicate') {
- $save_label = t('Duplicate @type_singular', ['@type_singular' => $bundle_label]);
- }
- else {
- $save_label = t('Update @type_singular', ['@type_singular' => $bundle_label]);
- }
- $element['actions']['ief_' . $element['#op'] . '_save']['#value'] = $save_label;
- }
- catch (\Exception $e) {
- watchdog_exception('entity_reference_layout', $e);
- }
+ public static function processLayoutOptions($element) {
+ foreach (Element::children($element) as $radio_item) {
+ $element[$radio_item]['#theme_wrappers'][] = 'entity_reference_layout_radio';
}
return $element;
}
/**
- * Add a title element inline entity forms.
+ * Form submit handler - adds a new item and opens its edit form.
*/
- public function iefFormTitle(array $element, FormStateInterface $form_state) {
- $entity_type = $element['#entity_type'];
- $bundle_info = $this->entityTypeBundleInfo->getBundleInfo($entity_type);
- $bundle_id = $element['#bundle'];
- $bundle_label = $bundle_info[$bundle_id]['label'];
- if ($element['#op'] == 'add') {
- $form_title = $this->t('Add new @bundle', ['@bundle' => $bundle_label]);
+ public function newItemSubmit(array $form, FormStateInterface $form_state) {
+
+ $element = $form_state->getTriggeringElement();
+ $parents = $element['#element_parents'];
+ $widget_state = static::getWidgetState($parents, $this->fieldName, $form_state);
+
+ if (!empty($element['#bundle_id'])) {
+ $bundle_id = $element['#bundle_id'];
}
- if ($element['#op'] == 'edit') {
- $form_title = $this->t('Edit @bundle', ['@bundle' => $bundle_label]);
+ else {
+ $element_parents = $element['#parents'];
+ array_splice($element_parents, -1, 1, 'type');
+ $bundle_id = $form_state->getValue($element_parents);
}
- // Hidden element, used in js.
- $element['ief_title'] = [
- '#markup' => '
' . $form_title . '
',
- '#weight' => -1000,
+
+ $entity_type = $this->entityTypeManager->getDefinition('paragraph');
+ $bundle_key = $entity_type->getKey('bundle');
+
+ $paragraphs_entity = $this->entityTypeManager->getStorage('paragraph')->create([
+ $bundle_key => $bundle_id,
+ ]);
+ $paragraphs_entity->setParentEntity($element['#host'], $this->fieldDefinition->getName());
+
+ $path = array_merge($parents, [$this->fieldDefinition->getName(), 'add_more']);
+ $new_region = $form_state->getValue(array_merge($path, ['_region']));
+ $parent_weight = intval($form_state->getValue(array_merge($path, ['_parent_weight'])));
+
+ $widget_state['items'][] = [
+ 'entity' => $paragraphs_entity,
+ 'is_new' => TRUE,
+ 'new_region' => $new_region,
+ 'parent_weight' => $parent_weight,
];
- return $element;
+ $widget_state['open_form'] = $widget_state['items_count'];
+ $widget_state['items_count']++;
+
+ static::setWidgetState($parents, $this->fieldName, $form_state, $widget_state);
+ $form_state->setRebuild();
}
/**
- * Build layout properties form.
- *
- * @todo: Use configuration options from layout plugins.
- * see https://www.drupal.org/project/entity_reference_layout/issues/3038533
+ * Form submit handler - opens the edit form for an existing item.
*/
- public function layoutPropertiesForm($element, $form_state) {
+ public function editItemSubmit($form, $form_state) {
- $default_properties = $element['#layout_properties'];
- $element['layout_properties'] = [
- '#type' => 'container',
- '#weight' => -1000,
- ];
- $element['layout_properties']['#attributes'] = ['class' => ['hidden']];
- $element['layout_properties']['layout'] = [
- '#type' => 'hidden',
- // Use value because this doesn't change from this form element.
- '#value' => isset($default_properties['layout']) ? $default_properties['layout'] : '',
- '#attributes' => [
- 'class' => [
- 'erl-layout-select',
- 'erl-layout',
- 'erl-layout-' . (!empty($default_properties['layout']) ? $default_properties['layout'] : ''),
- ],
- ],
- ];
- $element['layout_properties']['region'] = [
- '#type' => 'hidden',
- '#default_value' => !empty($default_properties['region']) ? $default_properties['region'] : '',
- '#attributes' => [
- 'class' => [
- 'erl-region-select',
- 'erl-region',
- 'erl-region-' . (!empty($default_properties['region']) ? $default_properties['region'] : ''),
- ],
- ],
- ];
- $element['layout_properties']['entity_bundle'] = [
- '#type' => 'value',
- '#value' => $default_properties['entity_bundle'],
- ];
- $element['layout_properties']['parent'] = [
- '#type' => 'hidden',
- '#default_value' => intval($default_properties['parent']),
- '#attributes' => [
- 'class' => ['parent-delta'],
- ],
- ];
+ $element = $form_state->getTriggeringElement();
+ $parents = $element['#element_parents'];
+ $delta = $element['#delta'];
- return $element;
+ $widget_state = static::getWidgetState($parents, $this->fieldName, $form_state);
+ $widget_state['open_form'] = $delta;
+
+ static::setWidgetState($parents, $this->fieldName, $form_state, $widget_state);
+ $form_state->setRebuild();
}
/**
- * Add theme wrappers to layout selection radios.
+ * Form submit handler - opens confirm removal form for an item.
*/
- public static function processLayoutOptions($element) {
- foreach (Element::children($element) as $radio_item) {
- $element[$radio_item]['#theme_wrappers'][] = 'entity_reference_layout_radio';
- }
- return $element;
+ public function removeItemSubmit($form, $form_state) {
+
+ $element = $form_state->getTriggeringElement();
+ $parents = $element['#element_parents'];
+ $delta = $element['#delta'];
+
+ $widget_state = static::getWidgetState($parents, $this->fieldName, $form_state);
+ $widget_state['remove_item'] = $delta;
+
+ static::setWidgetState($parents, $this->fieldName, $form_state, $widget_state);
+ $form_state->setRebuild();
}
/**
- *
+ * Form submit handler - removes/deletes an item.
*/
- public function radioThemeWrapper($element) {
- $element['#attributes']['class'][] = 'test-class';
- return \Drupal::service('renderer')->render($element);
+ public function removeItemConfirmSubmit($form, $form_state) {
+
+ $element = $form_state->getTriggeringElement();
+ $parents = $element['#element_parents'];
+ $delta = $element['#delta'];
+
+ $widget_state = static::getWidgetState($parents, $this->fieldName, $form_state);
+
+ unset($widget_state['items'][$delta]['entity']);
+ unset($widget_state['remove_item']);
+
+ static::setWidgetState($parents, $this->fieldName, $form_state, $widget_state);
+ $form_state->setRebuild();
}
/**
- * Build a layout options form.
- *
- * @todo: Use form from layout plugins.
- * See https://www.drupal.org/project/entity_reference_layout/issues/3038533
+ * Form submit handler - cancels item removal and closes confirmation form.
*/
- public function layoutOptionsForm($element, $form_state) {
+ public function removeItemCancelSubmit($form, $form_state) {
- $defaults = $element['#layout_properties'];
- $layout_options = [];
- foreach ($this->allowedLayouts as $layout_group) {
- foreach ($layout_group as $layout_id => $layout_name) {
- $layout_options[$layout_id] = $layout_name;
- }
- }
+ $element = $form_state->getTriggeringElement();
+ $parents = $element['#element_parents'];
- $default_layout = !empty($defaults['layout']) ? $defaults['layout'] : key($layout_options);
+ $widget_state = static::getWidgetState($parents, $this->fieldName, $form_state);
- $element['layout_selection'] = [
- '#type' => 'container',
- ];
- $element['layout_selection']['layout'] = [
- '#type' => 'radios',
- '#title' => $this->t('Select a layout:'),
- '#description' => $this->t('Choose a layout for this section.'),
- '#options' => $layout_options,
- '#default_value' => $default_layout,
- '#attributes' => [
- 'class' => ['erl-layout', 'erl-layout-section-select'],
- ],
- '#required' => TRUE,
- '#after_build' => [[get_class($this), 'processLayoutOptions']],
- '#ajax' => [
- 'event' => 'change',
- 'callback' => [$this, 'buildLayoutConfigurationFormAjaxCallback'],
- 'wrapper' => 'layout-config',
- 'trigger_as' => ['name' => 'update_layout'],
- ],
- ];
- // Add update button and submit handler to switch between layouts.
- $element['layout_selection']['update'] = [
- '#type' => 'submit',
- '#value' => $this->t('Update'),
- '#name' => 'update_layout',
- '#submit' => [
- [$this, 'updateLayoutOptionsForm'],
- ],
- '#limit_validation_errors' => [],
- '#ajax' => [
- 'callback' => [$this, 'buildLayoutConfigurationFormAjaxCallback'],
- 'wrapper' => 'layout-config',
- ],
- '#attributes' => [
- 'class' => ['js-hide'],
- ],
- ];
- if (isset($defaults['is_new'])) {
- $element['layout_selection']['update']['#submit'][] = 'inline_entity_form_open_form';
- $element['layout_selection']['update']['#ief_form'] = 'add';
- $element['layout_selection']['update']['#is_new'] = TRUE;
- }
- else {
- $element['layout_selection']['update']['#submit'][] = 'inline_entity_form_open_row_form';
- $element['layout_selection']['update']['#ief_row_delta'] = $element['#ief_row_delta'];
- $element['layout_selection']['update']['#ief_row_form'] = 'edit';
- $element['layout_selection']['update']['#is_new'] = FALSE;
- }
+ unset($widget_state['remove_item']);
- // Add config form.
- $element['layout_config'] = [
- '#prefix' => '
',
- '#suffix' => '
',
- '#type' => 'details',
- '#title' => $this->t('Layout Plugin Configuration'),
- '#access' => $this->currentUser->hasPermission('edit entity reference layout plugin config'),
- 'config' => [],
- ];
+ static::setWidgetState($parents, $this->fieldName, $form_state, $widget_state);
+ $form_state->setRebuild();
+ }
- // A layout has been selected, try to render it's plugin form.
- if ($default_layout) {
- $plugin_config = isset($defaults['config']) ? $defaults['config'] : [];
- /** @var \Drupal\Core\Layout\LayoutInterface $layout_instance */
- $layout_instance = $this->layoutPluginManager->createInstance($default_layout, $plugin_config);
- $plugin_form = $this->getLayoutPluginForm($layout_instance);
- if ($plugin_form) {
- // @todo Decide whether or not to use subformstate.
- $element['layout_config']['config'] = $plugin_form->buildConfigurationForm([], $form_state);
- }
- else {
- $element['layout_config']['#attributes']['class'][] = 'visually-hidden';
+ /**
+ * Form submit handler - saves an item.
+ */
+ public function saveItemSubmit($form, $form_state) {
+
+ $element = $form_state->getTriggeringElement();
+ $parents = $element['#element_parents'];
+ $delta = $element['#delta'];
+ $element_array_parents = $element['#array_parents'];
+ $item_array_parents = array_splice($element_array_parents, 0, -2);
+
+ $item_form = NestedArray::getValue($form, $item_array_parents);
+ $display = $item_form['#display'];
+ $widget_state = static::getWidgetState($parents, $this->fieldName, $form_state);
+
+ // Remove is_new flag since we're saving the entity.
+ unset($widget_state['items'][$delta]['is_new']);
+ // Save field values to entity.
+ $display->extractFormValues($widget_state['items'][$delta]['entity'], $item_form, $form_state);
+
+ // Save layout settings.
+ if (!empty($item_form['layout_selection']['layout'])) {
+
+ $layout = $form_state->getValue($item_form['layout_selection']['layout']['#parents']);
+ $widget_state['items'][$delta]['layout'] = $layout;
+
+ // Save layout config:
+ if (!empty($item_form['layout_plugin_form'])) {
+ $layout_instance = $this->layoutPluginManager->createInstance($layout);
+ if ($this->getLayoutPluginForm($layout_instance)) {
+ $subform_state = SubformState::createForSubform($item_form['layout_plugin_form'], $form_state->getCompleteForm(), $form_state);
+ $layout_instance->submitConfigurationForm($item_form['layout_plugin_form'], $subform_state);
+ $layout_config = $layout_instance->getConfiguration();
+ $widget_state['items'][$delta]['config'] = $layout_config;
+ }
}
}
- // Other layout options.
- $element['layout_options']['options'] = [
- '#type' => 'details',
- '#title' => $this->t('Additional Layout Options'),
- '#description' => $this->t('Classes will be applied to the container for this field item.'),
- '#open' => FALSE,
- '#access' => $this->currentUser->hasPermission('adjust entity reference layout options'),
- ];
- $element['layout_options']['options']['container_classes'] = [
- '#type' => 'textfield',
- '#title' => $this->t('Custom Classes for Layout Container'),
- '#description' => $this->t('Classes will be applied to the container for this field item.'),
- '#size' => 50,
- '#default_value' => !empty($defaults['options']['options']['container_classes']) ? $defaults['options']['options']['container_classes'] : '',
- '#placeholder' => $this->t('CSS Classes'),
- ];
- $element['layout_options']['options']['bg_color'] = [
- '#type' => 'textfield',
- '#title' => $this->t('Background Color for Layout Container'),
- '#description' => $this->t('Background will be applied to the layout container.'),
- '#size' => 10,
- '#default_value' => !empty($defaults['options']['options']['bg_color']) ? $defaults['options']['options']['bg_color'] : '',
- '#placeholder' => $this->t('Hex Code'),
- ];
- // Hide the options form if "Show options form" is not selected in settings.
- if (!empty($plugin_form) && $this->getSetting('always_show_options_form') != TRUE) {
- $element['layout_options']['options']['#access'] = FALSE;
+ // Save layout options (deprecated).
+ if (!empty($item_form['options'])) {
+ $options_path = array_merge($parents, [
+ $this->fieldName,
+ $delta,
+ 'entity_form',
+ 'options',
+ ]);
+ $widget_state['items'][$delta]['options'] = $form_state->getValue($options_path);
}
- $event = new ErlPropertiesFormEvent($element, $defaults);
- $this->eventDispatcher->dispatch(ErlPropertiesFormEvent::EVENT_NAME, $event);
+ // Close the entity form.
+ $widget_state['open_form'] = FALSE;
- return $element;
+ static::setWidgetState($parents, $this->fieldName, $form_state, $widget_state);
+ $form_state->setRebuild();
}
/**
- * Submit callback - saves the selected layout in $form_state.
+ * Form submit handler - cancels editing an item and closes form.
*/
- public function updateLayoutOptionsForm($form, $form_state) {
- $button = $form_state->getTriggeringElement();
- $button_parents = array_merge(array_splice($button['#parents'], 0, -1), ['layout']);
- $input = $form_state->getUserInput();
- $layout = NestedArray::getValue($input, $button_parents);
-
- if ($button['#is_new']) {
- $layout_properties_parents = [
- 'inline_entity_form',
- $this->getIefId(),
- 'default_layout_properties',
- 'layout',
- ];
- }
- else {
- $layout_properties_parents = [
- 'inline_entity_form',
- $this->getIefId(),
- 'entities',
- $button['#ief_row_delta'],
- 'layout_properties',
- 'layout',
- ];
+ public function cancelItemSubmit($form, $form_state) {
+
+ $element = $form_state->getTriggeringElement();
+ $parents = $element['#element_parents'];
+ $delta = $element['#delta'];
+ $widget_state = static::getWidgetState($parents, $this->fieldName, $form_state);
+
+ // If canceling an item that hasn't been created yet, remove it.
+ if (!empty($widget_state['items'][$delta]['is_new'])) {
+ array_splice($widget_state['items'], $delta, 1);
+ $widget_state['items_count'] = count($widget_state['items']);
}
- $form_state->set($layout_properties_parents, $layout);
+ $widget_state['open_form'] = FALSE;
+ static::setWidgetState($parents, $this->fieldName, $form_state, $widget_state);
+ $form_state->setRebuild();
}
/**
- * Render a layout plugin form, if it exists.
+ * Ajax callback to return the entire ERL element.
*/
- public function buildLayoutConfigurationFormAjaxCallback(array &$form, FormStateInterface &$form_state) {
+ public function elementAjax(array $form, FormStateInterface $form_state) {
$element = $form_state->getTriggeringElement();
- $parents = array_merge(array_splice($element['#array_parents'], 0, -2), ['layout_config']);
- $form_item = NestedArray::getValue($form, $parents);
+ $parents = $element['#element_parents'];
+ $field_state = static::getWidgetState($parents, $this->fieldName, $form_state);
+ return NestedArray::getValue($form, $field_state['array_parents']);
+ }
- if (empty($form_item)) {
- $form_item = [
- '#prefix' => '
',
- '#suffix' => '
',
- ];
+ /**
+ * Ajax callback to return a layout plugin configuration form.
+ */
+ public function buildLayoutConfigurationFormAjax(array $form, FormStateInterface $form_state) {
+
+ $element = $form_state->getTriggeringElement();
+ $parents = $element['#array_parents'];
+ $parents = array_splice($parents, 0, -2);
+ $parents = array_merge($parents, ['layout_plugin_form']);
+ if ($layout_plugin_form = NestedArray::getValue($form, $parents)) {
+ return $layout_plugin_form;
+ }
+ else {
+ return [];
}
- return $form_item;
}
/**
* Field instance settings form.
*/
public function settingsForm(array $form, FormStateInterface $form_state) {
- $form = parent::settingsForm($form, $form_state);
-
- // @FIXME
- // Allow existing doesn't work at the moment (it breaks all the widget).
- // It is thus disabled at the moment.
- $form['allow_existing']['#disabled'] = TRUE;
- $form['allow_existing']['#description'] = $this->t('This feature is not working at the moment (disabled)');
-
- // @FIXME
- // Allow duplicate doesn't work at the moment (unpredictable results).
- // correct behavior. It is thus disabled at the moment.
- $form['allow_duplicate']['#disabled'] = TRUE;
- $form['allow_duplicate']['#description'] = $this->t('This feature is not working at the moment (disabled)');
-
- $form['full_screen_editing'] = [
- '#type' => 'checkbox',
- '#default_value' => $this->getSetting('full_screen_editing'),
- '#title' => $this->t('Full screen editing'),
- '#description' => $this->t('Show node edit form full-screen to make room for layout builder'),
- ];
-
$form['always_show_options_form'] = [
'#type' => 'checkbox',
'#default_value' => $this->getSetting('always_show_options_form'),
@@ -871,13 +862,26 @@ class EntityReferenceLayoutWidget extends InlineEntityFormComplex {
return $form;
}
+ /**
+ * {@inheritdoc}
+ */
+ public function settingsSummary() {
+ $summary = [];
+ if ($this->getSetting('always_show_options_form')) {
+ $summary[] = t('Layout configuration: Show extra options form (deprecated).');
+ }
+ else {
+ $summary[] = t('Layout configuration: Rely on Layout Plugins (preferred).');
+ }
+ return $summary;
+ }
+
/**
* Default settings for widget.
*/
public static function defaultSettings() {
$defaults = parent::defaultSettings();
$defaults += [
- 'full_screen_editing' => FALSE,
'always_show_options_form' => FALSE,
];
@@ -885,28 +889,11 @@ class EntityReferenceLayoutWidget extends InlineEntityFormComplex {
}
/**
- * Massages the form values into the format expected for field values.
+ * {@inheritdoc}
*/
public function massageFormValues(array $values, array $form, FormStateInterface $form_state) {
-
- $field_name = $this->fieldDefinition->getName();
- $field_value = $form_state->getValue($field_name);
- $entities = $form_state->get([
- 'inline_entity_form',
- $this->getIefId(),
- 'entities',
- ]);
-
foreach ($values as $delta => $value) {
- if (isset($field_value['entities'][$delta])) {
- $values[$delta]['region'] = !empty($field_value['entities'][$delta]['layout_properties']['region']) ? $field_value['entities'][$delta]['layout_properties']['region'] : '';
- $values[$delta]['layout'] = !empty($field_value['entities'][$delta]['layout_properties']['layout']) ? $field_value['entities'][$delta]['layout_properties']['layout'] : '';
- }
- // Configuration provided by layout plugin.
- if (isset($entities[$delta]['layout_properties']['config'])) {
- $values[$delta]['options'] = $entities[$delta]['layout_properties']['options'];
- $values[$delta]['config'] = $entities[$delta]['layout_properties']['config'];
- }
+ unset($values[$delta]['actions']);
}
return $values;
}