custom_field-1.0.x-dev/src/Plugin/CustomField/FieldType/DurationOptionsTrait.php

src/Plugin/CustomField/FieldType/DurationOptionsTrait.php
<?php

declare(strict_types=1);

namespace Drupal\custom_field\Plugin\CustomField\FieldType;

use Drupal\Component\Utility\NestedArray;
use Drupal\Core\Ajax\AjaxResponse;
use Drupal\Core\Ajax\InvokeCommand;
use Drupal\Core\Ajax\ReplaceCommand;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Render\Element;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\Core\StringTranslation\TranslatableMarkup;

/**
 * Trait for list options field settings shared across field types.
 */
trait DurationOptionsTrait {

  use StringTranslationTrait;

  /**
   * Adds options settings to the field settings form.
   *
   * @param array<string, mixed> $element
   *   The form element array to add settings to (passed by reference).
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   The form state.
   * @param array<string, mixed> $settings
   *   Current field settings.
   */
  protected function durationOptions(array &$element, FormStateInterface $form_state, array $settings): void {
    $name = $this->getName();
    $form_state_key = $name . '_duration_options';
    $granularity_date = 'days';
    $granularity_datetime = 'days:hours:minutes';
    $granularity = $granularity_datetime;
    if ($this->getDataType() === 'daterange' && $this->getDatetimeType() !== 'datetime') {
      $granularity = $granularity_date;
    }
    if (!$form_state->get($form_state_key)) {
      $form_state->set($form_state_key, $settings['duration_options'] ?? []);
    }

    $allowed_values = $form_state->get($form_state_key);
    $wrapper_id = $name . '-duration-options-wrapper';

    $element['duration_options'] = [
      '#type' => 'fieldset',
      '#title' => $this->t('Duration options'),
      '#element_validate' => [[static::class, 'validateAllowedValues']],
      '#allowed_values' => $allowed_values,
      '#prefix' => '<div id="' . $wrapper_id . '">',
      '#suffix' => '</div>',
      '#field_name' => $name,
    ];
    $element['duration_options']['table'] = [
      '#type' => 'table',
      '#header' => [
        '',
        $this->t('@duration', [
          '@duration' => $granularity === $granularity_datetime ? 'Duration' : 'Days',
        ]),
        $this->t('Label'),
        $this->t('Delete'),
        $this->t('Weight'),
      ],
      '#attributes' => [
        'id' => $name . 'allowed-values-order',
        'data-field-list-table' => TRUE,
        'class' => ['allowed-values-table'],
      ],
      '#tabledrag' => [
        [
          'action' => 'order',
          'relationship' => 'sibling',
          'group' => 'weight',
        ],
      ],
    ];
    if (empty($allowed_values)) {
      $element['duration_options']['table']['#header'] = [
        $this->t('Items'),
      ];
    }

    foreach ($allowed_values as $delta => $allowed_value) {
      $element['duration_options']['table'][$delta] = [
        '#weight' => $delta,
      ];
      // Draggable rows have issues on empty value rows.
      if (!empty($allowed_value['key'])) {
        $element['duration_options']['table'][$delta]['#attributes']['class'] = [
          'draggable',
        ];
      }
      $element['duration_options']['table'][$delta]['handle'] = [
        '#type' => 'markup',
        '#markup' => '',
      ];
      $element['duration_options']['table'][$delta]['key'] = [
        '#type' => 'custom_field_duration',
        '#maxlength' => 255,
        '#title' => $this->t('Duration'),
        '#default_value' => $allowed_value['key'] ?? '',
        '#element_validate' => [[static::class, 'validateAllowedValue']],
        '#required' => TRUE,
        '#duration_granularity' => $granularity,
        '#attributes' => [
          'class' => ['duration-options'],
        ],
      ];
      $element['duration_options']['table'][$delta]['label'] = [
        '#type' => 'textfield',
        '#maxlength' => 255,
        '#title' => $this->t('Label'),
        '#default_value' => $allowed_value['label'] ?? '',
        '#required' => TRUE,
        '#attributes' => [
          'placeholder' => $this->t('e.g. 30 days'),
        ],
      ];
      if ($granularity === $granularity_date) {
        $element['duration_options']['table'][$delta]['label']['#title_display'] = 'invisible';
      }
      $element['duration_options']['table'][$delta]['delete'] = [
        '#type' => 'submit',
        '#value' => $this->t('Delete'),
        '#name' => 'remove_row_button__' . $name . '__' . $delta,
        '#delta' => $delta,
        '#submit' => [[static::class, 'deleteSubmit']],
        '#limit_validation_errors' => [],
        '#ajax' => [
          'callback' => [static::class, 'deleteAjax'],
          'wrapper' => $wrapper_id,
          'effect' => 'fade',
        ],
      ];
      $element['duration_options']['table'][$delta]['weight'] = [
        '#type' => 'weight',
        '#title' => $this->t('Weight for row @number', ['@number' => $delta + 1]),
        '#title_display' => 'invisible',
        '#delta' => 50,
        '#default_value' => 0,
        '#attributes' => [
          'class' => ['weight'],
        ],
      ];
    }
    $element['duration_options']['add_more_allowed_values'] = [
      '#type' => 'submit',
      '#name' => 'add_more_allowed_values__' . $name,
      '#value' => $this->t('Add another item'),
      '#attributes' => [
        'class' => ['field-add-more-submit'],
        'data-field-list-button' => TRUE,
      ],
      // Allow users to add another row without requiring existing rows to have
      // values.
      '#limit_validation_errors' => [],
      '#submit' => [[static::class, 'addMoreSubmit']],
      '#ajax' => [
        'callback' => [static::class, 'addMoreAjax'],
        'wrapper' => $wrapper_id,
        'effect' => 'fade',
        'progress' => [
          'type' => 'throbber',
          'message' => $this->t('Adding a new item...'),
        ],
      ],
    ];
  }

