From 548be2ed6604ddfc8488cd4a793c6271c2caf485 Mon Sep 17 00:00:00 2001 From: GeoSot Date: Tue, 2 Mar 2021 19:10:10 +0200 Subject: Offcanvas as component (#29017) * Add a new offcanvas component * offcanvas.js: switch to string constants and `event.key` * Remove unneeded code * Sass optimizations * Fixes Make sure the element is hidden and not offscreen when inactive fix close icon negative margins Add content in right & bottom examples Re-fix bottom offcanvas height not to cover all viewport * Wording tweaks * update tests and offcanvas class * separate scrollbar functionality and use it in offcanvas * Update .bundlewatch.config.json * fix focus * update btn-close / fix focus on close * add aria-modal and role return focus on trigger when offcanvas is closed change body scrolling timings * move common code to reusable functions * add aria-labelledby * Replace lorem ipsum text * fix focus when offcanvas is closed * updates * revert modal, add tests for scrollbar * show backdrop by default * Update offcanvas.md * Update offcanvas CSS to better match modals - Add background-clip for borders - Move from outline to border (less clever, more consistent) - Add scss-docs in vars * Revamp offcanvas docs - Add static example to show and explain the components - Split live examples and rename them - Simplify example content - Expand docs notes elsewhere - Add sass docs * Add .offcanvas-title instead of .modal-title * Rename offcanvas example to offcanvas-navbar to reflect it's purpose * labelledby references title and not header * Add default shadow to offcanvas * enable offcanvas-body to fill all the remaining wrapper area * Be more descriptive, on Accessibility area * remove redundant classes * ensure in case of an already open offcanvas, not to open another one * bring back backdrop|scroll combinations * bring back toggling class * refactor scrollbar method, plus tests * add check if element is not full-width, according to #30621 * revert all in modal * use documentElement innerWidth * Rename classes to -start and -end Also copyedit some docs wording * omit some things on scrollbar * PASS BrowserStack tests -- IOS devices, Android devices and Browsers on Mac, hide scrollbar by default and appear it, only while scrolling. * Rename '_handleClosing' to '_addEventListeners' * change pipe usage to comma * change Data.getData to Data.get Co-authored-by: XhmikosR Co-authored-by: Martijn Cuppens Co-authored-by: Mark Otto --- js/src/util/scrollbar.js | 70 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 js/src/util/scrollbar.js (limited to 'js/src/util/scrollbar.js') diff --git a/js/src/util/scrollbar.js b/js/src/util/scrollbar.js new file mode 100644 index 000000000..d2f3919e6 --- /dev/null +++ b/js/src/util/scrollbar.js @@ -0,0 +1,70 @@ +/** + * -------------------------------------------------------------------------- + * Bootstrap (v5.0.0-beta2): util/scrollBar.js + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) + * -------------------------------------------------------------------------- + */ + +import SelectorEngine from '../dom/selector-engine' +import Manipulator from '../dom/manipulator' + +const SELECTOR_FIXED_CONTENT = '.fixed-top, .fixed-bottom, .is-fixed' +const SELECTOR_STICKY_CONTENT = '.sticky-top' + +const getWidth = () => { + // https://developer.mozilla.org/en-US/docs/Web/API/Window/innerWidth#usage_notes + const documentWidth = document.documentElement.clientWidth + return Math.abs(window.innerWidth - documentWidth) +} + +const hide = (width = getWidth()) => { + document.body.style.overflow = 'hidden' + _setElementAttributes(SELECTOR_FIXED_CONTENT, 'paddingRight', calculatedValue => calculatedValue + width) + _setElementAttributes(SELECTOR_STICKY_CONTENT, 'marginRight', calculatedValue => calculatedValue - width) + _setElementAttributes('body', 'paddingRight', calculatedValue => calculatedValue + width) +} + +const _setElementAttributes = (selector, styleProp, callback) => { + const scrollbarWidth = getWidth() + SelectorEngine.find(selector) + .forEach(element => { + if (element !== document.body && window.innerWidth > element.clientWidth + scrollbarWidth) { + return + } + + const actualValue = element.style[styleProp] + const calculatedValue = window.getComputedStyle(element)[styleProp] + Manipulator.setDataAttribute(element, styleProp, actualValue) + element.style[styleProp] = callback(Number.parseFloat(calculatedValue)) + 'px' + }) +} + +const reset = () => { + document.body.style.overflow = 'auto' + _resetElementAttributes(SELECTOR_FIXED_CONTENT, 'paddingRight') + _resetElementAttributes(SELECTOR_STICKY_CONTENT, 'marginRight') + _resetElementAttributes('body', 'paddingRight') +} + +const _resetElementAttributes = (selector, styleProp) => { + SelectorEngine.find(selector).forEach(element => { + const value = Manipulator.getDataAttribute(element, styleProp) + if (typeof value === 'undefined' && element === document.body) { + element.style.removeProperty(styleProp) + } else { + Manipulator.removeDataAttribute(element, styleProp) + element.style[styleProp] = value + } + }) +} + +const isBodyOverflowing = () => { + return getWidth() > 0 +} + +export { + getWidth, + hide, + isBodyOverflowing, + reset +} -- cgit v1.2.3 From 220139a89ffc3864bbb6e1b35471667318eadc1f Mon Sep 17 00:00:00 2001 From: XhmikosR Date: Tue, 23 Mar 2021 18:26:54 +0200 Subject: Release v5.0.0-beta3 (#33439) --- js/src/util/scrollbar.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'js/src/util/scrollbar.js') diff --git a/js/src/util/scrollbar.js b/js/src/util/scrollbar.js index d2f3919e6..3e619ef51 100644 --- a/js/src/util/scrollbar.js +++ b/js/src/util/scrollbar.js @@ -1,6 +1,6 @@ /** * -------------------------------------------------------------------------- - * Bootstrap (v5.0.0-beta2): util/scrollBar.js + * Bootstrap (v5.0.0-beta3): util/scrollBar.js * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) * -------------------------------------------------------------------------- */ -- cgit v1.2.3 From 5cc53a0ef0718b43e376462f156d39bf5542fbf9 Mon Sep 17 00:00:00 2001 From: Rohit Sharma Date: Tue, 30 Mar 2021 11:27:05 +0530 Subject: Use template literals instead of concatenation (#33497) --- js/src/util/scrollbar.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'js/src/util/scrollbar.js') diff --git a/js/src/util/scrollbar.js b/js/src/util/scrollbar.js index 3e619ef51..e63a66bf2 100644 --- a/js/src/util/scrollbar.js +++ b/js/src/util/scrollbar.js @@ -35,7 +35,7 @@ const _setElementAttributes = (selector, styleProp, callback) => { const actualValue = element.style[styleProp] const calculatedValue = window.getComputedStyle(element)[styleProp] Manipulator.setDataAttribute(element, styleProp, actualValue) - element.style[styleProp] = callback(Number.parseFloat(calculatedValue)) + 'px' + element.style[styleProp] = `${callback(Number.parseFloat(calculatedValue))}px` }) } -- cgit v1.2.3 From 7b7f4a5ced176ae3d7d9d16583795245cb9c7df3 Mon Sep 17 00:00:00 2001 From: GeoSot Date: Sun, 11 Apr 2021 09:37:59 +0300 Subject: Decouple Modal's scrollbar functionality (#33245) --- js/src/util/scrollbar.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'js/src/util/scrollbar.js') diff --git a/js/src/util/scrollbar.js b/js/src/util/scrollbar.js index e63a66bf2..31b614375 100644 --- a/js/src/util/scrollbar.js +++ b/js/src/util/scrollbar.js @@ -8,7 +8,7 @@ import SelectorEngine from '../dom/selector-engine' import Manipulator from '../dom/manipulator' -const SELECTOR_FIXED_CONTENT = '.fixed-top, .fixed-bottom, .is-fixed' +const SELECTOR_FIXED_CONTENT = '.fixed-top, .fixed-bottom, .is-fixed, .sticky-top' const SELECTOR_STICKY_CONTENT = '.sticky-top' const getWidth = () => { @@ -19,6 +19,7 @@ const getWidth = () => { const hide = (width = getWidth()) => { document.body.style.overflow = 'hidden' + // trick: We adjust positive paddingRight and negative marginRight to sticky-top elements, to keep shown fullwidth _setElementAttributes(SELECTOR_FIXED_CONTENT, 'paddingRight', calculatedValue => calculatedValue + width) _setElementAttributes(SELECTOR_STICKY_CONTENT, 'marginRight', calculatedValue => calculatedValue - width) _setElementAttributes('body', 'paddingRight', calculatedValue => calculatedValue + width) @@ -49,7 +50,7 @@ const reset = () => { const _resetElementAttributes = (selector, styleProp) => { SelectorEngine.find(selector).forEach(element => { const value = Manipulator.getDataAttribute(element, styleProp) - if (typeof value === 'undefined' && element === document.body) { + if (typeof value === 'undefined') { element.style.removeProperty(styleProp) } else { Manipulator.removeDataAttribute(element, styleProp) -- cgit v1.2.3 From d381820d1678044257fe2cb7b2f178d1f001ed30 Mon Sep 17 00:00:00 2001 From: GeoSot Date: Sun, 25 Apr 2021 06:50:16 +0300 Subject: Scrollbar: respect the initial body overflow value (#33706) * add method to handle overflow on body element & tests * replace duplicated code on modal/offcanvas tests --- js/src/util/scrollbar.js | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) (limited to 'js/src/util/scrollbar.js') diff --git a/js/src/util/scrollbar.js b/js/src/util/scrollbar.js index 31b614375..352e3e11d 100644 --- a/js/src/util/scrollbar.js +++ b/js/src/util/scrollbar.js @@ -18,11 +18,21 @@ const getWidth = () => { } const hide = (width = getWidth()) => { - document.body.style.overflow = 'hidden' + _disableOverFlow() + // give padding to element to balances the hidden scrollbar width + _setElementAttributes('body', 'paddingRight', calculatedValue => calculatedValue + width) // trick: We adjust positive paddingRight and negative marginRight to sticky-top elements, to keep shown fullwidth _setElementAttributes(SELECTOR_FIXED_CONTENT, 'paddingRight', calculatedValue => calculatedValue + width) _setElementAttributes(SELECTOR_STICKY_CONTENT, 'marginRight', calculatedValue => calculatedValue - width) - _setElementAttributes('body', 'paddingRight', calculatedValue => calculatedValue + width) +} + +const _disableOverFlow = () => { + const actualValue = document.body.style.overflow + if (actualValue) { + Manipulator.setDataAttribute(document.body, 'overflow', actualValue) + } + + document.body.style.overflow = 'hidden' } const _setElementAttributes = (selector, styleProp, callback) => { @@ -41,10 +51,10 @@ const _setElementAttributes = (selector, styleProp, callback) => { } const reset = () => { - document.body.style.overflow = 'auto' + _resetElementAttributes('body', 'overflow') + _resetElementAttributes('body', 'paddingRight') _resetElementAttributes(SELECTOR_FIXED_CONTENT, 'paddingRight') _resetElementAttributes(SELECTOR_STICKY_CONTENT, 'marginRight') - _resetElementAttributes('body', 'paddingRight') } const _resetElementAttributes = (selector, styleProp) => { -- cgit v1.2.3 From bf0936748602c8109fd916c64b4560799fa1c3f8 Mon Sep 17 00:00:00 2001 From: XhmikosR Date: Wed, 5 May 2021 22:32:12 +0300 Subject: Release v5.0.0 (#33647) * Bump version to 5.0.0 * Fix npm tag * Dist --- js/src/util/scrollbar.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'js/src/util/scrollbar.js') diff --git a/js/src/util/scrollbar.js b/js/src/util/scrollbar.js index 352e3e11d..87ea1390a 100644 --- a/js/src/util/scrollbar.js +++ b/js/src/util/scrollbar.js @@ -1,6 +1,6 @@ /** * -------------------------------------------------------------------------- - * Bootstrap (v5.0.0-beta3): util/scrollBar.js + * Bootstrap (v5.0.0): util/scrollBar.js * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) * -------------------------------------------------------------------------- */ -- cgit v1.2.3 From 58b1be927f43c779377e478df2d119f2ddf956ca Mon Sep 17 00:00:00 2001 From: XhmikosR Date: Thu, 13 May 2021 19:22:20 +0300 Subject: Release v5.0.1 (#33972) * Bump version to 5.0.1. * Dist --- js/src/util/scrollbar.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'js/src/util/scrollbar.js') diff --git a/js/src/util/scrollbar.js b/js/src/util/scrollbar.js index 87ea1390a..98c076f25 100644 --- a/js/src/util/scrollbar.js +++ b/js/src/util/scrollbar.js @@ -1,6 +1,6 @@ /** * -------------------------------------------------------------------------- - * Bootstrap (v5.0.0): util/scrollBar.js + * Bootstrap (v5.0.1): util/scrollBar.js * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) * -------------------------------------------------------------------------- */ -- cgit v1.2.3 From 79c3bf47bc125fd64cf8cac6cd34b03270d34e2d Mon Sep 17 00:00:00 2001 From: GeoSot Date: Thu, 20 May 2021 16:29:04 +0300 Subject: Add Tests on scrollbar.js & better handling if a style property doesn't exists (#33948) * scrollbar.js: add some tests transfer test from modal.spec. to scrollbar.spec proper handling if style property doesn't exist --- js/src/util/scrollbar.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'js/src/util/scrollbar.js') diff --git a/js/src/util/scrollbar.js b/js/src/util/scrollbar.js index 98c076f25..79a3b12c7 100644 --- a/js/src/util/scrollbar.js +++ b/js/src/util/scrollbar.js @@ -44,8 +44,11 @@ const _setElementAttributes = (selector, styleProp, callback) => { } const actualValue = element.style[styleProp] + if (actualValue) { + Manipulator.setDataAttribute(element, styleProp, actualValue) + } + const calculatedValue = window.getComputedStyle(element)[styleProp] - Manipulator.setDataAttribute(element, styleProp, actualValue) element.style[styleProp] = `${callback(Number.parseFloat(calculatedValue))}px` }) } -- cgit v1.2.3 From cb47b8c9640abcc19c17908475153849b9d4ad60 Mon Sep 17 00:00:00 2001 From: GeoSot Date: Sun, 6 Jun 2021 09:26:36 +0300 Subject: Refactor scrollbar.js to be used as a Class (#33947) --- js/src/util/scrollbar.js | 121 ++++++++++++++++++++++++++--------------------- 1 file changed, 67 insertions(+), 54 deletions(-) (limited to 'js/src/util/scrollbar.js') diff --git a/js/src/util/scrollbar.js b/js/src/util/scrollbar.js index 79a3b12c7..e23415f1d 100644 --- a/js/src/util/scrollbar.js +++ b/js/src/util/scrollbar.js @@ -7,78 +7,91 @@ import SelectorEngine from '../dom/selector-engine' import Manipulator from '../dom/manipulator' +import { isElement } from './index' const SELECTOR_FIXED_CONTENT = '.fixed-top, .fixed-bottom, .is-fixed, .sticky-top' const SELECTOR_STICKY_CONTENT = '.sticky-top' -const getWidth = () => { - // https://developer.mozilla.org/en-US/docs/Web/API/Window/innerWidth#usage_notes - const documentWidth = document.documentElement.clientWidth - return Math.abs(window.innerWidth - documentWidth) -} +class ScrollBarHelper { + constructor() { + this._element = document.body + } -const hide = (width = getWidth()) => { - _disableOverFlow() - // give padding to element to balances the hidden scrollbar width - _setElementAttributes('body', 'paddingRight', calculatedValue => calculatedValue + width) - // trick: We adjust positive paddingRight and negative marginRight to sticky-top elements, to keep shown fullwidth - _setElementAttributes(SELECTOR_FIXED_CONTENT, 'paddingRight', calculatedValue => calculatedValue + width) - _setElementAttributes(SELECTOR_STICKY_CONTENT, 'marginRight', calculatedValue => calculatedValue - width) -} + getWidth() { + // https://developer.mozilla.org/en-US/docs/Web/API/Window/innerWidth#usage_notes + const documentWidth = document.documentElement.clientWidth + return Math.abs(window.innerWidth - documentWidth) + } -const _disableOverFlow = () => { - const actualValue = document.body.style.overflow - if (actualValue) { - Manipulator.setDataAttribute(document.body, 'overflow', actualValue) + hide() { + const width = this.getWidth() + this._disableOverFlow() + // give padding to element to balance the hidden scrollbar width + this._setElementAttributes(this._element, 'paddingRight', calculatedValue => calculatedValue + width) + // trick: We adjust positive paddingRight and negative marginRight to sticky-top elements to keep showing fullwidth + this._setElementAttributes(SELECTOR_FIXED_CONTENT, 'paddingRight', calculatedValue => calculatedValue + width) + this._setElementAttributes(SELECTOR_STICKY_CONTENT, 'marginRight', calculatedValue => calculatedValue - width) } - document.body.style.overflow = 'hidden' -} + _disableOverFlow() { + this._saveInitialAttribute(this._element, 'overflow') + this._element.style.overflow = 'hidden' + } -const _setElementAttributes = (selector, styleProp, callback) => { - const scrollbarWidth = getWidth() - SelectorEngine.find(selector) - .forEach(element => { - if (element !== document.body && window.innerWidth > element.clientWidth + scrollbarWidth) { + _setElementAttributes(selector, styleProp, callback) { + const scrollbarWidth = this.getWidth() + const manipulationCallBack = element => { + if (element !== this._element && window.innerWidth > element.clientWidth + scrollbarWidth) { return } - const actualValue = element.style[styleProp] - if (actualValue) { - Manipulator.setDataAttribute(element, styleProp, actualValue) - } - + this._saveInitialAttribute(element, styleProp) const calculatedValue = window.getComputedStyle(element)[styleProp] element.style[styleProp] = `${callback(Number.parseFloat(calculatedValue))}px` - }) -} + } -const reset = () => { - _resetElementAttributes('body', 'overflow') - _resetElementAttributes('body', 'paddingRight') - _resetElementAttributes(SELECTOR_FIXED_CONTENT, 'paddingRight') - _resetElementAttributes(SELECTOR_STICKY_CONTENT, 'marginRight') -} + 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) { + Manipulator.setDataAttribute(element, styleProp, actualValue) + } + } + + _resetElementAttributes(selector, styleProp) { + const manipulationCallBack = element => { + const value = Manipulator.getDataAttribute(element, styleProp) + if (typeof value === 'undefined') { + element.style.removeProperty(styleProp) + } else { + Manipulator.removeDataAttribute(element, styleProp) + element.style[styleProp] = value + } + } -const _resetElementAttributes = (selector, styleProp) => { - SelectorEngine.find(selector).forEach(element => { - const value = Manipulator.getDataAttribute(element, styleProp) - if (typeof value === 'undefined') { - element.style.removeProperty(styleProp) + this._applyManipulationCallback(selector, manipulationCallBack) + } + + _applyManipulationCallback(selector, callBack) { + if (isElement(selector)) { + callBack(selector) } else { - Manipulator.removeDataAttribute(element, styleProp) - element.style[styleProp] = value + SelectorEngine.find(selector, this._element).forEach(callBack) } - }) -} + } -const isBodyOverflowing = () => { - return getWidth() > 0 + isOverflowing() { + return this.getWidth() > 0 + } } -export { - getWidth, - hide, - isBodyOverflowing, - reset -} +export default ScrollBarHelper -- cgit v1.2.3 From 688bce4fa695cc360a0d084e34f029b0c192b223 Mon Sep 17 00:00:00 2001 From: XhmikosR Date: Tue, 22 Jun 2021 21:29:16 +0300 Subject: Release v5.0.2 (#34276) * Bump version to v5.0.2. * Dist --- js/src/util/scrollbar.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'js/src/util/scrollbar.js') diff --git a/js/src/util/scrollbar.js b/js/src/util/scrollbar.js index e23415f1d..fad9766ac 100644 --- a/js/src/util/scrollbar.js +++ b/js/src/util/scrollbar.js @@ -1,6 +1,6 @@ /** * -------------------------------------------------------------------------- - * Bootstrap (v5.0.1): util/scrollBar.js + * Bootstrap (v5.0.2): util/scrollBar.js * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) * -------------------------------------------------------------------------- */ -- cgit v1.2.3 From f20fece3a8cdd0e76a42c2737524b7652bf54d26 Mon Sep 17 00:00:00 2001 From: XhmikosR Date: Wed, 4 Aug 2021 18:41:51 +0300 Subject: Prepare v5.1.0. (#34674) --- js/src/util/scrollbar.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'js/src/util/scrollbar.js') diff --git a/js/src/util/scrollbar.js b/js/src/util/scrollbar.js index fad9766ac..c90d82907 100644 --- a/js/src/util/scrollbar.js +++ b/js/src/util/scrollbar.js @@ -1,6 +1,6 @@ /** * -------------------------------------------------------------------------- - * Bootstrap (v5.0.2): util/scrollBar.js + * Bootstrap (v5.1.0): util/scrollBar.js * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) * -------------------------------------------------------------------------- */ -- cgit v1.2.3