...
 
Commits (12)
##Description ## Description
Enable the "Make tables responsive" text format filter to display field tables responsively using the [Tablesaw Library](https://www.filamentgroup.com/lab/tablesaw.html). Enable the "Make tables responsive" text format filter to display field
tables responsively using the
[Tablesaw Library](https://www.filamentgroup.com/lab/tablesaw.html).
* For a full description of the module, visit the project page: * For a full description of the module, visit the project page:
https://drupal.org/project/responsive_tables_filter https://drupal.org/project/responsive_tables_filter
##Basic Usage ## Basic Usage
0. Place this module in your /modules directory and enable it. 0. Place this module in your /modules directory and enable it.
1. Go to admin/config/content/formats. 1. Go to admin/config/content/formats.
2. Enable the filter "Responsive Tables Filter" on any text format for which you 2. Enable the filter "Responsive Tables Filter" on any text format for which you
want to make tables responsive (e.g., Basic HTML). want to make tables responsive (e.g., Basic HTML).
3. Verify the text format(s) allow HTML table tags. If you are using the standard 3. Verify the text format(s) allow HTML table tags. If you are using the
"Limit HTML tags" filter (admin/config/content/formats), all of the following standard "Limit HTML tags" filter (admin/config/content/formats), all of the
should be allowed: following should be allowed:
`<table> <th> <tr> <td> <thead> <tbody> <tfoot>` (if you want class attributes `<table> <th> <tr> <td> <thead> <tbody> <tfoot>` (if you want class attributes
supplied by the user to co-exist, use the syntax `<table class>` here). supplied by the user to co-exist, use the syntax `<table class>` here).
Any fields that use the text format(s) which have tables in them will now be Any fields that use the text format(s) which have tables in them will now be
responsive. responsive.
The 8.x version of this module currently automatically makes Views tables responsive. The 8.x version of this module currently automatically makes Views tables
responsive.
##FAQ ##FAQ
Q: Can I override the tablesaw CSS? Q: Text filter sets a default Tablesaw mode. Can I override this on per-table
basis in the WYSIWYG?
A: Yes, but any CSS you add needs to include the tablesaw naming patterns so that the Javascript can find elements.
Q: Can I target specific tables within nodes? A: Yes. Add `class="tablesaw-stack"`, `class="tablesaw-columntoggle"`, or
`class="tablesaw-swipe"` to make a single table override the default.
A: The Drupal 8 version of this module does not yet support xpath query.
Q: Can I do anything to have tablesaw not be applied to a table (WYSIWYG Only). Q: Can I do anything to have tablesaw not be applied to a table (WYSIWYG Only).
...@@ -38,8 +39,3 @@ A: Yes! Add the class `no-tablesaw` to the table. ...@@ -38,8 +39,3 @@ A: Yes! Add the class `no-tablesaw` to the table.
Current maintainers: Current maintainers:
- Mark Fullmer (mark_fullmer) - https://www.drupal.org/u/mark_fullmer - Mark Fullmer (mark_fullmer) - https://www.drupal.org/u/mark_fullmer
views_enabled: '0'
views_tablesaw_mode: stack
responsive_tables_filter.settings:
type: config_object
mapping:
views_enabled:
type: string
label: "Views enabled"
views_tablesaw_mode:
type: string
label: "Views tablesaw mode"
This diff is collapsed.
/*! Tablesaw - v3.0.0-beta.1 - 2016-09-19
* https://github.com/filamentgroup/tablesaw
* Copyright (c) 2016 Filament Group; Licensed MIT */
table.tablesaw {
empty-cells: show;
max-width: 100%;
width: 100%;
}
.tablesaw {
border-collapse: collapse;
width: 100%;
}
/* Structure */
.tablesaw {
border: 0;
padding: 0;
}
.tablesaw th,
.tablesaw td {
box-sizing: border-box;
padding: .5em .7em;
}
.tablesaw thead tr:first-child th {
padding-top: .9em;
padding-bottom: .7em;
}
/* Table rows have a gray bottom stroke by default */
.tablesaw-stack tbody tr {
border-bottom: 1px solid #dfdfdf;
}
.tablesaw-stack td .tablesaw-cell-label,
.tablesaw-stack th .tablesaw-cell-label {
display: none;
}
/* Mobile first styles: Begin with the stacked presentation at narrow widths */
@media only all {
/* Show the table cells as a block level element */
.tablesaw-stack td,
.tablesaw-stack th {
text-align: left;
display: block;
}
.tablesaw-stack tr {
clear: both;
display: table-row;
}
/* Make the label elements a percentage width */
.tablesaw-stack td .tablesaw-cell-label,
.tablesaw-stack th .tablesaw-cell-label {
display: block;
padding: 0 .6em 0 0;
width: 30%;
display: inline-block;
}
/* For grouped headers, have a different style to visually separate the levels by classing the first label in each col group */
.tablesaw-stack th .tablesaw-cell-label-top,
.tablesaw-stack td .tablesaw-cell-label-top {
display: block;
padding: .4em 0;
margin: .4em 0;
}
.tablesaw-cell-label {
display: block;
}
/* Avoid double strokes when stacked */
.tablesaw-stack tbody th.group {
margin-top: -1px;
}
/* Avoid double strokes when stacked */
.tablesaw-stack th.group b.tablesaw-cell-label {
display: none !important;
}
}
@media (max-width: 39.9375em) {
.tablesaw-stack thead td,
.tablesaw-stack thead th {
display: none;
}
.tablesaw-stack tbody td,
.tablesaw-stack tbody th {
clear: left;
float: left;
width: 100%;
}
.tablesaw-cell-label {
vertical-align: top;
}
.tablesaw-cell-content {
max-width: 67%;
display: inline-block;
}
.tablesaw-stack td:empty,
.tablesaw-stack th:empty {
display: none;
}
}
/* Media query to show as a standard table at 560px (35em x 16px) or wider */
@media (min-width: 40em) {
.tablesaw-stack tr {
display: table-row;
}
/* Show the table header rows */
.tablesaw-stack td,
.tablesaw-stack th,
.tablesaw-stack thead td,
.tablesaw-stack thead th {
display: table-cell;
margin: 0;
}
/* Hide the labels in each cell */
.tablesaw-stack td .tablesaw-cell-label,
.tablesaw-stack th .tablesaw-cell-label {
display: none !important;
}
}
\ No newline at end of file
;(function( $ ) { /*! Tablesaw - v3.1.2 - 2019-03-19
Drupal.behaviors.responsiveTablesFilter = { * https://github.com/filamentgroup/tablesaw
attach: function (context, settings) { * Copyright (c) 2019 Filament Group; Licensed MIT */
// DOM-ready auto-init of plugins. (function (win) {
// Many plugins bind to an "enhance" event to init themselves on dom ready, or when new markup is inserted into the DOM "use strict";
$( function(){
/*! Tablesaw - v3.0.0-beta.1 - 2016-09-19 // DOM-ready auto-init of plugins.
* https://github.com/filamentgroup/tablesaw // Many plugins bind to an "enhance" event to init themselves on dom ready, or when new markup is inserted into the DOM
* Copyright (c) 2016 Filament Group; Licensed MIT // Use raw DOMContentLoaded instead of shoestring (may have issues in Android 2.3, exhibited by stack table)
*/ if (!("Tablesaw" in win)) {
$( document ).trigger( "enhance.tablesaw" ); throw new Error("Tablesaw library not found.");
});
}
} }
})( shoestring || jQuery ); if (!("init" in Tablesaw)) {
throw new Error("Your tablesaw-init.js is newer than the core Tablesaw version.");
}
Tablesaw.init();
})(typeof window !== "undefined" ? window : this);
\ No newline at end of file
This diff is collapsed.
This diff is collapsed.
...@@ -3,3 +3,4 @@ type: module ...@@ -3,3 +3,4 @@ type: module
description: Make tables in WYSIWYG fields responsive description: Make tables in WYSIWYG fields responsive
core: 8.x core: 8.x
package: Other package: Other
configure: responsive_tables_filter.settings
<?php
/**
* @file
* Responsive Tables Filter install file.
*/
/**
* Set initial responsive tables filter settings for existing sites.
*/
function responsive_tables_filter_update_8001() {
$config_factory = \Drupal::configFactory();
$config = $config_factory->getEditable('responsive_tables_filter.settings');
$config->set('views_enabled', '0');
$config->set('views_tablesaw_mode', 'Stack Mode');
$config->save();
};
...@@ -2,9 +2,9 @@ tablesaw-filter: ...@@ -2,9 +2,9 @@ tablesaw-filter:
version: 1.x version: 1.x
css: css:
theme: theme:
css/tablesaw.stackonly.css: {} css/tablesaw.css: {}
js: js:
js/tablesaw.stackonly.js: {} js/tablesaw.min.js: {}
js/tablesaw-init.js: {} js/tablesaw-init.js: {}
dependencies: dependencies:
- core/jquery - core/jquery
responsive_tables_filter.admin:
title: 'Responsive Tables'
parent: system.admin_config_content
description: Configure the Tablesaw responsive table behavior
route_name: responsive_tables_filter.settings
weight: 100
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
* Contains responsive_tables_filter.module.. * Contains responsive_tables_filter.module..
*/ */
use Drupal\Core\Url;
use Drupal\Core\Routing\RouteMatchInterface; use Drupal\Core\Routing\RouteMatchInterface;
/** /**
...@@ -18,7 +19,7 @@ function responsive_tables_filter_help($route_name, RouteMatchInterface $route_m ...@@ -18,7 +19,7 @@ function responsive_tables_filter_help($route_name, RouteMatchInterface $route_m
$output .= '<h3>' . t('About') . '</h3>'; $output .= '<h3>' . t('About') . '</h3>';
$output .= '<dt>' . t('Basic Usage : text format') . '</dt>'; $output .= '<dt>' . t('Basic Usage : text format') . '</dt>';
$output .= '<dd>' . t('Go to any of the defined formats on the <a href=":formats">Text formats page</a>. Enable "Apply responsive behavior to HTML tables" and save the form.', [':formats' => \Drupal::url('filter.admin_overview')]) . '</dd>'; $output .= '<dd>' . t('Go to any of the defined formats on the <a href=":formats">Text formats page</a>. Enable "Apply responsive behavior to HTML tables" and save the form.', [':formats' => Url::fromRoute('filter.admin_overview')]) . '</dd>';
$output .= '<dd>' . t('Under "Filter processing order," make sure this is placed after any filters that would strip table-related tags or the "class" attribute.') . '</dd>'; $output .= '<dd>' . t('Under "Filter processing order," make sure this is placed after any filters that would strip table-related tags or the "class" attribute.') . '</dd>';
$output .= '<dd>' . t('If the text format uses "Limit allowed HTML tags and correct faulty HTML," make sure all of the following are included in the allowed list:') . htmlentities(' <table> <th> <tr> <td> <thead> <tbody> <tfoot>') . '</dd>'; $output .= '<dd>' . t('If the text format uses "Limit allowed HTML tags and correct faulty HTML," make sure all of the following are included in the allowed list:') . htmlentities(' <table> <th> <tr> <td> <thead> <tbody> <tfoot>') . '</dd>';
$output .= '<p>Any fields that use the text format(s) which have tables in them will now be responsive.</p>'; $output .= '<p>Any fields that use the text format(s) which have tables in them will now be responsive.</p>';
...@@ -46,10 +47,14 @@ function responsive_tables_filter_theme() { ...@@ -46,10 +47,14 @@ function responsive_tables_filter_theme() {
* Adds tablesaw JS when tables present and CSS/JS aggregation is off to table. * Adds tablesaw JS when tables present and CSS/JS aggregation is off to table.
*/ */
function responsive_tables_filter_preprocess_views_view_table(&$variables) { function responsive_tables_filter_preprocess_views_view_table(&$variables) {
// Add tablesaw classes & data attribute. $config = \Drupal::config('responsive_tables_filter.settings');
$variables['attributes']['class'][] = 'tablesaw'; if ($enabled = $config->get('views_enabled')) {
$variables['attributes']['class'][] = 'tablesaw-stack'; $mode = $config->get('views_tablesaw_mode');
$variables['attributes']['data-tablesaw-mode'][] = 'stack'; // Add tablesaw classes & data attribute.
$variables['attributes']['class'][] = 'tablesaw';
$variables['view']->element['#attached']['library'][] = 'responsive_tables_filter/tablesaw-filter'; $variables['attributes']['class'][] = 'tablesaw-' . $mode;
$variables['attributes']['data-tablesaw-mode'][] = $mode;
$variables['view']->element['#attached']['library'][] = 'responsive_tables_filter/tablesaw-filter';
$variables['#cache']['tags'][] = 'config:responsive_tables_filter.settings';
}
} }
responsive_tables_filter.settings:
path: 'admin/config/content/responsive_tables_filter'
defaults:
_form: '\Drupal\responsive_tables_filter\Form\TablesawSettings'
_title: 'Responsive Tables'
requirements:
_permission: 'administer site configuration'
<?php
namespace Drupal\responsive_tables_filter\Form;
use Drupal\Core\Form\ConfigFormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\responsive_tables_filter\Plugin\Filter\FilterResponsiveTablesFilter;
/**
* Configure settings for the Responsive Tables Filter module.
*/
class TablesawSettings extends ConfigFormBase {
const SETTINGS = 'responsive_tables_filter.settings';
/**
* {@inheritdoc}
*/
public function getFormId() {
return 'responsive_tables_filter_settings';
}
/**
* {@inheritdoc}
*/
protected function getEditableConfigNames() {
return [
static::SETTINGS,
];
}
/**
* {@inheritdoc}
*/
public function buildForm(array $form, FormStateInterface $form_state) {
$config = $this->config(static::SETTINGS);
$form['views_enabled'] = [
'#type' => 'checkbox',
'#title' => $this->t('Automatically add to all table-based Drupal views'),
'#description' => $this->t('For more fine-grained control of which Views tables should use the Tablesaw library, leave this unchecked and add the table attributes programmatically. See https://github.com/filamentgroup/tablesaw'),
'#default_value' => $config->get('views_enabled'),
];
$form['views_tablesaw_mode'] = [
'#type' => 'select',
'#title' => $this->t('Tablesaw mode'),
'#default_value' => $config->get('views_tablesaw_mode') ?? 'stack',
'#description' => $this->t('This will apply to all Views-generated tables on the site. See documentation: https://github.com/filamentgroup/tablesaw'),
'#options' => FilterResponsiveTablesFilter::$modes,
'#states' => [
'visible' => [
':input[name="views_enabled"]' => ['checked' => TRUE],
],
],
];
return parent::buildForm($form, $form_state);
}
/**
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
$this->configFactory->getEditable(static::SETTINGS)
->set('views_enabled', $form_state->getValue('views_enabled'))
->set('views_tablesaw_mode', $form_state->getValue('views_tablesaw_mode'))
->save();
parent::submitForm($form, $form_state);
}
}
...@@ -4,6 +4,7 @@ namespace Drupal\responsive_tables_filter\Plugin\Filter; ...@@ -4,6 +4,7 @@ namespace Drupal\responsive_tables_filter\Plugin\Filter;
use Drupal\filter\FilterProcessResult; use Drupal\filter\FilterProcessResult;
use Drupal\filter\Plugin\FilterBase; use Drupal\filter\Plugin\FilterBase;
use Drupal\Core\Form\FormStateInterface;
/** /**
* Responsive Tables Filter class. Implements process() method only. * Responsive Tables Filter class. Implements process() method only.
...@@ -12,15 +13,24 @@ use Drupal\filter\Plugin\FilterBase; ...@@ -12,15 +13,24 @@ use Drupal\filter\Plugin\FilterBase;
* id = "filter_responsive_tables_filter", * id = "filter_responsive_tables_filter",
* title = @Translation("Apply responsive behavior to HTML tables."), * title = @Translation("Apply responsive behavior to HTML tables."),
* type = Drupal\filter\Plugin\FilterInterface::TYPE_MARKUP_LANGUAGE, * type = Drupal\filter\Plugin\FilterInterface::TYPE_MARKUP_LANGUAGE,
* settings = {
* "tablesaw_type" = "stack"
* }
* ) * )
*/ */
class FilterResponsiveTablesFilter extends FilterBase { class FilterResponsiveTablesFilter extends FilterBase {
public static $modes = [
'stack' => "Stack Mode",
'columntoggle' => "Column Toggle Mode",
'swipe' => "Swipe Mode",
];
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function process($text, $langcode) { public function process($text, $langcode) {
if ($filtered = $this->responsive_tables_filter($text)) { if ($filtered = $this->runFilter($text)) {
$result = new FilterProcessResult($filtered); $result = new FilterProcessResult($filtered);
// Attach Tablesaw library assets to this page. // Attach Tablesaw library assets to this page.
$result->setAttachments([ $result->setAttachments([
...@@ -34,10 +44,24 @@ class FilterResponsiveTablesFilter extends FilterBase { ...@@ -34,10 +44,24 @@ class FilterResponsiveTablesFilter extends FilterBase {
return $result; return $result;
} }
/**
* {@inheritdoc}
*/
public function settingsForm(array $form, FormStateInterface $form_state) {
$form['tablesaw_type'] = [
'#type' => 'select',
'#title' => $this->t('Default mode'),
'#default_value' => $this->settings['tablesaw_type'] ?? 'stack',
'#description' => $this->t('This will apply by default to tables in WYSIWYGs, but can be overridden on an individual basis by adding the <code>class</code> "tablesaw-stack", "tablesaw-columntoggle", or "tablesaw-swipe" to the <code>table</code> tag. See documentation: https://github.com/filamentgroup/tablesaw'),
'#options' => self::$modes,
];
return $form;
}
/** /**
* Business logic for adding classes & attributes to <table> tags. * Business logic for adding classes & attributes to <table> tags.
*/ */
public function responsive_tables_filter($text) { public function runFilter($text) {
// Older versions of libxml always add DOCTYPE, <html>, and <body> tags. // Older versions of libxml always add DOCTYPE, <html>, and <body> tags.
// See http://www.php.net/manual/en/libxml.constants.php. // See http://www.php.net/manual/en/libxml.constants.php.
// Sometimes, PHP is >= 5.4, but libxml is old enough that the constants are // Sometimes, PHP is >= 5.4, but libxml is old enough that the constants are
...@@ -66,11 +90,19 @@ class FilterResponsiveTablesFilter extends FilterBase { ...@@ -66,11 +90,19 @@ class FilterResponsiveTablesFilter extends FilterBase {
// Find existing class attributes, if any, and append tablesaw class. // Find existing class attributes, if any, and append tablesaw class.
$existing_classes = $table->getAttribute('class'); $existing_classes = $table->getAttribute('class');
if (strpos($existing_classes, 'no-tablesaw') === FALSE) { if (strpos($existing_classes, 'no-tablesaw') === FALSE) {
$new_classes = !empty($existing_classes) ? $existing_classes . ' tablesaw tablesaw-stack' : 'tablesaw tablesaw-stack'; $type = $this->settings['tablesaw_type'] ?? 'stack';
// Allow for class-based override of default.
foreach (array_keys(self::$modes) as $mode) {
if (strpos($existing_classes, "tablesaw-" . $mode) !== FALSE) {
$type = $mode;
break;
}
}
$new_classes = !empty($existing_classes) ? $existing_classes . ' tablesaw tablesaw-' . $type : 'tablesaw tablesaw-' . $type;
$table->setAttribute('class', $new_classes); $table->setAttribute('class', $new_classes);
// Set data-tablesaw-mode & minimap.
// Force data-tablesaw-mode attribute to be "stack". $table->setAttribute('data-tablesaw-mode', $type);
$table->setAttribute('data-tablesaw-mode', 'stack'); $table->setAttribute('data-tablesaw-minimap', NULL);
} }
} }
// Get innerHTML of root node. // Get innerHTML of root node.
......
...@@ -23,7 +23,7 @@ class FilterProcessTest extends UnitTestCase { ...@@ -23,7 +23,7 @@ class FilterProcessTest extends UnitTestCase {
* *
* @var \Drupal\responsive_tables_filter\Plugin\Filter\FilterResponsiveTablesFilter * @var \Drupal\responsive_tables_filter\Plugin\Filter\FilterResponsiveTablesFilter
*/ */
protected $responsive_filter; protected $responsiveFilter;
/** /**
* {@inheritdoc} * {@inheritdoc}
...@@ -36,16 +36,17 @@ class FilterProcessTest extends UnitTestCase { ...@@ -36,16 +36,17 @@ class FilterProcessTest extends UnitTestCase {
'allowed_html' => '<a href> <p> <em> <strong> <cite> <blockquote> <code> <ul> <ol> <li> <dl> <dt> <dd> <br> <h3 id> <table class additional> <th> <tr> <td> <thead> <tbody> <tfoot>', 'allowed_html' => '<a href> <p> <em> <strong> <cite> <blockquote> <code> <ul> <ol> <li> <dl> <dt> <dd> <br> <h3 id> <table class additional> <th> <tr> <td> <thead> <tbody> <tfoot>',
'filter_html_help' => 1, 'filter_html_help' => 1,
'filter_html_nofollow' => 0, 'filter_html_nofollow' => 0,
'filter_responsive_tables_filter' => ["tablesaw_type" => "stack"],
]; ];
$this->filter = new FilterHtml($configuration, 'filter_html', ['provider' => 'test']); $this->filter = new FilterHtml($configuration, 'filter_html', ['provider' => 'test']);
$this->filter->setStringTranslation($this->getStringTranslationStub()); $this->filter->setStringTranslation($this->getStringTranslationStub());
// See Drupal\Core\Plugin\PluginBase. // See Drupal\Core\Plugin\PluginBase.
$this->responsive_filter = new FilterResponsiveTablesFilter(array(), 'filter_responsive_tables_filter', ['provider' => 'test']); $this->responsiveFilter = new FilterResponsiveTablesFilter([], 'filter_responsive_tables_filter', ['provider' => 'test']);
} }
/** /**
* @covers ::responsive_tables_filter * @covers ::runFilter
* *
* @dataProvider providerFilterAttributes * @dataProvider providerFilterAttributes
* *
...@@ -56,7 +57,7 @@ class FilterProcessTest extends UnitTestCase { ...@@ -56,7 +57,7 @@ class FilterProcessTest extends UnitTestCase {
*/ */
public function testfilterAttributes($html, $expected) { public function testfilterAttributes($html, $expected) {
$html_filter = $this->filter->filterAttributes($html); $html_filter = $this->filter->filterAttributes($html);
$result = $this->responsive_filter->responsive_tables_filter($html_filter); $result = $this->responsiveFilter->runFilter($html_filter);
$this->assertSame($expected, $result); $this->assertSame($expected, $result);
} }
...@@ -68,10 +69,10 @@ class FilterProcessTest extends UnitTestCase { ...@@ -68,10 +69,10 @@ class FilterProcessTest extends UnitTestCase {
*/ */
public function providerFilterAttributes() { public function providerFilterAttributes() {
return [ return [
['<table></table>', '<table class="tablesaw tablesaw-stack" data-tablesaw-mode="stack"></table>'], ['<table></table>', '<table class="tablesaw tablesaw-stack" data-tablesaw-mode="stack" data-tablesaw-minimap=""></table>'],
['<table class="test"></table>', '<table class="test tablesaw tablesaw-stack" data-tablesaw-mode="stack"></table>'], ['<table class="test"></table>', '<table class="test tablesaw tablesaw-stack" data-tablesaw-mode="stack" data-tablesaw-minimap=""></table>'],
['<table class="no-tablesaw"></table>', '<table class="no-tablesaw"></table>'], ['<table class="no-tablesaw"></table>', '<table class="no-tablesaw"></table>'],
['<table additional="test"><thead><tr><th>Header One<th>Header 2<tbody><tr><td>Easily add tables with the WYSIWYG toolbar<td>Encoded characters test öô & , ?<tr><td>Tables respond to display on smaller screens<td>Fully accessible to screen readers</table>', '<table additional="test" class="tablesaw tablesaw-stack" data-tablesaw-mode="stack"> ['<table additional="test"><thead><tr><th>Header One<th>Header 2<tbody><tr><td>Easily add tables with the WYSIWYG toolbar<td>Encoded characters test öô & , ?<tr><td>Tables respond to display on smaller screens<td>Fully accessible to screen readers</table>', '<table additional="test" class="tablesaw tablesaw-stack" data-tablesaw-mode="stack" data-tablesaw-minimap="">
<thead><tr> <thead><tr>
<th>Header One</th> <th>Header One</th>
<th>Header 2</th> <th>Header 2</th>
......