Would love to see support for XY Grids.

Potentially a configuration option or separate set of templates that work for XY Grids.

Perhaps as a shim, add configuration for the "column" / "cell" classes?

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

scottsawyer created an issue. See original summary.

Saif Al-Dilaimi’s picture

any progress?

philosurfer’s picture

Let us start asking what needs to be done to support XY grids as this is the Zurb standard moving forward. ( >= 6.5)

philosurfer’s picture

After talking with my team and following up with the drupal.slack.com/#layouts channel, there are a number of things that need resolve to be compatible with Drupal 8.7+.

Specifically the attributes and classes need to use twig functions or else it breaks layout builder functionality.

The patch forthcoming should be added in an 8.7.x-dev release of this module.

philosurfer’s picture

Category: Feature request » Bug report
Priority: Normal » Critical
Status: Active » Needs work

moving this to a bug as it breaks zurb_foundation and layout builder functionality at this time.

philosurfer’s picture

Title: Support for Foundation > 6.4 XY Grids » Support for Foundation X-Y Grid and Layout Builder 8.7+
vinitk’s picture

Updated the Zurb foundation layout template markup with X-Y grid convention with internal padding in x-axis.

vinitk’s picture

Modified the Zurb foundation layout template markup with X-Y grid convention (without internal padding).

scottsawyer’s picture

I just wanted to update this issue:

I was frustrated with the limitations of the layouts provided Foundation Layouts for creating arbitrary grid cell widths, offsets, order, and I wanted to use XY Grid in my projects. I see xy_grid_layouts project addresses the latter challenge, but not really the former.

Rather than creating a layout for every conceivable combination of cell widths at every breakpoint, I decided to create fewer, more flexible layouts.

Example Use
Imagine if you wanted 3 columns:

<div class="cell small-6 medium-3 medium-order-1 medium-offset-1 large-4"></div>
<div class="cell small-6 medium-4 medium-order-0 large-4"></div>
<div class="cell small-12 medium-3 large-4"></div>

While this example is a bit contrived, designs don't always follow an expected pattern. Foundation provides a lot of classes to manipulate cells. It would be ridiculous to try to create every possible layout for every design requirement. Let's put the power in the hands of the site builder.

Features:

  • 4 templates ( 1 col, 2 col, 3 col, 4 col ). We can easily add support for more columns.
  • User selectable cell widths per cell per breakpoint ( small - 1, 2, 3, etc )
  • Support for cell order per breakpoint
  • Support for cell offset per breakpoint
  • Support for xlarge breakpoint

Since this module is currently in a private repo, and would need some additional cleanup before it would be ready for public consumption, I am just posting my layout plugin file and a template so the community can take a look and see if this is something that could be considered as an enhancement to this project. If nothing else, it may provide some inspiration to providing more flexible layouts.

If you are interested in collaborating, please let me know.


namespace Drupal\foundation_layouts\Plugin\Layout;

use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Plugin\PluginFormInterface;
use Drupal\Core\Layout\LayoutDefault;

/**
 * Layout class for Foundation Layouts.
 */
class FoundationLayouts extends LayoutDefault implements PluginFormInterface {

  /**
   * {@inheridoc}
   */
  public function __construct(array $configuration, $plugin_id, $plugin_definition) {
    parent::__construct($configuration, $plugin_id, $plugin_definition);
  }

  /**
   * {@inheritdoc}
   */
  public function defaultConfiguration() {
    return parent::defaultConfiguration() + [
      'wrappers' => [],
      'wrapper_classes' => '',
      'wrapper_container' => FALSE,
      'wrapper_gutters' => [],
      'sizes' => [],
      'order' => [],
      'offsets' => [],
    ];
  }

  /**
   * {@inheritdoc}
   */
  public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
    $configuration = $this->getConfiguration();
    $regions = $this->getPluginDefinition()->getRegions();
    $breakpoints = $this->_breakpoints();
    $region_sizes = $this->_region_sizes();
    $region_offsets = $this->_region_offsets();
    $region_orders = [];
    for($i = 0; $i < count($regions); $i++) {
      $region_orders[$i] = $i;
    }

