You are here

GridStackVariantForm.php in GridStack 8.2

File

modules/gridstack_ui/src/Form/GridStackVariantForm.php
View source
<?php

namespace Drupal\gridstack_ui\Form;

use Drupal\Core\Form\FormStateInterface;
use Drupal\gridstack\Entity\GridStack;
use Drupal\gridstack\Entity\GridStackVariant;
use Drupal\gridstack\GridStackDefault;

/**
 * Extends base form for gridstack instance configuration form.
 */
class GridStackVariantForm extends GridStackForm {

  /**
   * {@inheritdoc}
   */
  protected $isVariant = TRUE;

  /**
   * {@inheritdoc}
   */
  protected $niceName = 'GridStackVariant';

  /**
   * {@inheritdoc}
   */
  protected $machineName = 'gridstack_variant';

  /**
   * {@inheritdoc}
   */
  public function form(array $form, FormStateInterface $form_state) {
    $form['#attributes']['class'][] = 'form--gridstack-variant';
    $form['#attached']['library'][] = 'gridstack/admin_variant';
    $source = $this->entity
      ->source();
    $gridstack = $source ? GridStack::load($source) : NULL;

    // @todo remove after another check.
    if ($source && $gridstack) {
      $this
        ->setGridStack($gridstack);
    }
    $form = parent::form($form, $form_state);
    if (!$this->entity
      ->source()) {
      return [];
    }
    unset($form['options']['settings'], $form['options']['use_framework']);
    $form['source'] = [
      '#type' => 'hidden',
      '#default_value' => $this->entity
        ->source(),
    ];
    $description = $this
      ->t('Height is pre-determined by grids, not contents.');
    if ($gridstack && $gridstack
      ->isFramework()) {
      $description = $this
        ->t('Height is determined later by contents, not grids. Floating grid rules apply. Index order matters to float correctly.');
    }
    $id = $this->entity
      ->id();
    $label = $this->entity
      ->label();
    $pid = $gridstack ? $gridstack->id : NULL;
    $form_wrapper_id = GridStackDefault::variantWrapperId($id);
    $request_uri = $this
      ->getRequest()
      ->getRequestUri();
    $parts = array_filter(explode('/', $request_uri));
    $gid = NULL;
    $pub = $this
      ->getRequest()->query
      ->get('pub', NULL);
    $label_dup = $this
      ->getRequest()->query
      ->get('label', NULL);
    $applied = !empty($pub) && $pub == $id;
    $applied_differently = !empty($pub) && $pub != $id;
    $variant_pub = $pub ? GridStackVariant::load($pub) : NULL;

    // Extracts gid from URL.
    $is_ajax = $parts[1] == 'gridstack';
    if (isset($parts[4]) && $parts[3] == $source) {
      $gid = $parts[4];
    }
    $links = [];
    $config = [
      'vid' => $id,
      'optionset' => $pid,
      'gid' => $gid,
      'pub' => $pub,
    ];
    $duplicated_vid = $form_state
      ->get('new_vid') ?: $this->entity
      ->getRandomizedId();
    $input = $form_state
      ->getUserInput();
    if (empty($duplicated_vid)) {
      $duplicated_vid = isset($input['new_vid']) ? $input['new_vid'] : '';
    }
    if ($duplicated_vid) {
      $config['dup'] = $duplicated_vid;
    }
    if ($gridstack && $gid) {
      $links = $this->manager
        ->stylizer()
        ->builder($config)
        ->getVariantLinks($config, $gridstack);
    }

    // @todo remove once duplicate action works correctly.
    $duplicate = isset($links['duplicate']) ? $links['duplicate'] : [];
    if ($duplicate) {
      $duplicate['#title'] = $this
        ->t('Save as New');
    }
    $form['#prefix'] = '<div id="' . $form_wrapper_id . '">';
    $form['#suffix'] = '</div>';
    $form['#attributes']['data-gs-gid'] = $gid;
    $form['variant_actions'] = [
      '#type' => 'container',
      '#attributes' => [
        'class' => [
          'form--gridstack-variant__actions',
        ],
      ],
      '#tree' => TRUE,
      '#weight' => -100,
      '#settings' => $config,
      '#access' => $is_ajax,
      'actions' => [
        '#type' => 'container',
        '#attributes' => [
          'class' => [
            'btn-group',
          ],
        ],
        'close' => [
          '#type' => 'button',
          '#value' => $this
            ->t('Close'),
          '#name' => 'close',
          '#ajax' => [
            'callback' => [
              $this,
              'closeVariant',
            ],
            'effect' => 'fade',
            'wrapper' => GridStackDefault::variantWrapperId($gid),
            'progress' => [
              'type' => 'throbber',
              'message' => $this
                ->t('Closing.'),
            ],
          ],
          '#attributes' => [
            'class' => [
              'btn',
              'btn-dark',
              'btn--variant-close',
            ],
          ],
        ],
        'delete' => [
          '#type' => 'button',
          '#value' => $this
            ->t('Delete'),
          '#name' => 'delete',
          '#ajax' => [
            'callback' => [
              $this,
              'deleteVariant',
            ],
            'effect' => 'fade',
            'wrapper' => $form_wrapper_id,
          ],
          '#attributes' => [
            'class' => [
              'btn',
              'btn-danger',
              'btn--variant-delete',
            ],
            'data-gs-vid' => '',
            'data-gs-variant-ajax' => 'delete',
            'data-gs-variant-message' => $this
              ->t('Deleting and reverting'),
          ],
        ],
        'revert' => [
          '#type' => 'button',
          '#value' => $this
            ->t('Revert'),
          '#name' => 'revert',
          '#access' => $applied || $applied_differently,
          '#ajax' => [
            'callback' => [
              $this,
              'revertVariant',
            ],
            'effect' => 'fade',
            'wrapper' => $form_wrapper_id,
          ],
          '#attributes' => [
            'class' => [
              'btn',
              'btn-warning',
              'btn--variant-revert',
            ],
            'data-gs-vid' => '',
            'data-gs-variant-ajax' => 'revert',
            'data-gs-variant-message' => $this
              ->t('Reverting'),
          ],
        ],
        'duplicate' => $duplicate,
        /* @todo use this when things are fixed.
           'duplicate' => [
             '#type' => 'button',
             '#value' => $this->t('Save as New'),
             '#name' => 'duplicate',
             '#ajax' => [
               'callback' => [$this, 'duplicateVariant'],
               'effect' => 'fade',
               'wrapper' => GridStackDefault::variantWrapperId($gid),
               'method' => 'html',
               'progress' => [
                 'type' => 'throbber',
                 'message' => $this->t('Duplicating.'),
               ],
             ],
             '#attributes' => [
               'class' => ['btn', 'btn-info', 'btn--variant-duplicate'],
               'data-gs-vid' => '',
               'data-gs-variant-nojs' => 'duplicate',
               'data-gs-variant-message' => $this->t('Saving as new'),
               'data-gs-update-icon' => 1,
             ],
             // @todo bad: '#limit_validation_errors' => [],
           ],
             */
        'save' => [
          '#type' => 'button',
          '#value' => $this
            ->t('Save'),
          '#name' => 'save',
          '#ajax' => [
            'callback' => [
              $this,
              'saveVariant',
            ],
            'effect' => 'fade',
            'wrapper' => GridStackDefault::variantWrapperId($gid),
            'method' => 'html',
            'progress' => [
              'type' => 'throbber',
              'message' => $this
                ->t('Saving.'),
            ],
          ],
          '#attributes' => [
            'class' => [
              'btn',
              'btn-success',
              'btn--variant-save',
            ],
            'data-gs-vid' => $id,
            'data-gs-variant-nojs' => 'save',
            'data-gs-variant-message' => $this
              ->t('Saving'),
            'data-gs-update-icon' => 1,
          ],
        ],
        'apply_trigger' => [
          '#type' => 'submit',
          '#value' => $this
            ->t('Apply'),
          '#name' => 'apply_trigger',
          '#attributes' => [
            'class' => [
              'btn',
              'btn-success',
              'btn--variant-apply-trigger',
            ],
            'data-gs-vid' => $id,
            'data-gs-variant-trigger' => 'apply',
            'data-gs-variant-message' => $this
              ->t('Applying variant'),
            'data-gs-update-icon' => 1,
          ],
        ],
        'apply' => [
          '#type' => 'button',
          '#value' => $this
            ->t('Apply'),
          '#name' => 'apply',
          '#ajax' => [
            'callback' => [
              $this,
              'saveVariant',
            ],
            'effect' => 'fade',
            'wrapper' => $form_wrapper_id,
            'progress' => [
              'type' => 'fullscreen',
              'message' => $this
                ->t('Applying variant.'),
            ],
          ],
          '#attributes' => [
            'class' => [
              'btn',
              'btn--variant-apply',
              'visually-hidden',
            ],
            'data-gs-vid' => $id,
            'data-gs-variant-ajax' => 'apply',
            'data-gs-variant-message' => $this
              ->t('Applying'),
            'data-gs-update-icon' => 1,
          ],
        ],
        'select' => isset($links['select']) ? $links['select'] : [],
      ],
      'update' => [
        '#type' => 'submit',
        '#value' => $this
          ->t('Update Icon'),
        '#name' => 'update',
        '#attributes' => [
          'class' => [
            'btn',
            'btn-secondary',
            'btn--variant-update',
          ],
          'data-gs-vid' => $id,
          'data-gs-variant-js' => 'update',
          'data-gs-variant-message' => $this
            ->t('Updating'),
          'data-gs-update-icon' => 1,
        ],
      ],
      'description' => [
        '#markup' => '<p><small>' . $description . '</small></p>',
        '#allowed_tags' => [
          'p',
          'small',
        ],
      ],
    ];
    $css = $this->manager
      ->stylizer()
      ->style()
      ->getVariantClass($label);
    $label_suffix = $id;

    // Adjust few things for Layout Builder AJAX displays.
    if ($is_ajax) {
      $label_suffix .= ' (';
      $label_suffix .= $applied ? $this
        ->t('applied') : $this
        ->t('draft -- not applied');
      if ($applied_differently && $variant_pub) {
        $label_suffix .= $this
          ->t(', currently applied: @label', [
          '@label' => $variant_pub
            ->label(),
        ]);
      }
      $label_suffix .= ')';
      $form['actions']['#access'] = FALSE;
      $form['description']['#access'] = FALSE;
      unset($form['label']['#prefix'], $form['name']['#suffix']);
      $form['label']['#attributes']['class'][] = 'form-text--label-variant';
      if ($label_dup) {
        $form_state
          ->setValue('label', $label_dup);
        $form['label']['#default_value'] = $label_dup;
      }
    }
    $form['name']['#machine_name']['source'] = [
      'id',
    ];
    $form['name']['#wrapper_attributes']['class'][] = 'visually-hidden';
    $form['label']['#description'] = $this
      ->t('Label for this variant, also used as CSS class for unique styling: <code>@css</code>. <br>Changing a variant will change it everywhere else. To be unique, clone and give unique memorable label accordingly.', [
      '@css' => $css,
    ]);
    $form['label']['#field_suffix'] = $label_suffix;
    $form['label']['#weight'] = -99;
    $form['label']['#title_display'] = 'visually-hidden';
    $form['label']['#attributes']['placeholder'] = $this
      ->t('Label');
    if ($gridstack && ($uri = $gridstack
      ->getIconUri())) {
      $icon = [
        '#theme' => 'image',
        '#uri' => $uri,
        '#alt' => $this
          ->t('Thumbnail'),
      ];
      if ($this
        ->currentUser()
        ->hasPermission('administer gridstack')) {
        $image = $icon;
        $icon = [
          '#type' => 'link',
          '#url' => $gridstack
            ->toUrl('edit-form'),
          '#title' => [
            '#markup' => $this->manager
              ->getRenderer()
              ->render($image),
            '#allowed_tags' => [
              'img',
            ],
          ],
        ];
      }
      $form['screenshot_original'] = [
        '#type' => 'container',
        'icon' => $icon,
        '#weight' => 100,
        '#attributes' => [
          'class' => [
            'form--gridstack__screenshot',
            'form--gridstack__screenshot--original',
          ],
        ],
      ];
    }
    return $form;
  }

