From 01925e2c718f2a570288dcb516357ce3d8f05dcc Mon Sep 17 00:00:00 2001 From: sebastien Date: Wed, 19 Mar 2025 19:18:57 +0100 Subject: [PATCH 01/27] copy waypoint, remove logs --- .../components/IdealImage/index.js | 5 +- .../components/IdealImage/waypoint.js | 337 ++++++++++++++++++ 2 files changed, 339 insertions(+), 3 deletions(-) create mode 100644 packages/docusaurus-plugin-ideal-image/src/theme/IdealImageLegacy/components/IdealImage/waypoint.js diff --git a/packages/docusaurus-plugin-ideal-image/src/theme/IdealImageLegacy/components/IdealImage/index.js b/packages/docusaurus-plugin-ideal-image/src/theme/IdealImageLegacy/components/IdealImage/index.js index 4dfe4a68d9eb..8b32f3bec88b 100644 --- a/packages/docusaurus-plugin-ideal-image/src/theme/IdealImageLegacy/components/IdealImage/index.js +++ b/packages/docusaurus-plugin-ideal-image/src/theme/IdealImageLegacy/components/IdealImage/index.js @@ -1,6 +1,5 @@ -import React, {Component} from 'react'; -// import PropTypes from 'prop-types' -import {Waypoint} from 'react-waypoint'; +import {Component} from 'react'; +import {Waypoint} from './waypoint'; import Media from '../Media'; import {icons, loadStates} from '../constants'; import {xhrLoader, imageLoader, timeout, combineCancel} from '../loaders'; diff --git a/packages/docusaurus-plugin-ideal-image/src/theme/IdealImageLegacy/components/IdealImage/waypoint.js b/packages/docusaurus-plugin-ideal-image/src/theme/IdealImageLegacy/components/IdealImage/waypoint.js new file mode 100644 index 000000000000..11ab750a034a --- /dev/null +++ b/packages/docusaurus-plugin-ideal-image/src/theme/IdealImageLegacy/components/IdealImage/waypoint.js @@ -0,0 +1,337 @@ +import React from 'react'; +import {addEventListener} from 'consolidated-events'; + +import {isForwardRef} from 'react-is'; + +import computeOffsetPixels from './computeOffsetPixels'; +import {INVISIBLE, INSIDE, BELOW, ABOVE} from './constants'; +import ensureRefIsUsedByChild from './ensureRefIsUsedByChild'; +import isDOMElement from './isDOMElement'; +import getCurrentPosition from './getCurrentPosition'; +import onNextTick from './onNextTick'; +import resolveScrollableAncestorProp from './resolveScrollableAncestorProp'; + +const hasWindow = typeof window !== 'undefined'; + +const defaultProps = { + debug: false, + scrollableAncestor: undefined, + children: undefined, + topOffset: '0px', + bottomOffset: '0px', + horizontal: false, + onEnter() {}, + onLeave() {}, + onPositionChange() {}, + fireOnRapidScroll: true, +}; + +// Calls a function when you scroll to the element. +export class Waypoint extends React.PureComponent { + constructor(props) { + super(props); + + this.refElement = (e) => { + this._ref = e; + }; + } + + componentDidMount() { + if (!hasWindow) { + return; + } + + // this._ref may occasionally not be set at this time. To help ensure that + // this works smoothly and to avoid layout thrashing, we want to delay the + // initial execution until the next tick. + this.cancelOnNextTick = onNextTick(() => { + this.cancelOnNextTick = null; + const {children, debug} = this.props; + + // Berofe doing anything, we want to check that this._ref is avaliable in Waypoint + ensureRefIsUsedByChild(children, this._ref); + + this._handleScroll = this._handleScroll.bind(this); + this.scrollableAncestor = this._findScrollableAncestor(); + + this.scrollEventListenerUnsubscribe = addEventListener( + this.scrollableAncestor, + 'scroll', + this._handleScroll, + {passive: true}, + ); + + this.resizeEventListenerUnsubscribe = addEventListener( + window, + 'resize', + this._handleScroll, + {passive: true}, + ); + + this._handleScroll(null); + }); + } + + componentDidUpdate() { + if (!hasWindow) { + return; + } + + if (!this.scrollableAncestor) { + // The Waypoint has not yet initialized. + return; + } + + // The element may have moved, so we need to recompute its position on the + // page. This happens via handleScroll in a way that forces layout to be + // computed. + // + // We want this to be deferred to avoid forcing layout during render, which + // causes layout thrashing. And, if we already have this work enqueued, we + // can just wait for that to happen instead of enqueueing again. + if (this.cancelOnNextTick) { + return; + } + + this.cancelOnNextTick = onNextTick(() => { + this.cancelOnNextTick = null; + this._handleScroll(null); + }); + } + + componentWillUnmount() { + if (!hasWindow) { + return; + } + + if (this.scrollEventListenerUnsubscribe) { + this.scrollEventListenerUnsubscribe(); + } + if (this.resizeEventListenerUnsubscribe) { + this.resizeEventListenerUnsubscribe(); + } + + if (this.cancelOnNextTick) { + this.cancelOnNextTick(); + } + } + + /** + * 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. + */ + _findScrollableAncestor() { + const {horizontal, scrollableAncestor} = this.props; + + if (scrollableAncestor) { + return resolveScrollableAncestorProp(scrollableAncestor); + } + + let node = this._ref; + + while (node.parentNode) { + 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 overflowDirec = horizontal + ? style.getPropertyValue('overflow-x') + : style.getPropertyValue('overflow-y'); + const overflow = overflowDirec || 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; + } + + /** + * @param {Object} event the native scroll event coming from the scrollable + * ancestor, or resize event coming from the window. Will be undefined if + * called by a React lifecyle method + */ + _handleScroll(event) { + if (!this._ref) { + // There's a chance we end up here after the component has been unmounted. + return; + } + + const bounds = this._getBounds(); + const currentPosition = getCurrentPosition(bounds); + const previousPosition = this._previousPosition; + const {debug, onPositionChange, onEnter, onLeave, fireOnRapidScroll} = + this.props; + + // Save previous position as early as possible to prevent cycles + this._previousPosition = currentPosition; + + if (previousPosition === currentPosition) { + // No change since last trigger + return; + } + + const callbackArg = { + currentPosition, + previousPosition, + event, + waypointTop: bounds.waypointTop, + waypointBottom: bounds.waypointBottom, + viewportTop: bounds.viewportTop, + viewportBottom: bounds.viewportBottom, + }; + onPositionChange.call(this, callbackArg); + + if (currentPosition === INSIDE) { + onEnter.call(this, callbackArg); + } else if (previousPosition === INSIDE) { + onLeave.call(this, callbackArg); + } + + const isRapidScrollDown = + previousPosition === BELOW && currentPosition === ABOVE; + const isRapidScrollUp = + previousPosition === ABOVE && currentPosition === BELOW; + + if (fireOnRapidScroll && (isRapidScrollDown || isRapidScrollUp)) { + // If the scroll event isn't fired often enough to occur while the + // waypoint was visible, we trigger both callbacks anyway. + onEnter.call(this, { + currentPosition: INSIDE, + previousPosition, + event, + waypointTop: bounds.waypointTop, + waypointBottom: bounds.waypointBottom, + viewportTop: bounds.viewportTop, + viewportBottom: bounds.viewportBottom, + }); + onLeave.call(this, { + currentPosition, + previousPosition: INSIDE, + event, + waypointTop: bounds.waypointTop, + waypointBottom: bounds.waypointBottom, + viewportTop: bounds.viewportTop, + viewportBottom: bounds.viewportBottom, + }); + } + } + + _getBounds() { + const {horizontal, debug} = this.props; + const {left, top, right, bottom} = this._ref.getBoundingClientRect(); + const waypointTop = horizontal ? left : top; + const waypointBottom = horizontal ? right : bottom; + + let contextHeight; + let contextScrollTop; + if (this.scrollableAncestor === window) { + contextHeight = horizontal ? window.innerWidth : window.innerHeight; + contextScrollTop = 0; + } else { + contextHeight = horizontal + ? this.scrollableAncestor.offsetWidth + : this.scrollableAncestor.offsetHeight; + contextScrollTop = horizontal + ? this.scrollableAncestor.getBoundingClientRect().left + : this.scrollableAncestor.getBoundingClientRect().top; + } + + const {bottomOffset, topOffset} = this.props; + const topOffsetPx = computeOffsetPixels(topOffset, contextHeight); + const bottomOffsetPx = computeOffsetPixels(bottomOffset, contextHeight); + const contextBottom = contextScrollTop + contextHeight; + + return { + waypointTop, + waypointBottom, + viewportTop: contextScrollTop + topOffsetPx, + viewportBottom: contextBottom - bottomOffsetPx, + }; + } + + /** + * @return {Object} + */ + render() { + const {children} = this.props; + + if (!children) { + // We need an element that we can locate in the DOM to determine where it is + // rendered relative to the top of its context. + return ; + } + + if (isDOMElement(children) || isForwardRef(children)) { + const ref = (node) => { + this.refElement(node); + if (children.ref) { + if (typeof children.ref === 'function') { + children.ref(node); + } else { + children.ref.current = node; + } + } + }; + + return React.cloneElement(children, {ref}); + } + + return React.cloneElement(children, {innerRef: this.refElement}); + } +} + +if (process.env.NODE_ENV !== 'production') { + Waypoint.propTypes = { + children: PropTypes.element, + debug: PropTypes.bool, + onEnter: PropTypes.func, + onLeave: PropTypes.func, + onPositionChange: PropTypes.func, + fireOnRapidScroll: PropTypes.bool, + // eslint-disable-next-line react/forbid-prop-types + scrollableAncestor: PropTypes.any, + horizontal: PropTypes.bool, + + // `topOffset` can either be a number, in which case its a distance from the + // top of the container in pixels, or a string value. Valid string values are + // of the form "20px", which is parsed as pixels, or "20%", which is parsed + // as a percentage of the height of the containing element. + // For instance, if you pass "-20%", and the containing element is 100px tall, + // then the waypoint will be triggered when it has been scrolled 20px beyond + // the top of the containing element. + topOffset: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), + + // `bottomOffset` can either be a number, in which case its a distance from the + // bottom of the container in pixels, or a string value. Valid string values are + // of the form "20px", which is parsed as pixels, or "20%", which is parsed + // as a percentage of the height of the containing element. + // For instance, if you pass "20%", and the containing element is 100px tall, + // then the waypoint will be triggered when it has been scrolled 20px beyond + // the bottom of the containing element. + // Similar to `topOffset`, but for the bottom of the container. + bottomOffset: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), + }; +} + +Waypoint.above = ABOVE; +Waypoint.below = BELOW; +Waypoint.inside = INSIDE; +Waypoint.invisible = INVISIBLE; +Waypoint.defaultProps = defaultProps; +Waypoint.displayName = 'Waypoint'; From ee3dc2b00498abb2cb918c43e10e26db6790a468 Mon Sep 17 00:00:00 2001 From: sebastien Date: Wed, 19 Mar 2025 19:20:19 +0100 Subject: [PATCH 02/27] remove propTypes --- .../components/IdealImage/waypoint.js | 33 ------------------- 1 file changed, 33 deletions(-) diff --git a/packages/docusaurus-plugin-ideal-image/src/theme/IdealImageLegacy/components/IdealImage/waypoint.js b/packages/docusaurus-plugin-ideal-image/src/theme/IdealImageLegacy/components/IdealImage/waypoint.js index 11ab750a034a..1e62dc4dde0b 100644 --- a/packages/docusaurus-plugin-ideal-image/src/theme/IdealImageLegacy/components/IdealImage/waypoint.js +++ b/packages/docusaurus-plugin-ideal-image/src/theme/IdealImageLegacy/components/IdealImage/waypoint.js @@ -296,39 +296,6 @@ export class Waypoint extends React.PureComponent { } } -if (process.env.NODE_ENV !== 'production') { - Waypoint.propTypes = { - children: PropTypes.element, - debug: PropTypes.bool, - onEnter: PropTypes.func, - onLeave: PropTypes.func, - onPositionChange: PropTypes.func, - fireOnRapidScroll: PropTypes.bool, - // eslint-disable-next-line react/forbid-prop-types - scrollableAncestor: PropTypes.any, - horizontal: PropTypes.bool, - - // `topOffset` can either be a number, in which case its a distance from the - // top of the container in pixels, or a string value. Valid string values are - // of the form "20px", which is parsed as pixels, or "20%", which is parsed - // as a percentage of the height of the containing element. - // For instance, if you pass "-20%", and the containing element is 100px tall, - // then the waypoint will be triggered when it has been scrolled 20px beyond - // the top of the containing element. - topOffset: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), - - // `bottomOffset` can either be a number, in which case its a distance from the - // bottom of the container in pixels, or a string value. Valid string values are - // of the form "20px", which is parsed as pixels, or "20%", which is parsed - // as a percentage of the height of the containing element. - // For instance, if you pass "20%", and the containing element is 100px tall, - // then the waypoint will be triggered when it has been scrolled 20px beyond - // the bottom of the containing element. - // Similar to `topOffset`, but for the bottom of the container. - bottomOffset: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), - }; -} - Waypoint.above = ABOVE; Waypoint.below = BELOW; Waypoint.inside = INSIDE; From aaaacebe3e320a7663c57573edb57c7b6dd33fd7 Mon Sep 17 00:00:00 2001 From: sebastien Date: Wed, 19 Mar 2025 19:21:01 +0100 Subject: [PATCH 03/27] remove debug --- .../IdealImageLegacy/components/IdealImage/waypoint.js | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/packages/docusaurus-plugin-ideal-image/src/theme/IdealImageLegacy/components/IdealImage/waypoint.js b/packages/docusaurus-plugin-ideal-image/src/theme/IdealImageLegacy/components/IdealImage/waypoint.js index 1e62dc4dde0b..025e88d175f8 100644 --- a/packages/docusaurus-plugin-ideal-image/src/theme/IdealImageLegacy/components/IdealImage/waypoint.js +++ b/packages/docusaurus-plugin-ideal-image/src/theme/IdealImageLegacy/components/IdealImage/waypoint.js @@ -14,7 +14,6 @@ import resolveScrollableAncestorProp from './resolveScrollableAncestorProp'; const hasWindow = typeof window !== 'undefined'; const defaultProps = { - debug: false, scrollableAncestor: undefined, children: undefined, topOffset: '0px', @@ -46,7 +45,7 @@ export class Waypoint extends React.PureComponent { // initial execution until the next tick. this.cancelOnNextTick = onNextTick(() => { this.cancelOnNextTick = null; - const {children, debug} = this.props; + const {children} = this.props; // Berofe doing anything, we want to check that this._ref is avaliable in Waypoint ensureRefIsUsedByChild(children, this._ref); @@ -175,8 +174,7 @@ export class Waypoint extends React.PureComponent { const bounds = this._getBounds(); const currentPosition = getCurrentPosition(bounds); const previousPosition = this._previousPosition; - const {debug, onPositionChange, onEnter, onLeave, fireOnRapidScroll} = - this.props; + const {onPositionChange, onEnter, onLeave, fireOnRapidScroll} = this.props; // Save previous position as early as possible to prevent cycles this._previousPosition = currentPosition; @@ -233,7 +231,7 @@ export class Waypoint extends React.PureComponent { } _getBounds() { - const {horizontal, debug} = this.props; + const {horizontal} = this.props; const {left, top, right, bottom} = this._ref.getBoundingClientRect(); const waypointTop = horizontal ? left : top; const waypointBottom = horizontal ? right : bottom; From b115356f3d88096b660df6fe2614bd1c22f75427 Mon Sep 17 00:00:00 2001 From: sebastien Date: Wed, 19 Mar 2025 19:22:48 +0100 Subject: [PATCH 04/27] remove scrollableAncestor prop --- .../IdealImageLegacy/components/IdealImage/waypoint.js | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/packages/docusaurus-plugin-ideal-image/src/theme/IdealImageLegacy/components/IdealImage/waypoint.js b/packages/docusaurus-plugin-ideal-image/src/theme/IdealImageLegacy/components/IdealImage/waypoint.js index 025e88d175f8..63b44607d30c 100644 --- a/packages/docusaurus-plugin-ideal-image/src/theme/IdealImageLegacy/components/IdealImage/waypoint.js +++ b/packages/docusaurus-plugin-ideal-image/src/theme/IdealImageLegacy/components/IdealImage/waypoint.js @@ -9,12 +9,10 @@ import ensureRefIsUsedByChild from './ensureRefIsUsedByChild'; import isDOMElement from './isDOMElement'; import getCurrentPosition from './getCurrentPosition'; import onNextTick from './onNextTick'; -import resolveScrollableAncestorProp from './resolveScrollableAncestorProp'; const hasWindow = typeof window !== 'undefined'; const defaultProps = { - scrollableAncestor: undefined, children: undefined, topOffset: '0px', bottomOffset: '0px', @@ -124,11 +122,7 @@ export class Waypoint extends React.PureComponent { * as a fallback. */ _findScrollableAncestor() { - const {horizontal, scrollableAncestor} = this.props; - - if (scrollableAncestor) { - return resolveScrollableAncestorProp(scrollableAncestor); - } + const {horizontal} = this.props; let node = this._ref; From 6b363942132cae28913b77284bc7ba2ea182ad7e Mon Sep 17 00:00:00 2001 From: sebastien Date: Wed, 19 Mar 2025 19:24:20 +0100 Subject: [PATCH 05/27] remove onPositionChange --- .../theme/IdealImageLegacy/components/IdealImage/waypoint.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/docusaurus-plugin-ideal-image/src/theme/IdealImageLegacy/components/IdealImage/waypoint.js b/packages/docusaurus-plugin-ideal-image/src/theme/IdealImageLegacy/components/IdealImage/waypoint.js index 63b44607d30c..a674ef433e34 100644 --- a/packages/docusaurus-plugin-ideal-image/src/theme/IdealImageLegacy/components/IdealImage/waypoint.js +++ b/packages/docusaurus-plugin-ideal-image/src/theme/IdealImageLegacy/components/IdealImage/waypoint.js @@ -19,7 +19,6 @@ const defaultProps = { horizontal: false, onEnter() {}, onLeave() {}, - onPositionChange() {}, fireOnRapidScroll: true, }; @@ -168,7 +167,7 @@ export class Waypoint extends React.PureComponent { const bounds = this._getBounds(); const currentPosition = getCurrentPosition(bounds); const previousPosition = this._previousPosition; - const {onPositionChange, onEnter, onLeave, fireOnRapidScroll} = this.props; + const {onEnter, onLeave, fireOnRapidScroll} = this.props; // Save previous position as early as possible to prevent cycles this._previousPosition = currentPosition; @@ -187,7 +186,6 @@ export class Waypoint extends React.PureComponent { viewportTop: bounds.viewportTop, viewportBottom: bounds.viewportBottom, }; - onPositionChange.call(this, callbackArg); if (currentPosition === INSIDE) { onEnter.call(this, callbackArg); From 5761dbc0e1290dd2a973336cea2532b02e32f558 Mon Sep 17 00:00:00 2001 From: sebastien Date: Wed, 19 Mar 2025 19:25:23 +0100 Subject: [PATCH 06/27] remove horizontal prop --- .../components/IdealImage/waypoint.js | 22 +++++-------------- 1 file changed, 6 insertions(+), 16 deletions(-) diff --git a/packages/docusaurus-plugin-ideal-image/src/theme/IdealImageLegacy/components/IdealImage/waypoint.js b/packages/docusaurus-plugin-ideal-image/src/theme/IdealImageLegacy/components/IdealImage/waypoint.js index a674ef433e34..dcca62590af4 100644 --- a/packages/docusaurus-plugin-ideal-image/src/theme/IdealImageLegacy/components/IdealImage/waypoint.js +++ b/packages/docusaurus-plugin-ideal-image/src/theme/IdealImageLegacy/components/IdealImage/waypoint.js @@ -16,7 +16,6 @@ const defaultProps = { children: undefined, topOffset: '0px', bottomOffset: '0px', - horizontal: false, onEnter() {}, onLeave() {}, fireOnRapidScroll: true, @@ -121,8 +120,6 @@ export class Waypoint extends React.PureComponent { * as a fallback. */ _findScrollableAncestor() { - const {horizontal} = this.props; - let node = this._ref; while (node.parentNode) { @@ -134,9 +131,7 @@ export class Waypoint extends React.PureComponent { } const style = window.getComputedStyle(node); - const overflowDirec = horizontal - ? style.getPropertyValue('overflow-x') - : style.getPropertyValue('overflow-y'); + const overflowDirec = style.getPropertyValue('overflow-y'); const overflow = overflowDirec || style.getPropertyValue('overflow'); if ( @@ -223,23 +218,18 @@ export class Waypoint extends React.PureComponent { } _getBounds() { - const {horizontal} = this.props; const {left, top, right, bottom} = this._ref.getBoundingClientRect(); - const waypointTop = horizontal ? left : top; - const waypointBottom = horizontal ? right : bottom; + const waypointTop = top; + const waypointBottom = bottom; let contextHeight; let contextScrollTop; if (this.scrollableAncestor === window) { - contextHeight = horizontal ? window.innerWidth : window.innerHeight; + contextHeight = window.innerHeight; contextScrollTop = 0; } else { - contextHeight = horizontal - ? this.scrollableAncestor.offsetWidth - : this.scrollableAncestor.offsetHeight; - contextScrollTop = horizontal - ? this.scrollableAncestor.getBoundingClientRect().left - : this.scrollableAncestor.getBoundingClientRect().top; + contextHeight = this.scrollableAncestor.offsetHeight; + contextScrollTop = this.scrollableAncestor.getBoundingClientRect().top; } const {bottomOffset, topOffset} = this.props; From ed8ff4553845d07a08ccbd4ac82a665c0b276bb8 Mon Sep 17 00:00:00 2001 From: sebastien Date: Wed, 19 Mar 2025 19:28:19 +0100 Subject: [PATCH 07/27] remove fireOnRapidScroll --- .../theme/IdealImageLegacy/components/IdealImage/waypoint.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/docusaurus-plugin-ideal-image/src/theme/IdealImageLegacy/components/IdealImage/waypoint.js b/packages/docusaurus-plugin-ideal-image/src/theme/IdealImageLegacy/components/IdealImage/waypoint.js index dcca62590af4..d4da67569aee 100644 --- a/packages/docusaurus-plugin-ideal-image/src/theme/IdealImageLegacy/components/IdealImage/waypoint.js +++ b/packages/docusaurus-plugin-ideal-image/src/theme/IdealImageLegacy/components/IdealImage/waypoint.js @@ -18,7 +18,6 @@ const defaultProps = { bottomOffset: '0px', onEnter() {}, onLeave() {}, - fireOnRapidScroll: true, }; // Calls a function when you scroll to the element. @@ -162,7 +161,7 @@ export class Waypoint extends React.PureComponent { const bounds = this._getBounds(); const currentPosition = getCurrentPosition(bounds); const previousPosition = this._previousPosition; - const {onEnter, onLeave, fireOnRapidScroll} = this.props; + const {onEnter, onLeave} = this.props; // Save previous position as early as possible to prevent cycles this._previousPosition = currentPosition; @@ -193,7 +192,7 @@ export class Waypoint extends React.PureComponent { const isRapidScrollUp = previousPosition === ABOVE && currentPosition === BELOW; - if (fireOnRapidScroll && (isRapidScrollDown || isRapidScrollUp)) { + if (isRapidScrollDown || isRapidScrollUp) { // If the scroll event isn't fired often enough to occur while the // waypoint was visible, we trigger both callbacks anyway. onEnter.call(this, { From 8907c0c04ea608b0f504b16dcfeeabad1382d94b Mon Sep 17 00:00:00 2001 From: sebastien Date: Wed, 19 Mar 2025 19:37:21 +0100 Subject: [PATCH 08/27] remove useless render code --- .../components/IdealImage/waypoint.js | 31 +------------------ 1 file changed, 1 insertion(+), 30 deletions(-) diff --git a/packages/docusaurus-plugin-ideal-image/src/theme/IdealImageLegacy/components/IdealImage/waypoint.js b/packages/docusaurus-plugin-ideal-image/src/theme/IdealImageLegacy/components/IdealImage/waypoint.js index d4da67569aee..8bfc0550a3f8 100644 --- a/packages/docusaurus-plugin-ideal-image/src/theme/IdealImageLegacy/components/IdealImage/waypoint.js +++ b/packages/docusaurus-plugin-ideal-image/src/theme/IdealImageLegacy/components/IdealImage/waypoint.js @@ -1,12 +1,9 @@ import React from 'react'; import {addEventListener} from 'consolidated-events'; -import {isForwardRef} from 'react-is'; - import computeOffsetPixels from './computeOffsetPixels'; import {INVISIBLE, INSIDE, BELOW, ABOVE} from './constants'; import ensureRefIsUsedByChild from './ensureRefIsUsedByChild'; -import isDOMElement from './isDOMElement'; import getCurrentPosition from './getCurrentPosition'; import onNextTick from './onNextTick'; @@ -244,34 +241,8 @@ export class Waypoint extends React.PureComponent { }; } - /** - * @return {Object} - */ render() { - const {children} = this.props; - - if (!children) { - // We need an element that we can locate in the DOM to determine where it is - // rendered relative to the top of its context. - return ; - } - - if (isDOMElement(children) || isForwardRef(children)) { - const ref = (node) => { - this.refElement(node); - if (children.ref) { - if (typeof children.ref === 'function') { - children.ref(node); - } else { - children.ref.current = node; - } - } - }; - - return React.cloneElement(children, {ref}); - } - - return React.cloneElement(children, {innerRef: this.refElement}); + return React.cloneElement(this.props.children, {innerRef: this.refElement}); } } From 2427131aaa0a59ccc7d10aeed38eb721221b41c0 Mon Sep 17 00:00:00 2001 From: sebastien Date: Wed, 19 Mar 2025 19:40:35 +0100 Subject: [PATCH 09/27] remove ensureRefIsUsedByChild --- .../theme/IdealImageLegacy/components/IdealImage/waypoint.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/packages/docusaurus-plugin-ideal-image/src/theme/IdealImageLegacy/components/IdealImage/waypoint.js b/packages/docusaurus-plugin-ideal-image/src/theme/IdealImageLegacy/components/IdealImage/waypoint.js index 8bfc0550a3f8..040bd21d4aeb 100644 --- a/packages/docusaurus-plugin-ideal-image/src/theme/IdealImageLegacy/components/IdealImage/waypoint.js +++ b/packages/docusaurus-plugin-ideal-image/src/theme/IdealImageLegacy/components/IdealImage/waypoint.js @@ -3,7 +3,6 @@ import {addEventListener} from 'consolidated-events'; import computeOffsetPixels from './computeOffsetPixels'; import {INVISIBLE, INSIDE, BELOW, ABOVE} from './constants'; -import ensureRefIsUsedByChild from './ensureRefIsUsedByChild'; import getCurrentPosition from './getCurrentPosition'; import onNextTick from './onNextTick'; @@ -39,9 +38,6 @@ export class Waypoint extends React.PureComponent { this.cancelOnNextTick = null; const {children} = this.props; - // Berofe doing anything, we want to check that this._ref is avaliable in Waypoint - ensureRefIsUsedByChild(children, this._ref); - this._handleScroll = this._handleScroll.bind(this); this.scrollableAncestor = this._findScrollableAncestor(); From 38ebdb6a78cd5414da094a87884bb5f78c8c3dc2 Mon Sep 17 00:00:00 2001 From: sebastien Date: Wed, 19 Mar 2025 19:40:49 +0100 Subject: [PATCH 10/27] remove children prop --- .../src/theme/IdealImageLegacy/components/IdealImage/waypoint.js | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/docusaurus-plugin-ideal-image/src/theme/IdealImageLegacy/components/IdealImage/waypoint.js b/packages/docusaurus-plugin-ideal-image/src/theme/IdealImageLegacy/components/IdealImage/waypoint.js index 040bd21d4aeb..0a7d9bc93937 100644 --- a/packages/docusaurus-plugin-ideal-image/src/theme/IdealImageLegacy/components/IdealImage/waypoint.js +++ b/packages/docusaurus-plugin-ideal-image/src/theme/IdealImageLegacy/components/IdealImage/waypoint.js @@ -9,7 +9,6 @@ import onNextTick from './onNextTick'; const hasWindow = typeof window !== 'undefined'; const defaultProps = { - children: undefined, topOffset: '0px', bottomOffset: '0px', onEnter() {}, From cc76406d7f5804085a511e833843f70d1c3a7581 Mon Sep 17 00:00:00 2001 From: sebastien Date: Wed, 19 Mar 2025 19:41:15 +0100 Subject: [PATCH 11/27] inline constants --- .../IdealImageLegacy/components/IdealImage/waypoint.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/docusaurus-plugin-ideal-image/src/theme/IdealImageLegacy/components/IdealImage/waypoint.js b/packages/docusaurus-plugin-ideal-image/src/theme/IdealImageLegacy/components/IdealImage/waypoint.js index 0a7d9bc93937..ea9ff8345088 100644 --- a/packages/docusaurus-plugin-ideal-image/src/theme/IdealImageLegacy/components/IdealImage/waypoint.js +++ b/packages/docusaurus-plugin-ideal-image/src/theme/IdealImageLegacy/components/IdealImage/waypoint.js @@ -2,10 +2,14 @@ import React from 'react'; import {addEventListener} from 'consolidated-events'; import computeOffsetPixels from './computeOffsetPixels'; -import {INVISIBLE, INSIDE, BELOW, ABOVE} from './constants'; import getCurrentPosition from './getCurrentPosition'; import onNextTick from './onNextTick'; +const ABOVE = 'above'; +const INSIDE = 'inside'; +const BELOW = 'below'; +const INVISIBLE = 'invisible'; + const hasWindow = typeof window !== 'undefined'; const defaultProps = { From 87b9057b14e3ca330a8acb2265bc7a18b49a8784 Mon Sep 17 00:00:00 2001 From: sebastien Date: Wed, 19 Mar 2025 19:48:04 +0100 Subject: [PATCH 12/27] remove consolidated-events --- .../IdealImageLegacy/components/IdealImage/waypoint.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/packages/docusaurus-plugin-ideal-image/src/theme/IdealImageLegacy/components/IdealImage/waypoint.js b/packages/docusaurus-plugin-ideal-image/src/theme/IdealImageLegacy/components/IdealImage/waypoint.js index ea9ff8345088..34e840362a64 100644 --- a/packages/docusaurus-plugin-ideal-image/src/theme/IdealImageLegacy/components/IdealImage/waypoint.js +++ b/packages/docusaurus-plugin-ideal-image/src/theme/IdealImageLegacy/components/IdealImage/waypoint.js @@ -1,10 +1,16 @@ import React from 'react'; -import {addEventListener} from 'consolidated-events'; import computeOffsetPixels from './computeOffsetPixels'; import getCurrentPosition from './getCurrentPosition'; import onNextTick from './onNextTick'; +// Same API as https://github.com/lencioni/consolidated-events +// But removing the behavior that we don't need +function addEventListener(element, type, listener, options) { + element.addEventListener(type, listener, options); + return () => element.removeEventListener(type, listener, options); +} + const ABOVE = 'above'; const INSIDE = 'inside'; const BELOW = 'below'; From a32e41fa384599e72d20ee34c6d9b60e515a2a5a Mon Sep 17 00:00:00 2001 From: sebastien Date: Wed, 19 Mar 2025 19:50:25 +0100 Subject: [PATCH 13/27] copy getCurrentPosition --- .../components/IdealImage/waypoint.js | 42 ++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/packages/docusaurus-plugin-ideal-image/src/theme/IdealImageLegacy/components/IdealImage/waypoint.js b/packages/docusaurus-plugin-ideal-image/src/theme/IdealImageLegacy/components/IdealImage/waypoint.js index 34e840362a64..43891e540495 100644 --- a/packages/docusaurus-plugin-ideal-image/src/theme/IdealImageLegacy/components/IdealImage/waypoint.js +++ b/packages/docusaurus-plugin-ideal-image/src/theme/IdealImageLegacy/components/IdealImage/waypoint.js @@ -1,7 +1,6 @@ import React from 'react'; import computeOffsetPixels from './computeOffsetPixels'; -import getCurrentPosition from './getCurrentPosition'; import onNextTick from './onNextTick'; // Same API as https://github.com/lencioni/consolidated-events @@ -257,3 +256,44 @@ Waypoint.inside = INSIDE; Waypoint.invisible = INVISIBLE; Waypoint.defaultProps = defaultProps; Waypoint.displayName = 'Waypoint'; + +// Copy https://github.com/civiccc/react-waypoint/blob/master/src/getCurrentPosition.js +function getCurrentPosition(bounds) { + if (bounds.viewportBottom - bounds.viewportTop === 0) { + return INVISIBLE; + } + + // top is within the viewport + if ( + bounds.viewportTop <= bounds.waypointTop && + bounds.waypointTop <= bounds.viewportBottom + ) { + return INSIDE; + } + + // bottom is within the viewport + if ( + bounds.viewportTop <= bounds.waypointBottom && + bounds.waypointBottom <= bounds.viewportBottom + ) { + return INSIDE; + } + + // top is above the viewport and bottom is below the viewport + if ( + bounds.waypointTop <= bounds.viewportTop && + bounds.viewportBottom <= bounds.waypointBottom + ) { + return INSIDE; + } + + if (bounds.viewportBottom < bounds.waypointTop) { + return BELOW; + } + + if (bounds.waypointTop < bounds.viewportTop) { + return ABOVE; + } + + return INVISIBLE; +} From 0ecc96468a9a66639fc4884cc2cfd71d8bc4b7f5 Mon Sep 17 00:00:00 2001 From: sebastien Date: Wed, 19 Mar 2025 19:53:03 +0100 Subject: [PATCH 14/27] remove computeOffsetPixels --- .../components/IdealImage/waypoint.js | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/packages/docusaurus-plugin-ideal-image/src/theme/IdealImageLegacy/components/IdealImage/waypoint.js b/packages/docusaurus-plugin-ideal-image/src/theme/IdealImageLegacy/components/IdealImage/waypoint.js index 43891e540495..0ca3140d1eb8 100644 --- a/packages/docusaurus-plugin-ideal-image/src/theme/IdealImageLegacy/components/IdealImage/waypoint.js +++ b/packages/docusaurus-plugin-ideal-image/src/theme/IdealImageLegacy/components/IdealImage/waypoint.js @@ -1,6 +1,5 @@ import React from 'react'; -import computeOffsetPixels from './computeOffsetPixels'; import onNextTick from './onNextTick'; // Same API as https://github.com/lencioni/consolidated-events @@ -18,8 +17,8 @@ const INVISIBLE = 'invisible'; const hasWindow = typeof window !== 'undefined'; const defaultProps = { - topOffset: '0px', - bottomOffset: '0px', + topOffset: 0, + bottomOffset: 0, onEnter() {}, onLeave() {}, }; @@ -233,15 +232,13 @@ export class Waypoint extends React.PureComponent { } const {bottomOffset, topOffset} = this.props; - const topOffsetPx = computeOffsetPixels(topOffset, contextHeight); - const bottomOffsetPx = computeOffsetPixels(bottomOffset, contextHeight); const contextBottom = contextScrollTop + contextHeight; return { waypointTop, waypointBottom, - viewportTop: contextScrollTop + topOffsetPx, - viewportBottom: contextBottom - bottomOffsetPx, + viewportTop: contextScrollTop + topOffset, + viewportBottom: contextBottom - bottomOffset, }; } From 85c73cc16a158fbd2d125aa05fe3510776fe043e Mon Sep 17 00:00:00 2001 From: sebastien Date: Thu, 20 Mar 2025 09:48:13 +0100 Subject: [PATCH 15/27] extract findScrollableAncestor --- .../components/IdealImage/waypoint.js | 82 +++++++++---------- 1 file changed, 41 insertions(+), 41 deletions(-) diff --git a/packages/docusaurus-plugin-ideal-image/src/theme/IdealImageLegacy/components/IdealImage/waypoint.js b/packages/docusaurus-plugin-ideal-image/src/theme/IdealImageLegacy/components/IdealImage/waypoint.js index 0ca3140d1eb8..c352772a42e8 100644 --- a/packages/docusaurus-plugin-ideal-image/src/theme/IdealImageLegacy/components/IdealImage/waypoint.js +++ b/packages/docusaurus-plugin-ideal-image/src/theme/IdealImageLegacy/components/IdealImage/waypoint.js @@ -45,8 +45,7 @@ export class Waypoint extends React.PureComponent { this.cancelOnNextTick = null; const {children} = this.props; - this._handleScroll = this._handleScroll.bind(this); - this.scrollableAncestor = this._findScrollableAncestor(); + this.scrollableAncestor = findScrollableAncestor(this._ref); this.scrollEventListenerUnsubscribe = addEventListener( this.scrollableAncestor, @@ -110,49 +109,12 @@ export class Waypoint extends React.PureComponent { } } - /** - * 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. - */ - _findScrollableAncestor() { - let node = this._ref; - - while (node.parentNode) { - 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 overflowDirec = style.getPropertyValue('overflow-y'); - const overflow = overflowDirec || 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; - } - /** * @param {Object} event the native scroll event coming from the scrollable * ancestor, or resize event coming from the window. Will be undefined if * called by a React lifecyle method */ - _handleScroll(event) { + _handleScroll = (event) => { if (!this._ref) { // There's a chance we end up here after the component has been unmounted. return; @@ -214,7 +176,7 @@ export class Waypoint extends React.PureComponent { viewportBottom: bounds.viewportBottom, }); } - } + }; _getBounds() { const {left, top, right, bottom} = this._ref.getBoundingClientRect(); @@ -294,3 +256,41 @@ function getCurrentPosition(bounds) { return INVISIBLE; } + +/** + * 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) { + let node = inputNode; + + while (node.parentNode) { + 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; +} From d3177a63163d4b7eb7d329b8fbf66674251a57d3 Mon Sep 17 00:00:00 2001 From: sebastien Date: Thu, 20 Mar 2025 10:04:15 +0100 Subject: [PATCH 16/27] extract getBounds --- .../components/IdealImage/waypoint.js | 76 ++++++++++--------- 1 file changed, 40 insertions(+), 36 deletions(-) diff --git a/packages/docusaurus-plugin-ideal-image/src/theme/IdealImageLegacy/components/IdealImage/waypoint.js b/packages/docusaurus-plugin-ideal-image/src/theme/IdealImageLegacy/components/IdealImage/waypoint.js index c352772a42e8..09ed95ffd9fc 100644 --- a/packages/docusaurus-plugin-ideal-image/src/theme/IdealImageLegacy/components/IdealImage/waypoint.js +++ b/packages/docusaurus-plugin-ideal-image/src/theme/IdealImageLegacy/components/IdealImage/waypoint.js @@ -1,4 +1,4 @@ -import React from 'react'; +import React, {createRef} from 'react'; import onNextTick from './onNextTick'; @@ -25,12 +25,10 @@ const defaultProps = { // Calls a function when you scroll to the element. export class Waypoint extends React.PureComponent { + innerRef = createRef(); + constructor(props) { super(props); - - this.refElement = (e) => { - this._ref = e; - }; } componentDidMount() { @@ -38,14 +36,14 @@ export class Waypoint extends React.PureComponent { return; } - // this._ref may occasionally not be set at this time. To help ensure that + // this.innerRef may occasionally not be set at this time. To help ensure that // this works smoothly and to avoid layout thrashing, we want to delay the // initial execution until the next tick. this.cancelOnNextTick = onNextTick(() => { this.cancelOnNextTick = null; const {children} = this.props; - this.scrollableAncestor = findScrollableAncestor(this._ref); + this.scrollableAncestor = findScrollableAncestor(this.innerRef.current); this.scrollEventListenerUnsubscribe = addEventListener( this.scrollableAncestor, @@ -115,12 +113,19 @@ export class Waypoint extends React.PureComponent { * called by a React lifecyle method */ _handleScroll = (event) => { - if (!this._ref) { + const node = this.innerRef.current; + if (!node) { // There's a chance we end up here after the component has been unmounted. return; } - const bounds = this._getBounds(); + const bounds = getBounds({ + node, + scrollableAncestor: this.scrollableAncestor, + topOffset: props.topOffset, + bottomOffset: props.bottomOffset, + }); + const currentPosition = getCurrentPosition(bounds); const previousPosition = this._previousPosition; const {onEnter, onLeave} = this.props; @@ -178,34 +183,8 @@ export class Waypoint extends React.PureComponent { } }; - _getBounds() { - const {left, top, right, bottom} = this._ref.getBoundingClientRect(); - const waypointTop = top; - const waypointBottom = bottom; - - let contextHeight; - let contextScrollTop; - if (this.scrollableAncestor === window) { - contextHeight = window.innerHeight; - contextScrollTop = 0; - } else { - contextHeight = this.scrollableAncestor.offsetHeight; - contextScrollTop = this.scrollableAncestor.getBoundingClientRect().top; - } - - const {bottomOffset, topOffset} = this.props; - const contextBottom = contextScrollTop + contextHeight; - - return { - waypointTop, - waypointBottom, - viewportTop: contextScrollTop + topOffset, - viewportBottom: contextBottom - bottomOffset, - }; - } - render() { - return React.cloneElement(this.props.children, {innerRef: this.refElement}); + return React.cloneElement(this.props.children, {innerRef: this.innerRef}); } } @@ -294,3 +273,28 @@ function findScrollableAncestor(inputNode) { // do stuff on window. return window; } + +function getBounds({node, scrollableAncestor, topOffset, bottomOffset}) { + const {left, top, right, bottom} = node.getBoundingClientRect(); + const waypointTop = top; + const waypointBottom = bottom; + + let contextHeight; + let contextScrollTop; + if (scrollableAncestor === window) { + contextHeight = window.innerHeight; + contextScrollTop = 0; + } else { + contextHeight = scrollableAncestor.offsetHeight; + contextScrollTop = scrollableAncestor.getBoundingClientRect().top; + } + + const contextBottom = contextScrollTop + contextHeight; + + return { + waypointTop, + waypointBottom, + viewportTop: contextScrollTop + topOffset, + viewportBottom: contextBottom - bottomOffset, + }; +} From 9505b842af0480331f05db385881fc5eeb35c2aa Mon Sep 17 00:00:00 2001 From: sebastien Date: Thu, 20 Mar 2025 10:07:45 +0100 Subject: [PATCH 17/27] remove hasWindow --- .../components/IdealImage/waypoint.js | 22 +++++-------------- 1 file changed, 5 insertions(+), 17 deletions(-) diff --git a/packages/docusaurus-plugin-ideal-image/src/theme/IdealImageLegacy/components/IdealImage/waypoint.js b/packages/docusaurus-plugin-ideal-image/src/theme/IdealImageLegacy/components/IdealImage/waypoint.js index 09ed95ffd9fc..8f341b4647aa 100644 --- a/packages/docusaurus-plugin-ideal-image/src/theme/IdealImageLegacy/components/IdealImage/waypoint.js +++ b/packages/docusaurus-plugin-ideal-image/src/theme/IdealImageLegacy/components/IdealImage/waypoint.js @@ -1,7 +1,5 @@ import React, {createRef} from 'react'; -import onNextTick from './onNextTick'; - // Same API as https://github.com/lencioni/consolidated-events // But removing the behavior that we don't need function addEventListener(element, type, listener, options) { @@ -14,8 +12,6 @@ const INSIDE = 'inside'; const BELOW = 'below'; const INVISIBLE = 'invisible'; -const hasWindow = typeof window !== 'undefined'; - const defaultProps = { topOffset: 0, bottomOffset: 0, @@ -23,8 +19,12 @@ const defaultProps = { onLeave() {}, }; +export function Waypoint(props) { + const Comp = typeof window !== 'undefined' ? WaypointClient : props.children; +} + // Calls a function when you scroll to the element. -export class Waypoint extends React.PureComponent { +class WaypointClient extends React.PureComponent { innerRef = createRef(); constructor(props) { @@ -32,10 +32,6 @@ export class Waypoint extends React.PureComponent { } componentDidMount() { - if (!hasWindow) { - return; - } - // this.innerRef may occasionally not be set at this time. To help ensure that // this works smoothly and to avoid layout thrashing, we want to delay the // initial execution until the next tick. @@ -64,10 +60,6 @@ export class Waypoint extends React.PureComponent { } componentDidUpdate() { - if (!hasWindow) { - return; - } - if (!this.scrollableAncestor) { // The Waypoint has not yet initialized. return; @@ -91,10 +83,6 @@ export class Waypoint extends React.PureComponent { } componentWillUnmount() { - if (!hasWindow) { - return; - } - if (this.scrollEventListenerUnsubscribe) { this.scrollEventListenerUnsubscribe(); } From 03da1f4c6c02bc0166046f78af965239b53841f3 Mon Sep 17 00:00:00 2001 From: sebastien Date: Thu, 20 Mar 2025 10:13:15 +0100 Subject: [PATCH 18/27] remove onNextTick() --- .../components/IdealImage/waypoint.js | 147 +++++++----------- 1 file changed, 54 insertions(+), 93 deletions(-) diff --git a/packages/docusaurus-plugin-ideal-image/src/theme/IdealImageLegacy/components/IdealImage/waypoint.js b/packages/docusaurus-plugin-ideal-image/src/theme/IdealImageLegacy/components/IdealImage/waypoint.js index 8f341b4647aa..c5ab7b20539b 100644 --- a/packages/docusaurus-plugin-ideal-image/src/theme/IdealImageLegacy/components/IdealImage/waypoint.js +++ b/packages/docusaurus-plugin-ideal-image/src/theme/IdealImageLegacy/components/IdealImage/waypoint.js @@ -32,54 +32,27 @@ class WaypointClient extends React.PureComponent { } componentDidMount() { - // this.innerRef may occasionally not be set at this time. To help ensure that - // this works smoothly and to avoid layout thrashing, we want to delay the - // initial execution until the next tick. - this.cancelOnNextTick = onNextTick(() => { - this.cancelOnNextTick = null; - const {children} = this.props; - - this.scrollableAncestor = findScrollableAncestor(this.innerRef.current); - - this.scrollEventListenerUnsubscribe = addEventListener( - this.scrollableAncestor, - 'scroll', - this._handleScroll, - {passive: true}, - ); - - this.resizeEventListenerUnsubscribe = addEventListener( - window, - 'resize', - this._handleScroll, - {passive: true}, - ); - - this._handleScroll(null); - }); + this.scrollableAncestor = findScrollableAncestor(this.innerRef.current); + + this.scrollEventListenerUnsubscribe = addEventListener( + this.scrollableAncestor, + 'scroll', + this._handleScroll, + {passive: true}, + ); + + this.resizeEventListenerUnsubscribe = addEventListener( + window, + 'resize', + this._handleScroll, + {passive: true}, + ); + + this._handleScroll(null); } componentDidUpdate() { - if (!this.scrollableAncestor) { - // The Waypoint has not yet initialized. - return; - } - - // The element may have moved, so we need to recompute its position on the - // page. This happens via handleScroll in a way that forces layout to be - // computed. - // - // We want this to be deferred to avoid forcing layout during render, which - // causes layout thrashing. And, if we already have this work enqueued, we - // can just wait for that to happen instead of enqueueing again. - if (this.cancelOnNextTick) { - return; - } - - this.cancelOnNextTick = onNextTick(() => { - this.cancelOnNextTick = null; - this._handleScroll(null); - }); + this._handleScroll(null); } componentWillUnmount() { @@ -89,10 +62,6 @@ class WaypointClient extends React.PureComponent { if (this.resizeEventListenerUnsubscribe) { this.resizeEventListenerUnsubscribe(); } - - if (this.cancelOnNextTick) { - this.cancelOnNextTick(); - } } /** @@ -116,9 +85,6 @@ class WaypointClient extends React.PureComponent { const currentPosition = getCurrentPosition(bounds); const previousPosition = this._previousPosition; - const {onEnter, onLeave} = this.props; - - // Save previous position as early as possible to prevent cycles this._previousPosition = currentPosition; if (previousPosition === currentPosition) { @@ -126,6 +92,8 @@ class WaypointClient extends React.PureComponent { return; } + const {onEnter, onLeave} = this.props; + const callbackArg = { currentPosition, previousPosition, @@ -183,47 +151,6 @@ Waypoint.invisible = INVISIBLE; Waypoint.defaultProps = defaultProps; Waypoint.displayName = 'Waypoint'; -// Copy https://github.com/civiccc/react-waypoint/blob/master/src/getCurrentPosition.js -function getCurrentPosition(bounds) { - if (bounds.viewportBottom - bounds.viewportTop === 0) { - return INVISIBLE; - } - - // top is within the viewport - if ( - bounds.viewportTop <= bounds.waypointTop && - bounds.waypointTop <= bounds.viewportBottom - ) { - return INSIDE; - } - - // bottom is within the viewport - if ( - bounds.viewportTop <= bounds.waypointBottom && - bounds.waypointBottom <= bounds.viewportBottom - ) { - return INSIDE; - } - - // top is above the viewport and bottom is below the viewport - if ( - bounds.waypointTop <= bounds.viewportTop && - bounds.viewportBottom <= bounds.waypointBottom - ) { - return INSIDE; - } - - if (bounds.viewportBottom < bounds.waypointTop) { - return BELOW; - } - - if (bounds.waypointTop < bounds.viewportTop) { - return ABOVE; - } - - return INVISIBLE; -} - /** * Traverses up the DOM to find an ancestor container which has an overflow * style that allows for scrolling. @@ -286,3 +213,37 @@ function getBounds({node, scrollableAncestor, topOffset, bottomOffset}) { viewportBottom: contextBottom - bottomOffset, }; } + +function getCurrentPosition(bounds) { + if (bounds.viewportBottom - bounds.viewportTop === 0) { + return INVISIBLE; + } + // top is within the viewport + if ( + bounds.viewportTop <= bounds.waypointTop && + bounds.waypointTop <= bounds.viewportBottom + ) { + return INSIDE; + } + // bottom is within the viewport + if ( + bounds.viewportTop <= bounds.waypointBottom && + bounds.waypointBottom <= bounds.viewportBottom + ) { + return INSIDE; + } + // top is above the viewport and bottom is below the viewport + if ( + bounds.waypointTop <= bounds.viewportTop && + bounds.viewportBottom <= bounds.waypointBottom + ) { + return INSIDE; + } + if (bounds.viewportBottom < bounds.waypointTop) { + return BELOW; + } + if (bounds.waypointTop < bounds.viewportTop) { + return ABOVE; + } + return INVISIBLE; +} From 5cb5015b633bf9c4b6d57e3e81bc0653f41b26b6 Mon Sep 17 00:00:00 2001 From: sebastien Date: Thu, 20 Mar 2025 10:18:16 +0100 Subject: [PATCH 19/27] fixes --- .../components/IdealImage/waypoint.js | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/packages/docusaurus-plugin-ideal-image/src/theme/IdealImageLegacy/components/IdealImage/waypoint.js b/packages/docusaurus-plugin-ideal-image/src/theme/IdealImageLegacy/components/IdealImage/waypoint.js index c5ab7b20539b..0d1b029c426f 100644 --- a/packages/docusaurus-plugin-ideal-image/src/theme/IdealImageLegacy/components/IdealImage/waypoint.js +++ b/packages/docusaurus-plugin-ideal-image/src/theme/IdealImageLegacy/components/IdealImage/waypoint.js @@ -20,7 +20,11 @@ const defaultProps = { }; export function Waypoint(props) { - const Comp = typeof window !== 'undefined' ? WaypointClient : props.children; + return typeof window !== 'undefined' ? ( + + ) : ( + props.children + ); } // Calls a function when you scroll to the element. @@ -75,25 +79,26 @@ class WaypointClient extends React.PureComponent { // There's a chance we end up here after the component has been unmounted. return; } + const {topOffset, bottomOffset, onEnter, onLeave} = this.props; const bounds = getBounds({ node, scrollableAncestor: this.scrollableAncestor, - topOffset: props.topOffset, - bottomOffset: props.bottomOffset, + topOffset, + bottomOffset, }); const currentPosition = getCurrentPosition(bounds); const previousPosition = this._previousPosition; this._previousPosition = currentPosition; + console.log('handleScroll', {currentPosition, previousPosition}); + if (previousPosition === currentPosition) { // No change since last trigger return; } - const {onEnter, onLeave} = this.props; - const callbackArg = { currentPosition, previousPosition, From 4619b860baef67fe7ad9d1725a620954472d5171 Mon Sep 17 00:00:00 2001 From: sebastien Date: Thu, 20 Mar 2025 10:24:11 +0100 Subject: [PATCH 20/27] make it work, replace waypoint --- .../package.json | 1 - .../components/IdealImage/index.js | 2 +- .../components/IdealImage/waypoint.js | 26 ++++++------------- yarn.lock | 19 ++------------ 4 files changed, 11 insertions(+), 37 deletions(-) diff --git a/packages/docusaurus-plugin-ideal-image/package.json b/packages/docusaurus-plugin-ideal-image/package.json index b67f2377bce0..8e6f93cd412a 100644 --- a/packages/docusaurus-plugin-ideal-image/package.json +++ b/packages/docusaurus-plugin-ideal-image/package.json @@ -26,7 +26,6 @@ "@docusaurus/theme-translations": "3.7.0", "@docusaurus/types": "3.7.0", "@docusaurus/utils-validation": "3.7.0", - "react-waypoint": "^10.3.0", "sharp": "^0.32.3", "tslib": "^2.6.0", "webpack": "^5.88.1" diff --git a/packages/docusaurus-plugin-ideal-image/src/theme/IdealImageLegacy/components/IdealImage/index.js b/packages/docusaurus-plugin-ideal-image/src/theme/IdealImageLegacy/components/IdealImage/index.js index 8b32f3bec88b..5bf57e0535e2 100644 --- a/packages/docusaurus-plugin-ideal-image/src/theme/IdealImageLegacy/components/IdealImage/index.js +++ b/packages/docusaurus-plugin-ideal-image/src/theme/IdealImageLegacy/components/IdealImage/index.js @@ -1,4 +1,4 @@ -import {Component} from 'react'; +import React, {Component} from 'react'; import {Waypoint} from './waypoint'; import Media from '../Media'; import {icons, loadStates} from '../constants'; diff --git a/packages/docusaurus-plugin-ideal-image/src/theme/IdealImageLegacy/components/IdealImage/waypoint.js b/packages/docusaurus-plugin-ideal-image/src/theme/IdealImageLegacy/components/IdealImage/waypoint.js index 0d1b029c426f..65f60c58f1a2 100644 --- a/packages/docusaurus-plugin-ideal-image/src/theme/IdealImageLegacy/components/IdealImage/waypoint.js +++ b/packages/docusaurus-plugin-ideal-image/src/theme/IdealImageLegacy/components/IdealImage/waypoint.js @@ -12,13 +12,6 @@ const INSIDE = 'inside'; const BELOW = 'below'; const INVISIBLE = 'invisible'; -const defaultProps = { - topOffset: 0, - bottomOffset: 0, - onEnter() {}, - onLeave() {}, -}; - export function Waypoint(props) { return typeof window !== 'undefined' ? ( @@ -27,8 +20,14 @@ export function Waypoint(props) { ); } -// Calls a function when you scroll to the element. -class WaypointClient extends React.PureComponent { +class WaypointClient extends React.Component { + static defaultProps = { + topOffset: 0, + bottomOffset: 0, + onEnter() {}, + onLeave() {}, + }; + innerRef = createRef(); constructor(props) { @@ -92,8 +91,6 @@ class WaypointClient extends React.PureComponent { const previousPosition = this._previousPosition; this._previousPosition = currentPosition; - console.log('handleScroll', {currentPosition, previousPosition}); - if (previousPosition === currentPosition) { // No change since last trigger return; @@ -149,13 +146,6 @@ class WaypointClient extends React.PureComponent { } } -Waypoint.above = ABOVE; -Waypoint.below = BELOW; -Waypoint.inside = INSIDE; -Waypoint.invisible = INVISIBLE; -Waypoint.defaultProps = defaultProps; -Waypoint.displayName = 'Waypoint'; - /** * Traverses up the DOM to find an ancestor container which has an overflow * style that allows for scrolling. diff --git a/yarn.lock b/yarn.lock index 781ab1ef61c8..cf02d688820e 100644 --- a/yarn.lock +++ b/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" 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: version "0.5.2" resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.2.tgz#0cf68bb9ddf5f2be7961c3a85178cb85dba78cb4" @@ -14841,7 +14836,7 @@ promzard@^0.3.0: dependencies: 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" resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5" integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg== @@ -15052,7 +15047,7 @@ react-fast-compare@^3.2.0: react-fast-compare "^3.2.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" resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.3.1.tgz#e83557dc12eae63a99e003a46388b1dcbb44db7e" integrity sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg== @@ -15152,16 +15147,6 @@ react-test-renderer@^18.0.0: react-shallow-renderer "^16.15.0" 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: version "16.14.0" resolved "https://registry.yarnpkg.com/react/-/react-16.14.0.tgz#94d776ddd0aaa37da3eda8fc5b6b18a4c9a3114d" From d54e16188e1a3c17f6795393d275990ad5f3916f Mon Sep 17 00:00:00 2001 From: sebastien Date: Thu, 20 Mar 2025 10:31:01 +0100 Subject: [PATCH 21/27] slim down --- .../components/IdealImage/waypoint.js | 38 ++----------------- 1 file changed, 4 insertions(+), 34 deletions(-) diff --git a/packages/docusaurus-plugin-ideal-image/src/theme/IdealImageLegacy/components/IdealImage/waypoint.js b/packages/docusaurus-plugin-ideal-image/src/theme/IdealImageLegacy/components/IdealImage/waypoint.js index 65f60c58f1a2..d934668d63d4 100644 --- a/packages/docusaurus-plugin-ideal-image/src/theme/IdealImageLegacy/components/IdealImage/waypoint.js +++ b/packages/docusaurus-plugin-ideal-image/src/theme/IdealImageLegacy/components/IdealImage/waypoint.js @@ -92,52 +92,22 @@ class WaypointClient extends React.Component { this._previousPosition = currentPosition; if (previousPosition === currentPosition) { - // No change since last trigger return; } - const callbackArg = { - currentPosition, - previousPosition, - event, - waypointTop: bounds.waypointTop, - waypointBottom: bounds.waypointBottom, - viewportTop: bounds.viewportTop, - viewportBottom: bounds.viewportBottom, - }; - if (currentPosition === INSIDE) { - onEnter.call(this, callbackArg); + onEnter(); } else if (previousPosition === INSIDE) { - onLeave.call(this, callbackArg); + onLeave(); } const isRapidScrollDown = previousPosition === BELOW && currentPosition === ABOVE; const isRapidScrollUp = previousPosition === ABOVE && currentPosition === BELOW; - if (isRapidScrollDown || isRapidScrollUp) { - // If the scroll event isn't fired often enough to occur while the - // waypoint was visible, we trigger both callbacks anyway. - onEnter.call(this, { - currentPosition: INSIDE, - previousPosition, - event, - waypointTop: bounds.waypointTop, - waypointBottom: bounds.waypointBottom, - viewportTop: bounds.viewportTop, - viewportBottom: bounds.viewportBottom, - }); - onLeave.call(this, { - currentPosition, - previousPosition: INSIDE, - event, - waypointTop: bounds.waypointTop, - waypointBottom: bounds.waypointBottom, - viewportTop: bounds.viewportTop, - viewportBottom: bounds.viewportBottom, - }); + onEnter(); + onLeave(); } }; From f3cbcfcaf551dd9b78b712b210552dc560d965e7 Mon Sep 17 00:00:00 2001 From: sebastien Date: Thu, 20 Mar 2025 10:39:38 +0100 Subject: [PATCH 22/27] slim down --- .../components/IdealImage/waypoint.js | 25 ++++++++----------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/packages/docusaurus-plugin-ideal-image/src/theme/IdealImageLegacy/components/IdealImage/waypoint.js b/packages/docusaurus-plugin-ideal-image/src/theme/IdealImageLegacy/components/IdealImage/waypoint.js index d934668d63d4..88f8d690eea7 100644 --- a/packages/docusaurus-plugin-ideal-image/src/theme/IdealImageLegacy/components/IdealImage/waypoint.js +++ b/packages/docusaurus-plugin-ideal-image/src/theme/IdealImageLegacy/components/IdealImage/waypoint.js @@ -155,9 +155,7 @@ function findScrollableAncestor(inputNode) { } function getBounds({node, scrollableAncestor, topOffset, bottomOffset}) { - const {left, top, right, bottom} = node.getBoundingClientRect(); - const waypointTop = top; - const waypointBottom = bottom; + const {top, bottom} = node.getBoundingClientRect(); let contextHeight; let contextScrollTop; @@ -172,8 +170,8 @@ function getBounds({node, scrollableAncestor, topOffset, bottomOffset}) { const contextBottom = contextScrollTop + contextHeight; return { - waypointTop, - waypointBottom, + top, + bottom, viewportTop: contextScrollTop + topOffset, viewportBottom: contextBottom - bottomOffset, }; @@ -184,30 +182,27 @@ function getCurrentPosition(bounds) { return INVISIBLE; } // top is within the viewport - if ( - bounds.viewportTop <= bounds.waypointTop && - bounds.waypointTop <= bounds.viewportBottom - ) { + if (bounds.viewportTop <= bounds.top && bounds.top <= bounds.viewportBottom) { return INSIDE; } // bottom is within the viewport if ( - bounds.viewportTop <= bounds.waypointBottom && - bounds.waypointBottom <= bounds.viewportBottom + bounds.viewportTop <= bounds.bottom && + bounds.bottom <= bounds.viewportBottom ) { return INSIDE; } // top is above the viewport and bottom is below the viewport if ( - bounds.waypointTop <= bounds.viewportTop && - bounds.viewportBottom <= bounds.waypointBottom + bounds.top <= bounds.viewportTop && + bounds.viewportBottom <= bounds.bottom ) { return INSIDE; } - if (bounds.viewportBottom < bounds.waypointTop) { + if (bounds.viewportBottom < bounds.top) { return BELOW; } - if (bounds.waypointTop < bounds.viewportTop) { + if (bounds.top < bounds.viewportTop) { return ABOVE; } return INVISIBLE; From 2ec2778eebea52fdd098007c8aae6a5709d7d7fa Mon Sep 17 00:00:00 2001 From: sebastien Date: Thu, 20 Mar 2025 10:40:16 +0100 Subject: [PATCH 23/27] slim down --- .../theme/IdealImageLegacy/components/IdealImage/waypoint.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/packages/docusaurus-plugin-ideal-image/src/theme/IdealImageLegacy/components/IdealImage/waypoint.js b/packages/docusaurus-plugin-ideal-image/src/theme/IdealImageLegacy/components/IdealImage/waypoint.js index 88f8d690eea7..0e961755a301 100644 --- a/packages/docusaurus-plugin-ideal-image/src/theme/IdealImageLegacy/components/IdealImage/waypoint.js +++ b/packages/docusaurus-plugin-ideal-image/src/theme/IdealImageLegacy/components/IdealImage/waypoint.js @@ -74,10 +74,6 @@ class WaypointClient extends React.Component { */ _handleScroll = (event) => { const node = this.innerRef.current; - if (!node) { - // There's a chance we end up here after the component has been unmounted. - return; - } const {topOffset, bottomOffset, onEnter, onLeave} = this.props; const bounds = getBounds({ From 5b04702ff2a8eebf145ed4d9b5f418e4b7f26f57 Mon Sep 17 00:00:00 2001 From: sebastien Date: Thu, 20 Mar 2025 11:16:50 +0100 Subject: [PATCH 24/27] use TypeScript --- .../IdealImage/{waypoint.js => waypoint.tsx} | 119 +++++++++++------- website/docusaurus.config.ts | 2 +- 2 files changed, 78 insertions(+), 43 deletions(-) rename packages/docusaurus-plugin-ideal-image/src/theme/IdealImageLegacy/components/IdealImage/{waypoint.js => waypoint.tsx} (61%) diff --git a/packages/docusaurus-plugin-ideal-image/src/theme/IdealImageLegacy/components/IdealImage/waypoint.js b/packages/docusaurus-plugin-ideal-image/src/theme/IdealImageLegacy/components/IdealImage/waypoint.tsx similarity index 61% rename from packages/docusaurus-plugin-ideal-image/src/theme/IdealImageLegacy/components/IdealImage/waypoint.js rename to packages/docusaurus-plugin-ideal-image/src/theme/IdealImageLegacy/components/IdealImage/waypoint.tsx index 0e961755a301..3f8e4d7664f6 100644 --- a/packages/docusaurus-plugin-ideal-image/src/theme/IdealImageLegacy/components/IdealImage/waypoint.js +++ b/packages/docusaurus-plugin-ideal-image/src/theme/IdealImageLegacy/components/IdealImage/waypoint.tsx @@ -1,8 +1,15 @@ -import React, {createRef} from 'react'; +import React, {createRef, ReactNode} from 'react'; + +type ScrollContainer = Window | HTMLElement; // Same API as https://github.com/lencioni/consolidated-events // But removing the behavior that we don't need -function addEventListener(element, type, listener, options) { +function addEventListener( + element: ScrollContainer, + type: any, + listener: any, + options: any, +) { element.addEventListener(type, listener, options); return () => element.removeEventListener(type, listener, options); } @@ -12,15 +19,28 @@ const INSIDE = 'inside'; const BELOW = 'below'; const INVISIBLE = 'invisible'; -export function Waypoint(props) { +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' ? ( - + {props.children} ) : ( props.children ); } -class WaypointClient extends React.Component { +// 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 { static defaultProps = { topOffset: 0, bottomOffset: 0, @@ -28,64 +48,59 @@ class WaypointClient extends React.Component { onLeave() {}, }; - innerRef = createRef(); + scrollableAncestor?: ScrollContainer; + previousPosition: Position | null = null; + unsubscribe?: () => void; - constructor(props) { - super(props); - } + innerRef = createRef(); - componentDidMount() { - this.scrollableAncestor = findScrollableAncestor(this.innerRef.current); + override componentDidMount() { + this.scrollableAncestor = findScrollableAncestor(this.innerRef.current!); - this.scrollEventListenerUnsubscribe = addEventListener( - this.scrollableAncestor, + const unsubscribeScroll = addEventListener( + this.scrollableAncestor!, 'scroll', this._handleScroll, {passive: true}, ); - this.resizeEventListenerUnsubscribe = addEventListener( + const unsubscribeResize = addEventListener( window, 'resize', this._handleScroll, {passive: true}, ); - this._handleScroll(null); + this.unsubscribe = () => { + unsubscribeScroll(); + unsubscribeResize(); + }; + + this._handleScroll(); } - componentDidUpdate() { - this._handleScroll(null); + override componentDidUpdate() { + this._handleScroll(); } - componentWillUnmount() { - if (this.scrollEventListenerUnsubscribe) { - this.scrollEventListenerUnsubscribe(); - } - if (this.resizeEventListenerUnsubscribe) { - this.resizeEventListenerUnsubscribe(); - } + override componentWillUnmount() { + this.unsubscribe?.(); } - /** - * @param {Object} event the native scroll event coming from the scrollable - * ancestor, or resize event coming from the window. Will be undefined if - * called by a React lifecyle method - */ - _handleScroll = (event) => { + _handleScroll = () => { const node = this.innerRef.current; const {topOffset, bottomOffset, onEnter, onLeave} = this.props; const bounds = getBounds({ - node, - scrollableAncestor: this.scrollableAncestor, + node: node!, + scrollableAncestor: this.scrollableAncestor!, topOffset, bottomOffset, }); const currentPosition = getCurrentPosition(bounds); - const previousPosition = this._previousPosition; - this._previousPosition = currentPosition; + const previousPosition = this.previousPosition; + this.previousPosition = currentPosition; if (previousPosition === currentPosition) { return; @@ -107,7 +122,8 @@ class WaypointClient extends React.Component { } }; - render() { + override render() { + // @ts-expect-error: fix this implicit API return React.cloneElement(this.props.children, {innerRef: this.innerRef}); } } @@ -120,11 +136,12 @@ class WaypointClient extends React.Component { * allows for scrolling. If none is found, the `window` object is returned * as a fallback. */ -function findScrollableAncestor(inputNode) { - let node = inputNode; +function findScrollableAncestor(inputNode: HTMLElement): ScrollContainer { + let node: HTMLElement = inputNode; while (node.parentNode) { - node = 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. @@ -150,7 +167,24 @@ function findScrollableAncestor(inputNode) { return window; } -function getBounds({node, scrollableAncestor, topOffset, bottomOffset}) { +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; @@ -159,8 +193,9 @@ function getBounds({node, scrollableAncestor, topOffset, bottomOffset}) { contextHeight = window.innerHeight; contextScrollTop = 0; } else { - contextHeight = scrollableAncestor.offsetHeight; - contextScrollTop = scrollableAncestor.getBoundingClientRect().top; + const ancestorElement = scrollableAncestor as HTMLElement; + contextHeight = ancestorElement.offsetHeight; + contextScrollTop = ancestorElement.getBoundingClientRect().top; } const contextBottom = contextScrollTop + contextHeight; @@ -173,7 +208,7 @@ function getBounds({node, scrollableAncestor, topOffset, bottomOffset}) { }; } -function getCurrentPosition(bounds) { +function getCurrentPosition(bounds: Bounds) { if (bounds.viewportBottom - bounds.viewportTop === 0) { return INVISIBLE; } diff --git a/website/docusaurus.config.ts b/website/docusaurus.config.ts index a1ce8451531d..403d6394925c 100644 --- a/website/docusaurus.config.ts +++ b/website/docusaurus.config.ts @@ -364,7 +364,7 @@ export default async function createConfigAsync() { min: 640, steps: 2, // Use false to debug, but it incurs huge perf costs - disableInDev: true, + disableInDev: false, } satisfies IdealImageOptions, ], [ From fadd4da3ac83e91c3b89f3272f4f62374d79076b Mon Sep 17 00:00:00 2001 From: sebastien Date: Thu, 20 Mar 2025 11:25:04 +0100 Subject: [PATCH 25/27] slim down --- .../components/IdealImage/waypoint.tsx | 38 ++++++++----------- 1 file changed, 16 insertions(+), 22 deletions(-) diff --git a/packages/docusaurus-plugin-ideal-image/src/theme/IdealImageLegacy/components/IdealImage/waypoint.tsx b/packages/docusaurus-plugin-ideal-image/src/theme/IdealImageLegacy/components/IdealImage/waypoint.tsx index 3f8e4d7664f6..02fcc3f9e382 100644 --- a/packages/docusaurus-plugin-ideal-image/src/theme/IdealImageLegacy/components/IdealImage/waypoint.tsx +++ b/packages/docusaurus-plugin-ideal-image/src/theme/IdealImageLegacy/components/IdealImage/waypoint.tsx @@ -1,24 +1,18 @@ import React, {createRef, ReactNode} from 'react'; +import {AddEventListenerOptions} from 'undici-types/patch'; type ScrollContainer = Window | HTMLElement; -// Same API as https://github.com/lencioni/consolidated-events -// But removing the behavior that we don't need function addEventListener( element: ScrollContainer, - type: any, - listener: any, - options: any, + type: 'scroll' | 'resize', + listener: () => void, + options: AddEventListenerOptions, ) { element.addEventListener(type, listener, options); return () => element.removeEventListener(type, listener, options); } -const ABOVE = 'above'; -const INSIDE = 'inside'; -const BELOW = 'below'; -const INVISIBLE = 'invisible'; - type Position = 'above' | 'inside' | 'below' | 'invisible'; type Props = { @@ -106,16 +100,16 @@ class WaypointClient extends React.Component { return; } - if (currentPosition === INSIDE) { + if (currentPosition === 'inside') { onEnter(); - } else if (previousPosition === INSIDE) { + } else if (previousPosition === 'inside') { onLeave(); } const isRapidScrollDown = - previousPosition === BELOW && currentPosition === ABOVE; + previousPosition === 'below' && currentPosition === 'above'; const isRapidScrollUp = - previousPosition === ABOVE && currentPosition === BELOW; + previousPosition === 'above' && currentPosition === 'below'; if (isRapidScrollDown || isRapidScrollUp) { onEnter(); onLeave(); @@ -208,33 +202,33 @@ function getBounds({ }; } -function getCurrentPosition(bounds: Bounds) { +function getCurrentPosition(bounds: Bounds): Position { if (bounds.viewportBottom - bounds.viewportTop === 0) { - return INVISIBLE; + return 'invisible'; } // top is within the viewport if (bounds.viewportTop <= bounds.top && bounds.top <= bounds.viewportBottom) { - return INSIDE; + return 'inside'; } // bottom is within the viewport if ( bounds.viewportTop <= bounds.bottom && bounds.bottom <= bounds.viewportBottom ) { - return INSIDE; + return 'inside'; } // top is above the viewport and bottom is below the viewport if ( bounds.top <= bounds.viewportTop && bounds.viewportBottom <= bounds.bottom ) { - return INSIDE; + return 'inside'; } if (bounds.viewportBottom < bounds.top) { - return BELOW; + return 'below'; } if (bounds.top < bounds.viewportTop) { - return ABOVE; + return 'above'; } - return INVISIBLE; + return 'invisible'; } From 9c3cd84c70ab9fa5bd3ca9c12ee03dee7dce5d14 Mon Sep 17 00:00:00 2001 From: sebastien Date: Thu, 20 Mar 2025 11:27:00 +0100 Subject: [PATCH 26/27] slim down --- .../IdealImageLegacy/components/IdealImage/waypoint.tsx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/docusaurus-plugin-ideal-image/src/theme/IdealImageLegacy/components/IdealImage/waypoint.tsx b/packages/docusaurus-plugin-ideal-image/src/theme/IdealImageLegacy/components/IdealImage/waypoint.tsx index 02fcc3f9e382..54bd554b3f30 100644 --- a/packages/docusaurus-plugin-ideal-image/src/theme/IdealImageLegacy/components/IdealImage/waypoint.tsx +++ b/packages/docusaurus-plugin-ideal-image/src/theme/IdealImageLegacy/components/IdealImage/waypoint.tsx @@ -1,5 +1,10 @@ +/* +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'; -import {AddEventListenerOptions} from 'undici-types/patch'; type ScrollContainer = Window | HTMLElement; From 6436203bc636a74aa9124f1cc0daea01befaf368 Mon Sep 17 00:00:00 2001 From: sebastien Date: Thu, 20 Mar 2025 11:27:30 +0100 Subject: [PATCH 27/27] revert --- website/docusaurus.config.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docusaurus.config.ts b/website/docusaurus.config.ts index 403d6394925c..a1ce8451531d 100644 --- a/website/docusaurus.config.ts +++ b/website/docusaurus.config.ts @@ -364,7 +364,7 @@ export default async function createConfigAsync() { min: 640, steps: 2, // Use false to debug, but it incurs huge perf costs - disableInDev: false, + disableInDev: true, } satisfies IdealImageOptions, ], [