mirror of
https://github.com/twbs/bootstrap.git
synced 2025-10-03 00:03:44 -04:00
Compare commits
3 Commits
6c835c6ab4
...
e029f3c790
Author | SHA1 | Date | |
---|---|---|---|
|
e029f3c790 | ||
|
ad443baf3a | ||
|
9c526253b4 |
@ -12,7 +12,7 @@ import Backdrop from './util/backdrop.js'
|
||||
import { enableDismissTrigger } from './util/component-functions.js'
|
||||
import FocusTrap from './util/focustrap.js'
|
||||
import {
|
||||
defineJQueryPlugin, isRTL, isVisible, reflow
|
||||
defineJQueryPlugin, execute, isRTL, isVisible, reflow
|
||||
} from './util/index.js'
|
||||
import ScrollBarHelper from './util/scrollbar.js'
|
||||
|
||||
@ -54,9 +54,9 @@ const Default = {
|
||||
}
|
||||
|
||||
const DefaultType = {
|
||||
backdrop: '(boolean|string)',
|
||||
focus: 'boolean',
|
||||
keyboard: 'boolean'
|
||||
backdrop: '(boolean|string|function)',
|
||||
focus: '(boolean|function)',
|
||||
keyboard: '(boolean|function)'
|
||||
}
|
||||
|
||||
/**
|
||||
@ -157,7 +157,7 @@ class Modal extends BaseComponent {
|
||||
// Private
|
||||
_initializeBackDrop() {
|
||||
return new Backdrop({
|
||||
isVisible: Boolean(this._config.backdrop), // 'static' option will be translated to true, and booleans will keep their value,
|
||||
isVisible: Boolean(this._resolvePossibleFunction(this._config.backdrop)), // 'static' option will be translated to true, and booleans will keep their value
|
||||
isAnimated: this._isAnimated()
|
||||
})
|
||||
}
|
||||
@ -190,7 +190,7 @@ class Modal extends BaseComponent {
|
||||
this._element.classList.add(CLASS_NAME_SHOW)
|
||||
|
||||
const transitionComplete = () => {
|
||||
if (this._config.focus) {
|
||||
if (this._resolvePossibleFunction(this._config.focus)) {
|
||||
this._focustrap.activate()
|
||||
}
|
||||
|
||||
@ -209,7 +209,7 @@ class Modal extends BaseComponent {
|
||||
return
|
||||
}
|
||||
|
||||
if (this._config.keyboard) {
|
||||
if (this._resolvePossibleFunction(this._config.keyboard)) {
|
||||
this.hide()
|
||||
return
|
||||
}
|
||||
@ -230,12 +230,14 @@ class Modal extends BaseComponent {
|
||||
return
|
||||
}
|
||||
|
||||
if (this._config.backdrop === 'static') {
|
||||
const backdrop = this._resolvePossibleFunction(this._config.backdrop)
|
||||
|
||||
if (backdrop === 'static') {
|
||||
this._triggerBackdropTransition()
|
||||
return
|
||||
}
|
||||
|
||||
if (this._config.backdrop) {
|
||||
if (backdrop) {
|
||||
this.hide()
|
||||
}
|
||||
})
|
||||
@ -314,6 +316,10 @@ class Modal extends BaseComponent {
|
||||
this._element.style.paddingRight = ''
|
||||
}
|
||||
|
||||
_resolvePossibleFunction(arg) {
|
||||
return execute(arg, [this])
|
||||
}
|
||||
|
||||
// Static
|
||||
static jQueryInterface(config, relatedTarget) {
|
||||
return this.each(function () {
|
||||
|
@ -237,6 +237,7 @@ describe('Modal', () => {
|
||||
modal.show()
|
||||
})
|
||||
})
|
||||
|
||||
it('should set is transitioning if fade class is present', () => {
|
||||
return new Promise(resolve => {
|
||||
fixtureEl.innerHTML = '<div class="modal fade"><div class="modal-dialog"></div></div>'
|
||||
@ -550,6 +551,7 @@ describe('Modal', () => {
|
||||
modal.show()
|
||||
})
|
||||
})
|
||||
|
||||
it('should close modal when escape key is pressed with keyboard = true and backdrop is static', () => {
|
||||
return new Promise(resolve => {
|
||||
fixtureEl.innerHTML = '<div class="modal"><div class="modal-dialog"></div></div>'
|
||||
@ -677,6 +679,44 @@ describe('Modal', () => {
|
||||
modal.show()
|
||||
})
|
||||
})
|
||||
|
||||
it('should call .focus() when config function returns true', () => {
|
||||
return new Promise(resolve => {
|
||||
fixtureEl.innerHTML = '<div class="modal"><div class="modal-dialog"></div></div>'
|
||||
|
||||
const modalEl = fixtureEl.querySelector('.modal')
|
||||
const focusSpy = spyOn(modalEl, 'focus')
|
||||
const modal = new Modal(modalEl, {
|
||||
focus: () => true
|
||||
})
|
||||
|
||||
modalEl.addEventListener('shown.bs.modal', () => {
|
||||
expect(focusSpy).toHaveBeenCalled()
|
||||
resolve()
|
||||
})
|
||||
|
||||
modal.show()
|
||||
})
|
||||
})
|
||||
|
||||
it('should NOT call .focus() when config function returns false', () => {
|
||||
return new Promise(resolve => {
|
||||
fixtureEl.innerHTML = '<div class="modal"><div class="modal-dialog"></div></div>'
|
||||
|
||||
const modalEl = fixtureEl.querySelector('.modal')
|
||||
const focusSpy = spyOn(modalEl, 'focus')
|
||||
const modal = new Modal(modalEl, {
|
||||
focus: () => false
|
||||
})
|
||||
|
||||
modalEl.addEventListener('shown.bs.modal', () => {
|
||||
expect(focusSpy).not.toHaveBeenCalled()
|
||||
resolve()
|
||||
})
|
||||
|
||||
modal.show()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('hide', () => {
|
||||
@ -766,6 +806,129 @@ describe('Modal', () => {
|
||||
})
|
||||
})
|
||||
|
||||
it('should not close on Escape when "keyboard" option is dynamically changed to false', () => {
|
||||
return new Promise((resolve, reject) => {
|
||||
fixtureEl.innerHTML = '<div class="modal"><div class="modal-dialog"></div></div>'
|
||||
|
||||
const config = { keyboard: 'closing' }
|
||||
|
||||
const modalEl = fixtureEl.querySelector('.modal')
|
||||
const modal = new Modal(modalEl, {
|
||||
keyboard: () => config.keyboard === 'closing'
|
||||
})
|
||||
|
||||
modalEl.addEventListener('shown.bs.modal', () => {
|
||||
config.keyboard = 'nothing'
|
||||
|
||||
const keydownEscape = createEvent('keydown')
|
||||
keydownEscape.key = 'Escape'
|
||||
modalEl.dispatchEvent(keydownEscape)
|
||||
|
||||
expect(modal._isShown).toBeTrue()
|
||||
resolve()
|
||||
})
|
||||
|
||||
modalEl.addEventListener('hidden.bs.modal', () => {
|
||||
reject(new Error('Should not hide a modal'))
|
||||
})
|
||||
|
||||
modal.show()
|
||||
})
|
||||
})
|
||||
|
||||
it('should close on Escape when "keyboard" option is dynamically changed to true', () => {
|
||||
return new Promise(resolve => {
|
||||
fixtureEl.innerHTML = '<div class="modal"><div class="modal-dialog"></div></div>'
|
||||
|
||||
const config = { keyboard: 'nothing' }
|
||||
|
||||
const modalEl = fixtureEl.querySelector('.modal')
|
||||
const modal = new Modal(modalEl, {
|
||||
keyboard: () => config.keyboard === 'closing'
|
||||
})
|
||||
|
||||
modalEl.addEventListener('shown.bs.modal', () => {
|
||||
config.keyboard = 'closing'
|
||||
|
||||
const keydownEscape = createEvent('keydown')
|
||||
keydownEscape.key = 'Escape'
|
||||
modalEl.dispatchEvent(keydownEscape)
|
||||
})
|
||||
|
||||
modalEl.addEventListener('hidden.bs.modal', () => {
|
||||
resolve()
|
||||
})
|
||||
|
||||
modal.show()
|
||||
})
|
||||
})
|
||||
|
||||
it('should close by backdrop click when option dynamically changed to true', () => {
|
||||
return new Promise(resolve => {
|
||||
fixtureEl.innerHTML = '<div class="modal"><div class="modal-dialog"></div></div>'
|
||||
|
||||
const config = { backdrop: 'static' }
|
||||
const modalEl = fixtureEl.querySelector('.modal')
|
||||
const modal = new Modal(modalEl, {
|
||||
backdrop: () => config.backdrop
|
||||
})
|
||||
|
||||
const backdropSpy = spyOn(modal._backdrop, 'hide').and.callThrough()
|
||||
EventHandler.one(modalEl, 'click', () => {
|
||||
if (config.backdrop === false) {
|
||||
modal.hide()
|
||||
}
|
||||
})
|
||||
|
||||
modalEl.addEventListener('shown.bs.modal', () => {
|
||||
config.backdrop = false
|
||||
|
||||
modalEl.click()
|
||||
})
|
||||
|
||||
modalEl.addEventListener('hidden.bs.modal', () => {
|
||||
expect(backdropSpy).toHaveBeenCalled()
|
||||
resolve()
|
||||
})
|
||||
|
||||
modal.show()
|
||||
})
|
||||
})
|
||||
|
||||
it('should not close by backdrop click when option dynamically changed to "static"', () => {
|
||||
return new Promise((resolve, reject) => {
|
||||
fixtureEl.innerHTML = '<div class="modal"><div class="modal-dialog"></div></div>'
|
||||
|
||||
const config = { backdrop: false }
|
||||
const modalEl = fixtureEl.querySelector('.modal')
|
||||
const modal = new Modal(modalEl, {
|
||||
backdrop: () => config.backdrop
|
||||
})
|
||||
|
||||
const backdropSpy = spyOn(modal._backdrop, 'hide').and.callThrough()
|
||||
EventHandler.one(modalEl, 'click', () => {
|
||||
if (config.backdrop === false) {
|
||||
modal.hide()
|
||||
}
|
||||
})
|
||||
|
||||
modalEl.addEventListener('shown.bs.modal', () => {
|
||||
config.backdrop = 'static'
|
||||
|
||||
modalEl.click()
|
||||
|
||||
expect(backdropSpy).not.toHaveBeenCalled()
|
||||
resolve()
|
||||
})
|
||||
|
||||
modalEl.addEventListener('hidden.bs.modal', () => {
|
||||
reject(new Error('Should not hide a modal'))
|
||||
})
|
||||
|
||||
modal.show()
|
||||
})
|
||||
})
|
||||
|
||||
it('should do nothing is the modal is not shown', () => {
|
||||
fixtureEl.innerHTML = '<div class="modal"><div class="modal-dialog"></div></div>'
|
||||
|
||||
@ -1077,6 +1240,7 @@ describe('Modal', () => {
|
||||
modal.show()
|
||||
})
|
||||
})
|
||||
|
||||
it('should not focus the trigger if the modal is not visible', () => {
|
||||
return new Promise(resolve => {
|
||||
fixtureEl.innerHTML = [
|
||||
@ -1109,6 +1273,7 @@ describe('Modal', () => {
|
||||
trigger.click()
|
||||
})
|
||||
})
|
||||
|
||||
it('should not focus the trigger if the modal is not shown', () => {
|
||||
return new Promise(resolve => {
|
||||
fixtureEl.innerHTML = [
|
||||
@ -1162,6 +1327,7 @@ describe('Modal', () => {
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('jQueryInterface', () => {
|
||||
it('should create a modal', () => {
|
||||
fixtureEl.innerHTML = '<div class="modal"><div class="modal-dialog"></div></div>'
|
||||
|
@ -3,3 +3,5 @@ As options can be passed via data attributes or JavaScript, you can append an op
|
||||
As of Bootstrap 5.2.0, all components support an **experimental** reserved data attribute `data-bs-config` that can house simple component configuration as a JSON string. When an element has `data-bs-config='{"delay":0, "title":123}'` and `data-bs-title="456"` attributes, the final `title` value will be `456` and the separate data attributes will override values given on `data-bs-config`. In addition, existing data attributes are able to house JSON values like `data-bs-delay='{"show":0,"hide":150}'`.
|
||||
|
||||
The final configuration object is the merged result of `data-bs-config`, `data-bs-`, and `js object` where the latest given key-value overrides the others.
|
||||
|
||||
You can pass a callback function there, which undertakes to return the value type specified for the parameter. This will allow you to change the logic of the modal window in runtime mode after you have passed the configuration object.
|
||||
|
@ -782,11 +782,44 @@ const myModalAlternative = new bootstrap.Modal('#myModal', options)
|
||||
<BsTable>
|
||||
| Name | Type | Default | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `backdrop` | boolean, `’static'` | `true` | Includes a modal-backdrop element. Alternatively, specify `static` for a backdrop which doesn’t close the modal when clicked. |
|
||||
| `focus` | boolean | `true` | Puts the focus on the modal when initialized. |
|
||||
| `keyboard` | boolean | `true` | Closes the modal when escape key is pressed. |
|
||||
| `backdrop` | true, `’static'`, <br /> function | `true` | Includes a modal-backdrop element. Alternatively, specify `static` for a backdrop which doesn’t close the modal when clicked. |
|
||||
| `focus` | boolean, function | `true` | Puts the focus on the modal when initialized. |
|
||||
| `keyboard` | boolean, function | `true` | Closes the modal when escape key is pressed. |
|
||||
</BsTable>
|
||||
|
||||
For example, if you want to prevent the user from closing the modal window using the Escape key to avoid accidental loss
|
||||
of important data if they are entered, you can use instead:
|
||||
|
||||
```js
|
||||
const confirmInput = document.getElementById('confirmInput')
|
||||
const modalElement = document.getElementById('confirmModal')
|
||||
|
||||
new bootstrap.Modal(modalElement, { keyboard: true })
|
||||
|
||||
const keydownBlocker = event => {
|
||||
if (event.key === 'Escape' && confirmInput.value.toLowerCase() === 'save') {
|
||||
event.stopPropagation()
|
||||
}
|
||||
}
|
||||
|
||||
modalElement.addEventListener('show.bs.modal', () => {
|
||||
document.addEventListener('keydown', keydownBlocker, true)
|
||||
})
|
||||
|
||||
modalElement.addEventListener('hide.bs.modal', () => {
|
||||
document.removeEventListener('keydown', keydownBlocker, true)
|
||||
})
|
||||
```
|
||||
|
||||
The next option is to transfer the function:
|
||||
|
||||
```js
|
||||
const confirmInput = document.getElementById('confirmInput')
|
||||
const modalElement = document.getElementById('confirmModal')
|
||||
|
||||
new bootstrap.Modal(modalElement, { keyboard: () => confirmInput.value.toLowerCase() !== 'save' })
|
||||
```
|
||||
|
||||
### Methods
|
||||
|
||||
<Callout name="danger-async-methods" type="danger" />
|
||||
|
Loading…
x
Reference in New Issue
Block a user