@docusaurus/plugin-ideal-image 3.7.0-canary-6264 → 3.7.0-canary-6265

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,5 @@
1
1
  import React, {Component} from 'react';
2
- // import PropTypes from 'prop-types'
3
- import {Waypoint} from 'react-waypoint';
2
+ import {Waypoint} from './waypoint';
4
3
  import Media from '../Media';
5
4
  import {icons, loadStates} from '../constants';
6
5
  import {xhrLoader, imageLoader, timeout, combineCancel} from '../loaders';
@@ -0,0 +1,10 @@
1
+ import React, { ReactNode } from 'react';
2
+ type Props = {
3
+ topOffset: number;
4
+ bottomOffset: number;
5
+ onEnter: () => void;
6
+ onLeave: () => void;
7
+ children: ReactNode;
8
+ };
9
+ export declare function Waypoint(props: Props): string | number | boolean | Iterable<React.ReactNode> | React.JSX.Element | null | undefined;
10
+ export {};
@@ -0,0 +1,174 @@
1
+ /*
2
+ This is a slimmed down copy of https://github.com/civiccc/react-waypoint
3
+ The MIT License (MIT)
4
+ Copyright (c) 2015 Brigade
5
+ */
6
+ import React, {createRef} from 'react';
7
+ function addEventListener(element, type, listener, options) {
8
+ element.addEventListener(type, listener, options);
9
+ return () => element.removeEventListener(type, listener, options);
10
+ }
11
+ export function Waypoint(props) {
12
+ return typeof window !== 'undefined' ? (
13
+ <WaypointClient {...props}>{props.children}</WaypointClient>
14
+ ) : (
15
+ props.children
16
+ );
17
+ }
18
+ // TODO maybe replace this with IntersectionObserver later?
19
+ // IntersectionObserver doesn't support the "fast scroll" thing
20
+ // but it's probably not a big deal
21
+ class WaypointClient extends React.Component {
22
+ static defaultProps = {
23
+ topOffset: 0,
24
+ bottomOffset: 0,
25
+ onEnter() {},
26
+ onLeave() {},
27
+ };
28
+ scrollableAncestor;
29
+ previousPosition = null;
30
+ unsubscribe;
31
+ innerRef = createRef();
32
+ componentDidMount() {
33
+ this.scrollableAncestor = findScrollableAncestor(this.innerRef.current);
34
+ const unsubscribeScroll = addEventListener(
35
+ this.scrollableAncestor,
36
+ 'scroll',
37
+ this._handleScroll,
38
+ {passive: true},
39
+ );
40
+ const unsubscribeResize = addEventListener(
41
+ window,
42
+ 'resize',
43
+ this._handleScroll,
44
+ {passive: true},
45
+ );
46
+ this.unsubscribe = () => {
47
+ unsubscribeScroll();
48
+ unsubscribeResize();
49
+ };
50
+ this._handleScroll();
51
+ }
52
+ componentDidUpdate() {
53
+ this._handleScroll();
54
+ }
55
+ componentWillUnmount() {
56
+ this.unsubscribe?.();
57
+ }
58
+ _handleScroll = () => {
59
+ const node = this.innerRef.current;
60
+ const {topOffset, bottomOffset, onEnter, onLeave} = this.props;
61
+ const bounds = getBounds({
62
+ node: node,
63
+ scrollableAncestor: this.scrollableAncestor,
64
+ topOffset,
65
+ bottomOffset,
66
+ });
67
+ const currentPosition = getCurrentPosition(bounds);
68
+ const previousPosition = this.previousPosition;
69
+ this.previousPosition = currentPosition;
70
+ if (previousPosition === currentPosition) {
71
+ return;
72
+ }
73
+ if (currentPosition === 'inside') {
74
+ onEnter();
75
+ } else if (previousPosition === 'inside') {
76
+ onLeave();
77
+ }
78
+ const isRapidScrollDown =
79
+ previousPosition === 'below' && currentPosition === 'above';
80
+ const isRapidScrollUp =
81
+ previousPosition === 'above' && currentPosition === 'below';
82
+ if (isRapidScrollDown || isRapidScrollUp) {
83
+ onEnter();
84
+ onLeave();
85
+ }
86
+ };
87
+ render() {
88
+ // @ts-expect-error: fix this implicit API
89
+ return React.cloneElement(this.props.children, {innerRef: this.innerRef});
90
+ }
91
+ }
92
+ /**
93
+ * Traverses up the DOM to find an ancestor container which has an overflow
94
+ * style that allows for scrolling.
95
+ *
96
+ * @return {Object} the closest ancestor element with an overflow style that
97
+ * allows for scrolling. If none is found, the `window` object is returned
98
+ * as a fallback.
99
+ */
100
+ function findScrollableAncestor(inputNode) {
101
+ let node = inputNode;
102
+ while (node.parentNode) {
103
+ // @ts-expect-error: it's fine
104
+ node = node.parentNode;
105
+ if (node === document.body) {
106
+ // We've reached all the way to the root node.
107
+ return window;
108
+ }
109
+ const style = window.getComputedStyle(node);
110
+ const overflow =
111
+ style.getPropertyValue('overflow-y') ||
112
+ style.getPropertyValue('overflow');
113
+ if (
114
+ overflow === 'auto' ||
115
+ overflow === 'scroll' ||
116
+ overflow === 'overlay'
117
+ ) {
118
+ return node;
119
+ }
120
+ }
121
+ // A scrollable ancestor element was not found, which means that we need to
122
+ // do stuff on window.
123
+ return window;
124
+ }
125
+ function getBounds({node, scrollableAncestor, topOffset, bottomOffset}) {
126
+ const {top, bottom} = node.getBoundingClientRect();
127
+ let contextHeight;
128
+ let contextScrollTop;
129
+ if (scrollableAncestor === window) {
130
+ contextHeight = window.innerHeight;
131
+ contextScrollTop = 0;
132
+ } else {
133
+ const ancestorElement = scrollableAncestor;
134
+ contextHeight = ancestorElement.offsetHeight;
135
+ contextScrollTop = ancestorElement.getBoundingClientRect().top;
136
+ }
137
+ const contextBottom = contextScrollTop + contextHeight;
138
+ return {
139
+ top,
140
+ bottom,
141
+ viewportTop: contextScrollTop + topOffset,
142
+ viewportBottom: contextBottom - bottomOffset,
143
+ };
144
+ }
145
+ function getCurrentPosition(bounds) {
146
+ if (bounds.viewportBottom - bounds.viewportTop === 0) {
147
+ return 'invisible';
148
+ }
149
+ // top is within the viewport
150
+ if (bounds.viewportTop <= bounds.top && bounds.top <= bounds.viewportBottom) {
151
+ return 'inside';
152
+ }
153
+ // bottom is within the viewport
154
+ if (
155
+ bounds.viewportTop <= bounds.bottom &&
156
+ bounds.bottom <= bounds.viewportBottom
157
+ ) {
158
+ return 'inside';
159
+ }
160
+ // top is above the viewport and bottom is below the viewport
161
+ if (
162
+ bounds.top <= bounds.viewportTop &&
163
+ bounds.viewportBottom <= bounds.bottom
164
+ ) {
165
+ return 'inside';
166
+ }
167
+ if (bounds.viewportBottom < bounds.top) {
168
+ return 'below';
169
+ }
170
+ if (bounds.top < bounds.viewportTop) {
171
+ return 'above';
172
+ }
173
+ return 'invisible';
174
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@docusaurus/plugin-ideal-image",
3
- "version": "3.7.0-canary-6264",
3
+ "version": "3.7.0-canary-6265",
4
4
  "description": "Docusaurus Plugin to generate an almost ideal image (responsive, lazy-loading, and low quality placeholder).",
5
5
  "main": "lib/index.js",
6
6
  "types": "src/plugin-ideal-image.d.ts",
@@ -20,19 +20,18 @@
20
20
  },
