From 788a17b0863ea1b51b942d73baa594d8cc1aee51 Mon Sep 17 00:00:00 2001 From: Mark Otto Date: Thu, 11 Dec 2025 12:13:36 -0800 Subject: [PATCH] Remove all of Modal --- js/dist/modal.js | 300 ---- js/dist/modal.js.map | 1 - js/src/modal.js | 355 ----- js/tests/unit/modal.spec.js | 1239 ----------------- js/tests/visual/modal.html | 279 ---- scss/_modal.scss | 248 ---- scss/_variables.scss | 63 +- scss/bootstrap.scss | 1 - site/data/sidebar.yml | 1 - .../examples/cheatsheet-rtl/index.astro | 108 -- .../assets/examples/cheatsheet/index.astro | 133 +- site/src/assets/examples/modals/index.astro | 147 -- site/src/assets/examples/modals/modals.css | 7 - site/src/assets/partials/snippets.js | 25 - site/src/components/home/Plugins.astro | 2 +- site/src/content/docs/components/modal.mdx | 827 ----------- .../src/content/docs/components/offcanvas.mdx | 4 +- site/src/content/docs/components/popovers.mdx | 6 +- site/src/content/docs/customize/optimize.mdx | 8 +- .../docs/getting-started/javascript.mdx | 35 +- site/src/content/docs/utilities/z-index.mdx | 2 +- 21 files changed, 79 insertions(+), 3712 deletions(-) delete mode 100644 js/dist/modal.js delete mode 100644 js/dist/modal.js.map delete mode 100644 js/src/modal.js delete mode 100644 js/tests/unit/modal.spec.js delete mode 100644 js/tests/visual/modal.html delete mode 100644 scss/_modal.scss delete mode 100644 site/src/assets/examples/modals/index.astro delete mode 100644 site/src/assets/examples/modals/modals.css delete mode 100644 site/src/content/docs/components/modal.mdx diff --git a/js/dist/modal.js b/js/dist/modal.js deleted file mode 100644 index 21fe4e0923..0000000000 --- a/js/dist/modal.js +++ /dev/null @@ -1,300 +0,0 @@ -/*! - * Bootstrap modal.js v5.3.8 (https://getbootstrap.com/) - * Copyright 2011-2025 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors) - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) - */ -(function (global, factory) { - typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('./base-component.js'), require('./dom/event-handler.js'), require('./dom/selector-engine.js'), require('./util/backdrop.js'), require('./util/component-functions.js'), require('./util/focustrap.js'), require('./util/index.js'), require('./util/scrollbar.js')) : - typeof define === 'function' && define.amd ? define(['./base-component', './dom/event-handler', './dom/selector-engine', './util/backdrop', './util/component-functions', './util/focustrap', './util/index', './util/scrollbar'], factory) : - (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.Modal = factory(global.BaseComponent, global.EventHandler, global.SelectorEngine, global.Backdrop, global.ComponentFunctions, global.Focustrap, global.Index, global.Scrollbar)); -})(this, (function (BaseComponent, EventHandler, SelectorEngine, Backdrop, componentFunctions_js, FocusTrap, index_js, ScrollBarHelper) { 'use strict'; - - /** - * -------------------------------------------------------------------------- - * Bootstrap modal.js - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) - * -------------------------------------------------------------------------- - */ - - - /** - * Constants - */ - - const NAME = 'modal'; - const DATA_KEY = 'bs.modal'; - const EVENT_KEY = `.${DATA_KEY}`; - const DATA_API_KEY = '.data-api'; - const ESCAPE_KEY = 'Escape'; - const EVENT_HIDE = `hide${EVENT_KEY}`; - const EVENT_HIDE_PREVENTED = `hidePrevented${EVENT_KEY}`; - const EVENT_HIDDEN = `hidden${EVENT_KEY}`; - const EVENT_SHOW = `show${EVENT_KEY}`; - const EVENT_SHOWN = `shown${EVENT_KEY}`; - const EVENT_RESIZE = `resize${EVENT_KEY}`; - const EVENT_CLICK_DISMISS = `click.dismiss${EVENT_KEY}`; - const EVENT_MOUSEDOWN_DISMISS = `mousedown.dismiss${EVENT_KEY}`; - const EVENT_KEYDOWN_DISMISS = `keydown.dismiss${EVENT_KEY}`; - const EVENT_CLICK_DATA_API = `click${EVENT_KEY}${DATA_API_KEY}`; - const CLASS_NAME_OPEN = 'modal-open'; - const CLASS_NAME_FADE = 'fade'; - const CLASS_NAME_SHOW = 'show'; - const CLASS_NAME_STATIC = 'modal-static'; - const OPEN_SELECTOR = '.modal.show'; - const SELECTOR_DIALOG = '.modal-dialog'; - const SELECTOR_MODAL_BODY = '.modal-body'; - const SELECTOR_DATA_TOGGLE = '[data-bs-toggle="modal"]'; - const Default = { - backdrop: true, - focus: true, - keyboard: true - }; - const DefaultType = { - backdrop: '(boolean|string)', - focus: 'boolean', - keyboard: 'boolean' - }; - - /** - * Class definition - */ - - class Modal extends BaseComponent { - constructor(element, config) { - super(element, config); - this._dialog = SelectorEngine.findOne(SELECTOR_DIALOG, this._element); - this._backdrop = this._initializeBackDrop(); - this._focustrap = this._initializeFocusTrap(); - this._isShown = false; - this._isTransitioning = false; - this._scrollBar = new ScrollBarHelper(); - this._addEventListeners(); - } - - // Getters - static get Default() { - return Default; - } - static get DefaultType() { - return DefaultType; - } - static get NAME() { - return NAME; - } - - // Public - toggle(relatedTarget) { - return this._isShown ? this.hide() : this.show(relatedTarget); - } - show(relatedTarget) { - if (this._isShown || this._isTransitioning) { - return; - } - const showEvent = EventHandler.trigger(this._element, EVENT_SHOW, { - relatedTarget - }); - if (showEvent.defaultPrevented) { - return; - } - this._isShown = true; - this._isTransitioning = true; - this._scrollBar.hide(); - document.body.classList.add(CLASS_NAME_OPEN); - this._adjustDialog(); - this._backdrop.show(() => this._showElement(relatedTarget)); - } - hide() { - if (!this._isShown || this._isTransitioning) { - return; - } - const hideEvent = EventHandler.trigger(this._element, EVENT_HIDE); - if (hideEvent.defaultPrevented) { - return; - } - this._isShown = false; - this._isTransitioning = true; - this._focustrap.deactivate(); - this._element.classList.remove(CLASS_NAME_SHOW); - this._queueCallback(() => this._hideModal(), this._element, this._isAnimated()); - } - dispose() { - EventHandler.off(window, EVENT_KEY); - EventHandler.off(this._dialog, EVENT_KEY); - this._backdrop.dispose(); - this._focustrap.deactivate(); - super.dispose(); - } - handleUpdate() { - this._adjustDialog(); - } - - // Private - _initializeBackDrop() { - return new Backdrop({ - isVisible: Boolean(this._config.backdrop), - // 'static' option will be translated to true, and booleans will keep their value, - isAnimated: this._isAnimated() - }); - } - _initializeFocusTrap() { - return new FocusTrap({ - trapElement: this._element - }); - } - _showElement(relatedTarget) { - // try to append dynamic modal - if (!document.body.contains(this._element)) { - document.body.append(this._element); - } - this._element.style.display = 'block'; - this._element.removeAttribute('aria-hidden'); - this._element.setAttribute('aria-modal', true); - this._element.setAttribute('role', 'dialog'); - this._element.scrollTop = 0; - const modalBody = SelectorEngine.findOne(SELECTOR_MODAL_BODY, this._dialog); - if (modalBody) { - modalBody.scrollTop = 0; - } - index_js.reflow(this._element); - this._element.classList.add(CLASS_NAME_SHOW); - const transitionComplete = () => { - if (this._config.focus) { - this._focustrap.activate(); - } - this._isTransitioning = false; - EventHandler.trigger(this._element, EVENT_SHOWN, { - relatedTarget - }); - }; - this._queueCallback(transitionComplete, this._dialog, this._isAnimated()); - } - _addEventListeners() { - EventHandler.on(this._element, EVENT_KEYDOWN_DISMISS, event => { - if (event.key !== ESCAPE_KEY) { - return; - } - if (this._config.keyboard) { - this.hide(); - return; - } - this._triggerBackdropTransition(); - }); - EventHandler.on(window, EVENT_RESIZE, () => { - if (this._isShown && !this._isTransitioning) { - this._adjustDialog(); - } - }); - EventHandler.on(this._element, EVENT_MOUSEDOWN_DISMISS, event => { - // a bad trick to segregate clicks that may start inside dialog but end outside, and avoid listen to scrollbar clicks - EventHandler.one(this._element, EVENT_CLICK_DISMISS, event2 => { - if (this._element !== event.target || this._element !== event2.target) { - return; - } - if (this._config.backdrop === 'static') { - this._triggerBackdropTransition(); - return; - } - if (this._config.backdrop) { - this.hide(); - } - }); - }); - } - _hideModal() { - this._element.style.display = 'none'; - this._element.setAttribute('aria-hidden', true); - this._element.removeAttribute('aria-modal'); - this._element.removeAttribute('role'); - this._isTransitioning = false; - this._backdrop.hide(() => { - document.body.classList.remove(CLASS_NAME_OPEN); - this._resetAdjustments(); - this._scrollBar.reset(); - EventHandler.trigger(this._element, EVENT_HIDDEN); - }); - } - _isAnimated() { - return this._element.classList.contains(CLASS_NAME_FADE); - } - _triggerBackdropTransition() { - const hideEvent = EventHandler.trigger(this._element, EVENT_HIDE_PREVENTED); - if (hideEvent.defaultPrevented) { - return; - } - const isModalOverflowing = this._element.scrollHeight > document.documentElement.clientHeight; - const initialOverflowY = this._element.style.overflowY; - // return if the following background transition hasn't yet completed - if (initialOverflowY === 'hidden' || this._element.classList.contains(CLASS_NAME_STATIC)) { - return; - } - if (!isModalOverflowing) { - this._element.style.overflowY = 'hidden'; - } - this._element.classList.add(CLASS_NAME_STATIC); - this._queueCallback(() => { - this._element.classList.remove(CLASS_NAME_STATIC); - this._queueCallback(() => { - this._element.style.overflowY = initialOverflowY; - }, this._dialog); - }, this._dialog); - this._element.focus(); - } - - /** - * The following methods are used to handle overflowing modals - */ - - _adjustDialog() { - const isModalOverflowing = this._element.scrollHeight > document.documentElement.clientHeight; - const scrollbarWidth = this._scrollBar.getWidth(); - const isBodyOverflowing = scrollbarWidth > 0; - if (isBodyOverflowing && !isModalOverflowing) { - const property = index_js.isRTL() ? 'paddingLeft' : 'paddingRight'; - this._element.style[property] = `${scrollbarWidth}px`; - } - if (!isBodyOverflowing && isModalOverflowing) { - const property = index_js.isRTL() ? 'paddingRight' : 'paddingLeft'; - this._element.style[property] = `${scrollbarWidth}px`; - } - } - _resetAdjustments() { - this._element.style.paddingLeft = ''; - this._element.style.paddingRight = ''; - } - } - - /** - * Data API implementation - */ - - EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) { - const target = SelectorEngine.getElementFromSelector(this); - if (['A', 'AREA'].includes(this.tagName)) { - event.preventDefault(); - } - EventHandler.one(target, EVENT_SHOW, showEvent => { - if (showEvent.defaultPrevented) { - // only register focus restorer if modal will actually get shown - return; - } - EventHandler.one(target, EVENT_HIDDEN, () => { - if (index_js.isVisible(this)) { - this.focus(); - } - }); - }); - - // avoid conflict when clicking modal toggler while another one is open - const alreadyOpen = SelectorEngine.findOne(OPEN_SELECTOR); - if (alreadyOpen) { - Modal.getInstance(alreadyOpen).hide(); - } - const data = Modal.getOrCreateInstance(target); - data.toggle(this); - }); - componentFunctions_js.enableDismissTrigger(Modal); - - return Modal; - -})); -//# sourceMappingURL=modal.js.map diff --git a/js/dist/modal.js.map b/js/dist/modal.js.map deleted file mode 100644 index 227a507232..0000000000 --- a/js/dist/modal.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"modal.js","sources":["../src/modal.js"],"sourcesContent":["/**\n * --------------------------------------------------------------------------\n * Bootstrap modal.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport BaseComponent from './base-component.js'\nimport EventHandler from './dom/event-handler.js'\nimport SelectorEngine from './dom/selector-engine.js'\nimport Backdrop from './util/backdrop.js'\nimport { enableDismissTrigger } from './util/component-functions.js'\nimport FocusTrap from './util/focustrap.js'\nimport {\n isRTL, isVisible, reflow\n} from './util/index.js'\nimport ScrollBarHelper from './util/scrollbar.js'\n\n/**\n * Constants\n */\n\nconst NAME = 'modal'\nconst DATA_KEY = 'bs.modal'\nconst EVENT_KEY = `.${DATA_KEY}`\nconst DATA_API_KEY = '.data-api'\nconst ESCAPE_KEY = 'Escape'\n\nconst EVENT_HIDE = `hide${EVENT_KEY}`\nconst EVENT_HIDE_PREVENTED = `hidePrevented${EVENT_KEY}`\nconst EVENT_HIDDEN = `hidden${EVENT_KEY}`\nconst EVENT_SHOW = `show${EVENT_KEY}`\nconst EVENT_SHOWN = `shown${EVENT_KEY}`\nconst EVENT_RESIZE = `resize${EVENT_KEY}`\nconst EVENT_CLICK_DISMISS = `click.dismiss${EVENT_KEY}`\nconst EVENT_MOUSEDOWN_DISMISS = `mousedown.dismiss${EVENT_KEY}`\nconst EVENT_KEYDOWN_DISMISS = `keydown.dismiss${EVENT_KEY}`\nconst EVENT_CLICK_DATA_API = `click${EVENT_KEY}${DATA_API_KEY}`\n\nconst CLASS_NAME_OPEN = 'modal-open'\nconst CLASS_NAME_FADE = 'fade'\nconst CLASS_NAME_SHOW = 'show'\nconst CLASS_NAME_STATIC = 'modal-static'\n\nconst OPEN_SELECTOR = '.modal.show'\nconst SELECTOR_DIALOG = '.modal-dialog'\nconst SELECTOR_MODAL_BODY = '.modal-body'\nconst SELECTOR_DATA_TOGGLE = '[data-bs-toggle=\"modal\"]'\n\nconst Default = {\n backdrop: true,\n focus: true,\n keyboard: true\n}\n\nconst DefaultType = {\n backdrop: '(boolean|string)',\n focus: 'boolean',\n keyboard: 'boolean'\n}\n\n/**\n * Class definition\n */\n\nclass Modal extends BaseComponent {\n constructor(element, config) {\n super(element, config)\n\n this._dialog = SelectorEngine.findOne(SELECTOR_DIALOG, this._element)\n this._backdrop = this._initializeBackDrop()\n this._focustrap = this._initializeFocusTrap()\n this._isShown = false\n this._isTransitioning = false\n this._scrollBar = new ScrollBarHelper()\n\n this._addEventListeners()\n }\n\n // Getters\n static get Default() {\n return Default\n }\n\n static get DefaultType() {\n return DefaultType\n }\n\n static get NAME() {\n return NAME\n }\n\n // Public\n toggle(relatedTarget) {\n return this._isShown ? this.hide() : this.show(relatedTarget)\n }\n\n show(relatedTarget) {\n if (this._isShown || this._isTransitioning) {\n return\n }\n\n const showEvent = EventHandler.trigger(this._element, EVENT_SHOW, {\n relatedTarget\n })\n\n if (showEvent.defaultPrevented) {\n return\n }\n\n this._isShown = true\n this._isTransitioning = true\n\n this._scrollBar.hide()\n\n document.body.classList.add(CLASS_NAME_OPEN)\n\n this._adjustDialog()\n\n this._backdrop.show(() => this._showElement(relatedTarget))\n }\n\n hide() {\n if (!this._isShown || this._isTransitioning) {\n return\n }\n\n const hideEvent = EventHandler.trigger(this._element, EVENT_HIDE)\n\n if (hideEvent.defaultPrevented) {\n return\n }\n\n this._isShown = false\n this._isTransitioning = true\n this._focustrap.deactivate()\n\n this._element.classList.remove(CLASS_NAME_SHOW)\n\n this._queueCallback(() => this._hideModal(), this._element, this._isAnimated())\n }\n\n dispose() {\n EventHandler.off(window, EVENT_KEY)\n EventHandler.off(this._dialog, EVENT_KEY)\n\n this._backdrop.dispose()\n this._focustrap.deactivate()\n\n super.dispose()\n }\n\n handleUpdate() {\n this._adjustDialog()\n }\n\n // Private\n _initializeBackDrop() {\n return new Backdrop({\n isVisible: Boolean(this._config.backdrop), // 'static' option will be translated to true, and booleans will keep their value,\n isAnimated: this._isAnimated()\n })\n }\n\n _initializeFocusTrap() {\n return new FocusTrap({\n trapElement: this._element\n })\n }\n\n _showElement(relatedTarget) {\n // try to append dynamic modal\n if (!document.body.contains(this._element)) {\n document.body.append(this._element)\n }\n\n this._element.style.display = 'block'\n this._element.removeAttribute('aria-hidden')\n this._element.setAttribute('aria-modal', true)\n this._element.setAttribute('role', 'dialog')\n this._element.scrollTop = 0\n\n const modalBody = SelectorEngine.findOne(SELECTOR_MODAL_BODY, this._dialog)\n if (modalBody) {\n modalBody.scrollTop = 0\n }\n\n reflow(this._element)\n\n this._element.classList.add(CLASS_NAME_SHOW)\n\n const transitionComplete = () => {\n if (this._config.focus) {\n this._focustrap.activate()\n }\n\n this._isTransitioning = false\n EventHandler.trigger(this._element, EVENT_SHOWN, {\n relatedTarget\n })\n }\n\n this._queueCallback(transitionComplete, this._dialog, this._isAnimated())\n }\n\n _addEventListeners() {\n EventHandler.on(this._element, EVENT_KEYDOWN_DISMISS, event => {\n if (event.key !== ESCAPE_KEY) {\n return\n }\n\n if (this._config.keyboard) {\n this.hide()\n return\n }\n\n this._triggerBackdropTransition()\n })\n\n EventHandler.on(window, EVENT_RESIZE, () => {\n if (this._isShown && !this._isTransitioning) {\n this._adjustDialog()\n }\n })\n\n EventHandler.on(this._element, EVENT_MOUSEDOWN_DISMISS, event => {\n // a bad trick to segregate clicks that may start inside dialog but end outside, and avoid listen to scrollbar clicks\n EventHandler.one(this._element, EVENT_CLICK_DISMISS, event2 => {\n if (this._element !== event.target || this._element !== event2.target) {\n return\n }\n\n if (this._config.backdrop === 'static') {\n this._triggerBackdropTransition()\n return\n }\n\n if (this._config.backdrop) {\n this.hide()\n }\n })\n })\n }\n\n _hideModal() {\n this._element.style.display = 'none'\n this._element.setAttribute('aria-hidden', true)\n this._element.removeAttribute('aria-modal')\n this._element.removeAttribute('role')\n this._isTransitioning = false\n\n this._backdrop.hide(() => {\n document.body.classList.remove(CLASS_NAME_OPEN)\n this._resetAdjustments()\n this._scrollBar.reset()\n EventHandler.trigger(this._element, EVENT_HIDDEN)\n })\n }\n\n _isAnimated() {\n return this._element.classList.contains(CLASS_NAME_FADE)\n }\n\n _triggerBackdropTransition() {\n const hideEvent = EventHandler.trigger(this._element, EVENT_HIDE_PREVENTED)\n if (hideEvent.defaultPrevented) {\n return\n }\n\n const isModalOverflowing = this._element.scrollHeight > document.documentElement.clientHeight\n const initialOverflowY = this._element.style.overflowY\n // return if the following background transition hasn't yet completed\n if (initialOverflowY === 'hidden' || this._element.classList.contains(CLASS_NAME_STATIC)) {\n return\n }\n\n if (!isModalOverflowing) {\n this._element.style.overflowY = 'hidden'\n }\n\n this._element.classList.add(CLASS_NAME_STATIC)\n this._queueCallback(() => {\n this._element.classList.remove(CLASS_NAME_STATIC)\n this._queueCallback(() => {\n this._element.style.overflowY = initialOverflowY\n }, this._dialog)\n }, this._dialog)\n\n this._element.focus()\n }\n\n /**\n * The following methods are used to handle overflowing modals\n */\n\n _adjustDialog() {\n const isModalOverflowing = this._element.scrollHeight > document.documentElement.clientHeight\n const scrollbarWidth = this._scrollBar.getWidth()\n const isBodyOverflowing = scrollbarWidth > 0\n\n if (isBodyOverflowing && !isModalOverflowing) {\n const property = isRTL() ? 'paddingLeft' : 'paddingRight'\n this._element.style[property] = `${scrollbarWidth}px`\n }\n\n if (!isBodyOverflowing && isModalOverflowing) {\n const property = isRTL() ? 'paddingRight' : 'paddingLeft'\n this._element.style[property] = `${scrollbarWidth}px`\n }\n }\n\n _resetAdjustments() {\n this._element.style.paddingLeft = ''\n this._element.style.paddingRight = ''\n }\n}\n\n/**\n * Data API implementation\n */\n\nEventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) {\n const target = SelectorEngine.getElementFromSelector(this)\n\n if (['A', 'AREA'].includes(this.tagName)) {\n event.preventDefault()\n }\n\n EventHandler.one(target, EVENT_SHOW, showEvent => {\n if (showEvent.defaultPrevented) {\n // only register focus restorer if modal will actually get shown\n return\n }\n\n EventHandler.one(target, EVENT_HIDDEN, () => {\n if (isVisible(this)) {\n this.focus()\n }\n })\n })\n\n // avoid conflict when clicking modal toggler while another one is open\n const alreadyOpen = SelectorEngine.findOne(OPEN_SELECTOR)\n if (alreadyOpen) {\n Modal.getInstance(alreadyOpen).hide()\n }\n\n const data = Modal.getOrCreateInstance(target)\n\n data.toggle(this)\n})\n\nenableDismissTrigger(Modal)\n\nexport default Modal\n"],"names":["NAME","DATA_KEY","EVENT_KEY","DATA_API_KEY","ESCAPE_KEY","EVENT_HIDE","EVENT_HIDE_PREVENTED","EVENT_HIDDEN","EVENT_SHOW","EVENT_SHOWN","EVENT_RESIZE","EVENT_CLICK_DISMISS","EVENT_MOUSEDOWN_DISMISS","EVENT_KEYDOWN_DISMISS","EVENT_CLICK_DATA_API","CLASS_NAME_OPEN","CLASS_NAME_FADE","CLASS_NAME_SHOW","CLASS_NAME_STATIC","OPEN_SELECTOR","SELECTOR_DIALOG","SELECTOR_MODAL_BODY","SELECTOR_DATA_TOGGLE","Default","backdrop","focus","keyboard","DefaultType","Modal","BaseComponent","constructor","element","config","_dialog","SelectorEngine","findOne","_element","_backdrop","_initializeBackDrop","_focustrap","_initializeFocusTrap","_isShown","_isTransitioning","_scrollBar","ScrollBarHelper","_addEventListeners","toggle","relatedTarget","hide","show","showEvent","EventHandler","trigger","defaultPrevented","document","body","classList","add","_adjustDialog","_showElement","hideEvent","deactivate","remove","_queueCallback","_hideModal","_isAnimated","dispose","off","window","handleUpdate","Backdrop","isVisible","Boolean","_config","isAnimated","FocusTrap","trapElement","contains","append","style","display","removeAttribute","setAttribute","scrollTop","modalBody","reflow","transitionComplete","activate","on","event","key","_triggerBackdropTransition","one","event2","target","_resetAdjustments","reset","isModalOverflowing","scrollHeight","documentElement","clientHeight","initialOverflowY","overflowY","scrollbarWidth","getWidth","isBodyOverflowing","property","isRTL","paddingLeft","paddingRight","getElementFromSelector","includes","tagName","preventDefault","alreadyOpen","getInstance","data","getOrCreateInstance","enableDismissTrigger"],"mappings":"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"} \ No newline at end of file diff --git a/js/src/modal.js b/js/src/modal.js deleted file mode 100644 index 538d218653..0000000000 --- a/js/src/modal.js +++ /dev/null @@ -1,355 +0,0 @@ -/** - * -------------------------------------------------------------------------- - * Bootstrap modal.js - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) - * -------------------------------------------------------------------------- - */ - -import BaseComponent from './base-component.js' -import EventHandler from './dom/event-handler.js' -import SelectorEngine from './dom/selector-engine.js' -import Backdrop from './util/backdrop.js' -import { enableDismissTrigger } from './util/component-functions.js' -import FocusTrap from './util/focustrap.js' -import { - isRTL, isVisible, reflow -} from './util/index.js' -import ScrollBarHelper from './util/scrollbar.js' - -/** - * Constants - */ - -const NAME = 'modal' -const DATA_KEY = 'bs.modal' -const EVENT_KEY = `.${DATA_KEY}` -const DATA_API_KEY = '.data-api' -const ESCAPE_KEY = 'Escape' - -const EVENT_HIDE = `hide${EVENT_KEY}` -const EVENT_HIDE_PREVENTED = `hidePrevented${EVENT_KEY}` -const EVENT_HIDDEN = `hidden${EVENT_KEY}` -const EVENT_SHOW = `show${EVENT_KEY}` -const EVENT_SHOWN = `shown${EVENT_KEY}` -const EVENT_RESIZE = `resize${EVENT_KEY}` -const EVENT_CLICK_DISMISS = `click.dismiss${EVENT_KEY}` -const EVENT_MOUSEDOWN_DISMISS = `mousedown.dismiss${EVENT_KEY}` -const EVENT_KEYDOWN_DISMISS = `keydown.dismiss${EVENT_KEY}` -const EVENT_CLICK_DATA_API = `click${EVENT_KEY}${DATA_API_KEY}` - -const CLASS_NAME_OPEN = 'modal-open' -const CLASS_NAME_FADE = 'fade' -const CLASS_NAME_SHOW = 'show' -const CLASS_NAME_STATIC = 'modal-static' - -const OPEN_SELECTOR = '.modal.show' -const SELECTOR_DIALOG = '.modal-dialog' -const SELECTOR_MODAL_BODY = '.modal-body' -const SELECTOR_DATA_TOGGLE = '[data-bs-toggle="modal"]' - -const Default = { - backdrop: true, - focus: true, - keyboard: true -} - -const DefaultType = { - backdrop: '(boolean|string)', - focus: 'boolean', - keyboard: 'boolean' -} - -/** - * Class definition - */ - -class Modal extends BaseComponent { - constructor(element, config) { - super(element, config) - - this._dialog = SelectorEngine.findOne(SELECTOR_DIALOG, this._element) - this._backdrop = this._initializeBackDrop() - this._focustrap = this._initializeFocusTrap() - this._isShown = false - this._isTransitioning = false - this._scrollBar = new ScrollBarHelper() - - this._addEventListeners() - } - - // Getters - static get Default() { - return Default - } - - static get DefaultType() { - return DefaultType - } - - static get NAME() { - return NAME - } - - // Public - toggle(relatedTarget) { - return this._isShown ? this.hide() : this.show(relatedTarget) - } - - show(relatedTarget) { - if (this._isShown || this._isTransitioning) { - return - } - - const showEvent = EventHandler.trigger(this._element, EVENT_SHOW, { - relatedTarget - }) - - if (showEvent.defaultPrevented) { - return - } - - this._isShown = true - this._isTransitioning = true - - this._scrollBar.hide() - - document.body.classList.add(CLASS_NAME_OPEN) - - this._adjustDialog() - - this._backdrop.show(() => this._showElement(relatedTarget)) - } - - hide() { - if (!this._isShown || this._isTransitioning) { - return - } - - const hideEvent = EventHandler.trigger(this._element, EVENT_HIDE) - - if (hideEvent.defaultPrevented) { - return - } - - this._isShown = false - this._isTransitioning = true - this._focustrap.deactivate() - - this._element.classList.remove(CLASS_NAME_SHOW) - - this._queueCallback(() => this._hideModal(), this._element, this._isAnimated()) - } - - dispose() { - EventHandler.off(window, EVENT_KEY) - EventHandler.off(this._dialog, EVENT_KEY) - - this._backdrop.dispose() - this._focustrap.deactivate() - - super.dispose() - } - - handleUpdate() { - this._adjustDialog() - } - - // Private - _initializeBackDrop() { - return new Backdrop({ - isVisible: Boolean(this._config.backdrop), // 'static' option will be translated to true, and booleans will keep their value, - isAnimated: this._isAnimated() - }) - } - - _initializeFocusTrap() { - return new FocusTrap({ - trapElement: this._element - }) - } - - _showElement(relatedTarget) { - // try to append dynamic modal - if (!document.body.contains(this._element)) { - document.body.append(this._element) - } - - this._element.style.display = 'block' - this._element.removeAttribute('aria-hidden') - this._element.setAttribute('aria-modal', true) - this._element.setAttribute('role', 'dialog') - this._element.scrollTop = 0 - - const modalBody = SelectorEngine.findOne(SELECTOR_MODAL_BODY, this._dialog) - if (modalBody) { - modalBody.scrollTop = 0 - } - - reflow(this._element) - - this._element.classList.add(CLASS_NAME_SHOW) - - const transitionComplete = () => { - if (this._config.focus) { - this._focustrap.activate() - } - - this._isTransitioning = false - EventHandler.trigger(this._element, EVENT_SHOWN, { - relatedTarget - }) - } - - this._queueCallback(transitionComplete, this._dialog, this._isAnimated()) - } - - _addEventListeners() { - EventHandler.on(this._element, EVENT_KEYDOWN_DISMISS, event => { - if (event.key !== ESCAPE_KEY) { - return - } - - if (this._config.keyboard) { - this.hide() - return - } - - this._triggerBackdropTransition() - }) - - EventHandler.on(window, EVENT_RESIZE, () => { - if (this._isShown && !this._isTransitioning) { - this._adjustDialog() - } - }) - - EventHandler.on(this._element, EVENT_MOUSEDOWN_DISMISS, event => { - // a bad trick to segregate clicks that may start inside dialog but end outside, and avoid listen to scrollbar clicks - EventHandler.one(this._element, EVENT_CLICK_DISMISS, event2 => { - if (this._element !== event.target || this._element !== event2.target) { - return - } - - if (this._config.backdrop === 'static') { - this._triggerBackdropTransition() - return - } - - if (this._config.backdrop) { - this.hide() - } - }) - }) - } - - _hideModal() { - this._element.style.display = 'none' - this._element.setAttribute('aria-hidden', true) - this._element.removeAttribute('aria-modal') - this._element.removeAttribute('role') - this._isTransitioning = false - - this._backdrop.hide(() => { - document.body.classList.remove(CLASS_NAME_OPEN) - this._resetAdjustments() - this._scrollBar.reset() - EventHandler.trigger(this._element, EVENT_HIDDEN) - }) - } - - _isAnimated() { - return this._element.classList.contains(CLASS_NAME_FADE) - } - - _triggerBackdropTransition() { - const hideEvent = EventHandler.trigger(this._element, EVENT_HIDE_PREVENTED) - if (hideEvent.defaultPrevented) { - return - } - - const isModalOverflowing = this._element.scrollHeight > document.documentElement.clientHeight - const initialOverflowY = this._element.style.overflowY - // return if the following background transition hasn't yet completed - if (initialOverflowY === 'hidden' || this._element.classList.contains(CLASS_NAME_STATIC)) { - return - } - - if (!isModalOverflowing) { - this._element.style.overflowY = 'hidden' - } - - this._element.classList.add(CLASS_NAME_STATIC) - this._queueCallback(() => { - this._element.classList.remove(CLASS_NAME_STATIC) - this._queueCallback(() => { - this._element.style.overflowY = initialOverflowY - }, this._dialog) - }, this._dialog) - - this._element.focus() - } - - /** - * The following methods are used to handle overflowing modals - */ - - _adjustDialog() { - const isModalOverflowing = this._element.scrollHeight > document.documentElement.clientHeight - const scrollbarWidth = this._scrollBar.getWidth() - const isBodyOverflowing = scrollbarWidth > 0 - - if (isBodyOverflowing && !isModalOverflowing) { - const property = isRTL() ? 'paddingLeft' : 'paddingRight' - this._element.style[property] = `${scrollbarWidth}px` - } - - if (!isBodyOverflowing && isModalOverflowing) { - const property = isRTL() ? 'paddingRight' : 'paddingLeft' - this._element.style[property] = `${scrollbarWidth}px` - } - } - - _resetAdjustments() { - this._element.style.paddingLeft = '' - this._element.style.paddingRight = '' - } -} - -/** - * Data API implementation - */ - -EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) { - const target = SelectorEngine.getElementFromSelector(this) - - if (['A', 'AREA'].includes(this.tagName)) { - event.preventDefault() - } - - EventHandler.one(target, EVENT_SHOW, showEvent => { - if (showEvent.defaultPrevented) { - // only register focus restorer if modal will actually get shown - return - } - - EventHandler.one(target, EVENT_HIDDEN, () => { - if (isVisible(this)) { - this.focus() - } - }) - }) - - // avoid conflict when clicking modal toggler while another one is open - const alreadyOpen = SelectorEngine.findOne(OPEN_SELECTOR) - if (alreadyOpen) { - Modal.getInstance(alreadyOpen).hide() - } - - const data = Modal.getOrCreateInstance(target) - - data.toggle(this) -}) - -enableDismissTrigger(Modal) - -export default Modal diff --git a/js/tests/unit/modal.spec.js b/js/tests/unit/modal.spec.js deleted file mode 100644 index 1480521206..0000000000 --- a/js/tests/unit/modal.spec.js +++ /dev/null @@ -1,1239 +0,0 @@ -import EventHandler from '../../src/dom/event-handler.js' -import Modal from '../../src/modal.js' -import ScrollBarHelper from '../../src/util/scrollbar.js' -import { - clearBodyAndDocument, clearFixture, createEvent, getFixture -} from '../helpers/fixture.js' - -describe('Modal', () => { - let fixtureEl - - beforeAll(() => { - fixtureEl = getFixture() - }) - - afterEach(() => { - clearFixture() - clearBodyAndDocument() - document.body.classList.remove('modal-open') - - for (const backdrop of document.querySelectorAll('.modal-backdrop')) { - backdrop.remove() - } - }) - - beforeEach(() => { - clearBodyAndDocument() - }) - - describe('VERSION', () => { - it('should return plugin version', () => { - expect(Modal.VERSION).toEqual(jasmine.any(String)) - }) - }) - - describe('Default', () => { - it('should return plugin default config', () => { - expect(Modal.Default).toEqual(jasmine.any(Object)) - }) - }) - - describe('DATA_KEY', () => { - it('should return plugin data key', () => { - expect(Modal.DATA_KEY).toEqual('bs.modal') - }) - }) - - describe('constructor', () => { - it('should take care of element either passed as a CSS selector or DOM element', () => { - fixtureEl.innerHTML = '' - - const modalEl = fixtureEl.querySelector('.modal') - const modalBySelector = new Modal('.modal') - const modalByElement = new Modal(modalEl) - - expect(modalBySelector._element).toEqual(modalEl) - expect(modalByElement._element).toEqual(modalEl) - }) - }) - - describe('toggle', () => { - it('should call ScrollBarHelper to handle scrollBar on body', () => { - return new Promise(resolve => { - fixtureEl.innerHTML = '' - - const spyHide = spyOn(ScrollBarHelper.prototype, 'hide').and.callThrough() - const spyReset = spyOn(ScrollBarHelper.prototype, 'reset').and.callThrough() - const modalEl = fixtureEl.querySelector('.modal') - const modal = new Modal(modalEl) - - modalEl.addEventListener('shown.bs.modal', () => { - expect(spyHide).toHaveBeenCalled() - modal.toggle() - }) - - modalEl.addEventListener('hidden.bs.modal', () => { - expect(spyReset).toHaveBeenCalled() - resolve() - }) - - modal.toggle() - }) - }) - }) - - describe('show', () => { - it('should show a modal', () => { - return new Promise(resolve => { - fixtureEl.innerHTML = '' - - const modalEl = fixtureEl.querySelector('.modal') - const modal = new Modal(modalEl) - - modalEl.addEventListener('show.bs.modal', event => { - expect(event).toBeDefined() - }) - - modalEl.addEventListener('shown.bs.modal', () => { - expect(modalEl.getAttribute('aria-modal')).toEqual('true') - expect(modalEl.getAttribute('role')).toEqual('dialog') - expect(modalEl.getAttribute('aria-hidden')).toBeNull() - expect(modalEl.style.display).toEqual('block') - expect(document.querySelector('.modal-backdrop')).not.toBeNull() - resolve() - }) - - modal.show() - }) - }) - - it('should show a modal without backdrop', () => { - return new Promise(resolve => { - fixtureEl.innerHTML = '' - - const modalEl = fixtureEl.querySelector('.modal') - const modal = new Modal(modalEl, { - backdrop: false - }) - - modalEl.addEventListener('show.bs.modal', event => { - expect(event).toBeDefined() - }) - - modalEl.addEventListener('shown.bs.modal', () => { - expect(modalEl.getAttribute('aria-modal')).toEqual('true') - expect(modalEl.getAttribute('role')).toEqual('dialog') - expect(modalEl.getAttribute('aria-hidden')).toBeNull() - expect(modalEl.style.display).toEqual('block') - expect(document.querySelector('.modal-backdrop')).toBeNull() - resolve() - }) - - modal.show() - }) - }) - - it('should show a modal and append the element', () => { - return new Promise(resolve => { - const modalEl = document.createElement('div') - const id = 'dynamicModal' - - modalEl.setAttribute('id', id) - modalEl.classList.add('modal') - modalEl.innerHTML = '' - - const modal = new Modal(modalEl) - - modalEl.addEventListener('shown.bs.modal', () => { - const dynamicModal = document.getElementById(id) - expect(dynamicModal).not.toBeNull() - dynamicModal.remove() - resolve() - }) - - modal.show() - }) - }) - - it('should do nothing if a modal is shown', () => { - fixtureEl.innerHTML = '' - - const modalEl = fixtureEl.querySelector('.modal') - const modal = new Modal(modalEl) - - const spy = spyOn(EventHandler, 'trigger') - modal._isShown = true - - modal.show() - - expect(spy).not.toHaveBeenCalled() - }) - - it('should do nothing if a modal is transitioning', () => { - fixtureEl.innerHTML = '' - - const modalEl = fixtureEl.querySelector('.modal') - const modal = new Modal(modalEl) - - const spy = spyOn(EventHandler, 'trigger') - modal._isTransitioning = true - - modal.show() - - expect(spy).not.toHaveBeenCalled() - }) - - it('should not fire shown event when show is prevented', () => { - return new Promise((resolve, reject) => { - fixtureEl.innerHTML = '' - - const modalEl = fixtureEl.querySelector('.modal') - const modal = new Modal(modalEl) - - modalEl.addEventListener('show.bs.modal', event => { - event.preventDefault() - - const expectedDone = () => { - expect().nothing() - resolve() - } - - setTimeout(expectedDone, 10) - }) - - modalEl.addEventListener('shown.bs.modal', () => { - reject(new Error('shown event triggered')) - }) - - modal.show() - }) - }) - - it('should be shown after the first call to show() has been prevented while fading is enabled ', () => { - return new Promise(resolve => { - fixtureEl.innerHTML = '' - - const modalEl = fixtureEl.querySelector('.modal') - const modal = new Modal(modalEl) - - let prevented = false - modalEl.addEventListener('show.bs.modal', event => { - if (!prevented) { - event.preventDefault() - prevented = true - - setTimeout(() => { - modal.show() - }) - } - }) - - modalEl.addEventListener('shown.bs.modal', () => { - expect(prevented).toBeTrue() - expect(modal._isAnimated()).toBeTrue() - resolve() - }) - - modal.show() - }) - }) - it('should set is transitioning if fade class is present', () => { - return new Promise(resolve => { - fixtureEl.innerHTML = '' - - const modalEl = fixtureEl.querySelector('.modal') - const modal = new Modal(modalEl) - - modalEl.addEventListener('show.bs.modal', () => { - setTimeout(() => { - expect(modal._isTransitioning).toBeTrue() - }) - }) - - modalEl.addEventListener('shown.bs.modal', () => { - expect(modal._isTransitioning).toBeFalse() - resolve() - }) - - modal.show() - }) - }) - - it('should close modal when a click occurred on data-bs-dismiss="modal" inside modal', () => { - return new Promise(resolve => { - fixtureEl.innerHTML = [ - '' - ].join('') - - const modalEl = fixtureEl.querySelector('.modal') - const btnClose = fixtureEl.querySelector('[data-bs-dismiss="modal"]') - const modal = new Modal(modalEl) - - const spy = spyOn(modal, 'hide').and.callThrough() - - modalEl.addEventListener('shown.bs.modal', () => { - btnClose.click() - }) - - modalEl.addEventListener('hidden.bs.modal', () => { - expect(spy).toHaveBeenCalled() - resolve() - }) - - modal.show() - }) - }) - - it('should close modal when a click occurred on a data-bs-dismiss="modal" with "bs-target" outside of modal element', () => { - return new Promise(resolve => { - fixtureEl.innerHTML = [ - '', - '' - ].join('') - - const modalEl = fixtureEl.querySelector('.modal') - const btnClose = fixtureEl.querySelector('[data-bs-dismiss="modal"]') - const modal = new Modal(modalEl) - - const spy = spyOn(modal, 'hide').and.callThrough() - - modalEl.addEventListener('shown.bs.modal', () => { - btnClose.click() - }) - - modalEl.addEventListener('hidden.bs.modal', () => { - expect(spy).toHaveBeenCalled() - resolve() - }) - - modal.show() - }) - }) - - it('should set .modal\'s scroll top to 0', () => { - return new Promise(resolve => { - fixtureEl.innerHTML = [ - '' - ].join('') - - const modalEl = fixtureEl.querySelector('.modal') - const modal = new Modal(modalEl) - - modalEl.addEventListener('shown.bs.modal', () => { - expect(modalEl.scrollTop).toEqual(0) - resolve() - }) - - modal.show() - }) - }) - - it('should set modal body scroll top to 0 if modal body do not exists', () => { - return new Promise(resolve => { - fixtureEl.innerHTML = [ - '' - ].join('') - - const modalEl = fixtureEl.querySelector('.modal') - const modalBody = modalEl.querySelector('.modal-body') - const modal = new Modal(modalEl) - - modalEl.addEventListener('shown.bs.modal', () => { - expect(modalBody.scrollTop).toEqual(0) - resolve() - }) - - modal.show() - }) - }) - - it('should not trap focus if focus equal to false', () => { - return new Promise(resolve => { - fixtureEl.innerHTML = '' - - const modalEl = fixtureEl.querySelector('.modal') - const modal = new Modal(modalEl, { - focus: false - }) - - const spy = spyOn(modal._focustrap, 'activate').and.callThrough() - - modalEl.addEventListener('shown.bs.modal', () => { - expect(spy).not.toHaveBeenCalled() - resolve() - }) - - modal.show() - }) - }) - - it('should add listener when escape touch is pressed', () => { - return new Promise(resolve => { - fixtureEl.innerHTML = '' - - const modalEl = fixtureEl.querySelector('.modal') - const modal = new Modal(modalEl) - - const spy = spyOn(modal, 'hide').and.callThrough() - - modalEl.addEventListener('shown.bs.modal', () => { - const keydownEscape = createEvent('keydown') - keydownEscape.key = 'Escape' - - modalEl.dispatchEvent(keydownEscape) - }) - - modalEl.addEventListener('hidden.bs.modal', () => { - expect(spy).toHaveBeenCalled() - resolve() - }) - - modal.show() - }) - }) - - it('should do nothing when the pressed key is not escape', () => { - return new Promise(resolve => { - fixtureEl.innerHTML = '' - - const modalEl = fixtureEl.querySelector('.modal') - const modal = new Modal(modalEl) - - const spy = spyOn(modal, 'hide') - - const expectDone = () => { - expect(spy).not.toHaveBeenCalled() - - resolve() - } - - modalEl.addEventListener('shown.bs.modal', () => { - const keydownTab = createEvent('keydown') - keydownTab.key = 'Tab' - - modalEl.dispatchEvent(keydownTab) - setTimeout(expectDone, 30) - }) - - modal.show() - }) - }) - - it('should adjust dialog on resize', () => { - return new Promise(resolve => { - fixtureEl.innerHTML = '' - - const modalEl = fixtureEl.querySelector('.modal') - const modal = new Modal(modalEl) - - const spy = spyOn(modal, '_adjustDialog').and.callThrough() - - const expectDone = () => { - expect(spy).toHaveBeenCalled() - - resolve() - } - - modalEl.addEventListener('shown.bs.modal', () => { - const resizeEvent = createEvent('resize') - - window.dispatchEvent(resizeEvent) - setTimeout(expectDone, 10) - }) - - modal.show() - }) - }) - - it('should not close modal when clicking on modal-content', () => { - return new Promise((resolve, reject) => { - fixtureEl.innerHTML = [ - '' - ].join('') - - const modalEl = fixtureEl.querySelector('.modal') - const modal = new Modal(modalEl) - - const shownCallback = () => { - setTimeout(() => { - expect(modal._isShown).toEqual(true) - resolve() - }, 10) - } - - modalEl.addEventListener('shown.bs.modal', () => { - fixtureEl.querySelector('.modal-dialog').click() - fixtureEl.querySelector('.modal-content').click() - shownCallback() - }) - - modalEl.addEventListener('hidden.bs.modal', () => { - reject(new Error('Should not hide a modal')) - }) - - modal.show() - }) - }) - - it('should not close modal when clicking outside of modal-content if backdrop = false', () => { - return new Promise((resolve, reject) => { - fixtureEl.innerHTML = '' - - const modalEl = fixtureEl.querySelector('.modal') - const modal = new Modal(modalEl, { - backdrop: false - }) - - const shownCallback = () => { - setTimeout(() => { - expect(modal._isShown).toBeTrue() - resolve() - }, 10) - } - - modalEl.addEventListener('shown.bs.modal', () => { - modalEl.click() - shownCallback() - }) - - modalEl.addEventListener('hidden.bs.modal', () => { - reject(new Error('Should not hide a modal')) - }) - - modal.show() - }) - }) - - it('should not close modal when clicking outside of modal-content if backdrop = static', () => { - return new Promise((resolve, reject) => { - fixtureEl.innerHTML = '' - - const modalEl = fixtureEl.querySelector('.modal') - const modal = new Modal(modalEl, { - backdrop: 'static' - }) - - const shownCallback = () => { - setTimeout(() => { - expect(modal._isShown).toBeTrue() - resolve() - }, 10) - } - - modalEl.addEventListener('shown.bs.modal', () => { - modalEl.click() - shownCallback() - }) - - modalEl.addEventListener('hidden.bs.modal', () => { - reject(new Error('Should not hide a 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 = '' - - const modalEl = fixtureEl.querySelector('.modal') - const modal = new Modal(modalEl, { - backdrop: 'static', - keyboard: true - }) - - const shownCallback = () => { - setTimeout(() => { - expect(modal._isShown).toBeFalse() - resolve() - }, 10) - } - - modalEl.addEventListener('shown.bs.modal', () => { - const keydownEscape = createEvent('keydown') - keydownEscape.key = 'Escape' - - modalEl.dispatchEvent(keydownEscape) - shownCallback() - }) - - modal.show() - }) - }) - - it('should not close modal when escape key is pressed with keyboard = false', () => { - return new Promise((resolve, reject) => { - fixtureEl.innerHTML = '' - - const modalEl = fixtureEl.querySelector('.modal') - const modal = new Modal(modalEl, { - keyboard: false - }) - - const shownCallback = () => { - setTimeout(() => { - expect(modal._isShown).toBeTrue() - resolve() - }, 10) - } - - modalEl.addEventListener('shown.bs.modal', () => { - const keydownEscape = createEvent('keydown') - keydownEscape.key = 'Escape' - - modalEl.dispatchEvent(keydownEscape) - shownCallback() - }) - - modalEl.addEventListener('hidden.bs.modal', () => { - reject(new Error('Should not hide a modal')) - }) - - modal.show() - }) - }) - - it('should not overflow when clicking outside of modal-content if backdrop = static', () => { - return new Promise(resolve => { - fixtureEl.innerHTML = '' - - const modalEl = fixtureEl.querySelector('.modal') - const modal = new Modal(modalEl, { - backdrop: 'static' - }) - - modalEl.addEventListener('shown.bs.modal', () => { - modalEl.click() - setTimeout(() => { - expect(modalEl.clientHeight).toEqual(modalEl.scrollHeight) - resolve() - }, 20) - }) - - modal.show() - }) - }) - - it('should not queue multiple callbacks when clicking outside of modal-content and backdrop = static', () => { - return new Promise(resolve => { - fixtureEl.innerHTML = '' - - const modalEl = fixtureEl.querySelector('.modal') - const modal = new Modal(modalEl, { - backdrop: 'static' - }) - - modalEl.addEventListener('shown.bs.modal', () => { - const spy = spyOn(modal, '_queueCallback').and.callThrough() - const mouseDown = createEvent('mousedown') - - modalEl.dispatchEvent(mouseDown) - modalEl.click() - modalEl.dispatchEvent(mouseDown) - modalEl.click() - - setTimeout(() => { - expect(spy).toHaveBeenCalledTimes(1) - resolve() - }, 20) - }) - - modal.show() - }) - }) - - it('should trap focus', () => { - return new Promise(resolve => { - fixtureEl.innerHTML = '' - - const modalEl = fixtureEl.querySelector('.modal') - const modal = new Modal(modalEl) - - const spy = spyOn(modal._focustrap, 'activate').and.callThrough() - - modalEl.addEventListener('shown.bs.modal', () => { - expect(spy).toHaveBeenCalled() - resolve() - }) - - modal.show() - }) - }) - }) - - describe('hide', () => { - it('should hide a modal', () => { - return new Promise(resolve => { - fixtureEl.innerHTML = '' - - const modalEl = fixtureEl.querySelector('.modal') - const modal = new Modal(modalEl) - const backdropSpy = spyOn(modal._backdrop, 'hide').and.callThrough() - - modalEl.addEventListener('shown.bs.modal', () => { - modal.hide() - }) - - modalEl.addEventListener('hide.bs.modal', event => { - expect(event).toBeDefined() - }) - - modalEl.addEventListener('hidden.bs.modal', () => { - expect(modalEl.getAttribute('aria-modal')).toBeNull() - expect(modalEl.getAttribute('role')).toBeNull() - expect(modalEl.getAttribute('aria-hidden')).toEqual('true') - expect(modalEl.style.display).toEqual('none') - expect(backdropSpy).toHaveBeenCalled() - resolve() - }) - - modal.show() - }) - }) - - it('should close modal when clicking outside of modal-content', () => { - return new Promise(resolve => { - fixtureEl.innerHTML = '' - - const modalEl = fixtureEl.querySelector('.modal') - const dialogEl = modalEl.querySelector('.modal-dialog') - const modal = new Modal(modalEl) - - const spy = spyOn(modal, 'hide') - - modalEl.addEventListener('shown.bs.modal', () => { - const mouseDown = createEvent('mousedown') - - dialogEl.dispatchEvent(mouseDown) - modalEl.click() - expect(spy).not.toHaveBeenCalled() - - modalEl.dispatchEvent(mouseDown) - modalEl.click() - expect(spy).toHaveBeenCalled() - resolve() - }) - - modal.show() - }) - }) - - it('should not close modal when clicking on an element removed from modal content', () => { - return new Promise(resolve => { - fixtureEl.innerHTML = [ - '' - ].join('') - - const modalEl = fixtureEl.querySelector('.modal') - const buttonEl = modalEl.querySelector('.btn') - const modal = new Modal(modalEl) - - const spy = spyOn(modal, 'hide') - buttonEl.addEventListener('click', () => { - buttonEl.remove() - }) - - modalEl.addEventListener('shown.bs.modal', () => { - modalEl.dispatchEvent(createEvent('mousedown')) - buttonEl.click() - expect(spy).not.toHaveBeenCalled() - resolve() - }) - - modal.show() - }) - }) - - it('should do nothing is the modal is not shown', () => { - fixtureEl.innerHTML = '' - - const modalEl = fixtureEl.querySelector('.modal') - const modal = new Modal(modalEl) - - modal.hide() - - expect().nothing() - }) - - it('should do nothing is the modal is transitioning', () => { - fixtureEl.innerHTML = '' - - const modalEl = fixtureEl.querySelector('.modal') - const modal = new Modal(modalEl) - - modal._isTransitioning = true - modal.hide() - - expect().nothing() - }) - - it('should not hide a modal if hide is prevented', () => { - return new Promise((resolve, reject) => { - fixtureEl.innerHTML = '' - - const modalEl = fixtureEl.querySelector('.modal') - const modal = new Modal(modalEl) - - modalEl.addEventListener('shown.bs.modal', () => { - modal.hide() - }) - - const hideCallback = () => { - setTimeout(() => { - expect(modal._isShown).toBeTrue() - resolve() - }, 10) - } - - modalEl.addEventListener('hide.bs.modal', event => { - event.preventDefault() - hideCallback() - }) - - modalEl.addEventListener('hidden.bs.modal', () => { - reject(new Error('should not trigger hidden')) - }) - - modal.show() - }) - }) - - it('should release focus trap', () => { - return new Promise(resolve => { - fixtureEl.innerHTML = '' - - const modalEl = fixtureEl.querySelector('.modal') - const modal = new Modal(modalEl) - const spy = spyOn(modal._focustrap, 'deactivate').and.callThrough() - - modalEl.addEventListener('shown.bs.modal', () => { - modal.hide() - }) - - modalEl.addEventListener('hidden.bs.modal', () => { - expect(spy).toHaveBeenCalled() - resolve() - }) - - modal.show() - }) - }) - }) - - describe('dispose', () => { - it('should dispose a modal', () => { - fixtureEl.innerHTML = '' - - const modalEl = fixtureEl.querySelector('.modal') - const modal = new Modal(modalEl) - const focustrap = modal._focustrap - const spyDeactivate = spyOn(focustrap, 'deactivate').and.callThrough() - - expect(Modal.getInstance(modalEl)).toEqual(modal) - - const spyOff = spyOn(EventHandler, 'off') - - modal.dispose() - - expect(Modal.getInstance(modalEl)).toBeNull() - expect(spyOff).toHaveBeenCalledTimes(3) - expect(spyDeactivate).toHaveBeenCalled() - }) - }) - - describe('handleUpdate', () => { - it('should call adjust dialog', () => { - fixtureEl.innerHTML = '' - - const modalEl = fixtureEl.querySelector('.modal') - const modal = new Modal(modalEl) - - const spy = spyOn(modal, '_adjustDialog') - - modal.handleUpdate() - - expect(spy).toHaveBeenCalled() - }) - }) - - describe('data-api', () => { - it('should toggle modal', () => { - return new Promise(resolve => { - fixtureEl.innerHTML = [ - '', - '' - ].join('') - - const modalEl = fixtureEl.querySelector('.modal') - const trigger = fixtureEl.querySelector('[data-bs-toggle="modal"]') - - modalEl.addEventListener('shown.bs.modal', () => { - expect(modalEl.getAttribute('aria-modal')).toEqual('true') - expect(modalEl.getAttribute('role')).toEqual('dialog') - expect(modalEl.getAttribute('aria-hidden')).toBeNull() - expect(modalEl.style.display).toEqual('block') - expect(document.querySelector('.modal-backdrop')).not.toBeNull() - setTimeout(() => trigger.click(), 10) - }) - - modalEl.addEventListener('hidden.bs.modal', () => { - expect(modalEl.getAttribute('aria-modal')).toBeNull() - expect(modalEl.getAttribute('role')).toBeNull() - expect(modalEl.getAttribute('aria-hidden')).toEqual('true') - expect(modalEl.style.display).toEqual('none') - expect(document.querySelector('.modal-backdrop')).toBeNull() - resolve() - }) - - trigger.click() - }) - }) - - it('should not recreate a new modal', () => { - return new Promise(resolve => { - fixtureEl.innerHTML = [ - '', - '' - ].join('') - - const modalEl = fixtureEl.querySelector('.modal') - const modal = new Modal(modalEl) - const trigger = fixtureEl.querySelector('[data-bs-toggle="modal"]') - - const spy = spyOn(modal, 'show').and.callThrough() - - modalEl.addEventListener('shown.bs.modal', () => { - expect(spy).toHaveBeenCalled() - resolve() - }) - - trigger.click() - }) - }) - - it('should prevent default when the trigger is or ', () => { - return new Promise(resolve => { - fixtureEl.innerHTML = [ - '', - '' - ].join('') - - const modalEl = fixtureEl.querySelector('.modal') - const trigger = fixtureEl.querySelector('[data-bs-toggle="modal"]') - - const spy = spyOn(Event.prototype, 'preventDefault').and.callThrough() - - modalEl.addEventListener('shown.bs.modal', () => { - expect(modalEl.getAttribute('aria-modal')).toEqual('true') - expect(modalEl.getAttribute('role')).toEqual('dialog') - expect(modalEl.getAttribute('aria-hidden')).toBeNull() - expect(modalEl.style.display).toEqual('block') - expect(document.querySelector('.modal-backdrop')).not.toBeNull() - expect(spy).toHaveBeenCalled() - resolve() - }) - - trigger.click() - }) - }) - - it('should focus the trigger on hide', () => { - return new Promise(resolve => { - fixtureEl.innerHTML = [ - '', - '' - ].join('') - - const modalEl = fixtureEl.querySelector('.modal') - const trigger = fixtureEl.querySelector('[data-bs-toggle="modal"]') - - const spy = spyOn(trigger, 'focus') - - modalEl.addEventListener('shown.bs.modal', () => { - const modal = Modal.getInstance(modalEl) - - modal.hide() - }) - - const hideListener = () => { - setTimeout(() => { - expect(spy).toHaveBeenCalled() - resolve() - }, 20) - } - - modalEl.addEventListener('hidden.bs.modal', () => { - hideListener() - }) - - trigger.click() - }) - }) - - it('should open modal, having special characters in its id', () => { - return new Promise(resolve => { - fixtureEl.innerHTML = [ - '', - '' - ].join('') - - const modalEl = fixtureEl.querySelector('.modal') - const trigger = fixtureEl.querySelector('[data-bs-toggle="modal"]') - - modalEl.addEventListener('shown.bs.modal', () => { - resolve() - }) - - trigger.click() - }) - }) - - it('should not prevent default when a click occurred on data-bs-dismiss="modal" where tagName is DIFFERENT than or ', () => { - return new Promise(resolve => { - fixtureEl.innerHTML = [ - '' - ].join('') - - const modalEl = fixtureEl.querySelector('.modal') - const btnClose = fixtureEl.querySelector('button[data-bs-dismiss="modal"]') - const modal = new Modal(modalEl) - - const spy = spyOn(Event.prototype, 'preventDefault').and.callThrough() - - modalEl.addEventListener('shown.bs.modal', () => { - btnClose.click() - }) - - modalEl.addEventListener('hidden.bs.modal', () => { - expect(spy).not.toHaveBeenCalled() - resolve() - }) - - modal.show() - }) - }) - - it('should prevent default when a click occurred on data-bs-dismiss="modal" where tagName is or ', () => { - return new Promise(resolve => { - fixtureEl.innerHTML = [ - '' - ].join('') - - const modalEl = fixtureEl.querySelector('.modal') - const btnClose = fixtureEl.querySelector('a[data-bs-dismiss="modal"]') - const modal = new Modal(modalEl) - - const spy = spyOn(Event.prototype, 'preventDefault').and.callThrough() - - modalEl.addEventListener('shown.bs.modal', () => { - btnClose.click() - }) - - modalEl.addEventListener('hidden.bs.modal', () => { - expect(spy).toHaveBeenCalled() - resolve() - }) - - modal.show() - }) - }) - it('should not focus the trigger if the modal is not visible', () => { - return new Promise(resolve => { - fixtureEl.innerHTML = [ - '', - '' - ].join('') - - const modalEl = fixtureEl.querySelector('.modal') - const trigger = fixtureEl.querySelector('[data-bs-toggle="modal"]') - - const spy = spyOn(trigger, 'focus') - - modalEl.addEventListener('shown.bs.modal', () => { - const modal = Modal.getInstance(modalEl) - - modal.hide() - }) - - const hideListener = () => { - setTimeout(() => { - expect(spy).not.toHaveBeenCalled() - resolve() - }, 20) - } - - modalEl.addEventListener('hidden.bs.modal', () => { - hideListener() - }) - - trigger.click() - }) - }) - it('should not focus the trigger if the modal is not shown', () => { - return new Promise(resolve => { - fixtureEl.innerHTML = [ - '', - '' - ].join('') - - const modalEl = fixtureEl.querySelector('.modal') - const trigger = fixtureEl.querySelector('[data-bs-toggle="modal"]') - - const spy = spyOn(trigger, 'focus') - - const showListener = () => { - setTimeout(() => { - expect(spy).not.toHaveBeenCalled() - resolve() - }, 10) - } - - modalEl.addEventListener('show.bs.modal', event => { - event.preventDefault() - showListener() - }) - - trigger.click() - }) - }) - - it('should call hide first, if another modal is open', () => { - return new Promise(resolve => { - fixtureEl.innerHTML = [ - '', - '', - '' - ].join('') - - const trigger2 = fixtureEl.querySelector('button') - const modalEl1 = document.querySelector('#modal1') - const modalEl2 = document.querySelector('#modal2') - const modal1 = new Modal(modalEl1) - - modalEl1.addEventListener('shown.bs.modal', () => { - trigger2.click() - }) - modalEl1.addEventListener('hidden.bs.modal', () => { - expect(Modal.getInstance(modalEl2)).not.toBeNull() - expect(modalEl2).toHaveClass('show') - resolve() - }) - modal1.show() - }) - }) - }) - - describe('getInstance', () => { - it('should return modal instance', () => { - fixtureEl.innerHTML = '' - - const div = fixtureEl.querySelector('div') - const modal = new Modal(div) - - expect(Modal.getInstance(div)).toEqual(modal) - expect(Modal.getInstance(div)).toBeInstanceOf(Modal) - }) - - it('should return null when there is no modal instance', () => { - fixtureEl.innerHTML = '' - - const div = fixtureEl.querySelector('div') - - expect(Modal.getInstance(div)).toBeNull() - }) - }) - - describe('getOrCreateInstance', () => { - it('should return modal instance', () => { - fixtureEl.innerHTML = '
' - - const div = fixtureEl.querySelector('div') - const modal = new Modal(div) - - expect(Modal.getOrCreateInstance(div)).toEqual(modal) - expect(Modal.getInstance(div)).toEqual(Modal.getOrCreateInstance(div, {})) - expect(Modal.getOrCreateInstance(div)).toBeInstanceOf(Modal) - }) - - it('should return new instance when there is no modal instance', () => { - fixtureEl.innerHTML = '
' - - const div = fixtureEl.querySelector('div') - - expect(Modal.getInstance(div)).toBeNull() - expect(Modal.getOrCreateInstance(div)).toBeInstanceOf(Modal) - }) - - it('should return new instance when there is no modal instance with given configuration', () => { - fixtureEl.innerHTML = '
' - - const div = fixtureEl.querySelector('div') - - expect(Modal.getInstance(div)).toBeNull() - const modal = Modal.getOrCreateInstance(div, { - backdrop: true - }) - expect(modal).toBeInstanceOf(Modal) - - expect(modal._config.backdrop).toBeTrue() - }) - - it('should return the instance when exists without given configuration', () => { - fixtureEl.innerHTML = '
' - - const div = fixtureEl.querySelector('div') - const modal = new Modal(div, { - backdrop: true - }) - expect(Modal.getInstance(div)).toEqual(modal) - - const modal2 = Modal.getOrCreateInstance(div, { - backdrop: false - }) - expect(modal).toBeInstanceOf(Modal) - expect(modal2).toEqual(modal) - - expect(modal2._config.backdrop).toBeTrue() - }) - }) -}) diff --git a/js/tests/visual/modal.html b/js/tests/visual/modal.html deleted file mode 100644 index efb5127b5a..0000000000 --- a/js/tests/visual/modal.html +++ /dev/null @@ -1,279 +0,0 @@ - - - - - - - Modal - - - - - -
-

Modal Bootstrap Visual Test

- - - - - - - - - - - -

- - - (See Issue #18365) - -

- - - -

- - - - - -

- - -
- - - - - diff --git a/scss/_modal.scss b/scss/_modal.scss deleted file mode 100644 index b6cee700d9..0000000000 --- a/scss/_modal.scss +++ /dev/null @@ -1,248 +0,0 @@ -@use "sass:map"; -@use "config" as *; -@use "variables" as *; -@use "mixins/border-radius" as *; -@use "mixins/box-shadow" as *; -@use "mixins/transition" as *; -@use "mixins/gradients" as *; -@use "mixins/backdrop" as *; -@use "vendor/rfs" as *; -@use "layout/breakpoints" as *; - -// .modal-open - body class for killing the scroll -// .modal - container to scroll within -// .modal-dialog - positioning shell for the actual modal -// .modal-content - actual modal w/ bg and corners and stuff - -@layer components { - // Container that the modal scrolls within - .modal { - // scss-docs-start modal-css-vars - --#{$prefix}modal-zindex: #{$zindex-modal}; - --#{$prefix}modal-width: #{$modal-md}; - --#{$prefix}modal-padding: #{$modal-inner-padding}; - --#{$prefix}modal-margin: #{$modal-dialog-margin}; - --#{$prefix}modal-color: #{$modal-content-color}; - --#{$prefix}modal-bg: #{$modal-content-bg}; - --#{$prefix}modal-border-color: #{$modal-content-border-color}; - --#{$prefix}modal-border-width: #{$modal-content-border-width}; - --#{$prefix}modal-border-radius: #{$modal-content-border-radius}; - --#{$prefix}modal-box-shadow: #{$modal-content-box-shadow-xs}; - --#{$prefix}modal-inner-border-radius: #{$modal-content-inner-border-radius}; - --#{$prefix}modal-header-padding-x: #{$modal-header-padding-x}; - --#{$prefix}modal-header-padding-y: #{$modal-header-padding-y}; - --#{$prefix}modal-header-padding: #{$modal-header-padding}; // Todo in v6: Split this padding into x and y - --#{$prefix}modal-header-border-color: #{$modal-header-border-color}; - --#{$prefix}modal-header-border-width: #{$modal-header-border-width}; - --#{$prefix}modal-title-line-height: #{$modal-title-line-height}; - --#{$prefix}modal-footer-gap: #{$modal-footer-margin-between}; - --#{$prefix}modal-footer-bg: #{$modal-footer-bg}; - --#{$prefix}modal-footer-border-color: #{$modal-footer-border-color}; - --#{$prefix}modal-footer-border-width: #{$modal-footer-border-width}; - // scss-docs-end modal-css-vars - - position: fixed; - inset: 0 auto auto 0; - z-index: var(--#{$prefix}modal-zindex); - display: none; - width: 100%; - height: 100%; - overflow-x: hidden; - overflow-y: auto; - // Prevent Chrome on Windows from adding a focus outline. For details, see - // https://github.com/twbs/bootstrap/pull/10951. - outline: 0; - // We deliberately don't use `-webkit-overflow-scrolling: touch;` due to a - // gnarly iOS Safari bug: https://bugs.webkit.org/show_bug.cgi?id=158342 - // See also https://github.com/twbs/bootstrap/issues/17695 - } - - // Shell div to position the modal with bottom padding - .modal-dialog { - position: relative; - width: auto; - margin: var(--#{$prefix}modal-margin); - // allow clicks to pass through for custom click handling to close modal - pointer-events: none; - - // When fading in the modal, animate it to slide down - .modal.fade & { - transform: $modal-fade-transform; - @include transition($modal-transition); - } - .modal.show & { - transform: $modal-show-transform; - } - - // When trying to close, animate focus to scale - .modal.modal-static & { - transform: $modal-scale-transform; - } - } - - .modal-dialog-scrollable { - height: calc(100% - var(--#{$prefix}modal-margin) * 2); - - .modal-content { - max-height: 100%; - overflow: hidden; - } - - .modal-body { - overflow-y: auto; - } - } - - .modal-dialog-centered { - display: flex; - align-items: center; - min-height: calc(100% - var(--#{$prefix}modal-margin) * 2); - } - - // Actual modal - .modal-content { - position: relative; - display: flex; - flex-direction: column; - width: 100%; // Ensure `.modal-content` extends the full width of the parent `.modal-dialog` - // counteract the pointer-events: none; in the .modal-dialog - color: var(--#{$prefix}modal-color); - pointer-events: auto; - background-color: var(--#{$prefix}modal-bg); - background-clip: padding-box; - border: var(--#{$prefix}modal-border-width) solid var(--#{$prefix}modal-border-color); - @include border-radius(var(--#{$prefix}modal-border-radius)); - @include box-shadow(var(--#{$prefix}modal-box-shadow)); - // Remove focus outline from opened modal - outline: 0; - } - - // Modal background - .modal-backdrop { - // scss-docs-start modal-backdrop-css-vars - --#{$prefix}backdrop-zindex: #{$zindex-modal-backdrop}; - --#{$prefix}backdrop-bg: #{$modal-backdrop-bg}; - --#{$prefix}backdrop-opacity: #{$modal-backdrop-opacity}; - // scss-docs-end modal-backdrop-css-vars - - @include overlay-backdrop(var(--#{$prefix}backdrop-zindex), var(--#{$prefix}backdrop-bg), var(--#{$prefix}backdrop-opacity)); - } - - // Modal header - // Top section of the modal w/ title and dismiss - .modal-header { - display: flex; - flex-shrink: 0; - align-items: center; - padding: var(--#{$prefix}modal-header-padding); - border-block-end: var(--#{$prefix}modal-header-border-width) solid var(--#{$prefix}modal-header-border-color); - @include border-top-radius(var(--#{$prefix}modal-inner-border-radius)); - - .btn-close { - padding: calc(var(--#{$prefix}modal-header-padding-y) * .5) calc(var(--#{$prefix}modal-header-padding-x) * .5); - // Split properties to avoid invalid calc() function if value is 0 - margin-inline-start: auto; - margin-inline-end: calc(-.5 * var(--#{$prefix}modal-header-padding-x)); - margin-top: calc(-.5 * var(--#{$prefix}modal-header-padding-y)); - margin-bottom: calc(-.5 * var(--#{$prefix}modal-header-padding-y)); - } - } - - // Title text within header - .modal-title { - margin-bottom: 0; - line-height: var(--#{$prefix}modal-title-line-height); - } - - // Modal body - // Where all modal content resides (sibling of .modal-header and .modal-footer) - .modal-body { - position: relative; - // Enable `flex-grow: 1` so that the body take up as much space as possible - // when there should be a fixed height on `.modal-dialog`. - flex: 1 1 auto; - padding: var(--#{$prefix}modal-padding); - } - - // Footer (for actions) - .modal-footer { - display: flex; - flex-shrink: 0; - flex-wrap: wrap; - align-items: center; // vertically center - justify-content: flex-end; // Right align buttons with flex property because text-align doesn't work on flex items - padding: calc(var(--#{$prefix}modal-padding) - var(--#{$prefix}modal-footer-gap) * .5); - background-color: var(--#{$prefix}modal-footer-bg); - border-block-start: var(--#{$prefix}modal-footer-border-width) solid var(--#{$prefix}modal-footer-border-color); - @include border-bottom-radius(var(--#{$prefix}modal-inner-border-radius)); - - // Place margin between footer elements - // This solution is far from ideal because of the universal selector usage, - // but is needed to fix https://github.com/twbs/bootstrap/issues/24800 - > * { - margin: calc(var(--#{$prefix}modal-footer-gap) * .5); // Todo in v6: replace with gap on parent class - } - } - - // Scale up the modal - @include media-breakpoint-up(sm) { - .modal { - --#{$prefix}modal-margin: #{$modal-dialog-margin-y-sm-up}; - --#{$prefix}modal-box-shadow: #{$modal-content-box-shadow-sm-up}; - } - - // Automatically set modal's width for larger viewports - .modal-dialog { - max-width: var(--#{$prefix}modal-width); - margin-inline: auto; - } - - .modal-sm { - --#{$prefix}modal-width: #{$modal-sm}; - } - } - - @include media-breakpoint-up(lg) { - .modal-lg, - .modal-xl { - --#{$prefix}modal-width: #{$modal-lg}; - } - } - - @include media-breakpoint-up(xl) { - .modal-xl { - --#{$prefix}modal-width: #{$modal-xl}; - } - } - - // scss-docs-start modal-fullscreen-loop - @each $breakpoint in map.keys($grid-breakpoints) { - $infix: breakpoint-infix($breakpoint, $grid-breakpoints); - $postfix: if($infix != "", $infix + "-down", ""); - - @include media-breakpoint-down($breakpoint) { - .modal-fullscreen#{$postfix} { - width: 100vw; - max-width: none; - height: 100%; - margin: 0; - - .modal-content { - height: 100%; - border: 0; - @include border-radius(0); - } - - .modal-header, - .modal-footer { - @include border-radius(0); - } - - .modal-body { - overflow-y: auto; - } - } - } - } - // scss-docs-end modal-fullscreen-loop -} diff --git a/scss/_variables.scss b/scss/_variables.scss index 6d742f672a..44c7693b77 100644 --- a/scss/_variables.scss +++ b/scss/_variables.scss @@ -7,7 +7,7 @@ // Variables // // Variables should follow the `$component-state-property-size` formula for -// consistent naming. Ex: $nav-link-disabled-color and $modal-content-box-shadow-xs. +// consistent naming. Ex: $nav-link-disabled-color and $btn-padding-y-lg. // scss-docs-start theme-color-variables // $primary: $blue-500 !default; @@ -454,67 +454,22 @@ $zindex-levels: ( // scss-docs-end zindex-levels-map -// Modals - -// scss-docs-start modal-variables -$modal-inner-padding: $spacer !default; - -$modal-footer-margin-between: .5rem !default; - -$modal-dialog-margin: .5rem !default; -$modal-dialog-margin-y-sm-up: 1.75rem !default; - -$modal-title-line-height: $line-height-base !default; - -$modal-content-color: var(--#{$prefix}color-body) !default; -$modal-content-bg: var(--#{$prefix}bg-body) !default; -$modal-content-border-color: var(--#{$prefix}border-color-translucent) !default; -$modal-content-border-width: var(--#{$prefix}border-width) !default; -$modal-content-border-radius: var(--#{$prefix}border-radius-lg) !default; -$modal-content-inner-border-radius: calc(#{$modal-content-border-radius} - #{$modal-content-border-width}) !default; -$modal-content-box-shadow-xs: var(--#{$prefix}box-shadow-sm) !default; -$modal-content-box-shadow-sm-up: var(--#{$prefix}box-shadow) !default; - -$modal-backdrop-bg: $black !default; -$modal-backdrop-opacity: .5 !default; - -$modal-header-border-color: var(--#{$prefix}border-color) !default; -$modal-header-border-width: $modal-content-border-width !default; -$modal-header-padding-y: $modal-inner-padding !default; -$modal-header-padding-x: $modal-inner-padding !default; -$modal-header-padding: $modal-header-padding-y $modal-header-padding-x !default; // Keep this for backwards compatibility - -$modal-footer-bg: null !default; -$modal-footer-border-color: $modal-header-border-color !default; -$modal-footer-border-width: $modal-header-border-width !default; - -$modal-sm: 300px !default; -$modal-md: 500px !default; -$modal-lg: 800px !default; -$modal-xl: 1200px !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; -// scss-docs-end modal-variables - // Offcanvas // scss-docs-start offcanvas-variables -$offcanvas-padding-y: $modal-inner-padding !default; -$offcanvas-padding-x: $modal-inner-padding !default; +$offcanvas-padding-y: $spacer !default; +$offcanvas-padding-x: $spacer !default; $offcanvas-horizontal-width: 400px !default; $offcanvas-vertical-height: 30vh !default; $offcanvas-transition-duration: .3s !default; -$offcanvas-border-color: $modal-content-border-color !default; -$offcanvas-border-width: $modal-content-border-width !default; -$offcanvas-title-line-height: $modal-title-line-height !default; +$offcanvas-border-color: var(--#{$prefix}border-color-translucent) !default; +$offcanvas-border-width: var(--#{$prefix}border-width) !default; +$offcanvas-title-line-height: $line-height-base !default; $offcanvas-bg-color: var(--#{$prefix}bg-body) !default; $offcanvas-color: var(--#{$prefix}color-body) !default; -$offcanvas-box-shadow: $modal-content-box-shadow-xs !default; -$offcanvas-backdrop-bg: $modal-backdrop-bg !default; -$offcanvas-backdrop-opacity: $modal-backdrop-opacity !default; +$offcanvas-box-shadow: var(--#{$prefix}box-shadow-sm) !default; +$offcanvas-backdrop-bg: $black !default; +$offcanvas-backdrop-opacity: .5 !default; // scss-docs-end offcanvas-variables // Code diff --git a/scss/bootstrap.scss b/scss/bootstrap.scss index f849163feb..52fc04d4b1 100644 --- a/scss/bootstrap.scss +++ b/scss/bootstrap.scss @@ -20,7 +20,6 @@ @forward "dialog"; @forward "dropdown"; @forward "list-group"; -@forward "modal"; @forward "nav"; @forward "navbar"; @forward "offcanvas"; diff --git a/site/data/sidebar.yml b/site/data/sidebar.yml index bfa5c522a5..2cbfc90999 100644 --- a/site/data/sidebar.yml +++ b/site/data/sidebar.yml @@ -95,7 +95,6 @@ - title: Dialog - title: Dropdowns - title: List group - - title: Modal - title: Navbar - title: Navs & tabs - title: Offcanvas diff --git a/site/src/assets/examples/cheatsheet-rtl/index.astro b/site/src/assets/examples/cheatsheet-rtl/index.astro index 66a2ce8ef7..eb494e1572 100644 --- a/site/src/assets/examples/cheatsheet-rtl/index.astro +++ b/site/src/assets/examples/cheatsheet-rtl/index.astro @@ -57,7 +57,6 @@ import Placeholder from "@shortcodes/Placeholder.astro"
  • شرائح العرض
  • القوائم المنسدلة
  • مجموعة العناصر
  • -
  • الصندوق العائم
  • التنقل
  • شريط التنقل
  • ترقيم الصفحات
  • @@ -1118,31 +1117,6 @@ import Placeholder from "@shortcodes/Placeholder.astro" `]} /> - - - - - - diff --git a/site/src/assets/examples/cheatsheet/index.astro b/site/src/assets/examples/cheatsheet/index.astro index 08b18bb6a9..d5e5c6ad34 100644 --- a/site/src/assets/examples/cheatsheet/index.astro +++ b/site/src/assets/examples/cheatsheet/index.astro @@ -54,9 +54,9 @@ export const body_class = 'bg-body-tertiary'
  • Button group
  • Card
  • Carousel
  • +
  • Dialog
  • Dropdowns
  • List group
  • -
  • Modal
  • Navs
  • Navbar
  • Pagination
  • @@ -879,6 +879,23 @@ export const body_class = 'bg-body-tertiary' `} /> +
    +
    +

    Dialog

    + Documentation +
    + +
    + + Launch demo dialog + + + `} /> +
    +
    -