|
5 | 5 | getCalendarDays, |
6 | 6 | type CalendarDay, |
7 | 7 | applyTimePrecision, |
| 8 | + clampDate, |
| 9 | + clamp, |
8 | 10 | } from './date-utils.js' |
9 | 11 | import { getInnerLocale, type Locale } from './locale.js' |
10 | 12 | import { createEventDispatcher } from 'svelte' |
| 13 | + import { cloneDate, toValidDate } from './date-utils.js' |
11 | 14 |
|
12 | 15 | const dispatch = createEventDispatcher<{ |
13 | 16 | /** Fires when the user selects a new value by clicking on a date or by pressing enter */ |
14 | 17 | select: Date |
15 | 18 | }>() |
16 | 19 |
|
17 | | - function cloneDate(d: Date) { |
18 | | - return new Date(d.getTime()) |
19 | | - } |
20 | | -
|
21 | 20 | /** Date value. It's `null` if no date is selected */ |
22 | 21 | export let value: Date | null = null |
23 | 22 |
|
24 | 23 | function setValue(d: Date) { |
25 | 24 | if (d.getTime() !== value?.getTime()) { |
26 | | - browseDate = clamp(d, min, max) |
| 25 | + browseDate = toValidDate(value ?? browseDate, d, min, max, isDisabledDate) |
27 | 26 | applyTimePrecision(browseDate, timePrecision) |
28 | 27 | value = cloneDate(browseDate) |
29 | 28 | } |
30 | 29 | } |
31 | 30 |
|
32 | | - function setValueDate(d: Date) { |
33 | | - if (d.getTime() !== value?.getTime()) { |
34 | | - browseDate = clampDate(d, min, max) |
35 | | - value = cloneDate(browseDate) |
36 | | - } |
37 | | - } |
38 | | -
|
39 | 31 | /** Set the browseDate */ |
40 | 32 | function browse(d: Date) { |
41 | 33 | browseDate = clampDate(d, min, max) |
|
63 | 55 | export let min = new Date(defaultDate.getFullYear() - 20, 0, 1) |
64 | 56 | /** The latest year the user can select */ |
65 | 57 | export let max = new Date(defaultDate.getFullYear(), 11, 31, 23, 59, 59, 999) |
| 58 | + /** Disallow specific dates */ |
| 59 | + export let isDisabledDate: ((dateToCheck: Date) => boolean) | null = null |
| 60 | +
|
| 61 | + function handleDisabledDate(date: CalendarDay) { |
| 62 | + return isDisabledDate?.(new Date(date.year, date.month, date.number)) |
| 63 | + } |
| 64 | +
|
| 65 | + // Prevents a invalid date from being typed into the Dateinput text box |
66 | 66 | $: if (value && value > max) { |
67 | | - setValue(max) |
| 67 | + setValue(toValidDate(value, max, min, max, isDisabledDate)) |
68 | 68 | } else if (value && value < min) { |
69 | | - setValue(min) |
70 | | - } |
71 | | - function clamp(d: Date, min: Date, max: Date) { |
72 | | - if (d > max) { |
73 | | - return cloneDate(max) |
74 | | - } else if (d < min) { |
75 | | - return cloneDate(min) |
76 | | - } else { |
77 | | - return cloneDate(d) |
78 | | - } |
79 | | - } |
80 | | - function clampDate(d: Date, min: Date, max: Date) { |
81 | | - const limit = clamp(d, min, max) |
82 | | - if (limit.getTime() !== d.getTime()) { |
83 | | - d = new Date( |
84 | | - limit.getFullYear(), |
85 | | - limit.getMonth(), |
86 | | - limit.getDate(), |
87 | | - d.getHours(), |
88 | | - d.getMinutes(), |
89 | | - d.getSeconds(), |
90 | | - d.getMilliseconds(), |
91 | | - ) |
92 | | - d = clamp(d, min, max) |
93 | | - } |
94 | | - return d |
| 69 | + setValue(toValidDate(value, min, min, max, isDisabledDate)) |
| 70 | + } else if (value && isDisabledDate?.(value)) { |
| 71 | + setValue(toValidDate(browseDate, value, min, max, isDisabledDate)) |
95 | 72 | } |
96 | 73 |
|
97 | 74 | /** The date shown in the popup when none is selected */ |
|
156 | 133 | $: calendarDays = getCalendarDays(browseDate, iLocale.weekStartsOn) |
157 | 134 |
|
158 | 135 | function selectDay(calendarDay: CalendarDay) { |
159 | | - if (dayIsInRange(calendarDay, min, max)) { |
| 136 | + if (dayIsInRange(calendarDay, min, max) && !handleDisabledDate(calendarDay)) { |
160 | 137 | browseDate.setFullYear(0) |
161 | 138 | browseDate.setMonth(0) |
162 | 139 | browseDate.setDate(1) |
163 | 140 | browseDate.setFullYear(calendarDay.year) |
164 | 141 | browseDate.setMonth(calendarDay.month) |
165 | 142 | browseDate.setDate(calendarDay.number) |
166 | | - setValueDate(browseDate) |
| 143 | + setValue(browseDate) |
167 | 144 | dispatch('select', cloneDate(browseDate)) |
168 | 145 | } |
169 | 146 | } |
|
236 | 213 | return |
237 | 214 | } else if (e.key === 'ArrowUp') { |
238 | 215 | browseDate.setDate(browseDate.getDate() - 7) |
239 | | - setValueDate(browseDate) |
| 216 | + setValue(browseDate) |
240 | 217 | } else if (e.key === 'ArrowDown') { |
241 | 218 | browseDate.setDate(browseDate.getDate() + 7) |
242 | | - setValueDate(browseDate) |
| 219 | + setValue(browseDate) |
243 | 220 | } else if (e.key === 'ArrowLeft') { |
244 | 221 | browseDate.setDate(browseDate.getDate() - 1) |
245 | | - setValueDate(browseDate) |
| 222 | + setValue(browseDate) |
246 | 223 | } else if (e.key === 'ArrowRight') { |
247 | 224 | browseDate.setDate(browseDate.getDate() + 1) |
248 | | - setValueDate(browseDate) |
| 225 | + setValue(browseDate) |
249 | 226 | } else if (e.key === 'Enter') { |
250 | 227 | setValue(browseDate) |
251 | 228 | dispatch('select', cloneDate(browseDate)) |
|
355 | 332 | <div |
356 | 333 | class="cell" |
357 | 334 | on:click={() => selectDay(calendarDay)} |
358 | | - class:disabled={!dayIsInRange(calendarDay, min, max)} |
| 335 | + class:disabled={!dayIsInRange(calendarDay, min, max) || handleDisabledDate(calendarDay)} |
359 | 336 | class:selected={value && |
360 | 337 | calendarDay.year === value.getFullYear() && |
361 | 338 | calendarDay.month === value.getMonth() && |
|
0 commit comments