Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix(modal): adds overflow: hidden from code and fixes scrollbar measu…
…rement fix #4128
- Loading branch information
Showing
2 changed files
with
26 additions
and
64 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,80 +1,42 @@ | ||
import {Injectable, Inject} from '@angular/core'; | ||
import {DOCUMENT} from '@angular/common'; | ||
|
||
|
||
const noop = () => {}; | ||
|
||
|
||
|
||
/** Type for the callback used to revert the scrollbar compensation. */ | ||
export type CompensationReverter = () => void; | ||
|
||
|
||
/** Type for the callback used to revert the scrollbar. */ | ||
export type ScrollbarReverter = () => void; | ||
|
||
/** | ||
* Utility to handle the scrollbar. | ||
* | ||
* It allows to compensate the lack of a vertical scrollbar by adding an | ||
* equivalent padding on the right of the body, and to remove this compensation. | ||
* It allows to hide the scrollbar and compensate the lack of a vertical scrollbar | ||
* by adding an equivalent padding on the right of the body, and to revert this change. | ||
*/ | ||
@Injectable({providedIn: 'root'}) | ||
export class ScrollBar { | ||
constructor(@Inject(DOCUMENT) private _document: any) {} | ||
|
||
/** | ||
* To be called right before a potential vertical scrollbar would be removed: | ||
* | ||
* - if there was a scrollbar, adds some compensation padding to the body | ||
* to keep the same layout as when the scrollbar is there | ||
* - if there was none, there is nothing to do | ||
* To be called to hide a potential vertical scrollbar: | ||
* - if a scrollbar is there and has a width greater than 0, adds some compensation | ||
* padding to the body to keep the same layout as when the scrollbar is there | ||
* - adds overflow: hidden | ||
* | ||
* @return a callback used to revert the compensation (noop if there was none, | ||
* otherwise a function removing the padding) | ||
* @return a callback used to revert the change | ||
*/ | ||
compensate(): CompensationReverter { | ||
const width = this._getWidth(); | ||
return !this._isPresent(width) ? noop : this._adjustBody(width); | ||
} | ||
|
||
/** | ||
* Adds a padding of the given width on the right of the body. | ||
* | ||
* @return a callback used to revert the padding to its previous value | ||
*/ | ||
private _adjustBody(scrollbarWidth: number): CompensationReverter { | ||
hide(): ScrollbarReverter { | ||
const scrollbarWidth = Math.abs(window.innerWidth - this._document.documentElement.clientWidth); | ||
const body = this._document.body; | ||
const userSetPaddingStyle = body.style.paddingRight; | ||
const actualPadding = parseFloat(window.getComputedStyle(body)['padding-right']); | ||
body.style['padding-right'] = `${actualPadding + scrollbarWidth}px`; | ||
return () => body.style['padding-right'] = userSetPaddingStyle; | ||
} | ||
|
||
/** | ||
* Tells whether a scrollbar is currently present on the body. | ||
* | ||
* @return true if scrollbar is present, false otherwise | ||
*/ | ||
private _isPresent(scrollbarWidth: number): boolean { | ||
const rect = this._document.body.getBoundingClientRect(); | ||
const bodyToViewportGap = window.innerWidth - (rect.left + rect.right); | ||
const uncertainty = 0.1 * scrollbarWidth; | ||
return bodyToViewportGap >= scrollbarWidth - uncertainty; | ||
} | ||
|
||
/** | ||
* Calculates and returns the width of a scrollbar. | ||
* | ||
* @return the width of a scrollbar on this page | ||
*/ | ||
private _getWidth(): number { | ||
const measurer = this._document.createElement('div'); | ||
measurer.className = 'modal-scrollbar-measure'; | ||
|
||
const body = this._document.body; | ||
body.appendChild(measurer); | ||
const width = measurer.getBoundingClientRect().width - measurer.clientWidth; | ||
body.removeChild(measurer); | ||
|
||
return width; | ||
const bodyStyle = body.style; | ||
const {overflow, paddingRight} = bodyStyle; | ||
if (scrollbarWidth > 0) { | ||
const actualPadding = parseFloat(window.getComputedStyle(body).paddingRight); | ||
bodyStyle.paddingRight = `${actualPadding + scrollbarWidth}px`; | ||
} | ||
bodyStyle.overflow = 'hidden'; | ||
return () => { | ||
if (scrollbarWidth > 0) { | ||
bodyStyle.paddingRight = paddingRight; | ||
} | ||
bodyStyle.overflow = overflow; | ||
}; | ||
} | ||
} |