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

src/GridStackAttribute.php
<?php

namespace Drupal\gridstack;

use Drupal\Component\Serialization\Json;
use Drupal\Component\Utility\Html;
use Drupal\blazy\Blazy;
use Drupal\gridstack\Entity\GridStack;

/**
 * Provides GridStack attribute utilities.
 */
class GridStackAttribute {

  /**
   * Returns the wrapper attributes.
   */
  public static function prepare($optionset, array &$settings) {
    $nameshort = $settings['nameshort'];
    $attributes = empty($settings['attributes']) ? [] : self::parse($settings['attributes']);

    Blazy::containerAttributes($attributes, $settings);

    // ID is only relevant for JS layouts, not Bootstrap/Foundation ones.
    if (empty($settings['use_framework'])) {
      $attributes['id'] = $settings['id'];
    }
    else {
      if (!empty($settings['vm'])) {
        $attributes['class'][] = 'is-vm is-vm--' . $settings['vm'];
      }
    }

    // Add debug class for admin usages.
    if (!empty($settings['debug']) || !empty($settings['_access_ipe'])) {
      $attributes['class'][] = 'gridstack--debug';
      if (!empty($settings['_access_ipe'])) {
        $attributes['class'][] = 'is-gs-lb';
      }
    }

    // Adds wrapper classes for static grid Bootstrap/ Foundation, or js-driven.
    if (!empty($settings['wrapper_classes']) && is_string($settings['wrapper_classes'])) {
      self::parseClasses($attributes, $settings['wrapper_classes']);
    }

    // Empty it after being processed to not leak to children.
    unset($settings['attributes'], $settings['wrapper_classes'], $settings['fw_classes'], $settings['vm']);

    // Bail out if GridStack is being disabled such as for Isotope layouts.
    if (!empty($settings['ungridstack'])) {
      return $attributes;
    }

    // Only if using navice CSS Grid or original library layouts.
    if ($settings['use_js']) {
      // Adds attributes for js-driven layouts.
      // Gets options.breakpoints.sm.[width, column, image_style, grids], etc.
      $exclude_image_style = empty($settings['_admin']);
      $columns = $optionset->getJson('breakpoints');
      if ($responsives = array_filter($optionset->getBreakpoints())) {
        $data = [];
        foreach (GridStack::getConstantBreakpoints() as $breakpoint) {
          $responsive_grids = $optionset->getJsonSummaryBreakpoints($breakpoint, $exclude_image_style);
          $has_width = isset($responsives[$breakpoint]['width']) && $responsives[$breakpoint]['width'] > -1;
          if ($has_width && $responsive_grids) {
            $data[$responsives[$breakpoint]['width']] = Json::decode($responsive_grids);
          }
        }
        $attributes['data-' . $nameshort . '-data'] = $data ? Json::encode($data) : '';
      }

      // Add the required configuration as JSON object.
      // Supports dynamic like Isotope to have both native CSS and JS layouts.
      // Cannot rely on the library grid-stack-static, since not always there.
      // The .is-gs-layout can be used dynamically during Isotope filtering when
      // native CSS Grid is enabled to support them both as needed.
      $attributes['data-' . $nameshort . '-breakpoints'] = strpos($columns, '{"":12}') !== FALSE ? '' : $columns;
      $attributes['data-' . $nameshort . '-config'] = $optionset->getJson('settings');
      $attributes['data-' . $nameshort . '-column'] = (int) $settings['column'];
      $attributes['class'][] = empty($settings['gridnative']) ? 'gridstack--gs is-gs-layout' : 'gridstack--native';

      // Breakpoint related data-attributes helpers.
      if ($optionset->getSetting('minWidth')) {
        $attributes['data-min-width'] = (int) $optionset->getSetting('minWidth');
      }
    }

    return $attributes;
  }

  /**
   * Provides dynamic GridStack JS grid attributes.
   */
  public static function jsBox($optionset, array &$settings, $current = 'grids') {
    // The CSS BG rule is not good at .box, but .box__content for CSS framework
    // due to overlapping containers unlike js-driven layout which have margins.
    $attributes = empty($settings['use_framework']) ? self::regionAttributes($settings, $current) : [];
    $nameshort  = $settings['nameshort'];
    $id         = isset($settings['delta']) ? $settings['delta'] : 0;
    $nid        = isset($settings['nested_delta']) ? $settings['nested_delta'] : NULL;
    $grids      = $optionset->getEndBreakpointGrids($current);
    $nodes      = ['x', 'y', 'width', 'height'];

    // Native CSS Grid layout doesn't need DOM rect positions, just dimensions.
    if (!empty($settings['gridnative'])) {
      unset($nodes[0], $nodes[1]);
    }

    // Nested grids.
    if (isset($settings['nested_delta']) && $current == 'nested') {
      foreach ($nodes as $key) {
        if (!isset($grids[$id][$nid])) {
          continue;
        }

        $attributes['data-' . $nameshort . '-' . $key] = isset($grids[$id][$nid][$key]) ? (int) $grids[$id][$nid][$key] : 0;
      }
    }
    else {
      // The root element grids.
      foreach ($nodes as $key) {
        $attributes['data-' . $nameshort . '-' . $key] = isset($grids[$id][$key]) ? (int) $grids[$id][$key] : 0;
      }
    }

    return $attributes;
  }

