mirror of
https://github.com/facebook/docusaurus.git
synced 2025-05-01 03:08:17 +02:00
fix(ideal-image): Internalize react-waypoint
dependency, fix React 19 compatibility (#11014)
* copy waypoint, remove logs * remove propTypes * remove debug * remove scrollableAncestor prop * remove onPositionChange * remove horizontal prop * remove fireOnRapidScroll * remove useless render code * remove ensureRefIsUsedByChild * remove children prop * inline constants * remove consolidated-events * copy getCurrentPosition * remove computeOffsetPixels * extract findScrollableAncestor * extract getBounds * remove hasWindow * remove onNextTick() * fixes * make it work, replace waypoint * slim down * slim down * slim down * use TypeScript * slim down * slim down * revert
This commit is contained in:
parent
fcee060f40
commit
43fdb825e8
4 changed files with 242 additions and 20 deletions
|
@ -26,7 +26,6 @@
|
||||||
"@docusaurus/theme-translations": "3.7.0",
|
"@docusaurus/theme-translations": "3.7.0",
|
||||||
"@docusaurus/types": "3.7.0",
|
"@docusaurus/types": "3.7.0",
|
||||||
"@docusaurus/utils-validation": "3.7.0",
|
"@docusaurus/utils-validation": "3.7.0",
|
||||||
"react-waypoint": "^10.3.0",
|
|
||||||
"sharp": "^0.32.3",
|
"sharp": "^0.32.3",
|
||||||
"tslib": "^2.6.0",
|
"tslib": "^2.6.0",
|
||||||
"webpack": "^5.88.1"
|
"webpack": "^5.88.1"
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import React, {Component} from 'react';
|
import React, {Component} from 'react';
|
||||||
// import PropTypes from 'prop-types'
|
import {Waypoint} from './waypoint';
|
||||||
import {Waypoint} from 'react-waypoint';
|
|
||||||
import Media from '../Media';
|
import Media from '../Media';
|
||||||
import {icons, loadStates} from '../constants';
|
import {icons, loadStates} from '../constants';
|
||||||
import {xhrLoader, imageLoader, timeout, combineCancel} from '../loaders';
|
import {xhrLoader, imageLoader, timeout, combineCancel} from '../loaders';
|
||||||
|
|
|
@ -0,0 +1,239 @@
|
||||||
|
/*
|
||||||
|
This is a slimmed down copy of https://github.com/civiccc/react-waypoint
|
||||||
|
The MIT License (MIT)
|
||||||
|
Copyright (c) 2015 Brigade
|
||||||
|
*/
|
||||||
|
|
||||||
|
import React, {createRef, ReactNode} from 'react';
|
||||||
|
|
||||||
|
type ScrollContainer = Window | HTMLElement;
|
||||||
|
|
||||||
|
function addEventListener(
|
||||||
|
element: ScrollContainer,
|
||||||
|
type: 'scroll' | 'resize',
|
||||||
|
listener: () => void,
|
||||||
|
options: AddEventListenerOptions,
|
||||||
|
) {
|
||||||
|
element.addEventListener(type, listener, options);
|
||||||
|
return () => element.removeEventListener(type, listener, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
type Position = 'above' | 'inside' | 'below' | 'invisible';
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
topOffset: number;
|
||||||
|
bottomOffset: number;
|
||||||
|
onEnter: () => void;
|
||||||
|
onLeave: () => void;
|
||||||
|
children: ReactNode;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function Waypoint(props: Props) {
|
||||||
|
return typeof window !== 'undefined' ? (
|
||||||
|
<WaypointClient {...props}>{props.children}</WaypointClient>
|
||||||
|
) : (
|
||||||
|
props.children
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO maybe replace this with IntersectionObserver later?
|
||||||
|
// IntersectionObserver doesn't support the "fast scroll" thing
|
||||||
|
// but it's probably not a big deal
|
||||||
|
class WaypointClient extends React.Component<Props> {
|
||||||
|
static defaultProps = {
|
||||||
|
topOffset: 0,
|
||||||
|
bottomOffset: 0,
|
||||||
|
onEnter() {},
|
||||||
|
onLeave() {},
|
||||||
|
};
|
||||||
|
|
||||||
|
scrollableAncestor?: ScrollContainer;
|
||||||
|
previousPosition: Position | null = null;
|
||||||
|
unsubscribe?: () => void;
|
||||||
|
|
||||||
|
innerRef = createRef<HTMLElement>();
|
||||||
|
|
||||||
|
override componentDidMount() {
|
||||||
|
this.scrollableAncestor = findScrollableAncestor(this.innerRef.current!);
|
||||||
|
|
||||||
|
const unsubscribeScroll = addEventListener(
|
||||||
|
this.scrollableAncestor!,
|
||||||
|
'scroll',
|
||||||
|
this._handleScroll,
|
||||||
|
{passive: true},
|
||||||
|
);
|
||||||
|
|
||||||
|
const unsubscribeResize = addEventListener(
|
||||||
|
window,
|
||||||
|
'resize',
|
||||||
|
this._handleScroll,
|
||||||
|
{passive: true},
|
||||||
|
);
|
||||||
|
|
||||||
|
this.unsubscribe = () => {
|
||||||
|
unsubscribeScroll();
|
||||||
|
unsubscribeResize();
|
||||||
|
};
|
||||||
|
|
||||||
|
this._handleScroll();
|
||||||
|
}
|
||||||
|
|
||||||
|
override componentDidUpdate() {
|
||||||
|
this._handleScroll();
|
||||||
|
}
|
||||||
|
|
||||||
|
override componentWillUnmount() {
|
||||||
|
this.unsubscribe?.();
|
||||||
|
}
|
||||||
|
|
||||||
|
_handleScroll = () => {
|
||||||
|
const node = this.innerRef.current;
|
||||||
|
const {topOffset, bottomOffset, onEnter, onLeave} = this.props;
|
||||||
|
|
||||||
|
const bounds = getBounds({
|
||||||
|
node: node!,
|
||||||
|
scrollableAncestor: this.scrollableAncestor!,
|
||||||
|
topOffset,
|
||||||
|
bottomOffset,
|
||||||
|
});
|
||||||
|
|
||||||
|
const currentPosition = getCurrentPosition(bounds);
|
||||||
|
const previousPosition = this.previousPosition;
|
||||||
|
this.previousPosition = currentPosition;
|
||||||
|
|
||||||
|
if (previousPosition === currentPosition) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentPosition === 'inside') {
|
||||||
|
onEnter();
|
||||||
|
} else if (previousPosition === 'inside') {
|
||||||
|
onLeave();
|
||||||
|
}
|
||||||
|
|
||||||
|
const isRapidScrollDown =
|
||||||
|
previousPosition === 'below' && currentPosition === 'above';
|
||||||
|
const isRapidScrollUp =
|
||||||
|
previousPosition === 'above' && currentPosition === 'below';
|
||||||
|
if (isRapidScrollDown || isRapidScrollUp) {
|
||||||
|
onEnter();
|
||||||
|
onLeave();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
override render() {
|
||||||
|
// @ts-expect-error: fix this implicit API
|
||||||
|
return React.cloneElement(this.props.children, {innerRef: this.innerRef});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Traverses up the DOM to find an ancestor container which has an overflow
|
||||||
|
* style that allows for scrolling.
|
||||||
|
*
|
||||||
|
* @return {Object} the closest ancestor element with an overflow style that
|
||||||
|
* allows for scrolling. If none is found, the `window` object is returned
|
||||||
|
* as a fallback.
|
||||||
|
*/
|
||||||
|
function findScrollableAncestor(inputNode: HTMLElement): ScrollContainer {
|
||||||
|
let node: HTMLElement = inputNode;
|
||||||
|
|
||||||
|
while (node.parentNode) {
|
||||||
|
// @ts-expect-error: it's fine
|
||||||
|
node = node.parentNode!;
|
||||||
|
|
||||||
|
if (node === document.body) {
|
||||||
|
// We've reached all the way to the root node.
|
||||||
|
return window;
|
||||||
|
}
|
||||||
|
|
||||||
|
const style = window.getComputedStyle(node);
|
||||||
|
const overflow =
|
||||||
|
style.getPropertyValue('overflow-y') ||
|
||||||
|
style.getPropertyValue('overflow');
|
||||||
|
|
||||||
|
if (
|
||||||
|
overflow === 'auto' ||
|
||||||
|
overflow === 'scroll' ||
|
||||||
|
overflow === 'overlay'
|
||||||
|
) {
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// A scrollable ancestor element was not found, which means that we need to
|
||||||
|
// do stuff on window.
|
||||||
|
return window;
|
||||||
|
}
|
||||||
|
|
||||||
|
type Bounds = {
|
||||||
|
top: number;
|
||||||
|
bottom: number;
|
||||||
|
viewportTop: number;
|
||||||
|
viewportBottom: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
function getBounds({
|
||||||
|
node,
|
||||||
|
scrollableAncestor,
|
||||||
|
topOffset,
|
||||||
|
bottomOffset,
|
||||||
|
}: {
|
||||||
|
node: Element;
|
||||||
|
scrollableAncestor: ScrollContainer;
|
||||||
|
topOffset: number;
|
||||||
|
bottomOffset: number;
|
||||||
|
}): Bounds {
|
||||||
|
const {top, bottom} = node.getBoundingClientRect();
|
||||||
|
|
||||||
|
let contextHeight;
|
||||||
|
let contextScrollTop;
|
||||||
|
if (scrollableAncestor === window) {
|
||||||
|
contextHeight = window.innerHeight;
|
||||||
|
contextScrollTop = 0;
|
||||||
|
} else {
|
||||||
|
const ancestorElement = scrollableAncestor as HTMLElement;
|
||||||
|
contextHeight = ancestorElement.offsetHeight;
|
||||||
|
contextScrollTop = ancestorElement.getBoundingClientRect().top;
|
||||||
|
}
|
||||||
|
|
||||||
|
const contextBottom = contextScrollTop + contextHeight;
|
||||||
|
|
||||||
|
return {
|
||||||
|
top,
|
||||||
|
bottom,
|
||||||
|
viewportTop: contextScrollTop + topOffset,
|
||||||
|
viewportBottom: contextBottom - bottomOffset,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function getCurrentPosition(bounds: Bounds): Position {
|
||||||
|
if (bounds.viewportBottom - bounds.viewportTop === 0) {
|
||||||
|
return 'invisible';
|
||||||
|
}
|
||||||
|
// top is within the viewport
|
||||||
|
if (bounds.viewportTop <= bounds.top && bounds.top <= bounds.viewportBottom) {
|
||||||
|
return 'inside';
|
||||||
|
}
|
||||||
|
// bottom is within the viewport
|
||||||
|
if (
|
||||||
|
bounds.viewportTop <= bounds.bottom &&
|
||||||
|
bounds.bottom <= bounds.viewportBottom
|
||||||
|
) {
|
||||||
|
return 'inside';
|
||||||
|
}
|
||||||
|
// top is above the viewport and bottom is below the viewport
|
||||||
|
if (
|
||||||
|
bounds.top <= bounds.viewportTop &&
|
||||||
|
bounds.viewportBottom <= bounds.bottom
|
||||||
|
) {
|
||||||
|
return 'inside';
|
||||||
|
}
|
||||||
|
if (bounds.viewportBottom < bounds.top) {
|
||||||
|
return 'below';
|
||||||
|
}
|
||||||
|
if (bounds.top < bounds.viewportTop) {
|
||||||
|
return 'above';
|
||||||
|
}
|
||||||
|
return 'invisible';
|
||||||
|
}
|
19
yarn.lock
19
yarn.lock
|
@ -6249,11 +6249,6 @@ console-control-strings@^1.1.0:
|
||||||
resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e"
|
resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e"
|
||||||
integrity sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==
|
integrity sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==
|
||||||
|
|
||||||
"consolidated-events@^1.1.0 || ^2.0.0":
|
|
||||||
version "2.0.2"
|
|
||||||
resolved "https://registry.yarnpkg.com/consolidated-events/-/consolidated-events-2.0.2.tgz#da8d8f8c2b232831413d9e190dc11669c79f4a91"
|
|
||||||
integrity sha512-2/uRVMdRypf5z/TW/ncD/66l75P5hH2vM/GR8Jf8HLc2xnfJtmina6F6du8+v4Z2vTrMo7jC+W1tmEEuuELgkQ==
|
|
||||||
|
|
||||||
content-disposition@0.5.2:
|
content-disposition@0.5.2:
|
||||||
version "0.5.2"
|
version "0.5.2"
|
||||||
resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.2.tgz#0cf68bb9ddf5f2be7961c3a85178cb85dba78cb4"
|
resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.2.tgz#0cf68bb9ddf5f2be7961c3a85178cb85dba78cb4"
|
||||||
|
@ -14841,7 +14836,7 @@ promzard@^0.3.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
read "1"
|
read "1"
|
||||||
|
|
||||||
prop-types@^15.0.0, prop-types@^15.6.2, prop-types@^15.7.2, prop-types@^15.8.1:
|
prop-types@^15.6.2, prop-types@^15.7.2, prop-types@^15.8.1:
|
||||||
version "15.8.1"
|
version "15.8.1"
|
||||||
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5"
|
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5"
|
||||||
integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==
|
integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==
|
||||||
|
@ -15052,7 +15047,7 @@ react-fast-compare@^3.2.0:
|
||||||
react-fast-compare "^3.2.0"
|
react-fast-compare "^3.2.0"
|
||||||
shallowequal "^1.1.0"
|
shallowequal "^1.1.0"
|
||||||
|
|
||||||
"react-is@^16.12.0 || ^17.0.0 || ^18.0.0", "react-is@^17.0.1 || ^18.0.0", react-is@^18.0.0, react-is@^18.3.1:
|
"react-is@^16.12.0 || ^17.0.0 || ^18.0.0", react-is@^18.0.0, react-is@^18.3.1:
|
||||||
version "18.3.1"
|
version "18.3.1"
|
||||||
resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.3.1.tgz#e83557dc12eae63a99e003a46388b1dcbb44db7e"
|
resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.3.1.tgz#e83557dc12eae63a99e003a46388b1dcbb44db7e"
|
||||||
integrity sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==
|
integrity sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==
|
||||||
|
@ -15152,16 +15147,6 @@ react-test-renderer@^18.0.0:
|
||||||
react-shallow-renderer "^16.15.0"
|
react-shallow-renderer "^16.15.0"
|
||||||
scheduler "^0.23.2"
|
scheduler "^0.23.2"
|
||||||
|
|
||||||
react-waypoint@^10.3.0:
|
|
||||||
version "10.3.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/react-waypoint/-/react-waypoint-10.3.0.tgz#fcc60e86c6c9ad2174fa58d066dc6ae54e3df71d"
|
|
||||||
integrity sha512-iF1y2c1BsoXuEGz08NoahaLFIGI9gTUAAOKip96HUmylRT6DUtpgoBPjk/Y8dfcFVmfVDvUzWjNXpZyKTOV0SQ==
|
|
||||||
dependencies:
|
|
||||||
"@babel/runtime" "^7.12.5"
|
|
||||||
consolidated-events "^1.1.0 || ^2.0.0"
|
|
||||||
prop-types "^15.0.0"
|
|
||||||
react-is "^17.0.1 || ^18.0.0"
|
|
||||||
|
|
||||||
react@16.14.0:
|
react@16.14.0:
|
||||||
version "16.14.0"
|
version "16.14.0"
|
||||||
resolved "https://registry.yarnpkg.com/react/-/react-16.14.0.tgz#94d776ddd0aaa37da3eda8fc5b6b18a4c9a3114d"
|
resolved "https://registry.yarnpkg.com/react/-/react-16.14.0.tgz#94d776ddd0aaa37da3eda8fc5b6b18a4c9a3114d"
|
||||||
|
|
Loading…
Add table
Reference in a new issue