dropbutton.js 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233
  1. /**
  2. * @file
  3. * Dropbutton feature.
  4. */
  5. (function ($, Drupal) {
  6. 'use strict';
  7. /**
  8. * Process elements with the .dropbutton class on page load.
  9. *
  10. * @type {Drupal~behavior}
  11. *
  12. * @prop {Drupal~behaviorAttach} attach
  13. * Attaches dropButton behaviors.
  14. */
  15. Drupal.behaviors.dropButton = {
  16. attach: function (context, settings) {
  17. var $dropbuttons = $(context).find('.dropbutton-wrapper').once('dropbutton');
  18. if ($dropbuttons.length) {
  19. // Adds the delegated handler that will toggle dropdowns on click.
  20. var $body = $('body').once('dropbutton-click');
  21. if ($body.length) {
  22. $body.on('click', '.dropbutton-toggle', dropbuttonClickHandler);
  23. }
  24. // Initialize all buttons.
  25. var il = $dropbuttons.length;
  26. for (var i = 0; i < il; i++) {
  27. DropButton.dropbuttons.push(new DropButton($dropbuttons[i], settings.dropbutton));
  28. }
  29. }
  30. }
  31. };
  32. /**
  33. * Delegated callback for opening and closing dropbutton secondary actions.
  34. *
  35. * @function Drupal.DropButton~dropbuttonClickHandler
  36. *
  37. * @param {jQuery.Event} e
  38. * The event triggered.
  39. */
  40. function dropbuttonClickHandler(e) {
  41. e.preventDefault();
  42. $(e.target).closest('.dropbutton-wrapper').toggleClass('open');
  43. }
  44. /**
  45. * A DropButton presents an HTML list as a button with a primary action.
  46. *
  47. * All secondary actions beyond the first in the list are presented in a
  48. * dropdown list accessible through a toggle arrow associated with the button.
  49. *
  50. * @constructor Drupal.DropButton
  51. *
  52. * @param {HTMLElement} dropbutton
  53. * A DOM element.
  54. * @param {object} settings
  55. * A list of options including:
  56. * @param {string} settings.title
  57. * The text inside the toggle link element. This text is hidden
  58. * from visual UAs.
  59. */
  60. function DropButton(dropbutton, settings) {
  61. // Merge defaults with settings.
  62. var options = $.extend({title: Drupal.t('List additional actions')}, settings);
  63. var $dropbutton = $(dropbutton);
  64. /**
  65. * @type {jQuery}
  66. */
  67. this.$dropbutton = $dropbutton;
  68. /**
  69. * @type {jQuery}
  70. */
  71. this.$list = $dropbutton.find('.dropbutton');
  72. /**
  73. * Find actions and mark them.
  74. *
  75. * @type {jQuery}
  76. */
  77. this.$actions = this.$list.find('li').addClass('dropbutton-action');
  78. // Add the special dropdown only if there are hidden actions.
  79. if (this.$actions.length > 1) {
  80. // Identify the first element of the collection.
  81. var $primary = this.$actions.slice(0, 1);
  82. // Identify the secondary actions.
  83. var $secondary = this.$actions.slice(1);
  84. $secondary.addClass('secondary-action');
  85. // Add toggle link.
  86. $primary.after(Drupal.theme('dropbuttonToggle', options));
  87. // Bind mouse events.
  88. this.$dropbutton
  89. .addClass('dropbutton-multiple')
  90. .on({
  91. /**
  92. * Adds a timeout to close the dropdown on mouseleave.
  93. *
  94. * @ignore
  95. */
  96. 'mouseleave.dropbutton': $.proxy(this.hoverOut, this),
  97. /**
  98. * Clears timeout when mouseout of the dropdown.
  99. *
  100. * @ignore
  101. */
  102. 'mouseenter.dropbutton': $.proxy(this.hoverIn, this),
  103. /**
  104. * Similar to mouseleave/mouseenter, but for keyboard navigation.
  105. *
  106. * @ignore
  107. */
  108. 'focusout.dropbutton': $.proxy(this.focusOut, this),
  109. /**
  110. * @ignore
  111. */
  112. 'focusin.dropbutton': $.proxy(this.focusIn, this)
  113. });
  114. }
  115. else {
  116. this.$dropbutton.addClass('dropbutton-single');
  117. }
  118. }
  119. /**
  120. * Extend the DropButton constructor.
  121. */
  122. $.extend(DropButton, /** @lends Drupal.DropButton */{
  123. /**
  124. * Store all processed DropButtons.
  125. *
  126. * @type {Array.<Drupal.DropButton>}
  127. */
  128. dropbuttons: []
  129. });
  130. /**
  131. * Extend the DropButton prototype.
  132. */
  133. $.extend(DropButton.prototype, /** @lends Drupal.DropButton# */{
  134. /**
  135. * Toggle the dropbutton open and closed.
  136. *
  137. * @param {bool} [show]
  138. * Force the dropbutton to open by passing true or to close by
  139. * passing false.
  140. */
  141. toggle: function (show) {
  142. var isBool = typeof show === 'boolean';
  143. show = isBool ? show : !this.$dropbutton.hasClass('open');
  144. this.$dropbutton.toggleClass('open', show);
  145. },
  146. /**
  147. * @method
  148. */
  149. hoverIn: function () {
  150. // Clear any previous timer we were using.
  151. if (this.timerID) {
  152. window.clearTimeout(this.timerID);
  153. }
  154. },
  155. /**
  156. * @method
  157. */
  158. hoverOut: function () {
  159. // Wait half a second before closing.
  160. this.timerID = window.setTimeout($.proxy(this, 'close'), 500);
  161. },
  162. /**
  163. * @method
  164. */
  165. open: function () {
  166. this.toggle(true);
  167. },
  168. /**
  169. * @method
  170. */
  171. close: function () {
  172. this.toggle(false);
  173. },
  174. /**
  175. * @param {jQuery.Event} e
  176. * The event triggered.
  177. */
  178. focusOut: function (e) {
  179. this.hoverOut.call(this, e);
  180. },
  181. /**
  182. * @param {jQuery.Event} e
  183. * The event triggered.
  184. */
  185. focusIn: function (e) {
  186. this.hoverIn.call(this, e);
  187. }
  188. });
  189. $.extend(Drupal.theme, /** @lends Drupal.theme */{
  190. /**
  191. * A toggle is an interactive element often bound to a click handler.
  192. *
  193. * @param {object} options
  194. * Options object.
  195. * @param {string} [options.title]
  196. * The HTML anchor title attribute and text for the inner span element.
  197. *
  198. * @return {string}
  199. * A string representing a DOM fragment.
  200. */
  201. dropbuttonToggle: function (options) {
  202. return '<li class="dropbutton-toggle"><button type="button"><span class="dropbutton-arrow"><span class="visually-hidden">' + options.title + '</span></span></button></li>';
  203. }
  204. });
  205. // Expose constructor in the public space.
  206. Drupal.DropButton = DropButton;
  207. })(jQuery, Drupal);