@oslokommune/punkt-elements
Version:
Komponentbiblioteket til Punkt, et designsystem laget av Oslo Origo
398 lines (397 loc) • 14.2 kB
JavaScript
import { c as e, d as t, i as n, l as r, o as i, r as a, s as o, t as s } from "./element-cZ85T_aa.js";
import { t as c } from "./class-map-C8HuhNzZ.js";
import { t as l } from "./if-defined-Cjj8qN-Z.js";
import { t as u } from "./repeat-DV1O43YU.js";
import { n as d } from "./utils-BOOZ0ppt.js";
//#region ../../shared-utils/combobox/option-utils.ts
var f = (e, t) => t && (e.find((e) => e.value === t) || e.find((e) => e.label === t)) || null, p = (e, t) => {
if (!t) return -1;
let n = e.findIndex((e) => e.value === t);
return n >= 0 ? n : e.findIndex((e) => e.label === t);
}, m = (e, t) => {
if (!t) return [...e];
let n = t.toLowerCase();
return e.filter((e) => (e.fulltext || e.value + (e.label || "") + (e.prefix || "")).toLowerCase().includes(n));
}, h = (e) => e.value + (e.label || "") + (e.prefix || ""), g = (e, t) => t !== null && e >= t, _ = (e, t) => {
if (!t) return {
filtered: [...e],
suggestion: null
};
let n = t.toLowerCase(), r = e.filter((e) => e.fulltext?.toLowerCase().includes(n));
return {
filtered: r,
suggestion: r.find((e) => !e.selected && e.label?.toLowerCase().startsWith(n)) || null
};
}, v = (e, t, n, r) => {
if (!e.trim()) return {
addValueText: null,
userInfoMessage: ""
};
let i = e.trim().toLowerCase(), a = t.find((e) => e.toLowerCase() === i), o = n.filter((e) => e.label?.toLowerCase().includes(i) || e.value.toLowerCase().includes(i)), s = o.find((e) => e.label?.toLowerCase() === i || e.value.toLowerCase() === i);
return (o.length === 0 || !s) && r ? {
addValueText: e,
userInfoMessage: ""
} : o.length === 0 && !r ? {
addValueText: null,
userInfoMessage: "Ingen treff i søket"
} : a ? {
addValueText: null,
userInfoMessage: "Verdien er allerede valgt"
} : {
addValueText: null,
userInfoMessage: ""
};
}, y = (e) => /^[\p{L} ]$/u.test(e), b = (e = 500) => {
let t = "", n;
return {
append: (r) => (t += r.toLowerCase(), n !== void 0 && clearTimeout(n), n = setTimeout(() => {
t = "";
}, e), t),
reset: () => {
t = "", n !== void 0 && (clearTimeout(n), n = void 0);
},
getBuffer: () => t
};
}, x = (e, t) => {
let n = t.toLowerCase();
return e.findIndex((e) => e.textContent?.trim().toLowerCase().startsWith(n));
}, S = (e) => {
e.scrollIntoView({ block: "nearest" }), window.setTimeout(() => e.focus(), 0);
}, C = (e) => Array.from(e.querySelectorAll("[role=\"option\"]:not([data-disabled])") || []), w = (e) => {
let t = e.nextElementSibling;
t && S(t);
}, T = (e, t, n) => {
let r = e.previousElementSibling;
if (e.dataset.index === "0" && n) {
let e = t.querySelector("[role=\"searchbox\"]");
e && S(e);
} else r && S(r);
}, E = (e) => {
let t = C(e);
t[0] && S(t[0]);
}, D = (e) => {
let t = C(e), n = t[t.length - 1];
n && S(n);
}, O = (e, t) => {
if (t.disabled) return;
let n = C(e);
if (t.allowUserInput && t.customUserInput) {
let t = e.querySelector("[data-type=\"new-option\"]");
if (t) {
S(t);
return;
}
}
let r = n.find((e) => e.dataset.selected === "true");
if (r) {
S(r);
return;
}
if (t.includeSearch && !(document.activeElement instanceof HTMLInputElement)) {
let t = e.querySelector("[role=\"searchbox\"]");
if (t) {
window.setTimeout(() => t.focus(), 0);
return;
}
}
n[0] && S(n[0]);
}, k = class extends s {
constructor(...e) {
super(...e), this.id = d(), this.label = null, this.options = [], this.isOpen = !1, this.disabled = !1, this.includeSearch = !1, this.isMultiSelect = !1, this.allowUserInput = !1, this.maxIsReached = !1, this.customUserInput = null, this.searchPlaceholder = null, this.searchValue = null, this.maxLength = 0, this.userMessage = null, this._selectedOptions = 0, this.typeahead = b(), this._filteredOptions = [];
}
connectedCallback() {
super.connectedCallback(), this.includeSearch && !this.searchValue && (this.searchValue = ""), this.options.length > 0 && this.filterOptions(), this.setAttribute("tabindex", "-1"), this.addEventListener("focus", this.focusFirstOrSelectedOption);
}
disconnectedCallback() {
super.disconnectedCallback(), this.typeahead.reset();
}
updated(e) {
(e.has("options") || e.has("searchValue")) && this.filterOptions(), super.updated(e);
}
attributeChangedCallback(e, t, n) {
(e === "options" || e === "search-value") && this.filterOptions(), super.attributeChangedCallback(e, t, n);
}
get _hasOptions() {
return this._filteredOptions.length > 0 || this.options.length > 0;
}
render() {
return t`
<div
class=${c({
"pkt-listbox": !0,
"pkt-listbox__open": this.isOpen,
"pkt-txt-16-light": !0
})}
role=${l(this._hasOptions ? "listbox" : void 0)}
aria-multiselectable=${l(this._hasOptions && this.isMultiSelect ? "true" : void 0)}
aria-label=${l(this._hasOptions ? this.label ?? void 0 : void 0)}
>
<div class="pkt-listbox__banners">
${this.renderSearch()} ${this.renderMaximumReachedBanner()} ${this.renderUserMessage()}
${this.renderEmptyMessage()} ${this.renderNewOptionBanner()}
</div>
<ul class="pkt-listbox__options" role="presentation">
${this.renderList()}
</ul>
</div>
<div aria-live="polite" class="pkt-visually-hidden">${this.userMessage}</div>
`;
}
renderCheckboxOrCheckIcon(e, n) {
return this.isMultiSelect ? t`
<input
class="pkt-input-check__input-checkbox"
type="checkbox"
role="presentation"
tabindex="-1"
value=${e.value}
=${(e) => {
e.stopPropagation();
}}
.checked=${e.selected}
aria-labelledby=${this.id + "-option-label-" + n}
?disabled=${this.disabled || e.disabled || this.maxIsReached && !e.selected}
/>
` : e.selected ? t`<pkt-icon name="check-big"></pkt-icon>` : r;
}
renderEmptyMessage() {
return this.options.length > 0 || this._filteredOptions.length > 0 || this.userMessage ? r : t`<div class="pkt-listbox__banner pkt-listbox__banner--empty">
<pkt-icon
class="pkt-listbox__banner-icon"
name="exclamation-mark-circle"
size="large"
></pkt-icon>
Tom liste
</div>`;
}
renderList() {
return t`
${u(this._filteredOptions, (e) => e.value, (e, n) => t`
<li
=${() => {
this.toggleOption(e);
}}
aria-selected=${e.selected ? "true" : "false"}
=${this.handleOptionKeydown}
class=${c({
"pkt-listbox__option": !0,
"pkt-listbox__option--selected": !!(!this.isMultiSelect && e.selected),
"pkt-listbox__option--checkBox": this.isMultiSelect
})}
tabindex="${this.disabled || e.disabled ? "-1" : "0"}"
data-index=${n}
data-value=${e.value}
data-selected=${e.selected ? "true" : "false"}
?data-disabled=${this.disabled || e.disabled || this.maxIsReached && !e.selected}
aria-disabled=${this.disabled || e.disabled || this.maxIsReached && !e.selected ? "true" : "false"}
role="option"
id=${`${this.id}-${n}`}
>
${this.renderCheckboxOrCheckIcon(e, n)}
<span class="pkt-listbox__option-label" id=${this.id + "-option-label-" + n}>
${e.prefix ? t`<span class="pkt-listbox__option-prefix">${e.prefix}</span>` : r}
${e.label || e.value}
</span>
${e.description ? t`<span class="pkt-listbox__option-description pkt-txt-14-light"
>${e.description}</span
>` : r}
</li>
`)}
`;
}
renderNewOptionBanner() {
return this.allowUserInput && this.customUserInput ? t`
<div
class="pkt-listbox__banner pkt-listbox__banner--new-option pkt-listbox__option"
data-type="new-option"
data-value=${this.customUserInput}
data-selected="false"
tabindex="0"
=${() => this.toggleOption({ value: this.customUserInput || "" })}
=${this.handleOptionKeydown}
>
<pkt-icon class="pkt-listbox__banner-icon" name="plus-sign" size="large"></pkt-icon>
Legg til "${this.customUserInput}"
</div>
` : r;
}
renderMaximumReachedBanner() {
return this._selectedOptions = this.options.filter((e) => e.selected).length, this.isMultiSelect && this._selectedOptions > 0 && this.maxLength > 0 ? t`
<div class="pkt-listbox__banner pkt-listbox__banner--maximum-reached">
${this._selectedOptions} av maks ${this.maxLength} mulige er valgt.
</div>
` : r;
}
renderUserMessage() {
return this.userMessage ? t`<div class="pkt-listbox__banner pkt-listbox__banner--user-message">
<pkt-icon
class="pkt-listbox__banner-icon"
name="exclamation-mark-circle"
size="large"
></pkt-icon>
${this.userMessage}
</div>` : r;
}
renderSearch() {
return this.includeSearch ? t`
<div class="pkt-listbox__search">
<span class="pkt-listbox__search-icon">
<pkt-icon name="magnifying-glass-small" size="large"></pkt-icon>
</span>
<input
class="pkt-txt-16-light"
type="text"
aria-label="Søk i listen"
form=""
placeholder=${this.searchPlaceholder || n.forms.search.placeholder}
=${this.handleSearchInput}
=${this.handleSearchKeydown}
.value=${this.searchValue}
data-type="searchbox"
?disabled=${this.disabled}
?readonly=${this.disabled}
role="searchbox"
/>
</div>
` : r;
}
handleSearchInput(e) {
this.searchValue = e.target.value, this.dispatchEvent(new CustomEvent("search", {
detail: this.searchValue,
bubbles: !1
}));
}
handleSearchKeydown(e) {
switch (e.key) {
case "Enter":
e.preventDefault();
break;
case "ArrowUp":
case "Escape":
this.closeOptions(), e.preventDefault();
break;
case "ArrowDown":
this.focusFirstOrSelectedOption();
break;
case "Tab":
this.tabClose();
break;
}
}
handleOptionKeydown(e) {
let t = e.currentTarget, n = t.dataset.value, r = t.dataset.type, i = t.dataset.selected === "true";
if (!(!C(this).length && (!this.customUserInput || !this.allowUserInput && this.customUserInput) && r !== "new-option" && r !== "searchbox")) switch (e.key) {
case " ":
case "Enter":
this.toggleOption(t), e.preventDefault();
break;
case "Backspace":
n && (i ? this.toggleOption(t) : this.closeOptions()), e.preventDefault();
break;
case "Escape":
this.closeOptions(), e.preventDefault();
break;
case "Tab":
this.tabClose();
break;
case "ArrowDown":
e.altKey ? D(this) : r === "searchbox" || r === "new-option" ? E(this) : w(t), e.preventDefault();
break;
case "ArrowUp":
if (e.altKey) E(this);
else if (t.dataset.index === "0" && this.includeSearch) {
let e = this.querySelector("[role=\"searchbox\"]");
e && e.focus();
} else if (t.dataset.index === "0" && this.customUserInput) {
let e = this.querySelector("[data-type=\"new-option\"]");
e && e.focus();
} else T(t, this, this.includeSearch);
e.preventDefault();
break;
case "Home":
E(this), e.preventDefault();
break;
case "End":
D(this), e.preventDefault();
break;
default:
(e.metaKey || e.ctrlKey) && e.key === "a" && (this.selectAll(), e.preventDefault()), y(e.key) && (this.handleTypeAhead(e.key), e.preventDefault());
break;
}
}
focusFirstOrSelectedOption() {
O(this, {
disabled: this.disabled,
allowUserInput: this.allowUserInput,
customUserInput: this.customUserInput,
includeSearch: this.includeSearch
});
}
toggleOption(e) {
let t = e instanceof HTMLElement ? e.dataset.disabled : e.disabled;
if (this.disabled || t) return;
let n = e instanceof HTMLElement ? e.dataset.value : e.value;
this.dispatchEvent(new CustomEvent("option-toggle", {
detail: n,
bubbles: !1
}));
}
selectAll() {
this.dispatchEvent(new CustomEvent("select-all", { bubbles: !1 }));
}
closeOptions() {
this.dispatchEvent(new CustomEvent("close-options", { bubbles: !1 }));
}
tabClose() {
this.dispatchEvent(new CustomEvent("tab-close", { bubbles: !1 }));
}
filterOptions() {
this._filteredOptions = m(this.options, this.searchValue);
}
handleTypeAhead(e) {
let t = this.typeahead.append(e), n = C(this), r = x(n, t);
r >= 0 && S(n[r]);
}
};
a([o({ type: String })], k.prototype, "id", void 0), a([o({ type: String })], k.prototype, "label", void 0), a([o({ type: Array })], k.prototype, "options", void 0), a([o({
type: Boolean,
reflect: !0,
attribute: "is-open"
})], k.prototype, "isOpen", void 0), a([o({ type: Boolean })], k.prototype, "disabled", void 0), a([o({
type: Boolean,
attribute: "include-search"
})], k.prototype, "includeSearch", void 0), a([o({
type: Boolean,
attribute: "is-multi-select"
})], k.prototype, "isMultiSelect", void 0), a([o({
type: Boolean,
attribute: "allow-user-input"
})], k.prototype, "allowUserInput", void 0), a([o({
type: Boolean,
attribute: "max-is-reached"
})], k.prototype, "maxIsReached", void 0), a([o({
type: String,
attribute: "custom-user-input"
})], k.prototype, "customUserInput", void 0), a([o({
type: String,
attribute: "search-placeholder"
})], k.prototype, "searchPlaceholder", void 0), a([o({
type: String,
attribute: "search-value"
})], k.prototype, "searchValue", void 0), a([o({
type: Number,
attribute: "max-length"
})], k.prototype, "maxLength", void 0), a([o({
type: String,
attribute: "user-message"
})], k.prototype, "userMessage", void 0), a([i()], k.prototype, "_filteredOptions", void 0);
try {
e("pkt-listbox")(k);
} catch {
console.warn("Forsøker å definere <pkt-listbox>, men den er allerede definert");
}
//#endregion
//#region src/components/listbox/index.ts
var A = k;
//#endregion
export { p as a, g as c, f as i, k as n, _ as o, h as r, v as s, A as t };