dialog.ajax.es6.js 7.9 KB

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