  /**
   * Callback for cancelling a layout variant.
   */
  public function closeVariant(array $form, FormStateInterface $form_state) {
    $config = $form['variant_actions']['#settings'];
    $gridstack = $this
      ->gridStack();
    $editor = $this->manager
      ->stylizer()
      ->builder()
      ->getVariantEditor($config, $gridstack);
    return $editor['form'];
  }

  /**
   * Callback for deleting a layout variant.
   */
  public function deleteVariant(array $form, FormStateInterface $form_state) {
    $this->entity
      ->delete();
    return $form;
  }

  /**
   * Callback for reverting a layout variant to source layout.
   */
  public function revertVariant(array $form, FormStateInterface $form_state) {
    return $form;
  }

  /**
   * Callback for duplicating a layout variant.
   *
   * @fixme, no icon generated, duplicate worflow is not good.
   */
  public function duplicateVariant(array &$form, FormStateInterface $form_state) {
    $entity = $this->entity;
    $id = $entity
      ->getRandomizedId();
    $form_state
      ->set('new_vid', $id);
    $label = $form_state
      ->getValue('label') ?: $entity
      ->getLabelFromId($id);
    $options = $entity
      ->getOptions();
    unset($options['icon']);
    $clone = $entity
      ->createDuplicateVariant($id, $label, $options);
    $this
      ->setEntity($clone);
    $this->entity
      ->save();
    $id = $this->entity
      ->id();
    $form['variant_actions']['actions']['duplicate']['#attributes']['disabled'] = TRUE;
    $form['label']['#field_suffix'] = $id;
    $this
      ->validateForm($form, $form_state);
    $this
      ->save($form, $form_state);
    $form_wrapper_id = GridStackDefault::variantWrapperId($id);
    $form['#prefix'] = '<div id="' . $form_wrapper_id . '">';
    foreach ([
      'cancel',
      'duplicate',
      'revert',
      'update',
      'apply',
      'save',
    ] as $key) {
      $form['variant_actions']['actions'][$key]['#attributes']['data-gs-vid'] = $id;
      if (!in_array($key, [
        'cancel',
        'update',
      ])) {
        $form['variant_actions']['actions'][$key]['#ajax']['wrapper'] = $form_wrapper_id;
      }
    }
    return $form;
  }

