Skip to Content

Select

개요

Select는 사용자가 여러 옵션 중 하나를 선택할 수 있는 드롭다운 컴포넌트입니다. Cals 예약 관리 시스템에서는 서비스 선택, 시간대 선택, 담당자 선택 등 예약 프로세스의 핵심 기능으로 활용됩니다.

Cals 특화 기능:

  • 예약 상태별 색상 시스템으로 선택 옵션 시각화
  • 서비스 카테고리 및 메뉴 선택 최적화
  • 시간대 및 담당자 선택을 위한 구조화된 옵션
  • 예약 가능 여부에 따른 동적 옵션 활성화/비활성화

설치

npx @vortex/cli add select --package cals

기본 사용법

import "@vortex/ui-cals/theme"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@vortex/ui-cals"; export default function ServiceSelect() { return ( <Select> <SelectTrigger> <SelectValue placeholder="서비스를 선택하세요" /> </SelectTrigger> <SelectContent> <SelectItem value="haircut">컷 (30분)</SelectItem> <SelectItem value="perm">펌 (90분)</SelectItem> <SelectItem value="coloring">염색 (120분)</SelectItem> </SelectContent> </Select> ); }

Variants

Default

기본 스타일의 Select입니다.

<Select> <SelectTrigger> <SelectValue placeholder="옵션 선택" /> </SelectTrigger> <SelectContent> <SelectItem value="option1">옵션 1</SelectItem> <SelectItem value="option2">옵션 2</SelectItem> </SelectContent> </Select>

Outlined

아웃라인 스타일의 Select입니다.

<Select> <SelectTrigger className="border-2"> <SelectValue placeholder="옵션 선택" /> </SelectTrigger> <SelectContent> <SelectItem value="option1">옵션 1</SelectItem> <SelectItem value="option2">옵션 2</SelectItem> </SelectContent> </Select>

Sizes

Small

작은 크기의 Select입니다.

<Select> <SelectTrigger className="h-8 text-sm"> <SelectValue placeholder="Small" /> </SelectTrigger> <SelectContent> <SelectItem value="option1">옵션 1</SelectItem> </SelectContent> </Select>

Medium (Default)

기본 크기의 Select입니다.

<Select> <SelectTrigger> <SelectValue placeholder="Medium" /> </SelectTrigger> <SelectContent> <SelectItem value="option1">옵션 1</SelectItem> </SelectContent> </Select>

Large

큰 크기의 Select입니다.

<Select> <SelectTrigger className="h-12 text-lg"> <SelectValue placeholder="Large" /> </SelectTrigger> <SelectContent> <SelectItem value="option1">옵션 1</SelectItem> </SelectContent> </Select>

States

Disabled

비활성화된 Select입니다.

<Select disabled> <SelectTrigger> <SelectValue placeholder="비활성화됨" /> </SelectTrigger> <SelectContent> <SelectItem value="option1">옵션 1</SelectItem> </SelectContent> </Select>

With Label

레이블이 있는 Select입니다.

<div> <label className="text-sm font-medium text-gray-700">서비스 선택</label> <Select> <SelectTrigger className="mt-1"> <SelectValue placeholder="서비스를 선택하세요" /> </SelectTrigger> <SelectContent> <SelectItem value="service1">헤어컷</SelectItem> </SelectContent> </Select> </div>

🆕 Cals 브랜딩

브랜드 컬러

