MDL-68409 js: Add setButtonText modal helper
[moodle.git] / lib / amd / src / modal_factory.js
CommitLineData
2bcef559
RW
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/**
17 * Create a modal.
18 *
19 * @module core/modal_factory
20 * @class modal_factory
21 * @package core
22 * @copyright 2016 Ryan Wyllie <ryan@moodle.com>
23 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
24 */
0531ce1a 25define(['jquery', 'core/modal_events', 'core/modal_registry', 'core/modal',
c5681f7f 26 'core/modal_save_cancel', 'core/modal_cancel',
604887ce
AN
27 'core/templates', 'core/notification', 'core/custom_interaction_events',
28 'core/pending'],
c5681f7f 29 function($, ModalEvents, ModalRegistry, Modal, ModalSaveCancel,
604887ce 30 ModalCancel, Templates, Notification, CustomEvents, Pending) {
2bcef559
RW
31
32 // The templates for each type of modal.
33 var TEMPLATES = {
34 DEFAULT: 'core/modal',
35 SAVE_CANCEL: 'core/modal_save_cancel',
be2247fb 36 CANCEL: 'core/modal_cancel',
2bcef559
RW
37 };
38
2bcef559
RW
39 // The available types of modals.
40 var TYPES = {
41 DEFAULT: 'DEFAULT',
42 SAVE_CANCEL: 'SAVE_CANCEL',
be2247fb 43 CANCEL: 'CANCEL',
2bcef559
RW
44 };
45
0531ce1a
RW
46 // Register the common set of modals.
47 ModalRegistry.register(TYPES.DEFAULT, Modal, TEMPLATES.DEFAULT);
48 ModalRegistry.register(TYPES.SAVE_CANCEL, ModalSaveCancel, TEMPLATES.SAVE_CANCEL);
0531ce1a
RW
49 ModalRegistry.register(TYPES.CANCEL, ModalCancel, TEMPLATES.CANCEL);
50
2bcef559
RW
51 /**
52 * Set up the events required to show the modal and return focus when the modal
53 * is closed.
54 *
55 * @method setUpTrigger
72835770 56 * @param {Promise} modalPromise The modal instance
2bcef559 57 * @param {object} triggerElement The jQuery element to open the modal
2803c868 58 * @param {object} modalConfig The modal configuration given to the factory
2bcef559 59 */
2803c868
RW
60 var setUpTrigger = function(modalPromise, triggerElement, modalConfig) {
61 // The element that actually shows the modal.
62 var actualTriggerElement = null;
63 // Check if the client has provided a callback function to be called
64 // before the modal is displayed.
65 var hasPreShowCallback = (typeof modalConfig.preShowCallback == 'function');
66 // Function to handle the trigger element being activated.
67 var triggeredCallback = function(e, data) {
604887ce 68 var pendingPromise = new Pending('core/modal_factory:setUpTrigger:triggeredCallback');
2803c868 69 actualTriggerElement = $(e.currentTarget);
72835770 70 modalPromise.then(function(modal) {
2803c868
RW
71 if (hasPreShowCallback) {
72 // If the client provided a pre-show callback then execute
73 // it now before showing the modal.
74 modalConfig.preShowCallback(actualTriggerElement, modal);
75 }
76
77 modal.show();
72835770
AN
78
79 return modal;
604887ce
AN
80 })
81 .then(pendingPromise.resolve);
2803c868
RW
82 data.originalEvent.preventDefault();
83 };
84
85 // The trigger element can either be a single element or it can be an
86 // element + selector pair to create a delegated event handler to trigger
87 // the modal.
88 if (Array.isArray(triggerElement)) {
89 var selector = triggerElement[1];
90 triggerElement = triggerElement[0];
91
92 CustomEvents.define(triggerElement, [CustomEvents.events.activate]);
93 triggerElement.on(CustomEvents.events.activate, selector, triggeredCallback);
94 } else {
95 CustomEvents.define(triggerElement, [CustomEvents.events.activate]);
96 triggerElement.on(CustomEvents.events.activate, triggeredCallback);
2bcef559 97 }
2803c868
RW
98
99 modalPromise.then(function(modal) {
100 modal.getRoot().on(ModalEvents.hidden, function() {
101 // Focus on the trigger element that actually launched the modal.
102 if (actualTriggerElement !== null) {
103 actualTriggerElement.focus();
104 }
105 });
106
107 return modal;
108 });
2bcef559
RW
109 };
110
111 /**
112 * Create the correct instance of a modal based on the givem type. Sets up
113 * the trigger between the modal and the trigger element.
114 *
115 * @method createFromElement
0531ce1a 116 * @param {object} registryConf A config from the ModalRegistry
2bcef559 117 * @param {object} modalElement The modal HTML jQuery object
2bcef559
RW
118 * @return {object} Modal instance
119 */
72835770 120 var createFromElement = function(registryConf, modalElement) {
2bcef559 121 modalElement = $(modalElement);
0531ce1a
RW
122 var module = registryConf.module;
123 var modal = new module(modalElement);
2bcef559
RW
124
125 return modal;
126 };
127
128 /**
129 * Create the correct modal instance for the given type, including loading
2803c868 130 * the correct template.
2bcef559
RW
131 *
132 * @method createFromType
0531ce1a 133 * @param {object} registryConf A config from the ModalRegistry
2803c868 134 * @param {object} templateContext The context to render the template with
2bcef559
RW
135 * @return {promise} Resolved with a Modal instance
136 */
2803c868 137 var createFromType = function(registryConf, templateContext) {
0531ce1a 138 var templateName = registryConf.template;
2bcef559 139
72835770 140 var modalPromise = Templates.render(templateName, templateContext)
2bcef559
RW
141 .then(function(html) {
142 var modalElement = $(html);
72835770 143 return createFromElement(registryConf, modalElement);
2bcef559
RW
144 })
145 .fail(Notification.exception);
72835770 146
72835770 147 return modalPromise;
2bcef559
RW
148 };
149
150 /**
151 * Create a Modal instance.
152 *
153 * @method create
154 * @param {object} modalConfig The configuration to create the modal instance
155 * @param {object} triggerElement The trigger HTML jQuery object
156 * @return {promise} Resolved with a Modal instance
157 */
158 var create = function(modalConfig, triggerElement) {
159 var type = modalConfig.type || TYPES.DEFAULT;
160 var isLarge = modalConfig.large ? true : false;
0531ce1a 161 var registryConf = null;
a50768b9 162 var templateContext = {};
0531ce1a
RW
163
164 registryConf = ModalRegistry.get(type);
2bcef559 165
0531ce1a
RW
166 if (!registryConf) {
167 Notification.exception({message: 'Unable to find modal of type: ' + type});
2bcef559
RW
168 }
169
a50768b9
RW
170 if (typeof modalConfig.templateContext != 'undefined') {
171 templateContext = modalConfig.templateContext;
172 }
173
2803c868 174 var modalPromise = createFromType(registryConf, templateContext)
2bcef559
RW
175 .then(function(modal) {
176 if (typeof modalConfig.title != 'undefined') {
177 modal.setTitle(modalConfig.title);
178 }
179
180 if (typeof modalConfig.body != 'undefined') {
181 modal.setBody(modalConfig.body);
182 }
183
184 if (typeof modalConfig.footer != 'undefined') {
185 modal.setFooter(modalConfig.footer);
186 }
187
92810f7a
AN
188 if (modalConfig.buttons) {
189 Object.entries(modalConfig.buttons).forEach(function([key, value]) {
190 modal.setButtonText(key, value);
191 });
192 }
193
2bcef559
RW
194 if (isLarge) {
195 modal.setLarge();
196 }
197
fa6101ba
AN
198 if (typeof modalConfig.removeOnClose !== 'undefined') {
199 // If configured remove the modal when hiding it.
200 modal.setRemoveOnClose(modalConfig.removeOnClose);
201 }
202
2bcef559
RW
203 return modal;
204 });
2803c868
RW
205
206 if (typeof triggerElement != 'undefined') {
207 setUpTrigger(modalPromise, triggerElement, modalConfig);
208 }
209
210 return modalPromise;
2bcef559
RW
211 };
212
213 return {
214 create: create,
215 types: TYPES,
216 };
217});