Add editable month and year picker to calendar

This commit is contained in:
Andrew Kingston 2023-11-02 17:20:36 +00:00
parent afffd4d234
commit 4fa4b2944f
3 changed files with 100 additions and 17 deletions

View File

@ -1,8 +1,4 @@
const ignoredClasses = [ const ignoredClasses = [".flatpickr-calendar", ".download-js-link"]
".flatpickr-calendar",
".spectrum-Popover",
".download-js-link",
]
let clickHandlers = [] let clickHandlers = []
/** /**
@ -25,7 +21,14 @@ const handleClick = event => {
return return
} }
// Ignore clicks for modals, unless the handler is registered from a modal // Ignore clicks for popovers, unless the handler is registered from one
const sourceInPopover = handler.anchor.closest(".spectrum-Popover") != null
const clickInPopover = event.target.closest(".spectrum-Popover") != null
if (clickInPopover && !sourceInPopover) {
return
}
// Ignore clicks for modals, unless the handler is registered from one
const sourceInModal = handler.anchor.closest(".spectrum-Underlay") != null const sourceInModal = handler.anchor.closest(".spectrum-Underlay") != null
const clickInModal = event.target.closest(".spectrum-Underlay") != null const clickInModal = event.target.closest(".spectrum-Underlay") != null
if (clickInModal && !sourceInModal) { if (clickInModal && !sourceInModal) {

View File

@ -6,6 +6,7 @@
import Popover from "../../Popover/Popover.svelte" import Popover from "../../Popover/Popover.svelte"
import dayjs from "dayjs" import dayjs from "dayjs"
import { createEventDispatcher } from "svelte" import { createEventDispatcher } from "svelte"
import Select from "../Select.svelte"
export let id = null export let id = null
export let disabled = false export let disabled = false
@ -30,22 +31,40 @@
"Saturday", "Saturday",
"Sunday", "Sunday",
] ]
const MonthsOfYear = [
"January",
"February",
"March",
"April",
"May",
"June",
"July",
"August",
"September",
"October",
"November",
"December",
]
let isOpen = false let isOpen = false
let anchor let anchor
let calendar let calendar
let today = dayjs() let today = dayjs()
let activeMonth let calendarDate
$: parsedValue = parseValue(value) $: parsedValue = parseValue(value)
$: displayValue = getDisplayValue(parsedValue) $: displayValue = getDisplayValue(parsedValue)
$: activeMonth = dayjs(parsedValue || today).startOf("month") $: calendarDate = dayjs(parsedValue || today).startOf("month")
$: mondays = getMondays(activeMonth) $: mondays = getMondays(calendarDate)
const clearDateOnBackspace = event => { const clearDateOnBackspace = event => {
// Ignore if we're typing a value
if (document.activeElement?.tagName.toLowerCase() === "input") {
return
}
if (["Backspace", "Clear", "Delete"].includes(event.key)) { if (["Backspace", "Clear", "Delete"].includes(event.key)) {
handleChange(null) handleChange(null)
calendar.hide() calendar?.hide()
} }
} }
@ -129,6 +148,17 @@
dispatch("change", newValue) dispatch("change", newValue)
} }
const handleYearChange = e => {
let year = parseInt(e.target.value)
if (isNaN(year)) {
year = calendarDate.year()
} else {
year = Math.max(0, Math.min(9999, year))
}
e.target.value = year
calendarDate = calendarDate.year(year)
}
</script> </script>
<div <div
@ -200,13 +230,29 @@
aria-live="assertive" aria-live="assertive"
aria-atomic="true" aria-atomic="true"
> >
{activeMonth.format("MMMM YYYY")} <div class="month-selector">
<Select
placeholder={null}
options={MonthsOfYear.map((m, idx) => ({ label: m, value: idx }))}
value={calendarDate.month()}
on:change={e => (calendarDate = calendarDate.month(e.detail))}
autoWidth
/>
</div>
<input
class="year-selector"
on:change={handleYearChange}
type="number"
value={calendarDate.year()}
min="0"
max="9999"
/>
</div> </div>
<button <button
aria-label="Previous" aria-label="Previous"
title="Previous" title="Previous"
class="spectrum-ActionButton spectrum-ActionButton--quiet spectrum-Calendar-prevMonth" class="spectrum-ActionButton spectrum-ActionButton--quiet spectrum-Calendar-prevMonth"
on:click={() => (activeMonth = activeMonth.subtract(1, "month"))} on:click={() => (calendarDate = calendarDate.subtract(1, "month"))}
> >
<svg <svg
class="spectrum-Icon spectrum-UIIcon-ChevronLeft100" class="spectrum-Icon spectrum-UIIcon-ChevronLeft100"
@ -220,7 +266,7 @@
aria-label="Next" aria-label="Next"
title="Next" title="Next"
class="spectrum-ActionButton spectrum-ActionButton--quiet spectrum-Calendar-nextMonth" class="spectrum-ActionButton spectrum-ActionButton--quiet spectrum-Calendar-nextMonth"
on:click={() => (activeMonth = activeMonth.add(1, "month"))} on:click={() => (calendarDate = calendarDate.add(1, "month"))}
> >
<svg <svg
class="spectrum-Icon spectrum-UIIcon-ChevronRight100" class="spectrum-Icon spectrum-UIIcon-ChevronRight100"
@ -254,7 +300,7 @@
<tr> <tr>
{#each [0, 1, 2, 3, 4, 5, 6] as dayOffset} {#each [0, 1, 2, 3, 4, 5, 6] as dayOffset}
{@const date = monday.add(dayOffset, "days")} {@const date = monday.add(dayOffset, "days")}
{@const outsideMonth = date.month() !== activeMonth.month()} {@const outsideMonth = date.month() !== calendarDate.month()}
<td <td
class="spectrum-Calendar-tableCell" class="spectrum-Calendar-tableCell"
aria-disabled="true" aria-disabled="true"
@ -296,9 +342,6 @@
.spectrum-Datepicker .spectrum-Textfield { .spectrum-Datepicker .spectrum-Textfield {
width: 100%; width: 100%;
} }
:global(.flatpickr-calendar) {
font-family: var(--font-sans);
}
.is-disabled { .is-disabled {
pointer-events: none !important; pointer-events: none !important;
} }
@ -310,4 +353,40 @@
.is-outsideMonth { .is-outsideMonth {
pointer-events: none; pointer-events: none;
} }
.spectrum-Calendar-title {
display: flex;
justify-content: center;
align-items: center;
gap: 4px;
}
.spectrum-Calendar-header button {
border-radius: 4px;
}
.month-selector :global(.spectrum-Picker),
.year-selector {
background: none;
border: none;
outline: none;
color: var(--spectrum-alias-text-color);
padding: 4px 6px;
border-radius: 4px;
transition: background 130ms ease-out;
font-size: var(--spectrum-calendar-title-text-size);
font-weight: bold;
font-family: var(--font-sans);
}
.month-selector :global(.spectrum-Picker:hover),
.year-selector:hover {
background: var(--spectrum-global-color-gray-200);
}
.month-selector :global(.spectrum-Picker-label) {
font-size: var(--spectrum-calendar-title-text-size);
font-weight: bold;
color: var(--spectrum-alias-text-color);
}
.year-selector {
-webkit-font-smoothing: antialiased;
margin-right: -20px;
}
</style> </style>

View File

@ -234,6 +234,7 @@
<br /> <br />
<br /> <br />
<br /> <br />
<DatePicker />
</div> </div>
<Layout noPadding gap="L"> <Layout noPadding gap="L">