diff --git a/feeds.css b/feeds.css index 300d4035706bd0cb57fe08bc9912d25337b91331..f30a6badeb6cbac6b1e3a574f6cf39dc311fcafa 100644 --- a/feeds.css +++ b/feeds.css @@ -17,6 +17,24 @@ background: #d5e9f2; } +.feeds-mapping-form tr.mapping-property { + border-bottom: none; +} +.feeds-mapping-form tr.mapping-property td { + padding: 0.2rem 1rem; + vertical-align: middle; + border: 0; +} +.feeds-mapping-form tr.mapping-property-last { + border-bottom: 3px double #e6e4df; +} +.feeds-mapping-form tr.mapping-property-first td { + padding-top: 0.5rem; +} +.feeds-mapping-form tr.mapping-property-last td { + padding-bottom: 0.5rem; +} + .feeds-feed-type-secondary-settings { clear: both; } diff --git a/src/Form/MappingForm.php b/src/Form/MappingForm.php index 0113df9ef864b2ff2fbd5051754c84d2650785b5..348abe75cbf00c446035467a9a699991ad64c33b 100644 --- a/src/Form/MappingForm.php +++ b/src/Form/MappingForm.php @@ -4,6 +4,7 @@ namespace Drupal\feeds\Form; use Drupal\Component\Plugin\PluginManagerInterface; use Drupal\Component\Utility\Html; +use Drupal\Component\Utility\SortArray; use Drupal\Core\Config\Entity\ConfigEntityStorageInterface; use Drupal\Core\Form\FormBase; use Drupal\Core\Form\FormState; @@ -154,19 +155,7 @@ class MappingForm extends FormBase { $form['#suffix'] = '</div>'; $form['#attached']['library'][] = 'feeds/feeds'; - $table = [ - '#type' => 'table', - '#header' => [ - $this->t('Source'), - $this->t('Target'), - $this->t('Summary'), - $this->t('Configure'), - $this->t('Unique'), - $this->t('Remove'), - ], - '#sticky' => TRUE, - ]; - + $table = []; foreach ($feed_type->getMappings() as $delta => $mapping) { $table[$delta] = $this->buildRow($form, $form_state, $mapping, $delta); } @@ -213,9 +202,149 @@ class MappingForm extends FormBase { } } + // In the after build callback, put the mapping form in a table. + $form['mappings']['#after_build'][] = [$this, 'afterBuild']; + return $form; } + /** + * Puts mapping form in a table, with one row per property. + */ + public function afterBuild($element, FormStateInterface $form_state) { + $element['#type'] = 'table'; + $header = [ + 'source' => $this->t('Source'), + 'target' => $this->t('Target'), + 'summary' => $this->t('Summary'), + 'configure' => $this->t('Configure'), + 'unique' => $this->t('Unique'), + 'remove' => $this->t('Remove'), + ]; + $element['#header'] = []; + foreach ($header as $key => $label) { + $element['#header'][$key] = [ + 'class' => $key, + 'data' => $label, + ]; + } + $element['#sticky'] = TRUE; + + $rows = []; + // Loop through all mapping rows. + foreach (Element::children($element) as $index) { + // Check if the form element is a mapping row. One form element that is + // not a mapping row is the row for adding a new mapping target. + if (!isset($element[$index]['map'])) { + // Not a mapping row, but set to turn off striping to prevent that this + // row gets a css class called 'odd' or 'even'. + $element[$index]['#attributes']['no_striping'] = TRUE; + continue; + } + + $properties = Element::children($element[$index]['map']); + // Count how many properties there are for the current mapping, this is + // used to span some columns across multiple rows. + $property_count = count($properties); + // Keep track of on which number of property we are. Some columns only + // need a value for the first property row. + $property_delta = 0; + + // Loop through all properties of the current mapping row. + foreach ($properties as $property) { + $row = [ + '#attributes' => [ + 'class' => ['mapping-property'], + 'data-drupal-selector' => 'edit-mappings-' . $index . '-' . $property, + // Prevent css classes from 'odd' or 'even' being added as that + // could make it harder to see which table rows belong to the same + // mapping row. + 'no_striping' => TRUE, + ], + '#weight' => $element[$index]['#weight'], + '#parents' => $element['#parents'] + [ + $index . '.' . $property, + ], + '#array_parents' => $element['#array_parents'] + [ + $index . '.' . $property, + ], + ]; + // Add a css class for the mapping's first property. + if ($property_delta === 0) { + $row['#attributes']['class'][] = 'mapping-property-first'; + } + // Add a css class for the mapping's last property. + if ($property_delta == $property_count - 1) { + $row['#attributes']['class'][] = 'mapping-property-last'; + } + + // Source column. + $row['source'] = $element[$index]['map'][$property]; + + // Target column. + $row['target'] = $element[$index]['targets'][$property]; + + // Summary and configure columns, these are only displayed once per + // mapping row. + if ($property_delta === 0) { + $row['summary'] = $element[$index]['settings'] + [ + '#wrapper_attributes' => [ + 'rowspan' => $property_count, + ], + ]; + $row['configure'] = $element[$index]['configure'] + [ + '#wrapper_attributes' => [ + 'rowspan' => $property_count, + ], + ]; + } + + // Unique column. Not every property can be configured as unique. + if (isset($element[$index]['unique'][$property])) { + $row['unique'] = $element[$index]['unique'][$property]; + } + else { + $row['unique'] = [ + '#markup' => '', + '#parents' => $element['#parents'] + [ + $index . '.' . $property, + 'unique', + ], + '#array_parents' => $element['#array_parents'] + [ + $index . '.' . $property, + 'unique', + ], + ]; + } + + // Remove column. This is only displayed once per mapping row. + if ($property_delta === 0) { + $row['remove'] = $element[$index]['remove'] + [ + '#wrapper_attributes' => [ + 'rowspan' => $property_count, + ], + ]; + } + + $rows[$index . '.' . $property] = $row; + $property_delta++; + } + + // Remove the old built mapping row. + unset($element[$index]); + } + + $element = array_merge($element, $rows); + + // Set weight of "add" row. + $element['add']['#weight'] = 100; + + // And sort again. + uasort($element, [SortArray::class, 'sortByWeightProperty']); + + return $element; + } + /** * Builds a single mapping row. * @@ -263,6 +392,7 @@ class MappingForm extends FormBase { $target_definition = MissingTargetDefinition::create(); } + // If a config wheel is clicked, check which one was clicked. $ajax_delta = -1; $triggering_element = (array) $form_state->getTriggeringElement() + ['#op' => '']; if ($triggering_element['#op'] === 'configure') { @@ -271,11 +401,6 @@ class MappingForm extends FormBase { $row = ['#attributes' => ['class' => ['draggable', 'tabledrag-leaf']]]; $row['map'] = ['#type' => 'container']; - $row['targets'] = [ - '#theme' => 'item_list', - '#items' => [], - '#attributes' => ['class' => ['target']], - ]; if ($target_definition instanceof MissingTargetDefinition) { $row['#attributes']['class'][] = 'missing-target'; @@ -287,6 +412,8 @@ class MappingForm extends FormBase { unset($mapping['map'][$column]); continue; } + + // Source selection. $row['map'][$column] = [ 'select' => [ '#type' => 'select', @@ -298,8 +425,8 @@ class MappingForm extends FormBase { ]; $this->buildCustomSourceForms($row['map'][$column], $form_state, $delta, $column); + // Target label. $label = Html::escape($target_definition->getLabel() . ' (' . $mapping['target'] . ')'); - if (count($mapping['map']) > 1) { $desc = $target_definition->getPropertyLabel($column); } @@ -309,7 +436,7 @@ class MappingForm extends FormBase { if ($desc) { $label .= ': ' . $desc; } - $row['targets']['#items'][] = $label; + $row['targets'][$column] = ['#markup' => $label]; } $default_button = [ @@ -326,6 +453,7 @@ class MappingForm extends FormBase { $row['configure']['#markup'] = ''; if ($plugin && $this->pluginHasSettingsForm($plugin, $form_state)) { if ($delta == $ajax_delta) { + // The settings form is open. $row['settings'] = $plugin->buildConfigurationForm([], $form_state); $row['settings']['actions'] = [ '#type' => 'actions', @@ -347,6 +475,7 @@ class MappingForm extends FormBase { $row['#attributes']['class'][] = 'feeds-mapping-settings-editing'; } else { + // The settings form is closed. $row['settings'] = [ '#parents' => ['config_summary', $delta], ] + $this->buildSummary($plugin); @@ -363,7 +492,7 @@ class MappingForm extends FormBase { if (!empty($summary)) { $row['settings'] = [ '#parents' => ['config_summary', $delta], - ] + $this->buildSummary($plugin); + ] + $summary; } } @@ -755,7 +884,7 @@ class MappingForm extends FormBase { $plugin = $this->customSourcePluginManager->createInstance($custom_source_plugin_id, [ 'feed_type' => $this->feedType, ]); - $element = $form['mappings'][$delta]['map'][$column][$select]; + $element = $form['mappings'][$delta . '.' . $column]['source'][$select]; $plugin->validateConfigurationForm($element, $plugin_state); // Move errors to form_state above.