import { useRef, useEffect, useState } from "react"; import dateRangePickerStyles from "~/styles/components/date-range-picker.css?url"; export interface DateRangePickerProps { startDate: string; endDate: string; onStartDateChange: (value: string) => void; onEndDateChange: (value: string) => void; startLabel?: string; endLabel?: string; className?: string; startId?: string; endId?: string; format?: string; placeholder?: string; colorMode?: 'light' | 'dark' | 'auto'; } export function links() { return [ { rel: "stylesheet", href: dateRangePickerStyles } ]; } // 格式化日期函数 function formatDate(dateString: string, format = "yyyy-MM-dd"): string { if (!dateString) return ""; try { const date = new Date(dateString); if (isNaN(date.getTime())) return dateString; const year = date.getFullYear(); const month = String(date.getMonth() + 1).padStart(2, '0'); const day = String(date.getDate()).padStart(2, '0'); return format .replace("yyyy", year.toString()) .replace("MM", month) .replace("dd", day); } catch (e) { return dateString; } } /** * 日期范围选择器组件 * 用于选择日期范围,如开始日期和结束日期 */ export function DateRangePicker({ startDate, endDate, onStartDateChange, onEndDateChange, startLabel = "从", endLabel = "至", className = "", startId = "date-start", endId = "date-end", format = "yyyy-MM-dd", placeholder = "请选择时间", colorMode = 'auto' }: DateRangePickerProps) { // 使用ref获取input元素 const startInputRef = useRef(null); const endInputRef = useRef(null); // 添加状态跟踪哪个日期输入框被聚焦 const [focusedInput, setFocusedInput] = useState(null); // 格式化显示日期 const formattedStartDate = startDate ? formatDate(startDate, format) : ""; const formattedEndDate = endDate ? formatDate(endDate, format) : ""; // 保持原始日期输入框的显示格式 useEffect(() => { if (startInputRef.current) { startInputRef.current.setAttribute('data-display-value', formattedStartDate || placeholder); } if (endInputRef.current) { endInputRef.current.setAttribute('data-display-value', formattedEndDate || placeholder); } }, [formattedStartDate, formattedEndDate, placeholder]); // 处理日期输入框全局点击 const handleWrapperClick = (inputRef: React.RefObject) => { if (inputRef.current) { // console.log('Wrapper clicked, triggering date input'); // 点击整个包装器区域时,触发输入框点击 inputRef.current.focus(); inputRef.current.click(); try { // 检查并记录showPicker是否可用 const hasShowPicker = typeof inputRef.current.showPicker === 'function'; // console.log('showPicker API available:', hasShowPicker); // 尝试使用showPicker API if (hasShowPicker) { inputRef.current.showPicker(); // console.log('showPicker called successfully'); } } catch (error) { console.error('Failed to show date picker:', error); } } }; // 处理键盘事件 const handleKeyDown = (e: React.KeyboardEvent, inputRef: React.RefObject) => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); handleWrapperClick(inputRef); } }; // 处理聚焦和失焦 const handleFocus = (id: string) => { setFocusedInput(id); }; const handleBlur = () => { setFocusedInput(null); }; // 获取颜色模式类名 const getColorModeClass = () => { if (colorMode === 'light') return 'color-mode-light'; if (colorMode === 'dark') return 'color-mode-dark'; return ''; // auto模式不添加额外类名 }; return (
{ e.stopPropagation(); handleWrapperClick(startInputRef); }} onKeyDown={(e) => handleKeyDown(e, startInputRef)} tabIndex={0} role="button" aria-label={`选择${startLabel}日期`} > onStartDateChange(e.target.value)} onFocus={() => handleFocus(startId)} onBlur={handleBlur} />
{formattedStartDate || placeholder}
{ e.stopPropagation(); handleWrapperClick(endInputRef); }} onKeyDown={(e) => handleKeyDown(e, endInputRef)} tabIndex={0} role="button" aria-label={`选择${endLabel}日期`} > onEndDateChange(e.target.value)} onFocus={() => handleFocus(endId)} onBlur={handleBlur} />
{formattedEndDate || placeholder}
); } // 简化版日期范围选择器,适用于紧凑布局,不显示标签 export function SimpleDateRangePicker({ startDate, endDate, onStartDateChange, onEndDateChange, className = "", startId = "date-start-simple", endId = "date-end-simple", format = "yyyy-MM-dd", placeholder = "请选择时间", colorMode = 'auto' }: Omit) { // 使用ref获取input元素 const startInputRef = useRef(null); const endInputRef = useRef(null); // 添加状态跟踪哪个日期输入框被聚焦 const [focusedInput, setFocusedInput] = useState(null); // 格式化显示日期 const formattedStartDate = startDate ? formatDate(startDate, format) : ""; const formattedEndDate = endDate ? formatDate(endDate, format) : ""; // 保持原始日期输入框的显示格式 useEffect(() => { if (startInputRef.current) { startInputRef.current.setAttribute('data-display-value', formattedStartDate || placeholder); } if (endInputRef.current) { endInputRef.current.setAttribute('data-display-value', formattedEndDate || placeholder); } }, [formattedStartDate, formattedEndDate, placeholder]); // 处理日期输入框全局点击 const handleWrapperClick = (inputRef: React.RefObject) => { if (inputRef.current) { // console.log('Wrapper clicked, triggering date input'); // 点击整个包装器区域时,触发输入框点击 inputRef.current.focus(); inputRef.current.click(); try { // 检查并记录showPicker是否可用 const hasShowPicker = typeof inputRef.current.showPicker === 'function'; // console.log('showPicker API available:', hasShowPicker); // 尝试使用showPicker API if (hasShowPicker) { inputRef.current.showPicker(); // console.log('showPicker called successfully'); } } catch (error) { console.error('Failed to show date picker:', error); } } }; // 处理键盘事件 const handleKeyDown = (e: React.KeyboardEvent, inputRef: React.RefObject) => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); handleWrapperClick(inputRef); } }; // 处理聚焦和失焦 const handleFocus = (id: string) => { setFocusedInput(id); }; const handleBlur = () => { setFocusedInput(null); }; // 获取颜色模式类名 const getColorModeClass = () => { if (colorMode === 'light') return 'color-mode-light'; if (colorMode === 'dark') return 'color-mode-dark'; return ''; // auto模式不添加额外类名 }; return (
{ e.stopPropagation(); handleWrapperClick(startInputRef); }} onKeyDown={(e) => handleKeyDown(e, startInputRef)} tabIndex={0} role="button" aria-label="选择开始日期" > onStartDateChange(e.target.value)} onFocus={() => handleFocus(startId)} onBlur={handleBlur} aria-label="开始日期" />
{formattedStartDate || placeholder}
{ e.stopPropagation(); handleWrapperClick(endInputRef); }} onKeyDown={(e) => handleKeyDown(e, endInputRef)} tabIndex={0} role="button" aria-label="选择结束日期" > onEndDateChange(e.target.value)} onFocus={() => handleFocus(endId)} onBlur={handleBlur} aria-label="结束日期" />
{formattedEndDate || placeholder}
); }