Skip to content

Commit 3c6713a

Browse files
Combine shownDate and highlightedDate
1 parent 18bdf5a commit 3c6713a

File tree

1 file changed

+107
-124
lines changed

1 file changed

+107
-124
lines changed

src/lib/DatePicker.svelte

Lines changed: 107 additions & 124 deletions
Original file line numberDiff line numberDiff line change
@@ -7,37 +7,47 @@
77
88
const dispatch = createEventDispatcher<{ select: undefined }>()
99
10+
function cloneDate(d: Date) {
11+
return new Date(d.getTime())
12+
}
13+
1014
/** Date value. It's `null` if no date is selected */
1115
export let value: Date | null = null
16+
1217
function setValue(d: Date) {
1318
if (d.getTime() !== value?.getTime()) {
14-
value = d
19+
value = cloneDate(d)
20+
clamp(min, max)
1521
}
1622
}
17-
function updateValue(updater: (date: Date) => Date) {
18-
const newValue = updater(new Date(shownDate.getTime()))
19-
setValue(newValue)
23+
function browse(d: Date) {
24+
browseDate = value ? cloneDate(d) : cloneDate(defaultDate)
25+
if (!browseWithoutSelecting) {
26+
setValue(d)
27+
clamp(min, max)
28+
}
2029
}
2130
2231
/** Default Date to use */
2332
const defaultDate = new Date()
2433
25-
/** The date shown in the popup, for when `value` is null */
26-
let shownDate = value ?? defaultDate
27-
$: if (value) shownDate = value
28-
29-
/** Update the shownDate. The date is only selected if a date is already selected */
30-
function updateShownDate(updater: (date: Date) => Date) {
31-
shownDate = updater(new Date(shownDate.getTime()))
32-
if (!browseWithoutSelecting && value && shownDate.getTime() !== value.getTime()) {
33-
setValue(shownDate)
34-
}
35-
}
34+
/** The date shown in the popup when none is selected */
35+
let browseDate = value ? cloneDate(value) : cloneDate(defaultDate)
36+
$: if (value) browseDate = cloneDate(value)
3637
3738
/** The earliest year the user can select */
3839
export let min = new Date(defaultDate.getFullYear() - 20, 0, 1)
3940
/** The latest year the user can select */
4041
export let max = new Date(defaultDate.getFullYear(), 11, 31, 23, 59, 59, 999)
42+
function clamp(min: Date, max: Date) {
43+
if (browseDate && browseDate > max) {
44+
setValue(max)
45+
} else if (browseDate && browseDate < min) {
46+
setValue(min)
47+
}
48+
}
49+
$: if (value) clamp(min, max)
50+
4151
let years = getYears(min, max)
4252
$: years = getYears(min, max)
4353
function getYears(min: Date, max: Date) {
@@ -48,93 +58,70 @@
4858
return years
4959
}
5060
51-
$: if (shownDate > max) {
52-
updateShownDate(() => max)
53-
} else if (shownDate < min) {
54-
updateShownDate(() => min)
55-
}
56-
5761
/** Locale object for internationalization */
5862
export let locale: Locale = {}
5963
$: iLocale = getInnerLocale(locale)
6064
/** Wait with updating the date until a date is selected */
6165
export let browseWithoutSelecting = false
62-
// The highlighted date shown in the popup. When `browseWithoutSelecting`
63-
// is set to `true`, it is used to help determine whether a calendar day should
64-
// be highlighted.
65-
let highlightedDate = shownDate
66-
function shouldHighlightCalendarDay(day: CalendarDay) {
67-
return browseWithoutSelecting
68-
? day.year === highlightedDate.getFullYear() &&
69-
day.month === highlightedDate.getMonth() &&
70-
day.number === highlightedDate.getDate()
71-
: day.month === month && day.number === dayOfMonth
72-
}
7366
74-
let year = shownDate.getFullYear()
75-
const getYear = (tmpPickerDate: Date) => (year = tmpPickerDate.getFullYear())
76-
function setYear(year: number) {
77-
updateShownDate((tmpPickerDate) => {
78-
tmpPickerDate.setFullYear(year)
79-
return tmpPickerDate
80-
})
67+
let browseYear = browseDate.getFullYear()
68+
function getBrowseYear(tmpPickerDate: Date) {
69+
browseMonth = tmpPickerDate.getMonth()
70+
}
71+
$: getBrowseYear(browseDate)
72+
function setYear(newYear: number) {
73+
browseDate.setFullYear(newYear)
74+
browseDate = browseDate
75+
if (value) {
76+
browse(browseDate)
77+
}
8178
}
82-
$: getYear(shownDate)
83-
$: setYear(year)
79+
$: setYear(browseYear)
8480
85-
let month = shownDate.getMonth()
86-
const getMonth = (tmpPickerDate: Date) => (month = tmpPickerDate.getMonth())
87-
function setMonth(month: number) {
88-
let newYear = year
89-
let newMonth = month
90-
if (month === 12) {
81+
let browseMonth = browseDate.getMonth()
82+
function getBrowseMonth(d: Date) {
83+
browseMonth = d.getMonth()
84+
}
85+
$: getBrowseMonth(browseDate)
86+
function setMonth(newMonth: number) {
87+
let newYear = browseDate.getFullYear()
88+
if (newMonth === 12) {
9189
newMonth = 0
9290
newYear++
93-
} else if (month === -1) {
91+
} else if (newMonth === -1) {
9492
newMonth = 11
9593
newYear--
9694
}
9795
9896
const maxDate = getMonthLength(newYear, newMonth)
99-
const newDate = Math.min(shownDate.getDate(), maxDate)
100-
updateShownDate((date) => {
101-
return new Date(
102-
newYear,
103-
newMonth,
104-
newDate,
105-
date.getHours(),
106-
date.getMinutes(),
107-
date.getSeconds(),
108-
date.getMilliseconds()
109-
)
110-
})
97+
const newDate = Math.min(browseDate.getDate(), maxDate)
98+
browseDate = new Date(
99+
newYear,
100+
newMonth,
101+
newDate,
102+
browseDate.getHours(),
103+
browseDate.getMinutes(),
104+
browseDate.getSeconds(),
105+
browseDate.getMilliseconds()
106+
)
107+
browse(browseDate)
111108
}
112-
$: getMonth(shownDate)
113-
$: setMonth(month)
114-
115-
let dayOfMonth = value?.getDate() || null
116-
$: dayOfMonth = value?.getDate() || null
109+
$: setMonth(browseMonth)
117110
118-
$: calendarDays = getCalendarDays(shownDate, iLocale.weekStartsOn)
111+
$: calendarDays = getCalendarDays(browseDate, iLocale.weekStartsOn)
119112
120-
function setDay(calendarDay: CalendarDay) {
113+
function selectDay(calendarDay: CalendarDay) {
121114
if (dayIsInRange(calendarDay, min, max)) {
122-
updateValue((value) => {
123-
value.setFullYear(0)
124-
value.setMonth(0)
125-
value.setDate(1)
126-
value.setFullYear(calendarDay.year)
127-
value.setMonth(calendarDay.month)
128-
value.setDate(calendarDay.number)
129-
highlightedDate = value
130-
return value
131-
})
115+
browseDate.setFullYear(0)
116+
browseDate.setMonth(0)
117+
browseDate.setDate(1)
118+
browseDate.setFullYear(calendarDay.year)
119+
browseDate.setMonth(calendarDay.month)
120+
browseDate.setDate(calendarDay.number)
121+
setValue(browseDate)
122+
dispatch('select')
132123
}
133124
}
134-
function selectDay(calendarDay: CalendarDay) {
135-
setDay(calendarDay)
136-
dispatch('select')
137-
}
138125
function dayIsInRange(calendarDay: CalendarDay, min: Date, max: Date) {
139126
const date = new Date(calendarDay.year, calendarDay.month, calendarDay.number)
140127
const minDate = new Date(min.getFullYear(), min.getMonth(), min.getDate())
@@ -143,13 +130,13 @@
143130
}
144131
function shiftKeydown(e: KeyboardEvent) {
145132
if (e.shiftKey && e.key === 'ArrowUp') {
146-
setYear(year - 1)
133+
setYear(browseDate.getFullYear() - 1)
147134
} else if (e.shiftKey && e.key === 'ArrowDown') {
148-
setYear(year + 1)
135+
setYear(browseDate.getFullYear() + 1)
149136
} else if (e.shiftKey && e.key === 'ArrowLeft') {
150-
setMonth(month - 1)
137+
setMonth(browseDate.getMonth() - 1)
151138
} else if (e.shiftKey && e.key === 'ArrowRight') {
152-
setMonth(month + 1)
139+
setMonth(browseDate.getMonth() + 1)
153140
} else {
154141
return false
155142
}
@@ -162,13 +149,13 @@
162149
shiftKeydown(e)
163150
return
164151
} else if (e.key === 'ArrowUp') {
165-
setYear(year - 1)
152+
setYear(browseDate.getFullYear() - 1)
166153
} else if (e.key === 'ArrowDown') {
167-
setYear(year + 1)
154+
setYear(browseDate.getFullYear() + 1)
168155
} else if (e.key === 'ArrowLeft') {
169-
setMonth(month - 1)
156+
setMonth(browseDate.getMonth() - 1)
170157
} else if (e.key === 'ArrowRight') {
171-
setMonth(month + 1)
158+
setMonth(browseDate.getMonth() + 1)
172159
} else {
173160
shiftKeydown(e)
174161
return
@@ -181,13 +168,13 @@
181168
shiftKeydown(e)
182169
return
183170
} else if (e.key === 'ArrowUp') {
184-
setMonth(month - 1)
171+
setMonth(browseDate.getFullYear() - 1)
185172
} else if (e.key === 'ArrowDown') {
186-
setMonth(month + 1)
173+
setMonth(browseDate.getFullYear() + 1)
187174
} else if (e.key === 'ArrowLeft') {
188-
setMonth(month - 1)
175+
setMonth(browseDate.getFullYear() - 1)
189176
} else if (e.key === 'ArrowRight') {
190-
setMonth(month + 1)
177+
setMonth(browseDate.getFullYear() + 1)
191178
} else {
192179
shiftKeydown(e)
193180
return
@@ -203,31 +190,19 @@
203190
shiftKeydown(e)
204191
return
205192
} else if (e.key === 'ArrowUp') {
206-
updateShownDate((value) => {
207-
value.setDate(value.getDate() - 7)
208-
highlightedDate = value
209-
return value
210-
})
193+
browseDate.setDate(browseDate.getDate() - 7)
194+
setValue(browseDate)
211195
} else if (e.key === 'ArrowDown') {
212-
updateShownDate((value) => {
213-
value.setDate(value.getDate() + 7)
214-
highlightedDate = value
215-
return value
216-
})
196+
browseDate.setDate(browseDate.getDate() + 7)
197+
setValue(browseDate)
217198
} else if (e.key === 'ArrowLeft') {
218-
updateShownDate((value) => {
219-
value.setDate(value.getDate() - 1)
220-
highlightedDate = value
221-
return value
222-
})
199+
browseDate.setDate(browseDate.getDate() - 1)
200+
setValue(browseDate)
223201
} else if (e.key === 'ArrowRight') {
224-
updateShownDate((value) => {
225-
value.setDate(value.getDate() + 1)
226-
highlightedDate = value
227-
return value
228-
})
202+
browseDate.setDate(browseDate.getDate() + 1)
203+
setValue(browseDate)
229204
} else if (e.key === 'Enter') {
230-
setValue(shownDate)
205+
setValue(browseDate)
231206
dispatch('select')
232207
} else {
233208
return
@@ -239,7 +214,7 @@
239214
<div class="date-time-picker" on:focusout tabindex="0" on:keydown={keydown}>
240215
<div class="tab-container" tabindex="-1">
241216
<div class="top">
242-
<div class="page-button" tabindex="-1" on:click={() => setMonth(month - 1)}>
217+
<div class="page-button" tabindex="-1" on:click={() => setMonth(browseDate.getMonth() - 1)}>
243218
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"
244219
><path
245220
d="M5 3l3.057-3 11.943 12-11.943 12-3.057-3 9-9z"
@@ -248,11 +223,17 @@
248223
>
249224
</div>
250225
<div class="dropdown month">
251-
<select bind:value={month} on:keydown={monthKeydown}>
226+
<select
227+
bind:value={browseMonth}
228+
on:input={() => {
229+
setMonth(browseMonth)
230+
}}
231+
on:keydown={monthKeydown}
232+
>
252233
{#each iLocale.months as monthName, i}
253234
<option
254-
disabled={new Date(year, i, getMonthLength(year, i), 23, 59, 59, 999) < min ||
255-
new Date(year, i) > max}
235+
disabled={new Date(browseYear, i, getMonthLength(browseYear, i), 23, 59, 59, 999) <
236+
min || new Date(browseYear, i) > max}
256237
value={i}>{monthName}</option
257238
>
258239
{/each}
@@ -266,30 +247,30 @@
266247
-->
267248
<select class="dummy-select" tabindex="-1">
268249
{#each iLocale.months as monthName, i}
269-
<option value={i} selected={i === month}>{monthName}</option>
250+
<option value={i} selected={i === browseMonth}>{monthName}</option>
270251
{/each}
271252
</select>
272253
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"
273254
><path d="M6 0l12 12-12 12z" transform="rotate(90, 12, 12)" /></svg
274255
>
275256
</div>
276257
<div class="dropdown year">
277-
<select bind:value={year} on:keydown={yearKeydown}>
258+
<select bind:value={browseYear} on:keydown={yearKeydown}>
278259
{#each years as v}
279260
<option value={v}>{v}</option>
280261
{/each}
281262
</select>
282263
<!-- style <select> button without affecting menu popup -->
283264
<select class="dummy-select" tabindex="-1">
284265
{#each years as v}
285-
<option value={v} selected={v === year}>{v}</option>
266+
<option value={v} selected={v === browseDate.getFullYear()}>{v}</option>
286267
{/each}
287268
</select>
288269
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"
289270
><path d="M6 0l12 12-12 12z" transform="rotate(90, 12, 12)" /></svg
290271
>
291272
</div>
292-
<div class="page-button" tabindex="-1" on:click={() => setMonth(month + 1)}>
273+
<div class="page-button" tabindex="-1" on:click={() => setMonth(browseDate.getMonth() + 1)}>
293274
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"
294275
><path d="M5 3l3.057-3 11.943 12-11.943 12-3.057-3 9-9z" /></svg
295276
>
@@ -311,8 +292,10 @@
311292
class="cell"
312293
on:click={() => selectDay(calendarDay)}
313294
class:disabled={!dayIsInRange(calendarDay, min, max)}
314-
class:selected={shouldHighlightCalendarDay(calendarDay)}
315-
class:other-month={calendarDay.month !== month}
295+
class:selected={calendarDay.year === value?.getFullYear() &&
296+
calendarDay.month === value?.getMonth() &&
297+
calendarDay.number === value.getDate()}
298+
class:other-month={calendarDay.month !== browseMonth}
316299
>
317300
<span>{calendarDay.number}</span>
318301
</div>

0 commit comments

Comments
 (0)