테마 적용

This commit is contained in:
2026-02-11 14:06:06 +09:00
parent def87bd47a
commit 95291e6922
30 changed files with 1209 additions and 496 deletions

View File

@@ -1,34 +1,45 @@
import type { FormEvent } from "react";
import { Input } from "@/components/ui/input";
import { Button } from "@/components/ui/button";
import { Search } from "lucide-react";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
interface StockSearchFormProps {
keyword: string;
onKeywordChange: (value: string) => void;
onSubmit: (e: FormEvent) => void;
onSubmit: (event: FormEvent) => void;
onInputFocus?: () => void;
disabled?: boolean;
isLoading?: boolean;
}
/**
* @description 종목 검색 입력/제출 폼을 렌더링합니다.
* @see features/dashboard/components/DashboardContainer.tsx 검색 패널에서 키워드 입력 이벤트를 전달합니다.
*/
export function StockSearchForm({
keyword,
onKeywordChange,
onSubmit,
onInputFocus,
disabled,
isLoading,
}: StockSearchFormProps) {
return (
<form onSubmit={onSubmit} className="flex gap-2">
{/* ========== SEARCH INPUT ========== */}
<div className="relative flex-1">
<Search className="absolute left-2.5 top-2.5 h-4 w-4 text-muted-foreground" />
<Search className="absolute left-2.5 top-2.5 h-4 w-4 text-muted-foreground dark:text-brand-100/65" />
<Input
placeholder="종목명 또는 코드(6자리) 입력..."
className="pl-9"
value={keyword}
onChange={(e) => onKeywordChange(e.target.value)}
onFocus={onInputFocus}
placeholder="종목명 또는 종목코드(6자리)를 입력하세요."
autoComplete="off"
className="pl-9 dark:border-brand-700/55 dark:bg-brand-900/28 dark:text-brand-100 dark:placeholder:text-brand-100/55"
/>
</div>
{/* ========== SUBMIT BUTTON ========== */}
<Button type="submit" disabled={disabled || isLoading}>
{isLoading ? "검색 중..." : "검색"}
</Button>

View File

@@ -0,0 +1,88 @@
import { Clock3, Trash2, X } from "lucide-react";
import { Button } from "@/components/ui/button";
import { cn } from "@/lib/utils";
import type { DashboardStockSearchHistoryItem } from "@/features/dashboard/types/dashboard.types";
interface StockSearchHistoryProps {
items: DashboardStockSearchHistoryItem[];
selectedSymbol?: string;
onSelect: (item: DashboardStockSearchHistoryItem) => void;
onRemove: (symbol: string) => void;
onClear: () => void;
}
/**
* @description 최근 검색 종목 목록을 보여주고, 재검색/개별삭제/전체삭제를 제공합니다.
* @see features/dashboard/components/DashboardContainer.tsx 검색 패널에서 종목 재선택 UI로 사용합니다.
* @see features/dashboard/hooks/useStockSearch.ts searchHistory 상태를 화면에 렌더링합니다.
*/
export function StockSearchHistory({
items,
selectedSymbol,
onSelect,
onRemove,
onClear,
}: StockSearchHistoryProps) {
if (items.length === 0) return null;
return (
<section className="rounded-md border border-brand-200/80 bg-brand-50/45 p-2 dark:border-brand-700/50 dark:bg-brand-900/26">
{/* ========== HISTORY HEADER ========== */}
<div className="mb-1.5 flex items-center justify-between gap-2 px-1">
<div className="flex items-center gap-1.5 text-xs font-semibold text-brand-700 dark:text-brand-200">
<Clock3 className="h-3.5 w-3.5" />
</div>
<Button
type="button"
variant="ghost"
size="sm"
onClick={onClear}
className="h-7 px-2 text-[11px] text-muted-foreground hover:text-foreground dark:text-brand-100/75 dark:hover:text-brand-50"
>
<Trash2 className="mr-1 h-3.5 w-3.5" />
</Button>
</div>
{/* ========== HISTORY LIST ========== */}
<div className="max-h-36 space-y-1 overflow-y-auto pr-1">
{items.map((item) => {
const isSelected = item.symbol === selectedSymbol;
return (
<div key={item.symbol} className="flex items-center gap-1">
<Button
type="button"
variant="ghost"
onClick={() => onSelect(item)}
className={cn(
"h-8 flex-1 justify-between rounded-md border border-transparent px-2.5",
"text-left hover:bg-white/80 dark:hover:bg-brand-800/35",
isSelected &&
"border-brand-300 bg-white text-brand-700 dark:border-brand-500/55 dark:bg-brand-800/40 dark:text-brand-100",
)}
>
<span className="truncate text-sm font-medium">{item.name}</span>
<span className="ml-2 shrink-0 text-[11px] text-muted-foreground dark:text-brand-100/70">
{item.symbol}
</span>
</Button>
<Button
type="button"
variant="ghost"
size="icon"
onClick={() => onRemove(item.symbol)}
aria-label={`${item.name} 히스토리 삭제`}
className="h-8 w-8 shrink-0 text-muted-foreground hover:bg-white/80 hover:text-foreground dark:text-brand-100/70 dark:hover:bg-brand-800/35 dark:hover:text-brand-50"
>
<X className="h-4 w-4" />
</Button>
</div>
);
})}
</div>
</section>
);
}