  /**
   * Provides static Bootstrap/ Foundation CSS grid attributes.
   */
  public static function cssBox($optionset, array &$settings, $current = 'grids') {
    // The CSS BG rule is not good at .box, but .box__content for CSS framework
    // due to overlapping containers unlike js-driven layout which have margins.
    $attributes = empty($settings['use_framework']) ? self::regionAttributes($settings, $current) : [];
    $framework  = $settings['framework'];
    $points     = GridStackDefault::breakpoints();

    // Bootstrap 4 uses flexbox with `col` class, and has `xl` breakpoint.
    if ($framework == 'bootstrap') {
      $attributes['class'][] = 'col';
    }
    // @todo Foundation 6:
    // https://get.foundation/sites/docs/xy-grid.html
    // https://get.foundation/sites/docs/grid.html
    // Top container: grid-container full|fluid.
    // Container: grid-x grid-padding-x small-up-2 medium-up-4 large-up-6
    // Cell: cell auto small-6 medium-8 large-2 large-auto.
    elseif (strpos($framework, 'foundation') !== FALSE) {
      unset($points['xs'], $points['xl']);
    }

    $unique = $optionset->optimizeGridWidths($settings, $current);
    foreach ($points as $point => $label) {
      if (!isset($unique[$point])) {
        continue;
      }

      $prefix = $suffix = '';
      if (strpos($framework, 'bootstrap') !== FALSE) {
        // Specific to XS: Bootstrap 3: col-xs-*, Bootstrap 4: col-*.
        $prefix = 'col-' . $point . '-';
        if ($framework == 'bootstrap' && $point == 'xs') {
          $prefix = 'col-';
        }
      }
      elseif (strpos($framework, 'foundation') !== FALSE) {
        $prefix = $label . '-';
        $suffix = ' columns';
      }

      $attributes['class'][] = $prefix . $unique[$point] . $suffix;
    }

    return $attributes;
  }

  /**
   * Provides both CSS grid and js-driven attributes configurable via UI.
   */
  public static function regionAttributes(array &$settings, $current = 'grids') {
    $id         = isset($settings['delta']) ? $settings['delta'] : 0;
    $nid        = isset($settings['nested_delta']) ? $settings['nested_delta'] : NULL;
    $regions    = isset($settings['regions']) ? $settings['regions'] : [];
    $rid        = $current == 'nested' ? GridStackDefault::regionId($id . '_' . $nid) : GridStackDefault::regionId($id);
    $region     = empty($regions[$rid]) ? [] : $regions[$rid];
    $attributes = [];

    if ($region) {
      if (isset($region['attributes']) && !empty($region['attributes'])) {
        $attributes = self::parse($region['attributes']);
        unset($settings['regions'][$rid]['attributes']);
      }

      if (!empty($region['wrapper_classes'])) {
        self::parseClasses($attributes, $region['wrapper_classes']);
        unset($settings['regions'][$rid]['wrapper_classes']);
      }
    }

    return $attributes;
  }

  /**
   * Parses the given string classes.
   */
  private static function parseClasses(array &$attributes, $string = '') {
    $classes = array_map('\Drupal\Component\Utility\Html::cleanCssIdentifier', explode(' ', $string));
    $attributes['class'] = empty($attributes['class']) ? array_unique($classes) : array_unique(array_merge($attributes['class'], $classes));
  }

  /**
   * Parses the given string attribute.
   */
  private static function parse($string = '') {
    $attributes = [];
    // Given role|navigation,data-something|some value.
    $layout_attributes = explode(',', $string);
    foreach ($layout_attributes as $attribute) {
      $replaced_attribute = $attribute;

      // @nottodo: Token support.
      // No need to whitelist as this already requires admin priviledges.
      // With admin privileges, the site is already taken over before playing
      // around with attributes. However provides few basic sanitizations to
      // satisfy curious playful editors.
      if (strpos($attribute, '|') !== FALSE) {
        list($key, $value) = array_pad(array_map('trim', explode('|', $replaced_attribute, 2)), 2, NULL);
        $key = mb_substr($key, 0, 2) === 'on' ? 'data-' . $key : $key;
        $attributes[$key] = Html::cleanCssIdentifier(strip_tags($value));
      }
    }

    return $attributes;
  }

}

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

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