Skip to content

Commit 6fa1de1

Browse files
authored
Prevent initial rendering of DoenetML when it is hidden (#279)
1 parent 908c6e7 commit 6fa1de1

File tree

6 files changed

+80
-13
lines changed

6 files changed

+80
-13
lines changed

package-lock.json

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/doenetml-iframe/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"name": "@doenet/doenetml-iframe",
33
"type": "module",
44
"description": "A renderer for DoenetML contained in an iframe",
5-
"version": "0.7.0-alpha25",
5+
"version": "0.7.0-alpha26",
66
"license": "AGPL-3.0-or-later",
77
"homepage": "https://github.com/Doenet/DoenetML#readme",
88
"private": true,

packages/doenetml/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"name": "@doenet/doenetml",
33
"type": "module",
44
"description": "Semantic markup for building interactive web activities",
5-
"version": "0.7.0-alpha25",
5+
"version": "0.7.0-alpha26",
66
"license": "AGPL-3.0-or-later",
77
"homepage": "https://github.com/Doenet/DoenetML#readme",
88
"private": true,

packages/doenetml/src/doenetml.tsx

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import "./DoenetML.css";
22
// @ts-ignore
33
import { prng_alea } from "esm-seedrandom";
4-
import React, { useCallback, useRef, useState } from "react";
4+
import React, { useCallback, useEffect, useRef, useState } from "react";
55
import { DocViewer } from "./Viewer/DocViewer";
66
import { RecoilRoot } from "recoil";
77
import { MathJaxContext } from "better-react-mathjax";
@@ -12,6 +12,7 @@ import { Box, ChakraProvider, extendTheme } from "@chakra-ui/react";
1212
import "@doenet/virtual-keyboard/style.css";
1313
import { EditorViewer } from "./EditorViewer/EditorViewer.js";
1414
import VariantSelect from "./EditorViewer/VariantSelect";
15+
import { useIsOnPage } from "./utils/isVisible";
1516

1617
export const version: string = DOENETML_VERSION;
1718

@@ -101,7 +102,6 @@ export function DoenetViewer({
101102
userId,
102103
attemptNumber = 1,
103104
render = true,
104-
hidden = false,
105105
requestedVariantIndex,
106106
updateCreditAchievedCallback,
107107
setIsInErrorState,
@@ -129,7 +129,6 @@ export function DoenetViewer({
129129
userId?: string;
130130
attemptNumber?: number;
131131
render?: boolean;
132-
hidden?: boolean;
133132
requestedVariantIndex?: number;
134133
updateCreditAchievedCallback?: Function;
135134
setIsInErrorState?: Function;
@@ -160,6 +159,20 @@ export function DoenetViewer({
160159

161160
const variantIndex = useRef(1);
162161

162+
// Start off hidden and then unhide once the viewer is visible.
163+
// This is needed to delay the initialization of JSXgraph
164+
// until it is no longer hidden.
165+
// Otherwise, the graphs are often displayed in a garbled fashion
166+
// with the bounded calculated incorrectly
167+
const ref = useRef<HTMLDivElement>(null);
168+
const isOnPage = useIsOnPage(ref);
169+
const [hidden, setHidden] = useState(true);
170+
useEffect(() => {
171+
if (isOnPage) {
172+
setHidden(false);
173+
}
174+
}, [isOnPage]);
175+
163176
const flags: DoenetMLFlags = { ...defaultFlags, ...specifiedFlags };
164177

165178
if (userId) {
@@ -270,10 +283,12 @@ export function DoenetViewer({
270283
>
271284
<RecoilRoot>
272285
<MathJaxContext version={3} config={mathjaxConfig}>
273-
{variantSelector}
274-
{viewer}
275-
<div className="before-keyboard" />
276-
{keyboard}
286+
<div ref={ref}>
287+
{variantSelector}
288+
{viewer}
289+
<div className="before-keyboard" />
290+
{keyboard}
291+
</div>
277292
</MathJaxContext>
278293
</RecoilRoot>
279294
</ChakraProvider>
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import { RefObject, useEffect, useState } from "react";
2+
3+
/**
4+
* Returns `true` if the any portion of the element referenced by `ref`
5+
* is visible in the browser's viewport
6+
*
7+
* From: https://dev.to/jmalvarez/check-if-an-element-is-visible-with-react-hooks-27h8
8+
*/
9+
export function useIsVisible(ref: RefObject<HTMLElement>) {
10+
const [isIntersecting, setIntersecting] = useState(false);
11+
12+
useEffect(() => {
13+
const observer = new IntersectionObserver(([entry]) =>
14+
setIntersecting(entry.isIntersecting),
15+
);
16+
17+
if (ref.current) {
18+
observer.observe(ref.current);
19+
}
20+
return () => {
21+
observer.disconnect();
22+
};
23+
}, [ref]);
24+
25+
return isIntersecting;
26+
}
27+
28+
/**
29+
* Returns true if the element referenced by `ref` is anywhere on the page
30+
* (more precisely, within 1000000px of the browser's viewport).
31+
*
32+
* Used to approximately detect if the element is not hidden.
33+
*/
34+
export function useIsOnPage(ref: RefObject<HTMLElement>) {
35+
const [isIntersecting, setIntersecting] = useState(false);
36+
37+
useEffect(() => {
38+
const observer = new IntersectionObserver(
39+
([entry]) => setIntersecting(entry.isIntersecting),
40+
{ rootMargin: "1000000px" },
41+
);
42+
43+
if (ref.current) {
44+
observer.observe(ref.current);
45+
}
46+
return () => {
47+
observer.disconnect();
48+
};
49+
}, [ref]);
50+
51+
return isIntersecting;
52+
}

packages/standalone/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"name": "@doenet/standalone",
33
"type": "module",
44
"description": "Standalone renderer for DoenetML suitable for being included in a web page",
5-
"version": "0.7.0-alpha25",
5+
"version": "0.7.0-alpha26",
66
"license": "AGPL-3.0-or-later",
77
"homepage": "https://github.com/Doenet/DoenetML#readme",
88
"private": true,

0 commit comments

Comments
 (0)