diff --git a/index.html b/index.html
index 406f655c..1dba8fe7 100644
--- a/index.html
+++ b/index.html
@@ -4,6 +4,12 @@
${
- this._startView === 'yearGrid' ?
+
${
+ isStartViewYearGrid ?
html`
` :
- html`${
+ multiCldr ? html`${
repeat(multiCldr.calendars, ({ key }) => key, (calendar, idx) => {
const isVisibleCalendar = idx === 1;
const data: MonthCalendarData = {
@@ -274,12 +290,14 @@ export class DatePicker extends DatePickerMixin(DatePickerMinMaxMixin(LitElement
return html`
`;
})
- }`
+ }` : nothing
}
`;
}
@@ -299,16 +317,31 @@ export class DatePicker extends DatePickerMixin(DatePickerMinMaxMixin(LitElement
this._currentDate = newCurrentDate;
};
+ #updateSelectedDate = ({
+ detail: { value },
+ }: CustomEvent
): void => {
+ this.#updateSelectedAndCurrentDate(value);
+
+ // TODO: To fire value update event
+ };
+
#updateStartView = (): void => {
- const isYearGrid = this._startView === 'yearGrid';
+ const isYearGrid = this.startView === 'yearGrid';
+
+ this.startView = isYearGrid ? 'calendar' : 'yearGrid';
+ };
- this._startView = isYearGrid ? 'calendar' : 'yearGrid';
+ #updateYear = ({
+ detail: { year },
+ }: CustomEvent): void => {
+ this.#updateSelectedAndCurrentDate(this._selectedDate.setUTCFullYear(year));
+ this.startView = 'calendar';
};
- #updateYear = (ev: CustomEvent): void => {
- const { year } = ev.detail;
+ #updateSelectedAndCurrentDate = (maybeDate: Date | number | string): void => {
+ const newSelectedDate = new Date(maybeDate);
- this._selectedDate = new Date(this._selectedDate.setUTCFullYear(year));
- this._startView = 'calendar';
+ this._selectedDate = newSelectedDate;
+ this._currentDate = new Date(newSelectedDate);
};
}
diff --git a/src/helpers/to-closest-target.ts b/src/helpers/to-closest-target.ts
new file mode 100644
index 00000000..d5bbda1e
--- /dev/null
+++ b/src/helpers/to-closest-target.ts
@@ -0,0 +1,10 @@
+export function toClosestTarget(
+ event: TargetEvent,
+ selector: string
+): Target | undefined {
+ const matchedTarget = (
+ Array.from(event.composedPath()) as Target[]
+ ).find((element => element.nodeType === Node.ELEMENT_NODE && element.matches(selector)));
+
+ return matchedTarget;
+}
diff --git a/src/month-calendar/month-calendar.ts b/src/month-calendar/month-calendar.ts
index c822b67b..38ffbc41 100644
--- a/src/month-calendar/month-calendar.ts
+++ b/src/month-calendar/month-calendar.ts
@@ -7,7 +7,9 @@ import { classMap } from 'lit/directives/class-map.js';
import { resetShadowRoot } from '../ stylings.js';
import { keyCodesRecord } from '../constants.js';
import { computeNextFocusedDate } from '../helpers/compute-next-focused-date.js';
+import { dispatchCustomEvent } from '../helpers/dispatch-custom-event.js';
import { isInTargetMonth } from '../helpers/is-in-current-month.js';
+import { toClosestTarget } from '../helpers/to-closest-target.js';
import { toResolvedDate } from '../helpers/to-resolved-date.js';
import { monthCalendarStyling } from './stylings.js';
import type { MonthCalendarData, MonthCalendarProperties } from './typings.js';
@@ -88,9 +90,16 @@ export class MonthCalendar extends LitElement implements MonthCalendarProperties
focusedDate;
calendarContent = html`
-
-
- ${
+
+
+ ${
showCaption && secondMonthSecondCalendarDayFullDate ?
longMonthYearFormat(secondMonthSecondCalendarDayFullDate) :
''
@@ -98,16 +107,16 @@ export class MonthCalendar extends LitElement implements MonthCalendarProperties
- ${
+
${
weekdays.map(
weekday => html`
- ${weekday.value}
+ ${weekday.value}
| `
)
}
@@ -116,46 +125,49 @@ export class MonthCalendar extends LitElement implements MonthCalendarProperties
${
calendar.map((calendarRow) => {
return html`
- ${
+
${
calendarRow.map((calendarCol, i) => {
const { disabled, fullDate, label, value } = calendarCol;
/** Week label, if any */
if (!fullDate && value && showWeekNumber && i < 1) {
return html`${value} | `;
}
/** Empty day */
if (!value || !fullDate) {
- return html` | `;
+ return html` | `;
}
const curTime = +new Date(fullDate);
const isCurrentDate = +focusedDate === curTime;
const shouldTab = showCaption && $newFocusedDate.getUTCDate() === Number(value);
+ /** NOTE: lit-plugin does not like this */
+ const calendarDayClasses = classMap({
+ 'calendar-day': true,
+ 'day--disabled': disabled,
+ 'day--today': +todayDate === curTime,
+ 'day--focused': !disabled && isCurrentDate,
+ }) as unknown as string;
+
return html`
- ${value}
+ ${value}
|
`;
})
@@ -168,4 +180,52 @@ export class MonthCalendar extends LitElement implements MonthCalendarProperties
return html`${calendarContent}
`;
}
+
+ #updateSelectedDate = (ev: MouseEvent | KeyboardEvent): void => {
+ /** Do nothing when keyup.key is neither Enter nor ' ' (or Spacebar on older browsers) */
+ if (
+ (ev as KeyboardEvent).type === 'keyup' &&
+ !['Enter', ' ', 'Spacebar'].includes((ev as KeyboardEvent).key)
+ ) return;
+
+ const selectedCalendarDay = toClosestTarget(ev, '.calendar-day');
+
+ /** NOTE: Required condition check else these will trigger unwanted re-rendering */
+ if (
+ selectedCalendarDay == null ||
+ [
+ 'day--empty',
+ 'day--disabled',
+ 'day--focused',
+ 'weekday-label',
+ ].some(className => selectedCalendarDay.classList.contains(className))
+ ) return;
+
+ const { fullDate } = selectedCalendarDay;
+
+ dispatchCustomEvent(this, 'date-updated', {
+ isKeypress: false,
+ value: new Date(fullDate),
+ });
+
+ return;
+ };
+}
+
+declare global {
+ // #region HTML element type extensions
+ // interface HTMLButtonElement {
+ // year: number;
+ // }
+
+ // interface HTMLElement {
+ // part: HTMLElementPart;
+ // }
+
+ interface HTMLTableCellElement {
+ day: string;
+ fullDate: Date;
+ }
+ // #endregion HTML element type extensions
+
}
diff --git a/src/month-calendar/stylings.ts b/src/month-calendar/stylings.ts
index 0739b170..a05ac144 100644
--- a/src/month-calendar/stylings.ts
+++ b/src/month-calendar/stylings.ts
@@ -1,4 +1,89 @@
import { css } from 'lit';
export const monthCalendarStyling = css`
+.calendar-table,
+.calendar-day {
+ text-align: center;
+}
+
+.calendar-table {
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ user-select: none;
+
+ border-collapse: collapse;
+ border-spacing: 0;
+}
+
+.calendar-day,
+.calendar-day-value,
+.weekday-value {
+ position: relative;
+ font-size: 14px;
+}
+
+th,
+td {
+ padding: 0;
+}
+
+.weekday-value {
+ padding: 0 0 9px;
+ color: #8c8c8c;
+}
+
+.calendar-day::after,
+.calendar-day-value {
+ top: calc((32px - 28px) / 2);
+ right: 0;
+ bottom: 0;
+ left: calc((32px - 28px) / 2);
+ width: 28px;
+ height: 28px;
+}
+
+.calendar-day {
+ width: 32px;
+ height: 0;
+ padding-top: 32px;
+ outline: none;
+}
+.calendar-day::after {
+ content: '';
+ display: block;
+ content: '';
+ position: absolute;
+ border-radius: 50%;
+ opacity: 0;
+ pointer-events: none;
+ will-change: opacity;
+}
+.calendar-day[aria-selected="true"] {
+ color: #fff;
+}
+.calendar-day[aria-selected="true"]::after {
+ background-color: #1d1d1d;
+ opacity: 1;
+}
+.calendar-day:hover::after,
+.calendar-day:focus::after {
+ outline: 1px solid #1d1d1d;
+ opacity: 1;
+}
+.calendar-day[aria-disabled="true"],
+.calendar-day.day--empty {
+ background-color: none;
+ outline: none;
+ opacity: 0;
+}
+
+.calendar-day-value {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+
+ position: absolute;
+ color: currentColor;
+ z-index: 1;
+}
`;
diff --git a/src/stylings.ts b/src/stylings.ts
index f384c404..ae1f0a9d 100644
--- a/src/stylings.ts
+++ b/src/stylings.ts
@@ -1,12 +1,41 @@
import { css } from 'lit';
export const datePickerStyling = css`
+:host {
+ --date-picker-header-base-height: 52px;
+ --date-picker-year-grid-base-height: calc(4px + (32px * 7));
+ --date-picker-base-width: calc((16px * 2) + (32px * 7));
+ --date-picker-base-height: calc(32px + 36px + 12px + (32px * 6) + 8px);
+
+ --date-picker-with-week-number-width: calc(var(--date-picker-base-width) + 32px);
+ --date-picker-in-year-grid-height: calc(var(--date-picker-base-height) + var(--date-picker-year-grid-base-height));
+
+ --date-picker-width: var(--date-picker-base-width);
+ --date-picker-height: var(--date-picker-base-height);
+
+ min-width: var(--date-picker-width);
+ max-width: var(--date-picker-width);
+ min-height: var(--date-picker-height);
+ max-height: var(--date-picker-height);
+ width: 100%;
+ height: 100%;
+}
+:host([startview="calendar"][show-week-number]) {
+ --date-picker-width: var(--date-picker-with-week-number-width);
+}
+:host(startview="yearGrid") {
+ --date-picker-height: var(----date-picker-in-year-grid-height);
+}
+
.header {
display: grid;
grid-auto-flow: column;
justify-content: space-between;
- margin: 0 8px 0 24px;
+ max-height: var(--date-picker-base-height);
+ height: 100%;
+ margin: 4px 0 0;
+ padding: 0 0 0 24px;
}
/** #region header */
@@ -22,10 +51,18 @@ export const datePickerStyling = css`
.month-pagination {
display: flex;
+ margin: 0 -4px 0 0;
}
+/** #endregion header */
-[data-navigation="next"] {
- margin: 0 0 0 24px;
+.year-grid {
+ max-height: var(--date-picker-year-grid-base-height);
+ height: 100%;
+ padding: 4px 20px 8px 12px;
+ overflow: auto;
+}
+
+.calendar {
+ padding: 0 16px 8px;
}
-/** #endregion header */
`;
diff --git a/src/typings.ts b/src/typings.ts
index 74b8b44f..700ceb33 100644
--- a/src/typings.ts
+++ b/src/typings.ts
@@ -25,9 +25,13 @@ export type DatePickerChangedProperties = ChangedProperties;
+export interface DateUpdatedEvent {
+ isKeypress: boolean;
+ value: Date;
+}
+
export interface FirstUpdatedEvent {
focusableElements: HTMLElement[];
value: DatePickerMixinProperties['value'];
@@ -49,12 +53,18 @@ export interface Formatters extends Pick {
export interface SupportedCustomEvent {
['animation-finished']: null;
['changed']: ChangedEvent;
+ ['date-updated']: DateUpdatedEvent;
['first-updated']: FirstUpdatedEvent;
+ ['value-updated']: ValueUpdatedEvent;
['year-updated']: YearUpdatedEvent;
}
export type SupportedKeyCode = typeof keyCodesRecord[keyof typeof keyCodesRecord];
+export interface ValueUpdatedEvent extends Pick {
+ value: string;
+}
+
export interface YearUpdatedEvent {
year: number;
}
diff --git a/src/year-grid-button/stylings.ts b/src/year-grid-button/stylings.ts
index 07197376..832cd02a 100644
--- a/src/year-grid-button/stylings.ts
+++ b/src/year-grid-button/stylings.ts
@@ -9,6 +9,7 @@ export const yearGridButtonStyling = css`
width: 56px;
height: 32px;
+ pointer-events: none;
}
.mdc-button {
@@ -17,6 +18,9 @@ export const yearGridButtonStyling = css`
width: 52px;
height: 28px;
padding: 0;
+ font: inherit;
+ font-size: 14px;
border-radius: 52px;
+ pointer-events: auto;
}
`;
diff --git a/src/year-grid/year-grid.ts b/src/year-grid/year-grid.ts
index 831a3847..36ed27be 100644
--- a/src/year-grid/year-grid.ts
+++ b/src/year-grid/year-grid.ts
@@ -9,6 +9,7 @@ import { toUTCDate } from 'nodemod/dist/calendar/helpers/to-utc-date.js';
import { resetShadowRoot } from '../ stylings.js';
import { MAX_DATE } from '../constants.js';
import { dispatchCustomEvent } from '../helpers/dispatch-custom-event.js';
+import { toClosestTarget } from '../helpers/to-closest-target.js';
import { toResolvedDate } from '../helpers/to-resolved-date.js';
import { toYearList } from '../helpers/to-year-list.js';
import { APP_YEAR_GRID_BUTTON_NAME } from '../year-grid-button/constants.js';
@@ -73,17 +74,20 @@ export class YearGrid extends LitElement implements YearGridProperties {
`;
}
- #updateYear = (ev: MouseEvent): void => {
- const selectedYearGridButton = Array.from(
- ev.composedPath() as HTMLElement[]
- ).find(
- element =>
- element.localName === APP_YEAR_GRID_BUTTON_NAME &&
- element.hasAttribute('data-year')
- );
+ #updateYear = (ev: MouseEvent | KeyboardEvent): void => {
+ /** Do nothing when keyup.key is neither Enter nor ' ' (or Spacebar on older browsers) */
+ if (
+ (ev as KeyboardEvent).type === 'keyup' &&
+ !['Enter', ' ', 'Spacebar'].includes((ev as KeyboardEvent).key)
+ ) return;
+
+ const selectedYearGridButton = toClosestTarget(ev, `${APP_YEAR_GRID_BUTTON_NAME}[data-year]`);
+
+ /** Do nothing when not tapping on the year button */
+ if (selectedYearGridButton == null) return;
+
const year = Number(
- selectedYearGridButton?.getAttribute('data-year') ??
- this.data.date.getUTCFullYear()
+ selectedYearGridButton.getAttribute('data-year')
);
dispatchCustomEvent(this, 'year-updated', { year });