chore: import upstream snapshot with attribution

This commit is contained in:
wehub-resource-sync
2026-07-13 12:37:47 +08:00
commit 7653f56fed
1422 changed files with 359026 additions and 0 deletions
+5
View File
@@ -0,0 +1,5 @@
import classNames from "classnames";
export function cn(...classes: classNames.ArgumentArray) {
return classNames(...classes);
}
+30
View File
@@ -0,0 +1,30 @@
import { debounce } from "lodash-es";
export default (canvas: HTMLCanvasElement) => {
const { width, height } = canvas.getBoundingClientRect();
const ctx = canvas.getContext("2d")!;
canvas.style.width = `${width}px`;
canvas.style.height = `${height}px`;
const upscaleCanvas = () => {
const scale = window.visualViewport?.scale || 1;
const dpr = (window.devicePixelRatio || 1) * scale;
canvas.width = width * dpr;
canvas.height = height * dpr;
ctx.scale(dpr, dpr);
canvas.dispatchEvent(new Event("resize"));
};
upscaleCanvas();
const handleResize = debounce(upscaleCanvas, 500);
window.addEventListener("resize", handleResize);
window.visualViewport?.addEventListener("resize", handleResize);
return ctx;
};
+29
View File
@@ -0,0 +1,29 @@
const onVisible = (
element: HTMLElement,
callback: () => void,
threshold = 0.1,
) => {
const observer = new IntersectionObserver(
([entry]) => {
if (entry.isIntersecting) {
callback();
observer.disconnect();
}
},
{ threshold },
);
observer.observe(element);
return () => {
observer.disconnect();
};
};
export default onVisible;
export const waitUntilVisible = (element: HTMLElement, threshold = 0.1) => {
return new Promise((resolve) => {
onVisible(element, () => resolve(true), threshold);
});
};
@@ -0,0 +1,119 @@
export function setTimeoutOnVisible({
element,
callback,
timeout,
threshold = 0.01,
}: {
element?: HTMLElement;
callback: () => void;
timeout: number;
threshold?: number;
}) {
if (!element) {
return;
}
let timeoutId: NodeJS.Timeout | null = null;
let finished = false;
// Check if element is already visible at the beginning
const rect = element.getBoundingClientRect();
const isVisible = rect.top < window.innerHeight && rect.bottom > 0;
const setupTimeout = () => {
if (finished) return;
timeoutId = setTimeout(() => {
if (finished) return;
callback();
timeoutId = null;
finished = true;
}, timeout);
};
if (isVisible && !timeoutId) setupTimeout();
const observer = new IntersectionObserver(
(entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
// Element is visible, start the timeout
if (!timeoutId) {
setupTimeout();
observer.disconnect();
}
} else {
// Element is no longer visible, clear the timeout
if (timeoutId) {
clearTimeout(timeoutId);
timeoutId = null;
}
}
});
},
{ threshold },
);
observer.observe(element);
// Return a cleanup function
return () => {
if (timeoutId) {
clearTimeout(timeoutId);
}
observer.disconnect();
};
}
export function setIntervalOnVisible({
element,
callback,
interval,
threshold = 0.01,
}: {
element?: HTMLElement | null;
callback: () => void;
interval: number;
threshold?: number;
immediate?: boolean;
}) {
if (!element) {
return;
}
let intervalId: NodeJS.Timeout | null = null;
const observer = new IntersectionObserver(
(entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
// Element is visible, start the interval
if (!intervalId) {
intervalId = setInterval(callback, interval);
}
} else {
// Element is no longer visible, clear the interval
if (intervalId) {
clearInterval(intervalId);
intervalId = null;
}
}
});
},
{ threshold },
);
observer.observe(element);
// Return a cleanup function
return () => {
if (intervalId) {
clearInterval(intervalId);
}
observer.disconnect();
};
}
export default setTimeoutOnVisible;
+2
View File
@@ -0,0 +1,2 @@
export const sleep = (ms: number) =>
new Promise((resolve) => setTimeout(resolve, ms));