Add editable month and year picker to calendar
This commit is contained in:
parent
afffd4d234
commit
4fa4b2944f
|
@ -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) {
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -234,6 +234,7 @@
|
||||||
<br />
|
<br />
|
||||||
<br />
|
<br />
|
||||||
<br />
|
<br />
|
||||||
|
<DatePicker />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Layout noPadding gap="L">
|
<Layout noPadding gap="L">
|
||||||
|
|
Loading…
Reference in New Issue