mirror of
https://github.com/Redocly/redoc.git
synced 2025-03-01 08:30:33 +03:00
Fix Chrome scrolling by using alternate scrollIntoViewIfNeeded (#1823)
* Break out scrollIntoViewIfNeeded into freely usable function * Always use alternate implementation of scrollIntoViewIfNeeded Refs #1714 Refs #1742
This commit is contained in:
parent
ace52f7e49
commit
3028d3d7ac
|
@ -7,6 +7,7 @@ import { shortenHTTPVerb } from '../../utils/openapi';
|
||||||
import { MenuItems } from './MenuItems';
|
import { MenuItems } from './MenuItems';
|
||||||
import { MenuItemLabel, MenuItemLi, MenuItemTitle, OperationBadge } from './styled.elements';
|
import { MenuItemLabel, MenuItemLi, MenuItemTitle, OperationBadge } from './styled.elements';
|
||||||
import { l } from '../../services/Labels';
|
import { l } from '../../services/Labels';
|
||||||
|
import { scrollIntoViewIfNeeded } from '../../utils';
|
||||||
|
|
||||||
export interface MenuItemProps {
|
export interface MenuItemProps {
|
||||||
item: IMenuItem;
|
item: IMenuItem;
|
||||||
|
@ -33,7 +34,7 @@ export class MenuItem extends React.Component<MenuItemProps> {
|
||||||
|
|
||||||
scrollIntoViewIfActive() {
|
scrollIntoViewIfActive() {
|
||||||
if (this.props.item.active && this.ref.current) {
|
if (this.props.item.active && this.ref.current) {
|
||||||
this.ref.current.scrollIntoViewIfNeeded();
|
scrollIntoViewIfNeeded(this.ref.current);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -77,7 +78,7 @@ export class OperationMenuItemContent extends React.Component<OperationMenuItemC
|
||||||
|
|
||||||
componentDidUpdate() {
|
componentDidUpdate() {
|
||||||
if (this.props.item.active && this.ref.current) {
|
if (this.props.item.active && this.ref.current) {
|
||||||
this.ref.current.scrollIntoViewIfNeeded();
|
scrollIntoViewIfNeeded(this.ref.current);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -37,7 +37,6 @@ export class SideMenu extends React.Component<{ menu: MenuStore; className?: str
|
||||||
if (item && item.active && this.context.menuToggle) {
|
if (item && item.active && this.context.menuToggle) {
|
||||||
return item.expanded ? item.collapse() : item.expand();
|
return item.expanded ? item.collapse() : item.expand();
|
||||||
}
|
}
|
||||||
|
|
||||||
this.props.menu.activateAndScroll(item, true);
|
this.props.menu.activateAndScroll(item, true);
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
if (this._updateScroll) {
|
if (this._updateScroll) {
|
||||||
|
|
|
@ -24,52 +24,54 @@ export function html2Str(html: string): string {
|
||||||
.join(' ');
|
.join(' ');
|
||||||
}
|
}
|
||||||
|
|
||||||
// scrollIntoViewIfNeeded polyfill
|
// Alternate scrollIntoViewIfNeeded implementation.
|
||||||
|
// Used in all cases, since it seems Chrome's implementation is buggy
|
||||||
|
// when "Experimental Web Platform Features" is enabled (at least of version 96).
|
||||||
|
// See #1714, #1742
|
||||||
|
|
||||||
if (typeof Element !== 'undefined' && !(Element as any).prototype.scrollIntoViewIfNeeded) {
|
export function scrollIntoViewIfNeeded(el: HTMLElement, centerIfNeeded = true) {
|
||||||
(Element as any).prototype.scrollIntoViewIfNeeded = function (centerIfNeeded) {
|
const parent = el.parentNode as HTMLElement | null;
|
||||||
centerIfNeeded = arguments.length === 0 ? true : !!centerIfNeeded;
|
if (!parent) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const parentComputedStyle = window.getComputedStyle(parent, undefined);
|
||||||
|
const parentBorderTopWidth = parseInt(
|
||||||
|
parentComputedStyle.getPropertyValue('border-top-width'),
|
||||||
|
10,
|
||||||
|
);
|
||||||
|
const parentBorderLeftWidth = parseInt(
|
||||||
|
parentComputedStyle.getPropertyValue('border-left-width'),
|
||||||
|
10,
|
||||||
|
);
|
||||||
|
const overTop = el.offsetTop - parent.offsetTop < parent.scrollTop;
|
||||||
|
const overBottom =
|
||||||
|
el.offsetTop - parent.offsetTop + el.clientHeight - parentBorderTopWidth >
|
||||||
|
parent.scrollTop + parent.clientHeight;
|
||||||
|
const overLeft = el.offsetLeft - parent.offsetLeft < parent.scrollLeft;
|
||||||
|
const overRight =
|
||||||
|
el.offsetLeft - parent.offsetLeft + el.clientWidth - parentBorderLeftWidth >
|
||||||
|
parent.scrollLeft + parent.clientWidth;
|
||||||
|
const alignWithTop = overTop && !overBottom;
|
||||||
|
|
||||||
const parent = this.parentNode;
|
if ((overTop || overBottom) && centerIfNeeded) {
|
||||||
const parentComputedStyle = window.getComputedStyle(parent, undefined);
|
parent.scrollTop =
|
||||||
const parentBorderTopWidth = parseInt(
|
el.offsetTop -
|
||||||
parentComputedStyle.getPropertyValue('border-top-width'),
|
parent.offsetTop -
|
||||||
10,
|
parent.clientHeight / 2 -
|
||||||
);
|
parentBorderTopWidth +
|
||||||
const parentBorderLeftWidth = parseInt(
|
el.clientHeight / 2;
|
||||||
parentComputedStyle.getPropertyValue('border-left-width'),
|
}
|
||||||
10,
|
|
||||||
);
|
|
||||||
const overTop = this.offsetTop - parent.offsetTop < parent.scrollTop;
|
|
||||||
const overBottom =
|
|
||||||
this.offsetTop - parent.offsetTop + this.clientHeight - parentBorderTopWidth >
|
|
||||||
parent.scrollTop + parent.clientHeight;
|
|
||||||
const overLeft = this.offsetLeft - parent.offsetLeft < parent.scrollLeft;
|
|
||||||
const overRight =
|
|
||||||
this.offsetLeft - parent.offsetLeft + this.clientWidth - parentBorderLeftWidth >
|
|
||||||
parent.scrollLeft + parent.clientWidth;
|
|
||||||
const alignWithTop = overTop && !overBottom;
|
|
||||||
|
|
||||||
if ((overTop || overBottom) && centerIfNeeded) {
|
if ((overLeft || overRight) && centerIfNeeded) {
|
||||||
parent.scrollTop =
|
parent.scrollLeft =
|
||||||
this.offsetTop -
|
el.offsetLeft -
|
||||||
parent.offsetTop -
|
parent.offsetLeft -
|
||||||
parent.clientHeight / 2 -
|
parent.clientWidth / 2 -
|
||||||
parentBorderTopWidth +
|
parentBorderLeftWidth +
|
||||||
this.clientHeight / 2;
|
el.clientWidth / 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((overLeft || overRight) && centerIfNeeded) {
|
if ((overTop || overBottom || overLeft || overRight) && !centerIfNeeded) {
|
||||||
parent.scrollLeft =
|
el.scrollIntoView(alignWithTop);
|
||||||
this.offsetLeft -
|
}
|
||||||
parent.offsetLeft -
|
|
||||||
parent.clientWidth / 2 -
|
|
||||||
parentBorderLeftWidth +
|
|
||||||
this.clientWidth / 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((overTop || overBottom || overLeft || overRight) && !centerIfNeeded) {
|
|
||||||
this.scrollIntoView(alignWithTop);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user