diff options
| author | GeoSot <[email protected]> | 2021-03-02 19:10:10 +0200 |
|---|---|---|
| committer | GitHub <[email protected]> | 2021-03-02 19:10:10 +0200 |
| commit | 548be2ed6604ddfc8488cd4a793c6271c2caf485 (patch) | |
| tree | fdde8406dd05b0d7e157c7be58f9561d7626f8c1 /js/src/util/scrollbar.js | |
| parent | b9e51dc3c4400ede5e72991dd0efacf9dbcb694e (diff) | |
| download | bootstrap-548be2ed6604ddfc8488cd4a793c6271c2caf485.tar.xz bootstrap-548be2ed6604ddfc8488cd4a793c6271c2caf485.zip | |
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 <[email protected]>
Co-authored-by: Martijn Cuppens <[email protected]>
Co-authored-by: Mark Otto <[email protected]>
Diffstat (limited to 'js/src/util/scrollbar.js')
| -rw-r--r-- | js/src/util/scrollbar.js | 70 |
1 files changed, 70 insertions, 0 deletions
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 +} |
