UNPKG

@ebay/ebayui-core

Version:

Collection of core eBay components; considered to be the building blocks for all composite structures, pages & apps.

299 lines (298 loc) 12.7 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const date_utils_1 = require("../../common/dates/date-utils"); const dates_1 = require("../../common/dates"); const DAY_UPDATE_KEYMAP = { ArrowRight: 1, ArrowLeft: -1, ArrowDown: 7, ArrowUp: -7, }; class Calendar { onCreate(input) { this.locale = input.locale; const { firstDayOfWeek, weekdayLabels } = (0, date_utils_1.getWeekdayInfo)(input.locale); const todayISO = (0, date_utils_1.toLocalISO)(new Date()); this.state = { focusISO: null, baseISO: todayISO, tabindexISO: todayISO, todayISO, offset: 0, firstDayOfWeek, weekdayLabels, rangeStart: null, rangeEnd: null, disableBefore: null, disableAfter: null, disableWeekdays: [], disableList: [], }; } onInput(input) { var _a, _b, _c, _d, _e; if (input.locale !== this.locale) { this.locale = input.locale; const { firstDayOfWeek, weekdayLabels } = (0, date_utils_1.getWeekdayInfo)(input.locale); this.state.firstDayOfWeek = firstDayOfWeek; this.state.weekdayLabels = weekdayLabels; } if (input.todayISO) { const newTodayISO = (0, date_utils_1.toISO)(new Date(input.todayISO)); this.state.todayISO = newTodayISO; this.state.baseISO = newTodayISO; this.state.tabindexISO = newTodayISO; } if (input.selected) { // If no selected times are visible, snap the view to the first one const selectedISOs = Array.isArray(input.selected) ? input.selected : [input.selected]; const currFirstISO = this.getFirstVisibleISO(); const currLastISO = this.getLastVisibleISO(input); const selectedTimeInView = selectedISOs.find((time) => time >= currFirstISO && time <= currLastISO); if (selectedTimeInView === undefined) { this.state.baseISO = this.state.tabindexISO = selectedISOs[0]; this.state.offset = 0; } } this.calculateRangeDisplay(input); // handle changes regarding disabled dates this.state.disableBefore = (0, date_utils_1.dateArgToISO)(input.disableBefore); this.state.disableAfter = (0, date_utils_1.dateArgToISO)(input.disableAfter); this.state.disableWeekdays = (_a = input.disableWeekdays) !== null && _a !== void 0 ? _a : []; this.state.disableList = (_c = (_b = input.disableList) === null || _b === void 0 ? void 0 : _b.map(date_utils_1.dateArgToISO)) !== null && _c !== void 0 ? _c : []; if (this.isDisabled(this.state.tabindexISO)) { // The current tabindex is disabled, so we have to find a new one const firstActive = this.getFirstActiveISO(input); if (firstActive) { // This month has active days, so we can use the first one this.state.tabindexISO = firstActive; } else if (this.state.disableBefore && this.state.tabindexISO < this.state.disableBefore) { // This month has no active days and the start of _possible_ dates is in the future, so we change months to the start of possible dates this.state.baseISO = this.state.disableBefore; this.state.offset = 0; this.state.tabindexISO = (_d = this.getFirstActiveISO(input)) !== null && _d !== void 0 ? _d : this.state.disableBefore; } else if (this.state.disableAfter && this.state.tabindexISO > this.state.disableAfter) { // This month has no active days and the end of _possible_ dates is in the past, so we change months to the end of possible dates this.state.baseISO = this.state.disableAfter; this.state.offset = 0; this.state.tabindexISO = (_e = this.getFirstActiveISO(input)) !== null && _e !== void 0 ? _e : this.state.disableAfter; } else { // This may be reached in very specific edge cases, such as when the user has disabled all days in the current month manually // In this case, we leave the tabindex and position as is. This is a fall-through case. } } while (this.state.disableAfter && this.getMonthDate(this.state.offset + (input.numMonths || 1) - 1).toISOString() > this.state.disableAfter) { this.state.offset--; } } isDisabled(iso) { return ((this.state.disableBefore && iso < this.state.disableBefore) || (this.state.disableAfter && iso > this.state.disableAfter) || this.state.disableWeekdays.includes((0, date_utils_1.fromISO)(iso).getUTCDay()) || this.state.disableList.includes(iso)); } onDaySelect(day) { this.emit("select", { iso: day }); } onDayFocus(day) { this.state.focusISO = this.state.tabindexISO = day; this.calculateRangeDisplay(); } onDayBlur() { this.state.focusISO = null; this.calculateRangeDisplay(); } onKeyDown(event) { const dayChange = DAY_UPDATE_KEYMAP[event.key]; if (dayChange) { event.preventDefault(); // find new tabindex iso, skipping up to 7 disabled cells let tries = 7; let iso = this.state.tabindexISO; do { iso = (0, date_utils_1.offsetISO)(iso, dayChange); } while (tries-- > 0 && this.isDisabled(iso)); if (tries > 0) { // check for edges of calendar const firstVisibleISO = this.getFirstVisibleISO(); const lastVisibleISO = this.getLastVisibleISO(); if (iso < firstVisibleISO) { if (this.input.navigable) this.prevMonth(); else iso = firstVisibleISO; } else if (iso > lastVisibleISO) { if (this.input.navigable) this.nextMonth(); else iso = lastVisibleISO; } this.setTabindexAndFocus(iso); this.emit("focus", { iso: this.state.tabindexISO }); } } else { switch (event.key) { case "PageUp": this.prevMonth(true); break; case "PageDown": this.nextMonth(true); break; case "Home": { const firstActiveISO = this.getFirstActiveISO(); if (firstActiveISO) { this.setTabindexAndFocus(firstActiveISO); this.emit("focus", { iso: this.state.tabindexISO }); } break; } case "End": { const lastActiveISO = this.getLastActiveISO(); if (lastActiveISO) { this.setTabindexAndFocus(lastActiveISO); this.emit("focus", { iso: this.state.tabindexISO }); } break; } default: } } } getMonthDate(offset) { const baseDate = (0, date_utils_1.fromISO)(this.state.baseISO); const date = new Date(Date.UTC(baseDate.getUTCFullYear(), baseDate.getUTCMonth() + offset)); return date; } getFirstVisibleISO() { return (0, date_utils_1.toISO)(this.getMonthDate(this.state.offset)); } getLastVisibleISO(input = this.input) { const baseDate = (0, date_utils_1.fromISO)(this.state.baseISO); return (0, date_utils_1.toISO)(new Date(Date.UTC(baseDate.getUTCFullYear(), baseDate.getUTCMonth() + this.state.offset + (input.numMonths || 1), 0))); } getFirstActiveISO(input = this.input) { let iso = this.getFirstVisibleISO(); const lastVisible = this.getLastVisibleISO(input); while (iso <= lastVisible && this.isDisabled(iso)) { iso = (0, date_utils_1.offsetISO)(iso, 1); } return iso > lastVisible ? null : iso; } getLastActiveISO(input = this.input) { let iso = this.getLastVisibleISO(input); const firstVisible = this.getFirstVisibleISO(); while (iso >= firstVisible && this.isDisabled(iso)) { iso = (0, date_utils_1.offsetISO)(iso, -1); } return iso < firstVisible ? null : iso; } monthTitle(date) { const formatter = new Intl.DateTimeFormat((0, dates_1.localeDefault)(this.input.locale), { month: "long", year: "numeric", }); return formatter.format(new Date(date.getUTCFullYear(), date.getUTCMonth())); } prevMonth(focus) { if (this.state.disableBefore && this.getFirstVisibleISO() <= this.state.disableBefore) { return false; } this.state.offset--; let newTabindexISO = this.state.tabindexISO; const lastActiveISO = this.getLastActiveISO(); if (lastActiveISO && this.state.tabindexISO > lastActiveISO) { newTabindexISO = this.state.tabindexISO = lastActiveISO; } if (focus) { this.setTabindexAndFocus(newTabindexISO); } this.emit("month-change", { iso: (0, date_utils_1.toISO)(this.getMonthDate(this.state.offset)), }); return true; } nextMonth(focus) { if (this.state.disableAfter && this.getLastVisibleISO() >= this.state.disableAfter) { return false; } this.state.offset++; let newTabindexISO = this.state.tabindexISO; const firstActiveISO = this.getFirstActiveISO(); if (firstActiveISO && this.state.tabindexISO < firstActiveISO) { newTabindexISO = this.state.tabindexISO = firstActiveISO; } if (focus) { this.setTabindexAndFocus(newTabindexISO); } this.emit("month-change", { iso: (0, date_utils_1.toISO)(this.getMonthDate(this.state.offset + (this.input.numMonths || 1))), }); return true; } setTabindexAndFocus(iso) { this.state.tabindexISO = iso; // After UI updates, focus on the new tabindex date setTimeout(() => { var _a; return (_a = this.getEl(iso)) === null || _a === void 0 ? void 0 : _a.focus(); }); } calculateRangeDisplay(input = this.input) { if (input.selected && input.range) { // Determine range display (state.rangeStart-state.rangeEnd) let iso1, iso2; if (Array.isArray(input.selected)) { // Two elements are selected, we can use them as the ends of the range. [iso1, iso2] = input.selected; } else if (this.state.focusISO) { // One element is selected and the user is focused on a date, // so we use the selected component and the focus date instead iso1 = input.selected; iso2 = this.state.focusISO; } if (iso1 && iso2) { // Both ends of the range are valid, figure out which is first if (iso1 < iso2) { this.state.rangeStart = iso1; this.state.rangeEnd = iso2; } else { this.state.rangeStart = iso2; this.state.rangeEnd = iso1; } } else { // We can't display a range, so ensure that no range is highlighted this.state.rangeStart = this.state.rangeEnd = null; } } else { // We can't display a range, so ensure that no range is highlighted this.state.rangeStart = this.state.rangeEnd = null; } } isInRange(iso) { if (!this.state.rangeStart || !this.state.rangeEnd) // range doesn't exist return false; if (iso < this.state.rangeStart || iso > this.state.rangeEnd) // date is outside of range return false; return true; } } module.exports = Calendar;