Cals Select는 기본적으로 Cals의 Primary 색상(Pink #e91e63)을 focus 및 선택 상태에서 사용합니다.

<div className="space-y-4"> {/* Primary (Pink) - 기본 focus */} <Select> <SelectTrigger className="focus:border-[#e91e63] focus:ring-[#e91e63]"> <SelectValue placeholder="서비스 선택 (Primary)" /> </SelectTrigger> <SelectContent> <SelectItem value="haircut">헤어컷</SelectItem> <SelectItem value="perm">펌</SelectItem> </SelectContent> </Select> {/* Secondary (Blue) - 정보성 선택 */} <Select> <SelectTrigger className="focus:border-[#03a9f4] focus:ring-[#03a9f4]"> <SelectValue placeholder="시간대 선택 (Secondary)" /> </SelectTrigger> <SelectContent> <SelectItem value="morning">오전 (09:00-12:00)</SelectItem> <SelectItem value="afternoon">오후 (13:00-18:00)</SelectItem> </SelectContent> </Select> {/* Accent (Purple) - 강조 선택 */} <Select> <SelectTrigger className="focus:border-[#9c27b0] focus:ring-[#9c27b0]"> <SelectValue placeholder="담당자 선택 (Accent)" /> </SelectTrigger> <SelectContent> <SelectItem value="designer1">김디자이너</SelectItem> <SelectItem value="designer2">이디자이너</SelectItem> </SelectContent> </Select> </div>

예약 상태 컬러

Cals Select는 예약 관리의 5가지 상태를 옵션에 반영하여 시각적 피드백을 제공합니다.

<Select> <SelectTrigger className="focus:border-[#e91e63] focus:ring-[#e91e63]"> <SelectValue placeholder="예약 상태별 시간대 선택" /> </SelectTrigger> <SelectContent> {/* Available - 예약 가능 */} <SelectItem value="09:00" className="text-[#4caf50]"> 09:00 - 예약 가능 </SelectItem> {/* Pending - 승인 대기 */} <SelectItem value="10:00" className="text-[#ff9800]" disabled> 10:00 - 승인 대기 중 </SelectItem> {/* Confirmed - 예약 확정 */} <SelectItem value="11:00" className="text-[#03a9f4]" disabled> 11:00 - 예약 확정 (홍길동) </SelectItem> {/* Cancelled - 취소됨 */} <SelectItem value="14:00" className="text-[#f44336]" disabled> 14:00 - 취소됨 </SelectItem> {/* Available */} <SelectItem value="15:00" className="text-[#4caf50]"> 15:00 - 예약 가능 </SelectItem> {/* Completed - 서비스 완료 */} <SelectItem value="16:00" className="text-[#9c27b0]" disabled> 16:00 - 서비스 완료 </SelectItem> </SelectContent> </Select>

Cals 특화 사용 가이드

서비스 선택 (카테고리별 그룹화)

<div className="space-y-2"> <label className="text-sm font-medium text-gray-700"> 서비스 선택 <span className="text-[#e91e63]">*</span> </label> <Select> <SelectTrigger className="focus:border-[#e91e63] focus:ring-[#e91e63]"> <SelectValue placeholder="서비스를 선택하세요" /> </SelectTrigger> <SelectContent> <SelectGroup> <SelectLabel>기본 서비스</SelectLabel> <SelectItem value="haircut">컷 (30분) - ₩30,000</SelectItem> <SelectItem value="shampoo">샴푸 (20분) - ₩10,000</SelectItem> </SelectGroup> <SelectGroup> <SelectLabel>프리미엄 서비스</SelectLabel> <SelectItem value="perm">펌 (90분) - ₩120,000</SelectItem> <SelectItem value="coloring">염색 (120분) - ₩150,000</SelectItem> <SelectItem value="clinic">클리닉 (60분) - ₩80,000</SelectItem> </SelectGroup> </SelectContent> </Select> </div>

시간대 선택 (예약 가능 여부 표시)

<div className="space-y-2"> <label className="text-sm font-medium text-gray-700"> 예약 시간 <span className="text-[#e91e63]">*</span> </label> <Select> <SelectTrigger className="focus:border-[#03a9f4] focus:ring-[#03a9f4]"> <SelectValue placeholder="시간을 선택하세요" /> </SelectTrigger> <SelectContent> <SelectItem value="09:00"> <span className="flex items-center gap-2"> <span className="w-2 h-2 rounded-full bg-[#4caf50]"></span> 09:00 - 예약 가능 </span> </SelectItem> <SelectItem value="10:00" disabled> <span className="flex items-center gap-2"> <span className="w-2 h-2 rounded-full bg-[#ff9800]"></span> 10:00 - 승인 대기 </span> </SelectItem> <SelectItem value="11:00" disabled> <span className="flex items-center gap-2"> <span className="w-2 h-2 rounded-full bg-[#03a9f4]"></span> 11:00 - 예약 확정 </span> </SelectItem> <SelectItem value="14:00"> <span className="flex items-center gap-2"> <span className="w-2 h-2 rounded-full bg-[#4caf50]"></span> 14:00 - 예약 가능 </span> </SelectItem> </SelectContent> </Select> </div>

담당자 선택

<div className="space-y-2"> <label className="text-sm font-medium text-gray-700">담당자 선택</label> <Select> <SelectTrigger className="focus:border-[#9c27b0] focus:ring-[#9c27b0]"> <SelectValue placeholder="담당자를 선택하세요" /> </SelectTrigger> <SelectContent> <SelectItem value="designer1"> <div className="flex items-center gap-2"> <div className="w-6 h-6 rounded-full bg-[#e91e63] flex items-center justify-center text-white text-xs"> </div> <span>김디자이너 (원장)</span> </div> </SelectItem> <SelectItem value="designer2"> <div className="flex items-center gap-2"> <div className="w-6 h-6 rounded-full bg-[#03a9f4] flex items-center justify-center text-white text-xs"> </div> <span>이디자이너 (실장)</span> </div> </SelectItem> <SelectItem value="designer3"> <div className="flex items-center gap-2"> <div className="w-6 h-6 rounded-full bg-[#9c27b0] flex items-center justify-center text-white text-xs"> </div> <span>박디자이너 (디자이너)</span> </div> </SelectItem> </SelectContent> </Select> </div>

🆕 Foundation/iCignal과의 차이점

속성FoundationiCignalCals
Primary Color#3B82F6 (Blue)#2196f3 (Blue)#e91e63 (Pink)
Secondary Color#10B981 (Green)#4caf50 (Green)#03a9f4 (Blue)
Accent Color#8B5CF6 (Purple)#ff5722 (Deep Orange)#9c27b0 (Purple)
사용 맥락범용 웹 애플리케이션Analytics 대시보드예약 관리 시스템
예약 상태없음없음5가지 상태 (Available/Pending/Confirmed/Cancelled/Completed)
주요 선택범용 옵션 선택데이터 필터링서비스, 시간대, 담당자 선택
옵션 그룹화기본 그룹화카테고리 필터서비스 카테고리, 시간대별 예약 가능 여부
상태 표시없음없음예약 가능/불가능 상태 시각화

사용 시나리오 비교

Foundation Select: 범용 옵션 선택, 국가/언어 선택, 설정 변경 iCignal Select: 데이터 필터, 날짜 범위 선택, 차트 타입 선택 Cals Select: 서비스 선택, 시간대 선택, 담당자 선택, 예약 상태 필터

Props API

Select

PropTypeDefaultDescription
defaultValuestring-기본 선택 값
valuestring-제어 컴포넌트의 선택 값
onValueChange(value: string) => void-값 변경 이벤트 핸들러
disabledbooleanfalse비활성화 여부
namestring-Form name 속성
requiredbooleanfalse필수 선택 여부

SelectTrigger

PropTypeDefaultDescription
classNamestring-추가 CSS 클래스
disabledbooleanfalse비활성화 여부

SelectContent

PropTypeDefaultDescription
position'popper' | 'item-aligned''popper'드롭다운 위치
classNamestring-추가 CSS 클래스

SelectItem

PropTypeDefaultDescription
valuestring-옵션 값 (필수)
disabledbooleanfalse비활성화 여부
classNamestring-추가 CSS 클래스

접근성

Select 컴포넌트는 웹 접근성 표준을 준수합니다:

  • Radix UI 기반: WAI-ARIA 1.2 Select 패턴 구현
  • 키보드 네비게이션:
    • Space/Enter: 드롭다운 열기/닫기 및 옵션 선택
    • Arrow Up/Down: 옵션 간 이동
    • Home/End: 첫 번째/마지막 옵션으로 이동
    • Esc: 드롭다운 닫기
    • A-Z: 타이핑으로 옵션 검색
  • 스크린 리더: role="combobox"aria-expanded 지원
  • Focus 관리: 명확한 focus indicator 및 focus trap
  • Label 연결: aria-labelledby로 label과 연결
  • Required 표시: aria-required="true" 지원

접근성 예제

<div> <label id="service-label" className="text-sm font-medium text-gray-700"> 서비스 선택{" "} <span className="text-[#e91e63]" aria-label="필수"> * </span> </label> <Select required> <SelectTrigger aria-labelledby="service-label" aria-required="true"> <SelectValue placeholder="서비스를 선택하세요" /> </SelectTrigger> <SelectContent> <SelectItem value="haircut">컷 (30분)</SelectItem> <SelectItem value="perm">펌 (90분)</SelectItem> </SelectContent> </Select> </div>

예제

서비스 선택 폼

import { Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectTrigger, SelectValue, } from "@vortex/ui-cals"; export default function ServiceSelectionForm() { return ( <div className="space-y-4 max-w-md"> <div> <label className="block text-sm font-medium text-gray-700 mb-2"> 서비스 선택 <span className="text-[#e91e63]">*</span> </label> <Select> <SelectTrigger className="focus:border-[#e91e63] focus:ring-[#e91e63]"> <SelectValue placeholder="서비스를 선택하세요" /> </SelectTrigger> <SelectContent> <SelectGroup> <SelectLabel>기본 서비스</SelectLabel> <SelectItem value="haircut">컷 (30분) - ₩30,000</SelectItem> <SelectItem value="shampoo">샴푸 (20분) - ₩10,000</SelectItem> <SelectItem value="blowdry">드라이 (20분) - ₩15,000</SelectItem> </SelectGroup> <SelectGroup> <SelectLabel>프리미엄 서비스</SelectLabel> <SelectItem value="perm">펌 (90분) - ₩120,000</SelectItem> <SelectItem value="coloring">염색 (120분) - ₩150,000</SelectItem> <SelectItem value="clinic">클리닉 (60분) - ₩80,000</SelectItem> <SelectItem value="treatment"> 트리트먼트 (40분) - ₩50,000 </SelectItem> </SelectGroup> </SelectContent> </Select> </div> <div> <label className="block text-sm font-medium text-gray-700 mb-2"> 담당자 선택 </label> <Select> <SelectTrigger className="focus:border-[#9c27b0] focus:ring-[#9c27b0]"> <SelectValue placeholder="담당자를 선택하세요" /> </SelectTrigger> <SelectContent> <SelectItem value="any">지정 안함</SelectItem> <SelectItem value="designer1">김디자이너 (원장)</SelectItem> <SelectItem value="designer2">이디자이너 (실장)</SelectItem> <SelectItem value="designer3">박디자이너 (디자이너)</SelectItem> </SelectContent> </Select> </div> <button className="w-full bg-[#e91e63] text-white py-2 px-4 rounded-md hover:bg-[#c2185b]"> 다음 단계 </button> </div> ); }

예약 시간 선택 (상태 표시)

import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@vortex/ui-cals"; export default function TimeSlotSelector() { const timeSlots = [ { time: "09:00", status: "available", customer: null }, { time: "10:00", status: "pending", customer: "김철수" }, { time: "11:00", status: "confirmed", customer: "이영희" }, { time: "12:00", status: "available", customer: null }, { time: "14:00", status: "cancelled", customer: null }, { time: "15:00", status: "available", customer: null }, { time: "16:00", status: "completed", customer: "박민수" }, { time: "17:00", status: "available", customer: null }, ]; const statusConfig = { available: { color: "#4caf50", label: "예약 가능", disabled: false }, pending: { color: "#ff9800", label: "승인 대기", disabled: true }, confirmed: { color: "#03a9f4", label: "예약 확정", disabled: true }, cancelled: { color: "#f44336", label: "취소됨", disabled: false }, completed: { color: "#9c27b0", label: "완료됨", disabled: true }, }; return ( <div className="max-w-md"> <label className="block text-sm font-medium text-gray-700 mb-2"> 예약 시간 선택 <span className="text-[#e91e63]">*</span> </label> <Select> <SelectTrigger className="focus:border-[#03a9f4] focus:ring-[#03a9f4]"> <SelectValue placeholder="시간을 선택하세요" /> </SelectTrigger> <SelectContent> {timeSlots.map((slot) => { const config = statusConfig[slot.status]; return ( <SelectItem key={slot.time} value={slot.time} disabled={config.disabled} className="flex items-center gap-2" > <div className="flex items-center gap-2"> <span className="w-2 h-2 rounded-full" style={{ backgroundColor: config.color }} /> <span className="font-medium">{slot.time}</span> <span className="text-sm" style={{ color: config.color }}> {config.label} </span> {slot.customer && ( <span className="text-sm text-gray-500"> ({slot.customer}) </span> )} </div> </SelectItem> ); })} </SelectContent> </Select> <div className="mt-3 flex gap-2 text-xs"> <div className="flex items-center gap-1"> <span className="w-2 h-2 rounded-full bg-[#4caf50]" /> <span>예약 가능</span> </div> <div className="flex items-center gap-1"> <span className="w-2 h-2 rounded-full bg-[#ff9800]" /> <span>승인 대기</span> </div> <div className="flex items-center gap-1"> <span className="w-2 h-2 rounded-full bg-[#03a9f4]" /> <span>예약 확정</span> </div> </div> </div> ); }

예약 필터 (다중 Select)

import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@vortex/ui-cals"; export default function ReservationFilters() { return ( <div className="grid grid-cols-1 md:grid-cols-3 gap-4"> <div> <label className="block text-sm font-medium text-gray-700 mb-2"> 예약 상태 </label> <Select> <SelectTrigger className="focus:border-[#e91e63] focus:ring-[#e91e63]"> <SelectValue placeholder="전체" /> </SelectTrigger> <SelectContent> <SelectItem value="all">전체</SelectItem> <SelectItem value="available" className="text-[#4caf50]"> 예약 가능 </SelectItem> <SelectItem value="pending" className="text-[#ff9800]"> 승인 대기 </SelectItem> <SelectItem value="confirmed" className="text-[#03a9f4]"> 예약 확정 </SelectItem> <SelectItem value="cancelled" className="text-[#f44336]"> 예약 취소 </SelectItem> <SelectItem value="completed" className="text-[#9c27b0]"> 서비스 완료 </SelectItem> </SelectContent> </Select> </div> <div> <label className="block text-sm font-medium text-gray-700 mb-2"> 서비스 타입 </label> <Select> <SelectTrigger className="focus:border-[#03a9f4] focus:ring-[#03a9f4]"> <SelectValue placeholder="전체" /> </SelectTrigger> <SelectContent> <SelectItem value="all">전체</SelectItem> <SelectItem value="basic">기본 서비스</SelectItem> <SelectItem value="premium">프리미엄 서비스</SelectItem> </SelectContent> </Select> </div> <div> <label className="block text-sm font-medium text-gray-700 mb-2"> 담당자 </label> <Select> <SelectTrigger className="focus:border-[#9c27b0] focus:ring-[#9c27b0]"> <SelectValue placeholder="전체" /> </SelectTrigger> <SelectContent> <SelectItem value="all">전체</SelectItem> <SelectItem value="designer1">김디자이너</SelectItem> <SelectItem value="designer2">이디자이너</SelectItem> <SelectItem value="designer3">박디자이너</SelectItem> </SelectContent> </Select> </div> </div> ); }

관련 컴포넌트

Last updated on