|
19 | 19 |
|
20 | 20 | let showCalendar = false; |
21 | 21 | let currentMonth = new Date(); |
22 | | - let selectedDay: number = currentMonth.getDate(); |
23 | | - let selectedMonth: number = currentMonth.getMonth(); |
24 | | - let selectedYear: number = currentMonth.getFullYear(); |
| 22 | + let selectedDay: number | null = null; |
| 23 | + let selectedMonth: number | null = null; |
| 24 | + let selectedYear: number | null = null; |
25 | 25 | let calendarRef: HTMLDivElement; |
26 | 26 | let inputRef: HTMLInputElement; |
27 | 27 | let showAbove = false; |
28 | 28 | let isInvalid = false; |
29 | 29 | let internalValue = value; // Track internal value for validation |
| 30 | + let isClearing = false; // Add flag to track clearing state |
| 31 | +
|
| 32 | + // Track the original/previous date for clear functionality |
| 33 | + let originalValue = value; |
| 34 | + let hasChanged = false; |
| 35 | +
|
| 36 | + // Initialize selected date state when component mounts or value changes |
| 37 | + function initializeSelectedDate(dateValue: string) { |
| 38 | + const date = parseDate(dateValue); |
| 39 | + if (date) { |
| 40 | + selectedDay = date.getDate(); |
| 41 | + selectedMonth = date.getMonth(); |
| 42 | + selectedYear = date.getFullYear(); |
| 43 | + currentMonth = new Date(selectedYear, selectedMonth, 1); |
| 44 | + } else { |
| 45 | + selectedDay = null; |
| 46 | + selectedMonth = null; |
| 47 | + selectedYear = null; |
| 48 | + } |
| 49 | + } |
| 50 | +
|
| 51 | + // Initialize on mount if value exists |
| 52 | + onMount(() => { |
| 53 | + if (value) { |
| 54 | + initializeSelectedDate(value); |
| 55 | + originalValue = value; |
| 56 | + } |
| 57 | + }); |
30 | 58 |
|
31 | 59 | // Process min/max dates |
32 | 60 | let minDateObj: Date | null = null; |
|
137 | 165 | const target = event.target as HTMLInputElement; |
138 | 166 | internalValue = target.value; |
139 | 167 |
|
140 | | - // If empty, clear the date |
| 168 | + // If empty, clear the date and selected state |
141 | 169 | if (!internalValue.trim()) { |
142 | 170 | isInvalid = false; |
143 | 171 | value = ""; |
| 172 | + selectedDay = null; |
| 173 | + selectedMonth = null; |
| 174 | + selectedYear = null; |
144 | 175 | return; |
145 | 176 | } |
146 | 177 |
|
| 178 | + // Store original value if this is the first change |
| 179 | + if (!hasChanged && value) { |
| 180 | + originalValue = value; |
| 181 | + } |
| 182 | +
|
147 | 183 | // Try to parse the date |
148 | 184 | const date = parseDate(internalValue); |
149 | 185 | if (date) { |
|
160 | 196 | selectedMonth = date.getMonth(); |
161 | 197 | selectedYear = date.getFullYear(); |
162 | 198 | currentMonth = new Date(selectedYear, selectedMonth, 1); |
| 199 | + hasChanged = true; |
163 | 200 |
|
164 | 201 | // Dispatch change event |
165 | 202 | dispatch("change", value); |
|
231 | 268 | } |
232 | 269 | } |
233 | 270 |
|
234 | | - // Watch for external value changes |
| 271 | + // Watch for external value changes and track original value |
235 | 272 | $: if (value !== internalValue && !isInvalid) { |
236 | 273 | internalValue = value; |
237 | 274 |
|
238 | | - // Parse value to update selected date in calendar |
239 | | - const date = parseDate(value); |
240 | | - if (date) { |
241 | | - selectedDay = date.getDate(); |
242 | | - selectedMonth = date.getMonth(); |
243 | | - selectedYear = date.getFullYear(); |
244 | | - currentMonth = new Date(selectedYear, selectedMonth, 1); |
| 275 | + // If this is the first time setting a value (from props), store as original |
| 276 | + if (!hasChanged && value && !originalValue) { |
| 277 | + originalValue = value; |
245 | 278 | } |
| 279 | +
|
| 280 | + // Initialize selected date state for calendar |
| 281 | + initializeSelectedDate(value); |
| 282 | + } |
| 283 | +
|
| 284 | + // Also watch for direct value prop changes (when parent updates the value) |
| 285 | + $: if ( |
| 286 | + value && |
| 287 | + value === internalValue && |
| 288 | + (selectedDay === null || selectedMonth === null || selectedYear === null) |
| 289 | + ) { |
| 290 | + initializeSelectedDate(value); |
246 | 291 | } |
247 | 292 |
|
248 | 293 | function getDaysInMonth(date: Date) { |
|
300 | 345 | return; // Don't select disabled or non-current month days |
301 | 346 | } |
302 | 347 |
|
| 348 | + // Store original value if this is the first change |
| 349 | + if (!hasChanged && value) { |
| 350 | + originalValue = value; |
| 351 | + } |
| 352 | +
|
303 | 353 | selectedDay = dayObj.day; |
304 | 354 | selectedMonth = currentMonth.getMonth(); |
305 | 355 | selectedYear = currentMonth.getFullYear(); |
306 | 356 | value = formatDate(selectedDay, selectedMonth, selectedYear); |
307 | 357 | internalValue = value; |
308 | 358 | isInvalid = false; |
309 | 359 | showCalendar = false; |
| 360 | + hasChanged = true; |
310 | 361 |
|
311 | 362 | // Dispatch change event when date is selected |
312 | 363 | dispatch("change", value); |
|
327 | 378 | return; |
328 | 379 | } |
329 | 380 |
|
| 381 | + // Store original value if this is the first change |
| 382 | + if (!hasChanged && value) { |
| 383 | + originalValue = value; |
| 384 | + } |
| 385 | +
|
330 | 386 | currentMonth = new Date(today.getFullYear(), today.getMonth(), 1); |
331 | 387 | selectedDay = today.getDate(); |
332 | 388 | selectedMonth = today.getMonth(); |
|
335 | 391 | internalValue = value; |
336 | 392 | isInvalid = false; |
337 | 393 | showCalendar = false; |
| 394 | + hasChanged = true; |
338 | 395 | // Dispatch change event when Today is clicked |
339 | 396 | dispatch("change", value); |
340 | 397 | } |
341 | 398 |
|
342 | 399 | function handleClear() { |
343 | | - value = ""; |
344 | | - internalValue = ""; |
345 | | - isInvalid = false; |
346 | | - showCalendar = false; |
347 | | - // Dispatch change event when Clear is clicked |
348 | | - dispatch("change", ""); |
| 400 | + // If user has made changes, revert to the original value |
| 401 | + if (hasChanged && originalValue) { |
| 402 | + const originalDate = parseDate(originalValue); |
| 403 | + if (originalDate) { |
| 404 | + selectedDay = originalDate.getDate(); |
| 405 | + selectedMonth = originalDate.getMonth(); |
| 406 | + selectedYear = originalDate.getFullYear(); |
| 407 | + value = originalValue; |
| 408 | + internalValue = originalValue; |
| 409 | + currentMonth = new Date(selectedYear, selectedMonth, 1); |
| 410 | + hasChanged = false; // Reset the change flag |
| 411 | +
|
| 412 | + isInvalid = false; |
| 413 | + showCalendar = false; |
| 414 | +
|
| 415 | + // Dispatch change event with the reverted value |
| 416 | + dispatch("change", originalValue); |
| 417 | + } |
| 418 | + } else if (value && !hasChanged) { |
| 419 | + // If there's a date selected but no changes made, keep the current date (do nothing) |
| 420 | + showCalendar = false; |
| 421 | + // Don't dispatch change event as we're not changing the value |
| 422 | + return; |
| 423 | + } else { |
| 424 | + // No original value and no current value, clear completely |
| 425 | + value = ""; |
| 426 | + internalValue = ""; |
| 427 | + selectedDay = null; |
| 428 | + selectedMonth = null; |
| 429 | + selectedYear = null; |
| 430 | + originalValue = ""; |
| 431 | + hasChanged = false; |
| 432 | +
|
| 433 | + isInvalid = false; |
| 434 | + showCalendar = false; |
| 435 | +
|
| 436 | + // Dispatch change event with empty value |
| 437 | + dispatch("change", ""); |
| 438 | + } |
349 | 439 | } |
350 | 440 |
|
351 | 441 | const toggleCalendar = () => { |
|
0 commit comments