timvir 0.2.31 → 0.2.33

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.
@@ -217,54 +217,6 @@ function Viewport(props, ref) {
217
217
  setHeight(height);
218
218
  setMaxHeight(Math.max(height, maxHeight ?? 0));
219
219
  });
220
-
221
- /*
222
- * Note this useEffect runs on each render. This is fine beause it doesn't do any
223
- * expensive computation, and the Viewport component only re-renders when the iframe
224
- * itself changes height or the user resizes the width of the Viewport.
225
- */
226
- React.useEffect(() => {
227
- const document = iframeRef.current?.contentDocument;
228
- if (document) {
229
- injectStyle(document);
230
-
231
- /*
232
- * The <body> element of the iframe document is the one which we observe and
233
- * measure. It should not have any margins (we remove them by injecting a simple
234
- * style reset into the iframe document).
235
- */
236
- iframeRO.observe(document.body);
237
- }
238
- });
239
-
240
- /*
241
- * Inject a simple style reset into the document.
242
- */
243
- function injectStyle(document) {
244
- if (document.querySelector("style#timvir-viewport-style")) {
245
- return;
246
- }
247
- const style = document.createElement("style");
248
- style.id = "timvir-viewport-style";
249
- style.innerHTML = "body { margin: 0 }";
250
- document.head.appendChild(style);
251
- }
252
-
253
- /*
254
- * 20ms after the height has been set, we consider this block settled. This
255
- * time includes the 16ms CSS transition time, and a tiny bit more to allow
256
- * for the content in the iframe itself to finish rendering.
257
- */
258
- React.useEffect(() => {
259
- const timeoutId = setTimeout(() => {
260
- mutate(draft => {
261
- draft.settled = true;
262
- });
263
- }, 20);
264
- return () => {
265
- clearTimeout(timeoutId);
266
- };
267
- }, [mutate, height]);
268
220
  return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("div", {
269
221
  ref: containerRef
270
222
  }), /*#__PURE__*/React.createElement(Root, {
@@ -295,14 +247,68 @@ function Viewport(props, ref) {
295
247
  src: src,
296
248
  onLoad: ev => {
297
249
  const document = ev.currentTarget.contentDocument;
298
- if (document) {
299
- injectStyle(document);
300
- const {
301
- height
302
- } = document.body.getBoundingClientRect();
303
- setHeight(height);
304
- setMaxHeight(height);
250
+ if (!document) {
251
+ console.warn(`Viewport: iframe has no contentDocument`);
252
+ return;
305
253
  }
254
+
255
+ /*
256
+ * Some browsers (notably Firefox) don't always fully initialize the <iframe>
257
+ * element (see https://bugzilla.mozilla.org/show_bug.cgi?id=1191683). The
258
+ * 'head' and 'body' properties in Document (which we access below) may be null
259
+ * when this onLoad callback runs.
260
+ *
261
+ * We use the Document readyState to decide if we can interact and observe the
262
+ * DOM.
263
+ */
264
+
265
+ const initializeDocument = () => {
266
+ if (document.readyState === "complete") {
267
+ /*
268
+ * Inject a simple style reset into the document.
269
+ */
270
+ {
271
+ const style = document.createElement("style");
272
+ style.id = "timvir-viewport-style";
273
+ style.innerHTML = "body { margin: 0 }";
274
+ document.head.appendChild(style);
275
+ }
276
+ {
277
+ const {
278
+ height
279
+ } = document.body.getBoundingClientRect();
280
+ setHeight(height);
281
+ setMaxHeight(height);
282
+ }
283
+
284
+ /*
285
+ * The <body> element of the iframe document is the one which we observe and
286
+ * measure. It should not have any margins (we remove them by injecting a simple
287
+ * style reset into the iframe document).
288
+ */
289
+ iframeRO.observe(document.body);
290
+
291
+ /*
292
+ * 50ms after the iframe document is complete, we consider the Viewport block
293
+ * settled. This time includes the 16ms CSS transition time, and a tiny bit more
294
+ * to allow for the content in the iframe itself to finish rendering.
295
+ *
296
+ * This time is not quite enough if the iframe loads additional code or needs to
297
+ * load data from a remote source. But should be enough for statically rendered
298
+ * content.
299
+ */
300
+ setTimeout(() => {
301
+ mutate(draft => {
302
+ draft.settled = true;
303
+ });
304
+ }, 50);
305
+ } else {
306
+ document?.addEventListener("readystatechange", initializeDocument, {
307
+ once: true
308
+ });
309
+ }
310
+ };
311
+ initializeDocument();
306
312
  },
307
313
  className: "i11uos8q",
308
314
  style: {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "type": "module",
3
3
  "name": "timvir",
4
- "version": "0.2.31",
4
+ "version": "0.2.33",
5
5
  "license": "MIT",
6
6
  "sideEffects": false,
7
7
  "exports": {