lib/amd/src/modal_factory.js

  1. // This file is part of Moodle - http://moodle.org/
  2. //
  3. // Moodle is free software: you can redistribute it and/or modify
  4. // it under the terms of the GNU General Public License as published by
  5. // the Free Software Foundation, either version 3 of the License, or
  6. // (at your option) any later version.
  7. //
  8. // Moodle is distributed in the hope that it will be useful,
  9. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. // GNU General Public License for more details.
  12. //
  13. // You should have received a copy of the GNU General Public License
  14. // along with Moodle. If not, see <http://www.gnu.org/licenses/>.
  15. /**
  16. * Create a modal.
  17. *
  18. * @module core/modal_factory
  19. * @copyright 2016 Ryan Wyllie <ryan@moodle.com>
  20. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  21. * @deprecated since Moodle 4.3
  22. * @todo Final deprecation in Moodle 4.7/5.2. See MDL-79128/
  23. */
  24. import $ from 'jquery';
  25. import ModalEvents from 'core/modal_events';
  26. import * as ModalRegistry from 'core/modal_registry';
  27. import Modal from 'core/modal';
  28. import ModalSaveCancel from 'core/modal_save_cancel';
  29. import ModalDeleteCancel from 'core/modal_delete_cancel';
  30. import ModalCancel from 'core/modal_cancel';
  31. import ModalAlert from 'core/local/modal/alert';
  32. import * as Notification from 'core/notification';
  33. import * as CustomEvents from 'core/custom_interaction_events';
  34. import Pending from 'core/pending';
  35. /**
  36. * The available standard modals.
  37. *
  38. * @property {String} DEFAULT The default modal
  39. * @property {String} SAVE_CANCEL A modal which can be used to either save, or cancel.
  40. * @property {String} DELETE_CANCEL A modal which can be used to either delete, or cancel.
  41. * @property {String} CANCEL A modal which displayed a cancel button
  42. * @property {String} ALERT An information modal which only displays information.
  43. */
  44. export const types = {
  45. DEFAULT: 'DEFAULT',
  46. SAVE_CANCEL: ModalSaveCancel.TYPE,
  47. DELETE_CANCEL: ModalDeleteCancel.TYPE,
  48. CANCEL: ModalCancel.TYPE,
  49. ALERT: ModalAlert.TYPE,
  50. };
  51. // Most modals are self-registering.
  52. // We do not self-register the base Modal because we do not want to define a default TYPE
  53. // on the class that every other modal extends.
  54. ModalRegistry.register(types.DEFAULT, Modal, Modal.TEMPLATE);
  55. /**
  56. * Set up the events required to show the modal and return focus when the modal
  57. * is closed.
  58. *
  59. * @method setUpTrigger
  60. * @private
  61. * @param {Promise} modalPromise The modal instance
  62. * @param {object} triggerElement The jQuery element to open the modal
  63. * @param {object} modalConfig The modal configuration given to the factory
  64. */
  65. const setUpTrigger = (modalPromise, triggerElement, modalConfig) => {
  66. // The element that actually shows the modal.
  67. let actualTriggerElement = null;
  68. // Check if the client has provided a callback function to be called
  69. // before the modal is displayed.
  70. const hasPreShowCallback = (typeof modalConfig.preShowCallback == 'function');
  71. // Function to handle the trigger element being activated.
  72. const triggeredCallback = (e, data) => {
  73. const pendingPromise = new Pending('core/modal_factory:setUpTrigger:triggeredCallback');
  74. actualTriggerElement = $(e.currentTarget);
  75. // eslint-disable-next-line promise/catch-or-return
  76. modalPromise.then(function(modal) {
  77. if (hasPreShowCallback) {
  78. // If the client provided a pre-show callback then execute
  79. // it now before showing the modal.
  80. modalConfig.preShowCallback(actualTriggerElement, modal);
  81. }
  82. modal.show();
  83. return modal;
  84. })
  85. .then(pendingPromise.resolve);
  86. data.originalEvent.preventDefault();
  87. };
  88. // The trigger element can either be a single element or it can be an
  89. // element + selector pair to create a delegated event handler to trigger
  90. // the modal.
  91. if (Array.isArray(triggerElement)) {
  92. const selector = triggerElement[1];
  93. triggerElement = triggerElement[0];
  94. CustomEvents.define(triggerElement, [CustomEvents.events.activate]);
  95. triggerElement.on(CustomEvents.events.activate, selector, triggeredCallback);
  96. } else {
  97. CustomEvents.define(triggerElement, [CustomEvents.events.activate]);
  98. triggerElement.on(CustomEvents.events.activate, triggeredCallback);
  99. }
  100. // eslint-disable-next-line promise/catch-or-return
  101. modalPromise.then(function(modal) {
  102. modal.getRoot().on(ModalEvents.hidden, function() {
  103. // Focus on the trigger element that actually launched the modal.
  104. if (actualTriggerElement !== null) {
  105. actualTriggerElement.focus();
  106. }
  107. });
  108. return modal;
  109. });
  110. };
  111. /**
  112. * Create a Modal instance.
  113. *
  114. * @method create
  115. * @param {object} modalConfig The configuration to create the modal instance
  116. * @param {object} triggerElement The trigger HTML jQuery object
  117. * @return {promise} Resolved with a Modal instance
  118. */
  119. export const create = (modalConfig, triggerElement) => {
  120. window.console.warn(
  121. 'The modal_factory has been deprecated since Moodle 4.3. Please use the create method on your target modal type instead.',
  122. );
  123. // Use of the triggerElement has been deprecated.
  124. const type = modalConfig.type || types.DEFAULT;
  125. const registryConf = ModalRegistry.get(type);
  126. if (!registryConf) {
  127. Notification.exception({message: `Unable to find modal of type: ${type}`});
  128. }
  129. const modal = registryConf.module.create(modalConfig);
  130. if (triggerElement) {
  131. window.console.warn(
  132. 'The triggerElement feature of the modal_factory has been deprecated. Please use event listeners instead.',
  133. );
  134. setUpTrigger(modal, triggerElement, modalConfig);
  135. }
  136. return $.when(new Promise((resolve, reject) => {
  137. modal
  138. .then(resolve)
  139. .catch(reject);
  140. }));
  141. };
  142. export default {
  143. create,
  144. types,
  145. };