    $form['attributes'] = [
      '#group' => 'additional_settings',
      '#type' => 'details',
      '#title' => $this->t('Wrapper attributes'),
      '#description' => $this->t('Attributes for the outermost element.'),
      '#tree' => TRUE,
    ];

    $form['attributes']['wrapper_classes'] = [
      '#type' => 'textfield',
      '#title' => $this->t('Wrapper classes'),
      '#description' => $this->t('Add additional classes to the outermost element.'),
      '#default_value' => $configuration['wrapper_classes'],
      '#weight' => 1,
    ];

    $form['attributes']['wrapper_gutters']['x'] = [
      '#type' => 'select',
      '#title' => $this->t('X-axis gutters'),
      '#description' => $this->t('How gutters are applied to columns on the X-axis.'),
      '#options' => ['none' => 'None', 'grid-margin-x' => 'Margin', 'grid-padding-x' => 'Padding'],
      '#default_value' => isset($configuration['wrapper_gutters']['x']) ? $configuration['wrapper_gutters']['x'] : 'none',
    ];

    $form['attributes']['wrapper_gutters']['y'] = [
      '#type' => 'select',
      '#title' => $this->t('Y-axis gutters'),
      '#description' => $this->t('How gutters are applied to columns on the Y-axis.'),
      '#options' => ['none' => 'None', 'grid-margin-y' => 'Margin', 'grid-padding-y' => 'Padding'],
      '#default_value' => isset($configuration['wrapper_gutters']['y']) ? $configuration['wrapper_gutters']['y'] : 'none',
    ];
    
    $form['attributes']['wrapper_container'] = [
      '#type' => 'checkbox',
      '#title' => $this->t('Contained wrapper'),
      '#description' => $this->t('If checked, the wrapper will contain the regions to a max width.'),
      '#default_value' => $configuration['wrapper_container'],
      '#weight' => 0,
    ];

    $form['regions'] = [
      '#type' => 'fieldset',
      '#title' => $this->t('Region Configuration'),
      '#description' => $this->t('Customize each region configuration.'),
      '#weight' => 3,
      '#tree' => TRUE,
    ];

