this repo has no description

Calendar highlight fix, added more months, added date-fns to WeekPicker

+89 -35
+89 -35
islands/WeekPicker.tsx
··· 1 1 import { useEffect, useRef, useState } from "preact/hooks"; 2 2 import { IS_BROWSER } from "$fresh/runtime.ts"; 3 + import startOfMonth from "https://deno.land/x/date_fns@v2.22.1/startOfMonth/index.ts"; 4 + import startOfWeek from "https://deno.land/x/date_fns@v2.22.1/startOfWeek/index.ts"; 5 + import addMonths from "https://deno.land/x/date_fns@v2.22.1/addMonths/index.ts"; 6 + import format from "https://deno.land/x/date_fns@v2.22.1/format/index.js"; 7 + import subMonths from "https://deno.land/x/date_fns@v2.22.1/subMonths/index.ts"; 3 8 4 9 interface WeekPickerProps { 5 10 onWeekChange?: (startDate: Date, endDate: Date) => void; ··· 11 16 ) { 12 17 const [selectedDates, setSelectedDates] = useState<Date[]>([]); 13 18 const [displayText, setDisplayText] = useState<string>("Select a week..."); 19 + const [currentMonth, setCurrentMonth] = useState<Date>(new Date()); 14 20 const weekPickerRef = useRef<HTMLDivElement>(null); 15 21 const inputRef = useRef<HTMLInputElement>(null); 16 22 const [isPickerVisible, setIsPickerVisible] = useState(false); ··· 46 52 47 53 const generateWeekDates = (baseDate: Date): Date[] => { 48 54 const dates: Date[] = []; 49 - const weekStart = new Date(baseDate); 50 - const day = weekStart.getUTCDay(); 51 - const diff = day === 0 ? 6 : day - 1; // Adjust for Monday as week start 52 - 53 - weekStart.setUTCDate(weekStart.getUTCDate() - diff); 55 + // Use date-fns to get the Monday of the week in UTC 56 + const weekStart = startOfWeek(baseDate, { weekStartsOn: 1 }); 54 57 55 58 for (let i = 0; i < 7; i++) { 56 59 const date = new Date(weekStart); ··· 63 66 64 67 // Format date for display 65 68 const formatDate = (date: Date): string => { 66 - return date.toLocaleDateString("en-US", { 67 - weekday: "short", 68 - month: "short", 69 - day: "numeric", 70 - year: "numeric", 71 - timeZone: "UTC", // Ensure UTC formatting 72 - }); 69 + return format(date, "EEE, MMM d, yyyy", {}); 73 70 }; 74 71 75 72 // Handle date selection ··· 90 87 setIsPickerVisible(false); 91 88 }; 92 89 90 + // Navigate to previous month 91 + const goToPreviousMonth = (e: Event) => { 92 + e.stopPropagation(); 93 + setCurrentMonth(subMonths(currentMonth, 1)); 94 + }; 95 + 96 + // Navigate to next month 97 + const goToNextMonth = (e: Event) => { 98 + e.stopPropagation(); 99 + setCurrentMonth(addMonths(currentMonth, 1)); 100 + }; 101 + 93 102 // Toggle date picker visibility 94 103 const toggleDatePicker = () => { 95 104 setIsPickerVisible(!isPickerVisible); ··· 134 143 135 144 {isPickerVisible && ( 136 145 <div className="date-picker-popup"> 146 + <div className="month-navigation"> 147 + <button 148 + className="month-nav-button" 149 + onClick={goToPreviousMonth} 150 + > 151 + &lt; 152 + </button> 153 + <div className="current-month"> 154 + {format(currentMonth, "MMMM yyyy", {})} 155 + </div> 156 + <button 157 + className="month-nav-button" 158 + onClick={goToNextMonth} 159 + > 160 + &gt; 161 + </button> 162 + </div> 137 163 <div className="calendar-grid"> 138 164 {["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"].map( 139 165 (day) => ( ··· 146 172 ), 147 173 )} 148 174 149 - {generateCalendarDays().map((date, index) => ( 175 + {generateCalendarDays(currentMonth).map(( 176 + date, 177 + index, 178 + ) => ( 150 179 <div 151 180 key={index} 152 181 className={`calendar-day ${ 153 182 isDateInSelectedWeek(date) 154 183 ? "selected-day" 184 + : "" 185 + } ${ 186 + date.getMonth() !== currentMonth.getMonth() 187 + ? "other-month" 155 188 : "" 156 189 }`} 157 190 onClick={() => handleDateSelect(date)} ··· 203 236 z-index: 10; 204 237 } 205 238 239 + .month-navigation { 240 + display: flex; 241 + justify-content: space-between; 242 + align-items: center; 243 + margin-bottom: 12px; 244 + } 245 + 246 + .month-nav-button { 247 + background-color: #f3f4f6; 248 + border: 1px solid #ccc; 249 + border-radius: 4px; 250 + padding: 4px 8px; 251 + cursor: pointer; 252 + } 253 + 254 + .month-nav-button:hover { 255 + background-color: #e5e7eb; 256 + } 257 + 258 + .current-month { 259 + font-weight: bold; 260 + } 261 + 206 262 .calendar-grid { 207 263 display: grid; 208 264 grid-template-columns: repeat(7, 1fr); ··· 228 284 .selected-day { 229 285 background-color: #bfdbfe; 230 286 } 287 + 288 + .other-month { 289 + color: #9ca3af; 290 + } 231 291 `} 232 292 </style> 233 293 </div> 234 294 ); 235 295 236 - // Helper function to generate calendar days for current month view 237 - function generateCalendarDays(): Date[] { 238 - const today = new Date(); 239 - const year = today.getFullYear(); 240 - const month = today.getMonth(); 296 + // Helper function to generate calendar days for the specified month view 297 + function generateCalendarDays(date: Date): Date[] { 298 + const year = date.getFullYear(); 299 + const month = date.getMonth(); 241 300 242 301 // First day of the month 243 - const firstDay = new Date(year, month, 1); 244 - // Last day of the month 245 - const _lastDay = new Date(year, month + 1, 0); 302 + const firstDay = startOfMonth(new Date(year, month, 1)); 246 303 247 304 const days: Date[] = []; 248 305 249 306 // Get the first Monday (or previous Monday if 1st is not Monday) 250 - const firstMonday = new Date(firstDay); 251 307 const firstDayOfWeek = firstDay.getDay() || 7; // Convert Sunday (0) to 7 308 + const firstMonday = new Date(firstDay); 252 309 firstMonday.setDate(firstDay.getDate() - (firstDayOfWeek - 1)); 253 310 254 - // Generate at least 42 days (6 weeks) to ensure we have a complete view 311 + // Generate 6 weeks worth of days to ensure complete month view 255 312 for (let i = 0; i < 42; i++) { 256 - const date = new Date(firstMonday); 257 - date.setDate(firstMonday.getDate() + i); 258 - days.push(date); 259 - 260 - // Break if we've reached the end of the month and completed the week 261 - if (date.getMonth() > month && date.getDay() === 0) { 262 - break; 263 - } 313 + const day = new Date(firstMonday); 314 + day.setDate(firstMonday.getDate() + i); 315 + days.push(day); 264 316 } 265 317 266 318 return days; ··· 270 322 function isDateInSelectedWeek(date: Date): boolean { 271 323 if (selectedDates.length === 0) return false; 272 324 273 - return selectedDates.some((selectedDate) => 274 - selectedDate.getFullYear() === date.getFullYear() && 275 - selectedDate.getMonth() === date.getMonth() && 276 - selectedDate.getDate() === date.getDate() 325 + const startOfWeek = selectedDates[0]; 326 + const endOfWeek = selectedDates[6]; 327 + 328 + return ( 329 + date.getTime() >= startOfWeek.getTime() && 330 + date.getTime() <= endOfWeek.getTime() 277 331 ); 278 332 } 279 333 }