1/** 2 * @file 3 * Responsive table functionality. 4 */ 5 6(function ($, Drupal, window) { 7 /** 8 * The TableResponsive object optimizes table presentation for screen size. 9 * 10 * A responsive table hides columns at small screen sizes, leaving the most 11 * important columns visible to the end user. Users should not be prevented 12 * from accessing all columns, however. This class adds a toggle to a table 13 * with hidden columns that exposes the columns. Exposing the columns will 14 * likely break layouts, but it provides the user with a means to access 15 * data, which is a guiding principle of responsive design. 16 * 17 * @constructor Drupal.TableResponsive 18 * 19 * @param {HTMLElement} table 20 * The table element to initialize the responsive table on. 21 */ 22 function TableResponsive(table) { 23 this.table = table; 24 this.$table = $(table); 25 this.showText = Drupal.t('Show all columns'); 26 this.hideText = Drupal.t('Hide lower priority columns'); 27 // Store a reference to the header elements of the table so that the DOM is 28 // traversed only once to find them. 29 this.$headers = this.$table.find('th'); 30 // Add a link before the table for users to show or hide weight columns. 31 this.$link = $( 32 '<button type="button" class="link tableresponsive-toggle"></button>', 33 ) 34 .attr( 35 'title', 36 Drupal.t( 37 'Show table cells that were hidden to make the table fit within a small screen.', 38 ), 39 ) 40 .on('click', this.eventhandlerToggleColumns.bind(this)); 41 42 this.$table.before( 43 $('<div class="tableresponsive-toggle-columns"></div>').append( 44 this.$link, 45 ), 46 ); 47 48 // Attach a resize handler to the window. 49 $(window).on( 50 'resize.tableresponsive', 51 this.eventhandlerEvaluateColumnVisibility.bind(this), 52 ); 53 } 54 55 /** 56 * Attach the tableResponsive function to {@link Drupal.behaviors}. 57 * 58 * @type {Drupal~behavior} 59 * 60 * @prop {Drupal~behaviorAttach} attach 61 * Attaches tableResponsive functionality. 62 */ 63 Drupal.behaviors.tableResponsive = { 64 attach(context, settings) { 65 once('tableresponsive', 'table.responsive-enabled', context).forEach( 66 (table) => { 67 TableResponsive.tables.push(new TableResponsive(table)); 68 }, 69 ); 70 if (TableResponsive.tables.length) { 71 $(window).trigger('resize.tableresponsive'); 72 } 73 }, 74 }; 75 76 /** 77 * Extend the TableResponsive function with a list of managed tables. 78 */ 79 $.extend( 80 TableResponsive, 81 /** @lends Drupal.TableResponsive */ { 82 /** 83 * Store all created instances. 84 * 85 * @type {Array.<Drupal.TableResponsive>} 86 */ 87 tables: [], 88 }, 89 ); 90 91 /** 92 * Associates an action link with the table that will show hidden columns. 93 * 94 * Columns are assumed to be hidden if their header has the class priority-low 95 * or priority-medium. 96 */ 97 $.extend( 98 TableResponsive.prototype, 99 /** @lends Drupal.TableResponsive# */ { 100 /** 101 * @param {jQuery.Event} e 102 * The event triggered. 103 */ 104 eventhandlerEvaluateColumnVisibility(e) { 105 const pegged = parseInt(this.$link.data('pegged'), 10); 106 const hiddenLength = this.$headers.filter( 107 '.priority-medium:hidden, .priority-low:hidden', 108 ).length; 109 // If the table has hidden columns, associate an action link with the 110 // table to show the columns. 111 if (hiddenLength > 0) { 112 this.$link.show(); 113 this.$link[0].textContent = this.showText; 114 } 115 // When the toggle is pegged, its presence is maintained because the user 116 // has interacted with it. This is necessary to keep the link visible if 117 // the user adjusts screen size and changes the visibility of columns. 118 if (!pegged && hiddenLength === 0) { 119 this.$link.hide(); 120 this.$link[0].textContent = this.hideText; 121 } 122 }, 123 124 /** 125 * Toggle the visibility of columns based on their priority. 126 * 127 * Columns are classed with either 'priority-low' or 'priority-medium'. 128 * 129 * @param {jQuery.Event} e 130 * The event triggered. 131 */ 132 eventhandlerToggleColumns(e) { 133 e.preventDefault(); 134 const self = this; 135 const $hiddenHeaders = this.$headers.filter( 136 '.priority-medium:hidden, .priority-low:hidden', 137 ); 138 this.$revealedCells = this.$revealedCells || $(); 139 // Reveal hidden columns. 140 if ($hiddenHeaders.length > 0) { 141 $hiddenHeaders.each(function (index, element) { 142 const $header = $(this); 143 const position = $header.prevAll('th').length; 144 self.$table.find('tbody tr').each(function () { 145 const $cells = $(this).find('td').eq(position); 146 $cells.show(); 147 // Keep track of the revealed cells, so they can be hidden later. 148 self.$revealedCells = $().add(self.$revealedCells).add($cells); 149 }); 150 $header.show(); 151 // Keep track of the revealed headers, so they can be hidden later. 152 self.$revealedCells = $().add(self.$revealedCells).add($header); 153 }); 154 this.$link[0].textContent = this.hideText; 155 this.$link.data('pegged', 1); 156 } 157 // Hide revealed columns. 158 else { 159 this.$revealedCells.hide(); 160 // Strip the 'display:none' declaration from the style attributes of 161 // the table cells that .hide() added.
162 this.$revealedCells.each(function (index, element) { 163 const $cell = $(this); 164 const properties = $cell.attr('style').split(';'); 165 const newProps = []; 166 // The hide method adds display none to the element. The element 167 // should be returned to the same state it was in before the columns 168 // were revealed, so it is necessary to remove the display none value 169 // from the style attribute. 170 const match = /^display\s*:\s*none$/; 171 for (let i = 0; i < properties.length; i++) { 172 const prop = properties[i]; 173 prop.trim(); 174 // Find the display:none property and remove it. 175 const isDisplayNone = match.exec(prop); 176 if (isDisplayNone) { 177 continue; 178 } 179 newProps.push(prop); 180 } 181 // Return the rest of the style attribute values to the element. 182 $cell.attr('style', newProps.join(';')); 183 }); 184 this.$link[0].textContent = this.showText; 185 this.$link.data('pegged', 0); 186 // Refresh the toggle link. 187 $(window).trigger('resize.tableresponsive'); 188 } 189 }, 190 }, 191 ); 192 193 // Make the TableResponsive object available in the Drupal namespace. 194 Drupal.TableResponsive = TableResponsive; 195})(jQuery, Drupal, window);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.