UNPKG

web-ui-pack

Version:
928 lines (927 loc) 33 kB
import WUPBaseControl from "./baseControl"; import { WUPcssHidden } from "../styles"; import WUPScrolled from "../helpers/scrolled"; import { dateCopyTime, dateFromString, dateToString } from "../indexHelpers"; import localeInfo from "../objects/localeInfo"; const tagName = "wup-calendar"; export var PickersEnum; (function (PickersEnum) { PickersEnum[PickersEnum["Day"] = 0] = "Day"; PickersEnum[PickersEnum["Month"] = 1] = "Month"; PickersEnum[PickersEnum["Year"] = 2] = "Year"; })(PickersEnum || (PickersEnum = {})); const add = (el, tag) => el.appendChild(document.createElement(tag)); export default class WUPCalendarControl extends WUPBaseControl { #ctr = this.constructor; static get $styleRoot() { return `:root { --ctrl-clr-selected: #fff; --ctrl-clr-selected-bg: #009fbc; --ctrl-clr-w: 17em; --ctrl-clr-cell-gap: 0px; --ctrl-clr-padding: calc(4px + var(--ctrl-clr-cell-gap) / 2); --ctrl-clr-off: var(--ctrl-err); --ctrl-clr-off-bg: var(--ctrl-err-bg); } [wupdark] { --ctrl-clr-off: var(--ctrl-err); --ctrl-clr-off-bg: none; }`; } static get $style() { return `${super.$style} :host { max-width: fit-content; margin: auto auto 20px; box-shadow: 0 0 0 1px var(--ctrl-border); border-radius: var(--ctrl-border-radius); background: none; text-transform: capitalize; } :host label { max-width: var(--ctrl-clr-w); display: block; padding-right: 0; } :host strong { display: block; box-sizing: border-box; max-width: 100%; text-overflow: ellipsis; overflow: hidden; white-space: nowrap; } :host input {${WUPcssHidden}} :host [calendar] { --ctrl-clr-cell-day-w: calc(var(--ctrl-clr-w) / 7 - var(--ctrl-clr-cell-gap)); --ctrl-clr-h: calc((var(--ctrl-clr-cell-day-w) + var(--ctrl-clr-cell-gap)) * 6); width: var(--ctrl-clr-w); padding: var(--ctrl-clr-padding); border-radius: var(--ctrl-border-radius); background: var(--ctrl-bg); box-sizing: content-box; } :host [calendar="day"] { --ctrl-clr-cell-w: var(--ctrl-clr-cell-day-w); --ctrl-clr-cell-h: var(--ctrl-clr-cell-day-w); } :host [calendar="month"], :host [calendar="year"] { --ctrl-clr-cell-w: calc(var(--ctrl-clr-w) / 4 - var(--ctrl-clr-cell-gap)); --ctrl-clr-cell-h: calc((var(--ctrl-clr-h) + 1em + var(--ctrl-clr-padding)) / 4 - var(--ctrl-clr-cell-gap)); } :host [calendar="month"] ul, :host [calendar="year"] ul { display: none; } :host header, :host ol, :host ul { display: flex; flex-wrap: wrap; margin:0; padding:0; } :host header > button { box-sizing: border-box; cursor: pointer; margin: auto; padding: 4px 1em; font-size: larger; font-weight: initial; border: none; border-radius: 2em; color: inherit; background: none; box-shadow: none; text-transform: inherit; } :host header > button[disabled] { pointer-events: none; } :host ol, :host ul { margin-top: var(--ctrl-clr-padding); list-style: none; } :host ul { color: var(--ctrl-label); } :host li { height: var(--ctrl-clr-cell-h); width: var(--ctrl-clr-cell-w); padding: 0; margin: calc(var(--ctrl-clr-cell-gap) / 2); font: inherit; line-height: var(--ctrl-clr-cell-h); text-align: center; border-radius: 50%; } :host ul>li { margin: 0 calc(var(--ctrl-clr-cell-gap) / 2); height: 1em; line-height: 1em; } :host li[prev], :host li[next] { opacity: 0.6; } :host li[aria-current] { color: var(--ctrl-clr-selected-bg); font-weight: bold; opacity: 1; } :host li[aria-selected] { background-color: var(--ctrl-clr-selected-bg); color: var(--ctrl-clr-selected); opacity: 1; } :host li[focused] { box-shadow: 0 0 3px 1px inset var(--ctrl-focus); opacity: 1; } :host li[focused][aria-selected] { text-decoration: underline; } @media (hover: hover) and (pointer: fine) { :host header>button:hover { background-color: var(--ctrl-clr-selected-bg); color: var(--ctrl-clr-selected); } :host ol>li:hover:not([disabled]) { box-shadow: 0 0 3px 1px inset var(--ctrl-focus); opacity: 1; } } :host li[disabled] { color: var(--ctrl-clr-off); background-color: var(--ctrl-clr-off-bg); } :host li[disabled][aria-selected] { font-weight: bold; } @media not all and (prefers-reduced-motion) { @keyframes WUP-ZOOM-OUT-1 { 0% { transform: scale(1); opacity: 1; } 100% { transform: scale(0); opacity: 0; } } @keyframes WUP-ZOOM-OUT-2 { 0% { transform: scale(5); opacity: 0; } 100% { transform: scale(1); opacity: 1; } } @keyframes WUP-ZOOM-IN-1 { 0% { transform: scale(1); opacity: 1; } 100% { transform: scale(5); opacity: 0; } } @keyframes WUP-ZOOM-IN-2 { 0% { transform: scale(0); opacity: 0; } 100% { transform: scale(1); opacity: 1; } } :host [calendar]>div { overflow: hidden; } :host [zoom] { animation: none var(--anim-t) linear forwards; } :host [zoom="out"] { animation-name: WUP-ZOOM-OUT-1; } :host [zoom="out2"] { animation-name: WUP-ZOOM-OUT-2; } :host [zoom="in"] { animation-name: WUP-ZOOM-IN-1; } :host [zoom="in2"] { animation-name: WUP-ZOOM-IN-2; } }`; } static $daysOfMonth(year, month, firstWeekDay = 1) { let dt = new Date(year, month + 1, 0); const r = { total: dt.getDate(), nextTo: 0, first: 0 }; dt.setDate(1); let shift = (dt.getDay() || 7) - firstWeekDay; if (shift < 0) { shift += 7; } if (shift) { dt = new Date(year, month, 0); const to = dt.getDate(); const from = to - shift + 1; r.prev = { from, to }; dt.setDate(from); } r.first = this.$dateToUTC(dt).valueOf(); const weeksDays = 42; r.nextTo = weeksDays - shift - r.total; return r; } static $dateToUTC(localDate) { return new Date(localDate.valueOf() - localDate.getTimezoneOffset() * 60000); } static $parse(yyyyMMdd, asUTC) { const sep = yyyyMMdd.includes("T") ? "T" : " "; const format = `YYYY-MM-DD${sep}hh:mm:ss.fff${asUTC || yyyyMMdd.endsWith("Z") ? "Z" : ""}`; const v = dateFromString(yyyyMMdd, format, { throwOutOfRange: true }); if (v == null) { throw new Error(`Impossible to parse date from '${yyyyMMdd}'. Expected format '${format}'`); } return v; } static get mappedAttributes() { const m = super.mappedAttributes; m.min = { type: 4 }; m.max = { type: 4 }; m.firstweekday = { type: 1 }; m.startwith = { type: 2 }; return m; } static $defaults = { ...WUPBaseControl.$defaults, validationRules: { ...WUPBaseControl.$defaults.validationRules, }, utc: true, firstWeekDay: null, startWith: null, min: null, max: null, exclude: null, }; constructor() { super(); this.#ctr.$defaults.firstWeekDay ||= localeInfo.firstWeekDay; this._opts.firstWeekDay ||= this.#ctr.$defaults.firstWeekDay; } $refreshPicker() { this.changePicker(this._pickerValue, this._picker); } parse(text) { if (!text) { return undefined; } return this.#ctr.$parse(text, !!this._opts.utc); } $refCalendar = document.createElement("div"); $refCalendarTitle = document.createElement("button"); $refCalendarItems = document.createElement("ol"); renderInput() { const i = this.$refInput; i.type = "text"; i.id = this.#ctr.$uniqueId; this.$refLabel.setAttribute("for", i.id); i.setAttribute("role", "combobox"); const menuId = this.#ctr.$uniqueId; i.setAttribute("aria-owns", menuId); i.setAttribute("aria-controls", menuId); const s = add(this.$refLabel, "span"); s.appendChild(this.$refInput); s.appendChild(this.$refTitle); this.appendChild(this.$refLabel); return { menuId }; } renderControl() { const { menuId } = this.renderInput(); this.$refCalendar.id = menuId; const h = add(this.$refCalendar, "header"); h.appendChild(this.$refCalendarTitle); this.$refCalendarTitle.setAttribute("tabindex", "-1"); this.$refCalendarTitle.setAttribute("aria-hidden", true); this.$refCalendarTitle.setAttribute("type", "button"); const box = add(this.$refCalendar, "div"); const animBox = add(box, "div"); animBox.appendChild(this.$refCalendarItems); this.$refCalendarItems.setAttribute("role", "grid"); this.appendChild(this.$refCalendar); } valueToStorage(v) { return dateToString(v, "yyyy-MM-dd"); } appendItem(prevEl, text, v) { let el; if (prevEl) { el = prevEl; while (el.attributes.length > 0) { el.removeAttributeNode(el.attributes[0]); } } else { el = add(this.$refCalendarItems, "li"); } el.setAttribute("role", "gridcell"); el.textContent = text; el._value = v; return el; } _pickerValue = new Date(); _picker = 0; #refreshSelected; #clearPicker; #scrollObj; showNext(isNext) { return !this.#scrollObj ? Promise.resolve() : this.#scrollObj.goTo(isNext); } async changePicker(utcVal, pickerNext) { await this.#clearPicker?.call(this, pickerNext - this._picker > 0); this._picker = pickerNext; let r; let type; switch (pickerNext) { case 2: type = "year"; r = this.getYearPicker(utcVal); this.$refCalendarTitle.disabled = true; break; case 1: type = "month"; r = this.getMonthPicker(utcVal); this.$refCalendarTitle.disabled = false; break; default: type = "day"; r = this.getDayPicker(); this.$refCalendarTitle.disabled = false; } this.$refCalendar.setAttribute("calendar", type); this.#handleClickItem = r.onItemClick; const renderPicker = (next) => { this._pickerValue = next; const a = r.renderItems(this.$refCalendarItems, next); this.$refCalendarItems._items = a; const first = a[0]._value; const now = this.#ctr.$dateToUTC(new Date()); let i = r.getIndex(now, first); a[i]?.setAttribute("aria-current", "date"); this.#refreshSelected = () => { let uv = this.$value; let item; if (uv) { uv = this.normalizeToUTC(uv); i = r.getIndex(uv, first); item = a[i]; } this.selectItem(item); }; this.#refreshSelected(); this.disableItems(a, r.getIndex); this.$isFocused && this.$ariaSpeak(this.$refCalendarTitle.textContent); this.#focused && this.focusItem(a.find((e) => !e.hasAttribute("prev"))); return a; }; renderPicker(utcVal); const scrollObj = new WUPScrolled(this.$refCalendarItems, { onRender: (n) => { const nextDate = r.next(utcVal, n); const { scrollFrom, scrollTo } = this.#disabled; if (scrollFrom != null || scrollTo != null) { const nextDateEnd = r.next(new Date(utcVal), 1).setUTCMilliseconds(-1); if (scrollFrom > nextDateEnd || scrollTo < nextDate) { r.next(utcVal, (-1 * n)); return null; } } const arr = renderPicker(nextDate); return arr; }, }); this.#scrollObj = scrollObj; this.#clearPicker = async (isOut) => { const box = this.$refCalendar.children[1].children[0]; const animate = (attrVal) => new Promise((resolve) => { box.setAttribute("zoom", attrVal); const { animationDuration: ad } = window.getComputedStyle(box); if (!ad) { resolve(null); } else { setTimeout(resolve, Number.parseFloat(ad.substring(0, ad.length - 1)) * 1000); box.addEventListener("animationstart", () => box.addEventListener("animationend", resolve, { once: true }), { once: true }); } }); await animate(isOut ? "out" : "in"); scrollObj.dispose(); this.removeChildren.call(this.$refCalendarItems); this.#refreshSelected = undefined; animate(isOut ? "out2" : "in2").then(() => box.removeAttribute("zoom")); }; } #isDayWeeksAdded = false; getDayPicker() { if (!this.#isDayWeeksAdded) { const box = this.$refCalendarItems.parentElement; const days = document.createElement("ul"); days.setAttribute("aria-hidden", true); box.prepend(days); const names = localeInfo.namesDayShort; for (let i = 0, n = this._opts.firstWeekDay - 1; i < 7; ++i, ++n) { const d = add(days, "li"); if (n >= names.length) { n = 0; } d.textContent = names[n]; } this.#isDayWeeksAdded = true; } const getIndex = (b, first) => Math.floor((b.valueOf() - first) / 86400000); const { namesMonth } = localeInfo; const renderItems = (ol, v) => { const valMonth = v.getUTCMonth(); const valYear = v.getUTCFullYear(); this.$refCalendarTitle.textContent = `${namesMonth[valMonth]} ${valYear}`; const items = []; const r = this.#ctr.$daysOfMonth(valYear, valMonth, this._opts.firstWeekDay); let iPrev = -999; if (ol.children.length) { iPrev = getIndex(r.first, ol.children.item(0)._value); } let i = 0; const addItem = (n, attr) => { const d = this.appendItem(ol.children.item(iPrev), n.toString(), r.first + i * 86400000); attr && d.setAttribute(attr, ""); items.push(d); ++i; ++iPrev; }; if (r.prev) { for (let n = r.prev.from; n <= r.prev.to; ++n) { addItem(n, "prev"); } } for (let n = 1; n <= r.total; ++n) { addItem(n, ""); } for (let n = 1; n <= r.nextTo; ++n) { addItem(n, "next"); } return items; }; return { getIndex, renderItems, next: (v, n) => { v.setUTCMonth(v.getUTCMonth() + n); return v; }, onItemClick: ({ target }, v) => { this.selectItem(target); let dt = new Date(v); if (!this._opts.utc) { dt = new Date(dt.getUTCFullYear(), dt.getUTCMonth(), dt.getUTCDate()); } const prev = this.$value || this.$initValue; prev && dateCopyTime(dt, prev, !!this._opts.utc); this.setValue(dt, 4); this.$ariaSpeak(this.$refInput.value); }, }; } getMonthPicker(dateToRound) { const pageSize = 12; dateToRound.setUTCMonth(0, 1); const getIndex = (b, first) => b.getUTCFullYear() * pageSize + b.getUTCMonth() - first; const renderItems = (ol, v) => { const year = v.getUTCFullYear(); this.$refCalendarTitle.textContent = `${year}`; const namesShort = localeInfo.namesMonthShort; const items = []; const total = year * pageSize; let iPrev = -999; if (ol.children.length) { iPrev = getIndex(new Date(Date.UTC(year, 0)), ol.children.item(0)._value); } const addItem = (n, i) => { const d = this.appendItem(ol.children.item(iPrev), n, total + i); items.push(d); ++iPrev; return d; }; namesShort.forEach(addItem); const n = items.length; for (let i = 0; i < 4; ++i) { const d = addItem(namesShort[i], i + n); d.setAttribute("next", ""); } return items; }; return { renderItems, getIndex, next: (v, n) => { v.setUTCFullYear(v.getUTCFullYear() + n); return v; }, onItemClick: (_e, v) => this.changePicker(new Date(Date.UTC(Math.floor(v / pageSize), v % pageSize, 1)), 0), }; } getYearPicker(dateToRound) { const pageSize = 16; const getFirst = (v) => { const page = Math.floor((v.getUTCFullYear() - 1970) / pageSize); return page * pageSize + 1970; }; dateToRound.setUTCMonth(0, 1); dateToRound.setUTCFullYear(getFirst(dateToRound)); const renderItems = (_ol, v) => { let year = getFirst(v); this.$refCalendarTitle.textContent = `${year} ... ${year + pageSize - 1}`; const items = []; let i = 0; for (i = 0; i < pageSize; ++i) { const d = this.appendItem(undefined, year.toString(), year); items.push(d); ++year; } return items; }; return { renderItems, getIndex: (b, first) => b.getUTCFullYear() - first, next: (v, n) => { v.setUTCFullYear(v.getUTCFullYear() + n * pageSize); return v; }, onItemClick: (_e, v) => this.changePicker(new Date(Date.UTC(v, 0)), 1), }; } disableItems(items, getIndex) { const first = items[0]._value; let i = 0; const { min, max } = this.#disabled; if (min) { const last = getIndex(min, first); items.some((item, k) => { if (k >= last) { return true; } return item.setAttribute("disabled", ""); }); } if (max) { i = getIndex(max, first); for (++i; i < items.length; ++i) { items[i]?.setAttribute("disabled", ""); } } if (this._picker === 0) { const { exclude } = this.#disabled; if (!exclude) { return; } i = exclude.findIndex((d) => d.valueOf() >= first); if (i < 0) { return; } for (; i < exclude.length; ++i) { const el = items[getIndex(exclude[i], first)]; if (el) { el.setAttribute("disabled", ""); } else { return; } } } else { const arr = this._picker === 1 ? this.#disabled.months : this.#disabled.years; let k = 0; for (i = 0; i < items.length && k < arr.length; ++i) { const el = items[i]; for (; k < arr.length; ++k) { if (arr[k] > el._value) { break; } else if (arr[k] === el._value) { el.setAttribute("disabled", ""); ++k; break; } } } } } #disabled; calcDisabled() { const months = []; const years = []; let { min, max, utc } = this._opts; min = this.normalizeToUTC(min); max = this.normalizeToUTC(max); let ex = this._opts.exclude; if (ex?.length) { const from = min ? new Date(min).setUTCHours(23, 59, 59, 999) : Number.MIN_SAFE_INTEGER; const to = max ? new Date(max).setUTCHours(0, 0, 0, 0) : Number.MAX_SAFE_INTEGER; let prevY = null; let mCnt = 0; const last = ex.length - 1; if (!utc) { ex = ex.map((v) => this.normalizeToUTC(v)); } for (let i = 0; i < ex.length; ++i) { const iStart = i; const y = ex[i].getUTCFullYear(); const m = ex[i].getUTCMonth(); const nextM = new Date(Date.UTC(y, m + 1)); if (prevY !== y) { prevY = y; mCnt = 0; } for (; i < ex.length; ++i) { if (ex[i + 1] >= nextM || i === last) { const cnt = i - iStart + 1; const total = new Date(y, m + 1, 0).getDate(); const hasEnabled = ex[i] > from && cnt !== total && ex[i] < to; if (!hasEnabled) { months.push(y * 12 + m); ++mCnt === 12 && years.push(y); } if (i !== last) { break; } } } } } return { months, years, scrollFrom: min ? Date.UTC(min.getUTCFullYear(), min.getUTCMonth()) : undefined, scrollTo: max ? new Date(Date.UTC(max.getUTCFullYear(), max.getUTCMonth() + 1)).setUTCMilliseconds(-1) : undefined, min, max, exclude: ex, }; } #focused; focusItem(el) { if (el?.hasAttribute("focused")) { return; } const was = this.#focused; if (was) { was.removeAttribute("focused"); was.removeAttribute("aria-label"); was.removeAttribute("aria-disabled"); } this.#focused = el; if (el) { el.id = el.id || this.#ctr.$uniqueId; el.setAttribute("focused", ""); this.$refInput.setAttribute("aria-activedescendant", el.id); el.hasAttribute("aria-selected") && el.setAttribute("aria-selected", "false"); setTimeout(() => { el.setAttribute("aria-label", `${el.textContent} ${this.$refCalendarTitle.textContent}`); el.hasAttribute("disabled") && el.setAttribute("aria-disabled", true); el.hasAttribute("aria-selected") && el.setAttribute("aria-selected", "true"); }, 100); } else { !this.$isFocused && this.$refInput.removeAttribute("aria-activedescendant"); } } focusSibling(n) { const was = this.#focused; let will = null; const isSkip = (el) => el.hasAttribute("prev") || el.hasAttribute("next"); if (!was) { const items = this.$refCalendarItems._items; will = items.find((el) => el.hasAttribute("aria-selected") && !isSkip(el)) || items.find((el) => el.hasAttribute("aria-current") && !isSkip(el)) || items.find((el) => !el.hasAttribute("prev")); } else { const getSibling = (nn) => { let el = was; for (let i = 0; i < Math.abs(nn); ++i) { el = nn > 0 ? el.nextElementSibling : el.previousElementSibling; if (!el) break; } if (el && isSkip(el)) { return null; } return el; }; will = getSibling(n); if (!will) { this.showNext(n > 0); will = getSibling(n); } } will && this.focusItem(will); return will; } selectItem(el) { this.querySelector("[aria-selected]")?.removeAttribute("aria-selected"); el?.setAttribute("aria-selected", "true"); } setValue(v, reason) { const r = super.setValue(v, reason); this.setInputValue(v); this.#refreshSelected?.call(this); return r; } setInputValue(v) { if (v) { const key = this._opts.utc ? "UTC" : ""; const { namesMonth } = localeInfo; this.$refInput.value = `${v[`get${key}Date`]()} ${namesMonth[v[`get${key}Month`]()]} ${v[`get${key}FullYear`]()}`; } else { this.$refInput.value = ""; } } clearValue() { super.clearValue(); this.$isEmpty && this.selectItem(undefined); } normalizeToUTC(v) { if (v == null || this._opts.utc) { return v; } return this.#ctr.$dateToUTC(v); } gotReady() { super.gotReady(); let { startWith } = this._opts; let startDate = null; if (typeof startWith === "string") { const arr = startWith.split("-"); switch (arr.length) { case 1: startWith = 2; break; case 2: startWith = 1; break; case 3: startWith = 0; break; default: startWith = null; break; } if (startWith != null) { startDate = new Date(Date.UTC(+arr[0] || new Date().getFullYear(), (+arr[1] || 1) - 1, +arr[2] || 1)); } } startWith ??= this.$isEmpty ? 2 : 0; let v = this.$value ? this.normalizeToUTC(this.$value) : startDate || this.#ctr.$dateToUTC(new Date()); const max = this.normalizeToUTC(this._opts.max); const min = this.normalizeToUTC(this._opts.min); v = max && max < v ? max : v; v = min && min > v ? min : v; v = new Date(v); v.setUTCHours(0, 0, 0, 0); v.setUTCDate(1); this.changePicker(v, startWith); } gotChanges(propsChanged) { this.gotChangesSharable(); super.gotChanges(propsChanged); (this.$isReadOnly || this.$isDisabled) && this.focusItem(null); if (!this._opts.label) { this.$refTitle.classList.add(this.#ctr.classNameHidden); } else { this.$refTitle.classList.remove(this.#ctr.classNameHidden); } if (!propsChanged || propsChanged.includes("exclude")) { this._opts.exclude?.sort((a, b) => a.valueOf() - b.valueOf()); } const isNeedRecalc = propsChanged && ["min", "max", "exclude", "utc"].some((a) => propsChanged.includes(a)); if (!propsChanged || isNeedRecalc) { this.#disabled = this.calcDisabled(); } isNeedRecalc && this.$refreshPicker(); } gotChangesSharable() { this._opts.firstWeekDay ??= this.constructor.$defaults.firstWeekDay; const attr = this.getAttribute("w-startwith"); if (attr != null) { switch (attr.toLowerCase()) { case "year": this._opts.startWith = 2; break; case "month": this._opts.startWith = 1; break; case "day": this._opts.startWith = 0; break; case "": this._opts.startWith = null; break; default: this._opts.startWith = attr; break; } } } gotFormChanges(propsChanged) { super.gotFormChanges(propsChanged); const i = this.$refInput; i.readOnly = true; this.$isReadOnly ? i.removeAttribute("aria-readonly") : i.setAttribute("aria-readonly", false); } gotRemoved() { this.focusItem(null); super.gotRemoved(); } gotFocusLost() { this.focusItem(null); super.gotFocusLost(); } gotFocus(ev) { const r = super.gotFocus(ev); const i = this.$refInput; r.push(this.appendEvent(this, "click", (e) => this.gotClick(e), { passive: false })); r.push(this.appendEvent(i, "input", (e) => this.gotInput(e))); return r; } #handleClickItem; gotClick(e) { if (e.button || this.$isReadOnly || this.$isDisabled) { return; } let t = e.target; if (this.$refCalendarTitle === t || this.$refCalendarTitle.contains(t)) { e.preventDefault(); this._picker !== 2 && this.changePicker(this._pickerValue, this._picker + 1); } else if (this.$refCalendarItems.contains(t)) { while (t !== this.$refCalendarItems) { const v = t._value; if (v !== undefined) { e.preventDefault(); !t.hasAttribute("disabled") && this.#handleClickItem.call(this, e, v); break; } t = t.parentElement; } } } gotInput(e) { const v = this.parse(e.target.value); this.setValue(v, 3); } gotKeyDown(e) { super.gotKeyDown(e); if (e.altKey) { return; } let isHandled = true; const isDayPicker = this._picker === 0; const items = this.$refCalendarItems._items; switch (e.key) { case "Enter": isHandled = this.#focused != null; case " ": { const el = this.#focused; if (el && !el.hasAttribute("disabled")) { setTimeout(() => el.dispatchEvent(new MouseEvent("click", { cancelable: true, bubbles: true }))); } break; } case "ArrowLeft": this.focusSibling(-1); break; case "ArrowRight": this.focusSibling(1); break; case "ArrowUp": this.focusSibling(isDayPicker ? -7 : -4); break; case "ArrowDown": this.focusSibling(isDayPicker ? 7 : 4); break; case "Home": this.focusItem(items.find((el) => !el.hasAttribute("prev"))); break; case "End": for (let i = items.length - 1;; --i) { if (!items[i].hasAttribute("next")) { this.focusItem(items[i]); break; } } break; case "PageDown": this.showNext(true); if (e.shiftKey) { for (let i = 0; i < 11; ++i) { this.showNext(true); } } this.focusItem(this.$refCalendarItems._items.find((el) => !el.hasAttribute("prev"))); break; case "PageUp": this.showNext(false); if (e.shiftKey) { for (let i = 0; i < 11; ++i) { this.showNext(false); } } this.focusItem(this.$refCalendarItems._items.find((el) => !el.hasAttribute("prev"))); break; default: isHandled = false; } isHandled && e.preventDefault(); } } customElements.define(tagName, WUPCalendarControl);