128 lines
4.5 KiB
TypeScript
128 lines
4.5 KiB
TypeScript
|
|
import React from 'react';
|
|
|
|
// --- StatCard: 대시보드 및 자산 요약용 ---
|
|
interface StatCardProps {
|
|
title: string;
|
|
value: string;
|
|
change?: string;
|
|
isUp?: boolean;
|
|
icon: React.ReactNode;
|
|
}
|
|
|
|
export const StatCard: React.FC<StatCardProps> = ({ title, value, change, isUp, icon }) => (
|
|
<div className="bg-white p-4 rounded-2xl shadow-sm border border-slate-100 flex items-center justify-between group hover:border-blue-100 transition-all">
|
|
<div className="min-w-0 flex-1">
|
|
<p className="text-[10px] font-black text-slate-400 mb-1 uppercase tracking-widest truncate">{title}</p>
|
|
<h4 className="text-[18px] font-black text-slate-900 leading-none truncate">{value}</h4>
|
|
{change && (
|
|
<p className={`text-[10px] font-black mt-2 flex items-center gap-1.5 ${isUp ? 'text-emerald-500' : 'text-rose-500'}`}>
|
|
{change}
|
|
</p>
|
|
)}
|
|
</div>
|
|
<div className="bg-slate-50 p-3 rounded-xl group-hover:bg-blue-50 transition-colors shrink-0 ml-3">
|
|
{React.cloneElement(icon as React.ReactElement, { size: 18 })}
|
|
</div>
|
|
</div>
|
|
);
|
|
|
|
// --- FilterChip: 시장/정렬/카테고리 필터용 ---
|
|
interface FilterChipProps {
|
|
active: boolean;
|
|
onClick: () => void;
|
|
label: string;
|
|
}
|
|
|
|
export const FilterChip: React.FC<FilterChipProps> = ({ active, onClick, label }) => (
|
|
<button
|
|
onClick={onClick}
|
|
className={`px-5 py-2 rounded-full text-[12px] font-black transition-all ${active ? 'bg-slate-900 text-white shadow-lg' : 'bg-slate-100 text-slate-500 hover:bg-slate-200'}`}
|
|
>
|
|
{label}
|
|
</button>
|
|
);
|
|
|
|
// --- TabButton: 대분류 섹션 전환용 ---
|
|
interface TabButtonProps {
|
|
active: boolean;
|
|
onClick: () => void;
|
|
icon: React.ReactNode;
|
|
label: string;
|
|
}
|
|
|
|
export const TabButton: React.FC<TabButtonProps> = ({ active, onClick, icon, label }) => (
|
|
<button
|
|
onClick={onClick}
|
|
className={`flex items-center gap-3 pb-4 whitespace-nowrap transition-all border-b-2 ${active ? 'border-blue-600 text-blue-600 font-black' : 'border-transparent text-slate-400 font-bold hover:text-slate-600'}`}
|
|
>
|
|
{icon}
|
|
<span className="text-base tracking-tight">{label}</span>
|
|
</button>
|
|
);
|
|
|
|
// --- InputGroup: 설정창 및 폼 입력용 ---
|
|
interface InputGroupProps {
|
|
label: string;
|
|
value: string;
|
|
onChange: (v: string) => void;
|
|
placeholder: string;
|
|
type?: string;
|
|
icon?: React.ReactNode;
|
|
}
|
|
|
|
export const InputGroup: React.FC<InputGroupProps> = ({ label, value, onChange, placeholder, type = "text", icon }) => (
|
|
<div className="space-y-2">
|
|
<label className="text-[11px] font-black text-slate-400 uppercase tracking-widest block pl-1 flex items-center gap-2">
|
|
{icon} {label}
|
|
</label>
|
|
<input
|
|
type={type}
|
|
value={value}
|
|
onChange={(e) => onChange(e.target.value)}
|
|
className="w-full p-3 bg-slate-50 border border-slate-200 rounded-xl focus:border-blue-500 focus:bg-white outline-none transition-all font-bold text-slate-800 placeholder:text-slate-300 text-[13px] shadow-sm"
|
|
placeholder={placeholder}
|
|
/>
|
|
</div>
|
|
);
|
|
|
|
// --- SelectGroup: 일관된 디자인의 셀렉트 박스 ---
|
|
interface SelectGroupProps {
|
|
label: string;
|
|
value: string;
|
|
onChange: (v: string) => void;
|
|
options: { value: string, label: string }[];
|
|
icon?: React.ReactNode;
|
|
}
|
|
|
|
export const SelectGroup: React.FC<SelectGroupProps> = ({ label, value, onChange, options, icon }) => (
|
|
<div className="space-y-2">
|
|
<label className="text-[11px] font-black text-slate-400 uppercase tracking-widest block pl-1 flex items-center gap-2">
|
|
{icon} {label}
|
|
</label>
|
|
<select
|
|
value={value}
|
|
onChange={(e) => onChange(e.target.value)}
|
|
className="w-full p-3 bg-slate-50 border border-slate-200 rounded-xl focus:border-blue-500 focus:bg-white outline-none transition-all font-bold text-slate-800 text-[13px] shadow-sm appearance-none"
|
|
>
|
|
{options.map(opt => <option key={opt.value} value={opt.value}>{opt.label}</option>)}
|
|
</select>
|
|
</div>
|
|
);
|
|
|
|
// --- ToggleButton: 활성화/비활성 스위치 ---
|
|
interface ToggleButtonProps {
|
|
active: boolean;
|
|
onClick: () => void;
|
|
}
|
|
|
|
export const ToggleButton: React.FC<ToggleButtonProps> = ({ active, onClick }) => (
|
|
<button
|
|
type="button"
|
|
onClick={onClick}
|
|
className={`relative inline-flex h-8 w-14 items-center rounded-full transition-all focus:outline-none ${active ? 'bg-emerald-500' : 'bg-slate-300'}`}
|
|
>
|
|
<span className={`inline-block h-5 w-5 transform rounded-full bg-white transition-transform ${active ? 'translate-x-7' : 'translate-x-2'}`} />
|
|
</button>
|
|
);
|