Add month/year selector and use UTC date functions
This commit is contained in:
parent
fe3f515862
commit
6d5ad656b6
|
@ -30,13 +30,13 @@ html {
|
||||||
font: 100% system-ui;
|
font: 100% system-ui;
|
||||||
}
|
}
|
||||||
|
|
||||||
.CalendarView_heading {
|
.CalendarView_header {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
}
|
}
|
||||||
|
|
||||||
.CalendarView_heading_prevButton,
|
.CalendarView_header_prevButton,
|
||||||
.CalendarView_heading_nextButton {
|
.CalendarView_header_nextButton {
|
||||||
padding-left: 2em;
|
padding-left: 2em;
|
||||||
padding-right: 2em;
|
padding-right: 2em;
|
||||||
|
|
||||||
|
@ -44,12 +44,57 @@ html {
|
||||||
border: 0;
|
border: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.CalendarView_heading_prevButton:hover,
|
.CalendarView_header_prevButton:hover,
|
||||||
.CalendarView_heading_nextButton:hover {
|
.CalendarView_header_nextButton:hover {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.CalendarView_heading {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
.CalendarView_heading_text {
|
.CalendarView_heading_text {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.CalendarView_heading_monthInput {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
bottom: 0;
|
||||||
|
right: 0;
|
||||||
|
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fallback-input-month .CalendarView_heading_monthInput {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.CalendarView_heading_monthInput::-webkit-calendar-picker-indicator {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background-color: #f00;
|
||||||
|
}
|
||||||
|
|
||||||
|
.CalendarView_heading_yearSelectFallback {
|
||||||
|
display: none;
|
||||||
|
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
bottom: 0;
|
||||||
|
right: 0;
|
||||||
|
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fallback-input-month .CalendarView_heading_yearSelectFallback {
|
||||||
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
.CalendarView_calendar {
|
.CalendarView_calendar {
|
||||||
|
|
|
@ -16,7 +16,7 @@ function sameDay(date1, date2) {
|
||||||
//
|
//
|
||||||
// via https://stackoverflow.com/a/1184359/796832
|
// via https://stackoverflow.com/a/1184359/796832
|
||||||
function numDaysInMonthForDate(date) {
|
function numDaysInMonthForDate(date) {
|
||||||
return new Date(date.getYear(), date.getMonth() + 1, 0).getDate();
|
return new Date(date.getUTCFullYear(), date.getUTCMonth() + 1, 0).getUTCDate();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Map from day of week to the localized name of the day
|
// Map from day of week to the localized name of the day
|
||||||
|
@ -34,10 +34,10 @@ const DAYS_OF_WEEK = {
|
||||||
const today = new Date();
|
const today = new Date();
|
||||||
for (let i = 0; i < 7; i++) {
|
for (let i = 0; i < 7; i++) {
|
||||||
const lookupDate = new Date(today);
|
const lookupDate = new Date(today);
|
||||||
lookupDate.setDate(i + 1);
|
lookupDate.setUTCDate(i + 1);
|
||||||
|
|
||||||
const lookup = lookupDate.toLocaleString('en-US', { weekday: 'short' });
|
const lookup = lookupDate.toLocaleString('en-US', { weekday: 'short', timeZone: 'UTC' });
|
||||||
const localized = lookupDate.toLocaleString('default', { weekday: 'short' });
|
const localized = lookupDate.toLocaleString('default', { weekday: 'short', timeZone: 'UTC' });
|
||||||
|
|
||||||
DAYS_OF_WEEK[lookup] = localized;
|
DAYS_OF_WEEK[lookup] = localized;
|
||||||
}
|
}
|
||||||
|
@ -45,25 +45,84 @@ for (let i = 0; i < 7; i++) {
|
||||||
class CalendarView extends TemplateView {
|
class CalendarView extends TemplateView {
|
||||||
render(t, vm) {
|
render(t, vm) {
|
||||||
return t.div({ className: { CalendarView: true } }, [
|
return t.div({ className: { CalendarView: true } }, [
|
||||||
t.div({ className: { CalendarView_heading: true } }, [
|
t.div({ className: { CalendarView_header: true } }, [
|
||||||
t.button(
|
t.button(
|
||||||
{
|
{
|
||||||
className: { CalendarView_heading_prevButton: true },
|
className: { CalendarView_header_prevButton: true },
|
||||||
onClick: () => vm.prevMonth(),
|
onClick: () => vm.prevMonth(),
|
||||||
},
|
},
|
||||||
['\u276E']
|
['\u276E']
|
||||||
),
|
),
|
||||||
t.map(
|
t.map(
|
||||||
(vm) => vm.calendarDate,
|
(vm) => vm.calendarDate,
|
||||||
(date, t) => {
|
(calendarDate, t) => {
|
||||||
return t.h4({ className: { CalendarView_heading_text: true } }, [
|
return t.h4({ className: { CalendarView_heading: true } }, [
|
||||||
date.toLocaleString('default', { year: 'numeric', month: 'long' }),
|
t.span({ className: { CalendarView_heading_text: true } }, [
|
||||||
|
calendarDate.toLocaleString('default', {
|
||||||
|
year: 'numeric',
|
||||||
|
month: 'long',
|
||||||
|
timeZone: 'UTC',
|
||||||
|
}),
|
||||||
|
t.svg(
|
||||||
|
{
|
||||||
|
xmlns: 'http://www.w3.org/2000/svg',
|
||||||
|
width: '18',
|
||||||
|
height: '18',
|
||||||
|
viewBox: '0 0 18 18',
|
||||||
|
fill: 'none',
|
||||||
|
},
|
||||||
|
[
|
||||||
|
t.path({
|
||||||
|
d: 'M6 7.5L9 10.5L12 7.5',
|
||||||
|
stroke: 'currentColor',
|
||||||
|
'stroke-width': '1.5',
|
||||||
|
'stroke-linecap': 'round',
|
||||||
|
'stroke-linejoin': 'round',
|
||||||
|
}),
|
||||||
|
]
|
||||||
|
),
|
||||||
|
]),
|
||||||
|
|
||||||
|
t.input({
|
||||||
|
type: 'month',
|
||||||
|
className: { CalendarView_heading_monthInput: true },
|
||||||
|
value: `${calendarDate.getUTCFullYear()}-${calendarDate.getUTCMonth() + 1}`,
|
||||||
|
onChange: (e) => vm.onMonthInputChange(e),
|
||||||
|
}),
|
||||||
|
|
||||||
|
t.select(
|
||||||
|
{
|
||||||
|
className: {
|
||||||
|
CalendarView_heading_yearSelectFallback: true,
|
||||||
|
},
|
||||||
|
onChange: (e) => vm.onYearFallbackSelectChange(e),
|
||||||
|
},
|
||||||
|
[].concat(
|
||||||
|
(() => {
|
||||||
|
let yearSelectNodes = [];
|
||||||
|
const today = new Date();
|
||||||
|
for (let year = today.getUTCFullYear(); year > 1960; year--) {
|
||||||
|
yearSelectNodes.push(
|
||||||
|
t.option(
|
||||||
|
{
|
||||||
|
value: year,
|
||||||
|
selected: year === calendarDate.getUTCFullYear(),
|
||||||
|
},
|
||||||
|
[`${year}`]
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return yearSelectNodes;
|
||||||
|
})()
|
||||||
|
)
|
||||||
|
),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
t.button(
|
t.button(
|
||||||
{
|
{
|
||||||
className: { CalendarView_heading_nextButton: true },
|
className: { CalendarView_header_nextButton: true },
|
||||||
onClick: () => vm.nextMonth(),
|
onClick: () => vm.nextMonth(),
|
||||||
},
|
},
|
||||||
['\u276F']
|
['\u276F']
|
||||||
|
@ -84,14 +143,14 @@ class CalendarView extends TemplateView {
|
||||||
let dayNodes = [];
|
let dayNodes = [];
|
||||||
for (let i = 0; i < numDaysInMonthForDate(calendarDate); i++) {
|
for (let i = 0; i < numDaysInMonthForDate(calendarDate); i++) {
|
||||||
const dayNumberDate = new Date(calendarDate);
|
const dayNumberDate = new Date(calendarDate);
|
||||||
dayNumberDate.setDate(i);
|
dayNumberDate.setUTCDate(i);
|
||||||
const isDayInFuture = dayNumberDate.getTime() - todayTs > 0;
|
const isDayInFuture = dayNumberDate.getTime() - todayTs > 0;
|
||||||
|
|
||||||
// The current day displayed in the archive
|
// The current day displayed in the archive
|
||||||
const isActive = sameDay(dayNumberDate, vm.activeDate);
|
const isActive = sameDay(dayNumberDate, vm.activeDate);
|
||||||
|
|
||||||
// day number from 0 (monday) to 6 (sunday)
|
// day number from 0 (monday) to 6 (sunday)
|
||||||
const dayNumber = dayNumberDate.getDay();
|
const dayNumber = dayNumberDate.getUTCDay();
|
||||||
|
|
||||||
// +1 because we're going from 0-based day to 1-based `grid-column-start`
|
// +1 because we're going from 0-based day to 1-based `grid-column-start`
|
||||||
// +1 because we actually start the week on Sunday(6) instead of Monday(0)
|
// +1 because we actually start the week on Sunday(6) instead of Monday(0)
|
||||||
|
@ -113,7 +172,7 @@ class CalendarView extends TemplateView {
|
||||||
CalendarView_dayLink_disabled: isDayInFuture,
|
CalendarView_dayLink_disabled: isDayInFuture,
|
||||||
},
|
},
|
||||||
// Disable navigation to future days
|
// Disable navigation to future days
|
||||||
href: isDayInFuture ? null : vm.linkForDate(dayNumberDate),
|
href: isDayInFuture ? null : vm.archiveUrlForDate(dayNumberDate),
|
||||||
},
|
},
|
||||||
[String(i + 1)]
|
[String(i + 1)]
|
||||||
),
|
),
|
||||||
|
|
|
@ -12,14 +12,7 @@ class RightPanelContentView extends TemplateView {
|
||||||
todo: true,
|
todo: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
[
|
[t.view(new CalendarView(vm.calendarViewModel))]
|
||||||
t.div('test'),
|
|
||||||
t.input({
|
|
||||||
type: 'month',
|
|
||||||
value: vm.calendarViewModel.activeDate.toISOString().split('T')[0],
|
|
||||||
}),
|
|
||||||
t.view(new CalendarView(vm.calendarViewModel)),
|
|
||||||
]
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,6 +38,15 @@ assert(config);
|
||||||
assert(config.matrixServerUrl);
|
assert(config.matrixServerUrl);
|
||||||
assert(config.basePath);
|
assert(config.basePath);
|
||||||
|
|
||||||
|
function addSupportClasses() {
|
||||||
|
const input = document.createElement('input');
|
||||||
|
input.type = 'month';
|
||||||
|
const isMonthTypeSupported = input.type === 'month';
|
||||||
|
|
||||||
|
// Signal `<input type="month">` support to our CSS
|
||||||
|
document.body.classList.toggle('fallback-input-month', !isMonthTypeSupported);
|
||||||
|
}
|
||||||
|
|
||||||
let eventIndexCounter = 0;
|
let eventIndexCounter = 0;
|
||||||
const fragmentIdComparer = new FragmentIdComparer([]);
|
const fragmentIdComparer = new FragmentIdComparer([]);
|
||||||
function makeEventEntryFromEventJson(eventJson, memberEvent) {
|
function makeEventEntryFromEventJson(eventJson, memberEvent) {
|
||||||
|
@ -225,7 +234,7 @@ async function mountHydrogen() {
|
||||||
return this._calendarDate;
|
return this._calendarDate;
|
||||||
}
|
}
|
||||||
|
|
||||||
linkForDate(date) {
|
archiveUrlForDate(date) {
|
||||||
// Gives the date in YYYY-mm-dd format.
|
// Gives the date in YYYY-mm-dd format.
|
||||||
// date.toISOString() -> 2022-02-16T23:20:04.709Z
|
// date.toISOString() -> 2022-02-16T23:20:04.709Z
|
||||||
const urlDate = date.toISOString().split('T')[0].replaceAll('-', '/');
|
const urlDate = date.toISOString().split('T')[0].replaceAll('-', '/');
|
||||||
|
@ -235,16 +244,29 @@ async function mountHydrogen() {
|
||||||
|
|
||||||
prevMonth() {
|
prevMonth() {
|
||||||
const prevMonthDate = new Date(this._calendarDate);
|
const prevMonthDate = new Date(this._calendarDate);
|
||||||
prevMonthDate.setMonth(this._date.getMonth() - 1);
|
prevMonthDate.setUTCMonth(this._calendarDate.getUTCMonth() - 1);
|
||||||
this._calendarDate = prevMonthDate;
|
this._calendarDate = prevMonthDate;
|
||||||
this.emitChange('date');
|
this.emitChange('calendarDate');
|
||||||
}
|
}
|
||||||
|
|
||||||
nextMonth() {
|
nextMonth() {
|
||||||
const nextMonthDate = new Date(this._calendarDate);
|
const nextMonthDate = new Date(this._calendarDate);
|
||||||
nextMonthDate.setMonth(this._date.getMonth() + 1);
|
nextMonthDate.setUTCMonth(this._calendarDate.getUTCMonth() + 1);
|
||||||
|
console.log('nextMonthDate', nextMonthDate);
|
||||||
this._calendarDate = nextMonthDate;
|
this._calendarDate = nextMonthDate;
|
||||||
this.emitChange('date');
|
this.emitChange('calendarDate');
|
||||||
|
}
|
||||||
|
|
||||||
|
onMonthInputChange(e) {
|
||||||
|
const selectedDate = new Date(e.target.valueAsDate);
|
||||||
|
console.log('onMonthInputChange selectedDate', selectedDate, e.target.valueAsDate);
|
||||||
|
this._calendarDate = selectedDate;
|
||||||
|
this.emitChange('calendarDate');
|
||||||
|
}
|
||||||
|
|
||||||
|
onYearFallbackSelectChange(e) {
|
||||||
|
// TODO
|
||||||
|
console.log('onYearFallbackSelectChange', e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -269,6 +291,8 @@ async function mountHydrogen() {
|
||||||
|
|
||||||
//console.log('view.mount()', view.mount());
|
//console.log('view.mount()', view.mount());
|
||||||
app.replaceChildren(view.mount());
|
app.replaceChildren(view.mount());
|
||||||
|
|
||||||
|
addSupportClasses();
|
||||||
}
|
}
|
||||||
|
|
||||||
// N.B.: When we run this in a `vm`, it will return the last statement. It's
|
// N.B.: When we run this in a `vm`, it will return the last statement. It's
|
||||||
|
|
Loading…
Reference in New Issue