    foreach ($regions as $region_name => $region_definition) {

      $form['regions'][$region_name] = [
        '#type' => 'details',
        '#title' => $this->t('Region: @region', ['@region' => $region_name]),
        '#group' => 'region_config',
        '#weight' => $i,
        '#tree' => TRUE,
      ];

      foreach($breakpoints as $bp_name => $bp_label) {

        $form['regions'][$region_name][$bp_name] = [
          '#type' => 'details',
          '#title' => $this->t('@label settings', ['@label' => $bp_label]),
          '#tree' => TRUE,
        ];

        $form['regions'][$region_name][$bp_name]['size'] = [
          '#type' => 'select',
          '#title' => $this->t('@label columns', ['@label' => $bp_label]),
          '#description' => $this->t('Number of columns for @label screens.', ['@label' => $bp_label]),
          '#options' => $region_sizes,
          '#default_value' => isset($configuration['sizes'][$region_name][$bp_name]) ? $configuration['sizes'][$region_name][$bp_name] : 'auto',
        ];

        $form['regions'][$region_name][$bp_name]['offset'] = [
          '#type' => 'select',
          '#title' => $this->t('@label offset', ['@label' => $bp_label]),
          '#description' => $this->t('Number of columns offset for @label screens.', ['@label' => $bp_label]),
          '#options' => $region_offsets,
          '#default_value' => isset($configuration['offsets'][$region_name][$bp_name]) ? $configuration['offsets'][$region_name][$bp_name] : 0,
        ];

        $form['regions'][$region_name][$bp_name]['order'] = [
          '#type' => 'select',
          '#title' => $this->t('@label order', ['@label' => $bp_label]),
          '#description' => $this->t('Set the column order for @label screens.', ['@label' => $bp_label]),
          '#options' => $region_orders,
          '#default_value' => isset($configuration['orders'][$region_name][$bp_name]) ? $configuration['orders'][$region_name][$bp_name] : 0,
        ];

      }
    }
    return $form;
  }

  /**
   * {@inheritdoc}
   */
  public function validateConfigurationForm(array &$form, FormStateInterface $form_state) {

    $breakpoints = $this->_breakpoints();
    $regions = $form_state->getValue('regions');
    foreach ($regions as $region_name) {
      foreach ($breakpoints as $bp => $breakpoint) {
        $column_count = $regions[$region_name][$bp]['size'] + $regions[$region_name][$bp]['offset'];
        if ($column_count > 12) {
          $form_state->setErrorByName("regions][$region_name][$bp]", $this->t("The total columns, including offsets, can not total more than 12."));
        }
      }
    }

  }

  /**
   * {@inheritdoc}
   */
  public function submitConfigurationForm(array &$form, FormStateInterface $form_state) {

    $breakpoints = $this->_breakpoints();

    $this->configuration['attributes'] = $form_state->getValue('attributes');
    foreach (['wrapper_classes', 'wrapper_container', 'wrapper_gutters'] as $name) {
      $this->configuration[$name] = $this->configuration['attributes'][$name];
      unset($this->configuration['attributes'][$name]);
    }

    $regions = $form_state->getValue('regions');
    foreach ($regions as $region_name => $region_config) {
      foreach ($breakpoints as $bp => $breakpoint) {
        $this->configuration['sizes'][$region_name][$bp] = $regions[$region_name][$bp]['size'];
        $this->configuration['offsets'][$region_name][$bp] = $regions[$region_name][$bp]['offset'];
        $this->configuration['orders'][$region_name][$bp] = $regions[$region_name][$bp]['order'];
      }
    }

  }

  /**
   * Returns an array of breakpoints.
   *
   * @return array $breakpoints
   */
  private function _breakpoints() {
     $breakpoints = [
      'small' => 'Small',
      'medium' => 'Medium',
      'large' => 'Large',
      'xlarge' => 'Extra Large',
    ];
    return $breakpoints;
  }

  /**
   * Returns an array of region_sizes.
   *
   * @return array $region_sizes
   */
  private function _region_sizes() {
    // Column sizes.
    $region_sizes = [
      'auto' => 'Auto',
      1 => 'One Col',
      2 => 'Two Col',
      3 => 'Three Col',
      4 => 'Four Col',
      5 => 'Five Col',
      6 => 'Six Col',
      7 => 'Seven Col',
      8 => 'Eight Col',
      9 => 'Nine Col',
      10 => 'Ten Col',
      11 => 'Eleven Col',
      12 => 'Twelve Col',
    ];
    return $region_sizes;
  }

  /**
   * Returns an array of region_offsets.
   *
   * @return array $region_offsets
   */
  private function _region_offsets() {
    // Offsets.
    $region_offsets = [
      0 => '0 Cols',
      1 => '1 Col',
      2 => '2 Cols',
      3 => '3 Cols',
      4 => '4 Cols',
      5 => '5 Cols',
      6 => '6 Cols',
      7 => '7 Cols',
      8 => '8 Cols',
      9 => '9 Cols',
      10 => '10 Cols',
      11 => '11 Cols',
    ];
    return $region_offsets;
  }
}

Here is my 4 column template file:

