You are here

ScssFontFamily.php in SCSS Compiler 1.0.x

File

src/Element/ScssFontFamily.php
View source
<?php

namespace Drupal\compiler_scss\Element;

use Drupal\Component\Utility\Html;
use Drupal\Component\Utility\NestedArray;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Render\Element\FormElement;

/**
 * A form element to represent font families.
 *
 * Copyright (C) 2021  Library Solutions, LLC (et al.).
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * @FormElement("compiler_scss_font_family")
 */
class ScssFontFamily extends FormElement {

  /**
   * Ajax callback for the "Add another item" button.
   *
   * This returns the new element markup to replace the now obsolete element
   * markup with the newly added element child.
   *
   * @param array $form
   *   The form render array.
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   The current state of the form.
   */
  public static function addMoreAjax(array $form, FormStateInterface $form_state) {

    // Check if a triggering element can be found.
    if ($element = $form_state
      ->getTriggeringElement()) {

      // Go one level up in the form, to the container.
      $parents = array_slice($element['#array_parents'], 0, -1);
      $element = NestedArray::getValue($form, $parents);
      return $element;
    }
  }

  /**
   * Submission handler for the "Add another item" button.
   *
   * This method increments the total number of children for the element.
   *
   * @param array $form
   *   The form render array.
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   The current state of the form.
   */
  public static function addMoreSubmit(array $form, FormStateInterface $form_state) {

    // Check if a triggering element can be found.
    if ($element = $form_state
      ->getTriggeringElement()) {

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

      // Increment the total number of children by 1.
      $count = $form_state
        ->get($key = "{$element['#name']}_children");
      $form_state
        ->set($key, $count + 1);
      $form_state
        ->setRebuild();
    }
  }

  /**
   * Cleans a value for use in element production & storage.
   *
   * @param mixed $value
   *   The value to clean.
   *
   * @return array|null
   *   A zero-indexed array of element values.
   */
  protected static function cleanValue($value) {

    // Only allow an array of element delta values.
    if (is_array($value) && ($value = array_filter($value, 'strlen'))) {
      return array_values(array_filter($value, 'is_int', ARRAY_FILTER_USE_KEY));
    }
    return NULL;
  }

  /**
   * {@inheritdoc}
   */
  public function getInfo() {
    return [
      '#children' => [],
      '#element_validate' => [
        [
          get_class(),
          'validateFontFamily',
        ],
      ],
      '#input' => TRUE,
      '#maxlength' => NULL,
      '#required' => FALSE,
      '#size' => 60,
      '#theme' => 'container',
      '#theme_wrappers' => [
        'form_element',
      ],
      '#title' => NULL,
      '#tree' => TRUE,
      '#process' => [
        [
          get_class(),
          'processFontFamily',
        ],
        [
          get_class(),
          'processAjaxForm',
        ],
      ],
    ];
  }

  /**
   * Attempt to extract the value of the supplied element.
   *
   * @param array $element
   *   The element for which to attempt to extract a value.
   *
   * @return array|null
   *   The requested value from the supplied element.
   */
  protected static function getValue(array $element) {
    $value = $element['#value'] ?? NULL;
    return self::cleanValue($value);
  }

  /**
   * Process the element before it gets rendered in the form.
   *
   * @param array $element
   *   The element to process before being rendered.
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   The current state of the form.
   * @param array $complete_form
   *   The complete form.
   *
   * @return array
   *   The resulting element after having been processed.
   */
  public static function processFontFamily(array &$element, FormStateInterface $form_state, array &$complete_form) {

    // Create a unique ID for this element.
    $wrapper_id = implode('-', $element['#parents'] + [
      'add-more-wrapper',
    ]);
    $wrapper_id = Html::getUniqueId($wrapper_id);

    // Add the wrapper to the element so that it can be replaced via AJAX.
    $element['#prefix'] = '<div id="' . $wrapper_id . '">';
    $element['#suffix'] = '</div>';

    // Fetch the current value of the element.
    $value = self::getValue($element);

    // Check if the number of children for this element can be initialized based
    // on the current value of the element.
    if (!$form_state
      ->get($key = "{$element['#name']}_children")) {

      // If the value is an array, use its count as the initial value.
      $form_state
        ->set($key, is_array($value) ? count($value) : 0);
    }

    // Construct a child textfield for each delta in the element.
    for ($delta = 0; $delta < $form_state
      ->get($key); ++$delta) {
      $default = NULL;

      // Attempt to extract the current value for this textfield delta.
      if (is_array($value) && array_key_exists($delta, $value)) {
        $default = $value[$delta];
      }

      // Create the textfield for this delta in the element.
      $element[$delta] = [
        '#type' => 'textfield',
        '#name' => "{$element['#name']}[{$delta}]",
        '#maxlength' => $element['#maxlength'],
        '#size' => $element['#size'],
        '#title' => $element['#title'],
        '#title_display' => 'none',
        '#default_value' => $default,
      ];

      // Add the textfield to the array of children for this element.
      $element['#children'][$delta] =& $element[$delta];
    }

    // Create an "Add another item" button to add more textfields via AJAX.
    $element['add_more'] = [
      '#type' => 'submit',
      '#name' => "{$element['#name']}[add_more]",
      '#value' => t('Add another item'),
      '#limit_validation_errors' => [
        [
          $element['#name'],
        ],
      ],
      '#submit' => [
        [
          get_class(),
          'addMoreSubmit',
        ],
      ],
      '#ajax' => [
        'callback' => [
          get_class(),
          'addMoreAjax',
        ],
        'wrapper' => $wrapper_id,
        'effect' => 'fade',
      ],
    ];

    // Add the button to the array of children for this element.
    $element['#children']['add_more'] =& $element['add_more'];
    return $element;
  }

  /**
   * Validate this element's value on form submission.
   *
   * @param array $element
   *   The element that should be validated.
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   The current state of the form.
   * @param array $complete_form
   *   The complete form.
   */
  public static function validateFontFamily(array &$element, FormStateInterface $form_state, array &$complete_form) {

    // Store the sanitized element value in the form state.
    $form_state
      ->setValueForElement($element, self::getValue($element));
  }

  /**
   * {@inheritdoc}
   */
  public static function valueCallback(&$element, $input, FormStateInterface $form_state) {

    // Populate the element with a default value if none already exists.
    $element += [
      '#default_value' => NULL,
    ];

    // Check if the element's default value should be used in lieu of an input.
    if ($input === FALSE) {

      // Replace the input with the element's default value if FALSE.
      $input = $element['#default_value'];
    }
    return self::cleanValue($input);
  }

}

Classes

Namesort descending Description
ScssFontFamily A form element to represent font families.