  /**
   * Adds a new option.
   *
   * @param array $form
   *   The form array to add elements to.
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   The current state of the form.
   */
  public static function addMoreSubmit(array $form, FormStateInterface $form_state): void {
    $button = $form_state->getTriggeringElement();
    $user_input = $form_state->getUserInput();
    $parents = array_slice($button['#array_parents'], 0, -2);
    $name = end($parents);
    $form_state_key = $name . '_duration_options';

    // Get the input values to retain the ordered positioning.
    $input_allowed_values = NestedArray::getValue($user_input, [...$parents, 'duration_options', 'table']);
    $input_allowed_values[] = [
      'key' => '',
      'label' => '',
      'weight' => 0,
    ];
    $filtered_values = array_map(function ($item) {
      return [
        'key' => $item['key'],
        'label' => $item['label'],
      ];
    }, $input_allowed_values);

    // Reset the user input.
    NestedArray::setValue($user_input, [...$parents, 'duration_options', 'table'], array_values($input_allowed_values));
    $form_state->setUserInput($user_input);

    // Set the new value to the form state.
    $form_state->set($form_state_key, array_values($filtered_values));

    $element = NestedArray::getValue($form, array_slice($button['#array_parents'], 0, -1));
    // Set the new value on the table element.
    $form_state->setValueForElement($element, array_values($filtered_values));

    $form_state->setRebuild();
  }

  /**
   * Ajax callback for the "Add another item" button.
   */
  public static function addMoreAjax(array $form, FormStateInterface $form_state): AjaxResponse {
    $button = $form_state->getTriggeringElement();

    // Go one level up in the form.
    $element = NestedArray::getValue($form, array_slice($button['#array_parents'], 0, -1));
    $wrapper_id = $button['#ajax']['wrapper'];

    $response = new AjaxResponse();
    $response->addCommand(new ReplaceCommand('#' . $wrapper_id, $element));

    // Set the focus to the first input of the newly added item.
    $children = Element::children($element['table']);
    $last_child = end($children);
    $focus_input = $element['table'][$last_child]['key']['days']['#name'];
    $response->addCommand(new InvokeCommand(':input[name="' . $focus_input . '"]', 'focus'));

    return $response;
  }