{% set container = settings.wrapper_container %}
{% set sizes = settings.sizes %}
{% set offsets = settings.offsets %}
{% set orders = settings.orders %}
{%
  set leftOuter_classes = [
    'cell',
    'small-' ~ sizes.leftOuter.small,
    'medium-' ~ sizes.leftOuter.medium,
    'large-' ~ sizes.leftOuter.large,
    'xlarge-' ~ sizes.leftOuter.xlarge,
    'small-offset-' ~ offsets.leftOuter.small,
    'medium-offset-' ~ offsets.leftOuter.medium,
    'large-offset-' ~ offsets.leftOuter.large,
    'xlarge-offset-' ~ offsets.leftOuter.xlarge,
    'small-order-' ~ orders.leftOuter.small,
    'medium-order-' ~ orders.leftOuter.medium,
    'large-order-' ~ orders.leftOuter.large,
    'xlarge-order-' ~ orders.leftOuter.xlarge
  ]
%}
{%
  set leftInner_classes = [
    'cell',
    'small-' ~ sizes.leftInner.small,
    'medium-' ~ sizes.leftInner.medium,
    'large-' ~ sizes.leftInner.large,
    'xlarge-' ~ sizes.leftInner.xlarge,
    'small-offset-' ~ offsets.leftInner.small,
    'medium-offset-' ~ offsets.leftInner.medium,
    'large-offset-' ~ offsets.leftInner.large,
    'xlarge-offset-' ~ offsets.leftInner.xlarge,
    'small-order-' ~ orders.leftInner.small,
    'medium-order-' ~ orders.leftInner.medium,
    'large-order-' ~ orders.leftInner.large,
    'xlarge-order-' ~ orders.leftInner.xlarge
  ]
%}
{%
  set rightInner_classes = [
    'cell',
    'small-' ~ sizes.rightInner.small,
    'medium-' ~ sizes.rightInner.medium,
    'large-' ~ sizes.rightInner.large,
    'xlarge-' ~ sizes.rightInner.xlarge,
    'small-offset-' ~ offsets.rightInner.small,
    'medium-offset-' ~ offsets.rightInner.medium,
    'large-offset-' ~ offsets.rightInner.large,
    'xlarge-offset-' ~ offsets.rightInner.xlarge,
    'small-order-' ~ orders.rightInner.small,
    'medium-order-' ~ orders.rightInner.medium,
    'large-order-' ~ orders.rightInner.large,
    'xlarge-order-' ~ orders.rightInner.xlarge
  ]
%}
{%
  set rightOuter_classes = [
    'cell',
    'small-' ~ sizes.rightOuter.small,
    'medium-' ~ sizes.rightOuter.medium,
    'large-' ~ sizes.rightOuter.large,
    'xlarge-' ~ sizes.rightOuter.xlarge,
    'small-offset-' ~ offsets.rightOuter.small,
    'medium-offset-' ~ offsets.rightOuter.medium,
    'large-offset-' ~ offsets.rightOuter.large,
    'xlarge-offset-' ~ offsets.rightOuter.xlarge,
    'small-order-' ~ orders.rightOuter.small,
    'medium-order-' ~ orders.rightOuter.medium,
    'large-order-' ~ orders.rightOuter.large,
    'xlarge-order-' ~ orders.rightOuter.xlarge
  ]
%}
  
{% if container == TRUE %}
  <div class="grid-container">
{% endif %}
  <div {{ attributes.addClass('grid-x', settings.wrapper_classes, settings.wrapper_gutters.x, settings.wrapper_gutters.y) }}>
    {% if content.leftOuter %}
      <div {{ region_attributes.leftOuter.addClass(leftOuter_classes) }}>
        {{ content.leftOuter }}
      </div>
    {% endif %}
    {% if content.leftInner %}
      <div {{ region_attributes.leftInner.addClass(leftInner_classes) }}>
        {{ content.leftInner }}
      </div>
    {% endif %}
    {% if content.rightInner %}
      <div {{ region_attributes.rightInner.addClass(rightInner_classes) }}>
        {{ content.rightInner %}
      </div>
    {% endif %}
    {% if content.rightOuter %}
      <div {{ region_attributes.rightOuter.addClass(rightOuter_classes) }}>
        {{ content.right }}
      </div>
    {% endif %}
  </div>
{% if container == TRUE %}
  </div>
{% endif %}

I see this as a massive improvement, and I'd be willing to provide a public repo.