style(admin): 곡 검색 다이얼로그 선택 순서 번호 표시

- 체크 아이콘 대신 선택 순서(1, 2, 3...) 숫자 뱃지 표시
- 선택 해제 시 남은 곡 번호 자동 재계산
- 숫자 시각 중심 보정 (leading-none + translate-y)
- 순서는 이미 클릭 순서대로 세트리스트에 추가됨 (selectedTracks push 순서 유지)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
caadiq 2026-04-23 17:25:42 +09:00
parent 18efd952c4
commit 9e87549ca3

View file

@ -1,7 +1,7 @@
import { useState, useMemo } from "react"; import { useState, useMemo } from "react";
import { createPortal } from "react-dom"; import { createPortal } from "react-dom";
import { motion, AnimatePresence } from "framer-motion"; import { motion, AnimatePresence } from "framer-motion";
import { X, Search, Music, Check, Disc3 } from "lucide-react"; import { X, Search, Music, Disc3 } from "lucide-react";
/** /**
* 검색 다이얼로그 * 검색 다이얼로그
@ -71,7 +71,14 @@ function SongSearchDialog({ isOpen, onClose, onSelect, albums }) {
}); });
}; };
const isSelected = (trackId) => selectedTracks.some((t) => t.id === trackId); // (trackId )
const selectionOrder = useMemo(() => {
const m = new Map();
selectedTracks.forEach((t, i) => m.set(t.id, i + 1));
return m;
}, [selectedTracks]);
const isSelected = (trackId) => selectionOrder.has(trackId);
// //
const handleConfirm = () => { const handleConfirm = () => {
@ -169,41 +176,41 @@ function SongSearchDialog({ isOpen, onClose, onSelect, albums }) {
{/* 트랙 목록 */} {/* 트랙 목록 */}
<div className="space-y-1"> <div className="space-y-1">
{group.tracks.map((track) => ( {group.tracks.map((track) => {
<button const order = selectionOrder.get(track.id);
key={track.id} const selected = order !== undefined;
type="button" return (
onClick={() => toggleTrack(track)} <button
className={`w-full flex items-center gap-3 px-3 py-2.5 rounded-lg text-left transition-colors ${ key={track.id}
isSelected(track.id) type="button"
? "bg-primary/10" onClick={() => toggleTrack(track)}
: "hover:bg-gray-50" className={`w-full flex items-center gap-3 px-3 py-2.5 rounded-lg text-left transition-colors ${
}`} selected ? "bg-primary/10" : "hover:bg-gray-50"
>
<div
className={`w-5 h-5 rounded border flex items-center justify-center flex-shrink-0 ${
isSelected(track.id)
? "bg-primary border-primary"
: "border-gray-300"
}`} }`}
> >
{isSelected(track.id) && ( <div
<Check size={12} className="text-white" /> className={`w-5 h-5 rounded-full flex items-center justify-center flex-shrink-0 text-[11px] font-bold leading-none transition-colors ${
)} selected
</div> ? "bg-primary text-white"
<span className="text-sm text-gray-400 w-5 text-right flex-shrink-0"> : "border border-gray-300 text-transparent"
{track.trackNumber} }`}
</span> >
<span className="text-sm text-gray-900 flex-1 truncate"> <span className="translate-y-[0.5px]">{order ?? ""}</span>
{track.songName} </div>
</span> <span className="text-sm text-gray-400 w-5 text-right flex-shrink-0">
{!!track.isTitleTrack && ( {track.trackNumber}
<span className="text-[10px] px-1.5 py-0.5 bg-primary/10 text-primary rounded font-medium flex-shrink-0">
타이틀
</span> </span>
)} <span className="text-sm text-gray-900 flex-1 truncate">
</button> {track.songName}
))} </span>
{!!track.isTitleTrack && (
<span className="text-[10px] px-1.5 py-0.5 bg-primary/10 text-primary rounded font-medium flex-shrink-0">
타이틀
</span>
)}
</button>
);
})}
</div> </div>
</div> </div>
))} ))}