You are here

GridStackStylizer.php in GridStack 8.2

Namespace

Drupal\gridstack

File

src/GridStackStylizer.php
View source
<?php

namespace Drupal\gridstack;

use Drupal\Component\Utility\NestedArray;
use Drupal\Core\Cache\Cache;

/**
 * Implements GridStackStylizerInterface.
 */
class GridStackStylizer extends GridStackPluginManagerBase implements GridStackStylizerInterface {

  /**
   * {@inheritdoc}
   */
  protected static $path = 'Plugin/gridstack/stylizer';

  /**
   * {@inheritdoc}
   */
  protected static $interface = 'Drupal\\gridstack\\GridStackStylizerPluginInterface';

  /**
   * {@inheritdoc}
   */
  protected static $annotation = 'Drupal\\gridstack\\Annotation\\GridStackStylizer';

  /**
   * {@inheritdoc}
   */
  protected static $key = 'gridstack_stylizer';

  /**
   * The admin regions.
   *
   * @var array
   */
  protected $regions = [];

  /**
   * The loaded stylizer plugin.
   *
   * @var array
   */
  protected $loadStyle;

  /**
   * The style plugin.
   *
   * @var \Drupal\gridstack\Plugin\gridstack\stylizer\Style
   */
  protected $style;

  /**
   * The builder plugin.
   *
   * @var \Drupal\gridstack\Plugin\gridstack\stylizer\Builder
   */
  protected $builder;

  /**
   * The form plugin.
   *
   * @var \Drupal\gridstack\Plugin\gridstack\stylizer\Form
   */
  protected $form;

  /**
   * {@inheritdoc}
   */
  public function style(array $configuration = [], $reload = FALSE) {
    if (!isset($this->style) || $reload) {
      $this->style = $this
        ->loadStyle('style', $configuration, $reload);
    }
    return $this->style;
  }

  /**
   * Sets the style plugin.
   */
  private function setStyle(array $configuration = [], $reload = FALSE) {
    $this->style = $this
      ->loadStyle('style', $configuration, $reload);
    return $this;
  }

  /**
   * Returns a particular style from settings.
   *
   * @todo rename to ::getConfiguredStyle to avoid confusion?
   */
  public function getStyle($key, array $settings) {
    return $this
      ->style()
      ->getStyle($key, $settings);
  }

  /**
   * Returns a particular style from settings.
   */
  public function getConfiguredStyle($key, array $settings) {
    return $this
      ->style()
      ->getStyle($key, $settings);
  }

  /**
   * {@inheritdoc}
   */
  public function builder(array $configuration = [], $reload = FALSE) {
    if (!isset($this->builder) || $reload) {
      $this->builder = $this
        ->loadStyle('builder', $configuration, $reload);
    }
    return $this->builder;
  }

  /**
   * Sets the builder plugin.
   */
  private function setBuilder(array $configuration = [], $reload = FALSE) {
    $this->builder = $this
      ->loadStyle('builder', $configuration, $reload);
    return $this;
  }

  /**
   * {@inheritdoc}
   */
  public function form(array $configuration = [], $reload = FALSE) {
    if (!isset($this->form) || $reload) {
      $this->form = $this
        ->loadStyle('form', $configuration, $reload);
    }
    return $this->form;
  }

  /**
   * Sets the form plugin.
   */
  public function setForm(array $configuration = [], $reload = FALSE) {
    $this->form = $this
      ->loadStyle('form', $configuration, $reload);
    return $this;
  }

  /**
   * {@inheritdoc}
   */
  private function loadStyle($id = 'style', array $configuration = [], $reload = FALSE) {
    if (!isset($this->loadStyle[$id]) || $reload) {
      $this->loadStyle[$id] = $this
        ->load($id, $configuration);
    }
    return $this->loadStyle[$id];
  }

  /**
   * Provides gridstack skins and libraries.
   */
  public function attach(array &$load, array $attach = []) {
    if (!empty($attach['_stylizer'])) {
      $load['library'][] = 'gridstack/stylizer';
      $this
        ->setAttachments($load, $attach);
    }
  }

  /**
   * Prepares the settings, selector and active styles.
   */
  public function prepare(array &$element, array &$attributes, array &$settings, $optionset) {

    // Provides configurable attributes and background media if so configured.
    // Runs before ::containerAttributes to pass the media settings.
    $this
      ->setStyle($settings);
    if ($media = $this
      ->style()
      ->buildMedia($attributes, $settings)) {
      $element['#preface']['media'] = $media;
    }

    // Provides admin utilities.
    if (!empty($settings['_ipe'])) {
      $this
        ->setBuilder($settings);
      $this->regions = $this
        ->builder()
        ->regions($element);
      if ($links = $this
        ->builder()
        ->getVariantEditor($settings, $optionset)) {
        $pos = $this
          ->config('editor_pos') == 'bottom' ? '#bottom' : '#aside';
        $element[$pos]['layout_editor'] = $links;
      }
    }
  }

