| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051 |
- import { clsx, type ClassValue } from "clsx";
- import { twMerge } from "tailwind-merge";
- export function cn(...inputs: ClassValue[]) {
- return twMerge(clsx(inputs));
- }
- export function formatDateTime(value?: string | null) {
- if (!value) return "Never";
- return new Intl.DateTimeFormat(undefined, {
- month: "short",
- day: "numeric",
- hour: "2-digit",
- minute: "2-digit",
- }).format(new Date(value));
- }
- export function relativeTime(value?: string | null) {
- if (!value) return "Never";
- const deltaSeconds = Math.round((new Date(value).getTime() - Date.now()) / 1000);
- const units: Array<[Intl.RelativeTimeFormatUnit, number]> = [
- ["year", 31536000],
- ["month", 2592000],
- ["day", 86400],
- ["hour", 3600],
- ["minute", 60],
- ["second", 1],
- ];
- const formatter = new Intl.RelativeTimeFormat(undefined, { numeric: "auto" });
- const [unit, seconds] = units.find(([, amount]) => Math.abs(deltaSeconds) >= amount) ?? ["second", 1];
- return formatter.format(Math.round(deltaSeconds / seconds), unit);
- }
- export function truncateMiddle(value: string, max = 18) {
- if (value.length <= max) return value;
- const keep = Math.max(4, Math.floor((max - 3) / 2));
- return `${value.slice(0, keep)}...${value.slice(-keep)}`;
- }
- export async function copyToClipboard(value: string) {
- await navigator.clipboard.writeText(value);
- }
- export function slugifyName(value: string, fallback = "item") {
- const slug = value
- .trim()
- .toLowerCase()
- .replace(/[^a-z0-9]+/g, "_")
- .replace(/^_+|_+$/g, "");
- return slug || `${fallback}_${Date.now().toString(36)}`;
- }
|