tableresponsive.es6.js 6.4 KB

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