21
21
  "license": "MIT",
22
22
  "dependencies": {
23
- "@docusaurus/core": "3.7.0-canary-6264",
24
- "@docusaurus/lqip-loader": "3.7.0-canary-6264",
23
+ "@docusaurus/core": "3.7.0-canary-6265",
24
+ "@docusaurus/lqip-loader": "3.7.0-canary-6265",
25
25
  "@docusaurus/responsive-loader": "^1.7.0",
26
- "@docusaurus/theme-translations": "3.7.0-canary-6264",
27
- "@docusaurus/types": "3.7.0-canary-6264",
28
- "@docusaurus/utils-validation": "3.7.0-canary-6264",
29
- "react-waypoint": "^10.3.0",
26
+ "@docusaurus/theme-translations": "3.7.0-canary-6265",
27
+ "@docusaurus/types": "3.7.0-canary-6265",
28
+ "@docusaurus/utils-validation": "3.7.0-canary-6265",
30
29
  "sharp": "^0.32.3",
31
30
  "tslib": "^2.6.0",
32
31
  "webpack": "^5.88.1"
33
32
  },
34
33
  "devDependencies": {
35
- "@docusaurus/module-type-aliases": "3.7.0-canary-6264",
34
+ "@docusaurus/module-type-aliases": "3.7.0-canary-6265",
36
35
  "fs-extra": "^11.1.0"
37
36
  },
