toolbar.menu.es6.js 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
  1. /**
  2. * @file
  3. * Builds a nested accordion widget.
  4. *
  5. * Invoke on an HTML list element with the jQuery plugin pattern.
  6. *
  7. * @example
  8. * $('.toolbar-menu').drupalToolbarMenu();
  9. */
  10. (function($, Drupal, drupalSettings) {
  11. /**
  12. * Store the open menu tray.
  13. */
  14. let activeItem = Drupal.url(drupalSettings.path.currentPath);
  15. $.fn.drupalToolbarMenu = function() {
  16. const ui = {
  17. handleOpen: Drupal.t('Extend'),
  18. handleClose: Drupal.t('Collapse'),
  19. };
  20. /**
  21. * Toggle the open/close state of a list is a menu.
  22. *
  23. * @param {jQuery} $item
  24. * The li item to be toggled.
  25. *
  26. * @param {Boolean} switcher
  27. * A flag that forces toggleClass to add or a remove a class, rather than
  28. * simply toggling its presence.
  29. */
  30. function toggleList($item, switcher) {
  31. const $toggle = $item
  32. .children('.toolbar-box')
  33. .children('.toolbar-handle');
  34. switcher =
  35. typeof switcher !== 'undefined' ? switcher : !$item.hasClass('open');
  36. // Toggle the item open state.
  37. $item.toggleClass('open', switcher);
  38. // Twist the toggle.
  39. $toggle.toggleClass('open', switcher);
  40. // Adjust the toggle text.
  41. $toggle
  42. .find('.action')
  43. // Expand Structure, Collapse Structure.
  44. .text(switcher ? ui.handleClose : ui.handleOpen);
  45. }
  46. /**
  47. * Handle clicks from the disclosure button on an item with sub-items.
  48. *
  49. * @param {Object} event
  50. * A jQuery Event object.
  51. */
  52. function toggleClickHandler(event) {
  53. const $toggle = $(event.target);
  54. const $item = $toggle.closest('li');
  55. // Toggle the list item.
  56. toggleList($item);
  57. // Close open sibling menus.
  58. const $openItems = $item.siblings().filter('.open');
  59. toggleList($openItems, false);
  60. }
  61. /**
  62. * Handle clicks from a menu item link.
  63. *
  64. * @param {Object} event
  65. * A jQuery Event object.
  66. */
  67. function linkClickHandler(event) {
  68. // If the toolbar is positioned fixed (and therefore hiding content
  69. // underneath), then users expect clicks in the administration menu tray
  70. // to take them to that destination but for the menu tray to be closed
  71. // after clicking: otherwise the toolbar itself is obstructing the view
  72. // of the destination they chose.
  73. if (!Drupal.toolbar.models.toolbarModel.get('isFixed')) {
  74. Drupal.toolbar.models.toolbarModel.set('activeTab', null);
  75. }
  76. // Stopping propagation to make sure that once a toolbar-box is clicked
  77. // (the whitespace part), the page is not redirected anymore.
  78. event.stopPropagation();
  79. }
  80. /**
  81. * Add markup to the menu elements.
  82. *
  83. * Items with sub-elements have a list toggle attached to them. Menu item
  84. * links and the corresponding list toggle are wrapped with in a div
  85. * classed with .toolbar-box. The .toolbar-box div provides a positioning
  86. * context for the item list toggle.
  87. *
  88. * @param {jQuery} $menu
  89. * The root of the menu to be initialized.
  90. */
  91. function initItems($menu) {
  92. const options = {
  93. class: 'toolbar-icon toolbar-handle',
  94. action: ui.handleOpen,
  95. text: '',
  96. };
  97. // Initialize items and their links.
  98. $menu.find('li > a').wrap('<div class="toolbar-box">');
  99. // Add a handle to each list item if it has a menu.
  100. $menu.find('li').each((index, element) => {
  101. const $item = $(element);
  102. if ($item.children('ul.toolbar-menu').length) {
  103. const $box = $item.children('.toolbar-box');
  104. options.text = Drupal.t('@label', {
  105. '@label': $box.find('a').text(),
  106. });
  107. $item
  108. .children('.toolbar-box')
  109. .append(Drupal.theme('toolbarMenuItemToggle', options));
  110. }
  111. });
  112. }
  113. /**
  114. * Adds a level class to each list based on its depth in the menu.
  115. *
  116. * This function is called recursively on each sub level of lists elements
  117. * until the depth of the menu is exhausted.
  118. *
  119. * @param {jQuery} $lists
  120. * A jQuery object of ul elements.
  121. *
  122. * @param {number} level
  123. * The current level number to be assigned to the list elements.
  124. */
  125. function markListLevels($lists, level) {
  126. level = !level ? 1 : level;
  127. const $lis = $lists.children('li').addClass(`level-${level}`);
  128. $lists = $lis.children('ul');
  129. if ($lists.length) {
  130. markListLevels($lists, level + 1);
  131. }
  132. }
  133. /**
  134. * On page load, open the active menu item.
  135. *
  136. * Marks the trail of the active link in the menu back to the root of the
  137. * menu with .menu-item--active-trail.
  138. *
  139. * @param {jQuery} $menu
  140. * The root of the menu.
  141. */
  142. function openActiveItem($menu) {
  143. const pathItem = $menu.find(`a[href="${window.location.pathname}"]`);
  144. if (pathItem.length && !activeItem) {
  145. activeItem = window.location.pathname;
  146. }
  147. if (activeItem) {
  148. const $activeItem = $menu
  149. .find(`a[href="${activeItem}"]`)
  150. .addClass('menu-item--active');
  151. const $activeTrail = $activeItem
  152. .parentsUntil('.root', 'li')
  153. .addClass('menu-item--active-trail');
  154. toggleList($activeTrail, true);
  155. }
  156. }
  157. // Return the jQuery object.
  158. return this.each(function(selector) {
  159. const $menu = $(this).once('toolbar-menu');
  160. if ($menu.length) {
  161. // Bind event handlers.
  162. $menu
  163. .on('click.toolbar', '.toolbar-box', toggleClickHandler)
  164. .on('click.toolbar', '.toolbar-box a', linkClickHandler);
  165. $menu.addClass('root');
  166. initItems($menu);
  167. markListLevels($menu);
  168. // Restore previous and active states.
  169. openActiveItem($menu);
  170. }
  171. });
  172. };
  173. /**
  174. * A toggle is an interactive element often bound to a click handler.
  175. *
  176. * @param {object} options
  177. * Options for the button.
  178. * @param {string} options.class
  179. * Class to set on the button.
  180. * @param {string} options.action
  181. * Action for the button.
  182. * @param {string} options.text
  183. * Used as label for the button.
  184. *
  185. * @return {string}
  186. * A string representing a DOM fragment.
  187. */
  188. Drupal.theme.toolbarMenuItemToggle = function(options) {
  189. return `<button class="${options.class}"><span class="action">${
  190. options.action
  191. }</span> <span class="label">${options.text}</span></button>`;
  192. };
  193. })(jQuery, Drupal, drupalSettings);