  /**
   * Callback for saving a layout variant draft.
   */
  public function saveVariant(array &$form, FormStateInterface &$form_state) {
    $this
      ->validateForm($form, $form_state);
    $this
      ->save($form, $form_state);
    return $form;
  }

  /**
   * {@inheritdoc}
   */
  protected function getApplicableBreakpoints() {
    $gridstack = $this
      ->gridStack();
    $breakpoints = $gridstack ? [
      $gridstack
        ->getLastBreakpointKey(),
    ] : [
      'lg',
    ];

    // Allows alterations by those who can afford complication.
    $this->manager
      ->getModuleHandler()
      ->alter('gridstack_variant_applicable_breakpoints', $breakpoints, $gridstack, $this->engine, $this
      ->currentUser());
    return $breakpoints;
  }

  /**
   * {@inheritdoc}
   */
  public function buildEntity(array $form, FormStateInterface $form_state) {
    $entity = parent::buildEntity($form, $form_state);
    $request = $this
      ->getRequest();
    $request_uri = $request
      ->getRequestUri();
    $source = $entity
      ->source();
    if (empty($source)) {
      if ($this->operation == 'add' || $this->operation == 'duplicate') {
        $args = array_filter(explode('/', $request_uri));

        // Given non-ajax URL: /admin/structure/gridstack...
        if (isset($args[3]) && $args[3] == 'gridstack') {

          // The last parameter is add or duplicate, remove.
          array_pop($args);
          $source = end($args);
          if ($gridstack = GridStack::load($source)) {
            $entity
              ->set('source', $source);
            $this
              ->setGridStack($gridstack);
          }
        }
      }
    }
    return $entity;
  }

  /**
   * {@inheritdoc}
   */
  public function save(array $form, FormStateInterface $form_state) {
    $this->entity
      ->set('source', $this->entity
      ->source());
    parent::save($form, $form_state);
  }

}

Classes

Namesort descending Description
GridStackVariantForm Extends base form for gridstack instance configuration form.