diff options
author | Johann-S <johann.servoire@gmail.com> | 2019-10-28 17:28:11 +0300 |
---|---|---|
committer | XhmikosR <xhmikosr@gmail.com> | 2019-11-02 11:02:07 +0300 |
commit | dd96b832f787e91db03ebc687a934deea335bed5 (patch) | |
tree | d559dd6c2bee13b13db7efbcea9692d7f1c1ad3f | |
parent | 29f585365fe71a74958d56beac98ab4b7e27a6b1 (diff) |
backport #29516: added animation when modal backdrop is static
-rw-r--r-- | js/src/modal.js | 37 | ||||
-rw-r--r-- | js/tests/unit/modal.js | 22 | ||||
-rw-r--r-- | scss/_modal.scss | 5 | ||||
-rw-r--r-- | scss/_variables.scss | 1 | ||||
-rw-r--r-- | site/docs/4.3/components/modal.md | 65 |
5 files changed, 121 insertions, 9 deletions
diff --git a/js/src/modal.js b/js/src/modal.js index d6abfdec87..24b0425713 100644 --- a/js/src/modal.js +++ b/js/src/modal.js @@ -38,6 +38,7 @@ const DefaultType = { const Event = { HIDE : `hide${EVENT_KEY}`, + HIDE_PREVENTED : `hidePrevented${EVENT_KEY}`, HIDDEN : `hidden${EVENT_KEY}`, SHOW : `show${EVENT_KEY}`, SHOWN : `shown${EVENT_KEY}`, @@ -56,7 +57,8 @@ const ClassName = { BACKDROP : 'modal-backdrop', OPEN : 'modal-open', FADE : 'fade', - SHOW : 'show' + SHOW : 'show', + STATIC : 'modal-static' } const Selector = { @@ -234,6 +236,29 @@ class Modal { return config } + _triggerBackdropTransition() { + if (this._config.backdrop === 'static') { + const hideEventPrevented = $.Event(Event.HIDE_PREVENTED) + + $(this._element).trigger(hideEventPrevented) + if (hideEventPrevented.defaultPrevented) { + return + } + + this._element.classList.add(ClassName.STATIC) + + const modalTransitionDuration = Util.getTransitionDurationFromElement(this._element) + + $(this._element).one(Util.TRANSITION_END, () => { + this._element.classList.remove(ClassName.STATIC) + }) + .emulateTransitionEnd(modalTransitionDuration) + this._element.focus() + } else { + this.hide() + } + } + _showElement(relatedTarget) { const transition = $(this._element).hasClass(ClassName.FADE) const modalBody = this._dialog ? this._dialog.querySelector(Selector.MODAL_BODY) : null @@ -303,8 +328,7 @@ class Modal { if (this._isShown && this._config.keyboard) { $(this._element).on(Event.KEYDOWN_DISMISS, (event) => { if (event.which === ESCAPE_KEYCODE) { - event.preventDefault() - this.hide() + this._triggerBackdropTransition() } }) } else if (!this._isShown) { @@ -362,11 +386,8 @@ class Modal { if (event.target !== event.currentTarget) { return } - if (this._config.backdrop === 'static') { - this._element.focus() - } else { - this.hide() - } + + this._triggerBackdropTransition() }) if (animate) { diff --git a/js/tests/unit/modal.js b/js/tests/unit/modal.js index d22d8a1de7..b1ddc8512f 100644 --- a/js/tests/unit/modal.js +++ b/js/tests/unit/modal.js @@ -833,4 +833,26 @@ $(function () { }) .bootstrapModal('show') }) + + QUnit.test('should not close modal when clicking outside of modal-content if backdrop = static', function (assert) { + assert.expect(1) + var done = assert.async() + var $modal = $('<div class="modal" data-backdrop="static"><div class="modal-dialog" /></div>').appendTo('#qunit-fixture') + + $modal.on('shown.bs.modal', function () { + $modal.trigger('click') + setTimeout(function () { + var modal = $modal.data('bs.modal') + + assert.strictEqual(modal._isShown, true) + done() + }, 10) + }) + .on('hidden.bs.modal', function () { + assert.strictEqual(true, false, 'should not hide the modal') + }) + .bootstrapModal({ + backdrop: 'static' + }) + }) }) diff --git a/scss/_modal.scss b/scss/_modal.scss index 9a036883a0..9053c173f8 100644 --- a/scss/_modal.scss +++ b/scss/_modal.scss @@ -48,6 +48,11 @@ .modal.show & { transform: $modal-show-transform; } + + // When trying to close, animate focus to scale + .modal.modal-static & { + transform: $modal-scale-transform; + } } .modal-dialog-scrollable { diff --git a/scss/_variables.scss b/scss/_variables.scss index cc4e0ad5e4..28db6f9943 100644 --- a/scss/_variables.scss +++ b/scss/_variables.scss @@ -976,6 +976,7 @@ $modal-sm: 300px !default; $modal-fade-transform: translate(0, -50px) !default; $modal-show-transform: none !default; $modal-transition: transform .3s ease-out !default; +$modal-scale-transform: scale(1.02) !default; // Alerts diff --git a/site/docs/4.3/components/modal.md b/site/docs/4.3/components/modal.md index b2a7fc6a39..f74253df09 100644 --- a/site/docs/4.3/components/modal.md +++ b/site/docs/4.3/components/modal.md @@ -135,6 +135,65 @@ Toggle a working modal demo by clicking the button below. It will slide down and </div> {% endhighlight %} +### Static backdrop + +When backdrop is set to static, the modal will not close when clicking outside it. Click the button below to try it. + +<div id="staticBackdropLive" class="modal fade" data-backdrop="static" tabindex="-1" role="dialog" aria-labelledby="staticBackdropLiveLabel" aria-hidden="true"> + <div class="modal-dialog" role="document"> + <div class="modal-content"> + <div class="modal-header"> + <h5 class="modal-title" id="staticBackdropLiveLabel">Modal title</h5> + <button type="button" class="close" data-dismiss="modal" aria-label="Close"> + <span aria-hidden="true">×</span> + </button> + </div> + <div class="modal-body"> + <p>I will not close if you click outside me. Don't even try to press escape key.</p> + </div> + <div class="modal-footer"> + <button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button> + <button type="button" class="btn btn-primary">Understood</button> + </div> + </div> + </div> +</div> + +<div class="bd-example"> + <button type="button" class="btn btn-primary" data-toggle="modal" data-target="#staticBackdropLive"> + Launch static backdrop modal + </button> +</div> + +{% highlight html %} +<!-- Button trigger modal --> +<button type="button" class="btn btn-primary" data-toggle="modal" data-target="#staticBackdrop"> + Launch static backdrop modal +</button> + +<!-- Modal --> +<div class="modal fade" id="staticBackdrop" data-backdrop="static" tabindex="-1" role="dialog" aria-labelledby="staticBackdropLabel" aria-hidden="true"> + <div class="modal-dialog" role="document"> + <div class="modal-content"> + <div class="modal-header"> + <h5 class="modal-title" id="staticBackdropLabel">Modal title</h5> + <button type="button" class="close" data-dismiss="modal" aria-label="Close"> + <span aria-hidden="true">×</span> + </button> + </div> + <div class="modal-body"> + ... + </div> + <div class="modal-footer"> + <button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button> + <button type="button" class="btn btn-primary">Understood</button> + </div> + </div> + </div> +</div> +{% endhighlight %} + + ### Scrolling long content When modals become too long for the user's viewport or device, they scroll independent of the page itself. Try the demo below to see what we mean. @@ -743,7 +802,7 @@ Options can be passed via data attributes or JavaScript. For data attributes, ap <td>backdrop</td> <td>boolean or the string <code>'static'</code></td> <td>true</td> - <td>Includes a modal-backdrop element. Alternatively, specify <code>static</code> for a backdrop which doesn't close the modal on click.</td> + <td>Includes a modal-backdrop element. Alternatively, specify <code>static</code> for a backdrop which doesn't close the modal on click or on escape key press.</td> </tr> <tr> <td>keyboard</td> @@ -836,6 +895,10 @@ Bootstrap's modal class exposes a few events for hooking into modal functionalit <td>hidden.bs.modal</td> <td>This event is fired when the modal has finished being hidden from the user (will wait for CSS transitions to complete).</td> </tr> + <tr> + <td>hidePrevented.bs.modal</td> + <td>This event is fired when the modal is shown, its backdrop is <code>static</code> and a click outside the modal or a scape key press is performed.</td> + </tr> </tbody> </table> |