web-ui-pack
Version:
Web package with UI elements
928 lines (927 loc) • 33 kB
JavaScript
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;
}
(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;
}
not all and (prefers-reduced-motion) {
WUP-ZOOM-OUT-1 {
0% { transform: scale(1); opacity: 1; }
100% { transform: scale(0); opacity: 0; }
}
WUP-ZOOM-OUT-2 {
0% { transform: scale(5); opacity: 0; }
100% { transform: scale(1); opacity: 1; }
}
WUP-ZOOM-IN-1 {
0% { transform: scale(1); opacity: 1; }
100% { transform: scale(5); opacity: 0; }
}
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);