#nullable enable using System; using System.Collections.Generic; using Cysharp.Threading.Tasks; using UnityEngine; using UnityEngine.UIElements; using UVC.Locale; namespace UVC.UIToolkit { /// /// UIToolkit 기반 날짜/시간 피커 모달. /// 캘린더 그리드 + 시간 선택 + 범위 선택을 지원하는 날짜 선택 대화상자입니다. /// /// /// DatePicker(날짜 피커)란? /// /// DatePicker는 사용자가 달력에서 날짜를 선택할 수 있는 UI 컴포넌트입니다. /// 예약 시스템, 일정 관리, 검색 필터 등에서 널리 사용됩니다. /// 텍스트 입력보다 직관적이고 유효한 날짜만 선택할 수 있어 오류를 방지합니다. /// /// /// 피커 모드 (PickerMode): /// /// DateOnly - 날짜만 선택 /// DateAndTime - 날짜 + 시간(시/분) 선택 /// DateRange - 시작일~종료일 범위 선택 /// DateTimeRange - 시간 포함 범위 선택 /// /// /// UI 구성: /// /// 네비게이션 - 년/월 이동 버튼 /// 요일 헤더 - 일~토 요일 표시 (로컬라이제이션 지원) /// 달력 그리드 - 6행 7열 (42일) 버튼 /// 시간 선택 - 시/분 NumberStepper (DateAndTime 모드) /// 범위 정보 - 선택된 시작일/종료일 표시 (Range 모드) /// /// /// 날짜 스타일: /// /// 오늘 - 테두리로 강조 /// 선택된 날짜 - 배경색으로 강조 /// 일요일 - 빨간색 /// 토요일 - 파란색 /// 범위 내 날짜 - 연한 배경색 /// /// /// 요일 이름 커스터마이징: /// /// // 직접 지정 /// UTKDatePicker.SetDayNames(new[] { "일", "월", "화", "수", "목", "금", "토" }); /// /// // 로컬라이제이션 키 지정 /// UTKDatePicker.SetDayNameKeys(new[] { "day_sun", "day_mon", ... }); /// /// // 기본값으로 초기화 /// UTKDatePicker.ResetDayNames(); /// /// /// 실제 활용 예시: /// /// 예약 시스템 - 호텔, 항공권, 렌터카 예약일 /// 일정 관리 - 이벤트, 회의, 마감일 설정 /// 검색 필터 - 기간별 데이터 조회 /// 생년월일 입력 - 회원 가입, 프로필 설정 /// /// /// /// /// // 기본 사용법 (날짜만) /// var picker = UTKDatePicker.Show(rootVisualElement, DateTime.Today, UTKDatePicker.PickerMode.DateOnly, "Select Date"); /// picker.OnDateSelected += (date) => /// { /// Debug.Log($"Date selected: {date:yyyy-MM-dd}"); /// }; /// /// // 날짜 + 시간 선택 /// var dateTimePicker = UTKDatePicker.Show(rootVisualElement, DateTime.Now, UTKDatePicker.PickerMode.DateAndTime, "Select Date & Time"); /// dateTimePicker.OnDateSelected += (date) => /// { /// Debug.Log($"DateTime selected: {date:yyyy-MM-dd HH:mm}"); /// }; /// /// // async/await 사용법 (UniTask) /// DateTime? selectedDate = await UTKDatePicker.ShowAsync(rootVisualElement, DateTime.Today); /// if (selectedDate.HasValue) /// { /// Debug.Log($"Selected: {selectedDate.Value:yyyy-MM-dd}"); /// } /// else /// { /// Debug.Log("Cancelled"); /// } /// /// // 날짜 범위 선택 /// var rangePicker = UTKDatePicker.ShowRange(rootVisualElement, DateTime.Today, DateTime.Today.AddDays(7)); /// rangePicker.OnDateRangeSelected += (start, end) => /// { /// Debug.Log($"Range selected: {start:yyyy-MM-dd} ~ {end:yyyy-MM-dd}"); /// }; /// /// // 날짜 범위 async/await 사용법 /// var result = await UTKDatePicker.ShowRangeAsync(rootVisualElement, DateTime.Today, DateTime.Today.AddDays(7)); /// if (result.HasValue) /// { /// Debug.Log($"Range: {result.Value.Start:yyyy-MM-dd} ~ {result.Value.End:yyyy-MM-dd}"); /// } /// else /// { /// Debug.Log("Cancelled"); /// } /// /// // 요일 이름 커스터마이징 (static - 모든 인스턴스에 적용) /// UTKDatePicker.SetDayNames(new[] { "일", "월", "화", "수", "목", "금", "토" }); /// UTKDatePicker.SetDayNames(new[] { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" }); /// /// // 요일 이름 로컬라이제이션 키 설정 /// UTKDatePicker.SetDayNameKeys(new[] { "day_sun", "day_mon", "day_tue", "day_wed", "day_thu", "day_fri", "day_sat" }); /// /// // 요일 이름 기본값으로 초기화 /// UTKDatePicker.ResetDayNames(); /// /// // 인스턴스 직접 생성 /// var datePicker = new UTKDatePicker(); /// datePicker.SetDate(DateTime.Today); /// container.Add(datePicker); /// /// [UxmlElement] public partial class UTKDatePicker : VisualElement, IDisposable { #region Enums public enum PickerMode { DateOnly, DateAndTime, DateRange, DateTimeRange } /// /// 범위 선택 시 현재 선택 중인 날짜 타입 /// private enum RangeSelectionState { SelectingStart, SelectingEnd } #endregion #region Constants private const string UXML_PATH = "UIToolkit/Modal/UTKDatePicker"; private const string USS_PATH = "UIToolkit/Modal/UTKDatePicker"; private const int DAYS_IN_GRID = 42; // 6 rows x 7 columns private static readonly string[] DEFAULT_DAY_NAME_KEYS = { "day_sun", "day_mon", "day_tue", "day_wed", "day_thu", "day_fri", "day_sat" }; #endregion #region Static Fields private static string[] s_dayNameKeys = DEFAULT_DAY_NAME_KEYS; private static string[]? s_customDayNames; #endregion #region Fields private bool _disposed; private PickerMode _mode; private DateTime _selectedDate = DateTime.Today; private DateTime _displayMonth = new DateTime(DateTime.Today.Year, DateTime.Today.Month, 1); // 범위 선택 관련 필드 private DateTime? _rangeStartDate; private DateTime? _rangeEndDate; private RangeSelectionState _rangeState = RangeSelectionState.SelectingStart; private UTKModalBlocker? _blocker; private readonly List