tableresponsive.es6.js 6.8 KB

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