"use client";
import * as React from "react";
import { TextCycle } from "@/components/ui/text-cycle";
import { clsx, type ClassValue } from "clsx";
import { twMerge } from "tailwind-merge";
function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}
const DEFAULT_WORDS = ["engineer", "developer", "designer"] as const;
const settings = {
words: DEFAULT_WORDS.join("\n"),
interval: 2500,
paused: false,
};
const previewClass =
"font-display text-4xl font-semibold tracking-tight text-foreground sm:text-5xl";
function parseWords(raw: string): string[] {
return raw
.split("\n")
.map((line) => line.trim())
.filter(Boolean);
}
function CycleLine({
words,
interval,
paused,
className,
cycleKey,
}: {
words: string[];
interval?: number;
paused?: boolean;
className?: string;
cycleKey?: string;
}) {
let longest = words[0] ?? "developer";
for (const word of words) {
if (word.length > longest.length) longest = word;
}
return (
<span className={cn("relative inline-flex", className)}>
{/* Width lock so the block stays centered while words change */}
<span className="invisible whitespace-nowrap" aria-hidden="true">
21 · {longest}
</span>
<span className="absolute inset-0 flex items-baseline justify-start whitespace-nowrap">
<span className="shrink-0 text-muted-foreground">21 · </span>
<TextCycle
key={cycleKey}
words={words}
interval={interval}
paused={paused}
/>
</span>
</span>
);
}
export function TextCyclePreview(props: Partial<typeof settings> = {}) {
const state = { ...settings, ...props };
const words = parseWords(state.words);
const resolved = words.length > 0 ? words : [...DEFAULT_WORDS];
return (
<div className="flex min-h-[360px] w-full items-center justify-center px-4 py-12 sm:px-8">
<CycleLine
cycleKey={`${state.interval}-${resolved.join("|")}`}
words={resolved}
interval={state.interval}
paused={state.paused}
className={previewClass}
/>
</div>
);
}
export default function Demo(props: Partial<typeof settings>) {
return <TextCyclePreview {...props} />;
}