136 lines
3.6 KiB
TypeScript
136 lines
3.6 KiB
TypeScript
"use client";
|
|
|
|
interface DigitalClockProps {
|
|
time: Date;
|
|
timezone: string;
|
|
showDate: boolean;
|
|
showSeconds: boolean;
|
|
format24h: boolean;
|
|
theme: "light" | "dark" | "custom";
|
|
compact?: boolean; // 작은 크기에서 사용
|
|
customColor?: string; // 사용자 지정 색상
|
|
}
|
|
|
|
/**
|
|
* 디지털 시계 컴포넌트
|
|
* - 실시간 시간 표시
|
|
* - 타임존 지원
|
|
* - 날짜/초 표시 옵션
|
|
* - 12/24시간 형식 지원
|
|
*/
|
|
export function DigitalClock({
|
|
time,
|
|
timezone,
|
|
showDate,
|
|
showSeconds,
|
|
format24h,
|
|
theme,
|
|
compact = false,
|
|
customColor,
|
|
}: DigitalClockProps) {
|
|
// 시간 포맷팅 (타임존 적용)
|
|
const timeString = new Intl.DateTimeFormat("ko-KR", {
|
|
timeZone: timezone,
|
|
hour: "2-digit",
|
|
minute: "2-digit",
|
|
second: showSeconds ? "2-digit" : undefined,
|
|
hour12: !format24h,
|
|
}).format(time);
|
|
|
|
// 날짜 포맷팅 (타임존 적용)
|
|
const dateString = showDate
|
|
? new Intl.DateTimeFormat("ko-KR", {
|
|
timeZone: timezone,
|
|
year: "numeric",
|
|
month: "long",
|
|
day: "numeric",
|
|
weekday: "long",
|
|
}).format(time)
|
|
: null;
|
|
|
|
// 타임존 라벨
|
|
const timezoneLabel = getTimezoneLabel(timezone);
|
|
|
|
// 테마별 스타일
|
|
const themeClasses = getThemeClasses(theme, customColor);
|
|
|
|
return (
|
|
<div
|
|
className={`flex h-full flex-col items-center justify-center ${compact ? "p-0.5" : "p-2"} text-center ${themeClasses.container}`}
|
|
style={themeClasses.style}
|
|
>
|
|
{/* 날짜 표시 (compact 모드에서는 숨김) */}
|
|
{!compact && showDate && dateString && (
|
|
<div className={`mb-1 text-[10px] leading-tight font-medium ${themeClasses.date}`}>{dateString}</div>
|
|
)}
|
|
|
|
{/* 시간 표시 */}
|
|
<div className={`leading-none font-bold tabular-nums ${themeClasses.time} ${compact ? "text-sm" : "text-2xl"}`}>
|
|
{timeString}
|
|
</div>
|
|
|
|
{/* 타임존 표시 */}
|
|
<div className={`${compact ? "mt-0" : "mt-1"} text-[9px] font-medium ${themeClasses.timezone}`}>
|
|
{timezoneLabel}
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
/**
|
|
* 타임존 라벨 반환
|
|
*/
|
|
function getTimezoneLabel(timezone: string): string {
|
|
const timezoneLabels: Record<string, string> = {
|
|
"Asia/Seoul": "서울 (KST)",
|
|
"Asia/Tokyo": "도쿄 (JST)",
|
|
"Asia/Shanghai": "베이징 (CST)",
|
|
"America/New_York": "뉴욕 (EST)",
|
|
"America/Los_Angeles": "LA (PST)",
|
|
"Europe/London": "런던 (GMT)",
|
|
"Europe/Paris": "파리 (CET)",
|
|
"Australia/Sydney": "시드니 (AEDT)",
|
|
};
|
|
|
|
return timezoneLabels[timezone] || timezone;
|
|
}
|
|
|
|
/**
|
|
* 테마별 클래스 반환
|
|
*/
|
|
function getThemeClasses(theme: string, customColor?: string) {
|
|
if (theme === "custom" && customColor) {
|
|
// 사용자 지정 색상 사용
|
|
return {
|
|
container: "text-primary-foreground",
|
|
date: "text-primary-foreground/80",
|
|
time: "text-primary-foreground",
|
|
timezone: "text-primary-foreground/70",
|
|
style: { backgroundColor: customColor },
|
|
};
|
|
}
|
|
|
|
const themes = {
|
|
light: {
|
|
container: "bg-background text-foreground",
|
|
date: "text-foreground",
|
|
time: "text-foreground",
|
|
timezone: "text-muted-foreground",
|
|
},
|
|
dark: {
|
|
container: "bg-foreground text-background",
|
|
date: "text-background/80",
|
|
time: "text-background",
|
|
timezone: "text-background/70",
|
|
},
|
|
custom: {
|
|
container: "bg-gradient-to-br from-primary to-primary/80 text-primary-foreground",
|
|
date: "text-primary-foreground/70",
|
|
time: "text-primary-foreground",
|
|
timezone: "text-primary-foreground/80",
|
|
},
|
|
};
|
|
|
|
return themes[theme as keyof typeof themes] || themes.light;
|
|
}
|