dialog.ajax.es6.js 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258
  1. /**
  2. * @file
  3. * Extends the Drupal AJAX functionality to integrate the dialog API.
  4. */
  5. (function($, Drupal) {
  6. /**
  7. * Initialize dialogs for Ajax purposes.
  8. *
  9. * @type {Drupal~behavior}
  10. *
  11. * @prop {Drupal~behaviorAttach} attach
  12. * Attaches the behaviors for dialog ajax functionality.
  13. */
  14. Drupal.behaviors.dialog = {
  15. attach(context, settings) {
  16. const $context = $(context);
  17. // Provide a known 'drupal-modal' DOM element for Drupal-based modal
  18. // dialogs. Non-modal dialogs are responsible for creating their own
  19. // elements, since there can be multiple non-modal dialogs at a time.
  20. if (!$('#drupal-modal').length) {
  21. // Add 'ui-front' jQuery UI class so jQuery UI widgets like autocomplete
  22. // sit on top of dialogs. For more information see
  23. // http://api.jqueryui.com/theming/stacking-elements/.
  24. $('<div id="drupal-modal" class="ui-front"/>')
  25. .hide()
  26. .appendTo('body');
  27. }
  28. // Special behaviors specific when attaching content within a dialog.
  29. // These behaviors usually fire after a validation error inside a dialog.
  30. const $dialog = $context.closest('.ui-dialog-content');
  31. if ($dialog.length) {
  32. // Remove and replace the dialog buttons with those from the new form.
  33. if ($dialog.dialog('option', 'drupalAutoButtons')) {
  34. // Trigger an event to detect/sync changes to buttons.
  35. $dialog.trigger('dialogButtonsChange');
  36. }
  37. // Force focus on the modal when the behavior is run.
  38. $dialog.dialog('widget').trigger('focus');
  39. }
  40. const originalClose = settings.dialog.close;
  41. // Overwrite the close method to remove the dialog on closing.
  42. settings.dialog.close = function(event, ...args) {
  43. originalClose.apply(settings.dialog, [event, ...args]);
  44. $(event.target).remove();
  45. };
  46. },
  47. /**
  48. * Scan a dialog for any primary buttons and move them to the button area.
  49. *
  50. * @param {jQuery} $dialog
  51. * An jQuery object containing the element that is the dialog target.
  52. *
  53. * @return {Array}
  54. * An array of buttons that need to be added to the button area.
  55. */
  56. prepareDialogButtons($dialog) {
  57. const buttons = [];
  58. const $buttons = $dialog.find(
  59. '.form-actions input[type=submit], .form-actions a.button',
  60. );
  61. $buttons.each(function() {
  62. // Hidden form buttons need special attention. For browser consistency,
  63. // the button needs to be "visible" in order to have the enter key fire
  64. // the form submit event. So instead of a simple "hide" or
  65. // "display: none", we set its dimensions to zero.
  66. // See http://mattsnider.com/how-forms-submit-when-pressing-enter/
  67. const $originalButton = $(this).css({
  68. display: 'block',
  69. width: 0,
  70. height: 0,
  71. padding: 0,
  72. border: 0,
  73. overflow: 'hidden',
  74. });
  75. buttons.push({
  76. text: $originalButton.html() || $originalButton.attr('value'),
  77. class: $originalButton.attr('class'),
  78. click(e) {
  79. // If the original button is an anchor tag, triggering the "click"
  80. // event will not simulate a click. Use the click method instead.
  81. if ($originalButton.is('a')) {
  82. $originalButton[0].click();
  83. } else {
  84. $originalButton
  85. .trigger('mousedown')
  86. .trigger('mouseup')
  87. .trigger('click');
  88. e.preventDefault();
  89. }
  90. },
  91. });
  92. });
  93. return buttons;
  94. },
  95. };
  96. /**
  97. * Command to open a dialog.
  98. *
  99. * @param {Drupal.Ajax} ajax
  100. * The Drupal Ajax object.
  101. * @param {object} response
  102. * Object holding the server response.
  103. * @param {number} [status]
  104. * The HTTP status code.
  105. *
  106. * @return {bool|undefined}
  107. * Returns false if there was no selector property in the response object.
  108. */
  109. Drupal.AjaxCommands.prototype.openDialog = function(ajax, response, status) {
  110. if (!response.selector) {
  111. return false;
  112. }
  113. let $dialog = $(response.selector);
  114. if (!$dialog.length) {
  115. // Create the element if needed.
  116. $dialog = $(
  117. `<div id="${response.selector.replace(/^#/, '')}" class="ui-front"/>`,
  118. ).appendTo('body');
  119. }
  120. // Set up the wrapper, if there isn't one.
  121. if (!ajax.wrapper) {
  122. ajax.wrapper = $dialog.attr('id');
  123. }
  124. // Use the ajax.js insert command to populate the dialog contents.
  125. response.command = 'insert';
  126. response.method = 'html';
  127. ajax.commands.insert(ajax, response, status);
  128. // Move the buttons to the jQuery UI dialog buttons area.
  129. if (!response.dialogOptions.buttons) {
  130. response.dialogOptions.drupalAutoButtons = true;
  131. response.dialogOptions.buttons = Drupal.behaviors.dialog.prepareDialogButtons(
  132. $dialog,
  133. );
  134. }
  135. // Bind dialogButtonsChange.
  136. $dialog.on('dialogButtonsChange', () => {
  137. const buttons = Drupal.behaviors.dialog.prepareDialogButtons($dialog);
  138. $dialog.dialog('option', 'buttons', buttons);
  139. });
  140. // Open the dialog itself.
  141. response.dialogOptions = response.dialogOptions || {};
  142. const dialog = Drupal.dialog($dialog.get(0), response.dialogOptions);
  143. if (response.dialogOptions.modal) {
  144. dialog.showModal();
  145. } else {
  146. dialog.show();
  147. }
  148. // Add the standard Drupal class for buttons for style consistency.
  149. $dialog
  150. .parent()
  151. .find('.ui-dialog-buttonset')
  152. .addClass('form-actions');
  153. };
  154. /**
  155. * Command to close a dialog.
  156. *
  157. * If no selector is given, it defaults to trying to close the modal.
  158. *
  159. * @param {Drupal.Ajax} [ajax]
  160. * The ajax object.
  161. * @param {object} response
  162. * Object holding the server response.
  163. * @param {string} response.selector
  164. * The selector of the dialog.
  165. * @param {bool} response.persist
  166. * Whether to persist the dialog element or not.
  167. * @param {number} [status]
  168. * The HTTP status code.
  169. */
  170. Drupal.AjaxCommands.prototype.closeDialog = function(ajax, response, status) {
  171. const $dialog = $(response.selector);
  172. if ($dialog.length) {
  173. Drupal.dialog($dialog.get(0)).close();
  174. if (!response.persist) {
  175. $dialog.remove();
  176. }
  177. }
  178. // Unbind dialogButtonsChange.
  179. $dialog.off('dialogButtonsChange');
  180. };
  181. /**
  182. * Command to set a dialog property.
  183. *
  184. * JQuery UI specific way of setting dialog options.
  185. *
  186. * @param {Drupal.Ajax} [ajax]
  187. * The Drupal Ajax object.
  188. * @param {object} response
  189. * Object holding the server response.
  190. * @param {string} response.selector
  191. * Selector for the dialog element.
  192. * @param {string} response.optionsName
  193. * Name of a key to set.
  194. * @param {string} response.optionValue
  195. * Value to set.
  196. * @param {number} [status]
  197. * The HTTP status code.
  198. */
  199. Drupal.AjaxCommands.prototype.setDialogOption = function(
  200. ajax,
  201. response,
  202. status,
  203. ) {
  204. const $dialog = $(response.selector);
  205. if ($dialog.length) {
  206. $dialog.dialog('option', response.optionName, response.optionValue);
  207. }
  208. };
  209. /**
  210. * Binds a listener on dialog creation to handle the cancel link.
  211. *
  212. * @param {jQuery.Event} e
  213. * The event triggered.
  214. * @param {Drupal.dialog~dialogDefinition} dialog
  215. * The dialog instance.
  216. * @param {jQuery} $element
  217. * The jQuery collection of the dialog element.
  218. * @param {object} [settings]
  219. * Dialog settings.
  220. */
  221. $(window).on('dialog:aftercreate', (e, dialog, $element, settings) => {
  222. $element.on('click.dialog', '.dialog-cancel', e => {
  223. dialog.close('cancel');
  224. e.preventDefault();
  225. e.stopPropagation();
  226. });
  227. });
  228. /**
  229. * Removes all 'dialog' listeners.
  230. *
  231. * @param {jQuery.Event} e
  232. * The event triggered.
  233. * @param {Drupal.dialog~dialogDefinition} dialog
  234. * The dialog instance.
  235. * @param {jQuery} $element
  236. * jQuery collection of the dialog element.
  237. */
  238. $(window).on('dialog:beforeclose', (e, dialog, $element) => {
  239. $element.off('.dialog');
  240. });
  241. })(jQuery, Drupal);