feat: Improve V2Select multi-select dropdown item display

- Enhanced the display of selected items in the V2Select component to show labels in a comma-separated format, improving user visibility without needing to open the dropdown.
- Implemented tooltip functionality that activates only when the text is truncated, allowing users to see all selected items at a glance.
- Updated the DropdownSelect component to ensure consistent behavior across all screens using this component.
- Added necessary imports and state management for tooltip visibility and text truncation detection.

Made-with: Cursor
This commit is contained in:
syc0123
2026-03-04 10:11:48 +09:00
parent cfd49020a0
commit 2b324d083b
7 changed files with 919 additions and 83 deletions

View File

@@ -0,0 +1,146 @@
# [계획서] V2Select 다중 선택 드롭다운 - 선택 항목 표시 개선
> 관련 문서: [맥락노트](./MST[맥락노트]-v2select-multiselect-tooltip.md) | [체크리스트](./MST[체크리스트]-v2select-multiselect-tooltip.md)
## 개요
모든 화면에서 다중 선택 가능한 드롭다운(`V2Select` - `DropdownSelect`)의 선택 항목 표시 방식을 개선합니다.
---
## 현재 동작
- 다중 선택 시 `"3개 선택됨"` 같은 텍스트만 표시
- 어떤 항목이 선택되었는지 드롭다운을 열어야만 확인 가능
### 현재 코드 (V2Select.tsx - DropdownSelect, 174~178행)
```tsx
{selectedLabels.length > 0
? multiple
? `${selectedLabels.length}개 선택됨`
: selectedLabels[0]
: placeholder}
```
---
## 변경 후 동작
### 1. 선택된 항목 라벨을 쉼표로 연결하여 한 줄로 표시
- 예: `"구매품, 판매품, 재고품"`
- `truncate` (text-overflow: ellipsis)로 필드 너비를 넘으면 말줄임(`...`) 처리
- 무조건 한 줄 표시, 넘치면 `...`으로 숨김
### 2. 텍스트가 말줄임(`...`) 처리될 때만 호버 툴팁 표시
- 필드 너비를 넘어서 `...`으로 잘릴 때만 툴팁 활성화
- 필드 내에 전부 보이면 툴팁 불필요
- 툴팁 내용은 세로 나열로 각 항목을 한눈에 확인 가능
- 툴팁은 딜레이 없이 즉시 표시
---
## 시각적 동작 예시
| 상태 | 필드 내 표시 | 호버 시 툴팁 |
|------|-------------|-------------|
| 미선택 | `선택` (placeholder) | 없음 |
| 1개 선택 | `구매품` | 없음 |
| 3개 선택 (필드 내 수용) | `구매품, 판매품, 재고품` | 없음 (잘리지 않으므로) |
| 5개 선택 (필드 넘침) | `구매품, 판매품, 재고품, 외...` | 구매품 / 판매품 / 재고품 / 외주품 / 반제품 (세로 나열) |
---
## 변경 대상
- **파일**: `frontend/components/v2/V2Select.tsx`
- **컴포넌트**: `DropdownSelect` 내부 표시 텍스트 부분 (170~178행)
- **적용 범위**: `DropdownSelect`를 사용하는 모든 화면 (품목정보, 기타 모든 모달 포함)
- **변경 규모**: 약 30줄 내외 소규모 변경
---
## 코드 설계
### 추가 import
```tsx
import { useRef, useEffect, useState } from "react";
import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from "@/components/ui/tooltip";
```
### 말줄임 감지 로직
```tsx
// 텍스트가 잘리는지(truncated) 감지
const textRef = useRef<HTMLSpanElement>(null);
const [isTruncated, setIsTruncated] = useState(false);
useEffect(() => {
const el = textRef.current;
if (el) {
setIsTruncated(el.scrollWidth > el.clientWidth);
}
}, [selectedLabels]);
```
### 수정 코드 (DropdownSelect 내부, 170~178행 대체)
```tsx
const displayText = selectedLabels.length > 0
? (multiple ? selectedLabels.join(", ") : selectedLabels[0])
: placeholder;
const isPlaceholder = selectedLabels.length === 0;
// 렌더링 부분
{isTruncated && multiple ? (
<TooltipProvider delayDuration={0}>
<Tooltip>
<TooltipTrigger asChild>
<span
ref={textRef}
className={cn("truncate flex-1 text-left", isPlaceholder && "text-muted-foreground")}
{...(isPlaceholder ? { "data-placeholder": placeholder } : {})}
>
{displayText}
</span>
</TooltipTrigger>
<TooltipContent side="top" className="max-w-[300px]">
<div className="space-y-0.5 text-xs">
{selectedLabels.map((label, i) => (
<div key={i}>{label}</div>
))}
</div>
</TooltipContent>
</Tooltip>
</TooltipProvider>
) : (
<span
ref={textRef}
className={cn("truncate flex-1 text-left", isPlaceholder && "text-muted-foreground")}
{...(isPlaceholder ? { "data-placeholder": placeholder } : {})}
>
{displayText}
</span>
)}
```
---
## 설계 원칙
- 기존 단일 선택 동작은 변경하지 않음
- `DropdownSelect` 공통 컴포넌트 수정이므로 모든 화면에 자동 적용
- 무조건 한 줄 표시, 넘치면 `...`으로 말줄임
- 툴팁은 텍스트가 실제로 잘릴 때(`scrollWidth > clientWidth`)만 표시
- 툴팁 내용은 세로 나열로 각 항목 확인 용이
- 툴팁 딜레이 없음 (`delayDuration={0}`)
- shadcn 표준 Tooltip 컴포넌트 사용으로 프로젝트 일관성 유지

