/*! * All material copyright ESRI, All Rights Reserved, unless otherwise specified. * See https://github.com/Esri/calcite-components/blob/master/LICENSE.md for details. * v1.0.0-beta.97 */ import { h, Fragment } from "@stencil/core"; import { dateFromRange, nextMonth, prevMonth, getOrder } from "../../utils/date"; import { Heading } from "../functional/Heading"; import { BUDDHIST_CALENDAR_YEAR_OFFSET } from "./resources"; import { isActivationKey } from "../../utils/key"; import { numberStringFormatter } from "../../utils/locale"; import { closestElementCrossShadowBoundary } from "../../utils/dom"; export class DatePickerMonthHeader { constructor() { //-------------------------------------------------------------------------- // // Private State/Props // //-------------------------------------------------------------------------- this.globalAttributes = {}; //-------------------------------------------------------------------------- // // Private Methods // //-------------------------------------------------------------------------- /** * Increment year on UP/DOWN keys * * @param event */ this.onYearKey = (event) => { const localizedYear = this.parseCalendarYear(event.target.value); switch (event.key) { case "ArrowDown": event.preventDefault(); this.setYear({ localizedYear, offset: -1 }); break; case "ArrowUp": event.preventDefault(); this.setYear({ localizedYear, offset: 1 }); break; } }; this.onYearChange = (event) => { this.setYear({ localizedYear: this.parseCalendarYear(event.target.value) }); }; this.onYearInput = (event) => { this.setYear({ localizedYear: this.parseCalendarYear(event.target.value), commit: false }); }; this.prevMonthClick = (event) => { this.handleArrowClick(event, this.prevMonthDate); }; this.prevMonthKeydown = (event) => { if (isActivationKey(event.key)) { this.prevMonthClick(event); } }; this.nextMonthClick = (event) => { this.handleArrowClick(event, this.nextMonthDate); }; this.nextMonthKeydown = (event) => { if (isActivationKey(event.key)) { this.nextMonthClick(event); } }; /* * Update active month on clicks of left/right arrows */ this.handleArrowClick = (event, date) => { event.preventDefault(); this.calciteDatePickerSelect.emit(date); }; } //-------------------------------------------------------------------------- // // Lifecycle // //-------------------------------------------------------------------------- componentWillLoad() { this.parentDatePickerEl = closestElementCrossShadowBoundary(this.el, "calcite-date-picker"); } connectedCallback() { this.setNextPrevMonthDates(); } render() { return h("div", { class: "header" }, this.renderContent()); } renderContent() { var _a; if (!this.activeDate || !this.localeData) { return null; } if (this.parentDatePickerEl) { const { numberingSystem, lang: locale } = this.parentDatePickerEl; numberStringFormatter.numberFormatOptions = { useGrouping: false, ...(numberingSystem && { numberingSystem }), ...(locale && { locale }) }; } const activeMonth = this.activeDate.getMonth(); const { months, unitOrder } = this.localeData; const localizedMonth = (months.wide || months.narrow || months.abbreviated)[activeMonth]; const localizedYear = this.formatCalendarYear(this.activeDate.getFullYear()); const iconScale = this.scale === "l" ? "m" : "s"; const order = getOrder(unitOrder); const reverse = order.indexOf("y") < order.indexOf("m"); const suffix = (_a = this.localeData.year) === null || _a === void 0 ? void 0 : _a.suffix; return (h(Fragment, null, h("a", { "aria-disabled": `${this.prevMonthDate.getMonth() === activeMonth}`, "aria-label": this.intlPrevMonth, class: "chevron", href: "#", onClick: this.prevMonthClick, onKeyDown: this.prevMonthKeydown, role: "button", tabindex: this.prevMonthDate.getMonth() === activeMonth ? -1 : 0 }, h("calcite-icon", { "flip-rtl": true, icon: "chevron-left", scale: iconScale })), h("div", { class: { text: true, "text--reverse": reverse } }, h(Heading, { class: "month", level: this.headingLevel }, localizedMonth), h("span", { class: "year-wrap" }, h("input", { "aria-label": this.intlYear, class: { year: true, "year--suffix": !!suffix }, inputmode: "numeric", maxlength: "4", minlength: "1", onChange: this.onYearChange, onInput: this.onYearInput, onKeyDown: this.onYearKey, pattern: "\\d*", ref: (el) => (this.yearInput = el), type: "text", value: localizedYear }), suffix && h("span", { class: "suffix" }, suffix))), h("a", { "aria-disabled": `${this.nextMonthDate.getMonth() === activeMonth}`, "aria-label": this.intlNextMonth, class: "chevron", href: "#", onClick: this.nextMonthClick, onKeyDown: this.nextMonthKeydown, role: "button", tabindex: this.nextMonthDate.getMonth() === activeMonth ? -1 : 0 }, h("calcite-icon", { "flip-rtl": true, icon: "chevron-right", scale: iconScale })))); } setNextPrevMonthDates() { if (!this.activeDate) { return; } this.nextMonthDate = dateFromRange(nextMonth(this.activeDate), this.min, this.max); this.prevMonthDate = dateFromRange(prevMonth(this.activeDate), this.min, this.max); } formatCalendarYear(year) { const { localeData } = this; const buddhistCalendar = localeData["default-calendar"] === "buddhist"; const yearOffset = buddhistCalendar ? BUDDHIST_CALENDAR_YEAR_OFFSET : 0; return numberStringFormatter.localize(`${year + yearOffset}`); } parseCalendarYear(year) { const { localeData } = this; const buddhistCalendar = localeData["default-calendar"] === "buddhist"; const yearOffset = buddhistCalendar ? BUDDHIST_CALENDAR_YEAR_OFFSET : 0; const parsedYear = Number(numberStringFormatter.delocalize(year)) - yearOffset; return numberStringFormatter.localize(`${parsedYear}`); } getInRangeDate({ localizedYear, offset = 0 }) { const { min, max, activeDate } = this; const parsedYear = Number(numberStringFormatter.delocalize(localizedYear)); const length = parsedYear.toString().length; const year = isNaN(parsedYear) ? false : parsedYear + offset; const inRange = year && (!min || min.getFullYear() <= year) && (!max || max.getFullYear() >= year); // if you've supplied a year and it's in range if (year && inRange && length === localizedYear.length) { const nextDate = new Date(activeDate); nextDate.setFullYear(year); return dateFromRange(nextDate, min, max); } } /** * Parse localized year string from input, * set to active if in range * * @param root0 * @param root0.localizedYear * @param root0.commit * @param root0.offset */ setYear({ localizedYear, commit = true, offset = 0 }) { const { yearInput, activeDate } = this; const inRangeDate = this.getInRangeDate({ localizedYear, offset }); // if you've supplied a year and it's in range, update active date if (inRangeDate) { this.calciteDatePickerSelect.emit(inRangeDate); } if (commit) { yearInput.value = this.formatCalendarYear((inRangeDate || activeDate).getFullYear()); } } static get is() { return "calcite-date-picker-month-header"; } static get encapsulation() { return "shadow"; } static get originalStyleUrls() { return { "$": ["date-picker-month-header.scss"] }; } static get styleUrls() { return { "$": ["date-picker-month-header.css"] }; } static get properties() { return { "selectedDate": { "type": "unknown", "mutable": false, "complexType": { "original": "Date", "resolved": "Date", "references": { "Date": { "location": "global" } } }, "required": false, "optional": false, "docs": { "tags": [], "text": "Already selected date." } }, "activeDate": { "type": "unknown", "mutable": false, "complexType": { "original": "Date", "resolved": "Date", "references": { "Date": { "location": "global" } } }, "required": false, "optional": false, "docs": { "tags": [], "text": "Focused date with indicator (will become selected date if user proceeds)" } }, "headingLevel": { "type": "number", "mutable": false, "complexType": { "original": "HeadingLevel", "resolved": "1 | 2 | 3 | 4 | 5 | 6", "references": { "HeadingLevel": { "location": "import", "path": "../functional/Heading" } } }, "required": false, "optional": false, "docs": { "tags": [], "text": "Number at which section headings should start for this component." }, "attribute": "heading-level", "reflect": false }, "min": { "type": "unknown", "mutable": false, "complexType": { "original": "Date", "resolved": "Date", "references": { "Date": { "location": "global" } } }, "required": false, "optional": false, "docs": { "tags": [], "text": "Minimum date of the calendar below which is disabled." } }, "max": { "type": "unknown", "mutable": false, "complexType": { "original": "Date", "resolved": "Date", "references": { "Date": { "location": "global" } } }, "required": false, "optional": false, "docs": { "tags": [], "text": "Maximum date of the calendar above which is disabled." } }, "intlPrevMonth": { "type": "string", "mutable": false, "complexType": { "original": "string", "resolved": "string", "references": {} }, "required": false, "optional": false, "docs": { "tags": [], "text": "Localized string for previous month." }, "attribute": "intl-prev-month", "reflect": false }, "intlNextMonth": { "type": "string", "mutable": false, "complexType": { "original": "string", "resolved": "string", "references": {} }, "required": false, "optional": false, "docs": { "tags": [], "text": "Localized string for next month." }, "attribute": "intl-next-month", "reflect": false }, "intlYear": { "type": "string", "mutable": false, "complexType": { "original": "string", "resolved": "string", "references": {} }, "required": false, "optional": false, "docs": { "tags": [], "text": "Localized string for year." }, "attribute": "intl-year", "reflect": false }, "scale": { "type": "string", "mutable": false, "complexType": { "original": "Scale", "resolved": "\"l\" | \"m\" | \"s\"", "references": { "Scale": { "location": "import", "path": "../interfaces" } } }, "required": false, "optional": false, "docs": { "tags": [], "text": "specify the scale of the date picker" }, "attribute": "scale", "reflect": true }, "localeData": { "type": "unknown", "mutable": false, "complexType": { "original": "DateLocaleData", "resolved": "DateLocaleData", "references": { "DateLocaleData": { "location": "import", "path": "../date-picker/utils" } } }, "required": false, "optional": false, "docs": { "tags": [], "text": "CLDR locale data for translated calendar info" } } }; } static get states() { return { "globalAttributes": {}, "nextMonthDate": {}, "prevMonthDate": {} }; } static get events() { return [{ "method": "calciteDatePickerSelect", "name": "calciteDatePickerSelect", "bubbles": true, "cancelable": false, "composed": true, "docs": { "tags": [], "text": "Changes to active date" }, "complexType": { "original": "Date", "resolved": "Date", "references": { "Date": { "location": "global" } } } }]; } static get elementRef() { return "el"; } static get watchers() { return [{ "propName": "min", "methodName": "setNextPrevMonthDates" }, { "propName": "max", "methodName": "setNextPrevMonthDates" }, { "propName": "activeDate", "methodName": "setNextPrevMonthDates" }]; } }