Skip to content

Commit

Permalink
MDL-60489 javascript: add animations to modal setBody
Browse files Browse the repository at this point in the history
  • Loading branch information
ryanwyllie committed Oct 23, 2017
1 parent bcca46d commit 651f9e6
Show file tree
Hide file tree
Showing 6 changed files with 146 additions and 14 deletions.
2 changes: 1 addition & 1 deletion lib/amd/build/modal.min.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

113 changes: 100 additions & 13 deletions lib/amd/src/modal.js
Expand Up @@ -50,6 +50,12 @@ define(['jquery', 'core/templates', 'core/notification', 'core/key_codes',
*/
var backdropPromise;

/**
* A counter that gets incremented for each modal created. This can be
* used to generate unique values for the modals.
*/
var modalCounter = 0;

/**
* Constructor for the Modal.
*
Expand All @@ -66,6 +72,7 @@ define(['jquery', 'core/templates', 'core/notification', 'core/key_codes',
this.isAttached = false;
this.bodyJS = null;
this.footerJS = null;
this.modalCount = modalCounter++;

if (!this.root.is(SELECTORS.CONTAINER)) {
Notification.exception({message: 'Element is not a modal container'});
Expand Down Expand Up @@ -212,6 +219,16 @@ define(['jquery', 'core/templates', 'core/notification', 'core/key_codes',
return this.footer;
};

/**
* Get the unique modal count.
*
* @method getModalCount
* @return {int}
*/
Modal.prototype.getModalCount = function() {
return this.modalCount;
};

/**
* Set the modal title element.
*
Expand Down Expand Up @@ -253,25 +270,95 @@ define(['jquery', 'core/templates', 'core/notification', 'core/key_codes',
// Just set the value if it's a string.
body.html(value);
} else {
var jsPendingId = 'amd-modal-js-pending-id-' + this.getModalCount();
M.util.js_pending(jsPendingId);
// Otherwise we assume it's a promise to be resolved with
// html and javascript.
Templates.render(TEMPLATES.LOADING, {}).done(function(html) {
body.html(html);
var contentPromise = null;
body.css('overflow', 'hidden');

if (value.state() == 'pending') {
// We're still waiting for the body promise to resolve so
// let's show a loading icon.
body.animate({height: '100px'}, 150);

body.html('');
contentPromise = Templates.render(TEMPLATES.LOADING, {})
.then(function(html) {
var loadingIcon = $(html).hide();
body.html(loadingIcon);
loadingIcon.fadeIn(150);

// We only want the loading icon to fade out
// when the content for the body has finished
// loading.
return $.when(loadingIcon.promise(), value);
})
.then(function(loadingIcon) {
// Once the content has finished loading and
// the loading icon has been shown then we can
// fade the icon away to reveal the content.
return loadingIcon.fadeOut(100).promise();
})
.then(function() {
return value;
});
} else {
// The content is already loaded so let's just display
// it to the user. No need for a loading icon.
contentPromise = value;
}

value.done(function(html, js) {
// Now we can actually display the content.
contentPromise.then(function(html, js) {
var result = null;

if (this.isVisible()) {
// If the modal is visible then we should display
// the content gracefully for the user.
body.css('opacity', 0);
var currentHeight = body.innerHeight();
body.html(html);
// We need to clear any height values we've set here
// in order to measure the height of the content being
// added. This then allows us to animate the height
// transition.
body.css('height', '');
var newHeight = body.innerHeight();
body.css('height', currentHeight + 'px');
result = body.animate(
{height: newHeight + 'px', opacity: 1},
{duration: 150, queue: false}
).promise();
} else {
// Since the modal isn't visible we can just immediately
// set the content. No need to animate it.
body.html(html);
}

if (js) {
if (this.isAttached) {
// If we're in the DOM then run the JS immediately.
Templates.runTemplateJS(js);
} else {
// Otherwise cache it to be run when we're attached.
this.bodyJS = js;
}
if (js) {
if (this.isAttached) {
// If we're in the DOM then run the JS immediately.
Templates.runTemplateJS(js);
} else {
// Otherwise cache it to be run when we're attached.
this.bodyJS = js;
}
}.bind(this));
}.bind(this));
}

return result;
}.bind(this))
.fail(Notification.exception)
.always(function() {
// When we're done displaying all of the content we need
// to clear the custom values we've set here.
body.css('height', '');
body.css('overflow', '');
body.css('opacity', '');
M.util.js_complete(jsPendingId);

return;
});
}
};

Expand Down
1 change: 1 addition & 0 deletions theme/boost/scss/moodle.scss
Expand Up @@ -46,3 +46,4 @@ $breadcrumb-divider-rtl: "◀" !default;
@import "moodle/responsive-tabs";
@import "moodle/bs2-compat";
@import "moodle/print";
@import "moodle/modal";
16 changes: 16 additions & 0 deletions theme/boost/scss/moodle/modal.scss
@@ -0,0 +1,16 @@
.modal-body {
& > .loading-icon {
display: block;
position: relative;
width: 100%;
height: 100%;

.icon {
position: absolute;
top: 50%;
/*rtl:ignore*/
left: 50%;
transform: translate(-50%, -50%);
}
}
}
15 changes: 15 additions & 0 deletions theme/bootstrapbase/less/moodle/modal.less
Expand Up @@ -91,6 +91,21 @@ body {

.modal-body {
max-height: none;

& > .loading-icon {
display: block;
position: relative;
width: 100%;
height: 100%;

.icon {
position: absolute;
top: 50%;
/*rtl:ignore*/
left: 50%;
transform: translate(-50%, -50%);
}
}
}

.modal-footer {
Expand Down
13 changes: 13 additions & 0 deletions theme/bootstrapbase/style/moodle.css
Expand Up @@ -17053,6 +17053,19 @@ body.modal-open {
.modal-container .modal .modal-body {
max-height: none;
}
.modal-container .modal .modal-body > .loading-icon {
display: block;
position: relative;
width: 100%;
height: 100%;
}
.modal-container .modal .modal-body > .loading-icon .icon {
position: absolute;
top: 50%;
/*rtl:ignore*/
left: 50%;
transform: translate(-50%, -50%);
}
.modal-container .modal .modal-footer {
border-top: 1px solid #bbb;
text-align: center;
Expand Down

0 comments on commit 651f9e6

Please sign in to comment.