Compare commits

...

5 Commits

Author SHA1 Message Date
Denis Lopatin
6c835c6ab4
Merge ad443baf3a19598b3c5ac61159bd8f259ddc5ddd into 4189b3075c003b2d5dc9335195be7b750dfc2526 2025-10-01 08:19:41 +03:00
dependabot[bot]
4189b3075c
Build(deps): Bump github/codeql-action in the github-actions group (#41782)
Bumps the github-actions group with 1 update: [github/codeql-action](https://github.com/github/codeql-action).


Updates `github/codeql-action` from 3.30.3 to 3.30.5
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](192325c861...3599b3baa1)

---
updated-dependencies:
- dependency-name: github/codeql-action
  dependency-version: 3.30.5
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: github-actions
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-30 18:01:50 +02:00
Julien Déramond
8cd02aabd1
Build(deps-dev): Bump dependencies (#41780)
- cross-env  ^10.0.0  →  ^10.1.0
- globby     ^14.1.0  →  ^15.0.0
2025-09-29 21:59:14 +02:00
Denis
ad443baf3a docs(modal): Update examples for dynamic configuration 2025-09-02 00:14:59 +03:00
Denis
9c526253b4 feature(modal): add dynamic config
- adds the ability to change the configuration settings of the modal window during script execution
- adds tests for new functionality
2025-09-01 21:35:59 +03:00
8 changed files with 284 additions and 66 deletions

View File

@ -29,16 +29,16 @@ jobs:
persist-credentials: false persist-credentials: false
- name: Initialize CodeQL - name: Initialize CodeQL
uses: github/codeql-action/init@192325c86100d080feab897ff886c34abd4c83a3 # v3.30.3 uses: github/codeql-action/init@3599b3baa15b485a2e49ef411a7a4bb2452e7f93 # v3.30.5
with: with:
config-file: ./.github/codeql/codeql-config.yml config-file: ./.github/codeql/codeql-config.yml
languages: "javascript" languages: "javascript"
queries: +security-and-quality queries: +security-and-quality
- name: Autobuild - name: Autobuild
uses: github/codeql-action/autobuild@192325c86100d080feab897ff886c34abd4c83a3 # v3.30.3 uses: github/codeql-action/autobuild@3599b3baa15b485a2e49ef411a7a4bb2452e7f93 # v3.30.5
- name: Perform CodeQL Analysis - name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@192325c86100d080feab897ff886c34abd4c83a3 # v3.30.3 uses: github/codeql-action/analyze@3599b3baa15b485a2e49ef411a7a4bb2452e7f93 # v3.30.5
with: with:
category: "/language:javascript" category: "/language:javascript"

View File

@ -73,6 +73,6 @@ jobs:
# Upload the results to GitHub's code scanning dashboard (optional). # Upload the results to GitHub's code scanning dashboard (optional).
# Commenting out will disable upload of results to your repo's Code Scanning dashboard # Commenting out will disable upload of results to your repo's Code Scanning dashboard
- name: "Upload to code-scanning" - name: "Upload to code-scanning"
uses: github/codeql-action/upload-sarif@192325c86100d080feab897ff886c34abd4c83a3 # v3.30.3 uses: github/codeql-action/upload-sarif@3599b3baa15b485a2e49ef411a7a4bb2452e7f93 # v3.30.5
with: with:
sarif_file: results.sarif sarif_file: results.sarif

View File

@ -12,7 +12,7 @@ import Backdrop from './util/backdrop.js'
import { enableDismissTrigger } from './util/component-functions.js' import { enableDismissTrigger } from './util/component-functions.js'
import FocusTrap from './util/focustrap.js' import FocusTrap from './util/focustrap.js'
import { import {
defineJQueryPlugin, isRTL, isVisible, reflow defineJQueryPlugin, execute, isRTL, isVisible, reflow
} from './util/index.js' } from './util/index.js'
import ScrollBarHelper from './util/scrollbar.js' import ScrollBarHelper from './util/scrollbar.js'
@ -54,9 +54,9 @@ const Default = {
} }
const DefaultType = { const DefaultType = {
backdrop: '(boolean|string)', backdrop: '(boolean|string|function)',
focus: 'boolean', focus: '(boolean|function)',
keyboard: 'boolean' keyboard: '(boolean|function)'
} }
/** /**
@ -157,7 +157,7 @@ class Modal extends BaseComponent {
// Private // Private
_initializeBackDrop() { _initializeBackDrop() {
return new Backdrop({ 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() isAnimated: this._isAnimated()
}) })
} }
@ -190,7 +190,7 @@ class Modal extends BaseComponent {
this._element.classList.add(CLASS_NAME_SHOW) this._element.classList.add(CLASS_NAME_SHOW)
const transitionComplete = () => { const transitionComplete = () => {
if (this._config.focus) { if (this._resolvePossibleFunction(this._config.focus)) {
this._focustrap.activate() this._focustrap.activate()
} }
@ -209,7 +209,7 @@ class Modal extends BaseComponent {
return return
} }
if (this._config.keyboard) { if (this._resolvePossibleFunction(this._config.keyboard)) {
this.hide() this.hide()
return return
} }
@ -230,12 +230,14 @@ class Modal extends BaseComponent {
return return
} }
if (this._config.backdrop === 'static') { const backdrop = this._resolvePossibleFunction(this._config.backdrop)
if (backdrop === 'static') {
this._triggerBackdropTransition() this._triggerBackdropTransition()
return return
} }
if (this._config.backdrop) { if (backdrop) {
this.hide() this.hide()
} }
}) })
@ -314,6 +316,10 @@ class Modal extends BaseComponent {
this._element.style.paddingRight = '' this._element.style.paddingRight = ''
} }
_resolvePossibleFunction(arg) {
return execute(arg, [this])
}
// Static // Static
static jQueryInterface(config, relatedTarget) { static jQueryInterface(config, relatedTarget) {
return this.each(function () { return this.each(function () {

View File

@ -237,6 +237,7 @@ describe('Modal', () => {
modal.show() modal.show()
}) })
}) })
it('should set is transitioning if fade class is present', () => { it('should set is transitioning if fade class is present', () => {
return new Promise(resolve => { return new Promise(resolve => {
fixtureEl.innerHTML = '<div class="modal fade"><div class="modal-dialog"></div></div>' fixtureEl.innerHTML = '<div class="modal fade"><div class="modal-dialog"></div></div>'
@ -550,6 +551,7 @@ describe('Modal', () => {
modal.show() modal.show()
}) })
}) })
it('should close modal when escape key is pressed with keyboard = true and backdrop is static', () => { it('should close modal when escape key is pressed with keyboard = true and backdrop is static', () => {
return new Promise(resolve => { return new Promise(resolve => {
fixtureEl.innerHTML = '<div class="modal"><div class="modal-dialog"></div></div>' fixtureEl.innerHTML = '<div class="modal"><div class="modal-dialog"></div></div>'
@ -677,6 +679,44 @@ describe('Modal', () => {
modal.show() 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', () => { 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', () => { it('should do nothing is the modal is not shown', () => {
fixtureEl.innerHTML = '<div class="modal"><div class="modal-dialog"></div></div>' fixtureEl.innerHTML = '<div class="modal"><div class="modal-dialog"></div></div>'
@ -1077,6 +1240,7 @@ describe('Modal', () => {
modal.show() modal.show()
}) })
}) })
it('should not focus the trigger if the modal is not visible', () => { it('should not focus the trigger if the modal is not visible', () => {
return new Promise(resolve => { return new Promise(resolve => {
fixtureEl.innerHTML = [ fixtureEl.innerHTML = [
@ -1109,6 +1273,7 @@ describe('Modal', () => {
trigger.click() trigger.click()
}) })
}) })
it('should not focus the trigger if the modal is not shown', () => { it('should not focus the trigger if the modal is not shown', () => {
return new Promise(resolve => { return new Promise(resolve => {
fixtureEl.innerHTML = [ fixtureEl.innerHTML = [
@ -1162,6 +1327,7 @@ describe('Modal', () => {
}) })
}) })
}) })
describe('jQueryInterface', () => { describe('jQueryInterface', () => {
it('should create a modal', () => { it('should create a modal', () => {
fixtureEl.innerHTML = '<div class="modal"><div class="modal-dialog"></div></div>' fixtureEl.innerHTML = '<div class="modal"><div class="modal-dialog"></div></div>'

107
package-lock.json generated
View File

@ -43,7 +43,7 @@
"bundlewatch": "^0.4.1", "bundlewatch": "^0.4.1",
"clean-css-cli": "^5.6.3", "clean-css-cli": "^5.6.3",
"clipboard": "^2.0.11", "clipboard": "^2.0.11",
"cross-env": "^10.0.0", "cross-env": "^10.1.0",
"eslint": "8.57.1", "eslint": "8.57.1",
"eslint-config-xo": "0.45.0", "eslint-config-xo": "0.45.0",
"eslint-plugin-html": "^8.1.3", "eslint-plugin-html": "^8.1.3",
@ -52,7 +52,7 @@
"eslint-plugin-unicorn": "56.0.1", "eslint-plugin-unicorn": "56.0.1",
"find-unused-sass-variables": "^6.1.0", "find-unused-sass-variables": "^6.1.0",
"github-slugger": "^2.0.0", "github-slugger": "^2.0.0",
"globby": "^14.1.0", "globby": "^15.0.0",
"hammer-simulator": "0.0.1", "hammer-simulator": "0.0.1",
"htmlparser2": "^10.0.0", "htmlparser2": "^10.0.0",
"image-size": "^2.0.2", "image-size": "^2.0.2",
@ -2196,9 +2196,9 @@
} }
}, },
"node_modules/@cacheable/memory/node_modules/keyv": { "node_modules/@cacheable/memory/node_modules/keyv": {
"version": "5.5.2", "version": "5.5.3",
"resolved": "https://registry.npmjs.org/keyv/-/keyv-5.5.2.tgz", "resolved": "https://registry.npmjs.org/keyv/-/keyv-5.5.3.tgz",
"integrity": "sha512-TXcFHbmm/z7MGd1u9ASiCSfTS+ei6Z8B3a5JHzx3oPa/o7QzWVtPRpc4KGER5RR469IC+/nfg4U5YLIuDUua2g==", "integrity": "sha512-h0Un1ieD+HUrzBH6dJXhod3ifSghk5Hw/2Y4/KHBziPlZecrFyE9YOTPU6eOs0V9pYl8gOs86fkr/KN8lUX39A==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
@ -4322,9 +4322,9 @@
"license": "MIT" "license": "MIT"
}, },
"node_modules/@sindresorhus/merge-streams": { "node_modules/@sindresorhus/merge-streams": {
"version": "2.3.0", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-2.3.0.tgz", "resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-4.0.0.tgz",
"integrity": "sha512-LtoMMhxAlorcGhmFYI+LhPgbPZCkgP6ra1YL604EeF6U98pLlQ3iWIGMdWSC+vWmPBWBNgmDBAhnAobLROJmwg==", "integrity": "sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"engines": { "engines": {
@ -4552,9 +4552,9 @@
} }
}, },
"node_modules/@types/node": { "node_modules/@types/node": {
"version": "18.19.127", "version": "18.19.128",
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.127.tgz", "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.128.tgz",
"integrity": "sha512-gSjxjrnKXML/yo0BO099uPixMqfpJU0TKYjpfLU7TrtA2WWDki412Np/RSTPRil1saKBhvVVKzVx/p/6p94nVA==", "integrity": "sha512-m7wxXGpPpqxp2QDi/rpih5O772APRuBIa/6XiGqLNoM1txkjI8Sz1V4oSXJxQLTz/yP5mgy9z6UXEO6/lP70Gg==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
@ -5322,9 +5322,9 @@
} }
}, },
"node_modules/axios": { "node_modules/axios": {
"version": "0.30.1", "version": "0.30.2",
"resolved": "https://registry.npmjs.org/axios/-/axios-0.30.1.tgz", "resolved": "https://registry.npmjs.org/axios/-/axios-0.30.2.tgz",
"integrity": "sha512-2XabsR1u0/B6OoKy57/xJmPkQiUvdoV93oW4ww+Xjee7C2er/O5U77lvqycDkT2VQDtfjYcjw8ZV8GDaoqwjHQ==", "integrity": "sha512-0pE4RQ4UQi1jKY6p7u6i1Tkzqmu+d+/tHS7Q7rKunWLB9WyilBTpHHpXzPNMDj5hTbK0B0PTLSz07yqMBiF6xg==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
@ -5442,9 +5442,9 @@
} }
}, },
"node_modules/baseline-browser-mapping": { "node_modules/baseline-browser-mapping": {
"version": "2.8.7", "version": "2.8.9",
"resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.7.tgz", "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.9.tgz",
"integrity": "sha512-bxxN2M3a4d1CRoQC//IqsR5XrLh0IJ8TCv2x6Y9N0nckNz/rTjZB3//GGscZziZOxmjP55rzxg/ze7usFI9FqQ==", "integrity": "sha512-hY/u2lxLrbecMEWSB0IpGzGyDyeoMFQhCvZd2jGFSE5I17Fh01sYUBPCJtkWERw7zrac9+cIghxm/ytJa2X8iA==",
"dev": true, "dev": true,
"license": "Apache-2.0", "license": "Apache-2.0",
"bin": { "bin": {
@ -5804,9 +5804,9 @@
} }
}, },
"node_modules/cacheable/node_modules/keyv": { "node_modules/cacheable/node_modules/keyv": {
"version": "5.5.2", "version": "5.5.3",
"resolved": "https://registry.npmjs.org/keyv/-/keyv-5.5.2.tgz", "resolved": "https://registry.npmjs.org/keyv/-/keyv-5.5.3.tgz",
"integrity": "sha512-TXcFHbmm/z7MGd1u9ASiCSfTS+ei6Z8B3a5JHzx3oPa/o7QzWVtPRpc4KGER5RR469IC+/nfg4U5YLIuDUua2g==", "integrity": "sha512-h0Un1ieD+HUrzBH6dJXhod3ifSghk5Hw/2Y4/KHBziPlZecrFyE9YOTPU6eOs0V9pYl8gOs86fkr/KN8lUX39A==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
@ -6512,9 +6512,9 @@
} }
}, },
"node_modules/cross-env": { "node_modules/cross-env": {
"version": "10.0.0", "version": "10.1.0",
"resolved": "https://registry.npmjs.org/cross-env/-/cross-env-10.0.0.tgz", "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-10.1.0.tgz",
"integrity": "sha512-aU8qlEK/nHYtVuN4p7UQgAwVljzMg8hB4YK5ThRqD2l/ziSnryncPNn7bMLt5cFYsKVKBh8HqLqyCoTupEUu7Q==", "integrity": "sha512-GsYosgnACZTADcmEyJctkJIoqAhHjttw7RsFrVoJNXbsWWqaq6Ym+7kZjq6mS45O0jij6vtiReppKQEtqWy6Dw==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
@ -7090,9 +7090,9 @@
"license": "MIT" "license": "MIT"
}, },
"node_modules/electron-to-chromium": { "node_modules/electron-to-chromium": {
"version": "1.5.224", "version": "1.5.227",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.224.tgz", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.227.tgz",
"integrity": "sha512-kWAoUu/bwzvnhpdZSIc6KUyvkI1rbRXMT0Eq8pKReyOyaPZcctMli+EgvcN1PAvwVc7Tdo4Fxi2PsLNDU05mdg==", "integrity": "sha512-ITxuoPfJu3lsNWUi2lBM2PaBPYgH3uqmxut5vmBxgYvyI4AlJ6P3Cai1O76mOrkJCBzq0IxWg/NtqOrpu/0gKA==",
"dev": true, "dev": true,
"license": "ISC" "license": "ISC"
}, },
@ -8603,6 +8603,16 @@
"url": "https://github.com/sponsors/ljharb" "url": "https://github.com/sponsors/ljharb"
} }
}, },
"node_modules/generator-function": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/generator-function/-/generator-function-2.0.0.tgz",
"integrity": "sha512-xPypGGincdfyl/AiSGa7GjXLkvld9V7GjZlowup9SHIJnQnHLFiLODCd/DqKOp0PBagbHJ68r1KJI9Mut7m4sA==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.4"
}
},
"node_modules/gensync": { "node_modules/gensync": {
"version": "1.0.0-beta.2", "version": "1.0.0-beta.2",
"resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
@ -8836,21 +8846,21 @@
} }
}, },
"node_modules/globby": { "node_modules/globby": {
"version": "14.1.0", "version": "15.0.0",
"resolved": "https://registry.npmjs.org/globby/-/globby-14.1.0.tgz", "resolved": "https://registry.npmjs.org/globby/-/globby-15.0.0.tgz",
"integrity": "sha512-0Ia46fDOaT7k4og1PDW4YbodWWr3scS2vAr2lTbsplOt2WkKp0vQbkI9wKis/T5LV/dqPjO3bpS/z6GTJB82LA==", "integrity": "sha512-oB4vkQGqlMl682wL1IlWd02tXCbquGWM4voPEI85QmNKCaw8zGTm1f1rubFgkg3Eli2PtKlFgrnmUqasbQWlkw==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@sindresorhus/merge-streams": "^2.1.0", "@sindresorhus/merge-streams": "^4.0.0",
"fast-glob": "^3.3.3", "fast-glob": "^3.3.3",
"ignore": "^7.0.3", "ignore": "^7.0.5",
"path-type": "^6.0.0", "path-type": "^6.0.0",
"slash": "^5.1.0", "slash": "^5.1.0",
"unicorn-magic": "^0.3.0" "unicorn-magic": "^0.3.0"
}, },
"engines": { "engines": {
"node": ">=18" "node": ">=20"
}, },
"funding": { "funding": {
"url": "https://github.com/sponsors/sindresorhus" "url": "https://github.com/sponsors/sindresorhus"
@ -9915,13 +9925,14 @@
} }
}, },
"node_modules/is-generator-function": { "node_modules/is-generator-function": {
"version": "1.1.0", "version": "1.1.1",
"resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.0.tgz", "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.1.tgz",
"integrity": "sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ==", "integrity": "sha512-Gn8BWUdrTzf9XUJAvqIYP7QnSC3mKs8QjQdGdJ7HmBemzZo14wj/OVmmAwgxDX/7WhFEjboybL4VhXGIQYPlOA==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"call-bound": "^1.0.3", "call-bound": "^1.0.3",
"generator-function": "^2.0.0",
"get-proto": "^1.0.0", "get-proto": "^1.0.0",
"has-tostringtag": "^1.0.2", "has-tostringtag": "^1.0.2",
"safe-regex-test": "^1.1.0" "safe-regex-test": "^1.1.0"
@ -10521,16 +10532,16 @@
} }
}, },
"node_modules/jest-diff": { "node_modules/jest-diff": {
"version": "30.1.2", "version": "30.2.0",
"resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-30.1.2.tgz", "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-30.2.0.tgz",
"integrity": "sha512-4+prq+9J61mOVXCa4Qp8ZjavdxzrWQXrI80GNxP8f4tkI2syPuPrJgdRPZRrfUTRvIoUwcmNLbqEJy9W800+NQ==", "integrity": "sha512-dQHFo3Pt4/NLlG5z4PxZ/3yZTZ1C7s9hveiOj+GCN+uT109NC2QgsoVZsVOAvbJ3RgKkvyLGXZV9+piDpWbm6A==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@jest/diff-sequences": "30.0.1", "@jest/diff-sequences": "30.0.1",
"@jest/get-type": "30.1.0", "@jest/get-type": "30.1.0",
"chalk": "^4.1.2", "chalk": "^4.1.2",
"pretty-format": "30.0.5" "pretty-format": "30.2.0"
}, },
"engines": { "engines": {
"node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0"
@ -14834,9 +14845,9 @@
} }
}, },
"node_modules/pretty-format": { "node_modules/pretty-format": {
"version": "30.0.5", "version": "30.2.0",
"resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-30.0.5.tgz", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-30.2.0.tgz",
"integrity": "sha512-D1tKtYvByrBkFLe2wHJl2bwMJIiT8rW+XA+TiataH79/FszLQMrpGEvzUVkzPau7OCO0Qnrhpe87PqtOAIB8Yw==", "integrity": "sha512-9uBdv/B4EefsuAL+pWqueZyZS2Ba+LxfFeQ9DN14HU4bN8bhaxKdkpjpB6fs9+pSjIBu+FXQHImEg8j/Lw0+vA==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
@ -18971,9 +18982,9 @@
} }
}, },
"node_modules/vscode-css-languageservice": { "node_modules/vscode-css-languageservice": {
"version": "6.3.7", "version": "6.3.8",
"resolved": "https://registry.npmjs.org/vscode-css-languageservice/-/vscode-css-languageservice-6.3.7.tgz", "resolved": "https://registry.npmjs.org/vscode-css-languageservice/-/vscode-css-languageservice-6.3.8.tgz",
"integrity": "sha512-5TmXHKllPzfkPhW4UE9sODV3E0bIOJPOk+EERKllf2SmAczjfTmYeq5txco+N3jpF8KIZ6loj/JptpHBQuVQRA==", "integrity": "sha512-dBk/9ullEjIMbfSYAohGpDOisOVU1x2MQHOeU12ohGJQI7+r0PCimBwaa/pWpxl/vH4f7ibrBfxIZY3anGmHKQ==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
@ -18984,9 +18995,9 @@
} }
}, },
"node_modules/vscode-html-languageservice": { "node_modules/vscode-html-languageservice": {
"version": "5.5.1", "version": "5.5.2",
"resolved": "https://registry.npmjs.org/vscode-html-languageservice/-/vscode-html-languageservice-5.5.1.tgz", "resolved": "https://registry.npmjs.org/vscode-html-languageservice/-/vscode-html-languageservice-5.5.2.tgz",
"integrity": "sha512-/ZdEtsZ3OiFSyL00kmmu7crFV9KwWR+MgpzjsxO60DQH7sIfHZM892C/E4iDd11EKocr+NYuvOA4Y7uc3QzLEA==", "integrity": "sha512-QpaUhCjvb7U/qThOzo4V6grwsRE62Jk/vf8BRJZoABlMw3oplLB5uovrvcrLO9vYhkeMiSjyqLnCxbfHzzZqmw==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {

View File

@ -132,7 +132,7 @@
"bundlewatch": "^0.4.1", "bundlewatch": "^0.4.1",
"clean-css-cli": "^5.6.3", "clean-css-cli": "^5.6.3",
"clipboard": "^2.0.11", "clipboard": "^2.0.11",
"cross-env": "^10.0.0", "cross-env": "^10.1.0",
"eslint": "8.57.1", "eslint": "8.57.1",
"eslint-config-xo": "0.45.0", "eslint-config-xo": "0.45.0",
"eslint-plugin-html": "^8.1.3", "eslint-plugin-html": "^8.1.3",
@ -141,7 +141,7 @@
"eslint-plugin-unicorn": "56.0.1", "eslint-plugin-unicorn": "56.0.1",
"find-unused-sass-variables": "^6.1.0", "find-unused-sass-variables": "^6.1.0",
"github-slugger": "^2.0.0", "github-slugger": "^2.0.0",
"globby": "^14.1.0", "globby": "^15.0.0",
"hammer-simulator": "0.0.1", "hammer-simulator": "0.0.1",
"htmlparser2": "^10.0.0", "htmlparser2": "^10.0.0",
"image-size": "^2.0.2", "image-size": "^2.0.2",

View File

@ -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}'`. 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. 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.

View File

@ -782,11 +782,44 @@ const myModalAlternative = new bootstrap.Modal('#myModal', options)
<BsTable> <BsTable>
| Name | Type | Default | Description | | Name | Type | Default | Description |
| --- | --- | --- | --- | | --- | --- | --- | --- |
| `backdrop` | boolean, `static'` | `true` | Includes a modal-backdrop element. Alternatively, specify `static` for a backdrop which doesnt close the modal when clicked. | | `backdrop` | true, `static'`, <br /> function | `true` | Includes a modal-backdrop element. Alternatively, specify `static` for a backdrop which doesnt close the modal when clicked. |
| `focus` | boolean | `true` | Puts the focus on the modal when initialized. | | `focus` | boolean, function | `true` | Puts the focus on the modal when initialized. |
| `keyboard` | boolean | `true` | Closes the modal when escape key is pressed. | | `keyboard` | boolean, function | `true` | Closes the modal when escape key is pressed. |
</BsTable> </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 ### Methods
<Callout name="danger-async-methods" type="danger" /> <Callout name="danger-async-methods" type="danger" />