From c411e132266f1d380af8ae29737f1e63d09df2f5 Mon Sep 17 00:00:00 2001 From: "Rong Sen Ng (motss)" Date: Sun, 14 Nov 2021 23:17:19 +0800 Subject: [PATCH] refactor: implement .fire() to replace dispatchCustomEvent helper --- .../date-picker/app-date-picker.test.ts | 15 +++-- .../helpers/dispatch-custom-event.test.ts | 46 --------------- .../month-calendar/app-month-calendar.test.ts | 12 ++-- src/__tests__/year-grid/app-year-grid.test.ts | 6 +- .../app-date-picker-dialog-base.ts | 13 ++++ src/date-picker-dialog/constants.ts | 2 + .../date-picker-dialog-base.ts | 10 ++++ src/date-picker-dialog/stylings.ts | 19 ++++++ src/date-picker-dialog/typings.ts | 16 +++++ src/date-picker-input/date-picker-input.ts | 19 +++--- src/date-picker/date-picker.ts | 59 +++++++++++++++---- src/helpers/dispatch-custom-event.ts | 13 ---- src/mixins/date-picker-mixin.ts | 2 +- src/mixins/element-mixin.ts | 13 +++- src/mixins/typings.ts | 5 +- src/month-calendar/month-calendar.ts | 29 ++++++--- src/typings.ts | 52 ++++++++-------- src/year-grid/year-grid.ts | 10 ++-- 18 files changed, 205 insertions(+), 136 deletions(-) delete mode 100644 src/__tests__/helpers/dispatch-custom-event.test.ts create mode 100644 src/date-picker-dialog/app-date-picker-dialog-base.ts create mode 100644 src/date-picker-dialog/constants.ts create mode 100644 src/date-picker-dialog/date-picker-dialog-base.ts create mode 100644 src/date-picker-dialog/stylings.ts create mode 100644 src/date-picker-dialog/typings.ts delete mode 100644 src/helpers/dispatch-custom-event.ts diff --git a/src/__tests__/date-picker/app-date-picker.test.ts b/src/__tests__/date-picker/app-date-picker.test.ts index 66d8915e..a29bbe8b 100644 --- a/src/__tests__/date-picker/app-date-picker.test.ts +++ b/src/__tests__/date-picker/app-date-picker.test.ts @@ -6,11 +6,12 @@ import { elementUpdated, fixture, html, oneEvent } from '@open-wc/testing-helper import { MAX_DATE } from '../../constants'; import type { AppDatePicker } from '../../date-picker/app-date-picker'; import { appDatePickerName } from '../../date-picker/constants'; +import { toDateString } from '../../helpers/to-date-string'; import { toFormatters } from '../../helpers/to-formatters'; import { toResolvedDate } from '../../helpers/to-resolved-date'; import type { MaybeDate } from '../../helpers/typings'; import type { AppMonthCalendar } from '../../month-calendar/app-month-calendar'; -import type { DateUpdatedEvent, Formatters, StartView } from '../../typings'; +import type { CustomEventDetail, Formatters, StartView } from '../../typings'; import type { AppYearGrid } from '../../year-grid/app-year-grid'; import { messageFormatter } from '../test-utils/message-formatter'; @@ -297,9 +298,11 @@ describe(appDatePickerName, () => { newSelectedDate2 ); - const expectedDateUpdatedEvent: DateUpdatedEvent = { + const expectedDateUpdatedEvent: CustomEventDetail['date-updated']['detail'] = { isKeypress: false, - value: newSelectedDate2, + value: toDateString(newSelectedDate2), + valueAsDate: newSelectedDate2, + valueAsNumber: +newSelectedDate2, }; expect(dateUpdatedEvent.detail).deep.equal(expectedDateUpdatedEvent); @@ -351,9 +354,11 @@ describe(appDatePickerName, () => { ); expect(selectedDate?.fullDate).deep.equal(newSelectedDate); - const expectedDateUpdatedEvent: DateUpdatedEvent = { + const expectedDateUpdatedEvent: CustomEventDetail['date-updated']['detail'] = { isKeypress: false, - value: newSelectedDate, + value: toDateString(newSelectedDate), + valueAsDate: newSelectedDate, + valueAsNumber: +newSelectedDate, }; expect(dateUpdatedEvent.detail).deep.equal(expectedDateUpdatedEvent); diff --git a/src/__tests__/helpers/dispatch-custom-event.test.ts b/src/__tests__/helpers/dispatch-custom-event.test.ts deleted file mode 100644 index 0f4e68f5..00000000 --- a/src/__tests__/helpers/dispatch-custom-event.test.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { expect } from '@open-wc/testing'; - -import { dispatchCustomEvent } from '../../helpers/dispatch-custom-event'; -import type { SupportedCustomEventDetail } from '../../typings'; -import { messageFormatter } from '../test-utils/message-formatter'; - -type A = [SupportedCustomEventDetail['year-updated'] | undefined, SupportedCustomEventDetail['year-updated'] | null]; - -describe(dispatchCustomEvent.name, () => { - const cases: A[] = [ - [undefined, null], - [ - { - year: 2020, - }, - { - year: 2020, - }, - ], - ]; - - cases.forEach((a) => { - it( - messageFormatter('dispatches custom event (detail=%j)', a), - async () => { - const [testDetail, expected] = a; - const testEventName: keyof SupportedCustomEventDetail = 'year-updated'; - - const el = document.createElement('div'); - - const done = new Promise((resolve) => { - el.addEventListener(testEventName, (ev) => { - resolve((ev as CustomEvent).detail); - }, { once: true }); - }); - - dispatchCustomEvent(el, testEventName, testDetail); - - const result = await done; - - expect(result).deep.equal(expected); - } - ); - }); - -}); diff --git a/src/__tests__/month-calendar/app-month-calendar.test.ts b/src/__tests__/month-calendar/app-month-calendar.test.ts index c0598ed1..02b91d4e 100644 --- a/src/__tests__/month-calendar/app-month-calendar.test.ts +++ b/src/__tests__/month-calendar/app-month-calendar.test.ts @@ -9,11 +9,12 @@ import { getWeekdays } from 'nodemod/dist/calendar/helpers/get-weekdays.js'; import type { CalendarInit } from 'nodemod/dist/calendar/typings'; import type { confirmKeySet, navigationKeySetGrid } from '../../constants'; +import { toDateString } from '../../helpers/to-date-string'; import { toFormatters } from '../../helpers/to-formatters'; import type { AppMonthCalendar } from '../../month-calendar/app-month-calendar'; import { appMonthCalendarName } from '../../month-calendar/constants'; import type { MonthCalendarData } from '../../month-calendar/typings'; -import type { DateUpdatedEvent, InferredFromSet } from '../../typings'; +import type { CustomEventDetail, InferredFromSet } from '../../typings'; import { messageFormatter } from '../test-utils/message-formatter'; describe(appMonthCalendarName, () => { @@ -204,7 +205,7 @@ describe(appMonthCalendarName, () => { const dateUpdatedEventTask = new Promise((resolve) => { el.addEventListener('date-updated', function fn(ev) { - resolve((ev as CustomEvent).detail); + resolve((ev as CustomEvent).detail); el.removeEventListener('date-updated', fn); }); @@ -244,9 +245,12 @@ describe(appMonthCalendarName, () => { expect(newSelectedDate?.fullDate).deep.equal(calendarInit.date); const isKeypress = testEventType === 'keydown'; - const expectedDateUpdatedEvent: DateUpdatedEvent = { + const expectedDate = new Date('2020-02-09'); + const expectedDateUpdatedEvent: CustomEventDetail['date-updated']['detail'] = { isKeypress, - value: new Date('2020-02-09'), + value: toDateString(expectedDate), + valueAsDate: expectedDate, + valueAsNumber: +expectedDate, ...(isKeypress && { key: testKeyPayloads[0].down }), }; diff --git a/src/__tests__/year-grid/app-year-grid.test.ts b/src/__tests__/year-grid/app-year-grid.test.ts index 575ed38a..ce60c28e 100644 --- a/src/__tests__/year-grid/app-year-grid.test.ts +++ b/src/__tests__/year-grid/app-year-grid.test.ts @@ -5,7 +5,7 @@ import { sendKeys } from '@web/test-runner-commands'; import type { confirmKeySet } from '../../constants'; import { toFormatters } from '../../helpers/to-formatters'; -import type { InferredFromSet, YearUpdatedEvent } from '../../typings'; +import type { CustomEventDetail, InferredFromSet } from '../../typings'; import type { AppYearGrid } from '../../year-grid/app-year-grid'; import { appYearGridName } from '../../year-grid/constants'; import type { YearGridData } from '../../year-grid/typings'; @@ -98,7 +98,7 @@ describe(appYearGridName, () => { const yearUpdatedEventTask = new Promise((resolve) => { function fn(ev: Event) { - resolve((ev as CustomEvent).detail); + resolve((ev as CustomEvent).detail); el.removeEventListener('year-updated', fn); } @@ -135,7 +135,7 @@ describe(appYearGridName, () => { n.getAttribute('tabindex') ?? '', n.getAttribute('aria-selected') ?? '', ]); - const expectedYearUpdatedEvent: YearUpdatedEvent = { + const expectedYearUpdatedEvent: CustomEventDetail['year-updated']['detail'] = { year: data.max.getUTCFullYear(), }; diff --git a/src/date-picker-dialog/app-date-picker-dialog-base.ts b/src/date-picker-dialog/app-date-picker-dialog-base.ts new file mode 100644 index 00000000..c683e96d --- /dev/null +++ b/src/date-picker-dialog/app-date-picker-dialog-base.ts @@ -0,0 +1,13 @@ +import { customElement } from 'lit/decorators.js'; + +import { appDatePickerDialogBaseName } from './constants.js'; +import { DatePickerDialogBase } from './date-picker-dialog-base.js'; + +@customElement(appDatePickerDialogBaseName) +export class AppDatePickerDialogBase extends DatePickerDialogBase {} + +declare global { + interface HTMLElementTagNameMap { + [appDatePickerDialogBaseName]: AppDatePickerDialogBase; + } +} diff --git a/src/date-picker-dialog/constants.ts b/src/date-picker-dialog/constants.ts new file mode 100644 index 00000000..d0d50222 --- /dev/null +++ b/src/date-picker-dialog/constants.ts @@ -0,0 +1,2 @@ +export const appDatePickerDialogBaseName = 'app-date-picker-dialog-base' as const; +export const appDatePickerDialogName = 'app-date-picker-dialog' as const; diff --git a/src/date-picker-dialog/date-picker-dialog-base.ts b/src/date-picker-dialog/date-picker-dialog-base.ts new file mode 100644 index 00000000..504c0afe --- /dev/null +++ b/src/date-picker-dialog/date-picker-dialog-base.ts @@ -0,0 +1,10 @@ +import { Dialog } from '@material/mwc-dialog'; + +import { datePickerDialogBaseStyling } from './stylings.js'; + +export class DatePickerDialogBase extends Dialog { + public static override styles = [ + ...Dialog.styles, + datePickerDialogBaseStyling, + ]; +} diff --git a/src/date-picker-dialog/stylings.ts b/src/date-picker-dialog/stylings.ts new file mode 100644 index 00000000..e487e59f --- /dev/null +++ b/src/date-picker-dialog/stylings.ts @@ -0,0 +1,19 @@ +import { css, unsafeCSS } from 'lit'; + +import { appDatePickerDialogName } from './constants.js'; + +export const datePickerDialogBaseStyling = css` +:host { + display: block; +} + +.mdc-dialog__content { + padding: 0; +} +`; + +export const datePickerDialogStyling = css` +${unsafeCSS(appDatePickerDialogName)} { + display: block; +} +`; diff --git a/src/date-picker-dialog/typings.ts b/src/date-picker-dialog/typings.ts new file mode 100644 index 00000000..ae0bd119 --- /dev/null +++ b/src/date-picker-dialog/typings.ts @@ -0,0 +1,16 @@ +import type { Dialog } from '@material/mwc-dialog'; + +export interface DatePickerDialogProperties extends DialogProperties { + confirmLabel: string; + dismissLabel: string; + resetLabel: string; + show(): void; +} + +export interface DialogClosingEventDetail { + action: DialogClosingEventDetailAction; +} + +export type DialogClosingEventDetailAction = 'cancel' | 'reset' | 'set'; + +type DialogProperties = Pick; diff --git a/src/date-picker-input/date-picker-input.ts b/src/date-picker-input/date-picker-input.ts index 927fe15f..ec44ec1b 100644 --- a/src/date-picker-input/date-picker-input.ts +++ b/src/date-picker-input/date-picker-input.ts @@ -17,7 +17,7 @@ import { DatePickerMinMaxMixin } from '../mixins/date-picker-min-max-mixin.js'; import { DatePickerMixin } from '../mixins/date-picker-mixin.js'; import { ElementMixin } from '../mixins/element-mixin.js'; import type { DatePickerMixinProperties } from '../mixins/typings.js'; -import type { ChangedProperties, DatePickerProperties, SupportedCustomEventDetail } from '../typings.js'; +import type { ChangedProperties, CustomEventDetail, DatePickerProperties } from '../typings.js'; import { datePickerInputStyling } from './stylings.js'; export class DatePickerInput extends ElementMixin(DatePickerMixin(DatePickerMinMaxMixin(TextField))) implements DatePickerMixinProperties { @@ -193,21 +193,24 @@ export class DatePickerInput extends ElementMixin(DatePickerMixin(DatePickerMinM this._open = false; } - #onDatePickerFirstUpdated(ev: CustomEvent): void { - const [focusableElement] = ev.detail.focusableElements; - - this.#picker = ev.currentTarget as AppDatePicker; + #onDatePickerFirstUpdated({ + currentTarget, + detail: { + focusableElements: [focusableElement], + }, + }: CustomEvent): void { + this.#picker = currentTarget as AppDatePicker; this.#focusElement = focusableElement; } - async #onDatePickerDateUpdated(ev: CustomEvent): Promise { + async #onDatePickerDateUpdated(ev: CustomEvent): Promise { const { isKeypress, key, - value, + valueAsDate, } = ev.detail; - this.value = this.#valueFormatter.format(value); + this.value = this.#valueFormatter.format(valueAsDate); if (isKeypress && (key === 'Enter' || key === ' ')) { (await this.$inputSurface)?.close(); diff --git a/src/date-picker/date-picker.ts b/src/date-picker/date-picker.ts index 479f5849..8716e653 100644 --- a/src/date-picker/date-picker.ts +++ b/src/date-picker/date-picker.ts @@ -14,7 +14,6 @@ import { toUTCDate } from 'nodemod/dist/calendar/helpers/to-utc-date.js'; import { DateTimeFormat, MAX_DATE, startViews } from '../constants.js'; import { clampValue } from '../helpers/clamp-value.js'; import { dateValidator } from '../helpers/date-validator.js'; -import { dispatchCustomEvent } from '../helpers/dispatch-custom-event.js'; import { focusElement } from '../helpers/focus-element.js'; import { isInCurrentMonth } from '../helpers/is-in-current-month.js'; import { splitString } from '../helpers/split-string.js'; @@ -28,15 +27,20 @@ import { DatePickerMixin } from '../mixins/date-picker-mixin.js'; import type { AppMonthCalendar } from '../month-calendar/app-month-calendar.js'; import { RootElement } from '../root-element/root-element.js'; import { resetShadowRoot, webkitScrollbarStyling } from '../stylings.js'; -import type { DatePickerProperties, Formatters, StartView, ValueUpdatedEvent, YearUpdatedEvent } from '../typings.js'; +import type { CustomEventDetail, DatePickerProperties, Formatters, StartView, ValueUpdatedEvent } from '../typings.js'; import type { AppYearGrid } from '../year-grid/app-year-grid.js'; import type { YearGridData } from '../year-grid/typings.js'; import { datePickerStyling } from './stylings.js'; import type { DatePickerChangedProperties } from './typings.js'; export class DatePicker extends DatePickerMixin(DatePickerMinMaxMixin(RootElement)) implements DatePickerProperties { - public valueAsDate: Date; - public valueAsNumber: number; + public get valueAsDate(): Date { + return this.#valueAsDate; + } + + public get valueAsNumber(): number { + return +this.#valueAsDate; + } @queryAsync('app-month-calendar') private readonly _monthCalendar!: Promise; @@ -59,6 +63,7 @@ export class DatePicker extends DatePickerMixin(DatePickerMinMaxMixin(RootElemen #formatters: Formatters; #focusNavButtonWithKey = false; #today: Date; + #valueAsDate: Date; public static override styles = [ resetShadowRoot, @@ -77,8 +82,7 @@ export class DatePicker extends DatePickerMixin(DatePickerMinMaxMixin(RootElemen this._selectedDate = new Date(todayDate); this._currentDate = new Date(todayDate); this.#formatters = toFormatters(this.locale); - this.valueAsDate = new Date(todayDate); - this.valueAsNumber = +todayDate; + this.#valueAsDate = new Date(todayDate); } public override willUpdate(changedProperties: DatePickerChangedProperties): void { @@ -140,9 +144,26 @@ export class DatePicker extends DatePickerMixin(DatePickerMinMaxMixin(RootElemen this._max = newMax.date; this._currentDate = new Date(valueDate); this._selectedDate = new Date(valueDate); - this.valueAsDate = new Date(valueDate); - this.valueAsNumber = +valueDate; - this.value = toDateString(valueDate); + this.#valueAsDate = new Date(valueDate); + + /** + * Always override `value` when its value is nullish and dispatch `date-updated` event. + */ + if (this.value === null) { + const valueStr = toDateString(valueDate); + + this.value = valueStr; + + this.fire({ + detail: { + isKeypress: false, + value: valueStr, + valueAsDate: new Date(valueDate), + valueAsNumber: +valueDate, + }, + type: 'date-updated', + }); + } } if (changedProperties.has('startView')) { @@ -173,9 +194,16 @@ export class DatePicker extends DatePickerMixin(DatePickerMinMaxMixin(RootElemen } protected override async firstUpdated(): Promise { - dispatchCustomEvent(this, 'first-updated', { - focusableElements: await this.#queryAllFocusable(), - value: this.value, + const valueAsDate = this.#valueAsDate; + + this.fire({ + detail: { + focusableElements: await this.#queryAllFocusable(), + value: toDateString(valueAsDate), + valueAsDate: new Date(valueAsDate), + valueAsNumber: +valueAsDate, + }, + type: 'first-updated', }); } @@ -385,6 +413,11 @@ export class DatePicker extends DatePickerMixin(DatePickerMinMaxMixin(RootElemen this._selectedDate = newSelectedDate; this._currentDate = new Date(newSelectedDate); + + /** + * Always update `value` just like other native element such as `input`. + */ + this.value = toDateString(newSelectedDate); } #updateSelectedDate({ @@ -399,7 +432,7 @@ export class DatePicker extends DatePickerMixin(DatePickerMinMaxMixin(RootElemen #updateYear({ detail: { year }, - }: CustomEvent): void { + }: CustomEvent): void { this.#updateSelectedAndCurrentDate(this._selectedDate.setUTCFullYear(year)); this.startView = 'calendar'; } diff --git a/src/helpers/dispatch-custom-event.ts b/src/helpers/dispatch-custom-event.ts deleted file mode 100644 index 5e13c5d9..00000000 --- a/src/helpers/dispatch-custom-event.ts +++ /dev/null @@ -1,13 +0,0 @@ -import type { SupportedCustomEventDetail } from '../typings.js'; - -export function dispatchCustomEvent( - target: HTMLElement, - eventName: T, - detail?: SupportedCustomEventDetail[T] -): boolean { - return target.dispatchEvent(new CustomEvent(eventName, { - bubbles: true, - composed: true, - detail, - })); -} diff --git a/src/mixins/date-picker-mixin.ts b/src/mixins/date-picker-mixin.ts index 79021b68..e6090c5c 100644 --- a/src/mixins/date-picker-mixin.ts +++ b/src/mixins/date-picker-mixin.ts @@ -38,7 +38,7 @@ export const DatePickerMixin = ( * NOTE: `null` or `''` will always reset to the old valid date. In order to reset to * today's date, set `value` undefined. */ - @property() public value: string = toDateString(toResolvedDate()); + @property() public value = toDateString(toResolvedDate()); @property() public weekLabel = 'Wk'; diff --git a/src/mixins/element-mixin.ts b/src/mixins/element-mixin.ts index 75feae2b..8c6153d5 100644 --- a/src/mixins/element-mixin.ts +++ b/src/mixins/element-mixin.ts @@ -1,10 +1,21 @@ -import type { LitConstructor } from '../typings.js'; +import type { CustomEventAction, LitConstructor } from '../typings.js'; import type { ElementMixinProperties, MixinReturnType } from './typings.js'; export const ElementMixin = ( SuperClass: BaseConstructor ): MixinReturnType => { class ElementMixinClass extends SuperClass { + public fire>({ + detail, + type, + }: T): void { + this.dispatchEvent(new CustomEvent(type, { + bubbles: true, + composed: true, + detail, + })); + } + public query(selector: string): T | null { return this.root.querySelector(selector) as T; } diff --git a/src/mixins/typings.ts b/src/mixins/typings.ts index afd240b0..0d277833 100644 --- a/src/mixins/typings.ts +++ b/src/mixins/typings.ts @@ -1,6 +1,6 @@ import type { WeekNumberType } from 'nodemod/dist/calendar/typings.js'; -import type { Constructor, LitConstructor, StartView } from '../typings.js'; +import type { Constructor, CustomEventAction, LitConstructor, StartView } from '../typings.js'; export interface DatePickerMinMaxProperties { max?: string; @@ -19,13 +19,14 @@ export interface DatePickerMixinProperties { selectedDateLabel: string; showWeekNumber: boolean; startView: StartView; - value: string; + value?: string; weekLabel: string; weekNumberType: WeekNumberType; yearDropdownLabel: string; } export interface ElementMixinProperties { + fire>(action: T): void; query(selector: string): T | null; queryAll(selector: string): T[]; root: ShadowRoot; diff --git a/src/month-calendar/month-calendar.ts b/src/month-calendar/month-calendar.ts index 1694e9ee..6791202c 100644 --- a/src/month-calendar/month-calendar.ts +++ b/src/month-calendar/month-calendar.ts @@ -3,16 +3,16 @@ import { html, nothing } from 'lit'; import { property, queryAsync } from 'lit/decorators.js'; import { confirmKeySet, navigationKeySetGrid } from '../constants.js'; -import { dispatchCustomEvent } from '../helpers/dispatch-custom-event.js'; import { focusElement } from '../helpers/focus-element.js'; import { isInCurrentMonth } from '../helpers/is-in-current-month.js'; import { toClosestTarget } from '../helpers/to-closest-target.js'; +import { toDateString } from '../helpers/to-date-string.js'; import { toNextSelectedDate } from '../helpers/to-next-selected-date.js'; import { toResolvedDate } from '../helpers/to-resolved-date.js'; import { keyHome } from '../key-values.js'; import { RootElement } from '../root-element/root-element.js'; import { baseStyling, resetShadowRoot } from '../stylings.js'; -import type { Formatters, InferredFromSet, SupportedKey } from '../typings.js'; +import type { CustomEventDetail, Formatters, InferredFromSet, SupportedKey } from '../typings.js'; import { monthCalendarStyling } from './stylings.js'; import type { MonthCalendarData, MonthCalendarProperties, MonthCalendarRenderCalendarDayInit } from './typings.js'; @@ -285,17 +285,28 @@ export class MonthCalendar extends RootElement implements MonthCalendarPropertie this.#selectedDate = selectedCalendarDay.fullDate; } - const newSelectedDate = this.#selectedDate; + const selectedDate = this.#selectedDate; - if (newSelectedDate == null) return; + if (selectedDate == null) return; const isKeypress = Boolean(key); - - dispatchCustomEvent(this, 'date-updated', { - isKeypress, - value: new Date(newSelectedDate), - ...(isKeypress && { key }), + const newSelectedDate = new Date(selectedDate); + + this.fire({ + detail: { + isKeypress, + value: toDateString(newSelectedDate), + valueAsDate: newSelectedDate, + valueAsNumber: +newSelectedDate, + ...(isKeypress && { key }), + }, + type: 'date-updated', }); + + /** + * Reset `#selectedDate` after click or keyup event + */ + this.#selectedDate = undefined; }; } diff --git a/src/typings.ts b/src/typings.ts index 3869da0b..c989ad98 100644 --- a/src/typings.ts +++ b/src/typings.ts @@ -2,6 +2,7 @@ import type { LitElement } from 'lit'; import type { DateTimeFormatter } from 'nodemod/dist/calendar/typings.js'; import type { startViews } from './constants.js'; +import type { DatePicker } from './date-picker/date-picker.js'; import type { keyArrowDown, keyArrowLeft, keyArrowRight, keyArrowUp, keyEnd, keyEnter, keyHome, keyPageDown, keyPageUp, keySpace, keyTab } from './key-values.js'; import type { DatePickerMinMaxProperties, DatePickerMixinProperties, ElementMixinProperties } from './mixins/typings.js'; @@ -9,30 +10,24 @@ export type StartView = StartViewTuple[number]; export type StartViewTuple = typeof startViews; -export interface ChangedEvent extends KeyEvent { - value: DatePickerMixinProperties['value']; -} - export type ChangedProperties> = Map; // eslint-disable-next-line @typescript-eslint/no-explicit-any export type Constructor = new (...args: any[]) => T; +export interface CustomEventAction { + detail: CustomEventDetail; + type: T; +} + export interface DatePickerProperties extends DatePickerMinMaxProperties, DatePickerMixinProperties, ElementMixinProperties {} -export interface DateUpdatedEvent extends KeyEvent { - value: Date; -} - -export interface FirstUpdatedEvent { - focusableElements: HTMLElement[]; - value: DatePickerMixinProperties['value']; -} +type DatePickerValues = Required>; -export interface Formatters extends Pick { +export interface Formatters extends Pick { dayFormat: DateTimeFormatter; fullDateFormat: DateTimeFormatter; longWeekdayFormat: DateTimeFormatter; @@ -47,13 +42,23 @@ export type InferredFromSet = SetType extends Set ? T : never; export type LitConstructor = Constructor; -export interface SupportedCustomEventDetail { - // ['animation-finished']: null; - // ['changed']: ChangedEvent; - ['date-updated']: DateUpdatedEvent; - ['first-updated']: FirstUpdatedEvent; - // ['value-updated']: ValueUpdatedEvent; - ['year-updated']: YearUpdatedEvent; +export interface CustomEventDetail { + ['date-updated']: CustomEventAction<'date-updated', CustomEventDetailDateUpdated>; + ['first-updated']: CustomEventAction<'first-updated', CustomEventDetailFirstUpdated>; + ['year-updated']: CustomEventAction<'year-updated', CustomEventDetailYearUpdated>; +} + +interface CustomEventDetailDateUpdated extends KeyEvent, DatePickerValues {} + +interface CustomEventDetailFirstUpdated extends DatePickerValues { + focusableElements: HTMLElement[]; +} + +/** + * NOTE: No `KeyEvent` is needed as native `button` element will dispatch `click` event on keypress. + */ +interface CustomEventDetailYearUpdated { + year: number; } export type SupportedKey = @@ -73,13 +78,6 @@ export interface ValueUpdatedEvent extends KeyEvent { value: string; } -/** - * NOTE: No `KeyEvent` is needed as native `button` element will dispatch `click` event on keypress. - */ -export interface YearUpdatedEvent { - year: number; -} - interface KeyEvent { isKeypress: boolean; key?: SupportedKey; diff --git a/src/year-grid/year-grid.ts b/src/year-grid/year-grid.ts index a6d83822..cb446c03 100644 --- a/src/year-grid/year-grid.ts +++ b/src/year-grid/year-grid.ts @@ -4,14 +4,13 @@ import { property, queryAsync, state } from 'lit/decorators.js'; import { classMap } from 'lit/directives/class-map.js'; import { MAX_DATE, navigationKeySetGrid } from '../constants.js'; -import { dispatchCustomEvent } from '../helpers/dispatch-custom-event.js'; import { focusElement } from '../helpers/focus-element.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 { RootElement } from '../root-element/root-element.js'; import { baseStyling, resetButton, resetShadowRoot } from '../stylings.js'; -import type { Formatters, InferredFromSet } from '../typings.js'; +import type { CustomEventDetail, Formatters, InferredFromSet } from '../typings.js'; import { yearGridStyling } from './stylings.js'; import { toNextSelectedYear } from './to-next-selected-year.js'; import type { YearGridChangedProperties, YearGridData, YearGridProperties, YearGridRenderButtonInit } from './typings.js'; @@ -151,8 +150,11 @@ export class YearGrid extends RootElement implements YearGridProperties { this.$focusingYear = year; - dispatchCustomEvent(this, 'year-updated', { - year, + this.fire({ + detail: { + year, + }, + type: 'year-updated', }); } };