Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions apps/web/app/entry.client.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
* See the LICENSE file for details.
*/

import "@/lib/polyfills";
import { startTransition, StrictMode } from "react";
import { hydrateRoot } from "react-dom/client";
import { HydratedRouter } from "react-router/dom";
Expand Down
18 changes: 10 additions & 8 deletions apps/web/core/components/core/render-if-visible-HOC.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import type { ReactNode, MutableRefObject } from "react";
import React, { useState, useRef, useEffect } from "react";
import { cn } from "@plane/utils";
import { scheduleIdleCallback } from "@/lib/polyfills";

type Props = {
defaultHeight?: string;
Expand All @@ -23,6 +24,10 @@ type Props = {
forceRender?: boolean;
};

/**
* Renders children only when the element intersects the viewport (or is forced visible), using a placeholder and
* optional height recording to reduce work for long lists.
*/
function RenderIfVisible(props: Props) {
const {
defaultHeight = "300px",
Expand Down Expand Up @@ -51,8 +56,8 @@ function RenderIfVisible(props: Props) {
const observer = new IntersectionObserver(
(entries) => {
//DO no remove comments for future
if (typeof window !== undefined && window.requestIdleCallback && useIdletime) {
window.requestIdleCallback(() => setShouldVisible(entries[entries.length - 1].isIntersecting), {
if (typeof window !== "undefined" && useIdletime) {
scheduleIdleCallback(() => setShouldVisible(entries[entries.length - 1].isIntersecting), {
timeout: 300,
});
} else {
Expand All @@ -66,18 +71,15 @@ function RenderIfVisible(props: Props) {
);
observer.observe(intersectionRef.current);
return () => {
if (intersectionRef.current) {
// eslint-disable-next-line react-hooks/exhaustive-deps
observer.unobserve(intersectionRef.current);
}
observer.disconnect();
};
}
}, [intersectionRef, children, root, verticalOffset, horizontalOffset]);
}, [intersectionRef, root, verticalOffset, horizontalOffset, useIdletime]);

//Set height after render
useEffect(() => {
if (intersectionRef.current && isVisible && shouldRecordHeights) {
window.requestIdleCallback(() => {
scheduleIdleCallback(() => {
if (intersectionRef.current) placeholderHeight.current = `${intersectionRef.current.offsetHeight}px`;
});
}
Expand Down
29 changes: 26 additions & 3 deletions apps/web/core/lib/polyfills/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,16 @@
* See the LICENSE file for details.
*/

if (typeof window !== "undefined" && window) {
// Add request callback polyfill to browser in case it does not exist
/**
* Ensures `window.requestIdleCallback` and `window.cancelIdleCallback` exist.
* Installs minimal shims when the browser omits them (e.g. older Safari / WebKit).
* Safe to call repeatedly; only assigns missing APIs once.
*/
function ensureRequestIdleCallbackPolyfilled(): void {
if (typeof window === "undefined" || !window) {
return;
}

window.requestIdleCallback =
window.requestIdleCallback ??
function (cb) {
Expand All @@ -27,4 +35,19 @@ if (typeof window !== "undefined" && window) {
};
}

export {};
ensureRequestIdleCallbackPolyfilled();

/**
* Schedules work to run when the browser is idle, or after a short delay when idle scheduling is unavailable.
*
* @param callback - Invoked with an `IdleDeadline`-like object (native or polyfilled).
* @param options - Optional `timeout` forwarded to the native API when present.
* @returns An idle handle for cancellation, or `0` when `window` is undefined (SSR).
*/
export function scheduleIdleCallback(callback: IdleRequestCallback, options?: IdleRequestOptions): number {
ensureRequestIdleCallbackPolyfilled();
if (typeof window === "undefined") {
return 0;
}
return window.requestIdleCallback(callback, options);
}
Loading