  /**
   * Deletes a row/option.
   *
   * @param array $form
   *   The form array to add elements to.
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   The current state of the form.
   */
  public static function deleteSubmit(array $form, FormStateInterface $form_state): void {
    $button = $form_state->getTriggeringElement();
    $user_input = $form_state->getUserInput();
    $parents = array_slice($button['#array_parents'], 0, -4);
    $name = end($parents);
    $form_state_key = $name . '_duration_options';
    $remove_key = (int) $button['#delta'];

    // Get the input values to retain the ordered positioning.
    $input_allowed_values = NestedArray::getValue($user_input, [...$parents, 'duration_options', 'table']) ?? $form_state->get($form_state_key);
    unset($input_allowed_values[$remove_key]);
    $filtered_values = array_map(function ($item) {
      return [
        'key' => $item['key'],
        'label' => $item['label'],
      ];
    }, $input_allowed_values);

    // Set the new value to the form state.
    $form_state->set($form_state_key, array_values($filtered_values));

    // Reset the user input.
    NestedArray::setValue($user_input, [...$parents, 'duration_options', 'table'], array_values($input_allowed_values));
    $form_state->setUserInput($user_input);

    // Set the new value on the table element.
    $element = NestedArray::getValue($form, array_slice($button['#array_parents'], 0, -3));
    $form_state->setValueForElement($element, array_values($filtered_values));

    $form_state->setRebuild();
  }

  /**
   * Ajax callback for per row delete button.
   */
  public static function deleteAjax(array $form, FormStateInterface $form_state): AjaxResponse {
    $button = $form_state->getTriggeringElement();
    $wrapper_id = $button['#ajax']['wrapper'];
    $element = NestedArray::getValue($form, array_slice($button['#array_parents'], 0, -3));

    $response = new AjaxResponse();
    $response->addCommand(new ReplaceCommand('#' . $wrapper_id, $element));
    $children = Element::children($element['table']);

    // If there are remaining children, set the focus to the first input of the
    // last item.
    if (count($children) > 0) {
      $last_child = end($children);
      $focus_input = $element['table'][$last_child]['key']['days']['#name'] ?? '';
    }
    else {
      $focus_input = $element['add_more_allowed_values']['#name'] ?? '';
    }
    if (!empty($focus_input)) {
      $response->addCommand(new InvokeCommand(':input[name="' . $focus_input . '"]', 'focus'));
    }

    return $response;
  }

  /**
   * Render API callback: Validates the allowed values of an options field.
   *
   * This function is assigned as an #element_validate callback.
   *
   * @param array<string, mixed> $element
   *   An associative array containing the properties and children of the
   *   generic form element.
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   The current state of the form for the form this element belongs to.
   *
   * @see \Drupal\Core\Render\Element\FormElementBase::processPattern()
   */
  public static function validateAllowedValues(array $element, FormStateInterface $form_state): void {
    $items = array_filter(array_map(function ($item) use ($element) {
      $current_element = $element['table'][$item];
      $key_has_input = isset($current_element['key']['#value']) && $current_element['key']['#value'] !== '';
      $label_has_input = isset($current_element['label']['#value']) && $current_element['label']['#value'] !== '';
      if (!$key_has_input && !$label_has_input) {
        return NULL;
      }
      return [
        'key' => $current_element['key']['#value'],
        'label' => $current_element['label']['#value'],
      ];
    }, Element::children($element['table'])), function ($item) {
      return $item !== NULL;
    });
    if ($reordered_items = $form_state->getValue([...$element['#parents'], 'table'])) {
      uksort($items, function ($a, $b) use ($reordered_items) {
        $a_weight = $reordered_items[$a]['weight'] ?? 0;
        $b_weight = $reordered_items[$b]['weight'] ?? 0;
        return $a_weight <=> $b_weight;
      });
    }

    // Check that keys are valid for the field type.
    $unique_keys = [];
    foreach ($items as $value) {
      // Make sure each key is unique.
      if (!in_array($value['key'], $unique_keys)) {
        $unique_keys[] = $value['key'];
      }
      else {
        $form_state->setError($element, new TranslatableMarkup('Allowed values list: duplicate key @key.', ['@key' => $value['key']]));
      }
    }

    $form_state->setValueForElement($element, $items);
  }

  /**
   * Render API callback: Validates the allowed key value of an options field.
   *
   * This function is assigned as an #element_validate callback.
   *
   * @param array<string, mixed> $element
   *   An associative array containing the properties and children of the
   *   generic form element.
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   The current state of the form for the form this element belongs to.
   */
  public static function validateAllowedValue(array $element, FormStateInterface $form_state): void {
  }

}

Главная | Обратная связь

drupal hosting | друпал хостинг | it patrol .inc