Commit 5beb2f94 authored by alexpott's avatar alexpott
Browse files

Issue #1903746 by Mark Carver, mgifford, DaneMacaulay: Replace the views grid...

Issue #1903746 by Mark Carver, mgifford, DaneMacaulay: Replace the views grid table template with one using divs.
parent 31efff27
......@@ -29,15 +29,24 @@ views.style.grid:
columns:
type: integer
label: 'Number of columns'
automatic_width:
type: boolean
label: 'Automatic width'
alignment:
type: string
label: 'Alignment'
fill_single_line:
row_class_custom:
type: string
label: 'Custom row classes'
row_class_default:
type: boolean
label: 'Fill up single line'
summary:
label: 'Default views row classes'
col_class_custom:
type: string
label: 'Table summary'
label: 'Custom column classes'
col_class_default:
type: boolean
label: 'Default views column classes'
views.style.table:
type: views_style
......
......@@ -22,3 +22,13 @@
.view .progress-disabled {
float: none;
}
/* Grid style column align. */
.views-view-grid .views-col {
float: left;
}
.views-view-grid .views-row {
clear: both;
float: left;
width: 100%;
}
......@@ -33,28 +33,22 @@ class Grid extends StylePluginBase {
protected $usesRowPlugin = TRUE;
/**
* Does the style plugin support custom css class for the rows.
*
* @var bool
*/
protected $usesRowClass = TRUE;
/**
* Set default options
* {@inheritdoc}
*/
protected function defineOptions() {
$options = parent::defineOptions();
$options['columns'] = array('default' => '4');
$options['automatic_width'] = array('default' => TRUE);
$options['alignment'] = array('default' => 'horizontal');
$options['fill_single_line'] = array('default' => TRUE, 'bool' => TRUE);
$options['summary'] = array('default' => '');
$options['col_class_custom'] = array('default' => '');
$options['col_class_default'] = array('default' => TRUE);
$options['row_class_custom'] = array('default' => '');
$options['row_class_default'] = array('default' => TRUE);
return $options;
}
/**
* Render the given style.
* {@inheritdoc}
*/
public function buildOptionsForm(&$form, &$form_state) {
parent::buildOptionsForm($form, $form_state);
......@@ -63,7 +57,13 @@ public function buildOptionsForm(&$form, &$form_state) {
'#title' => t('Number of columns'),
'#default_value' => $this->options['columns'],
'#required' => TRUE,
'#min' => 0,
'#min' => 1,
);
$form['automatic_width'] = array(
'#type' => 'checkbox',
'#title' => t('Automatic width'),
'#description' => t('The width of each column will be calculated automatically based on the number of columns provided. If additional classes are entered or a theme injects classes based on a grid system, disabling this option may prove beneficial.'),
'#default_value' => $this->options['automatic_width'],
);
$form['alignment'] = array(
'#type' => 'radios',
......@@ -72,20 +72,36 @@ public function buildOptionsForm(&$form, &$form_state) {
'#default_value' => $this->options['alignment'],
'#description' => t('Horizontal alignment will place items starting in the upper left and moving right. Vertical alignment will place items starting in the upper left and moving down.'),
);
$form['fill_single_line'] = array(
$form['col_class_default'] = array(
'#title' => t('Default column classes'),
'#description' => t('Add the default views column classes like views-col, col-1 and clearfix to the output. You can use this to quickly reduce the amount of markup the view provides by default, at the cost of making it more difficult to apply CSS.'),
'#type' => 'checkbox',
'#title' => t('Fill up single line'),
'#description' => t('If you disable this option, a grid with only one row will have the same number of table cells (<TD>) as items. Disabling it can cause problems with your CSS.'),
'#default_value' => !empty($this->options['fill_single_line']),
'#default_value' => $this->options['col_class_default'],
);
$form['summary'] = array(
$form['col_class_custom'] = array(
'#title' => t('Custom column class'),
'#description' => t('Additional classes to provide on each column. Separated by a space.'),
'#type' => 'textfield',
'#default_value' => $this->options['col_class_custom'],
);
if ($this->usesFields()) {
$form['col_class_custom']['#description'] .= ' ' . t('You may use field tokens from as per the "Replacement patterns" used in "Rewrite the output of this field" for all fields.');
}
$form['row_class_default'] = array(
'#title' => t('Default row classes'),
'#description' => t('Adds the default views row classes like views-row, row-1 and clearfix to the output. You can use this to quickly reduce the amount of markup the view provides by default, at the cost of making it more difficult to apply CSS.'),
'#type' => 'checkbox',
'#default_value' => $this->options['row_class_default'],
);
$form['row_class_custom'] = array(
'#title' => t('Custom row class'),
'#description' => t('Additional classes to provide on each row. Separated by a space.'),
'#type' => 'textfield',
'#title' => t('Table summary'),
'#description' => t('This value will be displayed as table-summary attribute in the html. Set this for better accessiblity of your site.'),
'#default_value' => $this->options['summary'],
'#default_value' => $this->options['row_class_custom'],
);
if ($this->usesFields()) {
$form['row_class_custom']['#description'] .= ' ' . t('You may use field tokens from as per the "Replacement patterns" used in "Rewrite the output of this field" for all fields.');
}
}
}
<?php
/**
* @file
* Contains \Drupal\views\Tests\Plugin\StyleGridTest.
*/
namespace Drupal\views\Tests\Plugin;
use Drupal\views\ViewExecutable;
use Symfony\Component\HttpFoundation\Request;
/**
* Tests the grid style plugin.
*
* @see \Drupal\views\Plugin\views\style\Grid
*/
class StyleGridTest extends PluginTestBase {
/**
* Views used by this test.
*
* @var array
*/
public static $testViews = array('test_grid');
/**
* Keeps track of which alignments have been tested.
*/
protected $alignmentsTested = array();
/**
* {@inheritdoc}
*/
public static function getInfo() {
return array(
'name' => 'Style: Grid',
'description' => 'Tests the grid style plugin.',
'group' => 'Views Plugins',
);
}
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->enableViewsTestModule();
}
/**
* Tests the grid style.
*/
public function testGrid() {
$view = views_get_view('test_grid');
foreach (array('horizontal', 'vertical') as $alignment) {
$this->assertGrid($view, $alignment, 5);
$this->assertGrid($view, $alignment, 4);
$this->assertGrid($view, $alignment, 3);
$this->assertGrid($view, $alignment, 2);
$this->assertGrid($view, $alignment, 1);
}
}
/**
* Generates a grid and asserts that it is displaying correctly.
*
* @param \Drupal\views\ViewExecutable $view
* The executable to prepare.
* @param string $alignment
* The alignment of the grid to test.
* @param int $columns
* The number of columns in the grid to test.
*/
protected function assertGrid(ViewExecutable $view, $alignment, $columns) {
$view->setDisplay('default');
$view->initStyle();
$view->initHandlers();
$view->initQuery();
$view->style_plugin->options['alignment'] = $alignment;
$view->style_plugin->options['columns'] = $columns;
$this->executeView($view);
$output = $view->preview();
$output = drupal_render($output);
$this->drupalSetContent($output, 'internal://test-grid');
if (!in_array($alignment, $this->alignmentsTested)) {
$result = $this->xpath('//div[contains(@class, "views-view-grid") and contains(@class, :alignment) and contains(@class, :columns)]', array(':alignment' => $alignment, ':columns' => 'cols-' . $columns));
$this->assertTrue(count($result), ucfirst($alignment) . " grid markup detected.");
$this->alignmentsTested[] = $alignment;
}
$width = '0';
switch ($columns) {
case 5: $width = '20'; break;
case 4: $width = '25'; break;
case 3: $width = '33.3333'; break;
case 2: $width = '50'; break;
case 1: $width = '100'; break;
}
// Ensure last column exists.
$result = $this->xpath('//div[contains(@class, "views-col") and contains(@class, :columns) and starts-with(@style, :width)]', array(':columns' => 'col-' . $columns, ':width' => 'width: ' . $width));
$this->assertTrue(count($result), ucfirst($alignment) . " $columns column grid: last column exists and automatic width calculated correctly.");
// Ensure no extra columns were generated.
$result = $this->xpath('//div[contains(@class, "views-col") and contains(@class, :columns)]', array(':columns' => 'col-' . ($columns + 1)));
$this->assertFalse(count($result), ucfirst($alignment) . " $columns column grid: no extraneous columns exist.");
}
}
......@@ -4,13 +4,18 @@
* Default theme implementation for views to display rows in a grid.
*
* Available variables:
* - attributes: HTML attributes for the table element.
* - attributes: HTML attributes for the wrapping element.
* - title: The title of this group of rows.
* - rows: A list of rows. Each row contains a list of columns.
* - row_classes: HTML classes for each row including the row number and first
* or last.
* - column_classes: HTML classes for each column including the row number and
* first or last.
* - view: The view object.
* - rows: The rendered view results.
* - options: The view plugin style options.
* - items: A list of grid items. Each item contains a list of rows or columns.
* The order in what comes first (row or column) depends on which alignment
* type is chosen (horizontal or vertical).
* - attributes: HTML attributes for each row or column.
* - content: A list of columns or rows. Each row or column contains:
* - attributes: HTML attributes for each row or column.
* - content: The row or column contents.
*
* @see template_preprocess_views_view_grid()
*
......@@ -20,16 +25,26 @@
{% if title %}
<h3>{{ title }}</h3>
{% endif %}
<table{{ attributes }}>
<tbody>
{% for row_number, columns in rows %}
<tr{{ row_classes[row_number] }}>
{% for column_number, item in columns %}
<td{{ column_classes[row_number][column_number] }}>
{{ item }}
</td>
{% endfor %}
</tr>
<div{{ attributes }}>
{% if options.alignment == 'horizontal' %}
{% for row in items %}
<div{{ row.attributes }}>
{% for column in row.content %}
<div{{ column.attributes }}>
{{ column.content }}
</div>
{% endfor %}
</tbody>
</table>
</div>
{% endfor %}
{% else %}
{% for column in items %}
<div{{ column.attributes }}>
{% for row in column.content %}
<div{{ row.attributes }}>
{{ row.content }}
</div>
{% endfor %}
</div>
{% endfor %}
{% endif %}
</div>
base_table: views_test_data
core: '8'
description: ''
status: '1'
display:
default:
display_options:
defaults:
fields: '0'
pager: '0'
pager_options: '0'
sorts: '0'
fields:
age:
field: age
id: age
relationship: none
table: views_test_data
plugin_id: numeric
id:
field: id
id: id
relationship: none
table: views_test_data
plugin_id: numeric
name:
field: name
id: name
relationship: none
table: views_test_data
plugin_id: string
pager:
options:
offset: '0'
type: none
pager_options: { }
sorts:
id:
field: id
id: id
order: ASC
relationship: none
table: views_test_data
plugin_id: numeric
style:
type: grid
options:
grouping: { }
columns: '4'
automatic_width: '1'
alignment: horizontal
col_class_default: '1'
col_class_custom: ''
row_class_default: '1'
row_class_custom: ''
row:
type: fields
display_plugin: default
display_title: Master
id: default
position: '0'
page_1:
display_options:
path: test-grid
display_plugin: page
display_title: 'Page display'
id: page_1
position: '1'
label: ''
id: test_grid
tag: ''
......@@ -746,56 +746,102 @@ function template_preprocess_views_view_table(&$variables) {
* - rows: An array of row items. Each row is an array of content.
*/
function template_preprocess_views_view_grid(&$variables) {
$view = $variables['view'];
$options = $view->style_plugin->options;
$handler = $view->style_plugin;
$default_row_class = isset($options['default_row_class']) ? $options['default_row_class'] : TRUE;
$row_class_special = isset($options['row_class_special']) ? $options['row_class_special'] : TRUE;
$options = $variables['options'] = $variables['view']->style_plugin->options;
$horizontal = ($options['alignment'] === 'horizontal');
$variables['attributes']['class'] = array(
'views-view-grid',
$options['alignment'],
'cols-' . $options['columns'],
'clearfix',
);
$col = 0;
$row = 0;
$items = array();
$remainders = count($variables['rows']) % $options['columns'];
$num_rows = floor(count($variables['rows']) / $options['columns']);
$columns = $options['columns'];
$variables['attributes']['class'][] = 'views-view-grid cols-' . $columns;
$rows = array();
$row_indexes = array();
if ($options['alignment'] == 'horizontal') {
$row = array();
$col_count = 0;
$row_count = 0;
$count = 0;
foreach ($variables['rows'] as $row_index => $item) {
$count++;
$row[] = $item;
$row_indexes[$row_count][$col_count] = $row_index;
$col_count++;
if ($count % $columns == 0) {
$rows[] = $row;
$row = array();
$col_count = 0;
$row_count++;
// Iterate over each rendered views result row.
foreach ($variables['rows'] as $item) {
// Add the item.
if ($horizontal) {
$items[$row]['content'][$col]['content'] = $item;
}
else {
$items[$col]['content'][$row]['content'] = $item;
}
// Create attributes for rows.
if (!$horizontal || ($horizontal && empty($items[$row]['attributes']))) {
$row_attributes = array('class' => array());
// Add default views row classes.
if ($options['row_class_default']) {
$row_attributes['class'][] = 'views-row';
$row_attributes['class'][] = 'row-' . ($row + 1);
if ($horizontal) {
$row_attributes['class'][] = 'clearfix';
}
}
// Add custom row classes.
$row_class = array_filter(explode(' ', $options['row_class_custom']));
if (!empty($row_class)) {
$row_attributes['class'] = array_merge($row_attributes['class'], $row_class);
}
// Add row attributes to the item.
if ($horizontal) {
$items[$row]['attributes'] = new Attribute($row_attributes);
}
else {
$items[$col]['content'][$row]['attributes'] = new Attribute($row_attributes);
}
}
if ($row) {
// Fill up the last line only if it's configured, but this is default.
if (!empty($handler->options['fill_single_line']) && count($rows)) {
for ($i = 0; $i < ($columns - $col_count); $i++) {
$row[] = '';
// Create attributes for columns.
if ($horizontal || (!$horizontal && empty($items[$col]['attributes']))) {
$col_attributes = array('class' => array());
// Add default views column classes.
if ($options['col_class_default']) {
$col_attributes['class'][] = 'views-col';
$col_attributes['class'][] = 'col-' . ($col + 1);
if (!$horizontal) {
$col_attributes['class'][] = 'clearfix';
}
}
$rows[] = $row;
// Add custom column classes.
$col_class = array_filter(explode(' ', $options['col_class_custom']));
if (!empty($col_class)) {
$col_attributes['class'] = array_merge($col_attributes['class'], $col_class);
}
// Add automatic width for columns.
if ($options['automatic_width']) {
$col_attributes['style'] = 'width: ' . (100 / $options['columns']) . '%;';
}
// Add column attributes to the item.
if ($horizontal) {
$items[$row]['content'][$col]['attributes'] = new Attribute($col_attributes);
}
else {
$items[$col]['attributes'] = new Attribute($col_attributes);
}
}
}
else {
$num_rows = floor(count($variables['rows']) / $columns);
// The remainders are the 'odd' columns that are slightly longer.
$remainders = count($variables['rows']) % $columns;
$row = 0;
$col = 0;
foreach ($variables['rows'] as $count => $item) {
$rows[$row][$col] = $item;
$row_indexes[$row][$col] = $count;
$row++;
// Increase, decrease or reset appropriate integers.
if ($horizontal) {
if ($col == 0 && $col != ($options['columns'] - 1)) {
$col++;
}
elseif ($col >= ($options['columns'] - 1)) {
$col = 0;
$row++;
}
else {
$col++;
}
}
else {
$row++;
if (!$remainders && $row == $num_rows) {
$row = 0;
$col++;
......@@ -806,53 +852,10 @@ function template_preprocess_views_view_grid(&$variables) {
$remainders--;
}
}
for ($i = 0; $i < count($rows[0]); $i++) {
// This should be a string so this is ok.
if (!isset($rows[count($rows) - 1][$i])) {
$rows[count($rows) - 1][$i] = '';
}
}
}
// Apply the row classes.
foreach ($rows as $row_number => $row) {
$row_classes = array();
if ($default_row_class) {
$row_classes['class'][] = 'row-' . ($row_number + 1);
}
if ($row_class_special) {
if ($row_number == 0) {
$row_classes['class'][] = 'row-first';
}
if (count($rows) == ($row_number + 1)) {
$row_classes['class'][] = 'row-last';
}
}
$row_classes = new Attribute($row_classes);
foreach ($rows[$row_number] as $column_number => $item) {
$variables['column_classes'][$row_number][$column_number] = array();
if ($default_row_class) {
$variables['column_classes'][$row_number][$column_number]['class'][] = 'col-' . ($column_number + 1);
}
if ($row_class_special) {
if ($column_number == 0) {
$variables['column_classes'][$row_number][$column_number]['class'][] = 'col-first';
}
elseif (count($rows[$row_number]) == ($column_number + 1)) {
$variables['column_classes'][$row_number][$column_number]['class'][] = 'col-last';
}
}
if (isset($row_indexes[$row_number][$column_number]) && $column_class = $view->style_plugin->getRowClass($row_indexes[$row_number][$column_number])) {
$variables['column_classes'][$row_number][$column_number]['class'][] = $column_class;
}
$variables['column_classes'][$row_number][$column_number] = new Attribute($variables['column_classes'][$row_number][$column_number]);
}
}
$variables['rows'] = $rows;
if (!empty($handler->options['summary'])) {
$variables['attributes']['summary'] = $handler->options['summary'];
}
// Add items to the variables array.
$variables['items'] = $items;
}
/**
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment