/** * @file * Responsive table functionality. */ (function ($, Drupal, window) { /** * The TableResponsive object optimizes table presentation for screen size. * * A responsive table hides columns at small screen sizes, leaving the most * important columns visible to the end user. Users should not be prevented * from accessing all columns, however. This class adds a toggle to a table * with hidden columns that exposes the columns. Exposing the columns will * likely break layouts, but it provides the user with a means to access * data, which is a guiding principle of responsive design. * * @constructor Drupal.TableResponsive * * @param {HTMLElement} table * The table element to initialize the responsive table on. */ function TableResponsive(table) { this.table = table; this.$table = $(table); this.showText = Drupal.t('Show all columns'); this.hideText = Drupal.t('Hide lower priority columns'); // Store a reference to the header elements of the table so that the DOM is // traversed only once to find them. this.$headers = this.$table.find('th'); // Add a link before the table for users to show or hide weight columns. this.$link = $( '', ) .attr( 'title', Drupal.t( 'Show table cells that were hidden to make the table fit within a small screen.', ), ) .on('click', this.eventhandlerToggleColumns.bind(this)); this.$table.before( $('
').append( this.$link, ), ); // Attach a resize handler to the window. $(window) .on( 'resize.tableresponsive', this.eventhandlerEvaluateColumnVisibility.bind(this), ) .trigger('resize.tableresponsive'); } /** * Attach the tableResponsive function to {@link Drupal.behaviors}. * * @type {Drupal~behavior} * * @prop {Drupal~behaviorAttach} attach * Attaches tableResponsive functionality. */ Drupal.behaviors.tableResponsive = { attach(context, settings) { once('tableresponsive', 'table.responsive-enabled', context).forEach( (table) => { TableResponsive.tables.push(new TableResponsive(table)); }, ); }, }; /** * Extend the TableResponsive function with a list of managed tables. */ $.extend( TableResponsive, /** @lends Drupal.TableResponsive */ { /** * Store all created instances. * * @type {Array.} */ tables: [], }, ); /** * Associates an action link with the table that will show hidden columns. * * Columns are assumed to be hidden if their header has the class priority-low * or priority-medium. */ $.extend( TableResponsive.prototype, /** @lends Drupal.TableResponsive# */ { /** * @param {jQuery.Event} e * The event triggered. */ eventhandlerEvaluateColumnVisibility(e) { const pegged = parseInt(this.$link.data('pegged'), 10); const hiddenLength = this.$headers.filter( '.priority-medium:hidden, .priority-low:hidden', ).length; // If the table has hidden columns, associate an action link with the // table to show the columns. if (hiddenLength > 0) { this.$link.show(); this.$link[0].textContent = this.showText; } // When the toggle is pegged, its presence is maintained because the user // has interacted with it. This is necessary to keep the link visible if // the user adjusts screen size and changes the visibility of columns. if (!pegged && hiddenLength === 0) { this.$link.hide(); this.$link[0].textContent = this.hideText; } }, /** * Toggle the visibility of columns based on their priority. * * Columns are classed with either 'priority-low' or 'priority-medium'. * * @param {jQuery.Event} e * The event triggered. */ eventhandlerToggleColumns(e) { e.preventDefault(); const self = this; const $hiddenHeaders = this.$headers.filter( '.priority-medium:hidden, .priority-low:hidden', ); this.$revealedCells = this.$revealedCells || $(); // Reveal hidden columns. if ($hiddenHeaders.length > 0) { $hiddenHeaders.each(function (index, element) { const $header = $(this); const position = $header.prevAll('th').length; self.$table.find('tbody tr').each(function () { const $cells = $(this).find('td').eq(position); $cells.show(); // Keep track of the revealed cells, so they can be hidden later. self.$revealedCells = $().add(self.$revealedCells).add($cells); }); $header.show(); // Keep track of the revealed headers, so they can be hidden later. self.$revealedCells = $().add(self.$revealedCells).add($header); }); this.$link[0].textContent = this.hideText; this.$link.data('pegged', 1); } // Hide revealed columns. else { this.$revealedCells.hide(); // Strip the 'display:none' declaration from the style attributes of // the table cells that .hide() added. this.$revealedCells.each(function (index, element) { const $cell = $(this); const properties = $cell.attr('style').split(';'); const newProps = []; // The hide method adds display none to the element. The element // should be returned to the same state it was in before the columns // were revealed, so it is necessary to remove the display none value // from the style attribute. const match = /^display\s*:\s*none$/; for (let i = 0; i < properties.length; i++) { const prop = properties[i]; prop.trim(); // Find the display:none property and remove it. const isDisplayNone = match.exec(prop); if (isDisplayNone) { continue; } newProps.push(prop); } // Return the rest of the style attribute values to the element. $cell.attr('style', newProps.join(';')); }); this.$link[0].textContent = this.showText; this.$link.data('pegged', 0); // Refresh the toggle link. $(window).trigger('resize.tableresponsive'); } }, }, ); // Make the TableResponsive object available in the Drupal namespace. Drupal.TableResponsive = TableResponsive; })(jQuery, Drupal, window);