From 64e13162faa692aa2d12071ad9a14a3ac1b08a6f Mon Sep 17 00:00:00 2001 From: XhmikosR Date: Thu, 7 Oct 2021 17:48:36 +0300 Subject: Sanitizer: fix logic and add a test. (#35133) This was broken in 2596c97 inadvertently. Added a test so that we don't hit this in the future. --- js/src/util/sanitizer.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'js/src') diff --git a/js/src/util/sanitizer.js b/js/src/util/sanitizer.js index f5a8287cd..232416f3a 100644 --- a/js/src/util/sanitizer.js +++ b/js/src/util/sanitizer.js @@ -45,7 +45,7 @@ const allowedAttribute = (attribute, allowedAttributeList) => { // Check if a regular expression validates the attribute. return allowedAttributeList.filter(attributeRegex => attributeRegex instanceof RegExp) - .every(regex => regex.test(attributeName)) + .some(regex => regex.test(attributeName)) } export const DefaultAllowlist = { -- cgit v1.2.3 From eb0f705621a044d25d6de0e5d1b4dde306b5004d Mon Sep 17 00:00:00 2001 From: XhmikosR Date: Fri, 8 Oct 2021 12:28:05 +0300 Subject: scrollspy.js: chain functions (#35139) --- js/src/scrollspy.js | 35 ++++++++++++++++++----------------- 1 file changed, 18 insertions(+), 17 deletions(-) (limited to 'js/src') diff --git a/js/src/scrollspy.js b/js/src/scrollspy.js index df9a14e22..e0e2e9bf9 100644 --- a/js/src/scrollspy.js +++ b/js/src/scrollspy.js @@ -110,25 +110,26 @@ class ScrollSpy extends BaseComponent { this._scrollHeight = this._getScrollHeight() const targets = SelectorEngine.find(SELECTOR_LINK_ITEMS, this._config.target) - - for (const item of targets.map(element => { - const targetSelector = getSelectorFromElement(element) - const target = targetSelector ? SelectorEngine.findOne(targetSelector) : null - - if (target) { - const targetBCR = target.getBoundingClientRect() - if (targetBCR.width || targetBCR.height) { - return [ - Manipulator[offsetMethod](target).top + offsetBase, - targetSelector - ] + .map(element => { + const targetSelector = getSelectorFromElement(element) + const target = targetSelector ? SelectorEngine.findOne(targetSelector) : null + + if (target) { + const targetBCR = target.getBoundingClientRect() + if (targetBCR.width || targetBCR.height) { + return [ + Manipulator[offsetMethod](target).top + offsetBase, + targetSelector + ] + } } - } - return null - }) - .filter(item => item) - .sort((a, b) => a[0] - b[0])) { + return null + }) + .filter(item => item) + .sort((a, b) => a[0] - b[0]) + + for (const item of targets) { this._offsets.push(item[0]) this._targets.push(item[1]) } -- cgit v1.2.3 From 1a6fdfae6be09b09eaced8f0e442ca6f7680a61e Mon Sep 17 00:00:00 2001 From: XhmikosR Date: Sat, 9 Oct 2021 09:33:12 +0300 Subject: Bump version to 5.1.3. --- js/src/alert.js | 2 +- js/src/base-component.js | 4 ++-- js/src/button.js | 2 +- js/src/carousel.js | 2 +- js/src/collapse.js | 2 +- js/src/dom/data.js | 2 +- js/src/dom/event-handler.js | 2 +- js/src/dom/manipulator.js | 2 +- js/src/dom/selector-engine.js | 2 +- js/src/dropdown.js | 2 +- js/src/modal.js | 2 +- js/src/offcanvas.js | 2 +- js/src/popover.js | 2 +- js/src/scrollspy.js | 2 +- js/src/tab.js | 2 +- js/src/toast.js | 2 +- js/src/tooltip.js | 2 +- js/src/util/backdrop.js | 2 +- js/src/util/component-functions.js | 2 +- js/src/util/focustrap.js | 2 +- js/src/util/index.js | 2 +- js/src/util/sanitizer.js | 2 +- js/src/util/scrollbar.js | 2 +- 23 files changed, 24 insertions(+), 24 deletions(-) (limited to 'js/src') diff --git a/js/src/alert.js b/js/src/alert.js index 9b4cba4cf..192bea89f 100644 --- a/js/src/alert.js +++ b/js/src/alert.js @@ -1,6 +1,6 @@ /** * -------------------------------------------------------------------------- - * Bootstrap (v5.1.2): alert.js + * Bootstrap (v5.1.3): alert.js * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) * -------------------------------------------------------------------------- */ diff --git a/js/src/base-component.js b/js/src/base-component.js index cc6abd121..28cf877fb 100644 --- a/js/src/base-component.js +++ b/js/src/base-component.js @@ -1,6 +1,6 @@ /** * -------------------------------------------------------------------------- - * Bootstrap (v5.1.2): base-component.js + * Bootstrap (v5.1.3): base-component.js * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) * -------------------------------------------------------------------------- */ @@ -18,7 +18,7 @@ import EventHandler from './dom/event-handler' * ------------------------------------------------------------------------ */ -const VERSION = '5.1.2' +const VERSION = '5.1.3' class BaseComponent { constructor(element) { diff --git a/js/src/button.js b/js/src/button.js index c4e7c296d..82d1b87db 100644 --- a/js/src/button.js +++ b/js/src/button.js @@ -1,6 +1,6 @@ /** * -------------------------------------------------------------------------- - * Bootstrap (v5.1.2): button.js + * Bootstrap (v5.1.3): button.js * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) * -------------------------------------------------------------------------- */ diff --git a/js/src/carousel.js b/js/src/carousel.js index 96812d3b6..2cb71dcaa 100644 --- a/js/src/carousel.js +++ b/js/src/carousel.js @@ -1,6 +1,6 @@ /** * -------------------------------------------------------------------------- - * Bootstrap (v5.1.2): carousel.js + * Bootstrap (v5.1.3): carousel.js * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) * -------------------------------------------------------------------------- */ diff --git a/js/src/collapse.js b/js/src/collapse.js index 4ed0dadd6..33d5674eb 100644 --- a/js/src/collapse.js +++ b/js/src/collapse.js @@ -1,6 +1,6 @@ /** * -------------------------------------------------------------------------- - * Bootstrap (v5.1.2): collapse.js + * Bootstrap (v5.1.3): collapse.js * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) * -------------------------------------------------------------------------- */ diff --git a/js/src/dom/data.js b/js/src/dom/data.js index 5dbb87754..c702fc82c 100644 --- a/js/src/dom/data.js +++ b/js/src/dom/data.js @@ -1,6 +1,6 @@ /** * -------------------------------------------------------------------------- - * Bootstrap (v5.1.2): dom/data.js + * Bootstrap (v5.1.3): dom/data.js * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) * -------------------------------------------------------------------------- */ diff --git a/js/src/dom/event-handler.js b/js/src/dom/event-handler.js index 47f610493..e085ea138 100644 --- a/js/src/dom/event-handler.js +++ b/js/src/dom/event-handler.js @@ -1,6 +1,6 @@ /** * -------------------------------------------------------------------------- - * Bootstrap (v5.1.2): dom/event-handler.js + * Bootstrap (v5.1.3): dom/event-handler.js * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) * -------------------------------------------------------------------------- */ diff --git a/js/src/dom/manipulator.js b/js/src/dom/manipulator.js index 11c4e9d39..04982d63e 100644 --- a/js/src/dom/manipulator.js +++ b/js/src/dom/manipulator.js @@ -1,6 +1,6 @@ /** * -------------------------------------------------------------------------- - * Bootstrap (v5.1.2): dom/manipulator.js + * Bootstrap (v5.1.3): dom/manipulator.js * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) * -------------------------------------------------------------------------- */ diff --git a/js/src/dom/selector-engine.js b/js/src/dom/selector-engine.js index 9fc0e4b0e..54f270f4c 100644 --- a/js/src/dom/selector-engine.js +++ b/js/src/dom/selector-engine.js @@ -1,6 +1,6 @@ /** * -------------------------------------------------------------------------- - * Bootstrap (v5.1.2): dom/selector-engine.js + * Bootstrap (v5.1.3): dom/selector-engine.js * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) * -------------------------------------------------------------------------- */ diff --git a/js/src/dropdown.js b/js/src/dropdown.js index f241be699..6fafdb116 100644 --- a/js/src/dropdown.js +++ b/js/src/dropdown.js @@ -1,6 +1,6 @@ /** * -------------------------------------------------------------------------- - * Bootstrap (v5.1.2): dropdown.js + * Bootstrap (v5.1.3): dropdown.js * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) * -------------------------------------------------------------------------- */ diff --git a/js/src/modal.js b/js/src/modal.js index ec67f1fa1..00df5c482 100644 --- a/js/src/modal.js +++ b/js/src/modal.js @@ -1,6 +1,6 @@ /** * -------------------------------------------------------------------------- - * Bootstrap (v5.1.2): modal.js + * Bootstrap (v5.1.3): modal.js * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) * -------------------------------------------------------------------------- */ diff --git a/js/src/offcanvas.js b/js/src/offcanvas.js index 4fb2b2d9b..66378fd23 100644 --- a/js/src/offcanvas.js +++ b/js/src/offcanvas.js @@ -1,6 +1,6 @@ /** * -------------------------------------------------------------------------- - * Bootstrap (v5.1.2): offcanvas.js + * Bootstrap (v5.1.3): offcanvas.js * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) * -------------------------------------------------------------------------- */ diff --git a/js/src/popover.js b/js/src/popover.js index d499bedf1..aa9b0bc9e 100644 --- a/js/src/popover.js +++ b/js/src/popover.js @@ -1,6 +1,6 @@ /** * -------------------------------------------------------------------------- - * Bootstrap (v5.1.2): popover.js + * Bootstrap (v5.1.3): popover.js * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) * -------------------------------------------------------------------------- */ diff --git a/js/src/scrollspy.js b/js/src/scrollspy.js index 900d245c6..825b07fbc 100644 --- a/js/src/scrollspy.js +++ b/js/src/scrollspy.js @@ -1,6 +1,6 @@ /** * -------------------------------------------------------------------------- - * Bootstrap (v5.1.2): scrollspy.js + * Bootstrap (v5.1.3): scrollspy.js * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) * -------------------------------------------------------------------------- */ diff --git a/js/src/tab.js b/js/src/tab.js index 581162c50..139a16cb4 100644 --- a/js/src/tab.js +++ b/js/src/tab.js @@ -1,6 +1,6 @@ /** * -------------------------------------------------------------------------- - * Bootstrap (v5.1.2): tab.js + * Bootstrap (v5.1.3): tab.js * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) * -------------------------------------------------------------------------- */ diff --git a/js/src/toast.js b/js/src/toast.js index 0faecb8e4..780279be9 100644 --- a/js/src/toast.js +++ b/js/src/toast.js @@ -1,6 +1,6 @@ /** * -------------------------------------------------------------------------- - * Bootstrap (v5.1.2): toast.js + * Bootstrap (v5.1.3): toast.js * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) * -------------------------------------------------------------------------- */ diff --git a/js/src/tooltip.js b/js/src/tooltip.js index a26b8ada6..d8bb31a76 100644 --- a/js/src/tooltip.js +++ b/js/src/tooltip.js @@ -1,6 +1,6 @@ /** * -------------------------------------------------------------------------- - * Bootstrap (v5.1.2): tooltip.js + * Bootstrap (v5.1.3): tooltip.js * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) * -------------------------------------------------------------------------- */ diff --git a/js/src/util/backdrop.js b/js/src/util/backdrop.js index e5ca0c860..04c763518 100644 --- a/js/src/util/backdrop.js +++ b/js/src/util/backdrop.js @@ -1,6 +1,6 @@ /** * -------------------------------------------------------------------------- - * Bootstrap (v5.1.2): util/backdrop.js + * Bootstrap (v5.1.3): util/backdrop.js * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) * -------------------------------------------------------------------------- */ diff --git a/js/src/util/component-functions.js b/js/src/util/component-functions.js index c678ecadf..bd44c3fdc 100644 --- a/js/src/util/component-functions.js +++ b/js/src/util/component-functions.js @@ -1,6 +1,6 @@ /** * -------------------------------------------------------------------------- - * Bootstrap (v5.1.2): util/component-functions.js + * Bootstrap (v5.1.3): util/component-functions.js * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) * -------------------------------------------------------------------------- */ diff --git a/js/src/util/focustrap.js b/js/src/util/focustrap.js index 500a5b3bc..44d5f47eb 100644 --- a/js/src/util/focustrap.js +++ b/js/src/util/focustrap.js @@ -1,6 +1,6 @@ /** * -------------------------------------------------------------------------- - * Bootstrap (v5.1.2): util/focustrap.js + * Bootstrap (v5.1.3): util/focustrap.js * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) * -------------------------------------------------------------------------- */ diff --git a/js/src/util/index.js b/js/src/util/index.js index 7e9e9b046..d05a3cbd7 100644 --- a/js/src/util/index.js +++ b/js/src/util/index.js @@ -1,6 +1,6 @@ /** * -------------------------------------------------------------------------- - * Bootstrap (v5.1.2): util/index.js + * Bootstrap (v5.1.3): util/index.js * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) * -------------------------------------------------------------------------- */ diff --git a/js/src/util/sanitizer.js b/js/src/util/sanitizer.js index 2a0597be7..339c916c6 100644 --- a/js/src/util/sanitizer.js +++ b/js/src/util/sanitizer.js @@ -1,6 +1,6 @@ /** * -------------------------------------------------------------------------- - * Bootstrap (v5.1.2): util/sanitizer.js + * Bootstrap (v5.1.3): util/sanitizer.js * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) * -------------------------------------------------------------------------- */ diff --git a/js/src/util/scrollbar.js b/js/src/util/scrollbar.js index 2d5d0ffa6..a90f21a79 100644 --- a/js/src/util/scrollbar.js +++ b/js/src/util/scrollbar.js @@ -1,6 +1,6 @@ /** * -------------------------------------------------------------------------- - * Bootstrap (v5.1.2): util/scrollBar.js + * Bootstrap (v5.1.3): util/scrollBar.js * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) * -------------------------------------------------------------------------- */ -- cgit v1.2.3 From 24e3ca2474a51f996e9166c065da80b068e8e599 Mon Sep 17 00:00:00 2001 From: XhmikosR Date: Sun, 10 Oct 2021 14:49:41 +0300 Subject: tooltip.js: ignore a LGTM error (#35147) The code on this line is either sanitized or the user chose to not sanitize it. --- js/src/tooltip.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'js/src') diff --git a/js/src/tooltip.js b/js/src/tooltip.js index 423a192c0..7a8986548 100644 --- a/js/src/tooltip.js +++ b/js/src/tooltip.js @@ -421,7 +421,7 @@ class Tooltip extends BaseComponent { content = sanitizeHtml(content, this._config.allowList, this._config.sanitizeFn) } - element.innerHTML = content + element.innerHTML = content // lgtm [js/xss-through-dom] } else { element.textContent = content } -- cgit v1.2.3 From 8ec6c9452286472ddad12d1af59b173ede22b5ac Mon Sep 17 00:00:00 2001 From: GeoSot Date: Mon, 11 Oct 2021 17:04:43 +0300 Subject: Extract Carousel's swipe functionality to a separate Class (#32999) --- js/src/carousel.js | 91 +++++++++----------------------------- js/src/util/swipe.js | 122 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 142 insertions(+), 71 deletions(-) create mode 100644 js/src/util/swipe.js (limited to 'js/src') diff --git a/js/src/carousel.js b/js/src/carousel.js index 161e980c6..f28ee259b 100644 --- a/js/src/carousel.js +++ b/js/src/carousel.js @@ -8,9 +8,9 @@ import { defineJQueryPlugin, getElementFromSelector, + getNextActiveElement, isRTL, isVisible, - getNextActiveElement, reflow, triggerTransitionEnd, typeCheckConfig @@ -18,6 +18,7 @@ import { import EventHandler from './dom/event-handler' import Manipulator from './dom/manipulator' import SelectorEngine from './dom/selector-engine' +import Swipe from './util/swipe' import BaseComponent from './base-component' /** @@ -34,7 +35,6 @@ const DATA_API_KEY = '.data-api' const ARROW_LEFT_KEY = 'ArrowLeft' const ARROW_RIGHT_KEY = 'ArrowRight' const TOUCHEVENT_COMPAT_WAIT = 500 // Time for mouse compat events to fire after touch -const SWIPE_THRESHOLD = 40 const Default = { interval: 5000, @@ -69,11 +69,6 @@ const EVENT_SLID = `slid${EVENT_KEY}` const EVENT_KEYDOWN = `keydown${EVENT_KEY}` const EVENT_MOUSEENTER = `mouseenter${EVENT_KEY}` const EVENT_MOUSELEAVE = `mouseleave${EVENT_KEY}` -const EVENT_TOUCHSTART = `touchstart${EVENT_KEY}` -const EVENT_TOUCHMOVE = `touchmove${EVENT_KEY}` -const EVENT_TOUCHEND = `touchend${EVENT_KEY}` -const EVENT_POINTERDOWN = `pointerdown${EVENT_KEY}` -const EVENT_POINTERUP = `pointerup${EVENT_KEY}` const EVENT_DRAG_START = `dragstart${EVENT_KEY}` const EVENT_LOAD_DATA_API = `load${EVENT_KEY}${DATA_API_KEY}` const EVENT_CLICK_DATA_API = `click${EVENT_KEY}${DATA_API_KEY}` @@ -85,7 +80,6 @@ const CLASS_NAME_END = 'carousel-item-end' const CLASS_NAME_START = 'carousel-item-start' const CLASS_NAME_NEXT = 'carousel-item-next' const CLASS_NAME_PREV = 'carousel-item-prev' -const CLASS_NAME_POINTER_EVENT = 'pointer-event' const SELECTOR_ACTIVE = '.active' const SELECTOR_ACTIVE_ITEM = '.active.carousel-item' @@ -97,9 +91,6 @@ const SELECTOR_INDICATOR = '[data-bs-target]' const SELECTOR_DATA_SLIDE = '[data-bs-slide], [data-bs-slide-to]' const SELECTOR_DATA_RIDE = '[data-bs-ride="carousel"]' -const POINTER_TYPE_TOUCH = 'touch' -const POINTER_TYPE_PEN = 'pen' - /** * ------------------------------------------------------------------------ * Class Definition @@ -115,14 +106,10 @@ class Carousel extends BaseComponent { this._isPaused = false this._isSliding = false this.touchTimeout = null - this.touchStartX = 0 - this.touchDeltaX = 0 + this._swipeHelper = null this._config = this._getConfig(config) this._indicatorsElement = SelectorEngine.findOne(SELECTOR_INDICATORS, this._element) - this._touchSupported = 'ontouchstart' in document.documentElement || navigator.maxTouchPoints > 0 - this._pointerEvent = Boolean(window.PointerEvent) - this._addEventListeners() } @@ -214,6 +201,14 @@ class Carousel extends BaseComponent { this._slide(order, this._items[index]) } + dispose() { + if (this._swipeHelper) { + this._swipeHelper.dispose() + } + + super.dispose() + } + // Private _getConfig(config) { @@ -226,24 +221,6 @@ class Carousel extends BaseComponent { return config } - _handleSwipe() { - const absDeltax = Math.abs(this.touchDeltaX) - - if (absDeltax <= SWIPE_THRESHOLD) { - return - } - - const direction = absDeltax / this.touchDeltaX - - this.touchDeltaX = 0 - - if (!direction) { - return - } - - this._slide(direction > 0 ? DIRECTION_RIGHT : DIRECTION_LEFT) - } - _addEventListeners() { if (this._config.keyboard) { EventHandler.on(this._element, EVENT_KEYDOWN, event => this._keydown(event)) @@ -254,38 +231,17 @@ class Carousel extends BaseComponent { EventHandler.on(this._element, EVENT_MOUSELEAVE, event => this.cycle(event)) } - if (this._config.touch && this._touchSupported) { + if (this._config.touch && Swipe.isSupported()) { this._addTouchEventListeners() } } _addTouchEventListeners() { - const hasPointerPenTouch = event => { - return this._pointerEvent && - (event.pointerType === POINTER_TYPE_PEN || event.pointerType === POINTER_TYPE_TOUCH) - } - - const start = event => { - if (hasPointerPenTouch(event)) { - this.touchStartX = event.clientX - } else if (!this._pointerEvent) { - this.touchStartX = event.touches[0].clientX - } - } - - const move = event => { - // ensure swiping with one touch and not pinching - this.touchDeltaX = event.touches && event.touches.length > 1 ? - 0 : - event.touches[0].clientX - this.touchStartX + for (const itemImg of SelectorEngine.find(SELECTOR_ITEM_IMG, this._element)) { + EventHandler.on(itemImg, EVENT_DRAG_START, event => event.preventDefault()) } - const end = event => { - if (hasPointerPenTouch(event)) { - this.touchDeltaX = event.clientX - this.touchStartX - } - - this._handleSwipe() + const endCallBack = () => { if (this._config.pause === 'hover') { // If it's a touch-enabled device, mouseenter/leave are fired as // part of the mouse compatibility events on first tap - the carousel @@ -304,20 +260,13 @@ class Carousel extends BaseComponent { } } - for (const itemImg of SelectorEngine.find(SELECTOR_ITEM_IMG, this._element)) { - EventHandler.on(itemImg, EVENT_DRAG_START, event => event.preventDefault()) + const swipeConfig = { + leftCallback: () => this._slide(DIRECTION_LEFT), + rightCallback: () => this._slide(DIRECTION_RIGHT), + endCallback: endCallBack } - if (this._pointerEvent) { - EventHandler.on(this._element, EVENT_POINTERDOWN, event => start(event)) - EventHandler.on(this._element, EVENT_POINTERUP, event => end(event)) - - this._element.classList.add(CLASS_NAME_POINTER_EVENT) - } else { - EventHandler.on(this._element, EVENT_TOUCHSTART, event => start(event)) - EventHandler.on(this._element, EVENT_TOUCHMOVE, event => move(event)) - EventHandler.on(this._element, EVENT_TOUCHEND, event => end(event)) - } + this._swipeHelper = new Swipe(this._element, swipeConfig) } _keydown(event) { diff --git a/js/src/util/swipe.js b/js/src/util/swipe.js new file mode 100644 index 000000000..321572eb8 --- /dev/null +++ b/js/src/util/swipe.js @@ -0,0 +1,122 @@ +import EventHandler from '../dom/event-handler' +import { execute, typeCheckConfig } from './index' + +const EVENT_KEY = '.bs.swipe' +const EVENT_TOUCHSTART = `touchstart${EVENT_KEY}` +const EVENT_TOUCHMOVE = `touchmove${EVENT_KEY}` +const EVENT_TOUCHEND = `touchend${EVENT_KEY}` +const EVENT_POINTERDOWN = `pointerdown${EVENT_KEY}` +const EVENT_POINTERUP = `pointerup${EVENT_KEY}` +const POINTER_TYPE_TOUCH = 'touch' +const POINTER_TYPE_PEN = 'pen' +const CLASS_NAME_POINTER_EVENT = 'pointer-event' +const SWIPE_THRESHOLD = 40 +const NAME = 'swipe' + +const Default = { + leftCallback: null, + rightCallback: null, + endCallback: null +} + +const DefaultType = { + leftCallback: '(function|null)', + rightCallback: '(function|null)', + endCallback: '(function|null)' +} + +class Swipe { + constructor(element, config) { + this._element = element + + if (!element || !Swipe.isSupported()) { + return + } + + this._config = this._getConfig(config) + this._deltaX = 0 + this._supportPointerEvents = Boolean(window.PointerEvent) + this._initEvents() + } + + dispose() { + EventHandler.off(this._element, EVENT_KEY) + } + + _start(event) { + if (!this._supportPointerEvents) { + this._deltaX = event.touches[0].clientX + + return + } + + if (this._eventIsPointerPenTouch(event)) { + this._deltaX = event.clientX + } + } + + _end(event) { + if (this._eventIsPointerPenTouch(event)) { + this._deltaX = event.clientX - this._deltaX + } + + this._handleSwipe() + execute(this._config.endCallback) + } + + _move(event) { + this._deltaX = event.touches && event.touches.length > 1 ? + 0 : + event.touches[0].clientX - this._deltaX + } + + _handleSwipe() { + const absDeltaX = Math.abs(this._deltaX) + + if (absDeltaX <= SWIPE_THRESHOLD) { + return + } + + const direction = absDeltaX / this._deltaX + + this._deltaX = 0 + + if (!direction) { + return + } + + execute(direction > 0 ? this._config.rightCallback : this._config.leftCallback) + } + + _initEvents() { + if (this._supportPointerEvents) { + EventHandler.on(this._element, EVENT_POINTERDOWN, event => this._start(event)) + EventHandler.on(this._element, EVENT_POINTERUP, event => this._end(event)) + + this._element.classList.add(CLASS_NAME_POINTER_EVENT) + } else { + EventHandler.on(this._element, EVENT_TOUCHSTART, event => this._start(event)) + EventHandler.on(this._element, EVENT_TOUCHMOVE, event => this._move(event)) + EventHandler.on(this._element, EVENT_TOUCHEND, event => this._end(event)) + } + } + + _getConfig(config) { + config = { + ...Default, + ...(typeof config === 'object' ? config : {}) + } + typeCheckConfig(NAME, config, DefaultType) + return config + } + + _eventIsPointerPenTouch(event) { + return this._supportPointerEvents && (event.pointerType === POINTER_TYPE_PEN || event.pointerType === POINTER_TYPE_TOUCH) + } + + static isSupported() { + return 'ontouchstart' in document.documentElement || navigator.maxTouchPoints > 0 + } +} + +export default Swipe -- cgit v1.2.3 From db44392bda22f3d5319d2880c992f76d27d2a60c Mon Sep 17 00:00:00 2001 From: GeoSot Date: Tue, 12 Oct 2021 15:48:19 +0300 Subject: Swipe: add test to ensure that it ignores `pinch` events (#35161) --- js/src/util/swipe.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'js/src') diff --git a/js/src/util/swipe.js b/js/src/util/swipe.js index 321572eb8..a78f598d9 100644 --- a/js/src/util/swipe.js +++ b/js/src/util/swipe.js @@ -1,6 +1,7 @@ import EventHandler from '../dom/event-handler' import { execute, typeCheckConfig } from './index' +const NAME = 'swipe' const EVENT_KEY = '.bs.swipe' const EVENT_TOUCHSTART = `touchstart${EVENT_KEY}` const EVENT_TOUCHMOVE = `touchmove${EVENT_KEY}` @@ -11,7 +12,6 @@ const POINTER_TYPE_TOUCH = 'touch' const POINTER_TYPE_PEN = 'pen' const CLASS_NAME_POINTER_EVENT = 'pointer-event' const SWIPE_THRESHOLD = 40 -const NAME = 'swipe' const Default = { leftCallback: null, -- cgit v1.2.3 From e8f702666f285a3e69866ed1f8d29fa6eaaaeabb Mon Sep 17 00:00:00 2001 From: XhmikosR Date: Wed, 13 Oct 2021 15:19:28 +0300 Subject: JS: minor refactoring (#35183) * add missing comments * shorten block comments * reorder constants * reorder public/private methods * sort exports alphabetically in util/index.js * fix a couple of typos --- js/src/alert.js | 16 +------- js/src/base-component.js | 12 +++--- js/src/button.js | 18 +-------- js/src/carousel.js | 64 +++++++++++++------------------ js/src/collapse.js | 37 ++++++------------ js/src/dom/data.js | 2 - js/src/dom/event-handler.js | 12 +----- js/src/dom/selector-engine.js | 10 ++--- js/src/dropdown.js | 18 +-------- js/src/modal.js | 47 +++++++++-------------- js/src/offcanvas.js | 47 +++++++++-------------- js/src/popover.js | 19 ++-------- js/src/scrollspy.js | 41 +++++++------------- js/src/tab.js | 29 +++----------- js/src/toast.js | 17 +++------ js/src/tooltip.js | 88 +++++++++++++++++++------------------------ js/src/util/backdrop.js | 40 ++++++++++++-------- js/src/util/focustrap.js | 28 +++++++++----- js/src/util/index.js | 36 +++++++++--------- js/src/util/scrollbar.js | 32 ++++++++++------ js/src/util/swipe.js | 18 +++++++++ 21 files changed, 254 insertions(+), 377 deletions(-) (limited to 'js/src') diff --git a/js/src/alert.js b/js/src/alert.js index 192bea89f..7d4b555ea 100644 --- a/js/src/alert.js +++ b/js/src/alert.js @@ -11,9 +11,7 @@ import BaseComponent from './base-component' import { enableDismissTrigger } from './util/component-functions' /** - * ------------------------------------------------------------------------ * Constants - * ------------------------------------------------------------------------ */ const NAME = 'alert' @@ -26,20 +24,16 @@ const CLASS_NAME_FADE = 'fade' const CLASS_NAME_SHOW = 'show' /** - * ------------------------------------------------------------------------ - * Class Definition - * ------------------------------------------------------------------------ + * Class definition */ class Alert extends BaseComponent { // Getters - static get NAME() { return NAME } // Public - close() { const closeEvent = EventHandler.trigger(this._element, EVENT_CLOSE) @@ -61,7 +55,6 @@ class Alert extends BaseComponent { } // Static - static jQueryInterface(config) { return this.each(function () { const data = Alert.getOrCreateInstance(this) @@ -80,18 +73,13 @@ class Alert extends BaseComponent { } /** - * ------------------------------------------------------------------------ - * Data Api implementation - * ------------------------------------------------------------------------ + * Data API implementation */ enableDismissTrigger(Alert, 'close') /** - * ------------------------------------------------------------------------ * jQuery - * ------------------------------------------------------------------------ - * add .Alert to jQuery only if jQuery is present */ defineJQueryPlugin(Alert) diff --git a/js/src/base-component.js b/js/src/base-component.js index fb33d7b0c..3c5eb460a 100644 --- a/js/src/base-component.js +++ b/js/src/base-component.js @@ -13,13 +13,15 @@ import { import EventHandler from './dom/event-handler' /** - * ------------------------------------------------------------------------ * Constants - * ------------------------------------------------------------------------ */ const VERSION = '5.1.3' +/** + * Class definition + */ + class BaseComponent { constructor(element) { element = getElement(element) @@ -32,6 +34,7 @@ class BaseComponent { Data.set(this._element, this.constructor.DATA_KEY, this) } + // Public dispose() { Data.remove(this._element, this.constructor.DATA_KEY) EventHandler.off(this._element, this.constructor.EVENT_KEY) @@ -45,8 +48,7 @@ class BaseComponent { executeAfterTransition(callback, element, isAnimated) } - /** Static */ - + // Static static getInstance(element) { return Data.get(getElement(element), this.DATA_KEY) } @@ -60,7 +62,7 @@ class BaseComponent { } static get NAME() { - throw new Error('You have to implement the static method "NAME", for each component!') + throw new Error('You have to implement the static method "NAME" for each component!') } static get DATA_KEY() { diff --git a/js/src/button.js b/js/src/button.js index 82d1b87db..e2a52e7eb 100644 --- a/js/src/button.js +++ b/js/src/button.js @@ -10,9 +10,7 @@ import EventHandler from './dom/event-handler' import BaseComponent from './base-component' /** - * ------------------------------------------------------------------------ * Constants - * ------------------------------------------------------------------------ */ const NAME = 'button' @@ -21,33 +19,26 @@ const EVENT_KEY = `.${DATA_KEY}` const DATA_API_KEY = '.data-api' const CLASS_NAME_ACTIVE = 'active' - const SELECTOR_DATA_TOGGLE = '[data-bs-toggle="button"]' - const EVENT_CLICK_DATA_API = `click${EVENT_KEY}${DATA_API_KEY}` /** - * ------------------------------------------------------------------------ - * Class Definition - * ------------------------------------------------------------------------ + * Class definition */ class Button extends BaseComponent { // Getters - static get NAME() { return NAME } // Public - toggle() { // Toggle class and sync the `aria-pressed` attribute with the return value of the `.toggle()` method this._element.setAttribute('aria-pressed', this._element.classList.toggle(CLASS_NAME_ACTIVE)) } // Static - static jQueryInterface(config) { return this.each(function () { const data = Button.getOrCreateInstance(this) @@ -60,9 +51,7 @@ class Button extends BaseComponent { } /** - * ------------------------------------------------------------------------ - * Data Api implementation - * ------------------------------------------------------------------------ + * Data API implementation */ EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, event => { @@ -75,10 +64,7 @@ EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, event => { }) /** - * ------------------------------------------------------------------------ * jQuery - * ------------------------------------------------------------------------ - * add .Button to jQuery only if jQuery is present */ defineJQueryPlugin(Button) diff --git a/js/src/carousel.js b/js/src/carousel.js index f28ee259b..3589f2206 100644 --- a/js/src/carousel.js +++ b/js/src/carousel.js @@ -22,9 +22,7 @@ import Swipe from './util/swipe' import BaseComponent from './base-component' /** - * ------------------------------------------------------------------------ * Constants - * ------------------------------------------------------------------------ */ const NAME = 'carousel' @@ -36,34 +34,11 @@ const ARROW_LEFT_KEY = 'ArrowLeft' const ARROW_RIGHT_KEY = 'ArrowRight' const TOUCHEVENT_COMPAT_WAIT = 500 // Time for mouse compat events to fire after touch -const Default = { - interval: 5000, - keyboard: true, - slide: false, - pause: 'hover', - wrap: true, - touch: true -} - -const DefaultType = { - interval: '(number|boolean)', - keyboard: 'boolean', - slide: '(boolean|string)', - pause: '(string|boolean)', - wrap: 'boolean', - touch: 'boolean' -} - const ORDER_NEXT = 'next' const ORDER_PREV = 'prev' const DIRECTION_LEFT = 'left' const DIRECTION_RIGHT = 'right' -const KEY_TO_DIRECTION = { - [ARROW_LEFT_KEY]: DIRECTION_RIGHT, - [ARROW_RIGHT_KEY]: DIRECTION_LEFT -} - const EVENT_SLIDE = `slide${EVENT_KEY}` const EVENT_SLID = `slid${EVENT_KEY}` const EVENT_KEYDOWN = `keydown${EVENT_KEY}` @@ -91,11 +66,33 @@ const SELECTOR_INDICATOR = '[data-bs-target]' const SELECTOR_DATA_SLIDE = '[data-bs-slide], [data-bs-slide-to]' const SELECTOR_DATA_RIDE = '[data-bs-ride="carousel"]' +const KEY_TO_DIRECTION = { + [ARROW_LEFT_KEY]: DIRECTION_RIGHT, + [ARROW_RIGHT_KEY]: DIRECTION_LEFT +} + +const Default = { + interval: 5000, + keyboard: true, + slide: false, + pause: 'hover', + wrap: true, + touch: true +} + +const DefaultType = { + interval: '(number|boolean)', + keyboard: 'boolean', + slide: '(boolean|string)', + pause: '(string|boolean)', + wrap: 'boolean', + touch: 'boolean' +} + /** - * ------------------------------------------------------------------------ - * Class Definition - * ------------------------------------------------------------------------ + * Class definition */ + class Carousel extends BaseComponent { constructor(element, config) { super(element) @@ -114,7 +111,6 @@ class Carousel extends BaseComponent { } // Getters - static get Default() { return Default } @@ -124,7 +120,6 @@ class Carousel extends BaseComponent { } // Public - next() { this._slide(ORDER_NEXT) } @@ -210,7 +205,6 @@ class Carousel extends BaseComponent { } // Private - _getConfig(config) { config = { ...Default, @@ -451,7 +445,6 @@ class Carousel extends BaseComponent { } // Static - static carouselInterface(element, config) { const data = Carousel.getOrCreateInstance(element, config) @@ -513,9 +506,7 @@ class Carousel extends BaseComponent { } /** - * ------------------------------------------------------------------------ - * Data Api implementation - * ------------------------------------------------------------------------ + * Data API implementation */ EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_SLIDE, Carousel.dataApiClickHandler) @@ -529,10 +520,7 @@ EventHandler.on(window, EVENT_LOAD_DATA_API, () => { }) /** - * ------------------------------------------------------------------------ * jQuery - * ------------------------------------------------------------------------ - * add .Carousel to jQuery only if jQuery is present */ defineJQueryPlugin(Carousel) diff --git a/js/src/collapse.js b/js/src/collapse.js index b32ce0186..39093c7a2 100644 --- a/js/src/collapse.js +++ b/js/src/collapse.js @@ -20,9 +20,7 @@ import SelectorEngine from './dom/selector-engine' import BaseComponent from './base-component' /** - * ------------------------------------------------------------------------ * Constants - * ------------------------------------------------------------------------ */ const NAME = 'collapse' @@ -30,16 +28,6 @@ const DATA_KEY = 'bs.collapse' const EVENT_KEY = `.${DATA_KEY}` const DATA_API_KEY = '.data-api' -const Default = { - toggle: true, - parent: null -} - -const DefaultType = { - toggle: 'boolean', - parent: '(null|element)' -} - const EVENT_SHOW = `show${EVENT_KEY}` const EVENT_SHOWN = `shown${EVENT_KEY}` const EVENT_HIDE = `hide${EVENT_KEY}` @@ -59,10 +47,18 @@ const HEIGHT = 'height' const SELECTOR_ACTIVES = '.collapse.show, .collapse.collapsing' const SELECTOR_DATA_TOGGLE = '[data-bs-toggle="collapse"]' +const Default = { + toggle: true, + parent: null +} + +const DefaultType = { + toggle: 'boolean', + parent: '(null|element)' +} + /** - * ------------------------------------------------------------------------ - * Class Definition - * ------------------------------------------------------------------------ + * Class definition */ class Collapse extends BaseComponent { @@ -98,7 +94,6 @@ class Collapse extends BaseComponent { } // Getters - static get Default() { return Default } @@ -108,7 +103,6 @@ class Collapse extends BaseComponent { } // Public - toggle() { if (this._isShown()) { this.hide() @@ -230,7 +224,6 @@ class Collapse extends BaseComponent { } // Private - _getConfig(config) { config = { ...Default, @@ -281,7 +274,6 @@ class Collapse extends BaseComponent { } // Static - static jQueryInterface(config) { return this.each(function () { const _config = {} @@ -303,9 +295,7 @@ class Collapse extends BaseComponent { } /** - * ------------------------------------------------------------------------ - * Data Api implementation - * ------------------------------------------------------------------------ + * Data API implementation */ EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) { @@ -323,10 +313,7 @@ EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function ( }) /** - * ------------------------------------------------------------------------ * jQuery - * ------------------------------------------------------------------------ - * add .Collapse to jQuery only if jQuery is present */ defineJQueryPlugin(Collapse) diff --git a/js/src/dom/data.js b/js/src/dom/data.js index c702fc82c..4209f3188 100644 --- a/js/src/dom/data.js +++ b/js/src/dom/data.js @@ -6,9 +6,7 @@ */ /** - * ------------------------------------------------------------------------ * Constants - * ------------------------------------------------------------------------ */ const elementMap = new Map() diff --git a/js/src/dom/event-handler.js b/js/src/dom/event-handler.js index bf01694f4..b9ebce324 100644 --- a/js/src/dom/event-handler.js +++ b/js/src/dom/event-handler.js @@ -8,9 +8,7 @@ import { getjQuery } from '../util/index' /** - * ------------------------------------------------------------------------ * Constants - * ------------------------------------------------------------------------ */ const namespaceRegex = /[^.]*(?=\..*)\.|.*/ @@ -73,9 +71,7 @@ const nativeEvents = new Set([ ]) /** - * ------------------------------------------------------------------------ * Private methods - * ------------------------------------------------------------------------ */ function getUidEvent(element, uid) { @@ -143,7 +139,6 @@ function findHandler(events, handler, delegationSelector = null) { function normalizeParams(originalTypeEvent, handler, delegationFn) { const delegation = typeof handler === 'string' const originalHandler = delegation ? delegationFn : handler - let typeEvent = getTypeEvent(originalTypeEvent) const isNative = nativeEvents.has(typeEvent) @@ -224,7 +219,6 @@ function removeNamespacedHandlers(element, events, typeEvent, namespace) { for (const handlerKey of Object.keys(storeElementEvent)) { if (handlerKey.includes(namespace)) { const event = storeElementEvent[handlerKey] - removeHandler(element, events, typeEvent, event.originalHandler, event.delegationSelector) } } @@ -277,7 +271,6 @@ const EventHandler = { if (!inNamespace || originalTypeEvent.includes(handlerKey)) { const event = storeElementEvent[keyHandlers] - removeHandler(element, events, typeEvent, event.originalHandler, event.delegationSelector) } } @@ -312,10 +305,7 @@ const EventHandler = { evt = document.createEvent('HTMLEvents') evt.initEvent(typeEvent, bubbles, true) } else { - evt = new CustomEvent(event, { - bubbles, - cancelable: true - }) + evt = new CustomEvent(event, { bubbles, cancelable: true }) } // merge custom information in our event diff --git a/js/src/dom/selector-engine.js b/js/src/dom/selector-engine.js index 54f270f4c..af27dc379 100644 --- a/js/src/dom/selector-engine.js +++ b/js/src/dom/selector-engine.js @@ -5,14 +5,12 @@ * -------------------------------------------------------------------------- */ +import { isDisabled, isVisible } from '../util/index' + /** - * ------------------------------------------------------------------------ * Constants - * ------------------------------------------------------------------------ */ -import { isDisabled, isVisible } from '../util/index' - const NODE_TEXT = 3 const SelectorEngine = { @@ -25,13 +23,11 @@ const SelectorEngine = { }, children(element, selector) { - return [].concat(...element.children) - .filter(child => child.matches(selector)) + return [].concat(...element.children).filter(child => child.matches(selector)) }, parents(element, selector) { const parents = [] - let ancestor = element.parentNode while (ancestor && ancestor.nodeType === Node.ELEMENT_NODE && ancestor.nodeType !== NODE_TEXT) { diff --git a/js/src/dropdown.js b/js/src/dropdown.js index 360808eca..6129707e2 100644 --- a/js/src/dropdown.js +++ b/js/src/dropdown.js @@ -6,7 +6,6 @@ */ import * as Popper from '@popperjs/core' - import { defineJQueryPlugin, getElement, @@ -25,9 +24,7 @@ import SelectorEngine from './dom/selector-engine' import BaseComponent from './base-component' /** - * ------------------------------------------------------------------------ * Constants - * ------------------------------------------------------------------------ */ const NAME = 'dropdown' @@ -89,9 +86,7 @@ const DefaultType = { } /** - * ------------------------------------------------------------------------ - * Class Definition - * ------------------------------------------------------------------------ + * Class definition */ class Dropdown extends BaseComponent { @@ -105,7 +100,6 @@ class Dropdown extends BaseComponent { } // Getters - static get Default() { return Default } @@ -119,7 +113,6 @@ class Dropdown extends BaseComponent { } // Public - toggle() { return this._isShown() ? this.hide() : this.show() } @@ -193,7 +186,6 @@ class Dropdown extends BaseComponent { } // Private - _completeHide(relatedTarget) { const hideEvent = EventHandler.trigger(this._element, EVENT_HIDE, relatedTarget) if (hideEvent.defaultPrevented) { @@ -354,7 +346,6 @@ class Dropdown extends BaseComponent { } // Static - static jQueryInterface(config) { return this.each(function () { const data = Dropdown.getOrCreateInstance(this, config) @@ -474,9 +465,7 @@ class Dropdown extends BaseComponent { } /** - * ------------------------------------------------------------------------ - * Data Api implementation - * ------------------------------------------------------------------------ + * Data API implementation */ EventHandler.on(document, EVENT_KEYDOWN_DATA_API, SELECTOR_DATA_TOGGLE, Dropdown.dataApiKeydownHandler) @@ -489,10 +478,7 @@ EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function ( }) /** - * ------------------------------------------------------------------------ * jQuery - * ------------------------------------------------------------------------ - * add .Dropdown to jQuery only if jQuery is present */ defineJQueryPlugin(Dropdown) diff --git a/js/src/modal.js b/js/src/modal.js index 2efbaa4b3..bfb980236 100644 --- a/js/src/modal.js +++ b/js/src/modal.js @@ -23,9 +23,7 @@ import FocusTrap from './util/focustrap' import { enableDismissTrigger } from './util/component-functions' /** - * ------------------------------------------------------------------------ * Constants - * ------------------------------------------------------------------------ */ const NAME = 'modal' @@ -34,18 +32,6 @@ const EVENT_KEY = `.${DATA_KEY}` const DATA_API_KEY = '.data-api' const ESCAPE_KEY = 'Escape' -const Default = { - backdrop: true, - keyboard: true, - focus: true -} - -const DefaultType = { - backdrop: '(boolean|string)', - keyboard: 'boolean', - focus: 'boolean' -} - const EVENT_HIDE = `hide${EVENT_KEY}` const EVENT_HIDE_PREVENTED = `hidePrevented${EVENT_KEY}` const EVENT_HIDDEN = `hidden${EVENT_KEY}` @@ -68,10 +54,20 @@ const SELECTOR_DIALOG = '.modal-dialog' const SELECTOR_MODAL_BODY = '.modal-body' const SELECTOR_DATA_TOGGLE = '[data-bs-toggle="modal"]' +const Default = { + backdrop: true, + keyboard: true, + focus: true +} + +const DefaultType = { + backdrop: '(boolean|string)', + keyboard: 'boolean', + focus: 'boolean' +} + /** - * ------------------------------------------------------------------------ - * Class Definition - * ------------------------------------------------------------------------ + * Class definition */ class Modal extends BaseComponent { @@ -89,7 +85,6 @@ class Modal extends BaseComponent { } // Getters - static get Default() { return Default } @@ -99,7 +94,6 @@ class Modal extends BaseComponent { } // Public - toggle(relatedTarget) { return this._isShown ? this.hide() : this.show(relatedTarget) } @@ -189,7 +183,6 @@ class Modal extends BaseComponent { } // Private - _initializeBackDrop() { return new Backdrop({ isVisible: Boolean(this._config.backdrop), // 'static' option will be translated to true, and booleans will keep their value @@ -345,9 +338,9 @@ class Modal extends BaseComponent { this._element.focus() } - // ---------------------------------------------------------------------- - // the following methods are used to handle overflowing modals - // ---------------------------------------------------------------------- + /** + * The following methods are used to handle overflowing modals + */ _adjustDialog() { const isModalOverflowing = this._element.scrollHeight > document.documentElement.clientHeight @@ -369,7 +362,6 @@ class Modal extends BaseComponent { } // Static - static jQueryInterface(config, relatedTarget) { return this.each(function () { const data = Modal.getOrCreateInstance(this, config) @@ -388,9 +380,7 @@ class Modal extends BaseComponent { } /** - * ------------------------------------------------------------------------ - * Data Api implementation - * ------------------------------------------------------------------------ + * Data API implementation */ EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) { @@ -427,10 +417,7 @@ EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function ( enableDismissTrigger(Modal) /** - * ------------------------------------------------------------------------ * jQuery - * ------------------------------------------------------------------------ - * add .Modal to jQuery only if jQuery is present */ defineJQueryPlugin(Modal) diff --git a/js/src/offcanvas.js b/js/src/offcanvas.js index c501ca14d..6878b1f62 100644 --- a/js/src/offcanvas.js +++ b/js/src/offcanvas.js @@ -22,9 +22,7 @@ import FocusTrap from './util/focustrap' import { enableDismissTrigger } from './util/component-functions' /** - * ------------------------------------------------------------------------ * Constants - * ------------------------------------------------------------------------ */ const NAME = 'offcanvas' @@ -34,18 +32,6 @@ const DATA_API_KEY = '.data-api' const EVENT_LOAD_DATA_API = `load${EVENT_KEY}${DATA_API_KEY}` const ESCAPE_KEY = 'Escape' -const Default = { - backdrop: true, - keyboard: true, - scroll: false -} - -const DefaultType = { - backdrop: 'boolean', - keyboard: 'boolean', - scroll: 'boolean' -} - const CLASS_NAME_SHOW = 'show' const CLASS_NAME_BACKDROP = 'offcanvas-backdrop' const OPEN_SELECTOR = '.offcanvas.show' @@ -59,10 +45,20 @@ const EVENT_KEYDOWN_DISMISS = `keydown.dismiss${EVENT_KEY}` const SELECTOR_DATA_TOGGLE = '[data-bs-toggle="offcanvas"]' +const Default = { + backdrop: true, + keyboard: true, + scroll: false +} + +const DefaultType = { + backdrop: 'boolean', + keyboard: 'boolean', + scroll: 'boolean' +} + /** - * ------------------------------------------------------------------------ - * Class Definition - * ------------------------------------------------------------------------ + * Class definition */ class Offcanvas extends BaseComponent { @@ -77,7 +73,6 @@ class Offcanvas extends BaseComponent { } // Getters - static get NAME() { return NAME } @@ -87,7 +82,6 @@ class Offcanvas extends BaseComponent { } // Public - toggle(relatedTarget) { return this._isShown ? this.hide() : this.show(relatedTarget) } @@ -168,7 +162,6 @@ class Offcanvas extends BaseComponent { } // Private - _getConfig(config) { config = { ...Default, @@ -204,7 +197,6 @@ class Offcanvas extends BaseComponent { } // Static - static jQueryInterface(config) { return this.each(function () { const data = Offcanvas.getOrCreateInstance(this, config) @@ -223,9 +215,7 @@ class Offcanvas extends BaseComponent { } /** - * ------------------------------------------------------------------------ - * Data Api implementation - * ------------------------------------------------------------------------ + * Data API implementation */ EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) { @@ -247,9 +237,9 @@ EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function ( }) // avoid conflict when clicking a toggler of an offcanvas, while another is open - const allReadyOpen = SelectorEngine.findOne(OPEN_SELECTOR) - if (allReadyOpen && allReadyOpen !== target) { - Offcanvas.getInstance(allReadyOpen).hide() + const alreadyOpen = SelectorEngine.findOne(OPEN_SELECTOR) + if (alreadyOpen && alreadyOpen !== target) { + Offcanvas.getInstance(alreadyOpen).hide() } const data = Offcanvas.getOrCreateInstance(target) @@ -263,10 +253,9 @@ EventHandler.on(window, EVENT_LOAD_DATA_API, () => { }) enableDismissTrigger(Offcanvas) + /** - * ------------------------------------------------------------------------ * jQuery - * ------------------------------------------------------------------------ */ defineJQueryPlugin(Offcanvas) diff --git a/js/src/popover.js b/js/src/popover.js index aa9b0bc9e..144ec1cad 100644 --- a/js/src/popover.js +++ b/js/src/popover.js @@ -9,9 +9,7 @@ import { defineJQueryPlugin } from './util/index' import Tooltip from './tooltip' /** - * ------------------------------------------------------------------------ * Constants - * ------------------------------------------------------------------------ */ const NAME = 'popover' @@ -19,6 +17,9 @@ const DATA_KEY = 'bs.popover' const EVENT_KEY = `.${DATA_KEY}` const CLASS_PREFIX = 'bs-popover' +const SELECTOR_TITLE = '.popover-header' +const SELECTOR_CONTENT = '.popover-body' + const Default = { ...Tooltip.Default, placement: 'right', @@ -50,18 +51,12 @@ const Event = { MOUSELEAVE: `mouseleave${EVENT_KEY}` } -const SELECTOR_TITLE = '.popover-header' -const SELECTOR_CONTENT = '.popover-body' - /** - * ------------------------------------------------------------------------ - * Class Definition - * ------------------------------------------------------------------------ + * Class definition */ class Popover extends Tooltip { // Getters - static get Default() { return Default } @@ -79,7 +74,6 @@ class Popover extends Tooltip { } // Overrides - isWithContent() { return this.getTitle() || this._getContent() } @@ -90,7 +84,6 @@ class Popover extends Tooltip { } // Private - _getContent() { return this._resolvePossibleFunction(this._config.content) } @@ -100,7 +93,6 @@ class Popover extends Tooltip { } // Static - static jQueryInterface(config) { return this.each(function () { const data = Popover.getOrCreateInstance(this, config) @@ -117,10 +109,7 @@ class Popover extends Tooltip { } /** - * ------------------------------------------------------------------------ * jQuery - * ------------------------------------------------------------------------ - * add .Popover to jQuery only if jQuery is present */ defineJQueryPlugin(Popover) diff --git a/js/src/scrollspy.js b/js/src/scrollspy.js index bdc22653b..27bc0cd87 100644 --- a/js/src/scrollspy.js +++ b/js/src/scrollspy.js @@ -17,9 +17,7 @@ import SelectorEngine from './dom/selector-engine' import BaseComponent from './base-component' /** - * ------------------------------------------------------------------------ * Constants - * ------------------------------------------------------------------------ */ const NAME = 'scrollspy' @@ -27,18 +25,6 @@ const DATA_KEY = 'bs.scrollspy' const EVENT_KEY = `.${DATA_KEY}` const DATA_API_KEY = '.data-api' -const Default = { - offset: 10, - method: 'auto', - target: '' -} - -const DefaultType = { - offset: 'number', - method: 'string', - target: '(string|element)' -} - const EVENT_ACTIVATE = `activate${EVENT_KEY}` const EVENT_SCROLL = `scroll${EVENT_KEY}` const EVENT_LOAD_DATA_API = `load${EVENT_KEY}${DATA_API_KEY}` @@ -58,10 +44,20 @@ const SELECTOR_DROPDOWN_TOGGLE = '.dropdown-toggle' const METHOD_OFFSET = 'offset' const METHOD_POSITION = 'position' +const Default = { + offset: 10, + method: 'auto', + target: '' +} + +const DefaultType = { + offset: 'number', + method: 'string', + target: '(string|element)' +} + /** - * ------------------------------------------------------------------------ - * Class Definition - * ------------------------------------------------------------------------ + * Class definition */ class ScrollSpy extends BaseComponent { @@ -81,7 +77,6 @@ class ScrollSpy extends BaseComponent { } // Getters - static get Default() { return Default } @@ -91,7 +86,6 @@ class ScrollSpy extends BaseComponent { } // Public - refresh() { const autoMethod = this._scrollElement === this._scrollElement.window ? METHOD_OFFSET : @@ -141,7 +135,6 @@ class ScrollSpy extends BaseComponent { } // Private - _getConfig(config) { config = { ...Default, @@ -257,7 +250,6 @@ class ScrollSpy extends BaseComponent { } // Static - static jQueryInterface(config) { return this.each(function () { const data = ScrollSpy.getOrCreateInstance(this, config) @@ -276,9 +268,7 @@ class ScrollSpy extends BaseComponent { } /** - * ------------------------------------------------------------------------ - * Data Api implementation - * ------------------------------------------------------------------------ + * Data API implementation */ EventHandler.on(window, EVENT_LOAD_DATA_API, () => { @@ -288,10 +278,7 @@ EventHandler.on(window, EVENT_LOAD_DATA_API, () => { }) /** - * ------------------------------------------------------------------------ * jQuery - * ------------------------------------------------------------------------ - * add .ScrollSpy to jQuery only if jQuery is present */ defineJQueryPlugin(ScrollSpy) diff --git a/js/src/tab.js b/js/src/tab.js index 1fc00f797..4a018ca77 100644 --- a/js/src/tab.js +++ b/js/src/tab.js @@ -16,9 +16,7 @@ import SelectorEngine from './dom/selector-engine' import BaseComponent from './base-component' /** - * ------------------------------------------------------------------------ * Constants - * ------------------------------------------------------------------------ */ const NAME = 'tab' @@ -46,20 +44,16 @@ const SELECTOR_DROPDOWN_TOGGLE = '.dropdown-toggle' const SELECTOR_DROPDOWN_ACTIVE_CHILD = ':scope > .dropdown-menu .active' /** - * ------------------------------------------------------------------------ - * Class Definition - * ------------------------------------------------------------------------ + * Class definition */ class Tab extends BaseComponent { // Getters - static get NAME() { return NAME } // Public - show() { if ((this._element.parentNode && this._element.parentNode.nodeType === Node.ELEMENT_NODE && @@ -78,9 +72,7 @@ class Tab extends BaseComponent { } const hideEvent = previous ? - EventHandler.trigger(previous, EVENT_HIDE, { - relatedTarget: this._element - }) : + EventHandler.trigger(previous, EVENT_HIDE, { relatedTarget: this._element }) : null const showEvent = EventHandler.trigger(this._element, EVENT_SHOW, { @@ -94,12 +86,8 @@ class Tab extends BaseComponent { this._activate(this._element, listElement) const complete = () => { - EventHandler.trigger(previous, EVENT_HIDDEN, { - relatedTarget: this._element - }) - EventHandler.trigger(this._element, EVENT_SHOWN, { - relatedTarget: previous - }) + EventHandler.trigger(previous, EVENT_HIDDEN, { relatedTarget: this._element }) + EventHandler.trigger(this._element, EVENT_SHOWN, { relatedTarget: previous }) } if (target) { @@ -110,7 +98,6 @@ class Tab extends BaseComponent { } // Private - _activate(element, container, callback) { const activeElements = container && (container.nodeName === 'UL' || container.nodeName === 'OL') ? SelectorEngine.find(SELECTOR_ACTIVE_UL, container) : @@ -178,7 +165,6 @@ class Tab extends BaseComponent { } // Static - static jQueryInterface(config) { return this.each(function () { const data = Tab.getOrCreateInstance(this) @@ -195,9 +181,7 @@ class Tab extends BaseComponent { } /** - * ------------------------------------------------------------------------ - * Data Api implementation - * ------------------------------------------------------------------------ + * Data API implementation */ EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) { @@ -214,10 +198,7 @@ EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function ( }) /** - * ------------------------------------------------------------------------ * jQuery - * ------------------------------------------------------------------------ - * add .Tab to jQuery only if jQuery is present */ defineJQueryPlugin(Tab) diff --git a/js/src/toast.js b/js/src/toast.js index 780279be9..c45721c8f 100644 --- a/js/src/toast.js +++ b/js/src/toast.js @@ -16,9 +16,7 @@ import BaseComponent from './base-component' import { enableDismissTrigger } from './util/component-functions' /** - * ------------------------------------------------------------------------ * Constants - * ------------------------------------------------------------------------ */ const NAME = 'toast' @@ -52,9 +50,7 @@ const Default = { } /** - * ------------------------------------------------------------------------ - * Class Definition - * ------------------------------------------------------------------------ + * Class definition */ class Toast extends BaseComponent { @@ -69,7 +65,6 @@ class Toast extends BaseComponent { } // Getters - static get DefaultType() { return DefaultType } @@ -83,7 +78,6 @@ class Toast extends BaseComponent { } // Public - show() { const showEvent = EventHandler.trigger(this._element, EVENT_SHOW) @@ -145,7 +139,6 @@ class Toast extends BaseComponent { } // Private - _getConfig(config) { config = { ...Default, @@ -212,7 +205,6 @@ class Toast extends BaseComponent { } // Static - static jQueryInterface(config) { return this.each(function () { const data = Toast.getOrCreateInstance(this, config) @@ -228,13 +220,14 @@ class Toast extends BaseComponent { } } +/** + * Data API implementation + */ + enableDismissTrigger(Toast) /** - * ------------------------------------------------------------------------ * jQuery - * ------------------------------------------------------------------------ - * add .Toast to jQuery only if jQuery is present */ defineJQueryPlugin(Toast) diff --git a/js/src/tooltip.js b/js/src/tooltip.js index 7a8986548..f069dc751 100644 --- a/js/src/tooltip.js +++ b/js/src/tooltip.js @@ -6,7 +6,6 @@ */ import * as Popper from '@popperjs/core' - import { defineJQueryPlugin, findShadowRoot, @@ -25,9 +24,7 @@ import SelectorEngine from './dom/selector-engine' import BaseComponent from './base-component' /** - * ------------------------------------------------------------------------ * Constants - * ------------------------------------------------------------------------ */ const NAME = 'tooltip' @@ -36,25 +33,22 @@ const EVENT_KEY = `.${DATA_KEY}` const CLASS_PREFIX = 'bs-tooltip' const DISALLOWED_ATTRIBUTES = new Set(['sanitize', 'allowList', 'sanitizeFn']) -const DefaultType = { - animation: 'boolean', - template: 'string', - title: '(string|element|function)', - trigger: 'string', - delay: '(number|object)', - html: 'boolean', - selector: '(string|boolean)', - placement: '(string|function)', - offset: '(array|string|function)', - container: '(string|element|boolean)', - fallbackPlacements: 'array', - boundary: '(string|element)', - customClass: '(string|function)', - sanitize: 'boolean', - sanitizeFn: '(null|function)', - allowList: 'object', - popperConfig: '(null|object|function)' -} +const CLASS_NAME_FADE = 'fade' +const CLASS_NAME_MODAL = 'modal' +const CLASS_NAME_SHOW = 'show' + +const HOVER_STATE_SHOW = 'show' +const HOVER_STATE_OUT = 'out' + +const SELECTOR_TOOLTIP_INNER = '.tooltip-inner' +const SELECTOR_MODAL = `.${CLASS_NAME_MODAL}` + +const EVENT_MODAL_HIDE = 'hide.bs.modal' + +const TRIGGER_HOVER = 'hover' +const TRIGGER_FOCUS = 'focus' +const TRIGGER_CLICK = 'click' +const TRIGGER_MANUAL = 'manual' const AttachmentMap = { AUTO: 'auto', @@ -87,6 +81,26 @@ const Default = { popperConfig: null } +const DefaultType = { + animation: 'boolean', + template: 'string', + title: '(string|element|function)', + trigger: 'string', + delay: '(number|object)', + html: 'boolean', + selector: '(string|boolean)', + placement: '(string|function)', + offset: '(array|string|function)', + container: '(string|element|boolean)', + fallbackPlacements: 'array', + boundary: '(string|element)', + customClass: '(string|function)', + sanitize: 'boolean', + sanitizeFn: '(null|function)', + allowList: 'object', + popperConfig: '(null|object|function)' +} + const Event = { HIDE: `hide${EVENT_KEY}`, HIDDEN: `hidden${EVENT_KEY}`, @@ -100,27 +114,8 @@ const Event = { MOUSELEAVE: `mouseleave${EVENT_KEY}` } -const CLASS_NAME_FADE = 'fade' -const CLASS_NAME_MODAL = 'modal' -const CLASS_NAME_SHOW = 'show' - -const HOVER_STATE_SHOW = 'show' -const HOVER_STATE_OUT = 'out' - -const SELECTOR_TOOLTIP_INNER = '.tooltip-inner' -const SELECTOR_MODAL = `.${CLASS_NAME_MODAL}` - -const EVENT_MODAL_HIDE = 'hide.bs.modal' - -const TRIGGER_HOVER = 'hover' -const TRIGGER_FOCUS = 'focus' -const TRIGGER_CLICK = 'click' -const TRIGGER_MANUAL = 'manual' - /** - * ------------------------------------------------------------------------ - * Class Definition - * ------------------------------------------------------------------------ + * Class definition */ class Tooltip extends BaseComponent { @@ -131,7 +126,7 @@ class Tooltip extends BaseComponent { super(element) - // private + // Private this._isEnabled = true this._timeout = 0 this._hoverState = '' @@ -146,7 +141,6 @@ class Tooltip extends BaseComponent { } // Getters - static get Default() { return Default } @@ -164,7 +158,6 @@ class Tooltip extends BaseComponent { } // Public - enable() { this._isEnabled = true } @@ -358,7 +351,6 @@ class Tooltip extends BaseComponent { } // Protected - isWithContent() { return Boolean(this.getTitle()) } @@ -446,7 +438,6 @@ class Tooltip extends BaseComponent { } // Private - _initializeOnDelegatedTarget(event, context) { return context || this.constructor.getOrCreateInstance(event.delegateTarget, this._getDelegateConfig()) } @@ -754,10 +745,7 @@ class Tooltip extends BaseComponent { } /** - * ------------------------------------------------------------------------ * jQuery - * ------------------------------------------------------------------------ - * add .Tooltip to jQuery only if jQuery is present */ defineJQueryPlugin(Tooltip) diff --git a/js/src/util/backdrop.js b/js/src/util/backdrop.js index 04c763518..fb1b2776b 100644 --- a/js/src/util/backdrop.js +++ b/js/src/util/backdrop.js @@ -8,6 +8,15 @@ import EventHandler from '../dom/event-handler' import { execute, executeAfterTransition, getElement, reflow, typeCheckConfig } from './index' +/** + * Constants + */ + +const NAME = 'backdrop' +const CLASS_NAME_FADE = 'fade' +const CLASS_NAME_SHOW = 'show' +const EVENT_MOUSEDOWN = `mousedown.bs.${NAME}` + const Default = { className: 'modal-backdrop', isVisible: true, // if false, we use the backdrop helper without adding any element to the dom @@ -23,11 +32,10 @@ const DefaultType = { rootElement: '(element|string)', clickCallback: '(function|null)' } -const NAME = 'backdrop' -const CLASS_NAME_FADE = 'fade' -const CLASS_NAME_SHOW = 'show' -const EVENT_MOUSEDOWN = `mousedown.bs.${NAME}` +/** + * Class definition + */ class Backdrop { constructor(config) { @@ -36,6 +44,7 @@ class Backdrop { this._element = null } + // Public show(callback) { if (!this._config.isVisible) { execute(callback) @@ -69,8 +78,18 @@ class Backdrop { }) } - // Private + dispose() { + if (!this._isAppended) { + return + } + EventHandler.off(this._element, EVENT_MOUSEDOWN) + + this._element.remove() + this._isAppended = false + } + + // Private _getElement() { if (!this._element) { const backdrop = document.createElement('div') @@ -111,17 +130,6 @@ class Backdrop { this._isAppended = true } - dispose() { - if (!this._isAppended) { - return - } - - EventHandler.off(this._element, EVENT_MOUSEDOWN) - - this._element.remove() - this._isAppended = false - } - _emulateAnimation(callback) { executeAfterTransition(callback, this._getElement(), this._config.isAnimated) } diff --git a/js/src/util/focustrap.js b/js/src/util/focustrap.js index 44d5f47eb..a1975f489 100644 --- a/js/src/util/focustrap.js +++ b/js/src/util/focustrap.js @@ -9,15 +9,9 @@ import EventHandler from '../dom/event-handler' import SelectorEngine from '../dom/selector-engine' import { typeCheckConfig } from './index' -const Default = { - trapElement: null, // The element to trap focus inside of - autofocus: true -} - -const DefaultType = { - trapElement: 'element', - autofocus: 'boolean' -} +/** + * Constants + */ const NAME = 'focustrap' const DATA_KEY = 'bs.focustrap' @@ -29,6 +23,20 @@ const TAB_KEY = 'Tab' const TAB_NAV_FORWARD = 'forward' const TAB_NAV_BACKWARD = 'backward' +const Default = { + trapElement: null, // The element to trap focus inside of + autofocus: true +} + +const DefaultType = { + trapElement: 'element', + autofocus: 'boolean' +} + +/** + * Class definition + */ + class FocusTrap { constructor(config) { this._config = this._getConfig(config) @@ -36,6 +44,7 @@ class FocusTrap { this._lastTabNavDirection = null } + // Public activate() { const { trapElement, autofocus } = this._config @@ -64,7 +73,6 @@ class FocusTrap { } // Private - _handleFocusin(event) { const { target } = event const { trapElement } = this._config diff --git a/js/src/util/index.js b/js/src/util/index.js index 93f7223b1..0ba6ce6f8 100644 --- a/js/src/util/index.js +++ b/js/src/util/index.js @@ -19,9 +19,7 @@ const toType = obj => { } /** - * -------------------------------------------------------------------------- - * Public Util Api - * -------------------------------------------------------------------------- + * Public Util API */ const getUID = prefix => { @@ -113,7 +111,8 @@ const isElement = obj => { } const getElement = obj => { - if (isElement(obj)) { // it's a jQuery object or a node element + // it's a jQuery object or a node element + if (isElement(obj)) { return obj.jquery ? obj[0] : obj } @@ -196,8 +195,7 @@ const noop = () => {} * @see https://www.charistheo.io/blog/2021/02/restart-a-css-animation-with-javascript/#restarting-a-css-animation */ const reflow = element => { - // eslint-disable-next-line no-unused-expressions - element.offsetHeight + element.offsetHeight // eslint-disable-line no-unused-expressions } const getjQuery = () => { @@ -312,24 +310,24 @@ const getNextActiveElement = (list, activeElement, shouldGetNext, isCycleAllowed } export { + defineJQueryPlugin, + execute, + executeAfterTransition, + findShadowRoot, getElement, - getUID, - getSelectorFromElement, getElementFromSelector, + getjQuery, + getNextActiveElement, + getSelectorFromElement, getTransitionDurationFromElement, - triggerTransitionEnd, + getUID, + isDisabled, isElement, - typeCheckConfig, + isRTL, isVisible, - isDisabled, - findShadowRoot, noop, - getNextActiveElement, - reflow, - getjQuery, onDOMContentLoaded, - isRTL, - defineJQueryPlugin, - execute, - executeAfterTransition + reflow, + triggerTransitionEnd, + typeCheckConfig } diff --git a/js/src/util/scrollbar.js b/js/src/util/scrollbar.js index 16e14d1f3..55b7244ab 100644 --- a/js/src/util/scrollbar.js +++ b/js/src/util/scrollbar.js @@ -9,14 +9,23 @@ import SelectorEngine from '../dom/selector-engine' import Manipulator from '../dom/manipulator' import { isElement } from './index' +/** + * Constants + */ + const SELECTOR_FIXED_CONTENT = '.fixed-top, .fixed-bottom, .is-fixed, .sticky-top' const SELECTOR_STICKY_CONTENT = '.sticky-top' +/** + * Class definition + */ + class ScrollBarHelper { constructor() { this._element = document.body } + // Public getWidth() { // https://developer.mozilla.org/en-US/docs/Web/API/Window/innerWidth#usage_notes const documentWidth = document.documentElement.clientWidth @@ -33,6 +42,18 @@ class ScrollBarHelper { this._setElementAttributes(SELECTOR_STICKY_CONTENT, 'marginRight', calculatedValue => calculatedValue - width) } + reset() { + this._resetElementAttributes(this._element, 'overflow') + this._resetElementAttributes(this._element, 'paddingRight') + this._resetElementAttributes(SELECTOR_FIXED_CONTENT, 'paddingRight') + this._resetElementAttributes(SELECTOR_STICKY_CONTENT, 'marginRight') + } + + isOverflowing() { + return this.getWidth() > 0 + } + + // Private _disableOverFlow() { this._saveInitialAttribute(this._element, 'overflow') this._element.style.overflow = 'hidden' @@ -53,13 +74,6 @@ class ScrollBarHelper { this._applyManipulationCallback(selector, manipulationCallBack) } - reset() { - this._resetElementAttributes(this._element, 'overflow') - this._resetElementAttributes(this._element, 'paddingRight') - this._resetElementAttributes(SELECTOR_FIXED_CONTENT, 'paddingRight') - this._resetElementAttributes(SELECTOR_STICKY_CONTENT, 'marginRight') - } - _saveInitialAttribute(element, styleProp) { const actualValue = element.style[styleProp] if (actualValue) { @@ -90,10 +104,6 @@ class ScrollBarHelper { } } } - - isOverflowing() { - return this.getWidth() > 0 - } } export default ScrollBarHelper diff --git a/js/src/util/swipe.js b/js/src/util/swipe.js index a78f598d9..87a5f7f5a 100644 --- a/js/src/util/swipe.js +++ b/js/src/util/swipe.js @@ -1,6 +1,17 @@ +/** + * -------------------------------------------------------------------------- + * Bootstrap (v5.1.3): util/swipe.js + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) + * -------------------------------------------------------------------------- + */ + import EventHandler from '../dom/event-handler' import { execute, typeCheckConfig } from './index' +/** + * Constants + */ + const NAME = 'swipe' const EVENT_KEY = '.bs.swipe' const EVENT_TOUCHSTART = `touchstart${EVENT_KEY}` @@ -25,6 +36,10 @@ const DefaultType = { endCallback: '(function|null)' } +/** + * Class definition + */ + class Swipe { constructor(element, config) { this._element = element @@ -39,10 +54,12 @@ class Swipe { this._initEvents() } + // Public dispose() { EventHandler.off(this._element, EVENT_KEY) } + // Private _start(event) { if (!this._supportPointerEvents) { this._deltaX = event.touches[0].clientX @@ -114,6 +131,7 @@ class Swipe { return this._supportPointerEvents && (event.pointerType === POINTER_TYPE_PEN || event.pointerType === POINTER_TYPE_TOUCH) } + // Static static isSupported() { return 'ontouchstart' in document.documentElement || navigator.maxTouchPoints > 0 } -- cgit v1.2.3 From 1eea13286671d19bdd5c0ec52769b6d75789a928 Mon Sep 17 00:00:00 2001 From: GeoSot Date: Sun, 10 Oct 2021 12:36:18 +0300 Subject: collapse: extract duplicate code to a function --- js/src/collapse.js | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) (limited to 'js/src') diff --git a/js/src/collapse.js b/js/src/collapse.js index 39093c7a2..f4fa11de4 100644 --- a/js/src/collapse.js +++ b/js/src/collapse.js @@ -8,8 +8,8 @@ import { defineJQueryPlugin, getElement, - getSelectorFromElement, getElementFromSelector, + getSelectorFromElement, reflow, typeCheckConfig } from './util/index' @@ -120,9 +120,7 @@ class Collapse extends BaseComponent { let activesData if (this._config.parent) { - const children = SelectorEngine.find(CLASS_NAME_DEEPER_CHILDREN, this._config.parent) - // remove children if greater depth - actives = SelectorEngine.find(SELECTOR_ACTIVES, this._config.parent).filter(elem => !children.includes(elem)) + actives = this._getFirstLevelChildren(SELECTOR_ACTIVES) } const container = SelectorEngine.findOne(this._selector) @@ -245,10 +243,9 @@ class Collapse extends BaseComponent { return } - const children = SelectorEngine.find(CLASS_NAME_DEEPER_CHILDREN, this._config.parent) - const elements = SelectorEngine.find(SELECTOR_DATA_TOGGLE, this._config.parent).filter(elem => !children.includes(elem)) + const children = this._getFirstLevelChildren(SELECTOR_DATA_TOGGLE) - for (const element of elements) { + for (const element of children) { const selected = getElementFromSelector(element) if (selected) { @@ -257,6 +254,12 @@ class Collapse extends BaseComponent { } } + _getFirstLevelChildren(selector) { + const children = SelectorEngine.find(CLASS_NAME_DEEPER_CHILDREN, this._config.parent) + // remove children if greater depth + return SelectorEngine.find(selector, this._config.parent).filter(elem => !children.includes(elem)) + } + _addAriaAndCollapsedClass(triggerArray, isOpen) { if (!triggerArray.length) { return -- cgit v1.2.3 From 9640e2d5dd4ace95d4fd9f03af160b4329e97282 Mon Sep 17 00:00:00 2001 From: GeoSot Date: Sun, 10 Oct 2021 14:35:52 +0300 Subject: Change the way collapse handles its children on opening --- js/src/collapse.js | 30 +++++++++--------------------- 1 file changed, 9 insertions(+), 21 deletions(-) (limited to 'js/src') diff --git a/js/src/collapse.js b/js/src/collapse.js index f4fa11de4..642f7e840 100644 --- a/js/src/collapse.js +++ b/js/src/collapse.js @@ -13,7 +13,6 @@ import { reflow, typeCheckConfig } from './util/index' -import Data from './dom/data' import EventHandler from './dom/event-handler' import Manipulator from './dom/manipulator' import SelectorEngine from './dom/selector-engine' @@ -77,7 +76,6 @@ class Collapse extends BaseComponent { .filter(foundElem => foundElem === this._element) if (selector !== null && filterElement.length) { - this._selector = selector this._triggerArray.push(elem) } } @@ -116,21 +114,17 @@ class Collapse extends BaseComponent { return } - let actives = [] - let activesData + let activeChildren = [] + // find active children if (this._config.parent) { - actives = this._getFirstLevelChildren(SELECTOR_ACTIVES) + activeChildren = this._getFirstLevelChildren(SELECTOR_ACTIVES) + .filter(element => element !== this._element) + .map(element => Collapse.getOrCreateInstance(element, { toggle: false })) } - const container = SelectorEngine.findOne(this._selector) - if (actives.length) { - const tempActiveData = actives.find(elem => container !== elem) - activesData = tempActiveData ? Collapse.getInstance(tempActiveData) : null - - if (activesData && activesData._isTransitioning) { - return - } + if (activeChildren.length && activeChildren[0]._isTransitioning) { + return } const startEvent = EventHandler.trigger(this._element, EVENT_SHOW) @@ -138,14 +132,8 @@ class Collapse extends BaseComponent { return } - for (const elemActive of actives) { - if (container !== elemActive) { - Collapse.getOrCreateInstance(elemActive, { toggle: false }).hide() - } - - if (!activesData) { - Data.set(elemActive, DATA_KEY, null) - } + for (const activeInstance of activeChildren) { + activeInstance.hide() } const dimension = this._getDimension() -- cgit v1.2.3 From 94a596fbcb1011ba990da2078ba7e20b39dba2d9 Mon Sep 17 00:00:00 2001 From: GeoSot Date: Thu, 25 Nov 2021 19:14:02 +0200 Subject: Add a template factory helper to handle all template cases (#34519) Co-authored-by: XhmikosR --- js/src/popover.js | 10 ++- js/src/tooltip.js | 129 ++++++++++++-------------------- js/src/util/template-factory.js | 161 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 214 insertions(+), 86 deletions(-) create mode 100644 js/src/util/template-factory.js (limited to 'js/src') diff --git a/js/src/popover.js b/js/src/popover.js index 144ec1cad..0b255a585 100644 --- a/js/src/popover.js +++ b/js/src/popover.js @@ -78,12 +78,14 @@ class Popover extends Tooltip { return this.getTitle() || this._getContent() } - setContent(tip) { - this._sanitizeAndSetContent(tip, this.getTitle(), SELECTOR_TITLE) - this._sanitizeAndSetContent(tip, this._getContent(), SELECTOR_CONTENT) + // Private + _getContentForTemplate() { + return { + [SELECTOR_TITLE]: this.getTitle(), + [SELECTOR_CONTENT]: this._getContent() + } } - // Private _getContent() { return this._resolvePossibleFunction(this._config.content) } diff --git a/js/src/tooltip.js b/js/src/tooltip.js index f069dc751..c84596101 100644 --- a/js/src/tooltip.js +++ b/js/src/tooltip.js @@ -11,17 +11,16 @@ import { findShadowRoot, getElement, getUID, - isElement, isRTL, noop, typeCheckConfig } from './util/index' -import { DefaultAllowlist, sanitizeHtml } from './util/sanitizer' +import { DefaultAllowlist } from './util/sanitizer' import Data from './dom/data' import EventHandler from './dom/event-handler' import Manipulator from './dom/manipulator' -import SelectorEngine from './dom/selector-engine' import BaseComponent from './base-component' +import TemplateFactory from './util/template-factory' /** * Constants @@ -40,6 +39,7 @@ const CLASS_NAME_SHOW = 'show' const HOVER_STATE_SHOW = 'show' const HOVER_STATE_OUT = 'out' +const SELECTOR_TOOLTIP_ARROW = '.tooltip-arrow' const SELECTOR_TOOLTIP_INNER = '.tooltip-inner' const SELECTOR_MODAL = `.${CLASS_NAME_MODAL}` @@ -132,6 +132,7 @@ class Tooltip extends BaseComponent { this._hoverState = '' this._activeTrigger = {} this._popper = null + this._templateFactory = null // Protected this._config = this._getConfig(config) @@ -227,23 +228,9 @@ class Tooltip extends BaseComponent { return } - // A trick to recreate a tooltip in case a new title is given by using the NOT documented `data-bs-original-title` - // This will be removed later in favor of a `setContent` method - if (this.constructor.NAME === 'tooltip' && this.tip && this.getTitle() !== this.tip.querySelector(SELECTOR_TOOLTIP_INNER).innerHTML) { - this._disposePopper() - this.tip.remove() - this.tip = null - } - const tip = this.getTipElement() - const tipId = getUID(this.constructor.NAME) - - tip.setAttribute('id', tipId) - this._element.setAttribute('aria-describedby', tipId) - if (this._config.animation) { - tip.classList.add(CLASS_NAME_FADE) - } + this._element.setAttribute('aria-describedby', tip.getAttribute('id')) const placement = typeof this._config.placement === 'function' ? this._config.placement.call(this, tip, this._element) : @@ -268,11 +255,6 @@ class Tooltip extends BaseComponent { tip.classList.add(CLASS_NAME_SHOW) - const customClass = this._resolvePossibleFunction(this._config.customClass) - if (customClass) { - tip.classList.add(...customClass.split(' ')) - } - // If this is a touch-enabled device we add extra // empty mouseover listeners to the body's immediate children; // only needed because of broken event delegation on iOS @@ -360,69 +342,63 @@ class Tooltip extends BaseComponent { return this.tip } - const element = document.createElement('div') - element.innerHTML = this._config.template + const templateFactory = this._getTemplateFactory(this._getContentForTemplate()) - const tip = element.children[0] - this.setContent(tip) + const tip = templateFactory.toHtml() tip.classList.remove(CLASS_NAME_FADE, CLASS_NAME_SHOW) - this.tip = tip - return this.tip - } - - setContent(tip) { - this._sanitizeAndSetContent(tip, this.getTitle(), SELECTOR_TOOLTIP_INNER) - } + const tipId = getUID(this.constructor.NAME).toString() - _sanitizeAndSetContent(template, content, selector) { - const templateElement = SelectorEngine.findOne(selector, template) + tip.setAttribute('id', tipId) - if (!content && templateElement) { - templateElement.remove() - return + if (this._config.animation) { + tip.classList.add(CLASS_NAME_FADE) } - // we use append for html objects to maintain js events - this.setElementContent(templateElement, content) + this.tip = tip + return this.tip } - setElementContent(element, content) { - if (element === null) { - return + setContent(content) { + let isShown = false + if (this.tip) { + isShown = this.tip.classList.contains(CLASS_NAME_SHOW) + this.tip.remove() } - if (isElement(content)) { - content = getElement(content) + this._disposePopper() - // content is a DOM node or a jQuery - if (this._config.html) { - if (content.parentNode !== element) { - element.innerHTML = '' - element.append(content) - } - } else { - element.textContent = content.textContent - } + this.tip = this._getTemplateFactory(content).toHtml() - return + if (isShown) { + this.show() } + } - if (this._config.html) { - if (this._config.sanitize) { - content = sanitizeHtml(content, this._config.allowList, this._config.sanitizeFn) - } - - element.innerHTML = content // lgtm [js/xss-through-dom] + _getTemplateFactory(content) { + if (this._templateFactory) { + this._templateFactory.changeContent(content) } else { - element.textContent = content + this._templateFactory = new TemplateFactory({ + ...this._config, + // the `content` var has to be after `this._config` + // to override config.content in case of popover + content, + extraClass: this._resolvePossibleFunction(this._config.customClass) + }) } + + return this._templateFactory } - getTitle() { - const title = this._element.getAttribute('data-bs-original-title') || this._config.title + _getContentForTemplate() { + return { + [SELECTOR_TOOLTIP_INNER]: this.getTitle() + } + } - return this._resolvePossibleFunction(title) + getTitle() { + return this._resolvePossibleFunction(this._config.title) || this._element.getAttribute('title') } updateAttachment(attachment) { @@ -456,8 +432,8 @@ class Tooltip extends BaseComponent { return offset } - _resolvePossibleFunction(content) { - return typeof content === 'function' ? content.call(this._element) : content + _resolvePossibleFunction(arg) { + return typeof arg === 'function' ? arg.call(this._element) : arg } _getPopperConfig(attachment) { @@ -485,7 +461,7 @@ class Tooltip extends BaseComponent { { name: 'arrow', options: { - element: `.${this.constructor.NAME}-arrow` + element: SELECTOR_TOOLTIP_ARROW } }, { @@ -556,15 +532,9 @@ class Tooltip extends BaseComponent { _fixTitle() { const title = this._element.getAttribute('title') - const originalTitleType = typeof this._element.getAttribute('data-bs-original-title') - if (title || originalTitleType !== 'string') { - this._element.setAttribute('data-bs-original-title', title || '') - if (title && !this._element.getAttribute('aria-label') && !this._element.textContent) { - this._element.setAttribute('aria-label', title) - } - - this._element.setAttribute('title', '') + if (title && !this._element.getAttribute('aria-label') && !this._element.textContent) { + this._element.setAttribute('aria-label', title) } } @@ -670,11 +640,6 @@ class Tooltip extends BaseComponent { } typeCheckConfig(NAME, config, this.constructor.DefaultType) - - if (config.sanitize) { - config.template = sanitizeHtml(config.template, config.allowList, config.sanitizeFn) - } - return config } diff --git a/js/src/util/template-factory.js b/js/src/util/template-factory.js new file mode 100644 index 000000000..a9cee1086 --- /dev/null +++ b/js/src/util/template-factory.js @@ -0,0 +1,161 @@ +/** + * -------------------------------------------------------------------------- + * Bootstrap (v5.1.3): util/template-factory.js + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) + * -------------------------------------------------------------------------- + */ + +import { DefaultAllowlist, sanitizeHtml } from './sanitizer' +import { getElement, isElement, typeCheckConfig } from '../util/index' +import SelectorEngine from '../dom/selector-engine' + +/** + * Constants + */ + +const NAME = 'TemplateFactory' + +const Default = { + extraClass: '', + template: '
', + content: {}, // { selector : text , selector2 : text2 , } + html: false, + sanitize: true, + sanitizeFn: null, + allowList: DefaultAllowlist +} + +const DefaultType = { + extraClass: '(string|function)', + template: 'string', + content: 'object', + html: 'boolean', + sanitize: 'boolean', + sanitizeFn: '(null|function)', + allowList: 'object' +} + +const DefaultContentType = { + selector: '(string|element)', + entry: '(string|element|function|null)' +} + +/** + * Class definition + */ + +class TemplateFactory { + constructor(config) { + this._config = this._getConfig(config) + } + + // Getters + static get NAME() { + return NAME + } + + static get Default() { + return Default + } + + // Public + getContent() { + return Object.values(this._config.content) + .map(config => this._resolvePossibleFunction(config)) + .filter(Boolean) + } + + hasContent() { + return this.getContent().length > 0 + } + + changeContent(content) { + this._checkContent(content) + this._config.content = { ...this._config.content, ...content } + return this + } + + toHtml() { + const templateWrapper = document.createElement('div') + templateWrapper.innerHTML = this._maybeSanitize(this._config.template) + + for (const [selector, text] of Object.entries(this._config.content)) { + this._setContent(templateWrapper, text, selector) + } + + const template = templateWrapper.children[0] + const extraClass = this._resolvePossibleFunction(this._config.extraClass) + + if (extraClass) { + template.classList.add(...extraClass.split(' ')) + } + + return template + } + + // Private + _getConfig(config) { + config = { + ...Default, + ...(typeof config === 'object' ? config : {}) + } + + typeCheckConfig(NAME, config, DefaultType) + this._checkContent(config.content) + + return config + } + + _checkContent(arg) { + for (const [selector, content] of Object.entries(arg)) { + typeCheckConfig(NAME, { selector, entry: content }, DefaultContentType) + } + } + + _setContent(template, content, selector) { + const templateElement = SelectorEngine.findOne(selector, template) + + if (!templateElement) { + return + } + + content = this._resolvePossibleFunction(content) + + if (!content) { + templateElement.remove() + return + } + + if (isElement(content)) { + this._putElementInTemplate(getElement(content), templateElement) + return + } + + if (this._config.html) { + templateElement.innerHTML = this._maybeSanitize(content) + return + } + + templateElement.textContent = content + } + + _maybeSanitize(arg) { + return this._config.sanitize ? sanitizeHtml(arg, this._config.allowList, this._config.sanitizeFn) : arg + } + + _resolvePossibleFunction(arg) { + return typeof arg === 'function' ? arg(this) : arg + } + + _putElementInTemplate(element, templateElement) { + if (this._config.html) { + templateElement.innerHTML = '' + templateElement.append(element) + return + } + + templateElement.textContent = element.textContent + } +} + +export default TemplateFactory -- cgit v1.2.3 From 79e01c3bad6b7376cf7dc1ea9c6551dce9fdd10e Mon Sep 17 00:00:00 2001 From: GeoSot Date: Fri, 8 Oct 2021 02:19:19 +0300 Subject: Some refactoring on modal, to improve readability and generic functionality --- js/src/modal.js | 60 ++++++++++++++++++++++++++++----------------------------- 1 file changed, 30 insertions(+), 30 deletions(-) (limited to 'js/src') diff --git a/js/src/modal.js b/js/src/modal.js index bfb980236..1c7a96adc 100644 --- a/js/src/modal.js +++ b/js/src/modal.js @@ -112,10 +112,7 @@ class Modal extends BaseComponent { } this._isShown = true - - if (this._isAnimated()) { - this._isTransitioning = true - } + this._isTransitioning = true this._scrollBar.hide() @@ -149,11 +146,7 @@ class Modal extends BaseComponent { } this._isShown = false - const isAnimated = this._isAnimated() - - if (isAnimated) { - this._isTransitioning = true - } + this._isTransitioning = true this._setEscapeEvent() this._setResizeEvent() @@ -165,7 +158,7 @@ class Modal extends BaseComponent { EventHandler.off(this._element, EVENT_CLICK_DISMISS) EventHandler.off(this._dialog, EVENT_MOUSEDOWN_DISMISS) - this._queueCallback(() => this._hideModal(), this._element, isAnimated) + this._queueCallback(() => this._hideModal(), this._element, this._isAnimated()) } dispose() { @@ -207,9 +200,6 @@ class Modal extends BaseComponent { } _showElement(relatedTarget) { - const isAnimated = this._isAnimated() - const modalBody = SelectorEngine.findOne(SELECTOR_MODAL_BODY, this._dialog) - if (!this._element.parentNode || this._element.parentNode.nodeType !== Node.ELEMENT_NODE) { // Don't move modal's DOM position document.body.append(this._element) @@ -221,13 +211,12 @@ class Modal extends BaseComponent { this._element.setAttribute('role', 'dialog') this._element.scrollTop = 0 + const modalBody = SelectorEngine.findOne(SELECTOR_MODAL_BODY, this._dialog) if (modalBody) { modalBody.scrollTop = 0 } - if (isAnimated) { - reflow(this._element) - } + reflow(this._element) this._element.classList.add(CLASS_NAME_SHOW) @@ -242,30 +231,37 @@ class Modal extends BaseComponent { }) } - this._queueCallback(transitionComplete, this._dialog, isAnimated) + this._queueCallback(transitionComplete, this._dialog, this._isAnimated()) } _setEscapeEvent() { - if (this._isShown) { - EventHandler.on(this._element, EVENT_KEYDOWN_DISMISS, event => { - if (this._config.keyboard && event.key === ESCAPE_KEY) { - event.preventDefault() - this.hide() - } else if (!this._config.keyboard && event.key === ESCAPE_KEY) { - this._triggerBackdropTransition() - } - }) - } else { + if (!this._isShown) { EventHandler.off(this._element, EVENT_KEYDOWN_DISMISS) + return } + + EventHandler.on(this._element, EVENT_KEYDOWN_DISMISS, event => { + if (event.key !== ESCAPE_KEY) { + return + } + + if (this._config.keyboard) { + event.preventDefault() + this.hide() + return + } + + this._triggerBackdropTransition() + }) } _setResizeEvent() { if (this._isShown) { EventHandler.on(window, EVENT_RESIZE, () => this._adjustDialog()) - } else { - EventHandler.off(window, EVENT_RESIZE) + return } + + EventHandler.off(window, EVENT_RESIZE) } _hideModal() { @@ -274,6 +270,7 @@ class Modal extends BaseComponent { this._element.removeAttribute('aria-modal') this._element.removeAttribute('role') this._isTransitioning = false + this._backdrop.hide(() => { document.body.classList.remove(CLASS_NAME_OPEN) this._resetAdjustments() @@ -295,7 +292,10 @@ class Modal extends BaseComponent { if (this._config.backdrop === true) { this.hide() - } else if (this._config.backdrop === 'static') { + return + } + + if (this._config.backdrop === 'static') { this._triggerBackdropTransition() } }) -- cgit v1.2.3 From fc33ce4b4682edff6ade324bd24abe89f0782158 Mon Sep 17 00:00:00 2001 From: GeoSot Date: Fri, 8 Oct 2021 02:27:00 +0300 Subject: Tweak methods Name them to be more descriptive and have agnostic functionality --- js/src/modal.js | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'js/src') diff --git a/js/src/modal.js b/js/src/modal.js index 1c7a96adc..455d395f7 100644 --- a/js/src/modal.js +++ b/js/src/modal.js @@ -120,8 +120,8 @@ class Modal extends BaseComponent { this._adjustDialog() - this._setEscapeEvent() - this._setResizeEvent() + this._toggleEscapeEventListener(true) + this._toggleResizeEventListener(true) EventHandler.on(this._dialog, EVENT_MOUSEDOWN_DISMISS, () => { EventHandler.one(this._element, EVENT_MOUSEUP_DISMISS, event => { @@ -148,8 +148,8 @@ class Modal extends BaseComponent { this._isShown = false this._isTransitioning = true - this._setEscapeEvent() - this._setResizeEvent() + this._toggleEscapeEventListener(false) + this._toggleResizeEventListener(false) this._focustrap.deactivate() @@ -234,8 +234,8 @@ class Modal extends BaseComponent { this._queueCallback(transitionComplete, this._dialog, this._isAnimated()) } - _setEscapeEvent() { - if (!this._isShown) { + _toggleEscapeEventListener(enable) { + if (!enable) { EventHandler.off(this._element, EVENT_KEYDOWN_DISMISS) return } @@ -255,8 +255,8 @@ class Modal extends BaseComponent { }) } - _setResizeEvent() { - if (this._isShown) { + _toggleResizeEventListener(enable) { + if (enable) { EventHandler.on(window, EVENT_RESIZE, () => this._adjustDialog()) return } -- cgit v1.2.3 From 92e664c921ad6b39d9addd7d991a88f3ea6ea7bc Mon Sep 17 00:00:00 2001 From: GeoSot Date: Fri, 8 Oct 2021 02:46:11 +0300 Subject: Change check for dynamic modal --- js/src/modal.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'js/src') diff --git a/js/src/modal.js b/js/src/modal.js index 455d395f7..16181775a 100644 --- a/js/src/modal.js +++ b/js/src/modal.js @@ -200,8 +200,8 @@ class Modal extends BaseComponent { } _showElement(relatedTarget) { - if (!this._element.parentNode || this._element.parentNode.nodeType !== Node.ELEMENT_NODE) { - // Don't move modal's DOM position + // try to append dynamic modal + if (!document.body.contains(this._element)) { document.body.append(this._element) } -- cgit v1.2.3 From cc3e5789ecba18ccf759b60d90946a1a88ea0fa5 Mon Sep 17 00:00:00 2001 From: GeoSot Date: Fri, 8 Oct 2021 02:55:11 +0300 Subject: Remove some uncovered code that seems to be unused --- js/src/modal.js | 19 ------------------- 1 file changed, 19 deletions(-) (limited to 'js/src') diff --git a/js/src/modal.js b/js/src/modal.js index 16181775a..c96ad388c 100644 --- a/js/src/modal.js +++ b/js/src/modal.js @@ -40,8 +40,6 @@ const EVENT_SHOWN = `shown${EVENT_KEY}` const EVENT_RESIZE = `resize${EVENT_KEY}` const EVENT_CLICK_DISMISS = `click.dismiss${EVENT_KEY}` const EVENT_KEYDOWN_DISMISS = `keydown.dismiss${EVENT_KEY}` -const EVENT_MOUSEUP_DISMISS = `mouseup.dismiss${EVENT_KEY}` -const EVENT_MOUSEDOWN_DISMISS = `mousedown.dismiss${EVENT_KEY}` const EVENT_CLICK_DATA_API = `click${EVENT_KEY}${DATA_API_KEY}` const CLASS_NAME_OPEN = 'modal-open' @@ -79,7 +77,6 @@ class Modal extends BaseComponent { this._backdrop = this._initializeBackDrop() this._focustrap = this._initializeFocusTrap() this._isShown = false - this._ignoreBackdropClick = false this._isTransitioning = false this._scrollBar = new ScrollBarHelper() } @@ -123,14 +120,6 @@ class Modal extends BaseComponent { this._toggleEscapeEventListener(true) this._toggleResizeEventListener(true) - EventHandler.on(this._dialog, EVENT_MOUSEDOWN_DISMISS, () => { - EventHandler.one(this._element, EVENT_MOUSEUP_DISMISS, event => { - if (event.target === this._element) { - this._ignoreBackdropClick = true - } - }) - }) - this._showBackdrop(() => this._showElement(relatedTarget)) } @@ -155,9 +144,6 @@ class Modal extends BaseComponent { this._element.classList.remove(CLASS_NAME_SHOW) - EventHandler.off(this._element, EVENT_CLICK_DISMISS) - EventHandler.off(this._dialog, EVENT_MOUSEDOWN_DISMISS) - this._queueCallback(() => this._hideModal(), this._element, this._isAnimated()) } @@ -281,11 +267,6 @@ class Modal extends BaseComponent { _showBackdrop(callback) { EventHandler.on(this._element, EVENT_CLICK_DISMISS, event => { - if (this._ignoreBackdropClick) { - this._ignoreBackdropClick = false - return - } - if (event.target !== event.currentTarget) { return } -- cgit v1.2.3 From 0f9fd75d6c7bfd850f50d6a587c54b8e1227830c Mon Sep 17 00:00:00 2001 From: GeoSot Date: Fri, 8 Oct 2021 03:19:18 +0300 Subject: Respect modal's initial overflowY --- js/src/modal.js | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) (limited to 'js/src') diff --git a/js/src/modal.js b/js/src/modal.js index c96ad388c..ecbf5cadc 100644 --- a/js/src/modal.js +++ b/js/src/modal.js @@ -296,9 +296,9 @@ class Modal extends BaseComponent { const { classList, scrollHeight, style } = this._element const isModalOverflowing = scrollHeight > document.documentElement.clientHeight - + const initialOverflowY = style.overflowY // return if the following background transition hasn't yet completed - if ((!isModalOverflowing && style.overflowY === 'hidden') || classList.contains(CLASS_NAME_STATIC)) { + if (initialOverflowY === 'hidden' || classList.contains(CLASS_NAME_STATIC)) { return } @@ -309,11 +309,9 @@ class Modal extends BaseComponent { classList.add(CLASS_NAME_STATIC) this._queueCallback(() => { classList.remove(CLASS_NAME_STATIC) - if (!isModalOverflowing) { - this._queueCallback(() => { - style.overflowY = '' - }, this._dialog) - } + this._queueCallback(() => { + style.overflowY = initialOverflowY + }, this._dialog) }, this._dialog) this._element.focus() -- cgit v1.2.3 From 91ad255e07d3d16f2ea88ca5b49bbe569ea5e2b4 Mon Sep 17 00:00:00 2001 From: GeoSot Date: Fri, 8 Oct 2021 12:14:45 +0300 Subject: Change adjustDialog's if conditions to improve readability --- js/src/modal.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'js/src') diff --git a/js/src/modal.js b/js/src/modal.js index ecbf5cadc..b8b144774 100644 --- a/js/src/modal.js +++ b/js/src/modal.js @@ -326,12 +326,14 @@ class Modal extends BaseComponent { const scrollbarWidth = this._scrollBar.getWidth() const isBodyOverflowing = scrollbarWidth > 0 - if ((!isBodyOverflowing && isModalOverflowing && !isRTL()) || (isBodyOverflowing && !isModalOverflowing && isRTL())) { - this._element.style.paddingLeft = `${scrollbarWidth}px` + if (isBodyOverflowing && !isModalOverflowing) { + const property = isRTL() ? 'paddingLeft' : 'paddingRight' + this._element.style[property] = `${scrollbarWidth}px` } - if ((isBodyOverflowing && !isModalOverflowing && !isRTL()) || (!isBodyOverflowing && isModalOverflowing && isRTL())) { - this._element.style.paddingRight = `${scrollbarWidth}px` + if (!isBodyOverflowing && isModalOverflowing) { + const property = isRTL() ? 'paddingRight' : 'paddingLeft' + this._element.style[property] = `${scrollbarWidth}px` } } -- cgit v1.2.3 From 374eeecfbcabae20da08ce00d0f9268bb67b1e5e Mon Sep 17 00:00:00 2001 From: GeoSot Date: Thu, 25 Nov 2021 19:39:13 +0200 Subject: tooltip.js: use array.includes instead of for iteration (#35127) --- js/src/tooltip.js | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) (limited to 'js/src') diff --git a/js/src/tooltip.js b/js/src/tooltip.js index c84596101..bc59e6e94 100644 --- a/js/src/tooltip.js +++ b/js/src/tooltip.js @@ -598,13 +598,7 @@ class Tooltip extends BaseComponent { } _isWithActiveTrigger() { - for (const trigger in this._activeTrigger) { - if (this._activeTrigger[trigger]) { - return true - } - } - - return false + return Object.values(this._activeTrigger).includes(true) } _getConfig(config) { -- cgit v1.2.3 From 6f077ff7bcf5c3e8a9cd579e26a2325ce32543c9 Mon Sep 17 00:00:00 2001 From: GeoSot Date: Thu, 25 Nov 2021 20:08:11 +0200 Subject: Clean tooltip component unneeded functionality (#32692) --- js/src/popover.js | 5 ----- js/src/tooltip.js | 63 +++++++------------------------------------------------ 2 files changed, 7 insertions(+), 61 deletions(-) (limited to 'js/src') diff --git a/js/src/popover.js b/js/src/popover.js index 0b255a585..19c1e42a4 100644 --- a/js/src/popover.js +++ b/js/src/popover.js @@ -15,7 +15,6 @@ import Tooltip from './tooltip' const NAME = 'popover' const DATA_KEY = 'bs.popover' const EVENT_KEY = `.${DATA_KEY}` -const CLASS_PREFIX = 'bs-popover' const SELECTOR_TITLE = '.popover-header' const SELECTOR_CONTENT = '.popover-body' @@ -90,10 +89,6 @@ class Popover extends Tooltip { return this._resolvePossibleFunction(this._config.content) } - _getBasicClassPrefix() { - return CLASS_PREFIX - } - // Static static jQueryInterface(config) { return this.each(function () { diff --git a/js/src/tooltip.js b/js/src/tooltip.js index bc59e6e94..29be4d8d2 100644 --- a/js/src/tooltip.js +++ b/js/src/tooltip.js @@ -29,7 +29,6 @@ import TemplateFactory from './util/template-factory' const NAME = 'tooltip' const DATA_KEY = 'bs.tooltip' const EVENT_KEY = `.${DATA_KEY}` -const CLASS_PREFIX = 'bs-tooltip' const DISALLOWED_ATTRIBUTES = new Set(['sanitize', 'allowList', 'sanitizeFn']) const CLASS_NAME_FADE = 'fade' @@ -232,13 +231,6 @@ class Tooltip extends BaseComponent { this._element.setAttribute('aria-describedby', tip.getAttribute('id')) - const placement = typeof this._config.placement === 'function' ? - this._config.placement.call(this, tip, this._element) : - this._config.placement - - const attachment = this._getAttachment(placement) - this._addAttachmentClass(attachment) - const { container } = this._config Data.set(tip, this.constructor.DATA_KEY, this) @@ -250,6 +242,10 @@ class Tooltip extends BaseComponent { if (this._popper) { this._popper.update() } else { + const placement = typeof this._config.placement === 'function' ? + this._config.placement.call(this, tip, this._element) : + this._config.placement + const attachment = AttachmentMap[placement.toUpperCase()] this._popper = Popper.createPopper(this._element, tip, this._getPopperConfig(attachment)) } @@ -295,7 +291,6 @@ class Tooltip extends BaseComponent { tip.remove() } - this._cleanTipClass() this._element.removeAttribute('aria-describedby') EventHandler.trigger(this._element, this.constructor.Event.HIDDEN) @@ -346,6 +341,8 @@ class Tooltip extends BaseComponent { const tip = templateFactory.toHtml() tip.classList.remove(CLASS_NAME_FADE, CLASS_NAME_SHOW) + // todo on v6 the following can be done on css only + tip.classList.add(`bs-${this.constructor.NAME}-auto`) const tipId = getUID(this.constructor.NAME).toString() @@ -463,19 +460,8 @@ class Tooltip extends BaseComponent { options: { element: SELECTOR_TOOLTIP_ARROW } - }, - { - name: 'onChange', - enabled: true, - phase: 'afterWrite', - fn: data => this._handlePopperPlacementChange(data) - } - ], - onFirstUpdate: data => { - if (data.options.placement !== data.placement) { - this._handlePopperPlacementChange(data) } - } + ] } return { @@ -484,14 +470,6 @@ class Tooltip extends BaseComponent { } } - _addAttachmentClass(attachment) { - this.getTipElement().classList.add(`${this._getBasicClassPrefix()}-${this.updateAttachment(attachment)}`) - } - - _getAttachment(placement) { - return AttachmentMap[placement.toUpperCase()] - } - _setListeners() { const triggers = this._config.trigger.split(' ') @@ -652,33 +630,6 @@ class Tooltip extends BaseComponent { return config } - _cleanTipClass() { - const tip = this.getTipElement() - const basicClassPrefixRegex = new RegExp(`(^|\\s)${this._getBasicClassPrefix()}\\S+`, 'g') - const tabClass = tip.getAttribute('class').match(basicClassPrefixRegex) - if (tabClass !== null && tabClass.length > 0) { - for (const tClass of tabClass.map(token => token.trim())) { - tip.classList.remove(tClass) - } - } - } - - _getBasicClassPrefix() { - return CLASS_PREFIX - } - - _handlePopperPlacementChange(popperData) { - const { state } = popperData - - if (!state) { - return - } - - this.tip = state.elements.popper - this._cleanTipClass() - this._addAttachmentClass(this._getAttachment(state.placement)) - } - _disposePopper() { if (this._popper) { this._popper.destroy() -- cgit v1.2.3