  /**
   * Modifies item content and attributes.
   */
  public function modifyItem($delta, array &$settings, array &$content, array &$attributes, array &$content_attributes) {
    $rid = isset($settings['rid']) ? $settings['rid'] : -1;
    if (empty($settings['_ipe']) && !empty($settings['contentless'])) {
      $content['box'] = [];
    }

    // Layout Builder only output for granted users.
    if (!empty($settings['_ipe']) && isset($this->regions[$rid])) {
      $this
        ->builder()
        ->adminAttributes($content['box'], $content_attributes, $settings, $this->regions);
    }

    // Provides background media to support contentless, if any.
    if ($media = $this
      ->style()
      ->buildMedia($content_attributes, $settings)) {
      $content['box']['preface'] = $media;
      $content['box']['preface']['#weight'] = -100;
    }
  }

  /**
   * Builds aggregated styles based on the provided settings.
   *
   * This is not saved to public directory due to too small a portion.
   * Not a big deal, as it is always unique to an edited page, not re-usable.
   * Aside it is recommended by lighthouse relevant for the above-fold rules.
   */
  public function rootStyles(array &$element, array $styles, array $settings) {
    if ($rules = $this
      ->style()
      ->parseStyles($styles, TRUE)) {
      $css = implode('', reset($rules));
      $element['#attached']['html_head'][] = [
        [
          '#tag' => 'style',
          '#value' => $css,
          '#weight' => 1,
        ],
        'gridstack-style-' . key($rules),
      ];
      if (!empty($settings['_ipe'])) {
        $element['#attributes'] = isset($element['#attributes']) ? $element['#attributes'] : [];
        $this
          ->builder()
          ->rootAttributes($element['#attributes'], $styles);
      }
    }
  }

  /**
   * Parses the formatted styles per region based on settings.
   */
  public function styles(array &$attributes, array $settings) {
    $styles = $this
      ->style()
      ->getStyles($settings);
    if ($styles && ($data = $this
      ->style()
      ->getSelector($settings, '', $styles))) {

      // Only inline it to DOM element if at LB edit pages.
      if (!empty($settings['_ipe'])) {
        $this
          ->builder()
          ->inlineAttributes($attributes, $data);
      }
      return $data;
    }
    return [];
  }

  /**
   * Provides both CSS grid and js-driven attributes configurable via UI.
   */
  public function attributes(array &$attributes, array $settings) {
    $this
      ->style()
      ->attributes($attributes, $settings);
  }

  /**
   * Returns the module feature CSS classes, not available at CSS frameworks.
   */
  public function getInternalClasses() {
    return $this
      ->style()
      ->getInternalClasses();
  }

  /**
   * Returns classes that can be used for select options.
   */
  public function getMergedClasses($flatten = FALSE, array $framework_classes = []) {
    $cid = $flatten ? 'gridstack_classes_flattened' : 'gridstack_classes_grouped';
    if ($cache = $this->cacheBackend
      ->get($cid)) {
      return $cache->data;
    }
    $output = [];
    if ($classes = $this
      ->mergeFrameworkAndInternalClasses($framework_classes)) {
      $grouped = $ungrouped = [];
      foreach ($classes as $group => $data) {
        $grouped[$group] = $data;
        $items = [];
        foreach ($data as $datum) {
          $items[] = $datum;
        }
        $ungrouped = NestedArray::mergeDeep($ungrouped, $items);
      }

      // Do not use array_unique() here, was processed per group instead.
      $output = $flatten ? $ungrouped : $grouped;
      $this->moduleHandler
        ->alter('gridstack_classes_preset', $output, $grouped, $ungrouped, $flatten);
      ksort($output);
      $count = count($output);
      $tags = Cache::buildTags($cid, [
        'count:' . $count,
      ]);
      $this->cacheBackend
        ->set($cid, $output, Cache::PERMANENT, $tags);
    }
    return $output;
  }

  /**
   * Merges preset classes with the custom defined ones.
   */
  private function mergeFrameworkAndInternalClasses(array $classes = []) {
    $classes = $classes ? NestedArray::mergeDeep($this
      ->getInternalClasses(), $classes) : $this
      ->getInternalClasses();
    if ($fw_classes = $this
      ->config('fw_classes')) {
      $fw_classes = array_map('trim', explode("\n", $fw_classes));
      foreach ($fw_classes as $fw_class) {
        if (strpos($fw_class, '|') !== FALSE) {
          list($group, $group_class) = array_pad(array_map('trim', explode("|", $fw_class, 2)), 2, NULL);
          $group_classes = array_map('trim', explode(" ", $group_class));
          $new_group = [];
          foreach ($group_classes as $group_class) {
            $new_group[] = $group_class;
          }
          $new_group = isset($classes[$group]) ? NestedArray::mergeDeep($classes[$group], $new_group) : $new_group;
          $classes[$group] = array_unique($new_group);
        }
      }
    }
    return $classes;
  }

}

Classes

Namesort descending Description
GridStackStylizer Implements GridStackStylizerInterface.