You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
140 lines
3.4 KiB
140 lines
3.4 KiB
<script>
|
|
import Icon from '$/client/components/Icon.svelte';
|
|
import eachDayOfInterval from 'date-fns/eachDayOfInterval';
|
|
import add from 'date-fns/add';
|
|
import sub from 'date-fns/sub';
|
|
import isSunday from 'date-fns/isSunday';
|
|
import isSameDay from 'date-fns/isSameDay';
|
|
import {createEventDispatcher} from "svelte";
|
|
|
|
const dispatch = createEventDispatcher();
|
|
let today = new Date();
|
|
let year = today.getFullYear();
|
|
let month = today.getMonth();
|
|
let fom = new Date(year, month, 1);
|
|
let display_date = fom.toLocaleDateString("en-US", { year: "numeric", month: "long" });
|
|
|
|
/* Super American oriented, but future versions could use more of the Temporal API when it's more
|
|
prevalent. */
|
|
|
|
const first_sunday = () => {
|
|
if(isSunday(fom)) {
|
|
return fom; // awesome, it's a sunday
|
|
} else {
|
|
// looks like we have to find the previous sunday
|
|
let prev = sub(fom, {days: 1});
|
|
|
|
for(let i = 0; i < 7; i++) {
|
|
if(isSunday(prev)) {
|
|
return prev;
|
|
} else {
|
|
prev = sub(prev, {days: 1});
|
|
return prev;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
let start = first_sunday(month);
|
|
let end = add(start, { days: 35});
|
|
|
|
const make_dates = () => {
|
|
end = add(start, { days: (6 * 7) - 1});
|
|
return eachDayOfInterval({start, end});
|
|
}
|
|
|
|
let dates = make_dates(month);
|
|
|
|
const update_month = () => {
|
|
year = fom.getFullYear();
|
|
month = fom.getMonth();
|
|
start = first_sunday();
|
|
end = add(start, { days: (6 * 7) - 1});
|
|
display_date = fom.toLocaleDateString("en-US", { year: "numeric", month: "long" });
|
|
dates = make_dates();
|
|
}
|
|
|
|
const next_month = () => {
|
|
fom = add(fom, {months: 1});
|
|
update_month();
|
|
dispatch("next", {fom});
|
|
}
|
|
|
|
const prev_month = () => {
|
|
fom = sub(fom, {months: 1});
|
|
update_month();
|
|
dispatch("previous", {fom});
|
|
}
|
|
|
|
</script>
|
|
<style>
|
|
content {
|
|
display: flex;
|
|
flex-grow: 1;
|
|
}
|
|
|
|
calendar {
|
|
display: grid;
|
|
grid-template-columns: repeat(7, 1fr);
|
|
grid-template-rows: auto;
|
|
outline: 1px solid var(--color-accent);
|
|
-webkit-user-select: none;
|
|
-moz-user-select: none;
|
|
-ms-user-select: none;
|
|
user-select: none;
|
|
}
|
|
|
|
calendar day {
|
|
background-color: var(--color-bg-secondary);
|
|
font-weight: bold;
|
|
padding-left: 0.3rem;
|
|
padding-right: 0.3rem;
|
|
}
|
|
|
|
calendar month {
|
|
grid-column: span 5;
|
|
font-size: 1.5em;
|
|
font-weight: bold;
|
|
text-align: center;
|
|
justify-content: space-evenly;
|
|
}
|
|
|
|
calendar span {
|
|
text-align: center;
|
|
display: inline-block;
|
|
}
|
|
|
|
calendar date {
|
|
padding: 0.3rem;
|
|
padding-left: 0.5rem;
|
|
}
|
|
|
|
calendar date:hover {
|
|
background-color: var(--color-bg-secondary);
|
|
}
|
|
|
|
calendar date.not-month {
|
|
color: var(--color-inactive);
|
|
}
|
|
|
|
calendar date.today {
|
|
color: var(--color-bg);
|
|
background-color: var(--color);
|
|
}
|
|
</style>
|
|
|
|
<content>
|
|
<calendar>
|
|
<span on:click={ () => prev_month(-1) }><Icon name="arrow-left-circle" size="1.5em" /></span>
|
|
<month>
|
|
{ display_date }
|
|
</month>
|
|
<span on:click={ () => next_month(1) }><Icon name="arrow-right-circle" size="1.5em" /></span>
|
|
|
|
<day>Sun</day><day>Mon</day><day>Tue</day><day>Wed</day><day>Thu</day><day>Fri</day><day>Sat</day>
|
|
{#each dates as date}
|
|
<date class:today={ isSameDay(date, today) } class:not-month={ date.getMonth() !== fom.getMonth()} on:click={ () => dispatch("select", date) }>{date.getDate()}</date>
|
|
{/each}
|
|
</calendar>
|
|
</content>
|
|
|
|
|