98 lines
2.4 KiB
TypeScript
98 lines
2.4 KiB
TypeScript
import { type ClassValue, clsx } from "clsx";
|
|
import { twMerge } from "tailwind-merge";
|
|
|
|
export function cn(...inputs: ClassValue[]) {
|
|
return twMerge(clsx(inputs));
|
|
}
|
|
|
|
const clampToZero = (n: number) => Math.max(n, 0);
|
|
|
|
export const formatMs = (elapsed: number) => {
|
|
let seconds = clampToZero(Math.floor(elapsed / 1000));
|
|
let minutes = clampToZero(Math.floor(seconds / 60));
|
|
let hours = clampToZero(Math.floor(minutes / 60));
|
|
const days = clampToZero(Math.floor(hours / 24));
|
|
|
|
seconds = seconds % 60;
|
|
minutes = minutes % 60;
|
|
hours = hours % 24;
|
|
|
|
const ago =
|
|
days === 0 && hours === 0 && minutes === 0 && seconds === 0
|
|
? "now"
|
|
: days === 0 && hours === 0 && minutes === 0
|
|
? `${seconds}s ago`
|
|
: days === 0 && hours === 0
|
|
? `${minutes}m ago`
|
|
: days === 0
|
|
? `${hours}h ago`
|
|
: `${days}d ago`;
|
|
|
|
return {
|
|
ago,
|
|
hour: hours,
|
|
minute: minutes,
|
|
second: seconds,
|
|
day: days,
|
|
};
|
|
};
|
|
|
|
export function toDate(
|
|
time: string,
|
|
defaultDate: Date | null = new Date(0),
|
|
): Date | null {
|
|
time = time.replace(/\.(\d{3})\d*/, ".$1");
|
|
|
|
if (!time.endsWith("Z")) {
|
|
time += "Z";
|
|
}
|
|
|
|
const date = new Date(time);
|
|
|
|
if (isNaN(date.getTime())) {
|
|
return defaultDate;
|
|
}
|
|
|
|
return date;
|
|
}
|
|
|
|
/** Returns a date in the format 'July 14th at 4:52pm' */
|
|
export function formatDate(date: Date): string {
|
|
const options: Intl.DateTimeFormatOptions = {
|
|
month: "long",
|
|
day: "numeric",
|
|
hour: "numeric",
|
|
minute: "numeric",
|
|
hour12: true,
|
|
};
|
|
return date.toLocaleString("en-US", options);
|
|
}
|
|
|
|
/**
|
|
* Handle infinite scroll logic for loading more items
|
|
* @param event - React scroll event
|
|
* @param fetchNextPage - Function to fetch next page
|
|
* @param hasNextPage - Whether there are more pages to fetch
|
|
* @param isFetchingNextPage - Whether currently fetching
|
|
* @param scrollThreshold - Percentage threshold to trigger fetch (default 0.8)
|
|
*/
|
|
export const handleInfiniteScroll = (
|
|
event: React.UIEvent<HTMLDivElement>,
|
|
fetchNextPage: () => void,
|
|
hasNextPage: boolean,
|
|
isFetchingNextPage: boolean,
|
|
scrollThreshold: number = 0.8,
|
|
) => {
|
|
const target = event.currentTarget;
|
|
const scrollPercentage =
|
|
(target.scrollTop + target.clientHeight) / target.scrollHeight;
|
|
|
|
if (
|
|
scrollPercentage >= scrollThreshold &&
|
|
hasNextPage &&
|
|
!isFetchingNextPage
|
|
) {
|
|
fetchNextPage();
|
|
}
|
|
};
|