View File

@@ -0,0 +1,89 @@
# [맥락노트] V2Select 다중 선택 드롭다운 - 선택 항목 표시 개선
> 관련 문서: [계획서](./MST[계획서]-v2select-multiselect-tooltip.md) | [체크리스트](./MST[체크리스트]-v2select-multiselect-tooltip.md)
---
## 왜 이 작업을 하는가
- 사용자가 수정 모달에서 다중 선택 드롭다운을 사용할 때 `"3개 선택됨"` 만 보임
- 드롭다운을 다시 열어봐야만 무엇이 선택됐는지 확인 가능 → UX 불편
- 선택 항목을 직접 보여주고, 넘치면 툴팁으로 확인할 수 있게 개선
---
## 핵심 결정 사항과 근거
### 1. "n개 선택됨" → 라벨 쉼표 나열
- **결정**: `"구매품, 판매품, 재고품"` 형태로 표시
- **근거**: 사용자가 드롭다운을 열지 않아도 선택 내용을 바로 확인 가능
### 2. 무조건 한 줄, 넘치면 말줄임(`...`)
- **결정**: 여러 줄 줄바꿈 없이 한 줄 고정, `truncate`로 오버플로우 처리
- **근거**: 드롭다운 필드 높이가 고정되어 있어 여러 줄 표시 시 레이아웃이 깨짐
### 3. 텍스트가 잘릴 때만 툴팁 표시
- **결정**: `scrollWidth > clientWidth` 비교로 실제 잘림 여부 감지 후 툴팁 활성화
- **근거**: 전부 보이는데 툴팁이 뜨면 오히려 방해. 필요할 때만 보여야 함
- **대안 검토**: "2개 이상이면 항상 툴팁" → 기각 (불필요한 툴팁 발생)
### 4. 툴팁 내용은 세로 나열
- **결정**: 툴팁 안에서 항목을 줄바꿈으로 세로 나열
- **근거**: 가로 나열 시 툴팁도 길어져서 읽기 어려움. 세로가 한눈에 파악하기 좋음
### 5. 툴팁 딜레이 0ms
- **결정**: `delayDuration={0}` 즉시 표시
- **근거**: 사용자가 "무엇을 선택했는지" 확인하려는 의도적 행동이므로 즉시 응답해야 함
### 6. Radix Tooltip 대신 커스텀 호버 툴팁 사용
- **결정**: Radix Tooltip을 사용하지 않고 `onMouseEnter`/`onMouseLeave`로 직접 제어
- **근거**: Radix Tooltip + Popover 조합은 이벤트 충돌 발생. 내부 배치든 외부 래핑이든 Popover가 호버를 가로챔
- **시도 1**: Tooltip을 Button 안에 배치 → Popover가 이벤트 가로챔 (실패)
- **시도 2**: Radix 공식 패턴 (TooltipTrigger > PopoverTrigger > Button 체이닝) → 여전히 동작 안 함 (실패)
- **최종**: wrapper div에 마우스 이벤트 + 절대 위치 div로 툴팁 렌더링 (성공)
- **추가**: Popover 열릴 때 `setHoverTooltip(false)`로 툴팁 자동 숨김
### 7. DropdownSelect 공통 컴포넌트 수정
- **결정**: 특정 화면이 아닌 `DropdownSelect` 자체를 수정
- **근거**: 품목정보뿐 아니라 모든 화면에서 동일한 문제가 있으므로 공통 해결
---
## 관련 파일 위치
| 구분 | 파일 경로 | 설명 |
|------|----------|------|
| 수정 대상 | `frontend/components/v2/V2Select.tsx` | DropdownSelect 컴포넌트 (170~178행) |
| 타입 정의 | `frontend/types/v2-components.ts` | V2SelectProps, SelectOption 타입 |
| UI 컴포넌트 | `frontend/components/ui/tooltip.tsx` | shadcn Tooltip 컴포넌트 |
| 렌더러 | `frontend/lib/registry/components/v2-select/V2SelectRenderer.tsx` | V2Select를 레지스트리에 연결 |
| 수정 모달 | `frontend/components/screen/EditModal.tsx` | 공통 편집 모달 |
---
## 기술 참고
### truncate 감지 방식
```
scrollWidth: 텍스트의 실제 전체 너비 (보이지 않는 부분 포함)
clientWidth: 요소의 보이는 너비
scrollWidth > clientWidth → 텍스트가 잘리고 있음 (... 표시 중)
```
### selectedLabels 계산 흐름
```
value (string[]) → selectedValues → safeOptions에서 label 매칭 → selectedLabels (string[])
```
- `selectedLabels`는 이미 `DropdownSelect` 내부에서 `useMemo`로 계산됨 (126~130행)
- 추가 데이터 fetching 불필요, 기존 값을 `.join(", ")`로 결합하면 됨

