UNPKG

@oslokommune/punkt-elements

Version:

Komponentbiblioteket til Punkt, et designsystem laget av Oslo Origo

504 lines (503 loc) 20.7 kB
import { c as e, d as t, i as n, l as r, o as i, r as a, s as o } from "./element-cZ85T_aa.js"; import { t as s } from "./class-map-C8HuhNzZ.js"; import { n as c } from "./element-with-slot-4J2o3SeU.js"; import "./icon-Co72KtgF.js"; import { t as l } from "./if-defined-Cjj8qN-Z.js"; import { n as u, t as d } from "./ref-RS8Uv6uC.js"; import { t as f } from "./input-element-B1ciW1N2.js"; import "./input-wrapper-DVU68NcJ.js"; //#region ../../shared-utils/timepicker/time-utils.ts var p = (e) => /^\d{2}:\d{2}$/.test(e) && parseInt(e.slice(0, 2), 10) <= 23 && parseInt(e.slice(3), 10) <= 59, m = (e) => { let [t, n] = e.split(":").map(Number); return t * 60 + n; }, h = (e) => e ? Math.max(1, Math.round(e / 60)) : 1, g = (e, t) => { let n = e ? parseInt(String(e).split(":")[0], 10) : 0, r = t ? parseInt(String(t).split(":")[0], 10) : 23; return Array.from({ length: r - n + 1 }, (e, t) => t + n); }, _ = (e) => { let t = h(e); return Array.from({ length: Math.floor(60 / t) }, (e, n) => n * t); }, v = (e, t, n, r) => { let i = e === "" ? n === 1 ? 0 : 23 : parseInt(e, 10), a = t === "" ? n === 1 ? 0 : Math.floor(59 / r) * r : parseInt(t, 10); return a += n * r, a > 59 ? (a = 0, i = (i + 1) % 24) : a < 0 && (a = Math.floor(59 / r) * r, i = (i - 1 + 24) % 24), { hours: String(i).padStart(2, "0"), minutes: String(a).padStart(2, "0") }; }, y = class extends f { constructor(...e) { super(...e), this.hiddenInputRef = d(), this.hoursInputRef = d(), this.minutesInputRef = d(), this.buttonRef = d(), this.value = "", this.hidePicker = !1, this.stepArrows = !1, this._hours = "", this._minutes = "", this._isOpen = !1, this._hoursDigitCount = 0, this._hoursFirstDigit = -1, this._minutesDigitCount = 0, this._minutesFirstDigit = -1, this._hasFocus = !1, this._outsideClickHandler = (e) => { this.contains(e.target) || this._closePopup(); }, this._handleComponentFocusIn = () => { this._hasFocus || (this._hasFocus = !0, this.onFocus()); }, this._handleComponentFocusOut = (e) => { this.contains(e.relatedTarget) || (this._hasFocus = !1, this.onBlur()); }, this._handleHoursKeydown = (e) => { let t = e.target; switch (e.key) { case "ArrowUp": { e.preventDefault(); let t = this._hours === "" ? 0 : parseInt(this._hours, 10); this._hours = String((t + 1) % 24).padStart(2, "0"), this._syncValueFromDisplay(), this.onInput(); break; } case "ArrowDown": { e.preventDefault(); let t = this._hours === "" ? 0 : parseInt(this._hours, 10); this._hours = String((t - 1 + 24) % 24).padStart(2, "0"), this._syncValueFromDisplay(), this.onInput(); break; } case "ArrowRight": e.preventDefault(), this.minutesInputRef.value?.focus(); break; case "Backspace": case "Delete": this._hoursDigitCount = 0, this._hoursFirstDigit = -1; break; case "Tab": break; case "Enter": { e.preventDefault(); let n = this.internals.form; n ? n.requestSubmit() : t.blur(); break; } default: if (/^\d$/.test(e.key)) { e.preventDefault(); let t = parseInt(e.key, 10); if (this._hoursDigitCount === 0) this._hoursFirstDigit = t, this._hours = String(t).padStart(2, "0"), this._hoursDigitCount = 1, this.onInput(), t >= 3 && (this._hoursFirstDigit = -1, this._hoursDigitCount = 0, this._syncValueFromDisplay(), this.minutesInputRef.value?.focus()); else { let e = this._hoursFirstDigit * 10 + t; this._hours = e <= 23 ? String(e).padStart(2, "0") : String(t).padStart(2, "0"), this._hoursFirstDigit = -1, this._hoursDigitCount = 0, this._syncValueFromDisplay(), this.onInput(), this.minutesInputRef.value?.focus(); } } else e.preventDefault(); } }, this._handleHoursBlur = () => { this._hours !== "" && (this._hours = String(parseInt(this._hours, 10)).padStart(2, "0")), this._hoursDigitCount = 0, this._hoursFirstDigit = -1, this._syncValueFromDisplay(); }, this._handleMinutesKeydown = (e) => { let t = e.target; switch (e.key) { case "ArrowUp": { e.preventDefault(); let t = this._minutes === "" ? 0 : parseInt(this._minutes, 10); this._minutes = String((t + this._minuteStep) % 60).padStart(2, "0"), this._syncValueFromDisplay(), this.onInput(); break; } case "ArrowDown": { e.preventDefault(); let t = this._minutes === "" ? 0 : parseInt(this._minutes, 10); this._minutes = String((t - this._minuteStep + 60) % 60).padStart(2, "0"), this._syncValueFromDisplay(), this.onInput(); break; } case "ArrowLeft": e.preventDefault(), this.hoursInputRef.value?.focus(); break; case "Backspace": case "Delete": this._minutesDigitCount = 0, this._minutesFirstDigit = -1; break; case "Tab": break; case "Enter": { e.preventDefault(); let n = this.internals.form; n ? n.requestSubmit() : t.blur(); break; } default: if (/^\d$/.test(e.key)) { e.preventDefault(); let t = parseInt(e.key, 10); if (this._minutesDigitCount === 0) this._minutesFirstDigit = t, this._minutes = String(t).padStart(2, "0"), this._minutesDigitCount = 1, this.onInput(), t >= 6 && (this._minutesFirstDigit = -1, this._minutesDigitCount = 0, this._syncValueFromDisplay()); else { let e = this._minutesFirstDigit * 10 + t; this._minutes = e <= 59 ? String(e).padStart(2, "0") : String(t).padStart(2, "0"), this._minutesFirstDigit = -1, this._minutesDigitCount = 0, this._syncValueFromDisplay(), this.onInput(); } } else e.preventDefault(); } }, this._handleMinutesBlur = () => { this._minutes !== "" && (this._minutes = String(parseInt(this._minutes, 10)).padStart(2, "0")), this._minutesDigitCount = 0, this._minutesFirstDigit = -1, this._syncValueFromDisplay(); }, this._handlePopupKeydown = (e) => { let t = document.activeElement, n = t.closest(".pkt-timepicker-popup__col"); if (!n) return; let r = Array.from(n.querySelectorAll(".pkt-timepicker-popup__option")), i = r.indexOf(t), a = t.dataset.type ?? ""; switch (e.key) { case "ArrowDown": e.preventDefault(), this._focusOptionAndSync(r[Math.min(i + 1, r.length - 1)], a); break; case "ArrowUp": e.preventDefault(), this._focusOptionAndSync(r[Math.max(i - 1, 0)], a); break; case "Home": e.preventDefault(), this._focusOptionAndSync(r[0], a); break; case "End": e.preventDefault(), this._focusOptionAndSync(r[r.length - 1], a); break; case "ArrowRight": e.preventDefault(), a === "hour" && (this._focusOptionAndSync(t, a), this.updateComplete.then(() => { this._scrollToSelected(), this._focusSelectedOrFirst("minute"); })); break; case "ArrowLeft": e.preventDefault(), a === "minute" && (this._focusOptionAndSync(t, a), this.updateComplete.then(() => { this._scrollToSelected(), this._focusSelectedOrFirst("hour"); })); break; case "Enter": case " ": e.preventDefault(), t.click(); break; case "Escape": e.preventDefault(), this._closePopup(), this.buttonRef.value?.focus(); break; } }; } get inputRef() { return this.hiddenInputRef; } manageValidity(e) { let t = this.hoursInputRef.value; if (!t) return; if (this.required && !this.value) { this.internals.setValidity({ valueMissing: !0 }, n.forms.messages.required, t), this._setAriaInvalid(!0); return; } if (!this.value) { this.internals.setValidity({}), this._setAriaInvalid(!1); return; } let r = m(this.value), i = h(this.step); if (this.min && r < m(String(this.min))) { this.internals.setValidity({ rangeUnderflow: !0 }, n.forms.messages.rangeUnderflowMin.replace("{min}", String(this.min)), t), this._setAriaInvalid(!0); return; } if (this.max && r > m(String(this.max))) { this.internals.setValidity({ rangeOverflow: !0 }, n.forms.messages.rangeOverflowMax.replace("{max}", String(this.max)), t), this._setAriaInvalid(!0); return; } if (this.step && r % i !== 0) { let e = i === 60 ? n.forms.messages.timeStepMismatchHour : i === 30 ? n.forms.messages.timeStepMismatchHalfHour : n.forms.messages.timeStepMismatch.replace("{step}", `${i}, ${i * 2}, ${i * 3}`); this.internals.setValidity({ stepMismatch: !0 }, e, t), this._setAriaInvalid(!0); return; } this.internals.setValidity({}), this._setAriaInvalid(!1); } _setAriaInvalid(e) { if (!this.touched && e) return; let t = this.hoursInputRef.value, n = this.minutesInputRef.value; e ? (t?.setAttribute("aria-invalid", "true"), n?.setAttribute("aria-invalid", "true")) : (t?.removeAttribute("aria-invalid"), n?.removeAttribute("aria-invalid")); } connectedCallback() { super.connectedCallback(), this.addEventListener("focusin", this._handleComponentFocusIn), this.addEventListener("focusout", this._handleComponentFocusOut), document.addEventListener("click", this._outsideClickHandler); } disconnectedCallback() { super.disconnectedCallback(), this.removeEventListener("focusin", this._handleComponentFocusIn), this.removeEventListener("focusout", this._handleComponentFocusOut), document.removeEventListener("click", this._outsideClickHandler); } willUpdate(e) { super.willUpdate(e), e.has("value") && this._syncDisplayFromValue(); } updated(e) { super.updated(e), this.classList.toggle("pkt-timepicker--stepper", this.stepArrows), this.classList.toggle("pkt-timepicker--fullwidth", this.fullwidth), e.has("step") && this.step !== null && !this._isValidStep(this.step) && console.warn(`pkt-timepicker: step="${this.step}" er ikke en gyldig verdi. Step må være et multiplum av 60 (hele minutter) eller nøyaktig 3600 (hel time).`); } formResetCallback() { super.formResetCallback(), this._hours = "", this._minutes = "", this._isOpen = !1; } _isValidStep(e) { return e === 3600 || e < 3600 && 3600 % e == 0 && e % 60 == 0; } _syncDisplayFromValue() { let e = p(this.value) ? this.value.split(":") : null; e ? (this._hours = e[0], this._minutes = e[1]) : (this._hours = "", this._minutes = ""); } _syncValueFromDisplay() { if (this._hours !== "" && this._minutes !== "") { let e = `${this._hours}:${this._minutes}`; e !== this.value && (this.value = e, this.onChange(e)); } else this.value !== "" && (this.value = "", this.onChange("")); } get _minuteStep() { return h(this.step); } get _hourOptions() { return g(this.min, this.max); } get _minuteOptions() { return _(this.step); } _openPopup() { this._isOpen = !0, this.updateComplete.then(() => { this._scrollToSelected(), this._focusSelectedOrFirst("hour"); }); } _closePopup() { this._isOpen = !1, this._syncValueFromDisplay(); } _togglePopup() { this._isOpen ? this._closePopup() : this._openPopup(); } _scrollToSelected() { let e = this.querySelector(".pkt-timepicker-popup"); e && e.querySelectorAll(".pkt-timepicker-popup__col").forEach((e) => { let t = e.querySelector(".pkt-timepicker-popup__option--selected"); t && t.scrollIntoView({ block: "center" }); }); } _focusSelectedOrFirst(e) { let t = this.querySelector(".pkt-timepicker-popup"); if (!t) return; let n = t.querySelectorAll(".pkt-timepicker-popup__col"), r = e === "hour" ? n[0] : n[1]; if (!r) return; let i = r.querySelector(".pkt-timepicker-popup__option--selected"), a = r.querySelector(".pkt-timepicker-popup__option"); (i || a)?.focus(); } _handleOptionClick(e, t) { let n = String(e).padStart(2, "0"); t === "hour" ? (this._hours = n, this.updateComplete.then(() => this._focusSelectedOrFirst("minute"))) : (this._minutes = n, this._closePopup(), this.buttonRef.value?.focus()); } _focusOptionAndSync(e, t) { if (!e) return; let n = parseInt(e.dataset.value ?? "0", 10); t === "hour" ? this._hours = String(n).padStart(2, "0") : this._minutes = String(n).padStart(2, "0"), e.focus(); } _stepTime(e) { let t = v(this._hours, this._minutes, e, this._minuteStep); this._hours = t.hours, this._minutes = t.minutes, this._syncValueFromDisplay(); } _renderOption(e, n) { let r = String(e).padStart(2, "0"), i = e === (n === "hour" ? this._hours === "" ? NaN : parseInt(this._hours, 10) : this._minutes === "" ? NaN : parseInt(this._minutes, 10)); return t` <button class=${s({ "pkt-btn": !0, "pkt-btn--tertiary": !0, "pkt-btn--small": !0, "pkt-btn--label-only": !0, "pkt-timepicker-popup__option": !0, "pkt-timepicker-popup__option--selected": i })} type="button" role="option" aria-selected=${i ? "true" : "false"} tabindex=${i ? "0" : "-1"} data-type=${n} data-value=${e} @click=${(t) => { t.stopImmediatePropagation(), this._handleOptionClick(e, n); }} > <span class="pkt-btn__text pkt-txt-14-light">${r}</span> </button> `; } _renderPopup() { return t` <div class="pkt-timepicker-popup" id=${this.id + "-popup"} ?hidden=${!this._isOpen} role="group" aria-label=${this.strings.timepicker?.selectTime ?? "Velg tidspunkt"} @keydown=${this._handlePopupKeydown} @focusout=${(e) => { this.querySelector(".pkt-timepicker-popup")?.contains(e.relatedTarget) || this._closePopup(); }} > <div class="pkt-timepicker-popup__col" role="listbox" aria-label=${this.strings.timepicker?.hours ?? "Timer"} aria-orientation="vertical" > ${this._hourOptions.map((e) => this._renderOption(e, "hour"))} </div> <div class="pkt-timepicker-popup__col" role="listbox" aria-label=${this.strings.timepicker?.minutes ?? "Minutter"} aria-orientation="vertical" > ${this._minuteOptions.map((e) => this._renderOption(e, "minute"))} </div> </div> `; } _renderContainer() { let e = this.strings.timepicker?.hours ?? "Timer", n = this.strings.timepicker?.minutes ?? "Minutter", i = this.label ? `${e}, ${this.label}` : e, a = this.label ? `${n}, ${this.label}` : n; return t` <div class="pkt-input__container" @click=${(e) => { e.target.closest("button, input") || this.hoursInputRef.value?.focus(); }} > ${this.stepArrows ? t` <button class="pkt-input-icon pkt-btn pkt-btn--icon-only pkt-btn--tertiary pkt-timepicker__button pkt-timepicker__button--prev" type="button" aria-label=${this.strings.timepicker?.prevTime ?? "Forrige tidspunkt"} ?disabled=${this.disabled} @click=${() => this._stepTime(-1)} > <pkt-icon name="chevron-thin-left"></pkt-icon> <span class="pkt-btn__text" >${this.strings.timepicker?.prevTime ?? "Forrige tidspunkt"}</span > </button> ` : r} <input ${u(this.hoursInputRef)} type="text" inputmode="numeric" maxlength="2" size="2" class="pkt-input pkt-timepicker__input" id=${this.id + "-hours"} data-min="0" data-max="23" .value=${this._hours} placeholder="––" aria-label=${i} role="spinbutton" aria-invalid=${this.hasError ? "true" : r} aria-valuemin="0" aria-valuemax="23" aria-valuenow=${this._hours === "" ? r : this._hours} aria-valuetext=${this._hours === "" ? r : `${this._hours} ${e.toLowerCase()}`} autocomplete="off" ?disabled=${this.disabled} @keydown=${this._handleHoursKeydown} @blur=${this._handleHoursBlur} @focus=${(e) => { e.target.select(), e.stopImmediatePropagation(); }} /> <span class="pkt-timepicker__separator">:</span> <input ${u(this.minutesInputRef)} type="text" inputmode="numeric" maxlength="2" size="2" class="pkt-input pkt-timepicker__input" id=${this.id + "-minutes"} data-min="0" data-max="59" .value=${this._minutes} placeholder="––" aria-label=${a} role="spinbutton" aria-invalid=${this.hasError ? "true" : r} aria-valuemin="0" aria-valuemax="59" aria-valuenow=${this._minutes === "" ? r : this._minutes} aria-valuetext=${this._minutes === "" ? r : `${this._minutes} ${n.toLowerCase()}`} autocomplete="off" ?disabled=${this.disabled} @keydown=${this._handleMinutesKeydown} @blur=${this._handleMinutesBlur} @focus=${(e) => { e.target.select(), e.stopImmediatePropagation(); }} /> ${this.hidePicker && !this.stepArrows ? t`<pkt-icon class="pkt-input-icon pkt-timepicker__icon" name="clock" aria-hidden="true" ></pkt-icon>` : r} ${!this.hidePicker && !this.stepArrows ? t` <button ${u(this.buttonRef)} class="pkt-input-icon pkt-btn pkt-btn--icon-only pkt-btn--tertiary pkt-timepicker__button" type="button" aria-label=${this.strings.timepicker?.openPicker ?? "Åpne tidspunkt-velger"} aria-haspopup="listbox" aria-expanded=${this._isOpen ? "true" : "false"} aria-controls=${this.id + "-popup"} ?disabled=${this.disabled} @click=${this._togglePopup} > <pkt-icon name="clock"></pkt-icon> <span class="pkt-btn__text" >${this.strings.timepicker?.openPicker ?? "Åpne tidspunkt-velger"}</span > </button> ` : r} ${this.stepArrows ? t` <button class="pkt-input-icon pkt-btn pkt-btn--icon-only pkt-btn--tertiary pkt-timepicker__button pkt-timepicker__button--next" type="button" aria-label=${this.strings.timepicker?.nextTime ?? "Neste tidspunkt"} ?disabled=${this.disabled} @click=${() => this._stepTime(1)} > <pkt-icon name="chevron-thin-right"></pkt-icon> <span class="pkt-btn__text" >${this.strings.timepicker?.nextTime ?? "Neste tidspunkt"}</span > </button> ` : r} <input ${u(this.hiddenInputRef)} type="time" hidden name=${this.name || this.id} id=${this.id + "-input"} .value=${this.value} min=${l(this.min ?? void 0)} max=${l(this.max ?? void 0)} step=${l(this.step ?? void 0)} ?required=${this.required} ?disabled=${this.disabled} tabindex="-1" /> </div> `; } render() { return t` <pkt-input-wrapper label="${this.label}" ?disabled=${this.disabled} ?fullwidth=${this.fullwidth} ?hasError=${this.hasError} ?inline=${this.inline} ?optionalTag=${this.optionalTag} ?required=${this.required} ?requiredTag=${this.requiredTag} useWrapper=${this.useWrapper} .ariaDescribedBy=${this.ariaDescribedBy} .errorMessage=${this.errorMessage} .forId="${this.id + "-hours"}" .helptext=${this.helptext} .helptextDropdown=${this.helptextDropdown} .helptextDropdownButton=${this.helptextDropdownButton} .optionalText=${this.optionalText} .requiredText=${this.requiredText} .tagText=${this.tagText} > <div class="pkt-contents" slot="helptext">${c(this, "helptext")}</div> ${!this.hidePicker && !this.stepArrows ? t` <div class="pkt-timepicker__anchor"> ${this._renderContainer()} ${this._renderPopup()} </div> ` : this._renderContainer()} </pkt-input-wrapper> `; } }; a([o({ type: String, reflect: !0 })], y.prototype, "value", void 0), a([o({ type: Boolean, reflect: !0, attribute: "hide-picker" })], y.prototype, "hidePicker", void 0), a([o({ type: Boolean, reflect: !0, attribute: "step-arrows" })], y.prototype, "stepArrows", void 0), a([i()], y.prototype, "_hours", void 0), a([i()], y.prototype, "_minutes", void 0), a([i()], y.prototype, "_isOpen", void 0); try { e("pkt-timepicker")(y); } catch { console.warn("Forsøker å definere <pkt-timepicker>, men den er allerede definert"); } //#endregion export { y as t };