Skip to content
Open
Show file tree
Hide file tree
Changes from 3 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, children, root, verticalOffset, horizontalOffset, useIdletime]);
Comment thread
rbshh marked this conversation as resolved.
Outdated

//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