gridstack-8.x-2.5/src/GridStackManager.php

src/GridStackManager.php
<?php

namespace Drupal\gridstack;

use Drupal\Component\Utility\NestedArray;
use Drupal\Core\Render\Element;
use Drupal\Core\Security\TrustedCallbackInterface;
use Drupal\blazy\Blazy;
use Drupal\blazy\BlazyManagerBase;
use Drupal\gridstack\Entity\GridStack;
use Symfony\Component\DependencyInjection\ContainerInterface;

/**
 * Implements GridStackManagerInterface.
 */
class GridStackManager extends BlazyManagerBase implements GridStackManagerInterface, TrustedCallbackInterface {

  /**
   * The GridStack optionset.
   *
   * @var \Drupal\gridstack\Entity\GridStack
   */
  protected $gridStackOptionset;

  /**
   * If should ungridstack, no js/css-driven layouts, just re-use templates.
   *
   * @var bool
   */
  protected $unGridStack = FALSE;

  /**
   * The gridstack skin manager service.
   *
   * @var \Drupal\gridstack\GridStackSkinManagerInterface
   */
  protected $skinManager;

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container) {
    $instance = parent::create($container);
    $instance->setSkinManager($container->get('gridstack.skin_manager'));
    return $instance;
  }

  /**
   * Returns gridstack skin manager service.
   */
  public function skinManager() {
    return $this->skinManager;
  }

  /**
   * Sets gridstack skin manager service.
   */
  public function setSkinManager(GridStackSkinManagerInterface $skin_manager) {
    $this->skinManager = $skin_manager;
    return $this;
  }

  /**
   * {@inheritdoc}
   */
  public static function trustedCallbacks() {
    return ['preRenderGridStack'];
  }

  /**
   * {@inheritdoc}
   */
  public function attach(array $attach = []) {
    $load = parent::attach($attach);

    $this->skinManager->attach($load, $attach);
    $this->skinManager->attachSkin($load, $attach);

    $this->moduleHandler->alter('gridstack_attach', $load, $attach);
    return $load;
  }

  /**
   * {@inheritdoc}
   */
  public function boxAttributes(array &$settings, $current = 'grids') {
    // Allows extenders to provide own grid attributes.
    if (!empty($settings['ungridstack'])) {
      return [];
    }

    return empty($settings['use_js']) ? GridStackAttribute::cssBox($this->gridStackOptionset, $settings, $current) : GridStackAttribute::jsBox($this->gridStackOptionset, $settings, $current);
  }

  /**
   * {@inheritdoc}
   */
  public function buildItems(array $build, array $regions = []) {
    $settings = array_filter($build['settings']);
    $grids    = $this->gridStackOptionset->getEndBreakpointGrids();
    $items    = [];

    // Cleans up container setting wrapper to not leak to children.
    unset($settings['wrapper']);

    foreach ($build['items'] as $delta => $item) {
      $box        = isset($item['box']) ? $item['box'] : [];
      $attributes = isset($item['attributes']) ? $item['attributes'] : [];
      $settings   = isset($item['settings']) ? array_merge($settings, $item['settings']) : $settings;
      $rid        = GridStackDefault::regionId($delta);
      $content    = [
        'box'     => $box,
        'caption' => isset($item['caption']) ? $item['caption'] : [],
      ];

      $settings['delta'] = $settings['root_delta'] = $delta;
      $attributes = NestedArray::mergeDeep($attributes, $this->boxAttributes($settings, 'grids'));
      $content_attributes = [];

      if ($grids && empty($settings['ungridstack'])) {
        if (!empty($settings['use_framework'])) {
          $content_attributes = GridStackAttribute::regionAttributes($settings, 'grids');
        }

        // Skips if more than we can chew, otherwise broken grid anyway.
        // The grids is a fixed layout blueprint. The items is dynamic contents.
        // Dynamic contents must obey the blueprint to stay harmonious.
        if (!isset($grids[$delta])) {
          continue;
        }

        // Layout Builder or Panels IPE only output for granted users.
        if (!empty($settings['_access_ipe']) && $regions && isset($regions[$rid])) {
          GridStackBuilder::adminAttributes($content['box'], $attributes, $content_attributes, $settings, $regions, $rid);
        }

        // Node contains the main grids/boxes.
        $nested_grids = $this->gridStackOptionset->getNestedGridsByDelta($delta);
        $is_nested = array_filter($nested_grids);

        // Overrides wrapper via UI, if so configured.
        if (isset($settings['regions'], $settings['regions'][$rid])) {
          $settings = array_merge($settings, $settings['regions'][$rid]);
        }

        // Nested grids with preserved indices even if empty so to layout.
        // Only Bootstrap/ Foundation has nested grids, not js-driven layouts.
        if ($is_nested && !empty($box) && isset($box[0]['box'])) {
          $attributes['class'][] = 'box--nester';
          $settings['nested'] = TRUE;
          $settings['root'] = FALSE;
          $settings['use_inner'] = FALSE;
          $content['box'] = $this->buildNestedItems($item, $delta, $nested_grids, $settings, $regions);
        }
        else {
          $settings['nested'] = FALSE;
          $settings['use_inner'] = TRUE;
        }
      }

      $items[] = $this->buildItem($content, $delta, $attributes, $content_attributes, $settings);
      unset($content);
    }

    return $items;
  }

  /**
   * Provides nested items if so configured.
   */
  protected function buildNestedItems($item, $delta, $grids, $settings, $regions = []) {
    $items = [];
    $nested = $item['box'];

    // The nested elements.
    foreach (array_keys($grids) as $gid) {
      $rid = GridStackDefault::regionId($delta . '_' . $gid);
      $settings['nested_delta'] = $gid;
      $nested_box = $nested[$gid]['box'];
      $nested_settings = isset($nested[$gid]['settings']) ? array_merge($settings, $nested[$gid]['settings']) : $settings;
      $nested_attributes = isset($nested[$gid]['attributes']) ? $nested[$gid]['attributes'] : [];
      $content_attributes = [];
      if (!empty($settings['use_framework'])) {
        $content_attributes = GridStackAttribute::regionAttributes($settings, 'nested');
      }

      $nested_settings['nested_id'] = ($delta + 1) . '-' . ($gid + 1);
      $nested_settings['use_inner'] = TRUE;

      if (isset($nested_settings['regions'], $nested_settings['regions'][$rid])) {
        $nested_settings = array_merge($nested_settings, $nested_settings['regions'][$rid]);
      }

      // Layout Builder or Panels IPE integration only output for granted users.
      if (!empty($settings['_access_ipe']) && $regions && isset($regions[$rid])) {
        GridStackBuilder::adminAttributes($nested_box, $nested_attributes, $content_attributes, $nested_settings, $regions, $rid);
      }

      $nested_attributes = NestedArray::mergeDeep($nested_attributes, $this->boxAttributes($nested_settings, 'nested'));
      $nested_attributes['class'][] = 'box--nested';

      $items[] = $this->buildItem(['box' => $nested_box], $gid, $nested_attributes, $content_attributes, $nested_settings);
    }

    // Provides nested gridstack, gridstack within gridstack, if so configured.
    $box = [];
    $attributes = isset($item['wrapper_attributes']) ? $item['wrapper_attributes'] : [];
    $attributes['class'][] = 'gridstack--nested';

    $box['content'] = [
      '#theme'      => 'gridstack',
      '#items'      => $items,
      '#optionset'  => $this->gridStackOptionset,
      '#settings'   => $settings,
      '#attributes' => $attributes,
    ];

    // Update box with nested boxes.
    return $box;
  }

  /**
   * {@inheritdoc}
   */
  public function buildItem($item, $delta, array $attributes, array $content_attributes = [], array $settings = []) {
    return [
      '#theme'              => 'gridstack_box',
      '#item'               => $item,
      '#delta'              => $delta,
      '#attributes'         => $attributes,
      '#content_attributes' => $content_attributes,
      '#settings'           => $settings,
    ];
  }

  /**
   * Provides multi-breakpoint image styles.
   *
   * Overrides fallback breakpoint image_style with grid image_style.
   * This tells theme_blazy() to respect different image style per item.
   */
  public function buildImageStyleMultiple(array &$settings, $delta = 0) {
    foreach ($settings['breakpoints'] as $key => &$breakpoint) {
      if (isset($breakpoint['image_style']) && !empty($breakpoint['grids'][$delta]) && !empty($breakpoint['grids'][$delta]['image_style'])) {
        $breakpoint['image_style'] = $breakpoint['grids'][$delta]['image_style'];
      }

      // Overrides image style to use a defined image style per grid item.
      // This allows each individual box to have different image styles.
      if ($key == 'xl' && !empty($breakpoint['grids'][$delta]['image_style'])) {
        $settings['_dimensions'] = FALSE;
        $settings['image_style'] = $breakpoint['grids'][$delta]['image_style'];
      }
    }
  }

  /**
   * {@inheritdoc}
   */
  public function build(array $build = []) {
    foreach (['attached', 'layout'] + GridStackDefault::themeProperties() as $key) {
      $build[$key] = isset($build[$key]) ? $build[$key] : [];
    }

    $gridstack = [
      '#theme'      => 'gridstack',
      '#build'      => $build,
      '#pre_render' => [[$this, 'preRenderGridStack']],
      'items'       => [],
      // Required by Drupal\layout_builder\QuickEditIntegration since 8.7.3+.
      '#layout'     => $build['layout'],
    ];

    $this->moduleHandler->alter('gridstack_build', $gridstack, $build['settings']);
    return empty($build['items']) ? [] : $gridstack;
  }

  /**
   * Return the wrapper attributes.
   */
  public function prepareAttributes(array &$build) {
    $settings = &$build['settings'];
    return GridStackAttribute::prepare($this->gridStackOptionset, $settings);
  }

  /**
   * Returns the common settings inherited down to each item.
   */
  public function getGridStackSettings() {
    return array_intersect_key($this->configLoad('', 'gridstack.settings'), GridStackDefault::uiSettings());
  }

  /**
   * Build the HTML settings.
   */
  public function prepareSettings(array &$settings) {
    $settings += $this->getGridStackSettings() + GridStackDefault::htmlSettings();

    // Use static grid framework if so configured.
    $settings['ungridstack']   = empty($settings['ungridstack']) ? $this->unGridStack : $settings['ungridstack'];
    $settings['use_framework'] = !empty($settings['framework']) && $this->gridStackOptionset->getOption('use_framework');
    $settings['id']            = Blazy::getHtmlId('gridstack-' . $settings['optionset'], $settings['id']);
    $settings['library']       = $settings['_access_ipe'] ? $settings['library'] : '';
    $settings['column']        = $this->gridStackOptionset->getSetting('column') ?: 12;

    // Disable background and JS if using a CSS framework.
    if ($settings['use_framework']) {
      $settings['background'] = $settings['use_js'] = FALSE;
      // Admin UI always uses gridstack JS to build layouts regardless of this.
      $settings['use_framework'] = empty($settings['_admin']);
    }
    else {
      $settings['use_js'] = !empty($settings['root']) || !empty($settings['_admin']) || !empty($settings['gridnative']);
    }

    $settings['gridnative'] = empty($settings['use_framework']) && !empty($settings['gridnative']);
    if (empty($settings['breakpoints'])) {
      $this->gridStackOptionset->gridsJsonToArray($settings);
    }

    $keys = empty($settings['breakpoints']) ? [] : array_keys($settings['breakpoints']);
    $settings['_last'] = $keys ? end($keys) : 'xl';
  }

  /**
   * Returns the dummy box to measure cell height to fix aspect ratio.
   */
  protected function buildDummyItem() {
    $dummy['class'][] = 'gridstack__sizer sizer is-nixbox';
    $dummy['data-gs-height'] = 1;
    $dummy['data-gs-width'] = 1;
    $settings['use_inner'] = TRUE;

    return ['dummy' => $this->buildItem(NULL, 100, $dummy, [], $settings)];
  }

  /**
   * {@inheritdoc}
   */
  public function preRenderGridStack($element) {
    $build = $element['#build'];
    unset($element['#build']);

    // Build gridstack elements.
    $settings = &$build['settings'];
    $settings['_ipe'] = !empty($settings['_layouts']) || !empty($settings['_panels']);
    $is_ipe_page = isset($element['#attributes'], $element['#attributes']['data-layout-delta']) || (isset($element['#prefix']) && strpos($element['#prefix'], 'panels-ipe-content') !== FALSE);
    $settings['_access_ipe'] = $is_ipe_page && $settings['_ipe'];

    // Supports Blazy multi-breakpoint images if provided.
    if (!empty($settings['check_blazy']) && !empty($build['items'][0])) {
      $this->isBlazy($settings, $build['items'][0]);
    }

    // Prepare the settings.
    $this->gridStackOptionset = $build['optionset'] ?: GridStack::loadWithFallback($settings['optionset']);
    $this->prepareSettings($settings);

    // Adds regions for Layout Builder and Panels IPE integration.
    $regions     = $settings['_access_ipe'] ? GridStackBuilder::adminRegions($build, $element, $settings) : [];
    $attachments = $this->attach($settings);
    $attributes  = $this->prepareAttributes($build);

    // Adds the required elements for the template.
    $element['#attributes'] = empty($element['#attributes']) ? $attributes : NestedArray::mergeDeep($element['#attributes'], $attributes);
    $element['#optionset']  = $build['optionset'] = $this->gridStackOptionset;
    $element['#settings']   = $build['settings'] = $settings;
    $element['#attached']   = empty($build['attached']) ? $attachments : NestedArray::mergeDeep($build['attached'], $attachments);
    $element['#items']      = $this->buildItems($build, $regions);
    $element['#cache']      = $this->getCacheMetadata($build);
    $element['#postscript'] = $settings['use_js'] && empty($settings['ungridstack']) ? $this->buildDummyItem() : [];

    // Panels IPE, Layout Builder are happy, safe to free up wasted children.
    foreach (Element::children($element) as $child) {
      unset($element[$child]);
    }

    unset($build);
    return $element;
  }

}

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

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