diff --git a/parameters.module b/parameters.module index 1dff8c64d914c4372094ab711bae407f83a4e7f2..c005d7389e9618293622b60674613655bdf73051 100644 --- a/parameters.module +++ b/parameters.module @@ -59,7 +59,7 @@ function parameters_entity_translation_insert(EntityInterface $entity) { * @return array * Associative array with the role id as the key and the role name as value. */ -function _parameters__parameters_user_role_names($members_only = FALSE, $permission = NULL): array { +function _parameters_user_role_names($members_only = FALSE, $permission = NULL): array { return array_map(function ($item) { return $item->label(); }, _parameters_user_roles($members_only, $permission)); diff --git a/src/Internals/ItemSortTrait.php b/src/Internals/ItemSortTrait.php new file mode 100644 index 0000000000000000000000000000000000000000..29f82f7a92457e7132aaa67c7cffb3789393e93d --- /dev/null +++ b/src/Internals/ItemSortTrait.php @@ -0,0 +1,133 @@ +<?php + +namespace Drupal\parameters\Internals; + +use Drupal\Core\Form\FormStateInterface; + +/** + * Trait providing helpers for sorting parameter items. + */ +trait ItemSortTrait { + + /** + * The wrapper ID for refreshind the list via Ajax. + * + * @var string + */ + protected string $ajaxWrapperId = 'parameters-items-order-wrapper'; + + protected function ajaxHandlerForOrderFormElement(): array { + return [ + '#ajax' => [ + 'wrapper' => $this->ajaxWrapperId, + 'callback' => [static::class, 'orderFormElementAjax'], + 'method' => 'replaceWith', + ], + ]; + } + + /** + * Ajax handler for order form element. + * + * @param array &$form + * The form array. + * @param \Drupal\Core\Form\FormStateInterface $form_state + * The form state. + */ + public static function orderFormElementAjax(array &$form, FormStateInterface $form_state): array { + return $form['parameter']['settings']['order']; + } + + /** + * Builds a form element for sorting selected parameter items. + * + * @param string $key + * The configuration key that identifies the selected items. + * @param array $options + * The available options used to be displayed in the form. + * @param array &$form + * The form array. + * @param \Drupal\Core\Form\FormStateInterface $form_state + * The form state. + */ + protected function buildOrderFormElementForSelected(string $key, array $options, array &$form, FormStateInterface $form_state) { + $user_input = &$form_state->getUserInput(); + $selected = !empty($user_input) && isset($user_input[$key]) ? array_keys(array_filter($user_input[$key])) : ($this->configuration[$key] ?? []); + + $form['order'] = [ + '#prefix' => '<div id="' . $this->ajaxWrapperId . '">', + '#suffix' => '</div>', + ]; + if (empty($selected)) { + $form['order'] += ['#markup' => '']; + return; + } + + $form['order'] += [ + '#type' => 'table', + '#attributes' => ['id' => 'parameters-items-order'], + '#title' => $this->t('Order'), + '#tabledrag' => [ + [ + 'action' => 'order', + 'relationship' => 'sibling', + 'group' => 'item-order-weight', + ], + ], + '#input' => FALSE, + '#theme_wrappers' => ['form_element'], + '#weight' => 40, + ]; + + $submitted_orders = !empty($user_input) && isset($user_input['order']) ? $user_input['order'] : []; + $max_weight = 0; + foreach ($submitted_orders as $submitted_order) { + $max_weight = max($max_weight, $submitted_order['weight'] ?? 0); + } + foreach ($selected as $item) { + $item_weight = $submitted_orders[$item]['weight'] ?? (++$max_weight); + $label = $options[$item] ?? $item; + $form['order'][$item]['#attributes']['class'][] = 'draggable'; + $form['order'][$item]['#weight'] = $item_weight; + $form['order'][$item]['label'] = [ + '#markup' => $label, + ]; + $form['order'][$item]['weight'] = [ + '#type' => 'weight', + '#title' => $this->t('Weight for @title', ['@title' => $label]), + '#title_display' => 'invisible', + '#delta' => 10, + '#default_value' => $item_weight, + '#attributes' => ['class' => ['item-order-weight']], + ]; + } + } + + /** + * Submit handler for sorting selected parameter items. + * + * @param array $selected + * The selected parameter items. + * @param array &$form + * The form array. + * @param \Drupal\Core\Form\FormStateInterface $form_state + * The form state. + */ + protected function submitOrderFormElementForSelected(array &$selected, array &$form, FormStateInterface $form_state) { + if ($form_state->hasValue('order')) { + $order = $form_state->getValue('order'); + usort($selected, function($a, $b) use ($order) { + $left = $order[$a]['weight'] ?? 0; + $right = $order[$b]['weight'] ?? 0; + if ($left < $right) { + return -1; + } + if ($left > $right) { + return 1; + } + return 0; + }); + } + } + +} diff --git a/src/Plugin/Parameter/Bundles.php b/src/Plugin/Parameter/Bundles.php index 3b1946f02c7f6301b78d0c4e7efbf59751b2a16a..4680d6a7d80cc698265769eb4c7c47d41a250ec1 100644 --- a/src/Plugin/Parameter/Bundles.php +++ b/src/Plugin/Parameter/Bundles.php @@ -12,6 +12,7 @@ use Drupal\Core\Cache\CacheableDependencyInterface; use Drupal\Core\Entity\EntityTypeManagerInterface; use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Session\AccountInterface; +use Drupal\parameters\Internals\ItemSortTrait; use Drupal\parameters\Plugin\ParameterBase; use Drupal\parameters\Plugin\ParameterInterface; use Drupal\parameters\Plugin\ParameterManager; @@ -30,6 +31,8 @@ use Symfony\Component\DependencyInjection\ContainerInterface; */ class Bundles extends ParameterBase implements PropertyParameterInterface, DependentPluginInterface, AccessibleInterface, CacheableDependencyInterface { + use ItemSortTrait; + /** * {@inheritdoc} */ @@ -96,7 +99,10 @@ class Bundles extends ParameterBase implements PropertyParameterInterface, Depen '#options' => $options, '#default_value' => $this->configuration['selected'] ?? [], '#required' => FALSE, - ]; + ] + $this->ajaxHandlerForOrderFormElement(); + + $this->buildOrderFormElementForSelected('selected', $options, $form, $form_state); + return $form; } @@ -105,6 +111,9 @@ class Bundles extends ParameterBase implements PropertyParameterInterface, Depen */ public function submitConfigurationForm(array &$form, FormStateInterface $form_state) { $selected = array_keys(array_filter($form_state->getValue('selected', []))); + + $this->submitOrderFormElementForSelected($selected, $form, $form_state); + $this->configuration['selected'] = $selected; } diff --git a/src/Plugin/Parameter/Fields.php b/src/Plugin/Parameter/Fields.php index b67e5e7eb22e9fae27eef594b9df63f8852e06ba..d5c84037632a7ffaf7a54398a56fe6884b7cbcf2 100644 --- a/src/Plugin/Parameter/Fields.php +++ b/src/Plugin/Parameter/Fields.php @@ -15,6 +15,7 @@ use Drupal\Core\Entity\EntityTypeManagerInterface; use Drupal\Core\Extension\ModuleHandlerInterface; use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Session\AccountInterface; +use Drupal\parameters\Internals\ItemSortTrait; use Drupal\parameters\Plugin\ParameterBase; use Drupal\parameters\Plugin\ParameterInterface; use Drupal\parameters\Plugin\ParameterManager; @@ -33,6 +34,8 @@ use Symfony\Component\DependencyInjection\ContainerInterface; */ class Fields extends ParameterBase implements PropertyParameterInterface, DependentPluginInterface, AccessibleInterface, CacheableDependencyInterface { + use ItemSortTrait; + /** * {@inheritdoc} */ @@ -166,7 +169,9 @@ class Fields extends ParameterBase implements PropertyParameterInterface, Depend '#weight' => 20, '#prefix' => '<div id="' . $wrapper_id . '">', '#suffix' => '</div>', - ]; + ] + $this->ajaxHandlerForOrderFormElement(); + + $this->buildOrderFormElementForSelected('selected', $field_options, $form, $form_state); return $form; } @@ -205,6 +210,9 @@ class Fields extends ParameterBase implements PropertyParameterInterface, Depend $bundle = $form_state->getValue('bundle', '_none'); $this->configuration['bundle'] = $bundle === '_none' ? NULL : $bundle; $selected = array_keys(array_filter($form_state->getValue('selected', []))); + + $this->submitOrderFormElementForSelected($selected, $form, $form_state); + $this->configuration['selected'] = $selected; } diff --git a/src/Plugin/Parameter/Roles.php b/src/Plugin/Parameter/Roles.php index d3019599f267cd2441bb4631755b7b67e08daed7..f52693242511a5458f26e3cbe4633bc88e8bc478 100644 --- a/src/Plugin/Parameter/Roles.php +++ b/src/Plugin/Parameter/Roles.php @@ -11,6 +11,7 @@ use Drupal\Core\Cache\CacheableDependencyInterface; use Drupal\Core\Entity\EntityTypeManagerInterface; use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Session\AccountInterface; +use Drupal\parameters\Internals\ItemSortTrait; use Drupal\parameters\Plugin\ParameterBase; use Drupal\parameters\Plugin\ParameterInterface; use Drupal\parameters\Plugin\ParameterManager; @@ -30,6 +31,8 @@ use Symfony\Component\DependencyInjection\ContainerInterface; */ class Roles extends ParameterBase implements PropertyParameterInterface, DependentPluginInterface, AccessibleInterface, CacheableDependencyInterface { + use ItemSortTrait; + /** * {@inheritdoc} */ @@ -100,7 +103,10 @@ class Roles extends ParameterBase implements PropertyParameterInterface, Depende '#options' => $options, '#default_value' => $this->configuration['selected'] ?? [], '#required' => FALSE, - ]; + ] + $this->ajaxHandlerForOrderFormElement(); + + $this->buildOrderFormElementForSelected('selected', $options, $form, $form_state); + return $form; } @@ -109,6 +115,9 @@ class Roles extends ParameterBase implements PropertyParameterInterface, Depende */ public function submitConfigurationForm(array &$form, FormStateInterface $form_state) { $selected = array_keys(array_filter($form_state->getValue('selected', []))); + + $this->submitOrderFormElementForSelected($selected, $form, $form_state); + $this->configuration['selected'] = $selected; } diff --git a/src/Plugin/Parameter/Types.php b/src/Plugin/Parameter/Types.php index fb6743f1c024c486f3b989f1440674074fa0e596..1e4f649ccfbb89a5e05317253831944275f5cc29 100644 --- a/src/Plugin/Parameter/Types.php +++ b/src/Plugin/Parameter/Types.php @@ -9,6 +9,7 @@ use Drupal\Core\Access\AccessResult; use Drupal\Core\Entity\EntityTypeManagerInterface; use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Session\AccountInterface; +use Drupal\parameters\Internals\ItemSortTrait; use Drupal\parameters\Plugin\ParameterBase; use Drupal\parameters\Plugin\ParameterInterface; use Drupal\parameters\Plugin\ParameterManager; @@ -26,6 +27,8 @@ use Symfony\Component\DependencyInjection\ContainerInterface; */ class Types extends ParameterBase implements PropertyParameterInterface, DependentPluginInterface, AccessibleInterface { + use ItemSortTrait; + /** * {@inheritdoc} */ @@ -89,7 +92,10 @@ class Types extends ParameterBase implements PropertyParameterInterface, Depende '#options' => $options, '#default_value' => $this->configuration['selected'] ?? [], '#required' => FALSE, - ]; + ] + $this->ajaxHandlerForOrderFormElement(); + + $this->buildOrderFormElementForSelected('selected', $options, $form, $form_state); + return $form; } @@ -98,6 +104,9 @@ class Types extends ParameterBase implements PropertyParameterInterface, Depende */ public function submitConfigurationForm(array &$form, FormStateInterface $form_state) { $selected = array_keys(array_filter($form_state->getValue('selected', []))); + + $this->submitOrderFormElementForSelected($selected, $form, $form_state); + $this->configuration['selected'] = $selected; }