UNPKG

@oslokommune/punkt-elements

Version:

Komponentbiblioteket til Punkt, et designsystem laget av Oslo Origo

657 lines (656 loc) 23.5 kB
import { c as e, d as t, l as n, o as r, r as i, s as a, t as o } from "./element-cZ85T_aa.js"; import { t as s } from "./class-map-C8HuhNzZ.js"; import "./icon-Co72KtgF.js"; import { _ as c, a as l, b as u, d, f, g as p, l as m, m as h, o as g, p as _, t as v, v as y, y as b } from "./converters-jauYkvNh.js"; //#region ../../node_modules/date-fns/addDays.js function x(e, t, n) { let r = b(e, n?.in); return isNaN(t) ? u(n?.in || e, NaN) : (t && r.setDate(r.getDate() + t), r); } //#endregion //#region ../../node_modules/date-fns/_lib/normalizeInterval.js function S(e, t) { let [n, r] = y(e, t.start, t.end); return { start: n, end: r }; } //#endregion //#region ../../node_modules/date-fns/eachDayOfInterval.js function C(e, t) { let { start: n, end: r } = S(t?.in, e), i = +n > +r, a = i ? +n : +r, o = i ? r : n; o.setHours(0, 0, 0, 0); let s = t?.step ?? 1; if (!s) return []; s < 0 && (s = -s, i = !i); let c = []; for (; +o <= a;) c.push(u(n, o)), o.setDate(o.getDate() + s), o.setHours(0, 0, 0, 0); return i ? c.reverse() : c; } //#endregion //#region ../../node_modules/date-fns/getISODay.js function w(e, t) { let n = b(e, t?.in).getDay(); return n === 0 ? 7 : n; } var T = { name: "pkt-calendar", react: "PktCalendar", "css-class": "pkt-calendar", isElement: !0, isPureReact: !0, props: { id: { type: "string", name: "ID", description: "Unik identifikasjon for kalenderen" }, currentmonth: { type: "date", name: "Gjeldende måned", converter: "stringToDate", description: "Måneden som vises i kalenderen" }, selected: { type: "array", name: "Valgte datoer", converter: "csvToArray", reflect: !0, items: { type: "date" }, description: "Liste over valgte datoer" }, excludeweekdays: { type: "array", name: "Utelat ukedager", converter: "csvToArray", items: { type: "string" }, description: "Liste over ukedager (1-7) som skal ekskluderes" }, excludedates: { type: "array", name: "Utelat datoer", converter: "stringsToDate", items: { type: "date" }, description: "Liste over spesifikke datoer som skal ekskluderes" }, earliest: { type: "date", name: "Tidligst tillatte dato", converter: "stringToDate", default: null, description: "Tidligste dato som kan velges" }, latest: { type: "date", name: "Senest tillatte dato", converter: "stringToDate", default: null, description: "Seneste dato som kan velges" }, weeknumbers: { type: "boolean", name: "Vis ukenummer", reflect: !0, default: !1, description: "Vis ukenummer i kalenderen" }, withcontrols: { type: "boolean", name: "Med velgere for måned/år", reflect: !0, default: !1, description: "Vis navigasjonskontroller for måned og år" }, multiple: { type: "boolean", name: "Flervalg", reflect: !0, default: !1, description: "Tillat valg av flere datoer" }, maxMultiple: { attribute: "max-multiple", type: "number", name: "Maks antall i flervalg", default: 4, description: "Maksimalt antall datoer som kan velges" }, today: { type: "string", name: "Dagens dato (overstyring)", description: "ISO-datostreng som overstyrer dagens dato. Nyttig for snapshot-tester." }, range: { type: "boolean", name: "Datotidsrom (fra og til)", reflect: !0, default: !1, description: "Tillat valg av datotidsrom" }, dayStrings: { attribute: "day-strings", type: "array", name: "Korte dagsnavn", items: { type: "string" }, description: "Korte dagsnavn for kalenderoverskriften" }, dayStringsLong: { attribute: "day-strings-long", type: "array", name: "Fulle dagsnavn", items: { type: "string" }, description: "Fulle dagsnavn for tilgjengelighetsetiketter" }, monthStrings: { attribute: "month-strings", type: "array", name: "Månedsnavn", items: { type: "string" }, description: "Månedsnavn for kalendernavigasjonen" }, weekString: { attribute: "week-string", type: "string", name: "Ukenummer-etikett", description: "Etikett for ukenummerkolonnen" }, prevMonthString: { attribute: "prev-month-string", type: "string", name: "Forrige måned-etikett", description: "Tilgjengelighetsetikett for forrige måned-knappen" }, nextMonthString: { attribute: "next-month-string", type: "string", name: "Neste måned-etikett", description: "Tilgjengelighetsetikett for neste måned-knappen" } }, events: { "date-selected": { description: "Returnerer en liste med valgte datoer i ISO-format" }, close: { description: "Utløses når kalenderen ber om å lukkes (Escape-tast eller fokus forlater)" } } }; //#endregion //#region ../../shared-utils/calendar/date-validation.ts function E(e, t) { let n = (t.excludedates ?? []).map((e) => typeof e == "string" ? e : l(e)); return !m(e, t.earliest, t.latest, n, t.excludeweekdays ?? []); } function D(e, t, n, r) { return !!(E(e, n) || !t && r.multiple && r.maxMultiple > 0 && r.selectedCount >= r.maxMultiple); } function O(e, t, n) { let r = _(e, t, 0); return !(n && d(n) > r); } function k(e, t, n) { let r = _(t === 11 ? e + 1 : e, t === 11 ? 0 : t + 1, 1); return !(n && d(n) < r); } function A(e, t) { let n = _(e, t, 1), r = _(e, t + 1, 0), i = (n.getDay() + 6) % 7, a = r.getDate(); return { firstDayOfMonth: n, lastDayOfMonth: r, startingDay: i, numDays: a, numRows: Math.ceil((a + i) / 7), numDaysPrevMonth: _(e, t, 0).getDate(), initialWeek: c(n) }; } function j(e, t, n, r) { let { startingDay: i, numDays: a } = r; return e === 0 && t < i ? "prev-month" : n > a ? "next-month" : "current-month"; } function M(e, t, n, r) { let { startingDay: i, numDaysPrevMonth: a, numDays: o } = r; return e === "prev-month" ? a - (i - t - 1) : e === "next-month" ? n - o : n; } //#endregion //#region ../../shared-utils/calendar/selection-manager.ts function N(e) { return typeof e == "string" && (e = e.split(",")), e.length === 1 && e[0] === "" ? [] : e.map((e) => h(e)); } function P(e, t) { let n = C({ start: e, end: t }), r = {}; if (Array.isArray(n) && n.length) for (let i = 0; i < n.length; i++) { let a = n[i], o = a > e && a < t; r[l(a)] = o; } return r; } function F(e, t, n, r) { if (t.length !== 1) return !0; let i = C({ start: t[0], end: e }); if (!Array.isArray(i) || !i.length) return !0; for (let a = 0; a < i.length; a++) { for (let r of n ?? []) if (r > t[0] && r < e) return !1; if ((r ?? []).includes(w(i[a]).toString())) return !1; } return !0; } function I(e, t) { let n = l(e); return t.includes(n) ? t : [...t, n]; } function L(e, t) { let n = l(e), r = t.indexOf(n); if (r === -1) return t; if (t.length === 1) return []; let i = [...t]; return i.splice(r, 1), i; } function R(e, t, n) { let r = l(e); return t.includes(r) ? L(e, t) : n > 0 && t.length >= n ? t : I(e, t); } function z(e, t, n) { let r = l(e), i = N(t); return t.includes(r) ? t.indexOf(r) === 0 ? [] : L(e, t) : t.length > 1 || t.length === 1 && (!F(e, i, n.excludedates, n.excludeweekdays) || i[0] > e) ? [r] : I(e, t); } //#endregion //#region ../../shared-utils/calendar/keyboard-navigation.ts var B = { ArrowLeft: -1, ArrowRight: 1, ArrowUp: -7, ArrowDown: 7 }; function V(e) { let t = e.nodeName; return t === "INPUT" || t === "SELECT" || t === "BUTTON" && !e.dataset?.date; } function H(e, t, n) { let r = x(e, t); if (!r) return null; let i = n(`button[data-date="${l(r)}"]`); for (; i instanceof HTMLButtonElement && i.dataset.disabled;) if (r = x(r, t), i = n(`button[data-date="${l(r)}"]`), !i) return null; return r; } function U(e) { return B[e] ?? null; } //#endregion //#region src/components/calendar/calendar.ts var W = class extends o { constructor(...e) { super(...e), this.selected = [], this.multiple = T.props.multiple.default, this.maxMultiple = T.props.maxMultiple.default, this.range = T.props.range.default, this.earliest = T.props.earliest.default, this.latest = T.props.latest.default, this.excludedates = [], this.excludeweekdays = [], this.weeknumbers = T.props.weeknumbers.default, this.withcontrols = T.props.withcontrols.default, this.currentmonth = null, this.today = null, this.dayStrings = this.strings.dates.daysShort, this.dayStringsLong = this.strings.dates.days, this.monthStrings = this.strings.dates.months, this.weekString = this.strings.dates.week, this.prevMonthString = this.strings.dates.prevMonth, this.nextMonthString = this.strings.dates.nextMonth, this._selected = [], this.inRange = {}, this.rangeHovered = null, this.year = 0, this.month = 0, this.week = 0, this.currentmonthtouched = !1, this.focusedDate = null, this.selectableDates = [], this.tabIndexSet = 0; } get todayDate() { return this.today ? h(this.today) : p(); } firstUpdated(e) { this.addEventListener("keydown", this.handleKeydown); } disconnectedCallback() { this.removeEventListener("keydown", this.handleKeydown), super.disconnectedCallback(); } updated(e) { super.updated(e), e.has("selected") && this.convertSelected(); } convertSelected() { typeof this.selected == "string" && (this.selected = this.selected.split(",")), this.selected.length === 1 && this.selected[0] === "" && (this.selected = []), this._selected = N(this.selected), this.range && this.selected.length === 2 && (this.inRange = P(this._selected[0], this._selected[1])), this.setCurrentMonth(); } setCurrentMonth() { if (this.currentmonth === null && !this.currentmonthtouched && (this.currentmonthtouched = !0), this.selected.length && this.selected[0] !== "") { let e = h(this.selected[this.selected.length - 1]); this.currentmonth = isNaN(e.getTime()) ? f(this.todayDate) : e; } else this.currentmonth === null && (this.currentmonth = f(this.todayDate)); (!this.currentmonth || isNaN(this.currentmonth.getTime())) && (this.currentmonth = f(this.todayDate)), this.year = this.currentmonth.getFullYear(), this.month = this.currentmonth.getMonth(); } handleKeydown(e) { let t = U(e.key); t !== null && this.handleArrowKey(e, t); } handleArrowKey(e, t) { let n = e.target; if (V(n)) return; if (e.preventDefault(), !this.focusedDate) { this.focusOnCurrentDate(); return; } let r = H(this.focusedDate ? d(this.focusedDate) : _(this.year, this.month, 1), t, this.querySelector.bind(this)); if (r) { let e = this.querySelector(`button[data-date="${l(r)}"]`); e instanceof HTMLButtonElement && !e.dataset.disabled && (this.focusedDate = l(r), e.focus()); } } render() { return t` <div class="pkt-calendar ${this.weeknumbers ? "pkt-cal-weeknumbers" : ""}" @focusout=${this.closeEvent} @keydown=${(e) => { e.key === "Escape" && (e.preventDefault(), this.close()); }} > <nav class="pkt-cal-month-nav"> ${this.renderMonthNavButton("prev")} ${this.renderMonthNav()} ${this.renderMonthNavButton("next")} </nav> <table class="pkt-cal-days pkt-txt-12-medium pkt-calendar__body" role="grid" ?aria-multiselectable=${this.range || this.multiple} > <thead> ${this.renderDayNames()} </thead> <tbody> ${this.renderCalendarBody()} </tbody> </table> </div> `; } renderMonthNavButton(e) { let r = e === "prev", i = r ? this.isPrevMonthAllowed() : this.isNextMonthAllowed(), a = r ? this.prevMonthString : this.nextMonthString, o = r ? "chevron-thin-left" : "chevron-thin-right", s = r ? "pkt-calendar__prev-month" : "pkt-calendar__next-month", c = r ? () => this.prevMonth() : () => this.nextMonth(); return t`<div> <button type="button" aria-label="${a}" @click=${() => i && c()} @keydown=${(e) => { (e.key === "Enter" || e.key === " ") && (e.preventDefault(), i && c()); }} class="pkt-btn pkt-btn--tertiary pkt-btn--small pkt-btn--icon-only ${s} ${i ? "" : "pkt-invisible"}" .data-disabled=${i ? n : "disabled"} ?aria-disabled=${!i} tabindex=${i ? "0" : "-1"} > <pkt-icon class="pkt-btn__icon" name="${o}"></pkt-icon> <span class="pkt-btn__text">${a}</span> </button> </div>`; } renderDayNames() { let e = []; this.weeknumbers && e.push(t`<th><div class="pkt-calendar__week-number">${this.weekString}</div></th>`); for (let n = 0; n < this.dayStrings.length; n++) e.push(t`<th> <div class="pkt-calendar__day-name" aria-label="${this.dayStringsLong[n]}"> ${this.dayStrings[n]} </div> </th>`); return t`<tr class="pkt-cal-week-row">${e}</tr>`; } renderMonthNav() { return this.withcontrols ? t`<div class="pkt-cal-month-picker"> <label for="${this.id}-monthnav" class="pkt-hide">${this.strings.dates.month}</label> <select aria-label="${this.strings.dates.month}" class="pkt-input pkt-input-compact" id="${this.id}-monthnav" @change=${(e) => { e.stopImmediatePropagation(); let t = e.target; this.changeMonth(this.year, parseInt(t.value)); }} > ${this.monthStrings.map((e, n) => t`<option value=${n} ?selected=${this.month === n}>${e}</option>`)} </select> <label for="${this.id}-yearnav" class="pkt-hide">${this.strings.dates.year}</label> <input aria-label="${this.strings.dates.year}" class="pkt-input pkt-cal-input-year pkt-input-compact" id="${this.id}-yearnav" type="number" size="4" placeholder="0000" @change=${(e) => { e.stopImmediatePropagation(); let t = e.target; this.changeMonth(parseInt(t.value), this.month); }} .value=${this.year} /> </div>` : t`<div class="pkt-txt-16-medium pkt-calendar__month-title" aria-live="polite"> ${this.monthStrings[this.month]} ${this.year} </div>`; } getDayViewData(e, t) { let n = _(this.year, this.month, e), r = l(n), i = r === l(t), a = this.selected.includes(r), o = this.isDayDisabled(n, a), s = this.calculateTabIndex(r, o, e); return { currentDate: n, currentDateISO: r, isToday: i, isSelected: a, isDisabled: o, ariaLabel: g(n), tabindex: s }; } getDateConstraints() { return { earliest: this.earliest, latest: this.latest, excludedates: this.excludedates, excludeweekdays: this.excludeweekdays }; } isDayDisabled(e, t) { return D(e, t, this.getDateConstraints(), { multiple: this.multiple, maxMultiple: this.maxMultiple, selectedCount: this.selected.length }); } calculateTabIndex(e, t, n) { return this.focusedDate ? this.focusedDate === e && !t ? "0" : "-1" : !t && this.tabIndexSet === 0 ? (this.tabIndexSet = n, "0") : this.tabIndexSet === n ? "0" : "-1"; } getDayCellClasses(e) { let { currentDateISO: t, isToday: n, isSelected: r } = e, i = this.range && (this.selected.length === 2 || this.rangeHovered !== null) && t === this.selected[0], a = this.range && this.selected.length === 2 && t === this.selected[1]; return { "pkt-cal-today": n, "pkt-cal-selected": r, "pkt-cal-in-range": this.inRange[t], "pkt-cal-excluded": this.isExcluded(e.currentDate), "pkt-cal-in-range-first": i, "pkt-cal-in-range-last": a, "pkt-cal-range-hover": this.rangeHovered !== null && t === l(this.rangeHovered) }; } getDayButtonClasses(e) { let { currentDateISO: t, isToday: n, isSelected: r, isDisabled: i } = e, a = this.range && (this.selected.length === 2 || this.rangeHovered !== null) && t === this.selected[0], o = this.range && this.selected.length === 2 && t === this.selected[1]; return { "pkt-calendar__date": !0, "pkt-calendar__date--today": n, "pkt-calendar__date--selected": r, "pkt-calendar__date--disabled": i, "pkt-calendar__date--in-range": this.inRange[t], "pkt-calendar__date--in-range-hover": this.rangeHovered !== null && t === l(this.rangeHovered), "pkt-calendar__date--range-start": a, "pkt-calendar__date--range-end": o }; } handleDayFocus(e, t) { this.range && !this.isExcluded(e) && this.handleRangeHover(e), this.focusedDate = t; } renderDayView(e, r) { let i = this.getDayViewData(e, r), { currentDate: a, currentDateISO: o, isSelected: c, isDisabled: l, ariaLabel: u, tabindex: d } = i; this.selectableDates.push({ currentDateISO: o, isDisabled: l, tabindex: d }); let f = this.getDayCellClasses(i), p = this.getDayButtonClasses(i); return t`<td class=${s(f)}> <button type="button" aria-pressed=${c ? "true" : "false"} ?disabled=${l} class="pkt-btn pkt-btn--tertiary pkt-btn--small pkt-btn--label-only ${s(p)}" @mouseover=${() => this.range && !this.isExcluded(a) && this.handleRangeHover(a)} @focus=${() => this.handleDayFocus(a, o)} aria-label="${u}" tabindex=${d} data-disabled=${l ? "disabled" : n} data-date=${o} @keydown=${(e) => { (e.key === "Enter" || e.key === " ") && (e.preventDefault(), this.handleDateSelect(a)); }} @click=${(e) => { l || (e.preventDefault(), this.handleDateSelect(a)); }} > <span class="pkt-btn__text pkt-txt-14-light">${e}</span> </button> </td>`; } renderEmptyDayCell(e) { return t`<td class="pkt-cal-other"> <div class="pkt-btn pkt-btn--tertiary pkt-btn--small pkt-btn--label-only" data-disabled="disabled" > <span class="pkt-btn__text pkt-txt-14-light">${e}</span> </div> </td>`; } renderWeekRow(e) { return t`<tr class="pkt-cal-week-row" role="row">${e}</tr>`; } renderCalendarBody() { let e = this.todayDate, n = A(this.year, this.month); this.selectableDates = [], this.tabIndexSet = 0; let r = 1; this.week = n.initialWeek; let i = []; for (let a = 0; a < n.numRows; a++) { let o = []; this.weeknumbers && o.push(t`<td class="pkt-cal-week">${this.week}</td>`), this.week++; for (let t = 0; t < 7; t++) { let i = j(a, t, r, n); if (i === "current-month") o.push(this.renderDayView(r, e)), r++; else { let e = M(i, t, r, n); o.push(this.renderEmptyDayCell(e)), i === "next-month" && r++; } } i.push(this.renderWeekRow(o)); } return i; } isExcluded(e) { return E(e, this.getDateConstraints()); } isPrevMonthAllowed() { return O(this.year, this.month, this.earliest); } prevMonth() { let e = this.month === 0 ? 11 : this.month - 1, t = this.month === 0 ? this.year - 1 : this.year; this.changeMonth(t, e); } isNextMonthAllowed() { return k(this.year, this.month, this.latest); } nextMonth() { let e = this.month === 11 ? 0 : this.month + 1, t = this.month === 11 ? this.year + 1 : this.year; this.changeMonth(t, e); } changeMonth(e, t) { this.year = typeof e == "string" ? parseInt(e) : e, this.month = typeof t == "string" ? parseInt(t) : t, this.currentmonth = f(new Date(this.year, this.month, 1)), this.tabIndexSet = 0, this.focusedDate = null, this.selectableDates = []; } emptySelected() { this.selected = [], this._selected = [], this.inRange = {}; } normalizeSelected() { return typeof this.selected == "string" ? this.selected.split(",") : this.selected; } addToSelected(e) { this.selected = I(e, this.normalizeSelected()), this._selected = N(this.selected), this.range && this.selected.length === 2 && (this.convertSelected(), this.close()); } removeFromSelected(e) { this.selected = L(e, this.normalizeSelected()), this._selected = N(this.selected); } toggleSelected(e) { this.selected = R(e, this.normalizeSelected(), this.maxMultiple), this._selected = N(this.selected); } isRangeAllowed(e) { return F(e, this._selected, this.excludedates, this.excludeweekdays); } handleRangeSelect(e) { return this.selected = z(e, this.normalizeSelected(), { excludedates: this.excludedates, excludeweekdays: this.excludeweekdays }), this._selected = N(this.selected), this.selected.length === 2 ? (this.convertSelected(), this.close()) : this.selected.length === 1 && (this.inRange = {}), Promise.resolve(); } handleRangeHover(e) { if (!this.range || this._selected.length !== 1 || !this.isRangeAllowed(e) || this._selected[0] >= e) { this.rangeHovered = null; return; } this.rangeHovered = e, this.inRange = P(this._selected[0], e); } handleDateSelect(e) { return e ? (this.range ? this.handleRangeSelect(e) : this.multiple ? this.toggleSelected(e) : (this.selected.includes(l(e)) ? this.emptySelected() : (this.emptySelected(), this.addToSelected(e)), this.close()), this.dispatchEvent(new CustomEvent("date-selected", { detail: this.selected, bubbles: !0, composed: !0 })), Promise.resolve()) : Promise.resolve(); } focusOnCurrentDate() { let e = l(f(this.todayDate)), t = this.querySelector(`button[data-date="${e}"]`); if (t instanceof HTMLButtonElement) { this.focusedDate = e, t.focus(); return; } let n = this.selectableDates.find((e) => !e.isDisabled); if (n) { let e = this.querySelector(`button[data-date="${n.currentDateISO}"]`); e instanceof HTMLButtonElement && (this.focusedDate = n.currentDateISO, e.focus()); } } closeEvent(e) { !this.contains(e.relatedTarget) && !e.target.classList.contains("pkt-invisible") && this.close(); } close() { this.dispatchEvent(new CustomEvent("close", { detail: !0, bubbles: !0, composed: !0 })); } }; i([a({ converter: v.csvToArray })], W.prototype, "selected", void 0), i([a({ type: Boolean })], W.prototype, "multiple", void 0), i([a({ type: Number, attribute: "max-multiple" })], W.prototype, "maxMultiple", void 0), i([a({ type: Boolean })], W.prototype, "range", void 0), i([a({ type: String })], W.prototype, "earliest", void 0), i([a({ type: String })], W.prototype, "latest", void 0), i([a({ converter: v.stringsToDate })], W.prototype, "excludedates", void 0), i([a({ converter: v.csvToArray })], W.prototype, "excludeweekdays", void 0), i([a({ type: Boolean })], W.prototype, "weeknumbers", void 0), i([a({ type: Boolean })], W.prototype, "withcontrols", void 0), i([a({ converter: v.stringToDate })], W.prototype, "currentmonth", void 0), i([a({ type: String })], W.prototype, "today", void 0), i([a({ type: Array, attribute: "day-strings" })], W.prototype, "dayStrings", void 0), i([a({ type: Array, attribute: "day-strings-long" })], W.prototype, "dayStringsLong", void 0), i([a({ type: Array, attribute: "month-strings" })], W.prototype, "monthStrings", void 0), i([a({ type: String, attribute: "week-string" })], W.prototype, "weekString", void 0), i([a({ type: String, attribute: "prev-month-string" })], W.prototype, "prevMonthString", void 0), i([a({ type: String, attribute: "next-month-string" })], W.prototype, "nextMonthString", void 0), i([a({ type: Array })], W.prototype, "_selected", void 0), i([r()], W.prototype, "inRange", void 0), i([a({ type: Date })], W.prototype, "rangeHovered", void 0), i([a({ type: Number })], W.prototype, "year", void 0), i([a({ type: Number })], W.prototype, "month", void 0), i([a({ type: Number })], W.prototype, "week", void 0), i([r()], W.prototype, "currentmonthtouched", void 0), i([r()], W.prototype, "focusedDate", void 0); try { e("pkt-calendar")(W); } catch { console.warn("Forsøker å definere <pkt-calendar>, men den er allerede definert"); } //#endregion //#region src/components/calendar/index.ts var G = W; //#endregion export { W as n, G as t };