38
37
  "peerDependencies": {
@@ -48,5 +47,5 @@
48
47
  "engines": {
49
48
  "node": ">=18.0"
50
49
  },
51
- "gitHead": "8a3b8621983c8d84b95160db1fcfd223b4c4b979"
50
+ "gitHead": "fca9417fbb5dc82196c1528c0a9341fdd5af770e"
52
51
  }
@@ -1,6 +1,5 @@
1
1
  import React, {Component} from 'react';
2
- // import PropTypes from 'prop-types'
3
- import {Waypoint} from 'react-waypoint';
2
+ import {Waypoint} from './waypoint';
4
3
  import Media from '../Media';
5
4
  import {icons, loadStates} from '../constants';
6
5
  import {xhrLoader, imageLoader, timeout, combineCancel} from '../loaders';
@@ -0,0 +1,239 @@
1
+ /*
2
+ This is a slimmed down copy of https://github.com/civiccc/react-waypoint
3
+ The MIT License (MIT)
4
+ Copyright (c) 2015 Brigade
5
+ */
6
+
7
+ import React, {createRef, ReactNode} from 'react';
8
+
9
+ type ScrollContainer = Window | HTMLElement;
10
+
11
+ function addEventListener(
12
+ element: ScrollContainer,
13
+ type: 'scroll' | 'resize',
14
+ listener: () => void,
15
+ options: AddEventListenerOptions,
16
+ ) {
17
+ element.addEventListener(type, listener, options);
18
+ return () => element.removeEventListener(type, listener, options);
19
+ }
20
+
21
+ type Position = 'above' | 'inside' | 'below' | 'invisible';
22
+
23
+ type Props = {
24
+ topOffset: number;
25
+ bottomOffset: number;
26
+ onEnter: () => void;
27
+ onLeave: () => void;
28
+ children: ReactNode;
29
+ };
30
+
31
+ export function Waypoint(props: Props) {
32
+ return typeof window !== 'undefined' ? (
33
+ <WaypointClient {...props}>{props.children}</WaypointClient>
34
+ ) : (
35
+ props.children
36
+ );
37
+ }
38
+
39
+ // TODO maybe replace this with IntersectionObserver later?
40
+ // IntersectionObserver doesn't support the "fast scroll" thing
41
+ // but it's probably not a big deal
42
+ class WaypointClient extends React.Component<Props> {
43
+ static defaultProps = {
44
+ topOffset: 0,
45
+ bottomOffset: 0,
46
+ onEnter() {},
47
+ onLeave() {},
48
+ };
49
+
50
+ scrollableAncestor?: ScrollContainer;
51
+ previousPosition: Position | null = null;
52
+ unsubscribe?: () => void;
53
+
54
+ innerRef = createRef<HTMLElement>();
55
+
56
+ override componentDidMount() {
57
+ this.scrollableAncestor = findScrollableAncestor(this.innerRef.current!);
58
+
59
+ const unsubscribeScroll = addEventListener(
60
+ this.scrollableAncestor!,
61
+ 'scroll',
62
+ this._handleScroll,
63
+ {passive: true},
64
+ );
65
+
66
+ const unsubscribeResize = addEventListener(
67
+ window,
68
+ 'resize',
69
+ this._handleScroll,
70
+ {passive: true},
71
+ );
72
+
73
+ this.unsubscribe = () => {
74
+ unsubscribeScroll();
75
+ unsubscribeResize();
76
+ };
77
+
78
+ this._handleScroll();
79
+ }
80
+
81
+ override componentDidUpdate() {
82
+ this._handleScroll();
83
+ }
84
+
85
+ override componentWillUnmount() {
86
+ this.unsubscribe?.();
87
+ }
88
+
89
+ _handleScroll = () => {
90
+ const node = this.innerRef.current;
91
+ const {topOffset, bottomOffset, onEnter, onLeave} = this.props;
92
+
93
+ const bounds = getBounds({
94
+ node: node!,
95
+ scrollableAncestor: this.scrollableAncestor!,
96
+ topOffset,
97
+ bottomOffset,
98
+ });
99
+
100
+ const currentPosition = getCurrentPosition(bounds);
101
+ const previousPosition = this.previousPosition;
102
+ this.previousPosition = currentPosition;
103
+
104
+ if (previousPosition === currentPosition) {
105
+ return;
106
+ }
107
+
108
+ if (currentPosition === 'inside') {
109
+ onEnter();
110
+ } else if (previousPosition === 'inside') {
111
+ onLeave();
112
+ }
113
+
114
+ const isRapidScrollDown =
115
+ previousPosition === 'below' && currentPosition === 'above';
116
+ const isRapidScrollUp =
117
+ previousPosition === 'above' && currentPosition === 'below';
118
+ if (isRapidScrollDown || isRapidScrollUp) {
119
+ onEnter();
120
+ onLeave();
121
+ }
122
+ };
123
+
124
+ override render() {
125
+ // @ts-expect-error: fix this implicit API
126
+ return React.cloneElement(this.props.children, {innerRef: this.innerRef});
127
+ }
128
+ }
129
+
130
+ /**
131
+ * Traverses up the DOM to find an ancestor container which has an overflow
132
+ * style that allows for scrolling.
133
+ *
134
+ * @return {Object} the closest ancestor element with an overflow style that
135
+ * allows for scrolling. If none is found, the `window` object is returned
136
+ * as a fallback.
137
+ */
138
+ function findScrollableAncestor(inputNode: HTMLElement): ScrollContainer {
139
+ let node: HTMLElement = inputNode;
140
+
141
+ while (node.parentNode) {
142
+ // @ts-expect-error: it's fine
143
+ node = node.parentNode!;
144
+
145
+ if (node === document.body) {
146
+ // We've reached all the way to the root node.
147
+ return window;
148
+ }
149
+
150
+ const style = window.getComputedStyle(node);
151
+ const overflow =
152
+ style.getPropertyValue('overflow-y') ||
153
+ style.getPropertyValue('overflow');
154
+
155
+ if (
156
+ overflow === 'auto' ||
157
+ overflow === 'scroll' ||
158
+ overflow === 'overlay'
159
+ ) {
160
+ return node;
161
+ }
162
+ }
163
+
164
+ // A scrollable ancestor element was not found, which means that we need to
165
+ // do stuff on window.
166
+ return window;
167
+ }
168
+
169
+ type Bounds = {
170
+ top: number;
171
+ bottom: number;
172
+ viewportTop: number;
173
+ viewportBottom: number;
174
+ };
175
+
176
+ function getBounds({
177
+ node,
178
+ scrollableAncestor,
179
+ topOffset,
180
+ bottomOffset,
181
+ }: {
182
+ node: Element;
183
+ scrollableAncestor: ScrollContainer;
184
+ topOffset: number;
185
+ bottomOffset: number;
186
+ }): Bounds {
187
+ const {top, bottom} = node.getBoundingClientRect();
188
+
189
+ let contextHeight;
190
+ let contextScrollTop;
191
+ if (scrollableAncestor === window) {
192
+ contextHeight = window.innerHeight;
193
+ contextScrollTop = 0;
194
+ } else {
195
+ const ancestorElement = scrollableAncestor as HTMLElement;
196
+ contextHeight = ancestorElement.offsetHeight;
197
+ contextScrollTop = ancestorElement.getBoundingClientRect().top;
198
+ }
199
+
200
+ const contextBottom = contextScrollTop + contextHeight;
201
+
202
+ return {
203
+ top,
204
+ bottom,
205
+ viewportTop: contextScrollTop + topOffset,
206
+ viewportBottom: contextBottom - bottomOffset,
207
+ };
208
+ }
209
+
210
+ function getCurrentPosition(bounds: Bounds): Position {
211
+ if (bounds.viewportBottom - bounds.viewportTop === 0) {
212
+ return 'invisible';
213
+ }
214
+ // top is within the viewport
215
+ if (bounds.viewportTop <= bounds.top && bounds.top <= bounds.viewportBottom) {
216
+ return 'inside';
217
+ }
218
+ // bottom is within the viewport
219
+ if (
220
+ bounds.viewportTop <= bounds.bottom &&
221
+ bounds.bottom <= bounds.viewportBottom
222
+ ) {
223
+ return 'inside';
224
+ }
225
+ // top is above the viewport and bottom is below the viewport
226
+ if (
227
+ bounds.top <= bounds.viewportTop &&
228
+ bounds.viewportBottom <= bounds.bottom
229
+ ) {
230
+ return 'inside';
231
+ }
232
+ if (bounds.viewportBottom < bounds.top) {
233
+ return 'below';
234
+ }
235
+ if (bounds.top < bounds.viewportTop) {
236
+ return 'above';
237
+ }
238
+ return 'invisible';
239
+ }