]> git.ipfire.org Git - thirdparty/bootstrap.git/commitdiff
backport #29516: added animation when modal backdrop is static
authorJohann-S <johann.servoire@gmail.com>
Mon, 28 Oct 2019 14:28:11 +0000 (16:28 +0200)
committerXhmikosR <xhmikosr@gmail.com>
Sat, 2 Nov 2019 08:02:07 +0000 (10:02 +0200)
js/src/modal.js
js/tests/unit/modal.js
scss/_modal.scss
scss/_variables.scss
site/docs/4.3/components/modal.md

index d6abfdec871bb87ce50501831d62203eeac49b7f..24b0425713a01a35a674ebd63c94a11c562deff8 100644 (file)
@@ -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) {
index d22d8a1de7c365d636dcc46582bada4699a7be09..b1ddc8512ff517793d57f0627d2f30c77d0fe6f8 100644 (file)
@@ -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'
+      })
+  })
 })
index 9a036883a07a1f647b0968fa3aa296694ebfeb6c..9053c173f8c3894c56f61c736071ab7a61230313 100644 (file)
   .modal.show & {
     transform: $modal-show-transform;
   }
+
+  // When trying to close, animate focus to scale
+  .modal.modal-static & {
+    transform: $modal-scale-transform;
+  }
 }
 
 .modal-dialog-scrollable {
index cc4e0ad5e49fadbc5de9b9e59c6e588126960d0b..28db6f99433790f02812395a35658267e1e8bd2b 100644 (file)
@@ -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
index b2a7fc6a3933da088714b307c9ce8d24b44be5f7..f74253df093ff9da7bf9f4d1ebd9ab51433d2390 100644 (file)
@@ -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">&times;</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">&times;</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>