View File

@@ -0,0 +1,52 @@
# [체크리스트] V2Select 다중 선택 드롭다운 - 선택 항목 표시 개선
> 관련 문서: [계획서](./MST[계획서]-v2select-multiselect-tooltip.md) | [맥락노트](./MST[맥락노트]-v2select-multiselect-tooltip.md)
---
## 공정 상태
- 전체 진행률: **100%** (완료)
- 현재 단계: 전체 완료
---
## 구현 체크리스트
### 1단계: 코드 수정
- [x] `V2Select.tsx`에 Tooltip 관련 import 추가
- [x] `DropdownSelect` 내부에 `textRef`, `isTruncated` 상태 추가
- [x] `useEffect``scrollWidth > clientWidth` 감지 로직 추가
- [x] 표시 텍스트를 `selectedLabels.join(", ")`로 변경
- [x] `isTruncated && multiple` 조건으로 Tooltip 래핑
- [x] 툴팁 내용을 세로 나열 (`space-y-0.5`)로 구성
- [x] `delayDuration={0}` 설정
- [x] Radix Tooltip → 커스텀 호버 툴팁으로 변경 (onMouseEnter/onMouseLeave + 절대 위치 div)
### 2단계: 검증
- [x] 단일 선택 모드: 기존 동작 변화 없음 확인
- [x] 다중 선택 1개: 라벨 정상 표시, 툴팁 없음
- [x] 다중 선택 3개 (필드 내 수용): 쉼표 나열 표시, 툴팁 없음
- [x] 다중 선택 5개+ (필드 넘침): 말줄임 표시, 호버 시 툴팁 세로 나열
- [x] 품목정보 수정 모달에서 동작 확인
- [x] 다른 화면의 다중 선택 드롭다운에서도 동작 확인
### 3단계: 정리
- [x] 린트 에러 없음 확인
- [x] 이 체크리스트 완료 표시 업데이트
---
## 변경 이력
| 날짜 | 내용 |
|------|------|
| 2026-03-04 | 설계 문서 작성 완료 |
| 2026-03-04 | 맥락노트, 체크리스트 작성 완료 |
| 2026-03-04 | 파일명 MST 접두사 적용 |
| 2026-03-04 | 1단계 코드 수정 완료 (V2Select.tsx) |
| 2026-03-04 | Radix Tooltip이 Popover와 충돌 → 커스텀 호버 툴팁으로 변경 |
| 2026-03-04 | 사용자 검증 완료, 전체 작업 완료 |